Adetayo Akinsanya unkletayo.dev

Modern Web Networking: HTTP/2 Multiplexing, HTTP/3 QUIC, WebSockets, and Server-Sent Events

Deconstructing binary framing, transport head-of-line blocking, QUIC UDP encryption, and real-time streaming protocols

Part 4 in Series — Catch up on the previous article: TypeScript for System Design: Advanced Type Mechanics, Nominal Branding, and Turing Completeness (Part 3) before diving into this post.

Network communication forms the core pipeline of client-side web application latency. For decades, web developers worked within the constraints of HTTP/1.1—implementing domain sharding, image spriting, and manual resource concatenation to work around browser connection limits and head-of-line blocking.

Modern frontend web architecture relies on advanced transport protocols: HTTP/2 binary multiplexing, HTTP/3 QUIC over UDP, WebSockets, and Server-Sent Events (SSE).


1. HTTP/1.1 vs HTTP/2: Binary Framing & Multiplexing

HTTP/1.1 processes requests over plain-text streams. Because a single TCP connection can only handle one request-response exchange at a time, browsers enforced a strict limit of 6 concurrent TCP connections per origin.

HTTP/2 introduced the Binary Framing Layer, breaking requests and responses into discrete binary frames (DATA, HEADERS, SETTINGS, RST_STREAM) interleaved over a single TCP connection.

HTTP/1.1 (Sequential Request Blocking):
TCP Conn 1: [ Req 1: style.css ] --------> [ Resp 1: style.css ] ---> [ Req 2: app.js ]
TCP Conn 2: [ Req 3: logo.png  ] --------> [ Resp 3: logo.png  ]

HTTP/2 (Binary Multiplexing over Single TCP Connection):
Single TCP: [ Frame: Stream 1 (CSS) ][ Frame: Stream 3 (JS) ][ Frame: Stream 1 (CSS) ]

1.1 HPACK Header Compression

HTTP/1.1 sends verbose text headers (User-Agent, Cookie, Accept) with every single request, introducing multi-kilobyte overhead.

HTTP/2 uses HPACK Header Compression:

  1. Both client and server maintain a shared Static Table (common headers indexed 1–61) and a dynamic Dynamic Table (custom headers added during connection lifetime).
  2. Instead of resending "User-Agent: Mozilla/5.0..." on every request, the client transmits a 1-byte indexed reference pointer.

2. HTTP/2 Transport HOL Blocking vs HTTP/3 QUIC

While HTTP/2 solves application-layer head-of-line blocking, it introduces Transport-Layer Head-of-Line (HOL) Blocking at the TCP layer.

Because TCP guarantees strict in-order packet delivery:

  • If a single TCP packet carrying Frame Stream 3 is dropped on an unreliable Wi-Fi connection, the operating system kernel blocks all streams (Stream 1, Stream 2, Stream 4) in the TCP buffer until the lost packet is retransmitted.
TCP Layer HOL Blocking (HTTP/2):
[ Packet 1 (Stream 1) ][ Packet 2 (DROPPED) ][ Packet 3 (Stream 3) ]
                             |
                             v
          OS Kernel STALLS ALL Streams waiting for Packet 2 retransmission!

2.1 HTTP/3 & QUIC (Quick UDP Internet Connections)

HTTP/3 solves transport-layer HOL blocking by replacing TCP with QUIC, an encrypted transport protocol operating on top of UDP.

HTTP/2 Stack:                  HTTP/3 Stack:
+-------------------+          +-------------------+
|      HTTP/2       |          |      HTTP/3       |
+-------------------+          +-------------------+
|       TLS 1.3     |          |   QUIC (Built-in  |
+-------------------+          |   TLS 1.3 + Cong) |
|        TCP        |          +-------------------+
+-------------------+          |        UDP        |
|        IP         |          +-------------------+
+-------------------+          |        IP         |
                               +-------------------+
  • Independent Streams: QUIC implements stream multiplexing directly at the transport layer. A dropped UDP packet in Stream 1 only stalls Stream 1; Stream 2 and Stream 3 continue processing without delay.
  • 0-RTT Connection Establishment: TLS 1.3 is embedded directly inside the QUIC handshake. Re-connecting clients can send encrypted request data on the very first packet exchange (0-RTT).
  • Connection Migration: QUIC identifies sessions using a 64-bit Connection ID rather than an IP/Port 4-tuple. When a mobile device switches from Wi-Fi to 5G cellular, the QUIC connection stays alive without dropping or re-handshaking.

