QUIC

QUIC ist der UDP-basierte, mit TLS 1.3 verschlüsselte, multiplexte Transport, der in RFC 9000 definiert ist, und der Transport, auf dem HTTP/3 läuft.

Einführung

QUIC ist ein Allzweck-Transportprotokoll, das in drei Kerndokumenten spezifiziert ist:

RFCTitleWas es definiert
RFC 9000QUIC: A UDP-Based Multiplexed and Secure TransportDer Transport selbst: Pakete, Frames, Streams, Connection-IDs, Flusssteuerung, Verbindungslebenszyklus und Migration.
RFC 9001TLS zur Absicherung von QUIC verwendenWie TLS 1.3 in den Handshake eingebettet ist, der Key Schedule, der Paketschutz und der Header-Schutz.
RFC 9002QUIC-Verlusterkennung und ÜberlastungssteuerungBestätigungen, RTT-Schätzung, Verlusterkennung, Probe-Timeouts und der Congestion-Controller.

Warum es QUIC gibt

QUIC wurde entworfen, um Probleme zu beheben, die in TCP plus TLS struktureller Natur sind und die sich nicht beheben lassen, ohne den Transport selbst zu ändern.

QUIC-Engines

Diese Bibliothek enthält zwei QUIC-Engines. Sie werden durch die Aufzählung TsgcQUICMode dargestellt, die in sgcSSL_QUIC.pas deklariert ist:

TsgcQUICMode = (qmCustom, qmBuiltin);

Die Engine wird nicht direkt gesetzt. Sie folgt dem TLS-IOHandler der QUIC-Optionen (SSLQUICOptions beim QUIC-Client, TLSOptions beim HTTP/3-Client, QUICOptions.SSLOptions bei den Servern):

Mit den mit der Bibliothek ausgelieferten OpenSSL-Binärdateien erhalten Sie qmBuiltin. Die Server melden die Engine, mit der sie gestartet wurden, in der schreibgeschützten Eigenschaft QUICMode.

Mit iohNativeTLS schützt die sgc-Engine Pakete nativ, ohne jedes Define: Paket- und Header-Schutz (AES-128-GCM, AES-256-GCM, ChaCha20-Poly1305), Schlüsselableitung, Schlüsselaktualisierung und das Retry-Integritätstag laufen auf sgcCrypto statt auf OpenSSL, pro Verbindung. Zusammen mit dem nativen TLS-1.3-Handshake laufen QUIC und HTTP/3 vollständig ohne OpenSSL, siehe SSL ohne OpenSSL. Mit iohOpenSSL folgt der Paketschutz sgcCryptoBackend (oder dem Define SGC_NATIVE_CRYPTO).

Engine-Vergleich

Die folgende Tabelle spiegelt wider, was der Code tatsächlich tut, nicht wozu QUIC prinzipiell in der Lage ist.

 qmBuiltinqmCustom
Transport-ImplementierungNatives QUIC von OpenSSL 3.5+. Die Bibliothek steuert es über OSSL_QUIC_client_method, OSSL_QUIC_server_method, SSL_new_listener und SSL_accept_connection.Pascal-QUIC-Stack innerhalb der Bibliothek (die sgcQUIC_*-Units: Pakete, Frames, Überlastkontrolle, Flusssteuerung), wobei der TLS 1.3-Handshake über die OpenSSL-3.5-QUIC-TLS-API oder die native TLS-Engine abgewickelt wird.
Ausgewählt, wennIOHandler = iohOpenSSL und OpenSSL exportiert den integrierten QUIC-Stack. Das ist der Standard bei mainline OpenSSL 3.5+.IOHandler = iohNativeTLS, oder OpenSSL exportiert die QUIC-TLS-API, aber nicht den integrierten QUIC-Stack.
Verwendet von TsgcQUICClient und TsgcHTTP3ClientJaJa
Verwendet von TsgcQUICServer und TsgcHTTP3ServerJaJa. Ein leeres Host lauscht dual-stack, auf IPv4 und IPv6, wie bei der integrierten Engine.
Mehrere Streams pro Verbindung über die Komponenten-APIJa. OpenStream wird auf SSL_new_stream abgebildet, und vom Peer geöffnete Streams können eingesammelt werden.Ja. OpenStream, WriteStreamData und ConcludeStream funktionieren mit Flusssteuerung und erneuter Übertragung je Stream. Der HTTP/3-Client öffnet seine Control- und QPACK-Streams und verwendet für spätere Anfragen dieselbe QUIC-Verbindung erneut.
WebTransportJa. WebTransport benötigt mehrere Streams pro Verbindung, daher benötigt es diese Engine.Nein.

Kurz gesagt: qmBuiltin ist die Standard-Engine bei mainline OpenSSL 3.5+. qmCustom ist der Fallback, wenn der integrierte Stack nicht verfügbar ist, und die Engine, die bei iohNativeTLS verwendet wird.

Streams

Eine QUIC-Verbindung ist ein Container für Streams. Ein Stream ist eine geordnete, flussgesteuerte Folge von Bytes, und Streams sind voneinander unabhängig, was das Head-of-Line-Blocking auf Transportebene beseitigt. Streams gibt es in zwei Ausprägungen:

