Permalink
Browse files

Switch from using generic errors, to using named symbols.

  • Loading branch information...
1 parent f5ab391 commit 8904556565ffebd707e140fc95b53899ac026a38 @ahyatt committed Aug 6, 2012
Showing with 130 additions and 44 deletions.
  1. +27 −16 websocket-test.el
  2. +103 −28 websocket.el
View
@@ -54,9 +54,9 @@
(let ((f (lambda () (websocket-get-bytes "\x0\x0\x0\x1\x0\x0\x0\x1" 8))))
(if websocket-test-64-bit-p
(should (equal #x100000001 (funcall f)))
- (should-error (funcall f))))
+ (should-error (funcall f) :type 'websocket-unparseable-frame)))
(should-error (websocket-get-bytes "\x0\x0\x0" 3))
- (should-error (websocket-get-bytes "\x0" 2)))
+ (should-error (websocket-get-bytes "\x0" 2) :type 'websocket-unparseable-frame))
(ert-deftest websocket-get-opcode ()
(should (equal 'text (websocket-get-opcode websocket-test-hello))))
@@ -115,8 +115,11 @@
(ert-deftest websocket-verify-response-code ()
(should (websocket-verify-response-code "HTTP/1.1 101"))
- (should-error (websocket-verify-response-code "HTTP/1.1 400"))
- (should-error (websocket-verify-response-code "HTTP/1.1 200")))
+ (should
+ (eq 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"))))))
(ert-deftest websocket-verify-headers ()
(let ((accept "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=")
@@ -142,24 +145,30 @@
(should-error
(websocket-verify-headers
ws
- (websocket-test-header-with-lines invalid-accept upgrade connection)))
+ (websocket-test-header-with-lines invalid-accept upgrade connection))
+ :type 'websocket-invalid-header)
(should-error (websocket-verify-headers
ws
- (websocket-test-header-with-lines upgrade connection)))
+ (websocket-test-header-with-lines upgrade connection))
+ :type 'websocket-invalid-header)
(should-error (websocket-verify-headers
ws
- (websocket-test-header-with-lines accept connection)))
+ (websocket-test-header-with-lines accept connection))
+ :type 'websocket-invalid-header)
(should-error (websocket-verify-headers
ws
- (websocket-test-header-with-lines accept upgrade)))
+ (websocket-test-header-with-lines accept upgrade))
+ :type 'websocket-invalid-header)
(should-error (websocket-verify-headers
ws-with-protocol
- (websocket-test-header-with-lines accept upgrade connection)))
+ (websocket-test-header-with-lines accept upgrade connection))
+ :type 'websocket-invalid-header)
(should-error
(websocket-verify-headers
ws-with-protocol
(websocket-test-header-with-lines accept upgrade connection
- "Sec-Websocket-Protocol: foo")))
+ "Sec-Websocket-Protocol: foo"))
+ :type 'websocket-invalid-header)
(should
(websocket-verify-headers
ws-with-protocol
@@ -363,9 +372,9 @@
(should (equal (list frame2 frame1) processed-frames)))
(flet ((websocket-ready-state (websocket) 'connecting)
(websocket-close (websocket)))
- (should (equal "Bad HTTP response code while opening websocket connection: 500"
- (car (cdr (should-error
- (websocket-outer-filter fake-ws "HTTP/1.1 500\r\n\r\n")))))))))
+ (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)))))))
(ert-deftest websocket-outer-filter-bad-connection ()
(let* ((on-open-calledp)
@@ -393,11 +402,13 @@
(websocket-send ws (make-websocket-frame :opcode 'ping
:completep t)))
(should-error (websocket-send ws
- (make-websocket-frame :opcode 'text )))
+ (make-websocket-frame :opcode 'text)))
(should-error (websocket-send ws
(make-websocket-frame :opcode 'close
:payload "bye!"
- :completep t)))
+ :completep t))
+ :type 'websocket-illegal-frame)
(should-error (websocket-send ws
- (make-websocket-frame :opcode :close)))))
+ (make-websocket-frame :opcode :close))
+ :type 'websocket-illegal-frame)))
View
@@ -188,17 +188,25 @@ power of 2, up to 8."
(cval (calc-eval '("(2^32 * $ + $$)") nil
(aref 32-bit-parts 0) (aref 32-bit-parts 1))))
(when (calc-eval '("$ > $$") 'pred cval most-positive-fixnum)
- (error "websocket-get-bytes: value too large to parse!"))
+ (signal 'websocket-unparseable-frame
+ "Frame value found too large to parse!"))
(string-to-number cval))
;; n is not 8
- (bindat-get-field (bindat-unpack
- `((:val
- ,(cond ((= n 1) 'u8)
- ((= n 2) 'u16)
- ((= n 4) 'u32)
- (t (error
- "websocket-get-bytes: Unknown N: %s" n)))))
- s) :val)))
+ (bindat-get-field
+ (condition-case err
+ (bindat-unpack
+ `((:val
+ ,(cond ((= n 1) 'u8)
+ ((= n 2) 'u16)
+ ((= n 4) 'u32)
+ ;; This is an error with the library,
+ ;; not a user-facing, meaningful error.
+ (t (error
+ "websocket-get-bytes: Unknown N: %s" n)))))
+ s)
+ (args-out-of-range (signal 'websocket-unparseable-frame
+ (format "Frame unexpectedly shortly: %s" s))))
+ :val)))
(defun websocket-to-bytes (val nbytes)
"Encode the integer VAL in NBYTES of data.
@@ -207,13 +215,15 @@ NBYTES much be a power of 2, up to 8."
(< val (expt 2 (* 8 nbytes))))
(and (= nbytes 8)
(calc-eval "% < 2^(8 * %%)" 'pred val nbytes)))
- (error "websocket-to-bytes: Value %d could not be expressed in %d bytes"
- val nbytes))
+ ;; not a user-facing error, this must be caused from an error in
+ ;; this library
+ (error "websocket-to-bytes: Value %d could not be expressed in %d bytes"
+ val nbytes))
(if (= nbytes 8)
(progn
(when (calc-eval "$ < 4294967296" 'pred most-positive-fixnum)
- (error "Could not send an 8-byte value on this version of emacs.
-A 64-bit version of emacs may solve your problem."))
+ (signal 'websocket-frame-too-large
+ most-positive-fixnum))
;; Need to use calc even though at this point things are manageable,
;; since some emacs cannot parse the value 4294967296, even if
;; they never evaluate it.
@@ -224,6 +234,7 @@ A 64-bit version of emacs may solve your problem."))
`((:val ,(cond ((= nbytes 1) 'u8)
((= nbytes 2) 'u16)
((= nbytes 4) 'u32)
+ ;; Library error, not system error
(t (error "websocket-to-bytes: Unknown NBYTES: %s" nbytes)))))
`((:val . ,val)))))
@@ -339,6 +350,35 @@ the frame finishes. If the frame is not completed, return NIL."
"The default error handler used to handle errors in callbacks."
(message "Error found in callback `%S': %s" type (cdr err)))
+;; Error symbols in use by the library
+(put 'websocket-unsupported-protocol 'error-conditions
+ '(error websocket-error websocket-unsupported-protocol))
+(put 'websocket-unsupported-protocol 'error-message "Unsupport websocket protocol")
+(put 'websocket-received-error-http-response 'error-conditions
+ '(error websocket-error websocket-received-error-http-response))
+(put 'websocket-received-error-http-response 'error-message
+ "Error response received from websocket server")
+(put 'websocket-invalid-header 'error-conditions
+ '(error websocket-error websocket-invalid-header))
+(put 'websocket-invalid-header 'error-message
+ "Invalid HTTP header sent")
+(put 'websocket-illegal-frame 'error-conditions
+ '(error websocket-error websocket-illegal-frame))
+(put 'websocket-illegal-frame 'error-message
+ "Cannot send illegal frame to websocket")
+(put 'websocket-closed 'error-conditions
+ '(error websocket-error websocket-closed))
+(put 'websocket-closed 'error-message
+ "Cannot send message to a closed websocket")
+(put 'websocket-unparseable-frame 'error-conditions
+ '(error websocket-error websocket-unparseable-frame))
+(put 'websocket-unparseable-frame 'error-message
+ "Received an unparseable frame")
+(put 'websocket-frame-too-large 'error-conditions
+ '(error websocket-error websocket-frame-too-large))
+(put 'websocket-frame-too-large 'error-message
+ "The frame being sent is too large for this emacs to handle")
+
(defun* websocket-open (url &key protocol extensions (on-open 'identity)
(on-message (lambda (w f))) (on-close 'identity)
(on-error 'websocket-default-error-handler))
@@ -383,7 +423,24 @@ dangerous is the debugger is quit out of. If not specified,
For each of these event handlers, the client code can store
arbitrary data in the `client-data' slot in the returned
-websocket."
+websocket.
+
+The following errors might be thrown in this method or in
+websocket processing, all of them having the error-condition
+`websocket-error' in addition to their own symbol:
+
+`websocket-unsupported-protocol': Data in the error signal is the
+protocol (such as \"wss\") that is unsupported.
+
+`websocket-received-error-http-response': Data in the error
+signal is the integer error number.
+
+`websocket-invalid-header': Data in the error is a string
+describing the invalid header received from the server.
+
+`websocket-unparseable-frame': Data in the error is a string
+describing the problem with the frame.
+"
(let* ((name (format "websocket to %s" url))
(url-struct (url-generic-parse-url url))
(key (websocket-genkey))
@@ -403,8 +460,9 @@ websocket."
:service port :nowait nil)
(condition-case-no-debug nil
(open-network-stream name buf host port :type type :nowait nil)
- (wrong-number-of-arguments (error "No wss support in this Emacs version.")))))
- (error "Unknown protocol")))
+ (wrong-number-of-arguments
+ (signal 'websocket-unsupported-protocol "wss")))))
+ (signal 'websocket-unsupported-protocol (url-type url-struct))))
(websocket (websocket-inner-create
:conn conn
:url url
@@ -490,8 +548,8 @@ A t value will be returned on success, and an error thrown
if not."
(string-match "HTTP/1.1 \\([[:digit:]]+\\)" output)
(unless (equal "101" (match-string 1 output))
- (error "Bad HTTP response code while opening websocket connection: %s"
- (match-string 1 output)))
+ (signal 'websocket-received-error-http-response
+ (string-to-int (match-string 1 output))))
t)
(defun websocket-verify-headers (websocket output)
@@ -503,22 +561,25 @@ of populating the list of server extensions to WEBSOCKET."
(concat "Sec-WebSocket-Accept: " (websocket-accept-string websocket))))
(websocket-debug websocket "Checking for accept header: %s" accept-string)
(unless (string-match (regexp-quote accept-string) output)
- (error "Incorrect handshake from websocket: is this really a websocket connection?")))
+ (signal 'websocket-invalid-header
+ "Incorrect handshake from websocket: is this really a websocket connection?")))
(let ((case-fold-search t))
(websocket-debug websocket "Checking for upgrade header")
(unless (string-match "\r\nUpgrade: websocket\r\n" output)
- (error "No 'Upgrade: websocket' header found."))
+ (signal 'websocket-invalid-header
+ "No 'Upgrade: websocket' header found"))
(websocket-debug websocket "Checking for connection header")
(unless (string-match "\r\nConnection: upgrade\r\n" output)
- (error "No 'Connection: upgrade' header found"))
- ;; TODO(ahyatt) Implement checking for extensions
+ (signal 'websocket-invalid-header
+ "No 'Connection: upgrade' header found"))
(when (websocket-protocol websocket)
(websocket-debug websocket "Checking for protocol match: %s"
(websocket-protocol websocket))
(unless (string-match
(format "\r\nSec-Websocket-Protocol: %s\r\n"
(websocket-protocol websocket)) output)
- (error "Incorrect or missing protocol returned by the server.")))
+ (signal 'websocket-invalid-header
+ "Incorrect or missing protocol returned by the server.")))
(let ((pos 0)
(extensions))
(while (and pos
@@ -534,8 +595,8 @@ of populating the list of server extensions to WEBSOCKET."
(websocket-extensions websocket)))
(add-to-list 'extra-extensions (first (split-string ext "; ?")))))
(when extra-extensions
- (error "Non-requested extensions returned by server: %s"
- extra-extensions)))
+ (signal 'websocket-invalid-header
+ (format "Non-requested extensions returned by server: %S" extra-extensions))))
(setf (websocket-server-extensions websocket) extensions)))
;; return true
t)
@@ -611,15 +672,29 @@ connection is invalid, the connection will be closed."
(defun websocket-send (websocket frame)
"To the WEBSOCKET server, send the FRAME.
-This will raise an error if the frame is illegal."
+This will raise an error if the frame is illegal.
+
+The error signaled may be of type `websocket-illegal-frame' if
+the frame is malformed in some way, also having the condition
+type of `websocket-error'. The data associated with the signal
+is the frame being sent.
+
+If the websocket is closed a signal `websocket-closed' is sent,
+also with `websocket-error' condition. The data in the signal is
+also the frame.
+
+The frame may be too large for this buid of emacs, in which case
+`websocket-frame-too-large' is returned, with the data of the
+system's `most-positive-fixnum', whose length was exceeded. This
+also has the `websocket-error' condition."
(unless (websocket-check frame)
- (error "Cannot send illegal frame to websocket"))
+ (signal 'websocket-illegal-frame frame))
(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)
- (error "No webserver process to send data to!"))
+ (signal 'websocket-closed frame))
(process-send-string (websocket-conn websocket)
(websocket-encode-frame frame)))

0 comments on commit 8904556

Please sign in to comment.