Skip to content

Commit

Permalink
Fix issue with handling multibyte characters.
Browse files Browse the repository at this point in the history
Added new method `websocket-frame-text' to return the payload of a frame
as utf-8 text.

Added a test to make sure message masked aren't multibyte.

Changed the text in the functional test to multibyte to help test
against these kinds of issues.
  • Loading branch information
ahyatt committed Oct 23, 2016
1 parent 5675553 commit f7d3fb5
Show file tree
Hide file tree
Showing 3 changed files with 33 additions and 19 deletions.
26 changes: 13 additions & 13 deletions websocket-functional-test.el
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,8 @@
(websocket-open
"ws://127.0.0.1:9999"
:on-message (lambda (_websocket frame)
(push (websocket-frame-payload frame) wstest-msgs)
(message "ws frame: %S" (websocket-frame-payload frame))
(push (websocket-frame-text frame) wstest-msgs)
(message "ws frame: %S" (websocket-frame-text frame))
(error "Test error (expected)"))
:on-close (lambda (_websocket) (setq wstest-closed t))))

Expand All @@ -67,10 +67,10 @@

(assert (null wstest-msgs))

(websocket-send-text wstest-ws "Hi!")
(websocket-send-text wstest-ws "你好")

(sleep-for 0.1)
(assert (equal (car wstest-msgs) "You said: Hi!"))
(assert (equal (car wstest-msgs) "You said: 你好"))
(setf (websocket-on-error wstest-ws) (lambda (_ws _type _err)))
(websocket-send-text wstest-ws "Hi after error!")
(sleep-for 0.1)
Expand Down Expand Up @@ -104,8 +104,8 @@
:on-open (lambda (_websocket)
(message "Websocket opened"))
:on-message (lambda (_websocket frame)
(push (websocket-frame-payload frame) wstest-msgs)
(message "ws frame: %S" (websocket-frame-payload frame)))
(push (websocket-frame-text frame) wstest-msgs)
(message "ws frame: %S" (websocket-frame-text frame)))
:on-close (lambda (_websocket)
(message "Websocket closed")
(setq wstest-closed t)))
Expand All @@ -131,24 +131,24 @@
:host 'local
:on-message (lambda (ws frame)
(message "Server received text!")
(websocket-send-text ws
(websocket-frame-payload frame)))
(websocket-send-text
ws (websocket-frame-text frame)))
:on-open (lambda (_websocket) "Client connection opened!")
:on-close (lambda (_websocket)
(setq wstest-closed t)))))

(setq wstest-msgs nil
wstest-ws
(websocket-open
"ws://localhost:9998"
:on-message (lambda (_websocket frame)
(push (websocket-frame-payload frame) wstest-msgs)
(message "ws frame: %S" (websocket-frame-payload frame)))))
(message "ws frame: %S" (websocket-frame-text frame))
(push
(websocket-frame-text frame) wstest-msgs))))

(assert (websocket-openp wstest-ws))
(websocket-send-text wstest-ws "Hi to self!")
(websocket-send-text wstest-ws "你好")
(sleep-for 0.3)
(assert (equal (car wstest-msgs) "Hi to self!"))
(assert (equal (car wstest-msgs) "你好"))
(websocket-server-close server-conn))
(assert wstest-closed)
(websocket-close wstest-ws)
Expand Down
3 changes: 3 additions & 0 deletions websocket-test.el
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,9 @@
(should (equal '("ext1" "ext2; a=1")
(websocket-negotiated-extensions ws-with-extensions)))))

(ert-deftest websocket-mask-is-unibyte ()
(should-not (multibyte-string-p (websocket-mask "\344\275\240\345\245\275" "abcdef"))))

(ert-deftest websocket-create-headers ()
(let ((base-headers (concat "Host: www.example.com\r\n"
"Upgrade: websocket\r\n"
Expand Down
23 changes: 17 additions & 6 deletions websocket.el
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

;; Author: Andrew Hyatt <ahyatt@gmail.com>
;; Keywords: Communication, Websocket, Server
;; Version: 1.6
;; Version: 1.7
;;
;; This program is free software; you can redistribute it and/or
;; modify it under the terms of the GNU General Public License as
Expand Down Expand Up @@ -279,14 +279,25 @@ many bytes were consumed from the string."

(defstruct websocket-frame opcode payload length completep)

(defun websocket-frame-text (frame)
"Given FRAME, return the payload as a utf-8 encoded string."
(assert (websocket-frame-p frame))
(decode-coding-string (websocket-frame-payload frame) 'utf-8))

(defun websocket-mask (key data)
"Using string KEY, mask string DATA according to the RFC.
This is used to both mask and unmask data."
(apply
'string
(loop for b across data
for i from 0 to (length data)
collect (logxor (websocket-get-bytes (substring key (mod i 4)) 1) b))))
;; If we don't make the string unibyte here, a string of bytes that should be
;; interpreted as a unibyte string will instead be interpreted as a multibyte
;; string of the same length (for example, 6 multibyte chars for 你好 instead
;; of the correct 6 unibyte chars, which would convert into 2 multibyte
;; chars).
(string-make-unibyte (apply
'string
(loop for b across data
for i from 0 to (length data)
collect
(logxor (websocket-get-bytes (substring key (mod i 4)) 1) b)))))

(defun websocket-ensure-length (s n)
"Ensure the string S has at most N bytes.
Expand Down

0 comments on commit f7d3fb5

Please sign in to comment.