engineering notes
Server architecture
The TCP server keeps state per connection, accumulates bytes conservatively, and only routes a request after a complete HTTP frame is present.
Listener and socket ownership
The server starts in src/tcpServer.ts with Bun.listen(). The important decision there is that each socket owns its own Uint8Array buffer. That prevents the usual beginner mistake where partial bytes from separate clients are treated as one stream.
Socket state is intentionally small. The server only persists the unread bytes that still matter. There is no request queue, session state, or long-lived object graph. That keeps failure cases local and makes the transport layer easier to inspect.
What happens when bytes arrive
The data handler does not try to decode the request immediately. It appends the new bytes to the existing socket buffer and asks parseHttpRequest() whether the buffer now contains a complete message.
If the parser returns incomplete, the handler exits without side effects. If the parser returns complete, the handler preserves any unread trailing bytes, routes the parsed request, serializes the response, and closes the connection cleanly.
Failure and timeout behavior
Malformed input is converted into an HTTP error response instead of crashing the server. Errors related to invalid headers, invalid request lines, or impossible Content-Length values are handled in one place, which keeps the transport code small.
The timeout path matters because raw TCP clients can stall after sending only part of a request. The socket timeout returns 408 so incomplete traffic does not stay around forever.