TsgcHTTP3Client ist ein HTTP/3-Client (RFC 9114), der Anfragen über QUIC sendet und die zehn HTTP-Verben synchron und asynchron, Server Push, Extended-CONNECT-Tunnel und WebTransport unterstützt.
HTTP/3 (RFC 9114) ist die dritte Hauptrevision von HTTP. Es behält die Semantik bei, die Sie bereits kennen, dieselben Verben, Statuscodes und Header, ersetzt aber den darunterliegenden Transport. Statt über TCP läuft es über QUIC (RFC 9000), einen UDP-basierten Transport mit in den Handshake integriertem TLS 1.3.
Diese Änderung bringt drei Vorteile. Verschlüsselung ist verpflichtend, es gibt also kein HTTP/3 im Klartext und keine TLS-Version, die ausgehandelt werden müsste. Streams sind unabhängig, ein verlorenes Paket einer Anfrage hält also nicht mehr alle anderen Anfragen auf, die sich die Verbindung teilen, was das Head-of-Line-Blocking ist, das HTTP/2 einschränkt. Und der Handshake ist kürzer, sodass eine Verbindung schneller nutzbar ist.
Zwei praktische Konsequenzen sollten Sie kennen, bevor Sie beginnen. HTTP/3 läuft über UDP, sodass eine Firewall, die nur TCP 443 öffnet, es blockiert, obwohl die Portnummer dieselbe wie bei HTTPS ist. Und Header werden mit QPACK (RFC 9204) statt mit HPACK komprimiert, was die Komponente für Sie erledigt und über QPACKDynamicInserts meldet.
HTTP-Verben
Der Client implementiert die zehn unten aufgeführten Verben. Die Liste ist vollständig: Es ist die Aufzählung Th3Methods, die die Komponente in den Pseudo-Header :method jeder Anfrage codiert.
| Verb | Synchron | Asynchron | Stream-Überladung | Standard aContentType | Typische Verwendung |
|---|---|---|---|---|---|
GET | Get | GetAsync | Get(aURL, aResponseContent) | — | Ruft eine Ressource ab. Kein Request-Body. |
POST | Post | PostAsync | Post(aURL, aSource, aResponseContent) | application/x-www-form-urlencoded | Daten übermitteln, typischerweise um einen Datensatz zu erstellen oder eine Aktion auszulösen. |
PUT | Put | PutAsync | Put(aURL, aSource, aResponseContent) | application/json | Ersetzt die Zielressource. Idempotent. |
DELETE | Delete | DeleteAsync | Delete(aURL, aResponseContent) | — | Entfernt die Zielressource. Kein Anfrage-Body. |
HEAD | Head | — | — | — | Liest nur Status und Header, keinen Body. Gibt TsgcHTTP3Response zurück. |
PATCH | Patch | PatchAsync | Patch(aURL, aSource, aResponseContent) | application/json | Wendet eine partielle Aktualisierung auf die Zielressource an. |
OPTIONS | RequestOptions | OptionsAsync | RequestOptions(aURL, aResponseContent) | — | Prüfen Sie die Methoden und Fähigkeiten, die eine Ressource unterstützt. |
TRACE | Trace | TraceAsync | Trace(aURL, aResponseContent) | — | Loop-back-Diagnose. Von Servern häufig deaktiviert. |
CONNECT | Connect | ConnectAsync | — | — | Fordern Sie einen Tunnel an. Siehe ExtendedConnect für einen Tunnel, der offen bleibt. |
QUERY | Query | QueryAsync | Query(aURL, aSource, aResponseContent) | application/x-www-form-urlencoded | Sichere, idempotente Anfrage, die einen Body mitführt. Eignet sich für Such- und Filter-Payloads. |
Zwei Benennungen sind erwähnenswert. OPTIONS wird von der Methode mit dem Namen RequestOptions gesendet, weil Options mit einem bestehenden Bezeichner in Delphi und C++Builder kollidiert; ihr asynchrones Gegenstück behält den Protokollnamen und heißt OptionsAsync. CONNECT wird von der Überladung Connect(aURL) gesendet, die eine andere Methode ist als die Überladung Connect(aHost, aPort), welche die QUIC-Verbindung öffnet.
HEAD ist die Ausnahme von diesem Muster: Es gibt ein TsgcHTTP3Response statt einer Zeichenfolge zurück und hat weder eine asynchrone Variante noch eine Stream-Überladung. Jedes andere Verb außer CONNECT hat zusätzlich eine Stream-Überladung, die den Anfrage-Body aus einem Stream liest und den Antwort-Body in einen solchen schreibt, was den Aufbau großer Zeichenfolgen im Speicher vermeidet. Stream-Überladungen senden immer content-type: application/octet-stream.
Synchrone und asynchrone Anfragen
Jedes Verb kann auf zwei Arten gesendet werden. Ein synchroner Aufruf blockiert den aufrufenden Thread, bis die Antwort vollständig ist, und gibt den Body direkt zurück, begrenzt durch ReadTimeout. Ein asynchroner Aufruf kehrt sofort zurück, führt die Anfrage auf einem Hintergrund-Thread aus und liefert die Antwort über OnResponse.
Wenn Sie die asynchronen Methoden verwenden, behandeln Sie immer OnError: Eine Anfrage im Hintergrund hat keinen Aufrufer, zu dem hin ausgelöst werden könnte, ohne diesen Handler bleibt ein Fehler also stumm. Denken Sie außerdem daran, dass die Ereignisse im Hintergrund-Worker-Thread ausgelöst werden, synchronisieren Sie daher, bevor Sie VCL- oder FMX-Steuerelemente ansprechen.
Befolgen Sie die folgenden Schritte, um eine synchrone Anfrage zu senden:
1. Erstellen Sie eine neue Instanz von TsgcHTTP3Client. Es gibt keinen Host zuzuweisen: Host und Port sind schreibgeschützt und melden die aktuelle Verbindung.
2. Rufen Sie eine Verb-Methode mit einer absoluten URL auf. Die Verbindung wird bei Bedarf anhand dieser URL geöffnet, ein vorheriger Aufruf von Connect ist daher optional. Rufen Sie Disconnect auf, wenn Sie fertig sind. Beispiel:
oClient := TsgcHTTP3Client.Create(nil);
try
vResponse := oClient.Get('https://www.example.com/');
ShowMessage(vResponse);
oClient.Disconnect;
finally
oClient.Free;
end;
Um die Anfrage asynchron zu senden, weisen Sie OnResponse zu und rufen GetAsync auf; der Aufruf kehrt sofort zurück und die Antwort wird über das Ereignis geliefert. Dekodieren Sie den Body mit GetDataAsString, das den Zeichensatz im Header content-type berücksichtigt. Beispiel:
oClient := TsgcHTTP3Client.Create(nil);
oClient.OnResponse := OnResponseEvent;
oClient.OnError := OnErrorEvent;
oClient.GetAsync('https://www.example.com/');
procedure TfrmMain.OnResponseEvent(Sender: TObject;
const aResponse: TsgcHTTP3Response);
begin
// fires on the background thread
Log(aResponse.GetDataAsString);
end;
Server Push
Server Push ermöglicht es dem Server, eine Ressource zu senden, die der Client nie angefordert hat, in der Erwartung, dass sie benötigt wird (RFC 9114 Abschnitt 4.6). Push ist deaktiviert, bis der Client dies durch den Aufruf von SetMaxPushId aktiviert, womit er die höchste Push-ID ankündigt, die er akzeptieren wird.
Jeder Push trifft dann in zwei Stufen ein. OnPushPromise wird zuerst mit den Anfrage-Headern ausgelöst, die der Push beantworten wird, das ist der Moment, ihn mit CancelPush abzulehnen, wenn die Ressource bereits zwischengespeichert ist. Wird er nicht abgelehnt, trifft die vollständige gepushte Antwort mit derselben Push-ID auf OnPush ein.
Extended CONNECT und WebTransport
Extended CONNECT (RFC 9220, das HTTP/3-Gegenstück zu RFC 8441) verwandelt einen Anfrage-Stream in einen bidirektionalen Tunnel für ein anderes Protokoll. Rufen Sie ExtendedConnect mit dem Protokoll-Token auf; es gibt bei einem 2xx-Status True zurück und hält den Stream offen. Schreiben Sie mit TunnelSend in den Tunnel und lesen Sie daraus, indem Sie TunnelPump aufrufen, das eingehende Bytes über OnTunnelData bereitstellt. Der Server muss SETTINGS_ENABLE_CONNECT_PROTOCOL ankündigen, andernfalls gibt der Aufruf False zurück.
WebTransport ist dieser Mechanismus mit dem Protokoll-Token webtransport und einem Sitzungsobjekt darauf. WebTransportConnect führt den Handshake durch und gibt eine TsgcWebTransportSession zurück, und ServerSupportsWebTransport teilt Ihnen im Voraus mit, ob der Server dies unterstützt. Vom Peer geöffnete Streams werden nur eingesammelt, während WebTransportProcessEvents aufgerufen wird.
WebTransport-Sitzungen übertragen nur Streams. Datagramme erfordern die QUIC-DATAGRAM-Erweiterung aus RFC 9221, die die mit der Bibliothek ausgelieferte integrierte QUIC-Engine von OpenSSL 3.5 nicht bereitstellt. Das Senden eines Datagramms löst daher TsgcQUICException aus, statt die Payload stillschweigend zu verlieren.
| Name | Beschreibung |
|---|---|
| ConnectTimeout | Maximale Zeit in Millisekunden, die der Client auf den Aufbau der QUIC-Verbindung wartet. Standard 10000. |
| ReadTimeout | Maximale Zeit in Millisekunden, die der Client auf das Eintreffen einer Antwort wartet. Standard 30000. |
| MaxResponseSize | Maximale Anzahl an Antwort-Body-Bytes, die pro Anfrage akzeptiert werden. Standard 0, das bedeutet unbegrenzt. |
| TLSOptions | TLS 1.3-Parameter (Zertifikate, Überprüfung, OpenSSL), die von der QUIC-Verbindung verwendet werden. |
| Settings | HTTP/3-SETTINGS-Parameter, die dem Server beim Verbindungsaufbau angekündigt werden. |
| PeerSettings | HTTP/3-SETTINGS-Parameter, die vom Server empfangen wurden. Schreibgeschützt. |
| Name | Beschreibung |
|---|---|
| Get | Sendet eine synchrone HTTP/3-GET-Anfrage und gibt den Body als Zeichenfolge zurück oder schreibt ihn in einen Stream. |
| Post | Sendet eine synchrone HTTP/3-POST-Anfrage mit einem Anfrage-Body. |
| Query | Sendet eine synchrone HTTP/3-QUERY-Anfrage, ein sicheres und idempotentes Verb, das einen Body überträgt. |
| GetAsync | Sendet eine nicht-blockierende GET-Anfrage; die Antwort trifft über OnResponse ein. |
| ExtendedConnect | Führt einen Extended-CONNECT-Handshake (RFC 9220) durch und hält den Anfrage-Stream als Tunnel offen. |
| Disconnect | Schließt die HTTP/3-Verbindung und den zugrunde liegenden QUIC-Transport. |
| Name | Beschreibung |
|---|---|
| OnResponse | Wird ausgelöst, wenn der Client die vollständige Antwort (Status, Header und Body) vom Server empfängt. |
| OnError | Wird ausgelöst, wenn ein Anfrage- oder Verbindungsfehler auftritt. Unerlässlich bei Verwendung der asynchronen Methoden. |
| OnHTTP3BeforeRequest | Wird ausgelöst, bevor eine Anfrage gesendet wird, der Hook zum Hinzufügen von Autorisierungs- oder benutzerdefinierten Headern. |
| OnConnect | Wird ausgelöst, nachdem die HTTP/3-Verbindung aufgebaut wurde. |
| OnDisconnect | Wird ausgelöst, nachdem die HTTP/3-Verbindung geschlossen wurde. |
Das folgende Beispiel konfiguriert den Client, sendet einen GET, einen POST mit einem JSON-Body und eine QUERY-Anfrage, fügt jeder Anfrage einen Authorization-Header hinzu und schließt die Verbindung.
procedure TfrmMain.Example;
var
oClient: TsgcHTTP3Client;
vResponse: string;
begin
oClient := TsgcHTTP3Client.Create(nil);
try
oClient.ConnectTimeout := 10000;
oClient.ReadTimeout := 30000;
// refuse any response body larger than 10 MB
oClient.MaxResponseSize := 10 * 1024 * 1024;
oClient.TLSOptions.VerifyCertificate := True;
oClient.OnHTTP3BeforeRequest := OnBeforeRequestEvent;
vResponse := oClient.Get('https://www.example.com/');
ShowMessage(vResponse);
vResponse := oClient.Post('https://www.example.com/api/users',
'{"name": "John"}', 'application/json');
ShowMessage(vResponse);
// QUERY: a safe, idempotent request that carries a body
vResponse := oClient.Query('https://www.example.com/api/search',
'{"city": "Madrid"}', 'application/json');
ShowMessage(vResponse);
oClient.Disconnect;
finally
oClient.Free;
end;
end;
procedure TfrmMain.OnBeforeRequestEvent(Sender: TObject; var Headers: TStringList);
begin
// header names must be lowercase in HTTP/3
Headers.Add('authorization: Bearer ' + vToken);
end;
sgcVer.inc definiert ist.{$DEFINE SGC_PACK_HTTP3} in sgcVer.inc aufheben, was wiederum SGC_QUIC, SGC_HTTP3 und SGC_WEBTRANSPORT definiert.SGC_INDY_LIB verschachtelt, daher muss auch die Indy-Bibliothek aktiviert sein. Ohne sie definiert das Pack nichts und die Komponente wird nicht kompiliert.sgcHTTP für TsgcHTTP3Client zur uses-Klausel hinzu und sgcHTTP3_Classes, wenn Sie die Typen TsgcHTTP3Response und TsgcHTTP3Settings benötigen.TLSOptions.IOHandler = iohOpenSSL (Standard). Die Engine wird dann erkannt, wenn OpenSSL geladen wird: zuerst die integrierte OpenSSL-QUIC-Engine, die sgc-QUIC-Engine als Fallback. Mit TLSOptions.IOHandler = iohNativeTLS werden die sgc-Engine, der native TLS-1.3-Handshake und der native Paketschutz verwendet, und OpenSSL wird nicht geladen. Die sgc-Engine öffnet außerdem die HTTP/3-Control- und QPACK-Streams des Clients und verwendet für spätere Anfragen dieselbe QUIC-Verbindung erneut.