TsgcQUICClient ist ein Low-Level-QUIC-Transport-Client (RFC 9000), der eine QUIC-Verbindung über UDP zu einem Server öffnet und rohe Bytes über QUIC-Streams austauscht.
QUIC ist das in RFC 9000 standardisierte Transportprotokoll, mit seiner TLS-Integration in RFC 9001 und seiner Verlusterkennung und Überlastkontrolle in RFC 9002. Es ist der Transport, auf dem HTTP/3 läuft. Im Vergleich zu TCP plus TLS ändert es vier Dinge, die für eine Anwendung von Bedeutung sind:
TsgcQUICClient stellt diesen Transport direkt bereit, als rohe Bytes auf Streams. Er spricht nicht HTTP/3 oder ein anderes Anwendungsprotokoll: Verwenden Sie TsgcHTTP3Client, wenn Sie HTTP-Semantik über QUIC wünschen. Greifen Sie zu TsgcQUICClient, wenn Sie Ihr eigenes Protokoll über QUIC transportieren oder einen QUIC-Endpunkt testen.
Wissenswertes zum Umfang, bevor Sie beginnen. Die Komponente ist ein schlanker Client über dem QUIC-Transport, und zwei ihrer Einschränkungen überraschen häufig:
h3 gesetzt und kann nicht konfiguriert werden. 0-RTT-Wiederaufnahme und Verbindungsmigration sind in der zugrunde liegenden Transportklasse implementiert, werden aber nicht als Eigenschaften an dieser Komponente bereitgestellt.
Die Bibliothek kann QUIC über eine von zwei Engines betreiben. Die Engine folgt SSLQUICOptions.IOHandler:
iohOpenSSL (Standard) wird, wenn der Build OSSL_QUIC_client_method exportiert, die integrierte Engine (qmBuiltin) verwendet. Hier besitzt OpenSSL 3.5 oder höher die gesamte QUIC-Verbindung und die Bibliothek steuert sie über die OpenSSL-Stream-API.SSL_set_quic_tls_cbs), die sgc-Engine (qmCustom) als Fallback verwendet. Dies ist der reine Pascal-QUIC-Stack in den sgcQUIC_*-Units: Er erstellt und parst die Pakete selbst und verwendet OpenSSL nur, um den TLS 1.3-Handshake zu steuern. Ist keine der beiden APIs verfügbar, etwa bei einem quictls-Build, löst die Initialisierung der Verbindung eine Ausnahme aus.iohNativeTLS wird immer die sgc-Engine verwendet, und der TLS 1.3-Handshake läuft auf der nativen TLS-Engine, mit Session-Tickets, Wiederaufnahme und 0-RTT. Der Paketschutz ist ebenfalls nativ, sodass OpenSSL überhaupt nicht geladen wird.
Mit den mit den Demos gebündelten OpenSSL-3.5-Binärdateien wird qmBuiltin ausgewählt. IsOpenSSL_QUIC_Available und IsOpenSSL_QUIC_TLS_Available, deklariert in sgcIdSSLOpenSSLHeaders, melden, welche der beiden OpenSSL-APIs die geladene Bibliothek exportiert. Die Engines im Vergleich:
| Funktion | qmBuiltin (OpenSSL 3.5+) | qmCustom (sgc-Engine) |
|---|---|---|
| Verbinden, trennen, auf dem Standard-Stream senden | Ja | Ja |
| EnableMultiStream | Ja | Ja |
| OpenStream / WriteStreamData | Ja | Ja |
| OnQUICStreamData (der Lesepfad) | Ja, nach EnableMultiStream | Ja, nach EnableMultiStream |
| Feinabstimmung von QUICOptions.Settings | Größtenteils von OpenSSL ausgehandelt, Werte werden nicht herangezogen | Ja, treibt den Pascal-Stack an |
Siehe QUIC für den vollständigen Engine-Vergleich.
1. Erstellen Sie einen TsgcQUICClient und setzen Sie Host und Port des QUIC-Servers. QUIC läuft über UDP, Port ist daher ein UDP-Port, üblicherweise 443.
2. Konfigurieren Sie TLS über SSLQUICOptions. Die Überprüfung ist standardmäßig deaktiviert, aktivieren Sie daher VerifyCertificate und lassen Sie RootCertFile auf Ihre CA zeigen, außer bei einem reinen Wegwerftest. Bei aktivierter Überprüfung muss das Zertifikat jetzt zusätzlich zum Host passen, mit dem Sie sich verbinden, sodass ein selbstsigniertes Zertifikat oder eines, das für einen anderen Namen ausgestellt wurde, abgelehnt wird, wo es früher akzeptiert wurde.
3. Behandeln Sie OnQUICConnect, OnQUICDisconnect und OnQUICException. Die Behandlung von OnQUICException ist nicht optional: Verbindungsfehler lösen keine Ausnahme aus, sie werden dort gemeldet, und Connect gibt lediglich False zurück.
4. Öffnen Sie die Verbindung mit Connect (blockierend, gibt ein Ergebnis zurück) oder Start / Active := True (nicht-blockierend).
5. Wenn Sie etwas empfangen müssen, rufen Sie nach dem Verbinden EnableMultiStream auf und behandeln Sie OnQUICStreamData. Zum Senden verwenden Sie WriteData für den Standard-Stream oder OpenStream plus WriteStreamData für einen bestimmten.
oClient := TsgcQUICClient.Create(nil);
oClient.Host := 'www.example.com';
oClient.Port := 443;
oClient.SSLQUICOptions.VerifyCertificate := True;
oClient.SSLQUICOptions.RootCertFile := 'ca.pem';
oClient.SSLQUICOptions.ConnectTimeout := 10000;
oClient.OnQUICConnect := OnQUICConnectEvent;
oClient.OnQUICStreamData := OnQUICStreamDataEvent;
oClient.OnQUICException := OnQUICExceptionEvent;
if oClient.Connect then
oClient.WriteData('Hello QUIC');
| Name | Beschreibung |
|---|---|
| Active | Öffnet oder schließt die QUIC-Verbindung zum Remote-Server. |
| Host | IP-Adresse oder DNS-Name des QUIC-Servers, mit dem sich der Client verbindet. |
| Port | UDP-Port, der für die Verbindung zum QUIC-Server verwendet wird. |
| SSLQUICOptions | TLS 1.3-Einstellungen: Zertifikate, Peer-Überprüfung und das Handshake-Timeout. Die Überprüfung ist standardmäßig deaktiviert. |
| QUICOptions | Transport-Feinabstimmung für die QUIC-Verbindung, etwa PMTU und Flusssteuerungsfenster. |
| Name | Beschreibung |
|---|---|
| Connect | Öffnet die QUIC-Verbindung und wartet, bis sie hergestellt ist oder das Zeitlimit abläuft. |
| Disconnect | Schließt die QUIC-Verbindung zum Server. |
| WriteData | Sendet Daten an den Server über den Standard-QUIC-Stream. |
| EnableMultiStream | Schaltet die aktive Verbindung in den Multi-Stream-Modus. Erforderlich, bevor überhaupt etwas empfangen werden kann. |
| OpenStream | Öffnet einen neuen, vom Client initiierten QUIC-Stream und gibt ihn zurück, oder nil bei einem Fehler. |
| Name | Beschreibung |
|---|---|
| OnQUICConnect | Wird ausgelöst, sobald die QUIC-Verbindung hergestellt wurde und der Client bereit ist, Daten zu senden. |
| OnQUICStreamData | Wird mit den aus einem QUIC-Stream gelesenen Bytes ausgelöst. Ohne Wirkung, bis EnableMultiStream aufgerufen wurde. |
| OnQUICDisconnect | Wird ausgelöst, wenn die QUIC-Verbindung geschlossen wurde, und meldet den Close-Code und den Grund. |
| OnQUICException | Wird ausgelöst, wenn auf der QUIC-Verbindung eine Ausnahme ausgelöst wird. Die einzige Möglichkeit zu erfahren, warum ein Verbindungsversuch fehlgeschlagen ist. |
Verbinden Sie sich mit einem QUIC-Server, aktivieren Sie Multi-Stream, damit Daten empfangen werden können, senden Sie eine Nachricht auf einem dedizierten Stream und protokollieren Sie alles, was zurückkommt.
uses
sgcHTTP, sgcQUIC_Client, sgcQUIC_Classes;
procedure TForm1.Start;
begin
FClient := TsgcQUICClient.Create(nil);
FClient.Host := 'www.example.com';
FClient.Port := 443;
// TLS 1.3. Verification is off by default, turn it on.
FClient.SSLQUICOptions.VerifyCertificate := True;
FClient.SSLQUICOptions.RootCertFile := 'ca.pem';
// the handshake timeout, 10000 ms is already the default
FClient.SSLQUICOptions.ConnectTimeout := 10000;
FClient.OnQUICConnect := OnQUICConnectEvent;
FClient.OnQUICStreamData := OnQUICStreamDataEvent;
FClient.OnQUICDisconnect := OnQUICDisconnectEvent;
FClient.OnQUICException := OnQUICExceptionEvent;
// Connect returns False without raising, the reason arrives on OnQUICException
if not FClient.Connect(10000) then
Log('connect failed');
end;
procedure TForm1.OnQUICConnectEvent(Sender: TObject; const Connection: TsgcQUICConnection);
var
oStream: TsgcQUICClientStream;
begin
Log('connected');
// required before OnQUICStreamData will ever fire
FClient.EnableMultiStream;
oStream := FClient.OpenStream; // bidirectional
if Assigned(oStream) then
FClient.WriteStreamData(oStream, TEncoding.UTF8.GetBytes('Hello QUIC'))
else
Log('could not open a stream, is the builtin OpenSSL QUIC engine active?');
end;
procedure TForm1.OnQUICStreamDataEvent(Sender: TObject; aStreamId: Int64;
aUnidirectional: Boolean; const aData: TBytes);
begin
// raised on the read thread, do not touch the UI directly here
Log(Format('stream %d: %d bytes', [aStreamId, Length(aData)]));
end;
procedure TForm1.OnQUICDisconnectEvent(Sender: TObject; const Connection: TsgcQUICConnection;
Code: Int64; const Reason: string);
begin
if Code = 0 then
Log('closed cleanly')
else
Log(Format('closed, code %d: %s', [Code, Reason]));
end;
procedure TForm1.OnQUICExceptionEvent(Sender: TObject; const Connection: TsgcQUICConnection;
const E: Exception);
begin
Log('error: ' + E.Message);
end;
{$DEFINE SGC_PACK_HTTP3} innerhalb des {$IFDEF SGC_EDT_ALL}-Blocks auf, ausgeliefert wird es als {.$DEFINE SGC_PACK_HTTP3}. Das Aktivieren definiert SGC_QUIC, SGC_HTTP3 und SGC_WEBTRANSPORT, erstellen Sie danach die Packages neu.{$IFDEF SGC_INDY_LIB} verschachtelt, daher ist der SGC_INDY_LIB-Build erforderlich.TsgcQUICClient selbst hinzu, sgcQUIC_Client für TsgcQUICClientStream und sgcQUIC_Classes für TsgcQUICConnection in den Ereignishandlern.libssl-3 und libcrypto-3) für die eingebaute QUIC-Engine, die die ausgelieferte Engine ist. Frühere OpenSSL-3.x-Builds haben keine QUIC-Unterstützung und lösen eine Ausnahme aus, wenn die Verbindung initialisiert wird.PlatformNotSupportedException aus.