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

Receiver flow control #50

Open qm2k opened 5 years ago

qm2k commented 5 years ago

I'm trying to transfer potentially infinite stream from server to client (browser). When the client does not consume messages fast enough some get lost with the [WARN websocat::broadcast_reuse_peer] A client's sink is NotReady for start_send message in websocat stderr. I'd like the websocat in this situation to block until send buffer clears, is it possible? Since TCP already has flow control I'd prefer not to reproduce it on the application level, potentially ruining performance.

(Also, the warning is misleading, should be fatal error IMO since partial stream loss causes either protocol breakage or unintended operation.)

vi commented 5 years ago

What is your command line?

Maybe you want non-broadcast connection reuser? It should not drop messages, but provide backpressure instead.

$ websocat -E -b ws-l:127.0.0.1:1234 reuse-raw:sh-c:"cat /dev/zero | pv -N server"
% websocat -b ws://127.0.0.1:1234/ | pv -L 500k -N client > /dev/null

   server: 17.2MiB 0:00:16 [ 884KiB/s] [              <=> ]
   client: 7.81MiB 0:00:16 [ 503KiB/s] [    <=>           ]                  

(speed on server jumps to and fro, but buffered amount stays around 10 MB)

But in this mode if there is more than one connected client then undefined client gets each message. It is not regular round-robin random, more just who get lucky begin the first to be polled / who was used last gets the message. Also if no clients are connected, server gets blocked until there is a client.

If you need only one simultaneous connection, you can use --conncap 1.

vi commented 5 years ago

(Also, the warning is misleading, should be fatal error IMO since partial stream loss causes either protocol breakage or unintended operation.)

Whether it is a fatal error depends on usage scenario. Most of my own programs use websocat in broadcast drop-on-tardiness mode and deliver status updates, with non-recent statuses just being non-relevant and missing updates just fixable with another update. Blocking event source is not a good idea in this case.

Do you want additional connection reuser mode where server can get stuck forever if a poisonous (not reading it's socket) client gets connected?

qm2k commented 5 years ago

My command line was: myprogram | websocat --binary --exit-on-eof --server-mode 0.0.0.0:1234 I didn't intentionally enable broadcast mode, but I wanted it to bind to all addresses, not just localhost. Is it possible using the syntax I used?

The one you offered: websocat -E -b ws-l:0.0.0.0:1234 reuse-raw:sh-c:myprogram essentially works as I want, but I have no idea what it all means. Could you please explain how ws-l is different from --server-mode and what the reuse-raw:sh-c incantation is for? Also, i'd prefer to continue using pipes, is it possible?

non-recent statuses just being non-relevant and missing updates just fixable with another update

But how can you prevent partial update from being sent? Unless your messages are small and rare this is always a possibility.

Do you want additional connection reuser mode where server can get stuck forever if a poisonous (not reading it's socket) client gets connected?

Your broadcast mode is interesting, but my binary protocol does not let client to re-sync mid-stream so I cannot start using it right away. IMO server should just drop non-reading clients, but this is irrelevant to me now. Right now I'd prefer spawning separate process for each client, is it possible?

vi commented 5 years ago

but I have no idea what it all means

Generally Websocat has two main modes of invocation: simple (one-argument) and complex (two-argument). Simple mode is aimed to quick interactive tests, complex mode is aimed to be incorporated into scripts.

websocat --binary --exit-on-eof --server-mode 0.0.0.0:1234 is actually equivalent to websocat --binary --exit-on-eof ws-upgrade:tcp-listen:0.0.0.0:1234 broadcast:stdio: (without --binary the transformation is even tricker to make one text line always mean one message). This command line means approximatey this: "Websocat, using binary protocol, interpreting end-of-file as immediate connection close, use 0.0.0.0:1234 as socket address to listen TCP port, and perform a WebSocket handshake for each connected client. After that, connect each client to the broadcast:stdio: specifier. broadcast: overlay is a connection reuser: it means it can be invoked multiple times (when multiple clients connect, sequentially or in parallel), but in turn it invokes the underlying stdio: (use stdin/stdout) only once, broadcasting everything it gets to all currently connected clients, if there any.

reuse-raw: is an alternative reuser, and it is simpler: it assumes there to be only one active connection at a time and contains no list of connected clients. As a side effect it happens to provide backpressure.

ws-l: is just an alias for ws-upgrade:tcp-l:. There is --help=full and --help=doc for more exhaustive description of features.


Recommended command line:

myprogram | websocat --conncap 1 --exit-on-eof --binary ws-l:0.0.0.0:1234 reuse-raw:stdio:

You may also want to adjust --buffer-size.

vi commented 5 years ago

Also, the warning is misleading

Indeed, it should be rewritten in user-facing concepts instead of internal implementation terminology.

should be fatal error

Maybe there should be a warning starting Websocat in simple server mode where stdin is not a terminal (tty)?

qm2k commented 5 years ago

Thank you for the explanation, now it all became much more clear. I switched to using: myprogram | websocat --binary --exit-on-eof ws-upgrade:tcp-listen:0.0.0.0:1234 reuse-raw:stdio:

I still have one more question: can websocat quit or restart its program when the client drops connection? Currently nothing seem to happen in this case.

without --binary the transformation is even tricker to make one text line always mean one message

It explains a lot about synchronization.

Maybe there should be a warning starting Websocat in simple server mode where stdin is not a terminal (tty)?

I'd say a warning in binary simple server mode would be justified. Besides, I'd clarify that -s is shortcut for simple broadcasting server, and perhaps introduce -l for simple non-broadcasting one. Upon cursory reading the help output I thought that -s is analogous to netcat's -l, while in reality it does a much more complex thing.

vi commented 5 years ago

can websocat quit or restart its program when the client drops connection?

Do you mean --oneshot?

Or you want websocat to start custom program and restart it when new client connects? That's usual websocat --binary -E ws-l:0.0.0.0:1234 exec:./my_program - this way it would act like an "application server" for WebSockets - each connection gets separate instance of a program (with backpressure, without intermixed messages). I expect it to handle reasonable number of requests per second and parallel connections. Combined with --static-file it allows serving simplistic single page web apps.

vi commented 5 years ago

I'd say a warning in binary simple server mode would be justified.

OK, it would be v1.5.0-pending.

Non-broadcasting server would probably only server only 1 connection at a time by default then.

qm2k commented 5 years ago

Do you mean --oneshot?

Or you want websocat to start custom program and restart it when new client connects?

Yes and yes, thank you again!