vi / websocat

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

Does not detect connection close in double-listen configuration #183

Open masto opened 1 year ago

masto commented 1 year ago

Probably easier to demonstrate than explain. If you proxy a listening websocket to a listening TCP socket:

$ websocat ws-l:127.0.0.1:5818 tcp-l:127.0.0.1:5819

and then connect to the websocket and close the connection, it won't notice. Opening another connection results in:

[INFO  websocat::net_peer] Incoming TCP connection from Some(127.0.0.1:53318)
[INFO  websocat::sessionserve] Serving 1 ongoing connections
[INFO  websocat::ws_server_peer] Incoming connection to websocket: /
[INFO  websocat::ws_server_peer] Upgraded
<<<at this point, the first connection is terminated, but websocat doesn't notice>>>
[INFO  websocat::net_peer] Incoming TCP connection from Some(127.0.0.1:39270)
[INFO  websocat::sessionserve] Serving 2 ongoing connections
[INFO  websocat::ws_server_peer] Incoming connection to websocket: /
[INFO  websocat::ws_server_peer] Upgraded
websocat: Address in use (os error 98)

If you connect to the TCP socket, it then immediately prints

[INFO  websocat::ws_peer] Received WebSocket close message
[INFO  websocat::sessionserve] Forward finished

This behavior doesn't occur if, for example, it's in plain server (-s) mode, or using tcp:, literalreply:, etc. It seems specific to proxying to a listening port with nothing connected.

Expected behavior is that when the websocket connection is closed by the client, the websocat server should close the other side and become ready to serve again.

vi commented 1 year ago

Specifying listeners on both parts is typically not a good idea. What do you want to do?

Some things that may help:

  1. Specifying -E option to terminate connections more aggressively;
  2. Using reuse-raw: overlay (or maybe reuse-broadcast:, depending on use case);
  3. Using autoreconnect: (especially with some form of connection reuser).

Command line that works reasonably for me:

websocat --text -E ws-l:127.0.0.1:5818 reuse-raw:autoreconnect:tcp-l:127.0.0.1:5819

Note that port 5819 only starts being listened after the first WebSocket clients gets connected.

masto commented 1 year ago

Thanks very much for those overlays. I'd tried -E before with no luck, but using the options you suggested together seems to do the trick.

What I'm doing is, specifically, https://nabu.run (https://youtu.be/zabJhYvoI1k). It may not be a good idea to listen on both parts, but it is exactly what I need. Being able to do that with websocat makes my application possible, and saves me having to write my own proxy.

The situation I was trying to avoid was that if you clicked the "Connect WebSocket" button and then closed it or reloaded the page, it couldn't be used again.

Not sure if there's still a bug worth fixing, but I feel like I have a reasonable workaround now.

vi commented 1 year ago

If you want to listen WebSocket from both sides then maybe you want to use wsbroad as a component.

Websocat may still be needed to turn tcp-l:127.0.0.1:5819 into a connection to wsbroad, but overall it may be more flexible.

reuse-raw may correspond to wsbroad's --backpressure[-with-errors] option.