fukamachi / websocket-driver

WebSocket server/client implementation for Common Lisp
BSD 2-Clause "Simplified" License
103 stars 27 forks source link

server does not emit `:close` or `:error` when client disconnects without close frame #62

Open samuel-hunter opened 2 years ago

samuel-hunter commented 2 years ago

Reproduced on SBCL 2.2.5 on Debian, with websocket-driver v0.2.0, Clack v2.0.0, and Woo v0.12.0.

If a wss server socket closes without writing a close frame, I'd assume it would emit either an :error event, or a :close event, but it does neither.

I can reproduce the error by modifying the *echo-server* in the README:

;; Grabbing the latest websocket for debugging
(defvar *latest-ws*)

(defparameter *echo-server*
  (lambda (env)
    (let ((ws (wsd:make-server env)))
      (setf *latest-ws* ws)
      (wsd:on :message ws
              (lambda (message)
                (format *error-output* "Message Event [~S]~%" message)
                (wsd:send ws message)))
      (wsd:on :open ws
              (lambda ()
                (format *error-output* "Open Event~%")""))
      (wsd:on :close ws
              (lambda (&key code reason)
                (format *error-output* "Close Event [~D, ~S]~%" code reason)))
      (wsd:on :error ws
              (lambda (error)
                (format *error-output* "Error Event [~S]~%" error)))
      (lambda (responder)
        (declare (ignore responder))
        (wsd:start-connection ws)))))

(defvar myhandler (clack:clackup *echo-server* :server :woo :port 5000))

Client-side, I can reproduce it with netcat, with frames only, or webcat, with some content:

$ # Netcat way:
$ cat > header.txt <<EOF
GET / HTTP/1.1
Host: localhost:5000
Connection: Upgrade
Upgrade: websocket
Sec-WebSocket-Version: 13
Sec-WebSocket-Key: 1dqo3Zm0dEN93Nd7ijBz3g==

EOF
$ netcat -C localhost 5000 < text.txt & sleep 0.1; kill $!

Server-side results:

Open Event

(No mention of a Close Event or Error Event)

Similar results with webcat

$ echo hello | webcat ws://localhost:5000 & sleep 0.1; kill $!

Server-side results:

Open Event
Message Event ["hello"]

As an aside, the server socket is indeed registered as closed once the client quits:

CL-USER> (slot-value *latest-ws* 'wsd::socket)
#S(WOO.EV.SOCKET:SOCKET
   :WATCHERS #(#.(SB-SYS:INT-SAP #X7F1D70001AF0)
               #.(SB-SYS:INT-SAP #X7F1D70001AB0)
               #.(SB-SYS:INT-SAP #X7F1D70001A70))
   :LAST-ACTIVITY 1.6574218737941217d9
   :FD 11
   :REMOTE-ADDR "127.0.0.1"
   :REMOTE-PORT 32456
   :DATA NIL
   :TCP-READ-CB WOO.EV.TCP::TCP-READ-CB
   :READ-CB NIL
   :WRITE-CB NIL
   :OPEN-P NIL
   :BUFFER NIL
   :SENDFILE-FD NIL
   :SENDFILE-SIZE NIL
   :SENDFILE-OFFSET 0)
CL-USER> (slot-value (slot-value *latest-ws* 'wsd::socket) 'woo.ev.socket::open-p)
NIL

Unless I am mistaken, I expect that I need to only handle :close and :error if I want to clean up state following a closed client connection (for example, wrapping up a multiplayer game session after one player leaves). If there is no easy way to detect when this happens, this could lead into a footgun into adding a DOS vulnerability, where the attacker would rapid-fire requests before closing them, tricking the server into remembering state for connections that are no longer open.

Please let me know if I am mistaken about this. Thank you!

samuel-hunter commented 2 years ago

I'm poking around in the source code, trying to find a place where the program can catch when the socket disconnects and can emit a :close event with code 1006 (connection abnormally closed). Here is what I found:

Both of these footguns should be solved by calling (close-connection ws 1006 "websocket connection closed") after the detecting the socket is closed by peer.

byulparan commented 1 year ago

ha....can you find solution? when close connection, server not call close or error handler in woo server :-<

samuel-hunter commented 1 year ago

I've not found a solution unfortunately, so I've put my server dev work on hold until it's solved

ghost commented 5 months ago

I had this happen when I was testing the ws through the terminal with websocat and closing the connection there. It gave this error: Error while processing connection: Condition USOCKET:CONNECTION-ABORTED-ERROR was signalled.

So websocket-driver is not reacting on this event from usocket.

I can see this as a problem when a client's connection drops and a close frame does not reach the server.