branch: externals/websocket
commit f8b00eb017ec8ba0e6574425e4325ea3ab802469
Author: Andrew Hyatt <[email protected]>
Commit: Andrew Hyatt <[email protected]>

    Add tests, update copyright
---
 websocket-test.el | 166 ++++++++++++++++++++++++++++++------------------------
 websocket.el      | 152 ++++++++++++++++++++++++-------------------------
 2 files changed, 167 insertions(+), 151 deletions(-)

diff --git a/websocket-test.el b/websocket-test.el
index c133272662..0613e22b01 100644
--- a/websocket-test.el
+++ b/websocket-test.el
@@ -1,6 +1,6 @@
-;;; websocket-test.el --- Unit tests for the websocket layer
+;;; websocket-test.el --- Unit tests for the websocket layer  -*- 
lexical-binding:t -*-
 
-;; Copyright (c) 2013  Free Software Foundation, Inc.
+;; Copyright (c) 2013, 2026  Free Software Foundation, Inc.
 ;;
 ;; Author: Andrew Hyatt <ahyatt at gmail dot com>
 ;; Maintainer: Andrew Hyatt <ahyatt at gmail dot com>
@@ -24,6 +24,7 @@
 
 (require 'ert)
 (require 'websocket)
+(require 's)
 (eval-when-compile (require 'cl))
 
 (ert-deftest websocket-genbytes-length ()
@@ -266,12 +267,12 @@
 
 (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")))))
+              (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"
@@ -299,8 +300,8 @@
                "hello"
                (progn
                  (funcall (websocket-process-frame
-                   websocket
-                   (make-websocket-frame :opcode opcode :payload "hello")))
+                           websocket
+                           (make-websocket-frame :opcode opcode :payload 
"hello")))
                  processed))))
     (setq sent nil)
     (cl-letf (((symbol-function 'websocket-send)
@@ -357,9 +358,9 @@
   ;; We've tested websocket-read-frame, now we can use that to help
   ;; test websocket-encode-frame.
   (should (equal
-             websocket-test-hello
-             (websocket-encode-frame
-              (make-websocket-frame :opcode 'text :payload "Hello" :completep 
t) nil)))
+           websocket-test-hello
+           (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
@@ -371,9 +372,9 @@
   (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))))
+                   (websocket-encode-frame
+                    (make-websocket-frame :opcode 'text :payload "Hello"
+                                          :completep t) t))))
   (should-not
    (websocket-frame-completep
     (websocket-read-frame
@@ -382,14 +383,14 @@
                                                    :completep nil) t))))
   (should (equal 'close (websocket-frame-opcode
                          (websocket-read-frame
-                           (websocket-encode-frame
-                            (make-websocket-frame :opcode 'close :completep t) 
t)))))
+                          (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))))
+                       (websocket-encode-frame
+                        (make-websocket-frame :opcode opcode
+                                              :payload "data"
+                                              :completep t) t))))
       (should read-frame)
       (should (equal
                opcode
@@ -538,7 +539,7 @@
               ((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)))
+                                               :completep t)))
     (should-error (websocket-send ws
                                   (make-websocket-frame :opcode 'text)))
     (should-error (websocket-send ws
@@ -550,6 +551,21 @@
                                   (make-websocket-frame :opcode :close))
                   :type 'websocket-illegal-frame)))
 
+(ert-deftest websocket-ensure-handshake ()
+  (let ((sent-string nil))
+    (cl-letf (((symbol-function 'process-send-string)
+               (lambda (proc string) (setq sent-string string)))
+              ((symbol-function 'process-get)
+               (lambda (proc sym)
+                 (websocket-inner-create
+                  :conn t :url t :accept-string "key")))
+              ((symbol-function 'process-status)
+               (lambda (proc) 'run)))
+      (websocket-ensure-handshake "ws://example.com?query=1"
+                                  'conn 'key nil
+                                  nil nil nil)
+      (should (s-starts-with-p "GET /?query=1 HTTP/1.1\r\n" sent-string)))))
+
 (ert-deftest websocket-verify-client-headers ()
   (let* ((http "HTTP/1.1")
          (host "Host: authority")
@@ -567,10 +583,10 @@
               (mapconcat 'identity (append (list http "" protocol extensions1 
extensions2)
                                            all-required-headers) "\r\n"))))
     (should (websocket-verify-client-headers
-              (mapconcat 'identity
-                         (mapcar 'upcase
-                                 (append (list http "" protocol extensions1 
extensions2)
-                                         all-required-headers)) "\r\n")))
+             (mapconcat 'identity
+                        (mapcar 'upcase
+                                (append (list http "" protocol extensions1 
extensions2)
+                                        all-required-headers)) "\r\n")))
     (dolist (header all-required-headers)
       (should-not (websocket-verify-client-headers
                    (mapconcat 'identity (append (list http "")
@@ -620,34 +636,34 @@
     (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
+      ;; Bad request, in two parts
       (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")
-       (should (equal response "HTTP/1.1 400 Bad Request\r\n\r\n"))
-       (should-not (websocket-inflight-input ws)))
-    ;; Good request, followed by packet
-     (setq closed nil
-           response nil)
-     (setf (websocket-inflight-input ws) nil)
-     (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))
-       (should-not closed)
-       (should (equal response "response"))
-       (should processed)))))
+        (websocket-server-filter nil "HTTP/1.0 GET /foo \r\n")
+        (should-not closed)
+        (websocket-server-filter nil "\r\n")
+        (should (equal response "HTTP/1.1 400 Bad Request\r\n\r\n"))
+        (should-not (websocket-inflight-input ws)))
+      ;; Good request, followed by packet
+      (setq closed nil
+            response nil)
+      (setf (websocket-inflight-input ws) nil)
+      (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))
+        (should-not closed)
+        (should (equal response "response"))
+        (should processed)))))
 
 (ert-deftest websocket-complete-server-response-test ()
   ;; Example taken from RFC
@@ -659,24 +675,24 @@
                    "Sec-WebSocket-Protocol: chat\r\n\r\n"
                    )
            (let ((header-info
-                          (websocket-verify-client-headers
-                           (concat "GET /chat HTTP/1.1\r\n"
-                                   "Host: server.example.com\r\n"
-                                   "Upgrade: websocket\r\n"
-                                   "Connection: Upgrade\r\n"
-                                   "Sec-WebSocket-Key: 
dGhlIHNhbXBsZSBub25jZQ==\r\n"
-                                   "Sec-WebSocket-Protocol: chat, 
superchat\r\n"
-                                   "Sec-WebSocket-Version: 13\r\n"))))
-                     (should header-info)
-                     (let ((ws (websocket-inner-create
-                                :conn t :url t
-                                :accept-string (websocket-calculate-accept
-                                                (plist-get header-info :key))
-                                :protocols '("chat"))))
-                       (websocket-get-server-response
-                        ws
-                        (plist-get header-info :protocols)
-                        (plist-get header-info :extension)))))))
+                  (websocket-verify-client-headers
+                   (concat "GET /chat HTTP/1.1\r\n"
+                           "Host: server.example.com\r\n"
+                           "Upgrade: websocket\r\n"
+                           "Connection: Upgrade\r\n"
+                           "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n"
+                           "Sec-WebSocket-Protocol: chat, superchat\r\n"
+                           "Sec-WebSocket-Version: 13\r\n"))))
+             (should header-info)
+             (let ((ws (websocket-inner-create
+                        :conn t :url t
+                        :accept-string (websocket-calculate-accept
+                                        (plist-get header-info :key))
+                        :protocols '("chat"))))
+               (websocket-get-server-response
+                ws
+                (plist-get header-info :protocols)
+                (plist-get header-info :extension)))))))
 
 (ert-deftest websocket-server-close ()
   (let ((websocket-server-websockets
@@ -712,10 +728,10 @@
   (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))))
+                           (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)))))
diff --git a/websocket.el b/websocket.el
index 9dc59a9d1c..2669c9c192 100644
--- a/websocket.el
+++ b/websocket.el
@@ -1,6 +1,6 @@
 ;;; websocket.el --- Emacs WebSocket client and server  -*- lexical-binding:t 
