Challenge 3: Trace a Full Connection Lifecycle — Possible Solution ==================================================================== STEP-BY-STEP TRACE -------------------- 1. new ReconnectingSocket("wss://example.com/chat?token=...") is called. -> Chapter 5's wrapper class creates a fresh native WebSocket and begins the connection attempt. 2. The browser sends the HTTP Upgrade handshake request, including the token in the URL query string and an Origin header identifying the page's own origin. -> Chapter 2's protocol mechanics: this is still an ordinary HTTP request at this point, carrying Upgrade/Connection/Sec-WebSocket-* headers plus the Origin header the browser adds automatically. 3. The server's verifyClient hook runs BEFORE the handshake completes, checking the Origin header against an allowlist and validating the token from the query string. -> Chapter 8's authentication and origin-checking material. If either check fails, the server responds with 403 or 401 and the handshake never completes — no WebSocket connection is established at all. 4. Assuming both checks pass, the server responds 101 Switching Protocols, and the connection becomes a live, full-duplex WebSocket. -> Chapter 2 again: the same underlying TCP connection now carries WebSocket frames instead of further HTTP requests. 5. The server's "connection" handler fires, attaching socket.userId (from the verified token), initializing socket.rooms as an empty Set, and calling attachRateLimit(socket). -> Chapter 3's connection event and Chapter 6's per-socket room tracking, plus Chapter 8's rate limiter, all wired up at once. 6. On the client, the "open" event fires, which (a) hides any "Reconnecting..." banner and (b) sends a { type: "join", roomName: "lobby" } message. -> Chapter 5's reconnection-state UI feedback, and the capstone's own join-message pattern. 7. The server's "message" handler receives the join message, parses it, validates its shape, and — since data.type is "join" — calls joinRoom(socket, "lobby"). -> Chapter 8's JSON parsing/validation safety net wraps EVERY incoming message, including this one; Chapter 6's joinRoom adds the socket to the room's Set and broadcasts a "joined" presence event to the room. 8. Some time later, this same client sends a { type: "chat", roomName: "lobby", text: "hello" } message. -> Goes through the identical parse-then-validate path from step 7; since data.type is "chat" this time, the server calls sendToRoom("lobby", ...) instead of joinRoom. 9. sendToRoom iterates every socket currently in the "lobby" room's Set, checking readyState === OPEN before sending to each one. -> Chapter 3's readyState safety check, applied per-room instead of globally, exactly as Chapter 6 adapted it. 10. On another user's browser (also connected to "lobby"), the "message" event fires with this chat payload. The client renders it using messageDiv.textContent = ... rather than innerHTML. -> Chapter 8's XSS-prevention gotcha — rendering as text, not parsed HTML, is what stops a malicious "text" value from executing as script in this other user's browser. WHY THIS WORKS AS A TRACE ---------------------------- Each step names a SPECIFIC mechanism from a SPECIFIC earlier chapter, rather than describing the system only in vague, general terms — this is exactly what the capstone is meant to demonstrate: that a real feature isn't one new technique, but the deliberate combination of the protocol mechanics (Ch.2), server (Ch.3) and client (Ch.5) event handling, rooms and presence (Ch.6), and security practices (Ch.8) that were each built and tested in isolation throughout the rest of the course.