TsgcHTTP3Server › Ereignisse › OnH3Request
Wird ausgelöst, wenn eine vollständige HTTP/3-Anfrage empfangen wird, sodass die Anwendung die Antwort füllen kann.
property OnH3Request: TsgcHTTP3RequestEvent;
// TsgcHTTP3RequestEvent = procedure(Sender: TObject; const aConnection: TsgcQUICServerConnection; const aRequest: TsgcHTTP3Request; const aResponse: TsgcHTTP3Response) of object
—
| Name | Typ | Beschreibung |
|---|---|---|
Sender | TObject | Die TsgcHTTP3Server-Instanz, die das Ereignis ausgelöst hat. Casten Sie sie, um PushPromise innerhalb des Handlers aufzurufen. |
aConnection | const TsgcQUICServerConnection | QUIC-Verbindung, über die die Anfrage eingegangen ist. Stellt PeerIP, PeerPort, CreatedAt und den Verbindungszustand bereit. |
aRequest | const TsgcHTTP3Request | Die empfangene Anfrage. Nachfolgend im Detail beschrieben. |
aResponse | const TsgcHTTP3Response | Die zu füllende Antwort. Nachfolgend im Detail beschrieben. Wird vom Server mit auf 200 voreingestelltem StatusCode erstellt. |
OnH3Request wird ausgelöst, sobald der Server eine vollständige HTTP/3-Anfrage empfangen hat: den Header-Abschnitt sowie den Body bei Methoden, die einen tragen (POST, PUT, PATCH und QUERY). Der Server erstellt das Response-Objekt, bevor das Ereignis ausgelöst wird, und sobald der Handler zurückkehrt, kodiert er die Header per QPACK und sendet die Antwort automatisch auf dem Anfrage-Stream. Rufen Sie SendResponse nicht innerhalb dieses Ereignisses auf: Das würde eine zweite Antwort auf denselben Stream setzen.
Da StatusCode auf 200 voreingestellt ist, kann ein Handler, der nur ein leeres 200 zurückgeben muss, die Antwort unverändert lassen, und ein nicht zugewiesenes OnH3Request beantwortet jede Anfrage mit einem leeren 200.
Anfragen werden gemultiplext: Mehrere können auf einer Verbindung gleichzeitig unterwegs sein, jede auf ihrem eigenen QUIC-Stream, und jede löst ihr eigenes Ereignis aus. Das Ereignis wird im Hintergrund-Listener-Thread des Servers ausgelöst, es darf daher nicht direkt auf die UI zugreifen und sollte schnell zurückkehren, da die Verbindung während der Ausführung des Handlers nicht bedient wird.
Zwei Arten von Anfragen erreichen dieses Ereignis nie. Ein CONNECT, das eine WebTransport-Sitzung aufbaut, wird stattdessen an OnWebTransportSession geleitet, und DATA, die auf einem Stream eintreffen, der bereits zu einem Tunnel geworden ist, werden an OnH3TunnelData geleitet. Ein einfaches CONNECT erreicht OnH3Request sehr wohl: Wenn Sie es mit einem 2xx-Status beantworten, wird dessen Stream zu einem Tunnel.
| Member | Typ | Beschreibung |
|---|---|---|
Method | Th3Methods | Anfragemethode: h3meGet, h3meHead, h3mePost, h3mePut, h3meDelete, h3mePatch, h3meOptions, h3meTrace, h3meConnect oder h3meQuery. Eine nicht erkannte Methode wird als h3meGet gemeldet. |
Path | string | Der Pseudo-Header :path, zum Beispiel /api/time. Enthält auch eine eventuelle Query-Zeichenfolge; die Komponente trennt sie nicht für Sie ab. |
Host | string | Der Pseudo-Header :authority, das HTTP/3-Äquivalent des Host-Headers. |
Scheme | string | Der Pseudo-Header :scheme, normalerweise https. |
Protocol | string | Der Pseudo-Header :protocol eines Extended CONNECT (RFC 9220). Leer, wenn nicht vorhanden. |
Data | TBytes | Der Anfrage-Body, aus den DATA-Frames wieder zusammengesetzt. Leer bei Methoden ohne Body. |
ContentType | string | Der content-type-Header, der Einfachheit halber aus Headers herausgezogen. |
UserAgent, Accept, Authorization | string | Die zugehörigen Anfrage-Header, ebenfalls aus Headers herausgezogen. |
Headers | TStringList | Die übrigen regulären Header, ein Eintrag pro Header in der Form name=value mit kleingeschriebenem Namen. Die oben zu eigenen Eigenschaften erhobenen Header werden hier nicht wiederholt. Mehrere cookie-Header werden zu einem einzigen cookie-Eintrag mit "; " zusammengefügt, wie es RFC 9114 verlangt. |
Das Antwortobjekt gehört dem Server: Füllen Sie es, geben Sie es nicht frei und behalten Sie keine Referenz darauf, nachdem der Handler zurückgekehrt ist.
| Member | Typ | Beschreibung |
|---|---|---|
StatusCode | Integer | Der HTTP-Status, der als Pseudo-Header :status gesendet wird. Voreingestellt auf 200. Setzen Sie den Status hier, statt einen :status-Eintrag zu Headers hinzuzufügen, der verworfen würde. |
ContentType | string | Wird als content-type-Header gesendet, wenn er nicht leer ist. Geben Sie für Text den Zeichensatz an, zum Beispiel text/plain; charset=utf-8. |
Data | TBytes | Der Antwort-Body. Kodieren Sie Zeichenfolgen explizit, normalerweise mit TEncoding.UTF8.GetBytes. Lassen Sie ihn für eine Antwort ohne Body leer. |
Headers | TStringList | Zusätzliche Antwort-Header, hinzugefügt in der Form name=value, einer pro Eintrag. Die Namen werden für Sie in Kleinbuchstaben umgewandelt. |
ContentLength | Int64 | Wird beim Senden nicht verwendet. Der content-length-Header wird aus der tatsächlichen Länge von Data berechnet, daher muss diese Eigenschaft nicht gesetzt werden. |
Benutzerdefinierte Header verwenden name=value, nicht name: value. Ein Eintrag ohne = wird als Header gesendet, dessen Name die gesamte Zeichenfolge ist und dessen Wert leer bleibt, was fast nie das ist, was Sie wollen. Die Eigenschaften Names und Values dieser beiden TStringList-Objekte funktionieren wie erwartet, da ihr NameValueSeparator zur =-Form passt, in der die Einträge gespeichert sind. Values['content-type'] ist der empfohlene Zugriff: Die Suche beachtet die Groß-/Kleinschreibung nicht, sodass sie den Header unabhängig von der verwendeten Schreibweise findet. Names[i] gibt den Namen exakt so zurück, wie er gespeichert ist, und der Server schreibt Header-Namen klein, wie RFC 9114 es verlangt, sodass ein Vergleich wie Names[i] = 'Content-Type' dennoch fehlschlägt. Vergleichen Sie mit einem kleingeschriebenen Namen oder verwenden Sie Values[] und überlassen Sie diesem die Groß-/Kleinschreibung.
Der Server schreibt :status und fügt content-type und content-length hinzu, wo es zutrifft, diese müssen also nie von Hand hinzugefügt werden. Einträge, die RFC 9114 auf der Leitung verbietet, werden verworfen statt gesendet: jeder Pseudo-Header (ein Name, der mit : beginnt) sowie die verbindungsspezifischen Header connection, keep-alive, proxy-connection, transfer-encoding und upgrade.
Eine Antwort auf eine HEAD-Anfrage wird nur als Header-Abschnitt gesendet: Der Body wird automatisch unterdrückt, selbst wenn Data gesetzt ist, sodass ein HEAD-Handler die Antwort genau wie ein GET-Handler aufbauen kann.
.NET-Unterschied. Die .NET-Implementierung deklariert dieses Ereignis mit einer Verbindungs-ID-Zeichenfolge anstelle eines Verbindungsobjekts, und es wird nie ausgelöst. Der Grund ist kein fehlender Codec: Der .NET-Server implementiert HTTP/3-Framing und QPACK durchaus und verwendet beides für den SETTINGS-Handshake und für Extended CONNECT / WebTransport. Was fehlt, ist das Routing. Standardmäßig, also wenn H3Settings.Settings.WebTransportMaxSessions auf 0 belassen wird, wird jede Verbindung von einem Pfad behandelt, der jeden eingehenden Stream annimmt und verwirft, sodass nie eine Anfrage geparst wird. Bei aktiviertem WebTransport wird ein Anfrage-Header-Abschnitt korrekt per QPACK dekodiert und dann verworfen, weil nur ein CONNECT, das das WebTransport-:protocol trägt, weiterverarbeitet wird. Der .NET-Server nimmt daher QUIC-Verbindungen an und löst OnH3Connect und OnH3Disconnect aus, macht aber nie eine Anfrage über dieses Ereignis sichtbar. Verwenden Sie die Delphi/C++Builder-Implementierung, um HTTP/3-Anfragen zu bedienen.
procedure OnH3RequestEvent(Sender: TObject; const aConnection:
TsgcQUICServerConnection; const aRequest: TsgcHTTP3Request;
const aResponse: TsgcHTTP3Response);
begin
if (aRequest.Path = '/hello') and (aRequest.Method = h3meGet) then
begin
aResponse.StatusCode := 200;
aResponse.ContentType := 'text/plain; charset=utf-8';
aResponse.Headers.Add('cache-control=no-store'); // name=value
aResponse.Data := TEncoding.UTF8.GetBytes('Hello HTTP/3');
end
else if (aRequest.Path = '/echo') and (aRequest.Method = h3mePost) then
begin
// aRequest.Data holds the reassembled request body
aResponse.StatusCode := 200;
aResponse.ContentType := aRequest.ContentType;
aResponse.Data := aRequest.Data;
end
else
begin
aResponse.StatusCode := 404;
aResponse.ContentType := 'text/plain; charset=utf-8';
aResponse.Data := TEncoding.UTF8.GetBytes('Not Found');
end;
end;