3. Real-Time Streaming: WebSockets vs Server-Sent Events (SSE)

Choosing the correct real-time architecture requires analyzing directional data flow requirements:

FeatureWebSockets (ws://, wss://)Server-Sent Events (EventSource)
Data DirectionFull-Duplex (Client <---> Server)Unidirectional (Server ---> Client)
Underlying ProtocolCustom TCP Frame Protocol (HTTP Upgrade)Standard HTTP/2 or HTTP/1.1
ReconnectionManual implementation requiredBuilt-in automatic retry & Last-Event-ID
Binary SupportNative (ArrayBuffer, Blob)Text only (UTF-8 payload)
HTTP InfrastructureBypasses standard HTTP proxies/firewallsReuses standard HTTP load balancers/CDNs

3.1 Server-Sent Events (SSE) Production Client Implementation

For real-time dashboards, stock tickers, or AI stream completions where the client only reads server updates:

// Production Resilient SSE Connection Architecture
export class ResilientEventStream {
  private eventSource: EventSource | null = null;
  private reconnectAttempts = 0;
  private maxReconnectAttempts = 5;

  constructor(private url: string) {}

  public connect(onMessage: (data: unknown) => void) {
    this.eventSource = new EventSource(this.url);

    // Standard SSE message listener
    this.eventSource.onmessage = (event: MessageEvent) => {
      this.reconnectAttempts = 0; // Reset counter on success
      const parsedData = JSON.parse(event.data);
      onMessage(parsedData);
    };

    // Custom named SSE event listener
    this.eventSource.addEventListener("telemetry-update", (event: MessageEvent) => {
      console.log("Telemetry Payload:", event.data);
    });

    this.eventSource.onerror = (err) => {
      console.error("SSE Connection Error. Reconnecting...", err);
      if (this.eventSource?.readyState === EventSource.CLOSED) {
        this.handleReconnect(onMessage);
      }
    };
  }

  private handleReconnect(onMessage: (data: unknown) => void) {
    if (this.reconnectAttempts < this.maxReconnectAttempts) {
      this.reconnectAttempts++;
      const delay = Math.pow(2, this.reconnectAttempts) * 1000;
      setTimeout(() => this.connect(onMessage), delay);
    }
  }

  public disconnect() {
    if (this.eventSource) {
      this.eventSource.close();
      this.eventSource = null;
    }
  }
}

Summary & Key Takeaways

  • HTTP/2: Introduced binary framing and multiplexing over a single TCP connection, eliminating domain sharding and connection limits.
  • HPACK Compression: Eliminates repetitive HTTP header overhead using static and dynamic index tables.
  • HTTP/3 (QUIC): Replaces TCP with UDP to eliminate transport-layer head-of-line blocking and enable 0-RTT handshakes and connection migration across networks.
  • WebSockets vs SSE: Use WebSockets for bidirectional interactive applications (chat, collaborative drawing). Use Server-Sent Events (SSE) for unidirectional server broadcasts (live telemetry, AI response streaming) due to native auto-reconnection and HTTP compatibility.

References & Further Reading

  1. IETF. RFC 9113: HTTP/2 Infrastructure Specification. IETF Standard.
  2. IETF. RFC 9114: HTTP/3 Protocol & QUIC Transport. IETF Standard.
  3. WHATWG. Server-Sent Events Specification. WHATWG Standard.

Up Next in Series →

Part 5: React Architecture Under the Hood: Fiber Tree Reconciliation, Priority Scheduling, and Concurrent Mode

Continue to Part 5 →