TsgcWebTransportSession represents one WebTransport session and owns the streams multiplexed inside it over a single HTTP/3 QUIC connection.
A WebTransport session is established by one Extended CONNECT request (RFC 9220) carrying :protocol = webtransport. TsgcWebTransportSession is the object that represents it once the handshake succeeds. It is declared in the sgcWebTransport unit and it is a plain class, not a component: you do not drop it on a form and you do not construct it. It reaches you in one of two ways:
TsgcHTTP3Client.WebTransportConnect returns the session on :status = 200, or nil when the server rejects it.TsgcHTTP3Server.OnWebTransportSession hands you the session together with a var aAccept flag.
In both cases the session is owned by the connection's session registry, which frees it. Do not free it yourself. On the client, freeing the TsgcHTTP3Client releases every session it opened, and each session releases its streams.
The session multiplexes any number of TsgcWebTransportStream objects. Open one with OpenStream; streams the peer opens arrive on OnStream. The session itself carries no read or write method for application data: all payload travels on streams.
SessionId is the QUIC stream id of the Extended CONNECT request that created the session. It anchors the whole session on the wire: every WebTransport stream opened inside it is prefixed with varint(0x54) or varint(0x41) followed by varint(SessionId), which is how the receiving side knows which session a new QUIC stream belongs to. QuarterStreamId is the same identity in the compact form the datagram layer uses, SessionId div 4.
The session exposes SendDatagram and OnDatagram, but no shipping backend can carry a datagram: SendDatagram raises TsgcQUICException rather than lose your payload silently, and OnDatagram never fires. See WebTransport for the full explanation. Build your protocol on streams.
| Name | Description |
|---|---|
| SessionId | QUIC stream id of the Extended CONNECT request that established this session. |
| QuarterStreamId | SessionId div 4, the session identity as encoded in WebTransport datagrams. |
| StreamCount | Number of streams currently open on this session. |
| DatagramsSupported | Whether the transport bound to this session can really put a datagram on the wire. False on every shipping transport. |
| Name | Description |
|---|---|
| OpenStream | Opens a new outgoing stream, bidirectional or unidirectional, and sends its WebTransport header. |
| FindStreamById | Returns the stream with the given QUIC stream id, or nil. |
| Close | Closes the session's open streams and raises OnClose. It does not tear down the QUIC connection. |
| SendDatagram | Sends a WebTransport datagram. Raises TsgcQUICException as shipped, since no transport can carry one. |
| Name | Description |
|---|---|
| OnStream | Fires when the peer opens a new stream inside this session. |
| OnClose | Fires when Close is called on this session. |
| OnDatagram | Would deliver an inbound datagram. Never fires as shipped. |
A full client round-trip against a WebTransport echo server: connect, open a bidirectional stream, write, read the echo through OnData, then close. The QUIC connection is non-blocking, so a timer pumps it with WebTransportProcessEvents; without that pump no data is ever read.
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, the HTTP/3 feature pack SGC_PACK_HTTP3 is declared inside the All-Access edition block (SGC_EDT_ALL) and requires SGC_INDY_LIB.{$DEFINE SGC_PACK_HTTP3} in sgcVer.inc, which defines SGC_QUIC, SGC_HTTP3 and SGC_WEBTRANSPORT. The whole class is guarded by {$IFDEF SGC_WEBTRANSPORT}.sgcWebTransport declares the class; add sgcHTTP for TsgcHTTP3Client and TsgcHTTP3Server, and sgcQUIC_Server for TsgcQUICServerConnection.qmBuiltin QUIC engine (OpenSSL's native QUIC), which is auto-detected at OpenSSL load and is not user-selectable.esegece.sgcWebSockets.qmBuiltin has no RFC 9221 DATAGRAM path, so datagrams cannot be sent in any shipping configuration and SendDatagram raises.