emacs-elpa-diffs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[elpa] externals/websocket 413702e: Update websocket library to version


From: Andrew Hyatt
Subject: [elpa] externals/websocket 413702e: Update websocket library to version 1.13.
Date: Sat, 9 Jan 2021 20:55:15 -0500 (EST)

branch: externals/websocket
commit 413702e94fd95b796e2944705cbac63ca5131859
Author: Andrew Hyatt <ahyatt@gmail.com>
Commit: Andrew Hyatt <ahyatt@gmail.com>

    Update websocket library to version 1.13.
    
    The major changes are:
    - Made all tests runnable via ert, and re-introduced the python-based 
webserver test.
    - Fixes an issue with infinite loops when websockets get an error. We now 
do not attempt to reconnect when the connection is dropped.
    - Fixes an issue with nowait connection timings.
    - Fixes an issue with handshake protocol that was an issue on some servers.
---
 testserver.py                |   7 +-
 websocket-functional-test.el | 227 +++++++++++-----------------
 websocket-test.el            | 347 ++++++++++++++++++++++++++++++-------------
 websocket.el                 |  39 ++---
 4 files changed, 343 insertions(+), 277 deletions(-)

diff --git a/testserver.py b/testserver.py
index 5cfcb96..46cf62d 100644
--- a/testserver.py
+++ b/testserver.py
@@ -1,3 +1,4 @@
+#!/usr/bin/env python3
 import logging
 import tornado
 import tornado.web
@@ -12,8 +13,8 @@ class EchoWebSocket(websocket.WebSocketHandler):
         logging.info("OPEN")
 
     def on_message(self, message):
-        logging.info(u"ON_MESSAGE: {0}".format(message))
-        self.write_message(u"You said: {0}".format(message))
+        logging.info("ON_MESSAGE: {0}".format(message))
+        self.write_message(message)
 
     def on_close(self):
         logging.info("ON_CLOSE")
@@ -29,6 +30,6 @@ if __name__ == "__main__":
         (r"/", EchoWebSocket),
     ])
     server = httpserver.HTTPServer(application)
-    server.listen(9999)
+    server.listen(9999, "127.0.0.1")
     logging.info("STARTED: Server start listening")
     ioloop.IOLoop.instance().start()
diff --git a/websocket-functional-test.el b/websocket-functional-test.el
index cc9ac70..8a599d0 100644
--- a/websocket-functional-test.el
+++ b/websocket-functional-test.el
@@ -4,7 +4,7 @@
 
 ;; This program is free software; you can redistribute it and/or
 ;; modify it under the terms of the GNU General Public License as
-;; published by the Free Software Foundation; either version 3 of the
+;; published by the Free Software Foundation; either version 2 of the
 ;; License, or (at your option) any later version.
 ;;
 ;; This program is distributed in the hope that it will be useful, but
@@ -17,147 +17,90 @@
 
 ;;; Commentary:
 
-;; Usage: emacs -batch -Q -L . -l websocket-functional-test.el
+;; These are functional tests that may fail for various environmental reasons,
+;; such as blocked ports. For example Windows users have to have gnutls DLLs in
+;; the Emacs bin directory for this to work. A firewall may also interfere with
+;; these tests.
 ;;
-;; Note: this functional tests requires that you have python with the
-;; Tornado web server.  See http://www.tornadoweb.org/en/stable/ for
-;; information on aquiring.
+;; These tests are written to test the basic connectivity and message-sending.
+;; Corner-cases and error handling is tested in websocket-test.el.
 
