sgcWebSockets 2026.8: QUIC, HTTP/3, statisch OpenSSL en een TLS-revisie | eSeGeCe Blog

sgcWebSockets 2026.8: QUIC, HTTP/3, statisch OpenSSL en een TLS-revisie

· Releases

sgcWebSockets 2026.8 is de grootste release van het jaar. Er zijn vier nieuwe transportcomponenten, QUIC en HTTP/3 aan zowel de client- als de serverkant, OpenSSL kan nu binnen uw uitvoerbare bestand worden gelinkt zodat een TLS-toepassing zonder ook maar één DLL wordt uitgeleverd, en sgcHTML groeit met meer dan dertig componenten, krijgt een WebBroker- en DataSnap-dispatcher en past zich aan een telefoonscherm aan.

Daaronder is de SChannel TLS-laag uit elkaar gehaald en opnieuw opgebouwd. Als u de Windows TLS-stack gebruikt, is dit de release om te installeren: TLSOptions.Version werd stilzwijgend genegeerd, een ingetrokken certificaat werd geaccepteerd, een TLS 1.3-verbinding kwam nooit tot stand, en een heronderhandeling verving het certificaat zonder enige controle. Dat is allemaal opgelost, en controle op intrekking, een minimumversie en verificatie van clientcertificaten op de server zijn nu beschikbaar. De rest van dit bericht is de rondleiding, en elke sectie linkt naar het artikel dat de functie diepgaand behandelt.

QUIC en HTTP/3

Vier nieuwe componenten brengen QUIC (RFC 9000) en HTTP/3 (RFC 9114) naar Delphi en C++ Builder: TsgcQUICClient, TsgcQUICServer, TsgcHTTP3Client en TsgcHTTP3Server. Ze draaien op de native OpenSSL 3.5 QUIC-engine, dus er is geen stack van derden om uit te rollen, en u krijgt TLS 1.3 verweven in de handshake, gemultiplexte streams zonder head-of-line blocking, 0-RTT-hervatting en verbindingsmigratie wanneer de client van netwerk wisselt.

uses
  sgcQUIC_Client;

var
  oClient: TsgcQUICClient;
begin
  oClient := TsgcQUICClient.Create(nil);
  oClient.Host := 'www.example.com';
  oClient.Port := 443;
  oClient.OnQUICConnect    := OnQUICConnect;
  oClient.OnQUICStreamData := OnQUICStreamData;

  oClient.Active := True;          // TLS 1.3 handshake in a single flight
  oClient.WriteData('ping');       // send bytes on a QUIC stream
end;

De HTTP/3-client spreekt elk HTTP-werkwoord, doet QPACK-headercompressie, werkt blokkerend of asynchroon, ontdekt een HTTP/3-endpoint via Alt-Svc en verwerkt server push.

uses
  sgcHTTP3_Client;

var
  oClient: TsgcHTTP3Client;
  vBody: string;
begin
  oClient := TsgcHTTP3Client.Create(nil);
  oClient.OnResponse := OnResponse;
  oClient.OnAltSvc   := OnAltSvc;

  oClient.Connect('www.example.com', 443);
  vBody := oClient.Get('https://www.example.com/');
end;

Beide vereisen het sgcQUIC-pakket met sgcWebSockets Enterprise. Lees meer: QUIC client- en servercomponenten en HTTP/3 client- en servercomponenten.

OpenSSL binnen het uitvoerbare bestand

libcrypto en libssl naast de toepassing uitleveren was altijd al het minst plezierige deel van het uitbrengen van een TLS-client. Vanaf 2026.8 kan de bibliotheek OpenSSL 3.5.7 statisch linken: voeg één unit toe aan de uses-clausule van uw project en de DLL's zijn verdwenen. Verwijder de unit en de DLL's worden weer geladen precies zoals voorheen, dus de keuze is in beide richtingen een wijziging van één regel.

