QUIC is the UDP-based, TLS 1.3-encrypted, multiplexed transport defined in RFC 9000, and the transport that HTTP/3 runs on.
QUIC is a general-purpose transport protocol specified across three core documents:
| RFC | Title | What it defines |
|---|---|---|
| RFC 9000 | QUIC: A UDP-Based Multiplexed and Secure Transport | The transport itself: packets, frames, streams, connection IDs, flow control, connection lifecycle and migration. |
| RFC 9001 | Using TLS to Secure QUIC | How TLS 1.3 is embedded in the handshake, the key schedule, packet protection and header protection. |
| RFC 9002 | QUIC Loss Detection and Congestion Control | Acknowledgements, RTT estimation, loss detection, probe timeouts and the congestion controller. |
QUIC was designed to fix problems that are structural in TCP plus TLS, and that cannot be fixed without changing the transport itself.
This library contains two QUIC engines. They are represented by the TsgcQUICMode enumeration declared in sgcSSL_QUIC.pas:
TsgcQUICMode = (qmCustom, qmBuiltin);
The engine is not set directly. It follows the TLS IOHandler of the QUIC options (SSLQUICOptions on the QUIC client, TLSOptions on the HTTP/3 client, QUICOptions.SSLOptions on the servers):
iohOpenSSL, the default: the engine is detected when OpenSSL is loaded. If OpenSSL exports the built-in QUIC stack (OSSL_QUIC_client_method, OpenSSL 3.5 or later), qmBuiltin is used. Otherwise, if it exports the OpenSSL 3.5 QUIC TLS API (SSL_set_quic_tls_cbs), the sgc engine qmCustom is used as the fallback. IsOpenSSL_QUIC_Available and IsOpenSSL_QUIC_TLS_Available, declared in sgcIdSSLOpenSSLHeaders, report these two APIs. If neither is available, for example with a quictls build, the component raises an exception saying that QUIC is not available.iohNativeTLS: qmCustom is always used, and the TLS 1.3 handshake runs on the native TLS engine of the sgcCrypto pack. Session tickets, resumption and 0-RTT work as on OpenSSL.With the OpenSSL binaries shipped with the library you get qmBuiltin. The servers report the engine they started on in the read-only QUICMode property.
With iohNativeTLS, the sgc engine protects packets natively: packet and header protection (AES-128-GCM, AES-256-GCM, ChaCha20-Poly1305), key derivation, key update and the Retry integrity tag run on sgcCrypto instead of OpenSSL, per connection and without any define. Together with the native TLS 1.3 handshake, QUIC and HTTP/3 run without OpenSSL at all, see SSL without OpenSSL. With iohOpenSSL, the packet protection follows sgcCryptoBackend (or the SGC_NATIVE_CRYPTO define).
The table below reflects what the code actually does, not what QUIC is capable of in principle.
qmBuiltin | qmCustom | |
|---|---|---|
| Transport implementation | OpenSSL 3.5+ native QUIC. The library drives it through OSSL_QUIC_client_method, OSSL_QUIC_server_method, SSL_new_listener and SSL_accept_connection. | Pascal QUIC stack inside the library (the sgcQUIC_* units: packets, frames, congestion control, flow control), with the TLS 1.3 handshake driven through the OpenSSL 3.5 QUIC TLS API or the native TLS engine. |
| Selected when | IOHandler = iohOpenSSL and OpenSSL exports the built-in QUIC stack. This is the default with mainline OpenSSL 3.5+. | IOHandler = iohNativeTLS, or OpenSSL exports the QUIC TLS API but not the built-in QUIC stack. |
| Used by TsgcQUICClient and TsgcHTTP3Client | Yes | Yes |
| Used by TsgcQUICServer and TsgcHTTP3Server | Yes | Yes. An empty Host listens dual-stack, on IPv4 and IPv6, as on the built-in engine. |
| Multiple streams per connection through the component API | Yes. OpenStream maps onto SSL_new_stream, and peer-opened streams can be harvested. | Yes. OpenStream, WriteStreamData and ConcludeStream work with per-stream flow control and retransmission. The HTTP/3 client opens its control and QPACK streams and reuses one QUIC connection for later requests. |
| WebTransport | Yes. WebTransport needs multiple streams per connection, so it needs this engine. | No. |
In short: qmBuiltin is the default engine with mainline OpenSSL 3.5+. qmCustom is the fallback when the built-in stack is not available, and the engine used with iohNativeTLS.
A QUIC connection is a container for streams. A stream is an ordered, flow-controlled sequence of bytes, and streams are independent of one another, which is what removes transport head-of-line blocking. Streams come in two shapes:
Every stream has a 62-bit stream ID. The two least significant bits of the ID encode who opened the stream and whether it is bidirectional, which is the layout the library mirrors in TsgcQUICStreamTypes (sgcQUIC_Types.pas):
| Low 2 bits | Enumeration value | Meaning |
|---|---|---|
0x00 | qcstClientBidirectional | Client-initiated, bidirectional |
0x01 | qcstServerBidirectional | Server-initiated, bidirectional |
0x02 | qcstClientUnidirectional | Client-initiated, unidirectional |
0x03 | qcstServerUnidirectional | Server-initiated, unidirectional |
Stream IDs are assigned in increasing order within each of those four categories, and how many streams a peer may open is bounded by the transport parameters exchanged during the handshake. The QUIC components expose a single default stream out of the box. Additional streams are opt-in through EnableMultiStream and OpenStream, and as noted above they require the builtin engine.
| Component | Description |
|---|---|
| TsgcQUICClient | Low-level QUIC client. Opens a QUIC connection to a server and exchanges raw application bytes over QUIC streams. Use it when you want QUIC as a transport without HTTP semantics. |
| TsgcQUICServer | Low-level QUIC server. Listens for QUIC connections and exchanges raw application bytes over QUIC streams. |
The HTTP/3 components build on this layer. TsgcHTTP3Server descends directly from TsgcQUICServer and adds the HTTP/3 framing, settings and request handling on top of the QUIC connections its ancestor accepts. TsgcHTTP3Client is a separate component rather than a descendant of TsgcQUICClient, but it drives the same underlying QUIC transport. See HTTP/3.
The client connects to a host and port, then writes application bytes on the default stream. Full property, method and event documentation lives on the component pages.
oClient := TsgcQUICClient.Create(nil);
oClient.Host := 'localhost';
oClient.Port := 4433;
oClient.OnQUICConnect := OnQUICConnectHandler;
if oClient.Connect(10000) then
oClient.WriteData('hello over QUIC');
The server binds a UDP port, loads a certificate and key, and then raises an event for every buffer it receives.
oServer := TsgcQUICServer.Create(nil);
oServer.Port := 4433;
oServer.QUICOptions.SSLOptions.CertFile := 'cert.pem';
oServer.QUICOptions.SSLOptions.KeyFile := 'key.pem';
oServer.OnQUICData := OnQUICDataHandler;
oServer.Start;
QUIC always runs over UDP, so make sure the UDP port is open on any firewall in the path. A port that works for TCP is not automatically reachable over UDP.
The listener binds the address in Host. A value holding a colon is an IPv6 literal, and any other value, an IPv4 literal or a host name, is resolved over both families. An empty Host binds the dual-stack wildcard, so one listener serves IPv4 and IPv6 peers on the same port.
| Requirement | Detail |
|---|---|
| Edition | All-Access. The SGC_PACK_HTTP3 pack that enables QUIC sits in the SGC_EDT_ALL block of sgcVer.inc. |
| Pack define | SGC_PACK_HTTP3. It ships commented out in sgcVer.inc, so it must be enabled explicitly before QUIC is compiled in. |
| Feature defines | With SGC_PACK_HTTP3 and SGC_INDY_LIB both enabled, sgcVer.inc defines SGC_QUIC, SGC_HTTP3 and SGC_WEBTRANSPORT. The QUIC components are compiled under SGC_QUIC. |
| Indy | SGC_INDY_LIB is required. The QUIC defines are nested inside an SGC_INDY_LIB guard, so QUIC is not available in an Indy-free build. |
| OpenSSL | OpenSSL 3.5 or later. The server components need the native QUIC listener (SSL_new_listener, SSL_accept_connection) that was introduced in 3.5, and raise an exception on older versions. |
| Units | sgcHTTP.pas declares the components. The transport lives in the sgcQUIC_* units and the OpenSSL glue in sgcSSL_QUIC.pas. |
To enable QUIC, edit sgcVer.inc and uncomment the pack define inside the All-Access block:
{$IFDEF SGC_EDT_ALL} { ALL-ACCESS }
{$DEFINE SGC_PACK_HTML}
{$DEFINE SGC_PACK_HTTP3}
{.$DEFINE SGC_PACK_WEBRTC}
{$ENDIF}