-*-
 
-;; Copyright (c) 2013, 2016-2023  Free Software Foundation, Inc.
+;; Copyright (c) 2013, 2016-2023, 2026  Free Software Foundation, Inc.
 
 ;; Author: Andrew Hyatt <[email protected]>
 ;; Homepage: https://github.com/ahyatt/emacs-websocket
@@ -53,8 +53,8 @@
 ;;; Code:
 
 (cl-defstruct (websocket
-            (:constructor nil)
-            (:constructor websocket-inner-create))
+               (:constructor nil)
+               (:constructor websocket-inner-create))
   "A websocket structure.
 This follows the W3C Websocket API, except translated to elisp
 idioms.  The API is implemented in both the websocket struct and
@@ -320,25 +320,25 @@ We mask the frame or not, depending on SHOULD-MASK."
                                          (`ping         9)
                                          (`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)))))
+                              (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)))))
@@ -437,7 +437,7 @@ ERR should be a cons of error symbol and error data."
 (defun websocket-get-debug-buffer-create (websocket)
   "Get or create the buffer corresponding to WEBSOCKET."
   (let ((buf (get-buffer-create (format "*websocket %s debug*"
-                                    (websocket-url websocket)))))
+                                        (websocket-url websocket)))))
     (when (= 0 (buffer-size buf))
       (buffer-disable-undo buf))
     buf))
@@ -488,13 +488,13 @@ has connection termination."
   (let ((opcode (websocket-frame-opcode frame)))
     (cond ((memq opcode '(continuation text binary))
            (lambda () (websocket-try-callback 'websocket-on-message 'on-message
-                                         websocket frame)))
+                                              websocket frame)))
           ((eq opcode 'ping)
            (lambda () (websocket-send websocket
-                                 (make-websocket-frame
-                                  :opcode 'pong
-                                  :payload (websocket-frame-payload frame)
-                                  :completep t))))
+                                      (make-websocket-frame
+                                       :opcode 'pong
+                                       :payload (websocket-frame-payload frame)
+                                       :completep t))))
           ((eq opcode 'close)
            (lambda () (delete-process (websocket-conn websocket))))
           (t (lambda ())))))
