Repository navigation
Description
tinyweb serves WebSocket and SSE from code of its own: a raw TCP listener on a second and third port (web_server_with_ws, web_server_full) and frame code written over tcp.read and tcp.write. That code passes the tests it has, which send a few short text messages over loopback, but it is not correct on a real network:
WebSocket read (ws_read_frame):
- Each field is one
tcp.read, a single recv(2). A header, a length or a payload that arrives in two TCP segments is read short. For example, a message larger than one segment (about 1.4 KB off loopback) reads only its first part, and the rest is then parsed as the next frame's header. - Only the 7-bit and 16-bit lengths are handled. A 64-bit length (marker 127) is taken as a 127-byte payload.
tcp.readis the text path, so a binary payload is cut at its first zero byte.- A ping is handed to the handler as a text message and never answered with a pong. A fragmented message is not reassembled.
- A client quiet for 30 s (the socket's receive timeout) reads as a close, so an idle connection is dropped.
WebSocket write (ws_send_frame):
- A payload over 65535 bytes is sent with the 16-bit length form, and its length wraps, so the frame is corrupt.
- The frame goes out in one send whose count is ignored (A short TCP send fails a TLS record: std.tcp has no write that sends a whole buffer #3108).
SSE and chunked transfer:
- SSE events, the SSE headers, the 101 and 404 responses, and chunked bodies are written with one
tcp.writeeach, ignoring the count.sse_sendreturns nothing, so a handler cannot tell that its client has gone and keeps streaming to a dead socket. write_chunkbuilds the hex chunk size withstring.from_int(digit + 48), which gives the decimal text "53" where it should give the character '5'. Every chunk size it sends is wrong, so no client can read a chunked body from it. Nothing calls it in a test, though the README lists it.
ws_client.ae:
contrib/tinyweb/ws_client.aebuilds its frame headers withstring.from_int(129), which sends the digits "129" where it should send the byte 0x81. Nothing tests it.
Fix: tinyweb's WebSocket and SSE endpoints are served by std.http's server on the main port:
- WebSocket through
http.server_websocket, whose frame code is RFC 6455 conformance-tested against python websockets and handles 64-bit lengths, binary frames, ping/pong, fragments and close codes. The handler's sender is a std.http WebSocket handle. - SSE through
http.response_upgrade_sse. - Chunked streaming through std.http's streaming response.
tinyweb's own listener ports, its frame reader and writer, and ws_client.ae go away; std.http's ws_connect is the client. Tests drive the endpoints with real clients and cover: a message over 64 KiB, a binary payload with zero bytes, a ping, an idle period past 30 s on a shortened timeout, a client that leaves mid-stream (sse_send reports it), and a chunked body read back by std.http's client.
Configuration that is never applied. with_timeout, with_backlog, with_ws_backlog, with_keep_alive, on_error, on_server_error, on_ws_timeout and on_ws_error store their value in the server's map, and nothing ever reads it back; _invoke_error_handler has no caller. A program that sets a timeout or an error handler gets neither, without a word. The ones std.http's server supports (with_timeout, with_keep_alive: http.server_set_keepalive and its idle timeout) are applied at tw_start; the rest, which nothing underneath can do, are removed.
Activity
tinyweb serves its WebSocket, SSE and chunked endpoints through std.h…
fixup! tinyweb serves its WebSocket, SSE and chunked endpoints throug…