TsgcQUICServer

TsgcQUICServer ist ein Low-Level-QUIC-Server: Er lauscht auf QUIC-Verbindungen über UDP und tauscht rohe Anwendungsbytes über QUIC-Streams aus.

Einführung

TsgcQUICServer implementiert die Serverseite von QUIC (RFC 9000, mit TLS 1.3 gemäß RFC 9001 und Verlustbehebung gemäß RFC 9002). QUIC ist ein verbindungsorientierter, multiplexter, stets verschlüsselter Transport, der über UDP läuft. Diese Komponente stellt ihn als Byte-Transport bereit: Sie übergibt Ihnen die Bytes, die ein Peer sendet, und lässt Sie Bytes zurückschreiben. Sie implementiert kein darauf aufsetzendes Anwendungsprotokoll. Für HTTP/3 verwenden Sie TsgcHTTP3Server, das von dieser Komponente abgeleitet ist.

Was Ihnen die Komponente bietet:

Der QUIC-Server läuft standardmäßig auf der integrierten QUIC-Engine von OpenSSL 3.5+. Er ruft OSSL_QUIC_server_method und SSL_new_listener auf, wenn die geladene OpenSSL-Bibliothek diese exportiert. Andernfalls fällt er auf die sgc-QUIC-Engine zurück, und mit QUICOptions.SSLOptions.IOHandler = iohNativeTLS verwendet er immer die sgc-Engine mit dem TLS-1.3-Handshake auf der nativen TLS-Engine. Die schreibgeschützte Eigenschaft QUICMode meldet die beim Start gewählte Engine. Siehe QUIC für den Engine-Vergleich.

Befolgen Sie diese Schritte, um die Komponente zu konfigurieren:

1. Erstellen Sie eine neue Instanz von TsgcQUICServer.

2. Legen Sie den Port fest, auf dem gelauscht wird. Standardmäßig ist er 80, geerbt von der UDP-Basisklasse, setzen Sie ihn daher immer (4433 und 443 sind die üblichen QUIC-Werte).

3. Legen Sie das Zertifikat und den privaten Schlüssel in QUICOptions.SSLOptions fest. Beide sind PEM-Dateien und beide sind zwingend erforderlich. Für ein Testzertifikat:

openssl req -x509 -newkey rsa:2048 -keyout server.key -out server.crt -days 365 -nodes -subj "/CN=localhost"

4. Behandeln Sie OnQUICConnect und OnQUICData und rufen Sie Start innerhalb eines try..except auf, weil Konfigurationsfehler dort auftreten.


oServer := TsgcQUICServer.Create(nil);
oServer.Port := 4433;
oServer.QUICOptions.SSLOptions.CertFile := 'server.crt';
oServer.QUICOptions.SSLOptions.KeyFile := 'server.key';
oServer.OnQUICConnect := OnQUICConnectEvent;
oServer.OnQUICData := OnQUICDataEvent;
oServer.Start;

procedure TfrmServer.OnQUICDataEvent(Sender: TObject;
  const aConnection: TsgcQUICServerConnection; const aData: TBytes);
begin
  // echo the received bytes back to the peer
  oServer.WriteData(aConnection, aData);
end;

Threading. Jedes Ereignis wird im Hintergrund-Listener-Thread des Servers ausgelöst, und dieser eine Thread treibt die Ereignisschleife für alle Verbindungen an. Halten Sie Handler daher kurz und marshallen Sie zum Haupt-Thread, bevor Sie die UI ansprechen.

Lebenszyklus. Verwenden Sie Start und Stop oder die Eigenschaft Active, auf der sie aufbauen und die den tatsächlichen Listener-Zustand meldet. OnStartup und OnShutdown werden um diese Übergänge herum ausgelöst, sodass Sie aus einem Handler heraus oder nach der Rückkehr des Aufrufs reagieren können, je nachdem, was Ihnen besser passt.

Beziehung zu TsgcHTTP3Server

TsgcHTTP3Server_Base ist von TsgcQUICServer_Base abgeleitet, sodass TsgcHTTP3Server ein QUIC-Server mit darauf aufgesetztem HTTP/3-Protokoll ist. Verwenden Sie TsgcQUICServer, wenn Sie einen rohen Byte-Transport mit Ihrem eigenen Protokoll darauf möchten, und TsgcHTTP3Server, wenn Sie HTTP/3 ausliefern möchten.

Wenn Sie den HTTP/3-Server verwenden, beachten Sie, dass er einen Teil der Oberfläche dieser Komponente beansprucht: Sein Konstruktor setzt AlwaysMultiStream auf True (HTTP/3 benötigt die unidirektionalen Control- und QPACK-Streams des Clients, RFC 9114) und verdrahtet OnQUICConnect, OnQUICData, OnQUICStreamData und OnQUICDisconnect mit seinen eigenen Handlern. Das Zuweisen dieser Ereignisse oder das Zurücksetzen von AlwaysMultiStream beschädigt die HTTP/3-Schicht. OnQUICException ist die eine Ausnahme, der HTTP/3-Server beansprucht es nur, wenn Sie es nicht selbst zugewiesen haben.

Referenz

Zwei Klassen vervollständigen die API:

Wichtige Eigenschaften

NameBeschreibung
QUICOptionsDie gesamte Serverkonfiguration: das obligatorische TLS-Zertifikat und der private Schlüssel sowie das Verbindungslimit.
PortUDP-Port, an den sich der Server bindet. Standardmäßig 80, setzen Sie ihn daher immer.
AlwaysMultiStreamJeden Stream annehmen und lesen, den der Peer öffnet, nicht nur den ersten.
ConnectionsDie Liste der QUIC-Verbindungen, die derzeit vom Server akzeptiert sind.

Wichtige Methoden

