TsgcWebTransportSession › Methods › OpenStream

OpenStream Method

Opens a new outgoing WebTransport stream, bidirectional or unidirectional, sends its stream header and returns the stream object.

Syntax

function OpenStream(aUnidirectional: Boolean): TsgcWebTransportStream;

Parameters

NameTypeDescription
aUnidirectionalBooleanTrue opens a unidirectional stream: you can write, the peer can only read. False opens a bidirectional stream: both sides can write, so the peer can answer on the same stream.

Return Value

A new TsgcWebTransportStream, already registered with the session and ready to write. The session owns it; do not free it. The method never returns nil.

Remarks

OpenStream performs four steps in order:

  1. Creates the stream wrapper and gives it the session-level fallback writer.
  2. Raises OnOpenStream, which is where the transport allocates the real QUIC stream, fills in StreamId and binds a per-stream writer. Both the HTTP/3 client and server assign this hook.
  3. Writes the WebTransport stream header on that QUIC stream: varint(0x54) + varint(SessionId) for a unidirectional stream, varint(0x41) + varint(SessionId) for a bidirectional one, and sets HeaderSent to True.
  4. Adds the stream to the session's list, so it counts in StreamCount and can be found with FindStreamById.

The header is sent once, by OpenStream itself. Everything you subsequently pass to WriteData is pure application payload.

Assign OnData before you write. On a bidirectional stream the peer may answer immediately, so hook OnData on the returned stream before the first WriteData. On the client, also make sure WebTransportProcessEvents is being pumped, or the answer will never be read.

If no transport assigned OnOpenStream, the stream keeps its fallback writer, StreamId stays -1, and all bytes are routed through the session-level OnWriteData writer. That models a single multiplexed transport stream and is not what the shipping HTTP/3 client and server do.

.NET difference. On the managed stack the session assigns the stream id itself, as a sequential counter starting from SessionId, and the transport hook does not overwrite it. A locally-opened stream's StreamId is therefore a synthetic handle on .NET, not the real QUIC stream id it is in Delphi and C++Builder. Peer-opened streams carry the real QUIC stream id on both stacks. The bytes on the wire are identical either way; only the value you can read back differs.

Example


var
  oStream: TsgcWebTransportStream;
begin
  // False = bidirectional, so the server can answer on the same stream
  oStream := oSession.OpenStream(False);
  oStream.OnData := OnStreamDataEvent;    // assign BEFORE writing
  oStream.OnClose := OnStreamCloseEvent;
  oStream.WriteData('Request payload');

  memoLog.Lines.Add('bidi stream opened: id=' + IntToStr(oStream.StreamId));

  // A fire-and-forget notification channel: True = unidirectional
  with oSession.OpenStream(True) do
    WriteData('telemetry: started');
end;

Back to Methods