From 194f93884da7c0c03172fe27fff992f3c538eeae Mon Sep 17 00:00:00 2001 From: "Jan (janneke) Nieuwenhuizen" Date: Sun, 8 Nov 2020 08:30:39 +0100 Subject: [PATCH] websocket: Gracefully handle read errors and socket close. * 8sync/systems/websocket/client.scm (websocket-socket-open?): New method. (websocket-loop): Use it to gracefully handle errors socket close. --- 8sync/systems/websocket/client.scm | 90 ++++++++++++++++++------------ 1 file changed, 54 insertions(+), 36 deletions(-) diff --git a/8sync/systems/websocket/client.scm b/8sync/systems/websocket/client.scm index a637384..4986672 100644 --- a/8sync/systems/websocket/client.scm +++ b/8sync/systems/websocket/client.scm @@ -150,6 +150,10 @@ (define-method (websocket-init (websocket ) message) (and=> (.url websocket) (cut websocket-open websocket message <>))) +(define-method (websocket-socket-open? (websocket )) + "Return #t if .SOCKET of WEBSOCKET is open." + (not (port-closed? (.socket websocket)))) + (define-method (websocket-loop (websocket ) message) (define (handle-data-frame type data) @@ -170,42 +174,56 @@ (let loop ((fragments '()) (type #f)) - (let* ((socket (.socket websocket)) - (frame (and (websocket-open? websocket) - (read-frame-maybe)))) - (cond - ;; EOF - port is closed. - ;; @@: Sometimes the eof object appears here as opposed to - ;; at lookahead, but I'm not sure why - ((or (not frame) (eof-object? frame)) - (close-down)) - ;; Per section 5.4, control frames may appear interspersed - ;; along with a fragmented message. - ((close-frame? frame) - ;; Per section 5.5.1, echo the close frame back to the - ;; socket before closing the socket. The socket may no - ;; longer be listening. - (false-if-exception - (write-frame (make-close-frame (frame-data frame)) socket)) - (close-down)) - ((ping-frame? frame) - ;; Per section 5.5.3, a pong frame must include the exact - ;; same data as the ping frame. - (write-frame (make-pong-frame (frame-data frame)) socket) - (loop fragments type)) - ((pong-frame? frame) ; silently ignore pongs - (loop fragments type)) - ((first-fragment-frame? frame) ; begin accumulating fragments - (loop (list frame) (frame-type frame))) - ((final-fragment-frame? frame) ; concatenate all fragments - (handle-data-frame type (frame-concatenate - (reverse (cons frame fragments)))) - (loop '() #f)) - ((fragment-frame? frame) ; add a fragment - (loop (cons frame fragments) type)) - ((data-frame? frame) ; unfragmented data frame - (handle-data-frame (frame-type frame) (frame-data frame)) - (loop '() #f)))))) + (catch #t ;expect: wrong-type-arg (open port), system-error + (lambda _ + (let* ((socket (.socket websocket)) + (frame (and (websocket-open? websocket) + (read-frame-maybe)))) + (cond + ;; EOF - port is closed. + ;; @@: Sometimes the eof object appears here as opposed to + ;; at lookahead, but I'm not sure why + ((or (not frame) (eof-object? frame)) + (close-down)) + ;; Per section 5.4, control frames may appear interspersed + ;; along with a fragmented message. + ((close-frame? frame) + ;; Per section 5.5.1, echo the close frame back to the + ;; socket before closing the socket. The socket may no + ;; longer be listening. + (false-if-exception + (write-frame (make-close-frame (frame-data frame)) socket)) + (close-down)) + ((ping-frame? frame) + ;; Per section 5.5.3, a pong frame must include the exact + ;; same data as the ping frame. + (write-frame (make-pong-frame (frame-data frame)) socket) + (loop fragments type)) + ((pong-frame? frame) ; silently ignore pongs + (loop fragments type)) + ((first-fragment-frame? frame) ; begin accumulating fragments + (loop (list frame) (frame-type frame))) + ((final-fragment-frame? frame) ; concatenate all fragments + (handle-data-frame type (frame-concatenate + (reverse (cons frame fragments)))) + (loop '() #f)) + ((fragment-frame? frame) ; add a fragment + (loop (cons frame fragments) type)) + ((data-frame? frame) ; unfragmented data frame + (handle-data-frame (frame-type frame) (frame-data frame)) + (loop '() #f))))) + (lambda (key . args) + (unless (and + (memq key '(system-error wrong-type-arg)) + (match args + (((or "get-u8" "get-bytevector-n" "lookahead-u8") arg ...) + #t) + (_ #f))) + (apply throw key args)) + (let ((message (format #f "~a: ~s" key args))) + ((.on-error websocket) websocket (format #f "read failed: ~s\n" websocket)) + (if (websocket-socket-open? websocket) (loop '() #f) + (websocket-close websocket message))))))) ;; See Section 3 - WebSocket URIs (define (encrypted-websocket-scheme? uri) -- 2.31.1