TsgcWebTransportSession › Methods › OpenStream
Opens a new outgoing WebTransport stream, bidirectional or unidirectional, sends its stream header and returns the stream object.
function OpenStream(aUnidirectional: Boolean): TsgcWebTransportStream;
| Name | Type | Description |
|---|---|---|
aUnidirectional | Boolean | True 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. |
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.
OpenStream performs four steps in order:
StreamId and binds a per-stream writer. Both the HTTP/3 client and server assign this hook.varint(0x54) + varint(SessionId) for a unidirectional stream, varint(0x41) + varint(SessionId) for a bidirectional one, and sets HeaderSent to True.
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.
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;