TsgcWebTransportStream › Properties › Unidirectional
True when the stream carries data one way only, False when both sides can write.
property Unidirectional: Boolean read FUnidirectional;
Boolean (bool in C++Builder and C#).
Fixed at construction. For a stream you opened, it is the aUnidirectional argument you passed to Session.OpenStream. For a peer-opened stream, it is what the peer's stream header said: True after a 0x54 header, False after 0x41.
Read-only. A stream's direction is decided when it is opened and cannot change: QUIC encodes it in the stream id itself, and WebTransport encodes it again in the stream header. Choose it by the argument you pass to OpenStream.
| Value | Header | Who may write | Good for |
|---|---|---|---|
False (bidirectional) | varint(0x41) + varint(SessionId) | Both sides | Request and response, echo, any exchange where you expect an answer on the same stream. |
True (unidirectional) | varint(0x54) + varint(SessionId) | The opener only | Fire-and-forget: telemetry, log shipping, notifications, one-way media. |
The property is per-object, not per-peer. This is the subtlety worth internalising. Unidirectional = True on a stream you opened means you can write and the peer cannot. The same flag on a stream you received through OnStream means the opposite for you: the peer writes, and your copy is receive-only. So Unidirectional alone does not tell you whether you may write; you also need to know which side opened it. In practice that is easy, because the two cases arrive through different routes: streams from OpenStream are yours to write, streams from OnStream are the peer's.
Writing to the wrong end fails quietly. Calling WriteData on the receiving end of a unidirectional stream raises no error. The HTTP/3 client and server bind a per-stream writer only to peer-opened bidirectional streams, so a peer-opened unidirectional stream keeps the session-level fallback writer and your bytes go somewhere unhelpful. Guard the echo path with if not aStream.Unidirectional then, as the shipping demos do.
Note that a unidirectional stream is not more efficient than a bidirectional one. Use it because your data genuinely flows one way, which makes the intent explicit and saves the peer from waiting for a reply channel that will never carry anything.
// Opening: the argument decides the direction
FBidi := FSession.OpenStream(False); // we can write, peer can answer
FBidi.OnData := OnStreamData;
FBidi.WriteData('request');
FTelemetry := FSession.OpenStream(True); // we can write, peer only reads
FTelemetry.WriteData('telemetry: started');
// Receiving: the flag says what the PEER opened, so check before answering
procedure TfrmServer.OnWTStream(Sender: TObject;
const aStream: TsgcWebTransportStream);
begin
aStream.OnData := OnWTStreamData;
if aStream.Unidirectional then
memoLog.Lines.Add('peer opened a receive-only stream; we cannot answer on it')
else
memoLog.Lines.Add('peer opened a bidi stream; we can echo on it');
end;
procedure TfrmServer.OnWTStreamData(Sender: TObject; const aData: TBytes);
var
oStream: TsgcWebTransportStream;
begin
oStream := TsgcWebTransportStream(Sender);
// Guard the echo: writing to a peer-opened unidirectional stream does
// nothing useful and reports no error
if not oStream.Unidirectional then
oStream.WriteData(aData);
end;