branch: externals/websocket commit 542c6fec7447119c681d2a87da90a40223a81a36 Author: Andrew Hyatt <ahy...@gmail.com> Commit: Andrew Hyatt <ahy...@gmail.com>
Add support for cookies handling for ws clients. The url-cookie package is used to both supply cookie headers, and to parse them from the server. --- websocket-test.el | 71 +++++++++++++++++++++++++++++++++++++++++-------------- websocket.el | 30 +++++++++++++++++++---- 2 files changed, 78 insertions(+), 23 deletions(-) diff --git a/websocket-test.el b/websocket-test.el index 59e5aaa79b..7068bd0672 100644 --- a/websocket-test.el +++ b/websocket-test.el @@ -198,24 +198,59 @@ "Connection: Upgrade\r\n" "Sec-WebSocket-Key: key\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")))))) - (should - (string-match - "Host: www.example.com:123\r\n" - (websocket-create-headers "ws://www.example.com:123/path" "key" nil nil)))) + (flet ((url-cookie-generate-header-lines + (host localpart secure) "")) + (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")))))) + (flet ((url-cookie-generate-header-lines + (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) + (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))))) + +(ert-deftest websocket-process-headers () + (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"))) + (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) diff --git a/websocket.el b/websocket.el index f147383eb2..075bf5f74b 100644 --- a/websocket.el +++ b/websocket.el @@ -45,6 +45,7 @@ (require 'bindat) (require 'url-parse) +(require 'url-cookie) (eval-when-compile (require 'cl)) ;;; Code: @@ -611,6 +612,11 @@ The parameter strings are of the form \"key=value\" or \"value\". EXTENSIONS can be NIL if none are in use. An example value would be '(\"deflate-stream\" . (\"mux\" \"max-channels=4\")). +Cookies that are set via `url-cookie-store' will be used during +communication with the server, and cookies received from the +server will be stored in the same cookie storage that the +`url-cookie' package uses. + Optionally you can specify ON-OPEN, ON-MESSAGE and ON-CLOSE callbacks as well. @@ -721,6 +727,14 @@ describing the problem with the frame. (websocket-debug websocket "Websocket opened") websocket)) +(defun websocket-process-headers (url headers) + "On opening URL, process the HEADERS sent from the server." + (when (string-match "Set-Cookie: \(.*\)\r\n" headers) + ;; The url-current-object is assumed to be set by + ;; url-cookie-handle-set-cookie. + (let ((url-current-object (url-generic-parse-url url))) + (url-cookie-handle-set-cookie (match-string 1 headers))))) + (defun websocket-outer-filter (websocket output) "Filter the WEBSOCKET server's OUTPUT. This will parse headers and process frames repeatedly until there @@ -739,7 +753,8 @@ connection is invalid, the connection will be closed." (condition-case err (progn (websocket-verify-response-code text) - (websocket-verify-headers websocket text)) + (websocket-verify-headers websocket text) + (websocket-process-headers (websocket-url websocket) text)) (error (websocket-close websocket) (signal (car err) (cdr err)))) @@ -870,7 +885,13 @@ connection, which should be kept in order to pass to (defun websocket-create-headers (url key protocol extensions) "Create connections headers for the given URL, KEY, PROTOCOL and EXTENSIONS. These are defined as in `websocket-open'." - (let ((parsed-url (url-generic-parse-url url))) + (let* ((parsed-url (url-generic-parse-url url)) + (host-port (if (url-port-if-non-default parsed-url) + (format "%s:%s" (url-host parsed-url) (url-port parsed-url)) + (url-host parsed-url))) + (cookie-header (url-cookie-generate-header-lines + host-port (car (url-path-and-query parsed-url)) + (equal (url-type parsed-url) "wss")))) (format (concat "Host: %s\r\n" "Upgrade: websocket\r\n" "Connection: Upgrade\r\n" @@ -893,10 +914,9 @@ These are defined as in `websocket-open'." (when (cdr ext) (mapconcat 'identity (cdr ext) "; ")))) extensions ", "))) + (when cookie-header cookie-header) "\r\n") - (if (url-port-if-non-default parsed-url) - (format "%s:%s" (url-host parsed-url) (url-port parsed-url)) - (url-host parsed-url)) + host-port key protocol)))