How Chromium Implements QUIC and HTTP/3 Over UDP Sockets

This article provides a technical breakdown of how the Chromium web browser implements the QUIC transport protocol and HTTP/3 using User Datagram Protocol (UDP) sockets. It explores the architecture of Chromium's network stack, detailing the transition from traditional TCP to user-space UDP connections, the role of the QUICHE library, how the browser discovers HTTP/3 support, and how it manages encryption, multiplexing, and congestion control to deliver faster web performance.

Moving the Transport Layer to User Space

Traditionally, web browsers rely on the Transmission Control Protocol (TCP), which is managed entirely by the operating system's kernel. To implement QUIC—the transport layer underlying HTTP/3—Chromium bypasses the OS TCP stack and uses UDP instead.

Because UDP is connectionless and provides no built-in reliability, ordering, or congestion control, Chromium uses UDP sockets merely as a raw pipe to send and receive encrypted datagrams. It interacts with the operating system using its internal UDPClientSocket class. All the complex transport logic is moved out of the OS kernel and directly into Chromium's user-space C++ code. This allows Chromium engineers to update and optimize the transport protocol alongside regular browser updates without waiting for users to upgrade their operating systems.

Discovering HTTP/3 and Racing Connections

Chromium does not immediately know if a remote server supports HTTP/3 over UDP. To figure this out, the browser relies on protocol discovery and connection racing:

  1. Initial Connection: On the first visit to a website, Chromium usually connects via HTTP/2 over a standard TCP socket.
  2. The Alt-Svc Header: If the server supports HTTP/3, it includes an Alt-Svc (Alternative-Service) header in its HTTP response (e.g., Alt-Svc: h3=":443"). This tells Chromium that an HTTP/3 endpoint is available on UDP port 443.
  3. Caching and Racing: Chromium caches this Alt-Svc mapping in its HttpServerProperties. On subsequent requests to the same domain, Chromium’s HttpStreamFactory initiates a connection race. It simultaneously attempts to open a QUIC session over UDP and a fallback TCP connection (typically with a slight delay given to TCP). Whichever protocol completes its handshake first wins the race and handles the HTTP request.

The Core Architecture: QUICHE and the net/ Directory

Chromium’s implementation of QUIC and HTTP/3 lives primarily in the net/quic/ directory and is powered by QUICHE (QUIC, HTTP/2, Etc.), Google's shared, production-ready C++ library for networking protocols. Chromium wraps the core QUICHE engine in browser-specific classes to integrate it with its asynchronous event loop:

  • QuicStreamFactory: This is the central manager for all QUIC connections in Chromium. It handles socket creation, binds the UDPClientSocket, and pools existing QUIC sessions so multiple tabs requesting the same domain can share a single UDP socket.
  • QuicChromiumClientSession: This class represents a single, active QUIC connection to a server. It manages the state of the connection and handles stream multiplexing.
  • QuicChromiumClientStream: This represents an individual HTTP/3 request/response pair. Multiple streams are multiplexed over a single QuicChromiumClientSession. Because QUIC manages streams independently in user space, a lost UDP packet only delays the specific stream whose data was inside that packet, completely eliminating TCP's head-of-line blocking issue.

Integrated Encryption with BoringSSL

Unlike TCP, where the transport handshake and the TLS cryptographic handshake happen sequentially, QUIC combines them into a single process. Chromium uses BoringSSL (Google's fork of OpenSSL) to provide TLS 1.3 encryption for QUIC.

Instead of BoringSSL writing directly to a socket, Chromium hooks BoringSSL directly into the QUIC engine. BoringSSL generates the cryptographic handshake data, and Chromium encapsulates it into special QUIC CRYPTO frames, which are then packed into UDP datagrams. This tight integration allows Chromium to establish a secure HTTP/3 connection in a single round-trip (1-RTT). If Chromium has connected to the server previously, it caches the cryptographic keys and uses 0-RTT (Zero Round Trip Time) resumption, sending encrypted HTTP/3 request data in the very first UDP packet.

The Packet Read/Write Loop and Congestion Control

To make unreliable UDP act like a reliable stream for HTTP/3, Chromium implements a continuous packet processing loop coupled with user-space congestion control.

  • Writing Packets: When the browser makes an HTTP/3 request, Chromium compresses the HTTP headers using QPACK (the HTTP/3 equivalent of HPACK). The data is broken into QUIC STREAM frames, encrypted, and handed to the QuicChromiumPacketWriter. This class pushes the final UDP datagram to the OS socket. To prevent overwhelming the network, Chromium uses pacing timers to space out the transmission of these UDP packets.
  • Reading Packets: The QuicChromiumPacketReader continuously listens to the UDPClientSocket for incoming data. When a datagram arrives, it is read from the OS buffer, decrypted via BoringSSL, and parsed into frames. The data is then reassembled in the correct order and passed up to the browser's rendering engine.
  • Reliability and Congestion Control: Because the OS does not track dropped UDP packets, Chromium tracks them using QUIC packet numbers and ACK (Acknowledgment) frames. If a packet is not acknowledged by the server, Chromium retransmits the missing data in a newly numbered UDP packet. To manage network bandwidth, Chromium runs advanced congestion control algorithms—such as Cubic and BBR (Bottleneck Bandwidth and Round-trip propagation time)—directly in its network thread, dynamically adjusting the UDP sending rate based on packet loss and round-trip time measurements.

Connection Migration and TCP Fallback

Using UDP sockets allows Chromium to implement Connection Migration. In TCP, a connection is identified by a 4-tuple (Source IP, Source Port, Destination IP, Destination Port). If a user's device switches from Wi-Fi to cellular data, the IP changes, and the TCP connection breaks. Chromium's QUIC implementation circumvents this by embedding a random 64-bit Connection ID in every UDP packet header. When a network switch occurs, Chromium opens a new UDPClientSocket on the new network interface and continues sending packets with the same Connection ID. The server recognizes the ID, and the HTTP/3 session continues without interruption.

Finally, because some corporate firewalls and routers aggressively block UDP traffic, Chromium maintains a strict fallback mechanism. If the QuicStreamFactory detects that UDP packets are being blackholed (silently dropped by the network) either during the handshake or mid-connection, it immediately abandons the QUIC session, marks QUIC as broken for that specific network interface, and seamlessly retries the request over a standard TCP socket using HTTP/2.