TsgcHTTP3Server › Ereignisse › OnH3Request

OnH3Request Ereignis

Wird ausgelöst, wenn eine vollständige HTTP/3-Anfrage empfangen wird, sodass die Anwendung die Antwort füllen kann.

Syntax

property OnH3Request: TsgcHTTP3RequestEvent;
// TsgcHTTP3RequestEvent = procedure(Sender: TObject; const aConnection: TsgcQUICServerConnection; const aRequest: TsgcHTTP3Request; const aResponse: TsgcHTTP3Response) of object

Standardwert

—

Parameter

NameTypBeschreibung
SenderTObjectDie TsgcHTTP3Server-Instanz, die das Ereignis ausgelöst hat. Casten Sie sie, um PushPromise innerhalb des Handlers aufzurufen.
aConnectionconst TsgcQUICServerConnectionQUIC-Verbindung, über die die Anfrage eingegangen ist. Stellt PeerIP, PeerPort, CreatedAt und den Verbindungszustand bereit.
aRequestconst TsgcHTTP3RequestDie empfangene Anfrage. Nachfolgend im Detail beschrieben.
aResponseconst TsgcHTTP3ResponseDie zu füllende Antwort. Nachfolgend im Detail beschrieben. Wird vom Server mit auf 200 voreingestelltem StatusCode erstellt.

Hinweise

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.

Lesen der Anfrage (TsgcHTTP3Request)

MemberTypBeschreibung
MethodTh3MethodsAnfragemethode: h3meGet, h3meHead, h3mePost, h3mePut, h3meDelete, h3mePatch, h3meOptions, h3meTrace, h3meConnect oder h3meQuery. Eine nicht erkannte Methode wird als h3meGet gemeldet.
PathstringDer Pseudo-Header :path, zum Beispiel /api/time. Enthält auch eine eventuelle Query-Zeichenfolge; die Komponente trennt sie nicht für Sie ab.
HoststringDer Pseudo-Header :authority, das HTTP/3-Äquivalent des Host-Headers.
SchemestringDer Pseudo-Header :scheme, normalerweise https.
ProtocolstringDer Pseudo-Header :protocol eines Extended CONNECT (RFC 9220). Leer, wenn nicht vorhanden.
DataTBytesDer Anfrage-Body, aus den DATA-Frames wieder zusammengesetzt. Leer bei Methoden ohne Body.
ContentTypestringDer content-type-Header, der Einfachheit halber aus Headers herausgezogen.
UserAgent, Accept, AuthorizationstringDie zugehörigen Anfrage-Header, ebenfalls aus Headers herausgezogen.
HeadersTStringListDie ü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.

Aufbau der Antwort (TsgcHTTP3Response)

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.

MemberTypBeschreibung
StatusCodeIntegerDer 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.
ContentTypestringWird 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.
DataTBytesDer Antwort-Body. Kodieren Sie Zeichenfolgen explizit, normalerweise mit TEncoding.UTF8.GetBytes. Lassen Sie ihn für eine Antwort ohne Body leer.
HeadersTStringListZusätzliche Antwort-Header, hinzugefügt in der Form name=value, einer pro Eintrag. Die Namen werden für Sie in Kleinbuchstaben umgewandelt.
ContentLengthInt64Wird 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.

Beispiel


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;

Zurück zu Ereignissen