TsgcWebTransportSession

TsgcWebTransportSession represents one WebTransport session and owns the streams multiplexed inside it over a single HTTP/3 QUIC connection.

Introduction

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:

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.

Session identity

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.

Streams only

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.

Reference

Key Properties

NameDescription
SessionIdQUIC stream id of the Extended CONNECT request that established this session.
QuarterStreamIdSessionId div 4, the session identity as encoded in WebTransport datagrams.
StreamCountNumber of streams currently open on this session.
DatagramsSupportedWhether the transport bound to this session can really put a datagram on the wire. False on every shipping transport.

Key Methods

NameDescription
OpenStreamOpens a new outgoing stream, bidirectional or unidirectional, and sends its WebTransport header.
FindStreamByIdReturns the stream with the given QUIC stream id, or nil.
CloseCloses the session's open streams and raises OnClose. It does not tear down the QUIC connection.
SendDatagramSends a WebTransport datagram. Raises TsgcQUICException as shipped, since no transport can carry one.

Key Events

NameDescription
OnStreamFires when the peer opens a new stream inside this session.
OnCloseFires when Close is called on this session.
OnDatagramWould deliver an inbound datagram. Never fires as shipped.

Complete Example

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.

Requirements