TsgcQUICServer › TsgcQUICServerConnection

TsgcQUICServerConnection Class

A single accepted QUIC connection. This is the object handed to every TsgcQUICServer event, and the handle used to send data back to that peer.

Introduction

Every QUIC connection accepted by the listener is represented by one TsgcQUICServerConnection instance. The server creates it, owns it, and passes it as the aConnection parameter of OnQUICConnect, OnQUICData, OnQUICDisconnect, OnQUICException and OnQUICStreamData. You never create one yourself.

You use the connection for three things:

1. As the target of WriteData, which sends bytes back over the connection's default stream.

2. As the connection on which OpenStream creates additional server-initiated streams.

3. As the key for your own per-session state, because the instance is stable for the lifetime of the connection.

Lifetime. The instance is owned by the server's Connections list, which frees it when the peer disconnects or when Stop is called. Never free it yourself, and never keep a reference to it after OnQUICDisconnect has fired for that connection, because the object is destroyed shortly afterwards.

Closing a connection. There is no Disconnect method. Set Connected to False to make the server stop servicing the connection; the transport releases it on the next pass of the event loop. To drop every connection at once, call Stop.

Threading. The connection is created, read and destroyed on the server's background listener thread, and the events that receive it are raised on that same thread. Do not touch the VCL/FMX directly from a handler, and do not block, because the whole QUIC event loop (every connection, not just this one) stalls while a handler runs.

Declaration

TsgcQUICServerConnection = class
public
  function AddStream(aSSL: PSSL; aStreamId: Int64; aUnidirectional, aIncoming: Boolean): TsgcQUICServerStream;
  function FindStreamById(aStreamId: Int64): TsgcQUICServerStream;
  procedure RemoveStream(const aStream: TsgcQUICServerStream);
  property SSL: PSSL read FSSL write FSSL;
  property Stream: PSSL read FStream write FStream;
  property PeerIP: string read FPeerIP write FPeerIP;
  property PeerPort: Word read FPeerPort write FPeerPort;
  property Connected: Boolean read FConnected write FConnected;
  property ConnectNotified: Boolean read FConnectNotified write FConnectNotified;
  property DefaultStreamAdopted: Boolean read FDefaultStreamAdopted write FDefaultStreamAdopted;
  property CreatedAt: TDateTime read FCreatedAt;
  property Streams: TObjectList read GetStreams;
end;

Properties

NameTypeDescription
ConnectedBooleanWhether the connection is live. The server only pumps and reads connections whose Connected is True. Set it to False to make the server stop servicing this peer. The transport itself sets it to False just before raising OnQUICDisconnect.
CreatedAtTDateTimeRead-only. The timestamp (Now) at which the connection object was created, that is, when the QUIC connection was accepted. Useful for age-based housekeeping and logging.
PeerIPstringThe peer IP address. See the Remarks below before relying on this in Delphi.
PeerPortWordThe peer UDP port. See the Remarks below before relying on this in Delphi.
StreamsTObjectListRead-only. The additional (non-default) QUIC streams tracked for this connection, each a TsgcQUICServerStream. The list is created on first access and owns its items. It is only populated when the connection runs in multi-stream mode, see AlwaysMultiStream.
StreamPSSLThe raw OpenSSL handle of the connection's single default data stream, adopted from the first bidirectional stream the peer opened. This is the stream WriteData writes to. It is nil until the peer opens its first stream. Low-level, prefer WriteData.
SSLPSSLThe raw OpenSSL handle of the QUIC connection itself. Exposed for advanced interoperability with the OpenSSL API. Do not free it, the connection owns it.
ConnectNotifiedBooleanInternal bookkeeping. True once OnQUICConnect has been raised for this connection. The connect notification is deferred until the peer's first stream is accepted so that data written from the handler has a stream to target.
DefaultStreamAdoptedBooleanInternal bookkeeping. True once the first peer bidirectional stream has been adopted as Stream. It is adopted only once, so later request streams are demultiplexed by stream id instead of reusing the first request's parser state.

Methods

NameDescription
AddStream(aSSL, aStreamId, aUnidirectional, aIncoming)Creates a TsgcQUICServerStream wrapper, adds it to Streams and returns it. Called by the transport when it accepts a peer stream and by OpenStream. You normally call OpenStream instead of this.
FindStreamById(aStreamId)Returns the tracked stream whose StreamId matches, or nil. Use it to correlate the aStreamId reported by OnQUICStreamData with a stream you can write to.
RemoveStream(aStream)Removes the stream from Streams. Because the list owns its items, this also frees the wrapper and its OpenSSL stream handle.

Remarks

PeerIP and PeerPort are not populated by the Delphi server. Both properties are declared read/write, but the Delphi QUIC listener leaves them at '' and 0, because the peer address is not retrievable from OpenSSL for a connection accepted through the QUIC listener API. The shipped libssl-3.dll (OpenSSL 3.5.5) exports only SSL_set1_initial_peer_addr, a client-side setter used to tell a client where to connect. There is no matching SSL_get0_peer_addr getter to call on the server side, and connections accepted with SSL_accept_connection share the listener's single UDP socket, so there is no per-connection BIO whose peer address could be queried either. The limitation is upstream in OpenSSL, not something the transport skips. As a consequence FindConnectionByPeer never matches in Delphi.

In the .NET implementation both properties are populated, from the managed QuicConnection.RemoteEndPoint, which MsQuic exposes. So this is one of the few places where the two implementations differ. In Delphi, identify a session by the connection instance itself, which is stable and unique for the connection's lifetime, rather than by peer address.

The Streams list holds only the additional streams. The default data stream is not in it, it is the separate Stream handle. So a plain single-stream QUIC connection has an empty Streams list.

The .NET resolution of this class is deliberately smaller: it exposes PeerIP, PeerPort, Connected and CreatedAt only. The stream collection, the raw OpenSSL handles and the internal bookkeeping flags have no .NET counterpart, because the managed System.Net.Quic stack owns those details.

Example


procedure TfrmServer.OnQUICConnectEvent(Sender: TObject; const aConnection: TsgcQUICServerConnection);
begin
  // the instance identifies the session; PeerIP is not filled in by the Delphi server
  Log(Format('connected at %s (%d live)',
    [DateTimeToStr(aConnection.CreatedAt), oServer.ConnectionCount]));
  // greet the peer over its default stream
  oServer.WriteData(aConnection, TEncoding.UTF8.GetBytes('welcome'));
end;

procedure TfrmServer.OnQUICDataEvent(Sender: TObject; const aConnection: TsgcQUICServerConnection;
  const aData: TBytes);
begin
  // reject an oversized payload by closing this connection only
  if Length(aData) > 1024 * 1024 then
    aConnection.Connected := False
  else
    oServer.WriteData(aConnection, aData);
end;

Requirements

RequirementDetail
EditionAll-Access edition.
Compiler defineQUIC ships disabled. Uncomment {$DEFINE SGC_PACK_HTTP3} in the All-Access block of sgcVer.inc, it is delivered as {.$DEFINE SGC_PACK_HTTP3}. That in turn defines SGC_QUIC, but only when SGC_INDY_LIB is defined.
UnitsgcQUIC_Server
OpenSSLOpenSSL 3.5 or later, required.

Back to TsgcQUICServer