uses
  sgcWebSocket, sgcWebSocket_Classes,
  sgcIdSSLOpenSSL_Static;

Het geldt voor clients en servers, 32 en 64 bit, vanaf Delphi XE2. Lees meer: Statisch OpenSSL linken, geen DLL's meer.

sgcHTML: WebBroker, DataSnap en dertig nieuwe componenten

sgcHTML-pagina's zijn niet langer gebonden aan de sgcWebSockets-server. Een nieuwe dispatcher-component serveert ze vanuit elke WebBroker-toepassing, standalone, ISAPI, Apache of CGI, en vanuit DataSnap-servers, waar de bridge live WebSocket-updates toevoegt op dezelfde poort als uw REST-endpoints.

uses
  Web.HTTPApp, sgcHTMX_Engine_Server_WebBroker, sgcHTMX_Router;

// Any WebBroker host: the engine's Owner is the web module,
// so the standard WebBroker dispatcher calls it automatically
FEngine := TsgcHTMX_Engine_Server_WebBroker.Create(WebModule1);
FEngine.Router := FRouter;   // your htmx routes and the page

De componentenset is in deze release flink gegroeid, en alles wordt nog steeds op de server getekend, zonder client-side bibliotheek die geladen moet worden:

Pagina's passen zich nu ook aan de schermgrootte aan. Op een telefoon klapt het zijmenu weg achter een knop en gebruikt de inhoud de volle breedte, op een desktop verandert er niets. Zet de nieuwe eigenschap Responsive op False voor de vorige vaste lay-out. Lees meer: sgcHTML op WebBroker en DataSnap.

De SChannel TLS-revisie

SChannel is de Windows TLS-stack, degene die u krijgt zonder OpenSSL uit te rollen. Er was een reeks problemen die van buitenaf onzichtbaar waren, en dat is de ergste soort. TLSOptions.Version werd op Windows 11 en Windows Server 2022 helemaal niet gelezen, om TLS 1.3 vragen op Windows 10 gaf u stilzwijgend TLS 1.2, en de versie ongedefinieerd laten zette SSL 3.0, TLS 1.0 en TLS 1.1 weer aan. Een ingetrokken servercertificaat werd geaccepteerd, zelfs met VerifyCertificate op True, omdat de keten werd opgebouwd zonder naar enige intrekkingsstatus te vragen. Na een heronderhandeling werd een nieuw certificaat geaccepteerd zonder controle van de keten of de hostnaam. De verbindingen die Indy er terzijde opent, een HTTP-omleiding naar een andere host of een FTP-datakanaal, kwamen terug met een lege configuratie, dus draaiden ze met verificatie uit en niets meldde dat.

Dat is allemaal opgelost, de onderhandelde versie wordt nu gecontroleerd wanneer de handshake is voltooid, en met dat werk kwamen drie nieuwe optiegroepen mee: controle op intrekking, een minimumversie en Windows strong crypto.

// client
oClient.TLSOptions.Version     := tls1_3;   // highest version to use
oClient.TLSOptions.SChannel_Options.VersionMin := tls1_2;   // lowest accepted
oClient.TLSOptions.SChannel_Options.UseStrongCrypto := True;
oClient.TLSOptions.SChannel_Options.Revocation.Check   := scrcChainExcludeRoot;
oClient.TLSOptions.SChannel_Options.Revocation.Timeout := 5000;  // ms

// or all of it in one line
oClient.TLSOptions.Preset := tlspSecureDefaults;

De standaardwaarden voor intrekking zijn bewust mild: IgnoreRevocationOffline en IgnoreNoRevocationCheck staan op True, dus de controle inschakelen kan geen verbinding breken die vroeger werkte, en de Timeout begrenst het ophalen van CRL en OCSP zodat een onbereikbare responder de handshake niet kan blokkeren. Een certificaat dat werkelijk is ingetrokken wordt altijd geweigerd.

