Skip to content
Putting It All Together
step 1/5

Reading — step 1 of 5

Read

~2 min readProduction

Putting It All Together

Time to assemble the pieces into one connection lifecycle. Here is an entire WebSocket session, on the wire:

1. Client:  GET /chat HTTP/1.1 + Upgrade + Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
2. Server:  101 Switching Protocols + Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
3. Client:  81 85 37fa213d 7f9f4d5158        masked TEXT "Hello"
4. Server:  81 05 48656c6c6f                 unmasked TEXT "Hello" (echo)
5. Client:  89 80 <mask>                     PING          -> Server: 8a 00  PONG
6. Client:  88 82 <mask> <03e8 masked>       CLOSE 1000    -> Server: 88 02 03e8
7. TCP teardown

Every byte of that is something you've now implemented: the accept hash, the FIN/opcode/mask/length header, XOR unmasking, fragment reassembly, the pong echo, and the close reply.

The server as a state machine

What turns those parsers into a server is a per-connection state machine. A minimal one has three states:

  • open — data frames flow; PING gets an immediate PONG carrying the same payload; PONG absorbs silently (it answers a ping we sent earlier); CLOSE gets a CLOSE reply and moves us to closing.
  • closing — we've seen (or sent) a CLOSE. RFC 6455: after the close handshake begins, no further data frames may be processed. Only teardown remains.
  • closed — the TCP socket is gone.

The ordering discipline matters more than it looks. Control frames are handled immediately, even mid-fragmentation, without touching the reassembly buffer. And once CLOSE is in flight, everything else — including another PING — is refused. A server that keeps answering pings after agreeing to close will hang connections against strict peers.

What production adds on top

The protocol core you've built is what libraries like ws (Node), websockets (Python), gorilla/websocket (Go), and tokio-tungstenite (Rust) implement. Around it, a production deployment adds: TLS (wss:// — same frames, wrapped in TLS on 443), authentication at upgrade time (it's still HTTP there — cookies and tokens work), Origin checks so hostile pages can't ride a user's cookies onto your socket, per-connection rate limits and idle timeouts, and metrics. The next three lessons take on the remaining deep topics: permessage-deflate compression, backpressure against slow consumers, and RFC-conformance testing with Autobahn.

Your exercise

Build the capstone state machine: HANDSHAKE (lesson 2's accept), FRAME (lessons 5–7 combined), STATE. The traps the grader catches, with exact outputs: after a CLOSE, every frame is refused — even a PING gets ERR closed, while STATE still answers closing. The PING echo format is PONG <hex> built with an f-string — for FRAME 1 PING (empty payload) the expected line is PONG (the word, a space, nothing after); assembling it any other way drops the trailing space. A received PONG prints nothing — an extra line there shifts every subsequent expectation. And fragmentation keeps lesson 5's discard rule: mid-fragment TEXT prints only ERR new-data-mid-fragment.

Discussion

Ask a question, share an insight, or help someone who’s stuck.

Sign in to post a comment or reply.

Loading…