TsgcWebTransportStream › Events › OnClose

OnClose Event

Fires when this stream is closed, either directly or because its session was closed.

Syntax

property OnClose: TsgcWebTransportNotifyEvent read FOnClose write FOnClose;

// event type
TsgcWebTransportNotifyEvent = procedure(Sender: TObject) of object;

// handler signature
procedure MyStreamClose(Sender: TObject);

Parameters

NameTypeDescription
SenderTObjectThe TsgcWebTransportStream that is closing. Cast it to read StreamId or reach the Session.

The C# handler additionally receives an EventArgs parameter, which is always EventArgs.Empty and carries no information.

Default Value

Unassigned. When no handler is assigned, Close still marks the stream closed and unregisters it from its session.

Remarks

It is not a disconnect notification. The event is raised only by Close, so it fires when your own code calls oStream.Close, and also when you call Session.Close, which closes every stream still open on the session and therefore raises this event on each of them. Nothing else in the library closes a stream for you. It does not fire when the peer finishes a stream, when the peer sends a QUIC FIN or RESET_STREAM, when the QUIC connection drops, or when the session is destroyed and the stream is freed. If your protocol needs an end-of-stream marker, send one in the payload; the transport will not give you one.

The event fires at most once per stream: Close checks the closed flag first, so a second call does nothing. This holds on Delphi, C++Builder and .NET alike.

The stream is still fully valid inside the handler. Close frees nothing and closes no QUIC stream, so you can read StreamId, reach Session, even call WriteData, which still reaches the writer. Treat the stream as finished anyway, and stop writing to it. The stream has already been unregistered from its session by the time the handler runs, so StreamCount no longer counts it and Session.FindStreamById no longer returns it.

Do not free the stream from here, since the session owns it and releases it when the session is destroyed. There is no need to call Session.RemoveStream(Sender) either, from inside the handler or after it, because Close has already done it. Calling it anyway is harmless: it frees nothing and simply finds the stream already unregistered.

Threading. The handler runs on whichever thread called Close, with no synchronisation of its own. Since that is usually your own code, it is usually a thread you already know.

Use it for symmetry and for cleanup: releasing the per-stream state your application built, updating a UI, or logging. It is your own signal, routed through the stream so several parts of your code can observe it.

Example


// Assign alongside OnData, as soon as you have the stream
FStream := FSession.OpenStream(False);
FStream.OnData := OnStreamData;
FStream.OnClose := OnStreamClose;

procedure TfrmClient.OnStreamClose(Sender: TObject);
var
  oStream: TsgcWebTransportStream;
begin
  // The stream is still valid here, so its state can be read
  oStream := TsgcWebTransportStream(Sender);
  memoLog.Lines.Add(Format('stream %d closed (session %d)',
    [oStream.StreamId, oStream.Session.SessionId]));

  // Release only OUR per-stream state. Do not free the stream: the session
  // owns it. Close has already unregistered it for us.
  FPendingReplies.Remove(oStream.StreamId);
end;

// Either of these triggers it: closing the stream, or closing the session.
procedure TfrmClient.FinishExchange;
begin
  FStream.Close;    // unregisters the stream, then raises OnClose once
  FStream := nil;   // owned by the session, do not free
end;

Back to Events