Aan de serverkant kan SChannel de client nu om een certificaat vragen, iets wat het voorheen nooit deed. Het clientcertificaat doorloopt dezelfde controles die een client op een servercertificaat toepast, de keten, de datums en de OnSChannelVerifyPeer-gebeurtenis, zonder de hostnaamcontrole.

oServer.SSLOptions.VerifyCertificate := True;
oServer.SSLOptions.VerifyCertificate_Options.FailIfNoCertificate := True;

Twee andere oplossingen horen hier thuis. Een client die SChannel gebruikte merkte nooit dat de verbinding weg was wanneer de andere kant die verbrak zonder afsluitmelding, zoals gebeurt wanneer een proxy herstart, dus OnDisconnect werd nooit geactiveerd en de WatchDog liep nooit. En een TLS 1.3-verbinding kwam helemaal nooit tot stand, omdat het sessieticket dat de server direct na de handshake verzendt de client liet wachten op gegevens die de server al had verzonden. De handshake respecteert nu ConnectTimeout in plaats van alleen de TCP-connect te dekken, en er wordt een afsluitmelding verzonden voordat wordt gesloten, zowel bij SChannel- als bij Apple- en Android-verbindingen.

Serverlimieten en de IOCP- / EPOLL-engines

Servers kregen een reeks limieten die voorheen onbegrensd waren. De WebSocket-server accepteert standaard 10.000 gelijktijdige verbindingen in plaats van een onbeperkt aantal, en begrenst hoeveel controleframes een client per seconde mag verzenden (100), zodat hij niet met pings kan worden overspoeld. De HTTP/2-server kreeg MaxRequestSize, en de DTLS-component MaxConnections, HandshakeTimeout en IdleTimeout, zodat een vloed van losse pakketten van verzonnen adressen het geheugen van de server niet meer volloopt. Elk daarvan kan worden verhoogd of op 0 gezet voor het vorige gedrag.

De hoogperformante engines kregen een lange lijst reparaties: geheugen- en handle-lekken en crashes op verschillende opruimpaden, een client die de lijn verbreekt vlak nadat hij is geaccepteerd, een verbinding die tweemaal werd vrijgegeven wanneer de server midden in het opruimen werd gestopt, een use-after-free met werkthreads ingeschakeld, en een buffer die bij elk verwerkt bericht verloren ging. HTTP keep-alive werkte helemaal niet, dus de verbinding werd na elk verzoek gesloten en de server liep vol met sockets in TIME_WAIT. Op Linux lekte elk HTTP-verzoek stukken van het geparseerde verzoek, en alle workers deelden één verbindingswachtrij, dus deden een of twee threads bijna al het werk.

Twee nieuwe opties maken het compleet, beide standaard uit: keep-alive-probes en een idle-timeout, zodat half-open sockets zich niet meer opstapelen, en round-robin-verdeling van nieuwe verbindingen over de werkthreads.

Server.IOHandlerOptions.IOHandlerType := iohEPOLL;
Server.IOHandlerOptions.EPOLL.TCPKeepAlive.Enabled  := True;
Server.IOHandlerOptions.EPOLL.TCPKeepAlive.Time     := 60;  // seconds idle before probing
Server.IOHandlerOptions.EPOLL.TCPKeepAlive.Interval := 10;  // seconds between probes
Server.IOHandlerOptions.KeepAliveTimeout            := 120; // seconds

Lees meer: TCPKeepAlive en KeepAliveTimeout voor IOCP- en EPOLL-servers. Met dank aan Andrea voor het bijdragen van de patch waarop verschillende van de engine-fixes zijn gebaseerd.

Protocollen: STOMP, MQTT, AMQP en HTTP/2