@@ -953,30 +953,30 @@ All these parameters are defined as in `websocket-open'."
 (defun websocket-get-server-response (websocket client-protocols 
client-extensions)
   "Get the websocket response from client WEBSOCKET."
   (let ((separator "\r\n"))
-      (concat "HTTP/1.1 101 Switching Protocols" separator
-              "Upgrade: websocket" separator
-              "Connection: Upgrade" separator
-              "Sec-WebSocket-Accept: "
-              (websocket-accept-string websocket) separator
-              (let ((protocols
-                         (websocket-intersect client-protocols
-                                              (websocket-protocols 
websocket))))
-                    (when protocols
-                      (concat
-                       (mapconcat
-                        (lambda (protocol) (format "Sec-WebSocket-Protocol: %s"
-                                              protocol)) protocols separator)
-                       separator)))
-              (let ((extensions (websocket-intersect
-                                   client-extensions
-                                   (websocket-extensions websocket))))
-                  (when extensions
-                    (concat
-                     (mapconcat
-                      (lambda (extension) (format "Sec-Websocket-Extensions: 
%s"
-                                             extension)) extensions separator)
-                     separator)))
-              separator)))
+    (concat "HTTP/1.1 101 Switching Protocols" separator
+            "Upgrade: websocket" separator
+            "Connection: Upgrade" separator
+            "Sec-WebSocket-Accept: "
+            (websocket-accept-string websocket) separator
+            (let ((protocols
+                   (websocket-intersect client-protocols
+                                        (websocket-protocols websocket))))
+              (when protocols
+                (concat
+                 (mapconcat
+                  (lambda (protocol) (format "Sec-WebSocket-Protocol: %s"
+                                             protocol)) protocols separator)
+                 separator)))
+            (let ((extensions (websocket-intersect
+                               client-extensions
+                               (websocket-extensions websocket))))
+              (when extensions
+                (concat
+                 (mapconcat
+                  (lambda (extension) (format "Sec-Websocket-Extensions: %s"
+                                              extension)) extensions separator)
+                 separator)))
+            separator)))
 
 (defun websocket-server-filter (process output)
   "This acts on all OUTPUT from websocket clients PROCESS."
@@ -988,30 +988,30 @@ All these parameters are defined as in `websocket-open'."
            (let ((end-of-header-pos
                   (let ((pos (string-match "\r\n\r\n" text)))
                     (when pos (+ 4 pos)))))
-               (if end-of-header-pos
-                   (progn
-                     (let ((header-info (websocket-verify-client-headers 
text)))
-                       (if header-info
-                           (progn (setf (websocket-accept-string ws)
-                                        (websocket-calculate-accept
-                                         (plist-get header-info :key)))
-                                  (process-send-string
-                                   process
-                                   (websocket-get-server-response
-                                    ws (plist-get header-info :protocols)
-                                    (plist-get header-info :extensions)))
-                                  (setf (websocket-ready-state ws) 'open)
-                                  (setf (websocket-origin ws) (plist-get 
header-info :origin))
-                                  (websocket-try-callback 'websocket-on-open
-                                                          'on-open ws))
-                         (message "Invalid client headers found in: %s" output)
-                         (process-send-string process "HTTP/1.1 400 Bad 
Request\r\n\r\n")
-                         (websocket-close ws)))
-                     (when (> (length text) (+ 1 end-of-header-pos))
-                       (websocket-server-filter process (substring
-                                                           text
-                                                           
end-of-header-pos))))
-                 (setf (websocket-inflight-input ws) text))))
+             (if end-of-header-pos
+                 (progn
+                   (let ((header-info (websocket-verify-client-headers text)))
+                     (if header-info
+                         (progn (setf (websocket-accept-string ws)
+                                      (websocket-calculate-accept
+                                       (plist-get header-info :key)))
+                                (process-send-string
+                                 process
+                                 (websocket-get-server-response
+                                  ws (plist-get header-info :protocols)
+                                  (plist-get header-info :extensions)))
+                                (setf (websocket-ready-state ws) 'open)
+                                (setf (websocket-origin ws) (plist-get 
header-info :origin))
+                                (websocket-try-callback 'websocket-on-open
+                                                        'on-open ws))
+                       (message "Invalid client headers found in: %s" output)
+                       (process-send-string process "HTTP/1.1 400 Bad 
Request\r\n\r\n")
+                       (websocket-close ws)))
+                   (when (> (length text) (+ 1 end-of-header-pos))
+                     (websocket-server-filter process (substring
+                                                       text
+                                                       end-of-header-pos))))
+               (setf (websocket-inflight-input ws) text))))
           ((eq (websocket-ready-state ws) 'open)
            (websocket-process-input-on-open-ws ws text))
           ((eq (websocket-ready-state ws) 'closed)

Reply via email to