branch: externals/websocket
commit 69a377a537a9f21e22bea23e3165e1ddbc422cd2
Author: Andrew Hyatt <ahy...@gmail.com>
Commit: Andrew Hyatt <ahy...@gmail.com>

    Bring ping behavior in line with RFC
    
    The code previously did not allow ping and pong to have a payload.
    Now, it is optional.  Additionally, a received ping will pong back the
    payload received.
---
 websocket-test.el | 48 ++++++++++++++++++++++++++-------
 websocket.el      | 80 ++++++++++++++++++++++++++++++++-----------------------
 2 files changed, 86 insertions(+), 42 deletions(-)

diff --git a/websocket-test.el b/websocket-test.el
index 5bcb90b217..f9f06985f5 100644
--- a/websocket-test.el
+++ b/websocket-test.el
@@ -236,10 +236,11 @@
     (setq sent nil)
     (flet ((websocket-send (websocket content) (setq sent content)))
       (should (equal
-               (make-websocket-frame :opcode 'pong :completep t)
+               (make-websocket-frame :opcode 'pong :payload "data" :completep 
t)
                (progn
                  (funcall (websocket-process-frame websocket
-                                           (make-websocket-frame :opcode 
'ping)))
+                                                   (make-websocket-frame 
:opcode 'ping
+                                                                         
:payload "data")))
                  sent))))
     (flet ((delete-process (conn) (setq deleted t)))
       (should (progn
@@ -303,13 +304,42 @@
      (websocket-encode-frame (make-websocket-frame :opcode 'text
                                                    :payload "Hello"
                                                    :completep nil) t))))
-  (dolist (opcode '(close ping pong))
-    (should (equal
-             opcode
-             (websocket-frame-opcode
-              (websocket-read-frame
-               (websocket-encode-frame (make-websocket-frame :opcode opcode
-                                                             :completep t) 
t)))))))
+  (should (equal 'close (websocket-frame-opcode
+                         (websocket-read-frame
+                           (websocket-encode-frame
+                            (make-websocket-frame :opcode 'close :completep t) 
t)))))
+  (dolist (opcode '(ping pong))
+    (let ((read-frame (websocket-read-frame
+                        (websocket-encode-frame
+                         (make-websocket-frame :opcode opcode
+                                               :payload "data"
+                                               :completep t) t))))
+      (should read-frame)
+      (should (equal
+               opcode
+               (websocket-frame-opcode read-frame)))
+      (should (equal
+               "data" (websocket-frame-payload read-frame)))))
+  ;; A frame should be four bytes, even for no-data pings.
+  (should (equal 2 (websocket-frame-length
+                    (websocket-read-frame
+                     (websocket-encode-frame 
+                      (make-websocket-frame :opcode 'ping :completep t) t))))))
+
+(ert-deftest websocket-check ()
+  (should (websocket-check (make-websocket-frame :opcode 'close :completep t)))
+  (should-not
+   (websocket-check (make-websocket-frame :opcode 'close :completep nil)))
+  (should-not
+   (websocket-check (make-websocket-frame :opcode 'close :completep t :payload 
"")))
+  (should (websocket-check (make-websocket-frame :opcode 'text :completep nil
+                                                 :payload "incompl")))
+  (should (websocket-check (make-websocket-frame :opcode 'ping :completep t)))
+  (should (websocket-check (make-websocket-frame :opcode 'ping :completep t
+                                                 :payload "")))
+  (should (websocket-check (make-websocket-frame :opcode 'pong :completep t
+                                                 :payload "")))
+  (should-not (websocket-check (make-websocket-frame :opcode 'text))))
 
 (ert-deftest websocket-close ()
   (let ((sent-frames)
diff --git a/websocket.el b/websocket.el
index 969e70b26b..a96876d9d4 100644
--- a/websocket.el
+++ b/websocket.el
@@ -296,35 +296,40 @@ We mask the frame or not, depending on SHOULD-MASK."
   (let* ((opcode (websocket-frame-opcode frame))
          (payload (websocket-frame-payload frame))
          (fin (websocket-frame-completep frame))
-         (payloadp (memq opcode '(continuation text binary)))
+         (payloadp (and payload
+                        (memq opcode '(continuation ping pong text binary))))
          (mask-key (when should-mask (websocket-genbytes 4))))
     (apply 'unibyte-string
-           (append (list
-                    (logior (cond ((eq opcode 'continuation) 0)
-                                  ((eq opcode 'text) 1)
-                                  ((eq opcode 'binary) 2)
-                                  ((eq opcode 'close) 8)
-                                  ((eq opcode 'ping) 9)
-                                  ((eq opcode 'pong) 10))
-                            (if fin 128 0)))
-                   (when payloadp
-                     (list
-                      (logior
-                       (if should-mask 128 0)
-                       (cond ((< (length payload) 126) (length payload))
-                             ((< (length payload) 65536) 126)
-                             (t 127)))))
-                   (when (and payloadp (>= (length payload) 126))
-                     (append (websocket-to-bytes (length payload)
-                                          (cond ((< (length payload) 126) 1)
-                                                ((< (length payload) 65536) 2)
-                                                (t 8))) nil))
-                   (when (and payloadp should-mask)
-                     (append mask-key nil))
-                   (when payloadp
-                     (append (if should-mask (websocket-mask mask-key payload)
-                               payload)
-                             nil))))))
+           (let ((val (append (list
+                            (logior (cond ((eq opcode 'continuation) 0)
+                                          ((eq opcode 'text) 1)
+                                          ((eq opcode 'binary) 2)
+                                          ((eq opcode 'close) 8)
+                                          ((eq opcode 'ping) 9)
+                                          ((eq opcode 'pong) 10))
+                                    (if fin 128 0)))
+                           (when payloadp
+                             (list
+                              (logior
+                               (if should-mask 128 0)
+                               (cond ((< (length payload) 126) (length 
payload))
+                                     ((< (length payload) 65536) 126)
+                                     (t 127)))))
+                           (when (and payloadp (>= (length payload) 126))
+                             (append (websocket-to-bytes
+                                      (length payload)
+                                      (cond ((< (length payload) 126) 1)
+                                            ((< (length payload) 65536) 2)
+                                            (t 8))) nil))
+                           (when (and payloadp should-mask)
+                             (append mask-key nil))
+                           (when payloadp
+                             (append (if should-mask (websocket-mask mask-key 
payload)
+                                       payload)
+                                     nil)))))
+             ;; We have to make sure the non-payload data is a full 32-bit 
frame
+             (if (= 1 (length val))
+                 (append val '(0)) val)))))
 
 (defun websocket-read-frame (s)
   "Read from string S a `websocket-frame' struct with the contents.
@@ -334,7 +339,7 @@ the frame finishes.  If the frame is not completed, return 
NIL."
     (websocket-ensure-length s 1)
     (let* ((opcode (websocket-get-opcode s))
            (fin (logand 128 (websocket-get-bytes s 1)))
-           (payloadp (memq opcode '(continuation text binary)))
+           (payloadp (memq opcode '(continuation text binary ping pong)))
            (payload-len (when payloadp
                           (websocket-get-payload-len (substring s 1))))
            (maskp (and
@@ -475,7 +480,10 @@ has connection termination."
                                            lex-ws lex-frame)))
             ((eq opcode 'ping)
              (lambda () (websocket-send lex-ws
-                                   (make-websocket-frame :opcode 'pong 
:completep t))))
+                                        (make-websocket-frame
+                                         :opcode 'pong
+                                         :payload (websocket-frame-payload 
lex-frame)
+                                         :completep t))))
             ((eq opcode 'close)
              (lambda () (delete-process (websocket-conn lex-ws))))
             (t (lambda ()))))))
@@ -506,10 +514,16 @@ has connection termination."
 
 (defun websocket-check (frame)
   "Check FRAME for correctness, returning true if correct."
-  (and (equal (not (memq (websocket-frame-opcode frame)
-                         '(continuation text binary)))
-              (and (not (websocket-frame-payload frame))
-                   (websocket-frame-completep frame)))))
+  (or
+   ;; Text, binary, and continuation frames need payloads
+   (and (memq (websocket-frame-opcode frame) '(text binary continuation))
+        (websocket-frame-payload frame))
+   ;; Pings and pongs may optionally have them
+   (memq (websocket-frame-opcode frame) '(ping pong))
+   ;; And close shouldn't have any payload, and should always be complete.
+   (and (eq (websocket-frame-opcode frame) 'close)
+        (not (websocket-frame-payload frame))
+        (websocket-frame-completep frame))))
 
 (defun websocket-send (websocket frame)
   "To the WEBSOCKET server, send the FRAME.

Reply via email to