STOMP kreeg het meeste werk. Een nette Disconnect wacht nu tot de broker die met een receipt bevestigt, zodat er bij het sluiten niets verloren gaat. Berichten worden precies zo afgeleverd als de broker ze verzond, met content-length om de body te lezen, zodat die regeleindes en binaire nullen kan bevatten. Frames die met meerdere tegelijk in één WebSocket-bericht zijn verpakt worden allemaal verwerkt, een frame dat over twee berichten is verdeeld wordt weer samengevoegd, en binaire frames worden niet langer genegeerd. ACK en NACK verzenden de headers die de onderhandelde versie vereist, en heart-beats starten pas nadat de server de verbinding bevestigt.

oSTOMP.DisconnectTimeout := 10000;   // ms to wait for the receipt, 0 = return at once
oSTOMP.ACKEx(vMessageId, vSubscriptionId);
ShowMessage('STOMP ' + oSTOMP.NegotiatedVersion);

MQTT bouwt niet langer pakketten die brokers weigeren, een CONNECT met een gebruikersnaam en geen wachtwoord of een MQTT 5-pakket met een groter blok eigenschappen, en de MQTT 5-client stopte met lezen voorbij het einde van een pakket: een afgekapt pakket gaf voorheen alles wat er in het geheugen op volgde als eigenschapswaarden door aan de toepassing. Hij respecteert nu wat de broker hem vertelt, van de Keep Alive die in de CONNACK wordt teruggegeven tot een bericht dat alleen met een Topic Alias binnenkomt. Het publicatiepad voor QoS 2 verzendt de juiste vervolgbevestiging, verwerpt een bericht dat de broker heeft geweigerd in plaats van het eindeloos opnieuw te proberen, en probeert het volgens een verstandig schema opnieuw.

De AMQP 1.0-client is nu beschermd tegen een kwaadwillende broker: frames die in kleine stukjes binnenkomen, een ongeldige headergrootte, te diep geneste berichten, een klein bericht dat is opgezet om uit te dijen tot een enorme hoeveelheid geheugen. De standaard maximale framegrootte voor AMQP 0.9.1 en 1.0 is 1 MB in plaats van praktisch onbeperkt. HTTP/2-headercompressie is beschermd tegen geprepareerde headers, de verbinding weigert een DATA-frame voor een stream die nooit is geopend, een vloed van PRIORITY-frames, hergebruik van streamnummers en vloeden van lege continuations, en de server weigert headernamen en -waarden met regeleindes of nulls, wat request smuggling voorkomt. De geheugengroei door gereset streams en de "CONCURRENT STREAM limit has been exceeded" na ongeveer 100 verzoeken zijn beide opgelost.

MCP-server

Tools kunnen nu een volledig JSON Schema voor hun invoer aanbieden, met arrays met items, enums, integer- en nullable-types, geneste objecten en additionalProperties, wat sommige MCP-clients vereisen en anders zouden weigeren. De server biedt ook de metadata van het protocol 2025-11-25 aan: de readOnlyHint-annotatie en de annotatietitel, tool-iconen, servertitel, website en iconen, en de declaratie van task-ondersteuning in tools/list.

Twee oplossingen zijn van belang als u er al een draait. De server schreef zijn interne verbindings-id als een bericht op de event-stream die een client met GET opent, wat clients zoals VS Code GitHub Copilot meldden als "Failed to parse message". En de origin-controle liep alleen wanneer het verzoek een header bevatte die browsers nooit verzenden, dus liep hij nooit voor het geval dat hij moest tegenhouden: een webpagina die de gebruiker bezocht kon een MCP-server op hun eigen machine bereiken, de tools opsommen, ze uitvoeren en de resultaten lezen. De origin wordt nu bij elk verzoek gecontroleerd, inclusief de voorafgaande controle van de browser.

MCPServer.MCPOptions.HTTPStreamable.AllowedOrigins.Add('https://*.example.com');

Crypto-exchanges

