TsgcHTTP3Server implements an HTTP/3 server (RFC 9114) that serves HTTP requests over QUIC.
TsgcHTTP3Server implements an HTTP/3 server as defined in RFC 9114. HTTP/3 carries HTTP requests and responses over QUIC, a UDP-based transport with TLS 1.3 built into it, so every connection is encrypted and multiplexed without head-of-line blocking: a lost packet stalls only the request stream it belonged to, not the whole connection. By default the server runs on the native QUIC listener provided by OpenSSL 3.5 or later, and falls back to the sgc QUIC engine when that listener is not available (see QUICMode), so no external HTTP/3 library is required.
The component owns the whole HTTP/3 stack above QUIC: it opens the control and QPACK streams, exchanges SETTINGS, decodes QPACK header sections, reassembles the request frames on each stream and raises one event per complete request. Requests are multiplexed, so several may be in flight on a single connection, each on its own QUIC stream.
TLS is mandatory. QUIC has no plaintext mode, so the server cannot start without a certificate and a matching private key. Clients validate that certificate exactly as they would an HTTPS certificate, which means a self-signed certificate works only with clients configured to trust it. HTTP/3 also runs over UDP rather than TCP, so the listening port must be open for UDP in any firewall in between.
Follow the steps below to configure this component:
1. Create a new instance of TsgcHTTP3Server and set the Port the server will listen on. HTTP/3 uses UDP, and the inherited default port is not meaningful for HTTP/3, so always set it. Port 443 is the usual choice in production, since it lets the same authority be advertised through the Alt-Svc header of an HTTPS site.
2. Set the certificate and private key in QUICOptions.SSLOptions. Both are PEM files and both are required, because QUIC always negotiates TLS 1.3.
3. Handle the OnH3Request event and call Start. The aResponse parameter is created by the server with StatusCode preset to 200; set its StatusCode, ContentType and Data, and the server QPACK-encodes and sends it automatically when the handler returns. There is no need to call SendResponse. example:
oServer := TsgcHTTP3Server.Create(nil);
oServer.Port := 443;
oServer.QUICOptions.SSLOptions.CertFile := 'cert.pem';
oServer.QUICOptions.SSLOptions.KeyFile := 'key.pem';
oServer.OnH3Request := OnH3RequestEvent;
oServer.Start;
procedure OnH3RequestEvent(Sender: TObject; const aConnection:
TsgcQUICServerConnection; const aRequest: TsgcHTTP3Request;
const aResponse: TsgcHTTP3Response);
begin
aResponse.StatusCode := 200;
aResponse.ContentType := 'text/plain; charset=utf-8';
aResponse.Data := TEncoding.UTF8.GetBytes('Hello HTTP/3');
end;
A request is answered by filling the TsgcHTTP3Response the server hands to OnH3Request. The object is owned by the server, so fill it and do not free it.
| Member | Type | Description |
|---|---|---|
StatusCode | Integer | Sent as the :status pseudo-header. Preset to 200. |
ContentType | string | Sent as the content-type header when not empty. |
Data | TBytes | The response body. Encode strings explicitly, normally with TEncoding.UTF8.GetBytes. |
Headers | TStringList | Extra response headers, one entry per header in name=value form. |
ContentLength | Int64 | Not used when sending: content-length is computed from the length of Data. |
The server writes :status and adds content-type and content-length for you, so those never need to be added by hand. Custom headers use name=value and not name: value. Pseudo-headers and the connection-specific headers forbidden by RFC 9114 (connection, keep-alive, proxy-connection, transfer-encoding and upgrade) are dropped rather than sent. A response to a HEAD request is sent as a header section only, with the body suppressed automatically. See OnH3Request for the full request and response reference.
Server push. Call PushPromise from inside OnH3Request to promise an associated resource and deliver it on a server-initiated push stream. It returns False when the client has granted no push budget through MAX_PUSH_ID, which is normal and must be checked. Current mainstream browsers do not implement HTTP/3 server push, so it is useful mainly with clients you control.
Extended CONNECT and tunnels. Answering a CONNECT request with a 2xx status turns its request stream into a tunnel: the stream stays open, subsequent DATA frames stop being a request body and arrive at OnH3TunnelData, and TunnelSend writes bytes back. Set Settings.EnableConnectProtocol to advertise Extended CONNECT (RFC 9220), which lets a client send a CONNECT carrying a :protocol pseudo-header. Setting H3Settings.Settings.WebTransportMaxSessions above zero additionally enables WebTransport and raises OnWebTransportSession for each session, which is accepted unless the handler rejects it.
Graceful shutdown. Stop closes connections immediately. To drain instead, send SendGoAway on each connection first: it tells the peer the last request stream the server will handle, so clients stop opening new streams and can retry the rest elsewhere, while in-flight requests finish. Call Stop once they have.
| Name | Description |
|---|---|
| Port | UDP port the HTTP/3 server listens on. |
| QUICOptions | QUIC transport options, including the mandatory certificate and private key, and connection limits. |
| H3Settings | HTTP/3 protocol settings advertised to peers: QPACK and field-section limits, Extended CONNECT and WebTransport. |
| Active | Starts or stops the HTTP/3 server listener. |
| Name | Description |
|---|---|
| Start | Starts the QUIC listener and begins accepting HTTP/3 connections. |
| Stop | Stops the listener and closes all active connections. |
| SendGoAway | Sends a GOAWAY frame to start a graceful connection shutdown. |
| PushPromise | Pushes an associated resource to the client from inside OnH3Request. |
| Name | Description |
|---|---|
| OnH3Request | Fires when a complete HTTP/3 request is received so the application can fill the response. |
| OnH3Connect | Fires when a client establishes an HTTP/3 connection. |
| OnH3Disconnect | Fires when an HTTP/3 connection is closed. |
| OnH3Exception | Fires when an exception is raised while servicing an HTTP/3 connection. |
The following example starts an HTTP/3 server that routes a couple of paths and returns a JSON or a plain-text body, logs connections, and stops the server cleanly. The response is filled inside OnH3Request and sent automatically by the server.
// start
oServer := TsgcHTTP3Server.Create(nil);
oServer.Port := 4433;
oServer.QUICOptions.SSLOptions.CertFile := 'server.crt';
oServer.QUICOptions.SSLOptions.KeyFile := 'server.key';
oServer.OnH3Request := OnH3RequestEvent;
oServer.OnH3Connect := OnH3ConnectEvent;
oServer.OnH3Disconnect := OnH3DisconnectEvent;
oServer.OnH3Exception := OnH3ExceptionEvent;
oServer.Start;
procedure OnH3ConnectEvent(Sender: TObject; const aConnection:
TsgcQUICServerConnection);
begin
Log('HTTP/3 client connected: ' + aConnection.PeerIP + ':' +
IntToStr(aConnection.PeerPort));
end;
procedure OnH3DisconnectEvent(Sender: TObject; const aConnection:
TsgcQUICServerConnection; aCode: Int64; const aReason: string);
begin
Log('HTTP/3 client disconnected: ' + aReason);
end;
procedure OnH3ExceptionEvent(Sender: TObject; const aConnection:
TsgcQUICServerConnection; const aException: Exception);
begin
Log('HTTP/3 error: ' + aException.Message);
end;
// answer a request
procedure OnH3RequestEvent(Sender: TObject; const aConnection:
TsgcQUICServerConnection; const aRequest: TsgcHTTP3Request;
const aResponse: TsgcHTTP3Response);
begin
if aRequest.Path = '/api/time' then
begin
aResponse.StatusCode := 200;
aResponse.ContentType := 'application/json';
aResponse.Data := TEncoding.UTF8.GetBytes(
'{"time":"' + DateTimeToStr(Now) + '"}');
end
else
begin
aResponse.StatusCode := 404;
aResponse.ContentType := 'text/plain; charset=utf-8';
aResponse.Data := TEncoding.UTF8.GetBytes('Not Found');
end;
end;
// stop
oServer.Stop;
oServer.Free;
| Requirement | Detail |
|---|---|
| Edition | All-Access edition. |
| Compiler define | HTTP/3 ships disabled. Uncomment {$DEFINE SGC_PACK_HTTP3} in sgcVer.inc, which enables SGC_QUIC, SGC_HTTP3 and SGC_WEBTRANSPORT. |
| Indy | SGC_INDY_LIB must be enabled: the HTTP/3 defines are nested inside it. |
| Unit | sgcHTTP3_Server, or sgcHTTP for the registered TsgcHTTP3Server component. |
| OpenSSL | OpenSSL 3.5 or later with QUICOptions.SSLOptions.IOHandler = iohOpenSSL. There is no fallback on an older OpenSSL. With iohNativeTLS OpenSSL is not needed. |
| Transport | UDP. The listening port must be open for UDP, not TCP. |
| .NET | The .NET server accepts QUIC connections (.NET 8.0 or later, MsQuic) and implements the HTTP/3 framing and QPACK layer, which it uses for the SETTINGS handshake and for Extended CONNECT / WebTransport. It still does not serve plain HTTP/3 requests: they are never routed to OnH3Request, and SendResponse, SendGoAway and SendPush are not wired to the framing layer, so they do nothing. Use Delphi/C++Builder to serve HTTP/3 requests. |