-(require 'tls)   ;; tests a particular bug we had on emacs 23
-(setq debug-on-error t)
+(require 'tls)   ;; tests a particular bug we had on Emacs 23
 (require 'websocket)
-(eval-when-compile (require 'cl))
-
-;;;;;;;;;;;;;;;;;;;;;;;
-;; Local server test ;;
-;;;;;;;;;;;;;;;;;;;;;;;
-
-(message "Testing with local server")
-
-(setq websocket-debug t)
-
-(defvar wstest-server-buffer (get-buffer-create "*wstest-server*"))
-(defvar wstest-server-name "wstest-server")
-(defvar wstest-server-proc
-  (start-process wstest-server-name wstest-server-buffer
-                 "python" "testserver.py" "--log_to_stderr" "--logging=debug"))
-(sleep-for 1)
-
-(defvar wstest-msgs nil)
-(defvar wstest-closed nil)
-
-(message "Opening the websocket")
-
-(defvar wstest-ws
-  (websocket-open
-   "ws://127.0.0.1:9999"
-   :on-message (lambda (_websocket 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))))
-
-(defun wstest-pop-to-debug ()
-  "Open websocket log buffer. Not used in testing. Just for debugging."
-  (interactive)
-  (pop-to-buffer (websocket-get-debug-buffer-create wstest-ws)))
-
-(sleep-for 0.1)
-(assert (websocket-openp wstest-ws))
-
-(assert (null wstest-msgs))
-
-(websocket-send-text wstest-ws "你好")
-
-(sleep-for 0.1)
-(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)
-(assert (equal (car wstest-msgs) "You said: Hi after error!"))
-
-(websocket-close wstest-ws)
-(assert (null (websocket-openp wstest-ws)))
-
-(if (not (eq system-type 'windows-nt))
-    ; Windows doesn't have support for the SIGSTP signal, so we'll just kill
-    ; the process.
-    (stop-process wstest-server-proc))
-(kill-process wstest-server-proc)
-
-;; Make sure the processes are closed.  This happens asynchronously,
-;; so let's wait for it.
-(sleep-for 1)
-(assert (null (process-list)) t)
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;; Remote server test, with wss ;;
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
-;; echo.websocket.org has an untrusted certificate, for the test to
-;; proceed, we need to disable trust checking.
-(setq tls-checktrust nil)
-
-(when (>= (string-to-number (substring emacs-version 0 2)) 24)
-  (message "Testing with wss://echo.websocket.org")
-  (when (eq system-type 'windows-nt)
-    (message "Windows users must have gnutls DLLs in the emacs bin 
directory."))
-  (setq wstest-ws
-        (websocket-open
-         "wss://echo.websocket.org"
-         :on-open (lambda (_websocket)
-                    (message "Websocket opened"))
-         :on-message (lambda (_websocket 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)))
-        wstest-msgs nil)
-  (sleep-for 0.3)
-  (assert (websocket-openp wstest-ws))
-  (sleep-for 0.6)
-  (assert (eq 'open (websocket-ready-state wstest-ws)))
-  (assert (null wstest-msgs))
-  (websocket-send-text wstest-ws "Hi!")
-  (sleep-for 1)
-  (assert (equal (car wstest-msgs) "Hi!"))
-  (websocket-close wstest-ws))
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;; Local client and server ;;
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
-(message "Testing with emacs websocket server.")
-(message "If this does not pass, make sure your firewall allows the 
connection.")
-(setq wstest-closed nil)
-(let ((server-conn (websocket-server
-                    9998
-                    :host 'local
-                    :on-message (lambda (ws frame)
-                                  (message "Server received text!")
-                                  (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)
-                       (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 "你好")
-  (sleep-for 0.3)
-  (assert (equal (car wstest-msgs) "你好"))
-  (websocket-server-close server-conn))
-(assert wstest-closed)
-(websocket-close wstest-ws)
-
-(sleep-for 1)
-(assert (null (process-list)) t)
-(message "\nAll tests passed!\n")
+(require 'cl)
+
+;;; Code:
+
+(defmacro websocket-test-wait-with-timeout (timeout &rest body)
+  "Run BODY until true or TIMEOUT (in seconds) is reached.
+
+Will return false if the timeout was reached. This macro is not
+written to be used widely."
+  `(let ((begin (current-time))
+         (result nil))
+     (while (and (< (- (float-time (time-subtract (current-time) begin))) 
,timeout) (not result))
+       (setq result ,@body)
+       (sleep-for 0.5))
+     result))
+
+(defun websocket-functional-client-test (wstest-server-url)
+  "Run the main part of an ert test against WSTEST-SERVER-URL."
+  ;; the server may have an untrusted certificate, for the test to proceed, we
+  ;; need to disable trust checking.
+  (let* ((tls-checktrust nil)
+         (wstest-closed nil)
+         (wstest-msg)
+         (wstest-server-proc)
+         (wstest-ws
+          (websocket-open
+           wstest-server-url
+           :on-message (lambda (_websocket frame)
+                         (setq wstest-msg (websocket-frame-text frame)))
+           :on-close (lambda (_websocket) (setq wstest-closed t)))))
+    (should (websocket-test-wait-with-timeout 2 (websocket-openp wstest-ws)))
+    (should (websocket-test-wait-with-timeout 2 (eq 'open 
(websocket-ready-state wstest-ws))))
+    (should (null wstest-msg))
+    (websocket-send-text wstest-ws "Hi!")
+    (should (websocket-test-wait-with-timeout 5 (equal wstest-msg "Hi!")))
+    (websocket-close wstest-ws)))
+
+(ert-deftest websocket-client-with-local-server ()
+  ;; If testserver.py cannot start, this test will fail. In general, if you
+  ;; don't care about avoiding outside connections, the remote server variant 
is
+  ;; usually easier to run, and tests the same things..
+  (let ((proc (start-process
+               "websocket-testserver" "*websocket-testserver*"
+               "python3" "testserver.py" "--log_to_stderr" "--logging=debug")))
+    (when proc
+      (sleep-for 1)
+      (websocket-functional-client-test "ws://127.0.0.1:9999"))))
+
+(ert-deftest websocket-client-with-remote-server ()
+  ;; Emacs previous to Emacs 24 cannot handle wss.
+  (if (>= (string-to-number (substring emacs-version 0 2)) 24)
+      (websocket-functional-client-test "wss://echo.websocket.org")
+    (websocket-functional-client-test "ws://echo.websocket.org")))
+
+(ert-deftest websocket-server ()
+  (let* ((wstest-closed)
+         (wstest-msg)
+         (server-conn (websocket-server
+                       9998
+                       :host 'local
+                       :on-message (lambda (ws frame)
+                                     (websocket-send-text
+                                      ws (websocket-frame-text frame)))
+                       :on-close (lambda (_websocket)
+                                   (setq wstest-closed t))))
+         (wstest-ws (websocket-open
+                    "ws://localhost:9998"
+                    :on-message (lambda (_websocket frame)
+                                  (setq wstest-msg (websocket-frame-text 
frame))))))
+    (should (websocket-test-wait-with-timeout 1 (websocket-openp wstest-ws)))
+    (websocket-send-text wstest-ws "你好")
+    (should (websocket-test-wait-with-timeout 1 (equal wstest-msg "你好")))
+    (websocket-server-close server-conn)
+    (should (websocket-test-wait-with-timeout 1 wstest-closed))))
+
+(provide 'websocket-functional-test)
+;;; websocket-functional-test.el ends here
diff --git a/websocket-test.el b/websocket-test.el
index 5de21d3..c133272 100644
--- a/websocket-test.el
+++ b/websocket-test.el
@@ -7,7 +7,7 @@
 ;;
 ;; This program is free software; you can redistribute it and/or
 ;; modify it under the terms of the GNU General Public License as
-;; published by the Free Software Foundation; either version 3 of the
+;; published by the Free Software Foundation; either version 2 of the
 ;; License, or (at your option) any later version.
 ;;
 ;; This program is distributed in the hope that it will be useful, but
@@ -111,32 +111,41 @@
 (ert-deftest websocket-verify-response-code ()
   (should (websocket-verify-response-code "HTTP/1.1 101"))
   (should
-   (eq 400 (cdr (should-error (websocket-verify-response-code "HTTP/1.1 400")
-                          :type 'websocket-received-error-http-response))))
+   (equal '(400) (cdr (should-error (websocket-verify-response-code "HTTP/1.1 
400")
+                                    :type 
'websocket-received-error-http-response))))
   (should
-   (eq 200 (cdr (should-error (websocket-verify-response-code "HTTP/1.1 
200"))))))
+   (equal '(200) (cdr (should-error (websocket-verify-response-code "HTTP/1.1 
200")))))
+  (should-error (websocket-verify-response-code "HTTP/1.")
+                :type 'websocket-invalid-header))
 
 (ert-deftest websocket-verify-headers ()
   (let ((accept "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=")
+        (accept-alt-case "Sec-Websocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=")
         (invalid-accept "Sec-WebSocket-Accept: bad")
         (upgrade "Upgrade: websocket")
+        (upgrade-alt-case "Upgrade: Websocket")
         (connection "Connection: upgrade")
         (ws (websocket-inner-create
              :conn "fake-conn" :url "ws://foo/bar"
              :accept-string "s3pPLMBiTxaQ9kYGzzhZRbK+xOo="))
         (ws-with-protocol
          (websocket-inner-create
-             :conn "fake-conn" :url "ws://foo/bar"
-             :accept-string "s3pPLMBiTxaQ9kYGzzhZRbK+xOo="
-             :protocols '("myprotocol")))
+          :conn "fake-conn" :url "ws://foo/bar"
+          :accept-string "s3pPLMBiTxaQ9kYGzzhZRbK+xOo="
+          :protocols '("myprotocol")))
         (ws-with-extensions
          (websocket-inner-create
-             :conn "fake-conn" :url "ws://foo/bar"
-             :accept-string "s3pPLMBiTxaQ9kYGzzhZRbK+xOo="
-             :extensions '("ext1" "ext2"))))
+          :conn "fake-conn" :url "ws://foo/bar"
+          :accept-string "s3pPLMBiTxaQ9kYGzzhZRbK+xOo="
+          :extensions '("ext1" "ext2"))))
     (should (websocket-verify-headers
              ws
              (websocket-test-header-with-lines accept upgrade connection)))
+    ;; Force case sensitivity to make sure we aren't too case sensitive.
+    (let ((case-fold-search nil))
+      (should (websocket-verify-headers
+               ws
+               (websocket-test-header-with-lines accept-alt-case 
upgrade-alt-case connection))))
     (should-error
      (websocket-verify-headers
       ws
@@ -192,28 +201,86 @@
     (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-frame-correctly-encoded ()
+  ;; This example comes from 
https://github.com/ahyatt/emacs-websocket/issues/58.
+  (cl-letf ((text 
"{\"parent_header\":{},\"header\":{\"msg_id\":\"a2940bc8-619e-4872-97bd-4c8d6fb93017\",\"msg_type\":\"history_request\",\"version\":\"5.3\",\"username\":\"n\",\"session\":\"409cf442-74ba-462f-8183-6652503005af\",\"date\":\"2019-06-20T02:17:43.925049-0500\"},\"content\":{\"output\":false,\"raw\":false,\"hist_access_type\":\"tail\",\"n\":100},\"metadata\":{},\"buffers\":[],\"channel\":\"shell\"}")
+            ((symbol-function #'websocket-genbytes)
+             (lambda (&rest _) "\10\206\356\224")))
+    (let ((frame (websocket-read-frame
+                  (websocket-encode-frame
+                   (make-websocket-frame :opcode 'text
+                                         :payload (encode-coding-string text 
'raw-text)
+                                         :completep t)
+                   t))))
+      (should frame)
+      (should (equal (websocket-frame-payload frame) text)))))
+
 (ert-deftest websocket-create-headers ()
-  (let ((system-name "mysystem")
-        (base-headers (concat "Host: www.example.com\r\n"
+  (let ((base-headers (concat "Host: www.example.com\r\n"
                               "Upgrade: websocket\r\n"
                               "Connection: Upgrade\r\n"
                               "Sec-WebSocket-Key: key\r\n"
-                              "Origin: mysystem\r\n"
                               "Sec-WebSocket-Version: 13\r\n")))
-    (should (equal (concat base-headers "\r\n")
-                   (websocket-create-headers "ws://www.example.com/path"
-                                             "key" nil nil)))
-    (should (equal (concat base-headers
-                           "Sec-WebSocket-Protocol: protocol\r\n\r\n")
-                   (websocket-create-headers "ws://www.example.com/path"
-                                             "key" '("protocol") nil)))
-    (should (equal
-             (concat base-headers
-                     "Sec-WebSocket-Extensions: ext1; a; b=2, ext2\r\n\r\n")
-             (websocket-create-headers "ws://www.example.com/path"
-                                       "key" nil
-                                       '(("ext1" . ("a" "b=2"))
-                                         ("ext2")))))))
+    (cl-letf (((symbol-function 'url-cookie-generate-header-lines)
+               (lambda (host localpart secure) "")))
+      (should (equal (concat base-headers "\r\n")
+                     (websocket-create-headers "ws://www.example.com/path"
+                                               "key" nil nil nil)))
+      (should (equal (concat base-headers
+                             "Sec-WebSocket-Protocol: protocol\r\n\r\n")
+                     (websocket-create-headers "ws://www.example.com/path"
+                                               "key" '("protocol") nil nil)))
+      (should (equal
+               (concat base-headers
+                       "Sec-WebSocket-Extensions: ext1; a; b=2, ext2\r\n\r\n")
+               (websocket-create-headers "ws://www.example.com/path"
+                                         "key" nil
+                                         '(("ext1" . ("a" "b=2"))
+                                           ("ext2")) nil)))
+      (should (equal
+               (concat base-headers "Foo: bar\r\nBaz: boo\r\n\r\n")
+               (websocket-create-headers "ws://www.example.com/path"
+                                         "key" nil nil '(("Foo" . "bar") 
("Baz" . "boo"))))))
+    (cl-letf (((symbol-function 'url-cookie-generate-header-lines)
+               (lambda (host localpart secure)
+                 (should (equal host "www.example.com:123"))
+                 (should (equal localpart "/path"))
+                 (should secure)
+                 "Cookie: foo=bar\r\n")))
+      (should (equal (websocket-create-headers "wss://www.example.com:123/path"
+                                               "key" nil nil nil)
+                     (concat
+                      "Host: www.example.com:123\r\n"
+                      "Upgrade: websocket\r\n"
+                      "Connection: Upgrade\r\n"
+                      "Sec-WebSocket-Key: key\r\n"
+                      "Sec-WebSocket-Version: 13\r\n"
+                      "Cookie: foo=bar\r\n\r\n"))))
+    (should
+     (string-match
+      "Host: www.example.com:123\r\n"
+      (websocket-create-headers "ws://www.example.com:123/path" "key" nil nil 
nil)))))
+
+(ert-deftest websocket-process-headers ()
+  (cl-flet ((url-cookie-handle-set-cookie
+             (text)
+             (should (equal text "foo=bar;"))
+             ;; test that we have set the implicit buffer variable needed
+             ;; by url-cookie-handle-set-cookie
+             (should (equal url-current-object
+                            (url-generic-parse-url "ws://example.com/path")))))
+    (websocket-process-headers "ws://example.com/path"
+                               (concat
+                                "HTTP/1.1 101 Switching Protocols\r\n"
+                                "Upgrade: websocket\r\n"
+                                "Connection: Upgrade\r\n"
+                                "Set-Cookie: foo=bar;\r\n\r\n")))
+  (cl-flet ((url-cookie-handle-set-cookie (text) (should nil)))
+    (websocket-process-headers "ws://example.com/path"
+                               "HTTP/1.1 101 Switching Protocols\r\n")))
 
 (ert-deftest websocket-process-frame ()
   (let* ((sent)
@@ -236,14 +303,17 @@
                    (make-websocket-frame :opcode opcode :payload "hello")))
                  processed))))
     (setq sent nil)
-    (flet ((websocket-send (websocket content) (setq sent content)))
+    (cl-letf (((symbol-function 'websocket-send)
+               (lambda (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)))
+    (cl-letf (((symbol-function 'delete-process)
+               (lambda (conn) (setq deleted t))))
       (should (progn
                 (funcall
                  (websocket-process-frame websocket
@@ -271,7 +341,11 @@
   (should (equal 30 (websocket-get-bytes (websocket-to-bytes 30 1) 1)))
   (should (equal 300 (websocket-get-bytes (websocket-to-bytes 300 2) 2)))
   (should (equal 70000 (websocket-get-bytes (websocket-to-bytes 70000 8) 8)))
-  (should-error (websocket-to-bytes 536870912 8) :type 
'websocket-frame-too-large)
+  ;; Only run if the number we're testing with is not more than the system can
+  ;; handle.
+  (if (equal "1" (calc-eval (format "536870912 < %d" most-positive-fixnum)))
+      (should-error (websocket-to-bytes 536870912 8)
+                    :type 'websocket-frame-too-large))
   (should-error (websocket-to-bytes 30 3))
   (should-error (websocket-to-bytes 300 1))
   ;; I'd like to test the error for 32-byte systems on 8-byte lengths,
@@ -287,15 +361,16 @@
              (websocket-encode-frame
               (make-websocket-frame :opcode 'text :payload "Hello" :completep 
t) nil)))
   (dolist (len '(200 70000))
-        (let ((long-string (make-string len ?x)))
-          (should (equal long-string
-                         (websocket-frame-payload
-                          (websocket-read-frame
-                           (websocket-encode-frame
-                            (make-websocket-frame :opcode 'text
-                                                  :payload long-string) 
t)))))))
-  (flet ((websocket-genbytes (n) (substring websocket-test-masked-hello 2 6)))
-      (should (equal websocket-test-masked-hello
+    (let ((long-string (make-string len ?x)))
+      (should (equal long-string
+                     (websocket-frame-payload
+                      (websocket-read-frame
+                       (websocket-encode-frame
+                        (make-websocket-frame :opcode 'text
+                                              :payload long-string) t)))))))
+  (cl-letf (((symbol-function 'websocket-genbytes)
+             (lambda (n) (substring websocket-test-masked-hello 2 6))))
+    (should (equal websocket-test-masked-hello
                      (websocket-encode-frame
                       (make-websocket-frame :opcode 'text :payload "Hello"
                                             :completep t) t))))
@@ -305,22 +380,53 @@
      (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)
         (processes-deleted))
-    (flet ((websocket-send (websocket frame) (push frame sent-frames))
-           (websocket-openp (websocket) t)
-           (kill-buffer (buffer))
-           (delete-process (proc))
-           (process-buffer (conn) (add-to-list 'processes-deleted conn)))
+    (cl-letf (((symbol-function 'websocket-send)
+               (lambda (websocket frame) (push frame sent-frames)))
+              ((symbol-function 'websocket-openp)
+               (lambda (websocket) t))
+              ((symbol-function 'kill-buffer) (lambda (buffer) t))
+              ((symbol-function 'delete-process)
+               (lambda (proc) (add-to-list 'processes-deleted proc))))
       (websocket-close (websocket-inner-create
                         :conn "fake-conn"
                         :url t
@@ -350,12 +456,14 @@
           (concat
            (websocket-encode-frame frame1 t)
            (websocket-encode-frame frame2 t))))
-    (flet ((websocket-process-frame
-            (websocket frame)
-            (lexical-let ((frame frame))
-              (lambda () (push frame processed-frames))))
-           (websocket-verify-response-code (output) t)
-           (websocket-verify-headers (websocket output) t))
+    (cl-letf (((symbol-function 'websocket-process-frame)
+               (lambda (websocket frame)
+                 (lexical-let ((frame frame))
+                   (lambda () (push frame processed-frames)))))
+              ((symbol-function 'websocket-verify-headers)
+               (lambda (websocket output) t))
+              ((symbol-function 'websocket-close) (lambda (websocket) t)))
+      (websocket-outer-filter fake-ws "HTTP/1.1 101 Switching Protocols\r\n")
       (websocket-outer-filter fake-ws "Sec-")
       (should (eq (websocket-ready-state fake-ws) 'connecting))
       (should-not open-callback-called)
@@ -368,11 +476,17 @@
       (websocket-outer-filter fake-ws (substring websocket-frames 2))
       (should (equal (list frame2 frame1) processed-frames))
       (should-not (websocket-inflight-input fake-ws)))
-    (flet ((websocket-close (websocket)))
-      (setf (websocket-ready-state fake-ws) 'connecting)
-      (should (eq 500 (cdr (should-error
-                                (websocket-outer-filter fake-ws "HTTP/1.1 
500\r\n\r\n")
-                                :type 
'websocket-received-error-http-response)))))))
+    (cl-letf (((symbol-function 'websocket-close) (lambda (websocket) t)))
+      (let ((on-error-called))
+        (setf (websocket-ready-state fake-ws) 'connecting)
+        (setf (websocket-on-open fake-ws) (lambda (ws &rest _) t))
+        (setf (websocket-on-error fake-ws)
+              (lambda (_ type err)
+                (should (eq type 'on-open))
+                (should (equal '(websocket-received-error-http-response 500) 
err))
+                (setq on-error-called t)))
+        (websocket-outer-filter fake-ws "HTTP/1.1 500\r\n\r\n")
+        (should on-error-called)))))
 
 (ert-deftest websocket-outer-filter-bad-connection ()
   (let* ((on-open-calledp)
@@ -381,9 +495,12 @@
                    :conn t :url t :accept-string t
                    :on-open (lambda (websocket)
                               (setq on-open-calledp t)))))
-    (flet ((websocket-verify-response-code (output) t)
-           (websocket-verify-headers (websocket output) (error "Bad headers!"))
-           (websocket-close (websocket) (setq websocket-closed-calledp t)))
+    (cl-letf (((symbol-function 'websocket-verify-response-code)
+               (lambda (output) t))
+              ((symbol-function 'websocket-verify-headers)
+               (lambda (websocket output) (error "Bad headers!")))
+              ((symbol-function 'websocket-close)
+               (lambda (websocket) (setq websocket-closed-calledp t))))
       (condition-case err
           (progn (websocket-outer-filter fake-ws "HTTP/1.1 101\r\n\r\n")
                  (error "Should have thrown an error!"))
@@ -391,18 +508,34 @@
          (should-not on-open-calledp)
          (should websocket-closed-calledp))))))
 
+(ert-deftest websocket-outer-filter-fragmented-header ()
+  (let* ((on-open-calledp)
+         (websocket-closed-calledp)
+         (fake-ws (websocket-inner-create
+                   :protocols '("websocket")
+                   :conn t :url t :accept-string "17hG/VoPPd14L9xPSI7LtEr7PQc="
+                   :on-open (lambda (websocket)
+                              (setq on-open-calledp t)))))
+    (cl-letf (((symbol-function 'websocket-close) (lambda (websocket) t)))
+      (websocket-outer-filter fake-ws "HTTP/1.1 101 Web Socket Protocol 
Handsh")
+      (websocket-outer-filter fake-ws "ake\r\nConnection: Upgrade\r\n")
+      (websocket-outer-filter fake-ws "Upgrade: websocket\r\n")
+      (websocket-outer-filter fake-ws "Sec-websocket-Protocol: websocket\r\n")
+      (websocket-outer-filter fake-ws "Sec-WebSocket-Accept: 
17hG/VoPPd14L9xPSI7LtEr7PQc=\r\n\r\n"))))
+
 (ert-deftest websocket-send-text ()
-  (flet ((websocket-send (ws frame)
-                         (should (equal
-                                  (websocket-frame-payload frame)
-                                  "\344\275\240\345\245\275"))))
+  (cl-letf (((symbol-function 'websocket-send)
+             (lambda (ws frame)
+               (should (equal
+                        (websocket-frame-payload frame)
+                        "\344\275\240\345\245\275")))))
     (websocket-send-text nil "你好")))
 
 (ert-deftest websocket-send ()
   (let ((ws (websocket-inner-create :conn t :url t :accept-string t)))
-    (flet ((websocket-ensure-connected (websocket))
-           (websocket-openp (websocket) t)
-           (process-send-string (conn string)))
+    (cl-letf (((symbol-function 'websocket-ensure-connected) (lambda  
(websocket) t))
+              ((symbol-function 'websocket-openp) (lambda (websocket) t))
+              ((symbol-function 'process-send-string) (lambda (conn string) 
t)))
       ;; Just make sure there is no error.
       (websocket-send ws (make-websocket-frame :opcode 'ping
                                                        :completep t)))
@@ -423,7 +556,6 @@
          (upgrade "Upgrade: websocket")
          (key (format "Sec-Websocket-Key: %s" "key"))
          (version "Sec-Websocket-Version: 13")
-         (origin "Origin: origin")
          (protocol "Sec-Websocket-Protocol: protocol")
          (extensions1 "Sec-Websocket-Extensions: foo")
          (extensions2 "Sec-Websocket-Extensions: bar; baz=2")
@@ -485,11 +617,12 @@
         (closed)
         (response)
         (processed))
-    (flet ((process-send-string (p text) (setq response text))
-           (websocket-close (ws) (setq closed t))
-           (process-get (process sym) ws))
+    (cl-letf (((symbol-function 'process-send-string) (lambda (p text) (setq 
response text)))
+              ((symbol-function 'websocket-close) (lambda (ws) (setq closed 
t)))
+              ((symbol-function 'process-get) (lambda (process sym) ws)))
      ;; Bad request, in two parts
-     (flet ((websocket-verify-client-headers (text) nil))
+      (cl-letf (((symbol-function 'websocket-verify-client-headers)
+                 (lambda (text) nil)))
        (websocket-server-filter nil "HTTP/1.0 GET /foo \r\n")
        (should-not closed)
        (websocket-server-filter nil "\r\n")
@@ -499,13 +632,16 @@
      (setq closed nil
            response nil)
      (setf (websocket-inflight-input ws) nil)
-     (flet ((websocket-verify-client-headers (text) t)
-            (websocket-get-server-response (ws protocols extensions)
-                                           "response")
-            (websocket-process-input-on-open-ws (ws text)
-                                                (setq processed t)
-                                                (should
-                                                 (equal text 
websocket-test-hello))))
+     (cl-letf (((symbol-function 'websocket-verify-client-headers)
+                (lambda (text) t))
+               ((symbol-function 'websocket-get-server-response)
+                (lambda (ws protocols extensions)
+                  "response"))
+               ((symbol-function 'websocket-process-input-on-open-ws)
+                (lambda (ws text)
+                  (setq processed t)
+                  (should
+                   (equal text websocket-test-hello)))))
        (websocket-server-filter nil
                                 (concat "\r\n\r\n" websocket-test-hello))
        (should (equal (websocket-ready-state ws) 'open))
@@ -529,7 +665,6 @@
                                    "Upgrade: websocket\r\n"
                                    "Connection: Upgrade\r\n"
                                    "Sec-WebSocket-Key: 
dGhlIHNhbXBsZSBub25jZQ==\r\n"
-                                   "Origin: http://example.com\r\n";
                                    "Sec-WebSocket-Protocol: chat, 
superchat\r\n"
                                    "Sec-WebSocket-Version: 13\r\n"))))
                      (should header-info)
@@ -556,14 +691,16 @@
                                        :ready-state 'closed)))
         (deleted-processes)
         (closed-websockets))
-    (flet ((delete-process (conn) (add-to-list 'deleted-processes conn))
-           (websocket-close (ws)
-                            ;; we always remove on closing in the
-                            ;; actual code.
-                            (setq websocket-server-websockets
-                                  (remove ws websocket-server-websockets))
-                            (should-not (eq (websocket-ready-state ws) 
'closed))
-                            (add-to-list 'closed-websockets ws)))
+    (cl-letf (((symbol-function 'delete-process)
+               (lambda (conn) (add-to-list 'deleted-processes conn)))
+              ((symbol-function 'websocket-close)
+               (lambda (ws)
+                 ;; we always remove on closing in the
+                 ;; actual code.
+                 (setq websocket-server-websockets
+                       (remove ws websocket-server-websockets))
+                 (should-not (eq (websocket-ready-state ws) 'closed))
+                 (add-to-list 'closed-websockets ws))))
       (websocket-server-close 'b))
     (should (equal deleted-processes '(b)))
     (should (eq 1 (length closed-websockets)))
@@ -572,16 +709,16 @@
     (should (eq 'conn-a (websocket-conn (car websocket-server-websockets))))))
 
 (ert-deftest websocket-default-error-handler ()
-  (flet ((try-error
-          (callback-type err expected-message)
-          (flet ((display-warning
-                  (type message &optional level buffer-name)
-                  (should (eq type 'websocket))
-                  (should (eq level :error))
-                  (should (string= message expected-message))))
-            (websocket-default-error-handler nil
-                                             callback-type
-                                             err))))
+  (cl-letf (((symbol-function 'try-error)
+             (lambda (callback-type err expected-message)
+               (cl-flet ((display-warning
+                          (type message &optional level buffer-name)
+                          (should (eq type 'websocket))
+                          (should (eq level :error))
+                          (should (string= message expected-message))))
+                 (websocket-default-error-handler nil
+                                                  callback-type
+                                                  err)))))
     (try-error
      'on-message
      '(end-of-buffer)
diff --git a/websocket.el b/websocket.el
index 1d69508..3424852 100644
--- a/websocket.el
+++ b/websocket.el
@@ -3,8 +3,9 @@
 ;; Copyright (c) 2013, 2016-2017  Free Software Foundation, Inc.
 
 ;; Author: Andrew Hyatt <ahyatt@gmail.com>
+;; Homepage: https://github.com/ahyatt/emacs-websocket
 ;; Keywords: Communication, Websocket, Server
-;; Version: 1.12
+;; Version: 1.13
 ;; Package-Requires: ((cl-lib "0.5"))
 ;;
 ;; This program is free software; you can redistribute it and/or
@@ -556,7 +557,6 @@ the `websocket-error' condition."
   (websocket-debug websocket "Sending frame, opcode: %s payload: %s"
                    (websocket-frame-opcode frame)
                    (websocket-frame-payload frame))
-  (websocket-ensure-connected websocket)
   (unless (websocket-openp websocket)
     (signal 'websocket-closed (list frame)))
   (process-send-string (websocket-conn websocket)
@@ -580,21 +580,6 @@ the `websocket-error' condition."
     (setf (websocket-ready-state websocket) 'closed))
   (delete-process (websocket-conn websocket)))
 
-(defun websocket-ensure-connected (websocket)
-  "If the WEBSOCKET connection is closed, open it."
-  (unless (and (websocket-conn websocket)
-               (cl-ecase (process-status (websocket-conn websocket))
-                 ((run open listen) t)
-                 ((stop exit signal closed connect failed nil) nil)))
-    (websocket-close websocket)
-    (websocket-open (websocket-url websocket)
-                    :protocols (websocket-protocols websocket)
-                    :extensions (websocket-extensions websocket)
-                    :on-open (websocket-on-open websocket)
-                    :on-message (websocket-on-message websocket)
-                    :on-close (websocket-on-close websocket)
-                    :on-error (websocket-on-error websocket))))
-
 ;;;;;;;;;;;;;;;;;;;;;;
 ;; Websocket client ;;
 ;;;;;;;;;;;;;;;;;;;;;;
@@ -722,7 +707,7 @@ to the websocket protocol.
      conn
      (websocket-sentinel url conn key protocols extensions custom-header-alist 
nowait))
     (set-process-query-on-exit-flag conn nil)
-    (websocket-ensure-handshake url conn key protocols extensions 
custom-header-alist)
+    (websocket-ensure-handshake url conn key protocols extensions 
custom-header-alist nowait)
     websocket))
 
 (defun websocket-sentinel (url conn key protocols extensions 
custom-header-alist nowait)
@@ -731,26 +716,26 @@ to the websocket protocol.
         (websocket-debug websocket "State change to %s" change)
         (let ((status (process-status process)))
           (when (and nowait (eq status 'open))
-            (websocket-ensure-handshake url conn key protocols extensions 
custom-header-alist))
+            (websocket-ensure-handshake url conn key protocols extensions 
custom-header-alist nowait))
 
           (when (and (member status '(closed failed exit signal))
                      (not (eq 'closed (websocket-ready-state websocket))))
             (websocket-try-callback 'websocket-on-close 'on-close 
websocket))))))
 
-(defun websocket-ensure-handshake (url conn key protocols extensions 
custom-header-alist)
+(defun websocket-ensure-handshake (url conn key protocols extensions 
custom-header-alist nowait)
   (let ((url-struct (url-generic-parse-url url))
         (websocket (process-get conn :websocket)))
     (when (and (eq 'connecting (websocket-ready-state websocket))
-               (eq 'open (process-status conn)))
-      (process-send-string conn
-                           (format "GET %s HTTP/1.1\r\n"
-                                   (let ((path (url-filename url-struct)))
-                                     (if (> (length path) 0) path "/"))))
+               (memq (process-status conn)
+                     (list 'run (if nowait 'connect 'open))))
       (websocket-debug websocket "Sending handshake, key: %s, acceptance: %s"
                        key (websocket-accept-string websocket))
       (process-send-string conn
-                           (websocket-create-headers
-                            url key protocols extensions 
custom-header-alist)))))
+                           (format "GET %s HTTP/1.1\r\n%s"
+                                   (let ((path (url-filename url-struct)))
+                                     (if (> (length path) 0) path "/"))
+                                   (websocket-create-headers
+                                    url key protocols extensions 
custom-header-alist))))))
 
 (defun websocket-process-headers (url headers)
   "On opening URL, process the HEADERS sent from the server."



reply via email to

[Prev in Thread] Current Thread [Next in Thread]