vi / websocat

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

Infinite loop with autoreconnect:reuse #186

Open josh-endries opened 1 year ago

josh-endries commented 1 year ago

I'm not sure if this is intended and I'm just using it wrong, but I ran into what seems like an infinite loop bug due to overlay order (hopefully that's the right terminology, this is my first time using websocat).

Repro:

  1. Run a server in one shell on port 12345
  2. In another "proxy" shell I run:
websocat -u -t ws-l:127.0.0.1:12346 autoreconnect:reuse:ws://127.0.0.1:12345
  1. In a third shell I dump some text or a file into a client connecting to port 12346. This works as expected and the events come through to the server.
  2. Abort the server process and restart it. The proxy process logs a connection error and doesn't stop (as expected).
  3. Send some more text via the client. The proxy shell will continually spin, seemingly forever, outputting the following:
    [WARN  websocat::reconnect_peer] WebSocketError: I/O failure
    [WARN  websocat::reconnect_peer] WebSocketError: I/O failure
    [WARN  websocat::reconnect_peer] WebSocketError: I/O failure

    It never reconnects.

However, if I run the proxy this way, with reuse and autoreconnect flipped, it works mostly as expected:

websocat -u -t ws-l:127.0.0.1:12346 reuse:autoreconnect:ws://127.0.0.1:12345

After aborting and restarting the server, the client's events usually get through. Sometimes the first batch of lines I send don't make it (or partially). I suspect this loop is because the overlays just decorate each other, so maybe it makes sense that this fails, but I'm not entirely sure if I should expect the first case to fail so I wrote this up.

I wish there was a way, from a client perspective, to know if events made it or not, so I could know to retry sending them. For example, setting an exit code if it can't connect, but this might be tough with buffering.

My ultimate use case is periodically (e.g. every second or via tail) reading a file, filtering lines, and dumping results to a (real) server. I'd rather not reconnect every second so I was experimenting with reuse/reconnect to act as a pseudo-persistent connection.

vi commented 1 year ago

Indeed, autoreconnect:reuse: does not make much sense. Maybe I'll implement a lint that shows a warning when trying to use such combination.

vi commented 1 year ago

My ultimate use case is periodically (e.g. every second or via tail) reading a file, filtering lines, and dumping results to a (real) server. I'd rather not reconnect every second so I was experimenting with reuse/reconnect to act as a pseudo-persistent connection.

Something like

tail -f some_file.txt | websocat -ut - ws://127.0.0.1:12345

? Why do you need reuse:?

Also note that reuse: have two modes: reuse-raw: and broadcast: (the default). They have different buffering behaviour and sometimes single client mode (raw) works better.

josh-endries commented 1 year ago

I was originally thinking of sending frequent batches via cron, or a script loop, and reuse seemed like a way to try to keep connections open instead of opening a new one for every batch. With a streaming approach it's not needed.