Twee klantmeldingen lagen aan het grootste deel hiervan ten grondslag. Een verzoek dat mislukt met een HTTP-foutstatus meldt nu de headers waarmee de server antwoordde, zodat Retry-After bij een 429, of de rate limit-tellers die exchanges teruggeven, eindelijk gelezen kunnen worden. De kant-en-klare API-clients werpen EsgcHTTPAPIProtocolException op, die afstamt van de eerder opgeworpen exception, dus bestaande handlers blijven werken.

try
  vResponse := oBinance.GetAggregateTrades('BTCUSDT');
except
  on E: EsgcHTTPAPIProtocolException do
  begin
    if E.ErrorCode = 429 then
      Sleep(E.RetryAfterMs);
    vWeight := E.GetHeader('X-MBX-USED-WEIGHT-1M');
    // E.ResponseHeaders holds the complete list
  end;
end;

Elke exchange-client verzond na een herverbinding zijn hele abonnementenlijst in één keer opnieuw, over een verbinding van enkele milliseconden oud, dus een exchange die het aantal berichten per seconde beperkt sloot die meteen en de cyclus herhaalde zich. De replay verloopt nu gedoseerd, en bij Binance wordt hij als gecombineerde frames verzonden. Er is ook een nieuwe throttle aan de clientkant op de berichten die u verzendt.

oBinance.Throttle.Enabled     := True;
oBinance.Throttle.MaxMessages := 4;      // per window
oBinance.Throttle.IntervalMs  := 1000;
// paced reconnect replay is on by default:
// oBinance.Throttle.PaceResubscribe := False;  restores the old burst

// subscribe a whole watchlist with ONE frame
oBinance.SubscribeStreams(['btcusdt@trade', 'ethusdt@trade', 'bnbusdt@trade']);

Binance heeft de listenKey-endpoints waarop de Spot user data stream was gebouwd uitgefaseerd, dus account-, order- en saldo-updates komen nu van de Binance WebSocket API, over een tweede verbinding die de component zelf opent en na een herverbinding vernieuwt. De events komen in dezelfde vorm binnen, dus uw handlers blijven werken, maar het nieuwe abonnement is ondertekend: Binance.ApiSecret moet nu net als Binance.ApiKey worden ingesteld. Binance.us en Futures gebruiken nog steeds een listenKey. USD-M Futures-marktgegevens worden nu vanaf twee adressen geleverd, geselecteerd met de nieuwe eigenschap Binance.FuturesStreamEndpoint (bfsePublic standaard, bfseMarket voor aggregate trades, mark price, kline en liquidation streams).

Ook opgelost: OKX-prijzen en -groottes werden voor verzending afgerond op vijf decimalen, dus een order op een instrument met een fijnere tick was niet de order die u had gevraagd, en een waarde onder 0.00001 werd als nul verzonden. De keepalives van OKX en KuCoin verzenden nu de tekst-ping die elke exchange vereist in plaats van een WebSocket-ping, en verbinden opnieuw wanneer er geen pong terugkomt. Lees meer: Binance rate limits: batching, pacing en leesbare 429's.

zlib 1.3.1 en een stuklijst

De meegeleverde zlib gaat van 1.2.12 naar 1.3.1, wat CVE-2022-37434 corrigeert, een heap over-read in inflate. De gelinkte objecten zijn opnieuw gebouwd voor Delphi 7 tot Delphi 13, 32 en 64 bit, en geverifieerd op elke compiler. Een bijkomende oplossing: de zlib-objecten werden op Delphi 10 Seattle 64 bit helemaal niet gelinkt, waardoor de 32 bit-objecten werden gekozen en het compileren mislukte.

