sgcQUIC in five minutes

QUIC and HTTP/3 in native Object Pascal, on the QUIC engine inside OpenSSL. Four components ship. The shortest path to something running is the HTTP/3 client, so this page makes one request, reads the status code, and is precise about which OpenSSL you need.

QUIC RFC 9000 and HTTP/3 RFC 9114
OpenSSL 3.2 and later for the client
All-Access edition

What the first request needs

One component, one URL, and two OpenSSL libraries beside your executable.

Component

TsgcHTTP3Client on the SGC QUIC palette page, declared in sgcQUIC.pas. The page also carries TsgcQUICClient, TsgcQUICServer and TsgcHTTP3Server.

Unit

sgcQUIC for the component. Add sgcHTTP3_Classes for TsgcHTTP3Response, and sgcHTTP_AltSvc if you handle the Alt-Svc event.

The call

Get(aURL) returns the body as a string and raises on failure. The status code and headers arrive separately, on OnResponse.

The OpenSSL requirement

The client needs the QUIC API in OpenSSL 3.2 or later, or a quictls build. The server needs 3.5 or later, because it calls an API that only exists there. Ship libcrypto-3.dll and libssl-3.dll beside your executable, as every demo folder does.

Requirements and editions

The edition column is the define that gates the code, with the line it sits on in Source/sgcVer.inc.

What Value
IDE Delphi 7 through RAD Studio 13, and C++Builder 2007 through 13. There is no separate sgcQUIC download: the components are in the sgcWebSockets package group.
Uses clause sgcQUIC, plus sgcHTTP3_Classes for the response object and sgcHTTP_AltSvc for the Alt-Svc types.
Pack define SGC_PACK_QUIC is defined on line 872, inside the {$IFDEF SGC_EDT_ALL} block that runs from line 870 to line 874. So All-Access.
Feature defines Inside the {$IFDEF SGC_PACK_QUIC} block on lines 894 to 899: SGC_QUIC on line 896, SGC_HTTP3 on line 897 and SGC_WEBTRANSPORT on line 898. All three sit inside an {$IFDEF SGC_INDY_LIB} at line 895, so a build without the custom Indy library gets none of them.
OpenSSL, client 3.2 or later, or a quictls build. The library says so itself: the error raised when QUIC is unavailable reads QUIC is not available. Requires quictls/openssl or OpenSSL 3.2+.
OpenSSL, server 3.5 or later. The QUIC server calls SSL_new_listener, and the error it raises when that is missing reads QUIC Server requires OpenSSL 3.5 or later. msquic is not used and is not needed.
Platforms No unit-scope platform guard on sgcQUIC.pas, sgcQUIC_Client.pas, sgcHTTP3_Client.pas or sgcHTTP3_Server.pas, and all four components are registered with ComponentPlatforms(0). The server unit selects the socket API per platform, with both a Windows and a POSIX branch.

Not sure the engine is present at runtime? Call IsOpenSSL_QUIC_Available, which returns whether the OpenSSL you loaded exposes the QUIC client method. The shipped QUIC client demo logs it on startup for exactly this reason.

Install and find the palette page

There is no separate sgcQUIC installer. The components arrive with sgcWebSockets and appear once the edition enables them.

1. Unzip

Unzip the sgcWebSockets download to a folder, called {$DIR} below.

2. Library path

Tools, Options, Library. Add {$DIR}\source and the lib folder for your IDE, for example {$DIR}\libD13\$(Platform).

3. Build the packages

Open the package group for your IDE version under {$DIR}\Packages\. Compile the runtime .dpk first, then install the design-time dcl one. There is no QUIC-specific package.

4. Check the palette

A page called SGC QUIC appears with TsgcQUICClient, TsgcQUICServer, TsgcHTTP3Client and TsgcHTTP3Server. If the page is missing, the build is not All-Access, because SGC_PACK_QUIC is defined only on line 872 inside that block.

5. Put OpenSSL beside the exe

Copy libcrypto-3.dll and libssl-3.dll next to your executable, 3.2 or later for a client and 3.5 or later for a server. Every folder under Demos\22.QUIC_Protocol ships them, so you can copy from there.

One HTTP/3 request

Create the client, wire three events, call Get. The reply comes back as a string and the status code arrives on OnResponse.

FHTTP3Client.pas
uses
  Classes, SysUtils,
  // sgc
  sgcQUIC, sgcHTTP3_Classes;

