TsgcWebTransportSession repräsentiert eine WebTransport-Sitzung und besitzt die Streams, die darin über eine einzige HTTP/3-QUIC-Verbindung multiplext werden.
Eine WebTransport-Sitzung wird durch eine Extended CONNECT-Anfrage (RFC 9220) aufgebaut, die :protocol = webtransport trägt. TsgcWebTransportSession ist das Objekt, das sie repräsentiert, sobald der Handshake erfolgreich war. Es ist in der Unit sgcWebTransport deklariert und ist eine einfache Klasse, keine Komponente: Sie legen es nicht auf einem Formular ab und Sie erzeugen es nicht selbst. Es erreicht Sie auf eine von zwei Arten:
TsgcHTTP3Client.WebTransportConnect gibt bei :status = 200 die Sitzung zurück oder nil, wenn der Server sie ablehnt.TsgcHTTP3Server.OnWebTransportSession übergibt Ihnen die Sitzung zusammen mit einem var aAccept-Flag.
In beiden Fällen gehört die Sitzung der Sitzungsregistrierung der Verbindung, die sie freigibt. Geben Sie sie nicht selbst frei. Auf dem Client gibt das Freigeben von TsgcHTTP3Client jede von ihm geöffnete Sitzung frei, und jede Sitzung gibt ihre Streams frei.
Die Sitzung multiplext beliebig viele TsgcWebTransportStream-Objekte. Öffnen Sie einen mit OpenStream; Streams, die der Peer öffnet, treffen über OnStream ein. Die Sitzung selbst besitzt keine Lese- oder Schreibmethode für Anwendungsdaten: Die gesamte Payload wird über Streams übertragen.
SessionId ist die QUIC-Stream-ID der Extended-CONNECT-Anfrage, die die Sitzung erstellt hat. Sie verankert die gesamte Sitzung auf der Leitung: Jedem WebTransport-Stream, der darin geöffnet wird, wird varint(0x54) oder varint(0x41) gefolgt von varint(SessionId) vorangestellt, so weiß die empfangende Seite, zu welcher Sitzung ein neuer QUIC-Stream gehört. QuarterStreamId ist dieselbe Identität in der kompakten Form, die die Datagramm-Schicht verwendet, SessionId div 4.
Die Sitzung stellt SendDatagram und OnDatagram bereit, aber kein ausgeliefertes Backend kann ein Datagramm übertragen: SendDatagram löst TsgcQUICException aus, statt Ihre Payload stillschweigend zu verlieren, und OnDatagram wird nie ausgelöst. Siehe WebTransport für die vollständige Erklärung. Bauen Sie Ihr Protokoll auf Streams auf.
| Name | Beschreibung |
|---|---|
| SessionId | QUIC-Stream-ID der Extended-CONNECT-Anfrage, die diese Sitzung aufgebaut hat. |
| QuarterStreamId | SessionId div 4, die Sitzungsidentität, wie sie in WebTransport-Datagrammen kodiert ist. |
| StreamCount | Anzahl der aktuell auf dieser Sitzung geöffneten Streams. |
| DatagramsSupported | Ob der an diese Sitzung gebundene Transport wirklich ein Datagramm auf die Leitung legen kann. False bei jedem ausgelieferten Transport. |
| Name | Beschreibung |
|---|---|
| OpenStream | Öffnet einen neuen ausgehenden Stream, bidirektional oder unidirektional, und sendet dessen WebTransport-Header. |
| FindStreamById | Gibt den Stream mit der angegebenen QUIC-Stream-ID zurück oder nil. |
| Close | Schließt die offenen Streams der Sitzung und löst OnClose aus. Die QUIC-Verbindung wird dabei nicht abgebaut. |
| SendDatagram | Sendet ein WebTransport-Datagramm. Löst im Auslieferungszustand eine TsgcQUICException aus, da kein Transport eines übertragen kann. |
| Name | Beschreibung |
|---|---|
| OnStream | Wird ausgelöst, wenn der Peer einen neuen Stream innerhalb dieser Sitzung öffnet. |
| OnClose | Wird ausgelöst, wenn Close für diese Sitzung aufgerufen wird. |
| OnDatagram | Würde ein eingehendes Datagramm liefern. Wird in der ausgelieferten Version nie ausgelöst. |
Ein vollständiger Client-Roundtrip gegen einen WebTransport-Echo-Server: verbinden, einen bidirektionalen Stream öffnen, schreiben, das Echo über OnData lesen, dann schließen. Die QUIC-Verbindung ist nicht-blockierend, daher pumpt ein Timer sie mit WebTransportProcessEvents; ohne diese Pumpe werden nie Daten gelesen.
unit FWebTransportClient;
interface
uses
Classes, SysUtils, Forms, StdCtrls, ExtCtrls,
// sgc
sgcHTTP, sgcHTTP3_Classes, sgcHTTP3_Types, sgcWebTransport;
type
TfrmClient = class(TForm)
memoLog: TMemo;
tmrPump: TTimer;
btnConnect: TButton;
btnClose: TButton;
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure btnConnectClick(Sender: TObject);
procedure btnCloseClick(Sender: TObject);
procedure tmrPumpTimer(Sender: TObject);
private
FClient: TsgcHTTP3Client;
FSession: TsgcWebTransportSession;
FStream: TsgcWebTransportStream;
procedure OnStreamData(Sender: TObject; const aData: TBytes);
procedure OnStreamClose(Sender: TObject);
procedure OnSessionClose(Sender: TObject);
end;
implementation
{$R *.dfm}
procedure TfrmClient.FormCreate(Sender: TObject);
begin
FClient := TsgcHTTP3Client.Create(nil);
FClient.TLSOptions.VerifyCertificate := False; // self-signed test cert
FClient.ConnectTimeout := 10000;
FClient.UserAgent := 'sgcWebSockets/WebTransportClient';
tmrPump.Interval := 50;
tmrPump.Enabled := False;
end;
procedure TfrmClient.FormDestroy(Sender: TObject);
begin
tmrPump.Enabled := False;
// FSession is owned by FClient.WebTransportSessions and FStream is owned by
// the session, so freeing the client releases both.
FClient.Free;
end;
procedure TfrmClient.btnConnectClick(Sender: TObject);
begin
// Opens a dedicated QUIC connection and performs the Extended CONNECT
// (:protocol=webtransport). Returns nil when the server rejects the session
// or does not support WebTransport.
FSession := FClient.WebTransportConnect('https://127.0.0.1:4433/echo');
if not Assigned(FSession) then
begin
memoLog.Lines.Add('WebTransport connect failed. ServerSupportsWebTransport=' +
BoolToStr(FClient.ServerSupportsWebTransport, True));
Exit;
end;
memoLog.Lines.Add('session established: id=' + IntToStr(FSession.SessionId));
FSession.OnClose := OnSessionClose;
// False = bidirectional, so the server can answer on the same stream.
FStream := FSession.OpenStream(False);
FStream.OnData := OnStreamData;
FStream.OnClose := OnStreamClose;
memoLog.Lines.Add('bidi stream opened: id=' + IntToStr(FStream.StreamId));
// Start pumping BEFORE writing, so the echo cannot be missed.
tmrPump.Enabled := True;
FStream.WriteData('Hello WebTransport'); // UTF-8 encoded for you
memoLog.Lines.Add('sent: Hello WebTransport');
end;
procedure TfrmClient.tmrPumpTimer(Sender: TObject);
begin
// Reads peer packets and harvests peer-opened streams. Nothing arrives
// without this call.
FClient.WebTransportProcessEvents;
end;
procedure TfrmClient.OnStreamData(Sender: TObject; const aData: TBytes);
var
oStream: TsgcWebTransportStream;
begin
oStream := TsgcWebTransportStream(Sender);
memoLog.Lines.Add('echo on stream ' + IntToStr(oStream.StreamId) + ': ' +
TEncoding.UTF8.GetString(aData));
end;
procedure TfrmClient.OnStreamClose(Sender: TObject);
begin
memoLog.Lines.Add('stream closed');
end;
procedure TfrmClient.OnSessionClose(Sender: TObject);
begin
memoLog.Lines.Add('session closed');
end;
procedure TfrmClient.btnCloseClick(Sender: TObject);
begin
tmrPump.Enabled := False;
if Assigned(FStream) then
FStream.Close; // fires OnStreamClose
if Assigned(FSession) then
FSession.Close; // fires OnSessionClose
end;
end.
sgcVer.inc ist das HTTP/3-Feature-Pack SGC_PACK_HTTP3 innerhalb des Editionsblocks All-Access (SGC_EDT_ALL) deklariert und erfordert SGC_INDY_LIB.{$DEFINE SGC_PACK_HTTP3} in sgcVer.inc, was SGC_QUIC, SGC_HTTP3 und SGC_WEBTRANSPORT definiert. Die gesamte Klasse ist durch {$IFDEF SGC_WEBTRANSPORT} geschützt.sgcWebTransport deklariert die Klasse; fügen Sie sgcHTTP für TsgcHTTP3Client und TsgcHTTP3Server hinzu sowie sgcQUIC_Server für TsgcQUICServerConnection.qmBuiltin (das native QUIC von OpenSSL), die beim Laden von OpenSSL automatisch erkannt wird und nicht vom Benutzer wählbar ist.esegece.sgcWebSockets.qmBuiltin besitzt keinen DATAGRAM-Pfad nach RFC 9221, daher können Datagramme in keiner ausgelieferten Konfiguration gesendet werden und SendDatagram löst eine Ausnahme aus.