Elke setup installeert nu ook sbom.cdx.json, een CycloneDX software bill of materials met de componenten waaruit de bibliotheek is opgebouwd, met hun versies en licenties. Enterprise en All-Access vermelden de versies van de aangepaste Indy en zijn zlib, Core, Standard en Professional noteren dat Indy degene is die met Delphi of C++ Builder wordt meegeleverd. Hij wordt per editie gegenereerd op het moment dat de installer wordt gebouwd, dus hij komt altijd overeen met wat u hebt geïnstalleerd. Lees meer: zlib 1.3.1 in sgcWebSockets en sgcOpenAPI en sgcWebSockets installeert nu zijn eigen SBOM.

Beveiligingsoplossingen

Naast TLS dicht deze release een aantal gaten. Lees de lijst, en als een van deze componenten in uw toepassing zit, upgrade dan.

Nog één die makkelijk te missen is: de meeste TLS-opties gingen verloren bij het kopiëren van de ene component naar de andere. Alleen de IO handler, de ALPN-protocollen en de OpenSSL-opties werden gekopieerd, dus de certificaatbestanden, het wachtwoord, het rootcertificaat, de TLS-versie en VerifyCertificate bleven leeg, en een zo opgezette component verifieerde uiteindelijk helemaal niets.

Kleinere toevoegingen

Ook nieuw rond deze release

Twee onderdelen die met de bibliotheek worden meegeleverd kregen deze maand hun eigen artikel: de REST-servercomponenten, een HTTP-server gebouwd voor REST API's met CORS, metrics, health-endpoints, multi-tenancy en een OpenAPI-plug-in die één specificatiebestand omzet in routering, validatie, beveiliging en Swagger UI, en sgcProtoBuf, de codegenerator die .proto-bestanden omzet in Delphi-units. Lees meer: TsgcHTTPRESTServer: de nieuwe REST-servercomponent, REST-server + OpenAPI, Gebruikers, multi-tenancy en metrics en sgcProtoBuf: van .proto-bestanden naar Delphi-units.

De .NET-editie

sgcWebSockets .NET 2026.8 bevat de gedeelde helft van deze release. Het SChannel-werk zit er allemaal in, de versie die werd genegeerd, de instellingen die op gekloonde verbindingen verloren gingen, de ongecontroleerde heronderhandeling, de TLS 1.3-verbinding die nooit tot stand kwam, de verbroken verbinding die onopgemerkt bleef, de onbegrensde handshake en de race bij het opstarten, plus de afsluitmelding voor het sluiten en de verificatie van clientcertificaten op de server. De IOCP- en EPOLL-fixes, de validatie van de WebSocket-handshake en -frames, de hardening van MQTT, STOMP, AMQP en HTTP/2, de beveiligingsoplossingen voor OAuth2, WebAuthn, MCP en het Files-protocol, en de exchange-wijzigingen (de Binance Spot user data stream, de gedoseerde replay bij herverbinden, de keepalives van OKX en KuCoin) zitten er eveneens in. De nette STOMP-Disconnect met zijn receipt, en de limiet op controleframes in de WebSocket-server, zijn daar ook nieuw.

Upgraden

2026.8 is een drop-in upgrade voor bestaande 2026.x-projecten, met twee dingen die het waard zijn om te weten voordat u bouwt. De Binance Spot user data stream ondertekent nu zijn abonnement, dus Binance.ApiSecret moet naast Binance.ApiKey worden ingesteld. En de WebSocket-server accepteert nu standaard 10.000 gelijktijdige verbindingen in plaats van een onbeperkt aantal, dus verhoog MaxConnections of zet het op 0 als u daarboven zit.

Al het andere staat uit tot u erom vraagt: controle op intrekking, de minimumversie, strong crypto, verificatie van clientcertificaten, keep-alive op de IOCP- en EPOLL-engines, de berichtenthrottle en de lijst met toegestane MCP-origins staan allemaal standaard op het vorige gedrag.

Klanten met een actief abonnement kunnen de nieuwe build downloaden uit het klantengedeelte, of van esegece.com/products/websockets/download.

Vragen, feedback of hulp bij migratie? Neem contact op. U krijgt antwoord van de mensen die de code hebben geschreven.