procedure TfrmHTTP3Client.FormCreate(Sender: TObject);
begin
  FClient := TsgcHTTP3Client.Create(nil);
  FClient.OnConnect := OnH3Connect;
  FClient.OnError := OnH3Error;
  FClient.OnResponse := OnH3Response;
  FClient.TLSOptions.VerifyCertificate := True;
  FClient.ConnectTimeout := 10000;
  FClient.ReadTimeout := 30000;
  FClient.UserAgent := 'sgcWebSockets/HTTP3Client';
end;

procedure TfrmHTTP3Client.btnGetClick(Sender: TObject);
var
  vResult: string;
begin
  try
    // the target comes from the URL, because Host and Port
    // are read-only on this component
    vResult := FClient.Get('https://www.google.com/');
    memoBody.Lines.Text := vResult;
    DoLog('Response received: ' + IntToStr(Length(vResult)) + ' bytes');
  except
    on E: Exception do
      DoLog('Error: ' + E.Message);
  end;
end;

Post, Put and Delete take the same shape, and each has a stream overload for a body you do not want to hold in a string. Connect(const aHost: string; aPort: Integer = 443) opens the connection ahead of the first request when you want to separate the two.

FHTTP3Client.pas
// OnConnect and OnDisconnect are plain TNotifyEvent on this
// component: one parameter, no connection object.
procedure TfrmHTTP3Client.OnH3Connect(Sender: TObject);
begin
  DoLog('Connected to ' + FClient.Host + ':' + IntToStr(FClient.Port));
end;

procedure TfrmHTTP3Client.OnH3Error(Sender: TObject; const aError: string);
begin
  DoLog('Error: ' + aError);
end;

procedure TfrmHTTP3Client.OnH3Response(Sender: TObject;
  const aResponse: TsgcHTTP3Response);
begin
  DoLog('Status: ' + IntToStr(aResponse.StatusCode));
  memoHeaders.Lines.Assign(aResponse.Headers);
end;

Reading FClient.Host and FClient.Port inside OnConnect is exactly what those two properties are for. They report the connection, they do not configure it.

FQUICClient.pas
uses
  Classes, SysUtils,
  // sgc
  sgcIdSSLOpenSSLHeaders;

procedure TfrmQUICClient.FormCreate(Sender: TObject);
begin
  DoLog('OpenSSL QUIC Support:');
  DoLog('  quictls API: ' +
    BoolToStr(IsOpenSSL_QUIC_TLS_Available, True));
  DoLog('  Builtin QUIC (3.2+): ' +
    BoolToStr(IsOpenSSL_QUIC_Available, True));
end;

Run this once before anything else. If both report false, the OpenSSL beside your executable has no QUIC, and every connection failure after that is a symptom of this one fact rather than of the network.

The first two tabs come from the shipped demo Demos\22.QUIC_Protocol\03.HTTP3_Client\FHTTP3Client.pas, with the form controls replaced by literals. The third is the runtime availability check from 01.QUIC_Client\FQUICClient.pas. There are six QUIC demos in that folder, including a WebTransport pair.

Read the status code, not just the body

Get returns the body. The response object carries everything else, and it arrives on its own event.

The return value

Get returns the response body as a string and raises on failure, so the demo wraps it in a try except. A body of the length you expect is the first proof.

OnResponse

procedure(Sender: TObject; const aResponse: TsgcHTTP3Response). StatusCode is the number you actually want, Headers is a TStringList, and GetDataAsString gives you the body again from the response object.

OnConnect

A plain TNotifyEvent. Firing at all means QUIC negotiated and the HTTP/3 session opened, which is the part most likely to fail on a first run.

Before you blame the code

IsOpenSSL_QUIC_Available answers the only question worth asking first. QUIC also runs over UDP 443, and a network that allows TCP 443 does not necessarily allow that.

What usually goes wrong the first time

Six problems account for nearly every failed first request.

Cannot assign to Host or Port

They are read-only on TsgcHTTP3Client, declared as property Host: string read FHost and property Port: Integer read FPort. They report where the client is connected. To choose a target, pass a full URL to Get or call Connect(aHost, aPort).

QUIC is not available

The OpenSSL you loaded is too old or was built without QUIC. The client needs 3.2 or later, or quictls. Check at runtime with IsOpenSSL_QUIC_Available before blaming the network.

The server will not start

The QUIC server needs OpenSSL 3.5 or later, because it calls SSL_new_listener. A 3.2 build is enough for the client and not for the server, and the error message says so explicitly.

Wrong arity on OnConnect

OnConnect and OnDisconnect on this component are plain TNotifyEvent, so the handler takes only Sender: TObject. They do not hand you a connection object, unlike the WebSocket components.

UDP is blocked

QUIC runs over UDP on port 443, and plenty of corporate networks allow TCP 443 and drop UDP 443. If a browser can reach the host over HTTP/3 and your application cannot, suspect the firewall before the code.

