vi / websocat

Command-line client for WebSockets, like netcat (or curl) for ws:// with advanced socat-like functions
MIT License
6.74k stars 261 forks source link

websocat doesn’t shut down when the server shuts down immediately #63

Closed FGasper closed 4 years ago

FGasper commented 4 years ago

I’ve got an application where the server will close the WS session immediately after the handshake with 1008 (policy violation) depending on certain query string parameters in the URL.

When this happens, websocat hangs. The last few lines of debug output are:

[INFO  websocat::ws_client_peer] Connected to ws
[DEBUG websocat::ws_peer] incoming close
[DEBUG websocat::my_copy] BrokenPipe: read_done
[DEBUG websocat::my_copy] done
[INFO  websocat::sessionserve] Reverse finished
[INFO  websocat::sessionserve] Reverse shutdown finished

If I hit CTRL-D, then websocat will end the connection.

What may be going on is that websocat reacts badly to the server’s having already closed the TCP connection … which I’m guessing is what prompts the BrokenPipe. While this may be counter to the WS specification, websocat ought not to hang in response to it.

wscat2 (https://www.npmjs.com/package/wscat2) handles this case correctly.

vi commented 4 years ago

Does it still happen if you also specify -E (--exit-on-eof) command-line parameter?

Unfortunately, current Websocat tends to ignore incoming RST, FIN or WebSocket close messages.

FGasper commented 4 years ago

Ah, yes. Sorry, I didn’t see that flag and thought that would be default behavior.

Thank you!

FGasper commented 4 years ago

FWIW I’m unclear about what precisely that flag is meant to do differently. I find that EOF on STDIN closes the connection even when I don’t give that flag. So as far as I can tell, the only thing this flag changes is to make websocat terminate when the remote initiates the shutdown … which seems like it would be always desirable … ?

vi commented 4 years ago

Default behaviour for interactive/simple (one-argument) mode may indeed be changed. This probably needs splitting -E to two options ("stop session when stdin/lister_part is closed" and "stop session when WebSocket connection / connecting_part is closed").

EOF on stdin may not actually close the connection immediately without -E if server is slugglish to reply with the close event.

For example, echo make_request | websocat ws://server/url > output.txt may fail (produce empty output) if -E is specified, as connection would get closed before any reply has chance to be received.

haolian9 commented 2 years ago

according to the rfc

If an endpoint receives a Close frame and did not previously send a Close frame, the endpoint MUST send a Close frame in response.

it seems websocat did not send a close frame response when the server sent a close frame first (i did not dig into the websocat's source code, instead, i found the missing in the server's log) @vi

$ websocat ws://127.0.0.1:9003 -v -E
[INFO  websocat::lints] Auto-inserting the line mode
[INFO  websocat::stdio_threaded_peer] get_stdio_peer (threaded)
[INFO  websocat::ws_client_peer] get_ws_client_peer
[INFO  websocat::ws_client_peer] Connected to ws
hi
[INFO  websocat::ws_peer] Received WebSocket close message
[INFO  websocat::sessionserve] Reverse finished
[INFO  websocat::sessionserve] One of directions finished

i also tried same operations in firefox's console new WebSocet ... which do have sent a close frame back to the server.

vi commented 2 years ago

Looks like a peculiarity of the current implementation of -E option. Direction gets aborted abruptly, including its shutdown procedure. It may make sense to at least try sending Close frame (without waiting if it cannot be done immediately), but such unreliable behaviour can also surprise users.

allburov commented 2 weeks ago

that would be default behavior.

omg, that's true. I had to check in Postman to make sure the server does send the close event.