Jeder Stream hat eine 62-Bit-Stream-ID. Die beiden niedrigstwertigen Bits der ID kodieren, wer den Stream geöffnet hat und ob er bidirektional ist, und genau dieses Layout bildet die Bibliothek in TsgcQUICStreamTypes (sgcQUIC_Types.pas) ab:

Untere 2 BitsAufzählungswertBedeutung
0x00qcstClientBidirectionalClient-initiiert, bidirektional
0x01qcstServerBidirectionalServer-initiiert, bidirektional
0x02qcstClientUnidirectionalClient-initiiert, unidirektional
0x03qcstServerUnidirectionalVom Server initiiert, unidirektional

Stream-IDs werden innerhalb jeder dieser vier Kategorien in aufsteigender Reihenfolge vergeben, und wie viele Streams ein Peer öffnen darf, wird durch die während des Handshakes ausgetauschten Transportparameter begrenzt. Die QUIC-Komponenten stellen von Haus aus einen einzigen Standard-Stream bereit. Zusätzliche Streams müssen über EnableMultiStream und OpenStream aktiviert werden, und wie oben erwähnt erfordern sie die builtin-Engine.

Komponenten

KomponenteBeschreibung
TsgcQUICClientLow-Level-QUIC-Client. Öffnet eine QUIC-Verbindung zu einem Server und tauscht rohe Anwendungsbytes über QUIC-Streams aus. Verwenden Sie ihn, wenn Sie QUIC als Transport ohne HTTP-Semantik nutzen möchten.
TsgcQUICServerLow-Level-QUIC-Server. Lauscht auf QUIC-Verbindungen und tauscht rohe Anwendungsbytes über QUIC-Streams aus.

Die HTTP/3-Komponenten bauen auf dieser Schicht auf. TsgcHTTP3Server leitet sich direkt von TsgcQUICServer ab und ergänzt das HTTP/3-Framing, die Einstellungen und die Anfrageverarbeitung auf den QUIC-Verbindungen, die sein Vorfahr annimmt. TsgcHTTP3Client ist eine eigenständige Komponente und kein Nachfahre von TsgcQUICClient, steuert aber denselben zugrunde liegenden QUIC-Transport. Siehe HTTP/3.

Schnellstart

Der Client verbindet sich mit einem Host und einem Port und schreibt dann Anwendungsbytes auf den Standard-Stream. Die vollständige Dokumentation zu Eigenschaften, Methoden und Ereignissen finden Sie auf den Komponentenseiten.

oClient := TsgcQUICClient.Create(nil);
oClient.Host := 'localhost';
oClient.Port := 4433;
oClient.OnQUICConnect := OnQUICConnectHandler;
if oClient.Connect(10000) then
  oClient.WriteData('hello over QUIC');

Der Server bindet einen UDP-Port, lädt ein Zertifikat und einen Schlüssel und löst dann für jeden empfangenen Puffer ein Ereignis aus.

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 läuft immer über UDP, stellen Sie daher sicher, dass der UDP-Port auf jeder Firewall im Pfad geöffnet ist. Ein Port, der für TCP funktioniert, ist nicht automatisch über UDP erreichbar.

Der Listener bindet die Adresse in Host. Ein Wert, der einen Doppelpunkt enthält, ist ein IPv6-Literal, und jeder andere Wert, ein IPv4-Literal oder ein Hostname, wird über beide Adressfamilien aufgelöst. Ein leeres Host bindet den Dual-Stack-Platzhalter, sodass ein einziger Listener IPv4- und IPv6-Gegenstellen auf demselben Port bedient.

Anforderungen

VoraussetzungDetail
EditionAll-Access. Das Paket SGC_PACK_HTTP3, das QUIC aktiviert, befindet sich im Block SGC_EDT_ALL von sgcVer.inc.
Pack-DefineSGC_PACK_HTTP3. Es wird in sgcVer.inc auskommentiert ausgeliefert und muss daher ausdrücklich aktiviert werden, bevor QUIC einkompiliert wird.
Feature-DefinesWenn SGC_PACK_HTTP3 und SGC_INDY_LIB beide aktiviert sind, definiert sgcVer.inc die Symbole SGC_QUIC, SGC_HTTP3 und SGC_WEBTRANSPORT. Die QUIC-Komponenten werden unter SGC_QUIC kompiliert.
IndySGC_INDY_LIB ist erforderlich. Die QUIC-Defines sind in einen SGC_INDY_LIB-Guard eingebettet, sodass QUIC in einem Indy-freien Build nicht verfügbar ist.
OpenSSLOpenSSL 3.5 oder neuer. Die Serverkomponenten benötigen den nativen QUIC-Listener (SSL_new_listener, SSL_accept_connection), der in 3.5 eingeführt wurde, und lösen bei älteren Versionen eine Ausnahme aus.
UnitssgcHTTP.pas deklariert die Komponenten. Der Transport befindet sich in den sgcQUIC_*-Units und die OpenSSL-Anbindung in sgcSSL_QUIC.pas.

Um QUIC zu aktivieren, bearbeiten Sie sgcVer.inc und entfernen Sie die Kommentarzeichen des Pack-Defines im All-Access-Block:

{$IFDEF SGC_EDT_ALL} { ALL-ACCESS }
  {$DEFINE SGC_PACK_HTML}
  {$DEFINE SGC_PACK_HTTP3}
  {.$DEFINE SGC_PACK_WEBRTC}
{$ENDIF}

Siehe auch