The palette page is missing

SGC_PACK_QUIC is defined only on line 872, inside the All-Access block. It also requires SGC_INDY_LIB, because the whole pack block on lines 894 to 899 sits inside that guard.

Beyond the first request

Four directions, all inside the same package.

Run an HTTP/3 server

TsgcHTTP3Server serves HTTP/3 over QUIC directly. Remember the OpenSSL 3.5 floor on the server side.

HTTP/3 server component

Raw QUIC, without HTTP

TsgcQUICClient and TsgcQUICServer give you QUIC streams without the HTTP/3 layer, which is what you want for a custom protocol that needs multiplexing without head of line blocking.

QUIC client and QUIC server

WebTransport

Bidirectional streams and datagrams to a browser over HTTP/3, gated by SGC_WEBTRANSPORT on line 898. Two demos ship.

sgcQUIC features

Discover HTTP/3 from HTTP/2

A server advertises HTTP/3 with an Alt-Svc header. Handle OnAltSvc and you can upgrade an existing connection to QUIC when the origin offers it.

HTTP/2 client

Reference, demos and documentation

Demo projects ship inside the download, under Demos\22.QUIC_Protocol. There are six of them.

HTTP/3 client component What TsgcHTTP3Client exposes, property by property.
HTTP/3 server component The server side, including the OpenSSL 3.5 requirement.
QUIC client component Raw QUIC streams without the HTTP/3 layer.
sgcQUIC features QPACK, 0-RTT, connection migration, WebTransport and the rest.
Download the trial One installer per IDE version, with the QUIC components already inside.
Online help The generated reference, always in step with the current release.

Related reading: the QUIC client and server components and the HTTP/3 components. If you are choosing between transports, the real-time transport guide compares them. Every product has its own quick start, listed on the getting started page.

sgcQUIC quick start questions

TsgcHTTP3Client, from the unit sgcQUIC, on the SGC QUIC palette page. Add sgcHTTP3_Classes for TsgcHTTP3Response, which is the parameter type of OnResponse, and sgcHTTP_AltSvc if you handle OnAltSvc. The palette page also carries TsgcQUICClient, TsgcQUICServer and TsgcHTTP3Server.
It depends which side you are building. The client needs the QUIC API added in OpenSSL 3.2, or a quictls build, and the library says so in the message it raises: QUIC is not available. Requires quictls/openssl or OpenSSL 3.2+. The server needs 3.5 or later, because it calls SSL_new_listener, and its error message names that version explicitly. Ship libcrypto-3.dll and libssl-3.dll beside your executable. msquic is not used.
Because they are read-only. TsgcHTTP3Client declares them as property Host: string read FHost and property Port: Integer read FPort, so they report the current connection rather than configure it. Pass a full URL to Get, Post, Put or Delete, or call Connect(const aHost: string; aPort: Integer = 443) first.
A plain TNotifyEvent, so procedure(Sender: TObject). Same for OnDisconnect. This differs from the WebSocket components, whose events hand you a TsgcWSConnection, and it is a common source of a first compile error. OnResponse is procedure(Sender: TObject; const aResponse: TsgcHTTP3Response) and OnError is procedure(Sender: TObject; const aError: string).
From the response object on OnResponse. TsgcHTTP3Response exposes StatusCode, Headers as a TStringList, and GetDataAsString for the body. The Get method itself returns only the body as a string, which is why the demo wires OnResponse as well.
SGC_PACK_QUIC is defined on line 872 of sgcVer.inc, inside the {$IFDEF SGC_EDT_ALL} block that runs from line 870 to line 874. So All-Access. The pack block itself, lines 894 to 899, also sits inside an {$IFDEF SGC_INDY_LIB}, so the custom Indy library has to be part of the build as well. Inside that block, SGC_QUIC is line 896, SGC_HTTP3 is line 897 and SGC_WEBTRANSPORT is line 898.
No. The trial installer is per IDE version and already contains the QUIC and HTTP/3 components, and there is no QUIC-specific package file. Install sgcWebSockets and the SGC QUIC palette page appears when the edition enables it.
Yes, and you should. IsOpenSSL_QUIC_Available returns whether the loaded OpenSSL exposes the QUIC client method, and IsOpenSSL_QUIC_TLS_Available does the same for the QUIC TLS callbacks. The shipped QUIC client demo writes both to its log on startup, which turns a mysterious connection failure into a one-line answer.
Best value: All-AccessEvery eSeGeCe product, Premium Support included, from €1,059/year.
See All-Access pricing

Ready to try HTTP/3 from Delphi?

Download the trial and run the HTTP/3 client demo against a real origin.