NameBeschreibung
StartLädt OpenSSL, erstellt den QUIC-Listener und beginnt, Verbindungen anzunehmen.
StopBeendet das Lauschen, schließt jede aktive Verbindung und gibt den QUIC-Listener frei.
WriteDataSendet Anwendungs-Bytes an einen verbundenen Peer über dessen Standard-QUIC-Stream.
OpenStreamÖffnet einen neuen, vom Server initiierten QUIC-Stream auf einer Verbindung.

Wichtige Ereignisse

NameBeschreibung
OnQUICConnectWird ausgelöst, wenn eine neue QUIC-Verbindung angenommen wurde und ihr erster Stream bereit ist.
OnQUICDataWird ausgelöst, wenn Anwendungsdaten von einem Peer über dessen Standard-QUIC-Stream empfangen werden.
OnQUICStreamDataWird mit Bytes ausgelöst, die von einem zusätzlichen QUIC-Stream gelesen wurden, versehen mit dessen Stream-ID.
OnQUICDisconnectWird ausgelöst, wenn eine QUIC-Verbindung geschlossen wurde.

Vollständiges Beispiel

Ein Echo-Server, der Verbindungen und Fehler meldet. Beachten Sie, dass PeerIP vom Delphi-Server nicht gefüllt wird, da OpenSSL keinen Getter dafür bereitstellt, die Sitzung wird daher über die Verbindungsinstanz identifiziert.


uses
  sgcQUIC_Server, sgcHTTP;

procedure TfrmServer.StartServer;
begin
  oServer := TsgcQUICServer.Create(nil);
  oServer.Port := 4433;

  // mandatory: QUIC always runs over TLS 1.3
  oServer.QUICOptions.SSLOptions.CertFile := 'server.crt';
  oServer.QUICOptions.SSLOptions.KeyFile := 'server.key';
  oServer.QUICOptions.MaxConnections := 1000;

  oServer.OnQUICConnect := OnQUICConnectEvent;
  oServer.OnQUICData := OnQUICDataEvent;
  oServer.OnQUICDisconnect := OnQUICDisconnectEvent;
  oServer.OnQUICException := OnQUICExceptionEvent;
  oServer.OnStartup := OnStartupEvent;

  // Start raises on a bad configuration, OnStartup fires once it is listening
  try
    oServer.Start;
  except
    on E: Exception do
      Log('start failed: ' + E.Message);
  end;
end;

procedure TfrmServer.StopServer;
begin
  oServer.Stop; // OnQUICDisconnect is NOT raised for these connections
  Log('QUIC server stopped');
  oServer.Free;
end;

procedure TfrmServer.OnStartupEvent(Sender: TObject);
begin
  // raised once the listener is bound and accepting
  Log('QUIC server listening on UDP port ' + IntToStr(oServer.Port));
end;

procedure TfrmServer.OnQUICConnectEvent(Sender: TObject;
  const aConnection: TsgcQUICServerConnection);
begin
  Log(Format('connected at %s (%d live)',
    [DateTimeToStr(aConnection.CreatedAt), oServer.ConnectionCount]));
end;

procedure TfrmServer.OnQUICDataEvent(Sender: TObject;
  const aConnection: TsgcQUICServerConnection; const aData: TBytes);
begin
  // aData is a chunk, not a message: frame at the application level
  oServer.WriteData(aConnection, aData);
end;

procedure TfrmServer.OnQUICDisconnectEvent(Sender: TObject;
  const aConnection: TsgcQUICServerConnection; aCode: Int64; const aReason: string);
begin
  // aCode 0 / empty reason on a normal close, otherwise an OpenSSL error code
  Log(Format('disconnected (code=%d %s)', [aCode, aReason]));
end;

procedure TfrmServer.OnQUICExceptionEvent(Sender: TObject;
  const aConnection: TsgcQUICServerConnection; const aException: Exception);
begin
  // aConnection is nil for a listener error, otherwise it is the failing session
  if Assigned(aConnection) then
    Log('connection error: ' + aException.Message)
  else
    Log('listener error: ' + aException.Message);
end;

Anforderungen

VoraussetzungDetail
EditionAll-Access-Edition.
Compiler-DefineQUIC wird deaktiviert ausgeliefert. Entfernen Sie im All-Access-Block von sgcVer.inc den Kommentar vor {$DEFINE SGC_PACK_HTTP3}, ausgeliefert wird es als {.$DEFINE SGC_PACK_HTTP3}. Dieser Block definiert SGC_QUIC, SGC_HTTP3 und SGC_WEBTRANSPORT, aber nur, wenn auch SGC_INDY_LIB definiert ist.
UnitsgcQUIC_Server für die Klassen, sgcHTTP für die registrierte Komponente TsgcQUICServer.
OpenSSLOpenSSL 3.5 oder neuer mit IOHandler = iohOpenSSL. Die integrierte Engine verwendet die native QUIC-Listener-API (OSSL_QUIC_server_method, SSL_new_listener) und die sgc-Engine verwendet die OpenSSL-3.5-QUIC-TLS-API, keine von beiden gibt es in früheren Versionen. Mit iohNativeTLS wird kein OpenSSL benötigt. QUICOptions.SSLOptions.OpenSSL_Options.APIVersion muss oslAPI_3_0 bleiben.
TLSEin Zertifikat und ein passender privater Schlüssel, beide im PEM-Format, sind zwingend erforderlich. Ein verschlüsselter privater Schlüssel wird unterstützt, setzen Sie dessen Passphrase in QUICOptions.SSLOptions.Password.
.NETnet8.0 oder höher und MsQuic zur Laufzeit. Der QUIC-Server existiert aus Gründen der Parität in jedem Ziel-Framework, aber unter netstandard2.0 löst Start() eine PlatformNotSupportedException aus.