sgcWebSockets 2026.7 : clustering, historique des messages, async / await et clients renforcés | Blog eSeGeCe

sgcWebSockets 2026.7 : clustering, historique des messages, async / await et clients renforcés

· Versions

sgcWebSockets 2026.7 est une version consacrée à la mise en production. Vous pouvez désormais répartir une application WebSocket sur plusieurs serveurs et garder les canaux, le pub/sub et la présence fonctionnels sur l'ensemble. Les canaux peuvent se souvenir de leurs messages récents, de sorte qu'un client qui tombe et revient récupère tout ce qu'il a manqué. Les clients se reconnectent avec un backoff exponentiel et du jitter au lieu de marteler le serveur au même instant, la file d'envoi peut être bornée pour qu'un consommateur lent ne puisse pas épuiser la mémoire du serveur, et le client HTTP a gagné un ensemble de protections contre les redirections qui fuitent les identifiants, le TLS dégradé et les réponses sans limite de taille.

À cela s'ajoutent une couche async / await pour Delphi, gzip et deflate sur les serveurs HTTP, la nouvelle méthode HTTP QUERY, la vérification d'identité pour le chiffrement de bout en bout, OAuth2 sur le client MCP, et une longue liste de corrections. Cet article est la visite guidée. Chaque section renvoie à l'article qui traite la fonctionnalité en profondeur.

Le clustering sur plusieurs serveurs

Le nouveau composant TsgcWSCluster relie plusieurs serveurs sgcWebSockets en un seul serveur logique. Un message publié sur le nœud A atteint les abonnés connectés au nœud B, et la présence renvoie la liste de l'ensemble du cluster au lieu du seul nœud local. Deux backplanes sont disponibles : un maillage intégré, où les nœuds communiquent directement entre eux et où vous n'installez rien de plus, et Redis Pub/Sub pour les déploiements plus importants (Enterprise).

uses
  sgcWebSocket, sgcWebSocket_Protocols, sgcWebSocket_Cluster;

oCluster := TsgcWSCluster.Create(nil);
oCluster.EngineType  := clusterMesh;          // no extra infrastructure
oCluster.ClusterPort := 5410;                 // this node's mesh listener
oCluster.Peers.Add('192.168.1.101:5410');     // the other nodes
oCluster.Attach(oProtocol);                   // cluster this protocol's pub/sub
oCluster.Start;

En savoir plus : Répartir les serveurs WebSocket sur plusieurs nœuds.

Historique des messages et récupération à la reconnexion

Le serveur du protocole sgc peut désormais conserver les N derniers messages de chaque canal, éventuellement avec une durée de vie. Lorsqu'un client se reconnecte et se réabonne, il envoie l'offset du dernier message qu'il a réellement vu, et le serveur rejoue tout ce qui a été publié pendant son absence. Rien ne change dans le code de votre client au-delà du réabonnement que vous faites déjà, et l'ensemble est désactivé tant que vous ne l'activez pas.

oProtocol.History.Enabled    := True;
oProtocol.History.Size       := 1000;   // last 1000 messages per channel
oProtocol.History.TTLSeconds := 3600;   // optional: also expire after 1 hour

En savoir plus : Historique des messages et récupération à la reconnexion.

async / await, et un Connect qui attend vraiment

La nouvelle unité sgcBase_AsyncAwait apporte les tâches et les futures à la bibliothèque. Vous pouvez attendre une requête HTTP, une connexion WebSocket ou une complétion de chat AI sans câbler le moindre gestionnaire d'événement, chaîner le résultat avec ThenProc et OnError, et annuler une tâche d'une manière qui interrompt réellement la requête sous-jacente.

Dans le même esprit, Connect bloque maintenant jusqu'à ce que la connexion soit complètement établie, et non simplement démarrée, et Disconnect attend que le socket soit réellement fermé. Cela referme une situation de concurrence qui posait problème lors des reconnexions rapides. Lorsqu'une connexion ou un envoi échoue, la raison est disponible dans la nouvelle propriété LastError, et il existe une surcharge de Connect qui vous renvoie directement la chaîne d'erreur, ce qui vous permet de diagnostiquer un échec sans vous abonner à OnError.

var
  vError: string;
begin
  if sgcWebSocketClient1.Connect(10000) then
    sgcWebSocketClient1.WriteData('hello')
  else
    ShowMessage('Connect failed: ' + sgcWebSocketClient1.LastError);

  // or get the reason back directly from the call
  if not sgcWebSocketClient1.Connect(vError, 10000) then
    ShowMessage('Connect failed: ' + vError);
end;

En savoir plus : async / await en Delphi.

Le renforcement du client HTTP

Une redirection est le moyen le plus simple de perdre un jeton. Si un serveur répond à votre requête authentifiée par un 302 vers un hôte différent, le client y rejoue allègrement les en-têtes Authorization et Cookie. 2026.7 ajoute StripAuthOnCrossHostRedirect pour les supprimer lors d'une redirection intersites et NoInsecureRedirect pour refuser une redirection qui rétrograde HTTPS vers du HTTP en clair.

Les réponses peuvent également être bornées, avec MaxChunkSize pour un seul morceau d'une réponse chunked et MaxResponseSize pour le total. RejectPublicSuffixCookies jette les cookies revendiqués pour des domaines trop larges tels que "com" ou "co.uk". Pour les téléversements, il y a Expect: 100-continue, qui demande la permission du serveur avant d'envoyer un corps volumineux, et un corps de requête chunked pour diffuser un téléversement dont vous ne connaissez pas encore la taille.

oHTTP.StripAuthOnCrossHostRedirect := True;
oHTTP.NoInsecureRedirect           := True;
oHTTP.MaxResponseSize              := 10 * 1024 * 1024;  // 10 MB, 0 = unlimited
oHTTP.MaxChunkSize                 := 1024 * 1024;       // 1 MB per chunk
oHTTP.RejectPublicSuffixCookies    := True;

Tous ces commutateurs sont désactivés par défaut, et aucun d'entre eux ne vous oblige à descendre jusqu'à l'objet HTTP de bas niveau : ils sont également publiés sur les clients d'API prêts à l'emploi (AI/LLM, plateformes crypto, OAuth2, Google Cloud, WhatsApp, AWS SQS, WebPush). Les protections de redirection et de taille sont également présentes sur le client HTTP/2, et HTTP/3 gagne la limite de taille de réponse. En savoir plus : Renforcer le client HTTP.

Nouvelles tentatives, backoff et jitter

Le WatchDog se reconnectait auparavant à intervalle fixe, ce qui signifie qu'un millier de clients qui perdent le serveur reviennent tous au même instant. Il prend désormais en charge le backoff exponentiel avec un multiplicateur, un plafond et un jitter aléatoire, afin que la flotte s'étale. L'intervalle fixe reste la valeur par défaut, donc rien ne change tant que vous ne le demandez pas.

Le client HTTP a gagné sa propre politique de nouvelles tentatives : réessayer sur une liste configurable de codes de statut, attendre plus longtemps à chaque fois, et respecter l'indication Retry-After du serveur lorsqu'il en envoie une. Les clients OpenAI, Anthropic et Gemini l'utilisent aussi.

sgcWebSocketClient1.WatchDog.Backoff           := wdbExponential;
sgcWebSocketClient1.WatchDog.BackoffMultiplier := 2.0;   // double every attempt
sgcWebSocketClient1.WatchDog.MaxInterval       := 60;    // seconds, the ceiling
sgcWebSocketClient1.WatchDog.Jitter            := 0.2;   // up to 20% random spread

En savoir plus : Backoff exponentiel, jitter et Retry-After.

Contre-pression sur la file d'envoi

Lorsqu'un client lit plus lentement que vous n'écrivez, les messages mis en file s'accumulent dans la mémoire du serveur. Vous pouvez désormais plafonner le nombre de messages en attente par connexion avec QueueOptions.MaxQueueSize et choisir ce qui se passe lorsque le plafond est atteint : supprimer le message le plus ancien, supprimer le plus récent, ou déconnecter la connexion. Le nouvel événement OnSendBufferFull se déclenche avant toute suppression, ce qui vous permet de le journaliser, de le compter, ou d'opposer un veto à la suppression pour une connexion que vous ne pouvez pas vous permettre de tronquer. La file est illimitée par défaut.

oServer.QueueOptions.MaxQueueSize   := 1000;
oServer.QueueOptions.OverflowPolicy := qopDropOldest;  // or qopDropNewest, qopDisconnect

En savoir plus : Limites de file d'envoi pour les clients WebSocket lents.

gzip et deflate sur le serveur HTTP

Les serveurs HTTP peuvent maintenant compresser leurs réponses dès que le client annonce Accept-Encoding. Cela s'applique aux fichiers servis depuis DocumentRoot et aux réponses que vous construisez vous-même dans OnCommandGet, cela ignore les corps en dessous d'une taille minimale ainsi que les types de contenu qui ne se réduiraient pas, et c'est disponible sur le serveur HTTP.sys également. La compression est désactivée par défaut, et seules les réponses HTTP/1.1 sont compressées pour l'instant.

sgcWebSocketHTTPServer1.HTTPCompression.Enabled := True;
sgcWebSocketHTTPServer1.HTTPCompression.Level   := 6;     // 1..9
sgcWebSocketHTTPServer1.HTTPCompression.MinSize := 1024;  // bytes

En savoir plus : Compression gzip et deflate sur le serveur HTTP.

La méthode HTTP QUERY

QUERY est une nouvelle méthode HTTP de l'IETF qui envoie une recherche dans le corps de la requête comme un POST, tout en restant sûre et cacheable comme un GET, de sorte que votre requête n'est plus comprimée dans une URL avec une limite de longueur. 2026.7 l'implémente sur les clients HTTP/1.x, HTTP/2, HTTP/3 et REST, et les serveurs ainsi que le proxy la prennent en charge.

vResult := oClient.Query('https://api.example.org/contacts', oQuery);

En savoir plus : La nouvelle méthode HTTP QUERY en Delphi.

Vérification d'identité E2EE

Le chiffrement de bout en bout garde la charge utile privée, mais à lui seul il ne vous dit pas qui se trouve à l'autre bout. Un serveur ou un relais au milieu peut remettre à chaque partie sa propre clé et tout lire. Dans 2026.7, chaque partie peut signer sa clé de chiffrement avec une clé d'identité à long terme, et l'autre partie vérifie cette signature, de sorte qu'un échange de clé silencieux est détecté au lieu d'être accepté.

Cela fonctionne pour le un à un et pour les discussions de groupe, cela ajoute des événements pour approuver ou épingler l'identité d'un pair et pour vous avertir lorsque la clé d'identité d'un pair change, et c'est désactivé par défaut et totalement rétrocompatible avec les pairs 2026.6.

E2EE.E2EE_Options.Identity.Enabled    := True;
E2EE.E2EE_Options.Identity.PrivateKey := LoadIdentityPrivateKey;  // PEM
E2EE.E2EE_Options.Identity.PublicKey  := LoadIdentityPublicKey;   // PEM
E2EE.OnE2EEVerifyPeerIdentity := OnE2EEVerifyPeerIdentityEvent;
E2EE.OnE2EEKeyChange          := OnE2EEKeyChangeEvent;

En savoir plus : Vérification d'identité E2EE.

OAuth2 pour le client MCP, et le grant Identity Assertion

Le client MCP peut maintenant s'authentifier avec OAuth2 au lieu d'une clé d'API statique. Il récupère lui-même le jeton, le met en cache jusqu'à son expiration puis le rafraîchit, et lorsque OAuth2 est activé il a priorité sur la clé d'API. À côté de cela, le client OAuth2 gagne un nouveau type de grant, l'Identity Assertion Authorization Grant, qui chaîne une identité d'un domaine vers un autre : le client exécute pour vous tout l'échange en plusieurs étapes et déclenche des événements pour que vous puissiez suivre chaque étape.

MCPClient.MCPOptions.AuthenticationOptions.OAuth2.Enabled := True;
MCPClient.MCPOptions.AuthenticationOptions.OAuth2.GrantType := auth2ClientCredentials;
MCPClient.MCPOptions.AuthenticationOptions.OAuth2.TokenURL := 'https://auth.example.com/oauth2/token';
MCPClient.MCPOptions.AuthenticationOptions.OAuth2.ClientId := 'YOUR_CLIENT_ID';
MCPClient.MCPOptions.AuthenticationOptions.OAuth2.ClientSecret := 'YOUR_CLIENT_SECRET';

En savoir plus : OAuth2 pour le client MCP.

Flux crypto : réabonnement et limitation de débit

Jusqu'à présent, lorsqu'un socket de données de marché tombait et que le WatchDog le rétablissait, le socket était de nouveau debout mais vous n'étiez abonné à rien tant que vous n'aviez pas rejoué vous-même chaque abonnement. Les clients de plateforme peuvent désormais le faire pour vous : définissez Resubscribe := True et vos flux sont rétablis après une reconnexion. Cela couvre Binance, Kraken, Coinbase et environ quatorze autres plateformes, ainsi que le flux basé sur une session de XTB. La valeur par défaut est False.

Les clients d'API REST gagnent un limiteur de débit optionnel côté client, afin qu'une rafale de requêtes ne vous vaille pas un bannissement temporaire de la part d'une plateforme. Plafonnez le nombre de requêtes envoyées dans une fenêtre de temps, et choisissez si une requête excédentaire attend ou est refusée. Désactivé par défaut.

oBinance.RateLimit.Enabled     := True;
oBinance.RateLimit.IntervalMs  := 60000;   // one minute window
oBinance.RateLimit.MaxRequests := 1000;
oBinance.RateLimit.Behavior    := rlbWait;

En savoir plus : Le réabonnement et le limiteur de débit côté client.

Ajouts plus modestes

Corrections et renforcement

2026.7 apporte une longue liste de corrections. Plutôt que de toutes les répéter, voici ce à quoi elles aboutissent.

Sûreté mémoire et renforcement des analyseurs. Des lectures au-delà de la fin du tampon ont été corrigées dans le client MQTT 5 (un broker annonçant plus de données qu'il n'en a envoyées), le client AMQP 1.0 (décodage d'UUID), l'analyseur STUN/TURN (y compris une boucle infinie et les adresses IPv6), le canal de données SCTP de WebRTC et le scanner de trames TCP brut. Le client et le serveur UDP, DTLS compris, ne libèrent plus deux fois un socket et ne le laissent plus pendant à la déconnexion. Des plafonds internes ont été ajoutés là où un pair mal intentionné pouvait autrement faire croître la mémoire sans limite : la file de messages en attente MQTT, les abonnements par connexion du protocole sgc, les appels en attente du serveur WAMP, les lignes de trailer d'une réponse chunked et la profondeur d'imbrication des tables de champs AMQP.

HTTP/2. Un plafond sur la taille décompressée (64 Mo par défaut) referme une faille de saturation mémoire avec les réponses de type "zip bomb", la limite de taille de réponse ne peut plus être contournée par la dernière partie d'une réponse, et une requête en échec échoue désormais immédiatement au lieu d'attendre l'expiration complète du délai.

Cryptographie et aléa. Le serveur WebAuthn, le serveur d'autorisation OAuth2 et les identifiants de session HTTP intégrés utilisent maintenant un générateur aléatoire cryptographiquement sûr, les challenges sont à usage unique, et les secrets sont comparés en temps constant (secret client, signature JWT, mot de passe Basic de l'API HTTP). Un JWT avec une signature valide mais une revendication incorrecte est désormais rejeté au lieu d'être accepté. Avec StrictVerify activé sous OpenSSL, le certificat est aussi vérifié par rapport au nom d'hôte, donc un certificat valide émis pour un autre hôte est rejeté. Les connexions vers une adresse IP et les réglages existants ne sont pas affectés.

Server-Sent Events. Une réponse en flux qui livrait plusieurs événements en une seule lecture, ou un événement réparti sur deux lectures, perdait tout sauf le premier. C'est corrigé, ce qui rétablit le streaming jeton par jeton pour les clients AI/LLM. Le chat en streaming d'OpenAI ne définissait pas non plus le drapeau de flux, donc il diffuse maintenant comme les autres fournisseurs.

Clients de plateformes. Les identifiants de requête Kraken sont maintenant strictement croissants, BitMEX envoie son expiration en secondes au lieu de millisecondes, MEXC signe les paramètres encodés qu'il envoie réellement, Cryptorobotics n'intervertit plus deux valeurs dans les appels par hachage, Deribit rafraîchit son jeton de connexion avant qu'il n'expire, et le client Forex signale l'erreur qu'il avalait auparavant lors de la lecture de l'identifiant de compte.

Lazarus et Free Pascal. Trois corrections de compilation : les interfaces de l'unité JSON utilisent maintenant la bonne convention d'appel selon la plateforme (elles ne compilaient pas sous Linux), le client Forex n'appelle plus une fonction propre à Delphi pour lire le format des nombres, et l'unité d'échange de clés post-quantique ne dépend plus de size_t.

Il y en a davantage dans history.txt, notamment le client gRPC (des appels concurrents pouvaient recevoir les réponses des autres, et un échec d'envoi était signalé comme un succès), le serveur OpenAPI (le chargement de la spécification est maintenant thread-safe et vos gestionnaires ne s'exécutent plus pendant qu'un verrou interne est détenu), une fuite de connexion et de socket serveur à chaque échec de connexion, un plantage sur les réponses OpenAI sans champ "message" comme celles d'Ollama et de LM Studio, la correction de WriteAndWaitData sur le client WinHTTP, et la censure des secrets dans le journal du client HTTP, de sorte qu'activer la journalisation n'écrit plus les jetons en clair sur le disque.

L'édition .NET

sgcWebSockets .NET 2026.7 se concentre sur le client HTTP. Elle livre le même ensemble de protections que l'édition Delphi : suppression des identifiants lors d'une redirection intersites, blocage d'une rétrogradation de HTTPS vers HTTP, plafonds de taille par chunk et sur la réponse totale, rejet des cookies de suffixe public, Expect: 100-continue et corps de requête chunked. Elles sont disponibles sur les composants client HTTP et sur les clients d'API prêts à l'emploi (les clients AI/LLM, OAuth2, Google Cloud, WhatsApp, AWS SQS, WebPush), et les mêmes protections de redirection et de taille sont présentes sur le client HTTP/2. Les corrections de la gestion des redirections et de l'expiration des cookies sont également incluses. Tout est désactivé par défaut.

Mise à jour

2026.7 est une mise à jour transparente pour les projets 2026.x existants. Chaque nouvelle fonctionnalité de cette version est désactivée par défaut : le clustering, l'historique des messages, le backoff, le plafond de la file d'envoi, la compression HTTP, les protections du client, la vérification d'identité E2EE et le réabonnement des plateformes doivent tous être activés explicitement, donc installer la nouvelle version ne change aucun comportement tant que vous n'avez pas choisi de les activer.

Les clients disposant d'un abonnement actif peuvent télécharger la nouvelle version depuis l'espace client, ou sur esegece.com/products/websockets/download.

Des questions, des remarques ou besoin d'aide pour migrer ? Contactez-nous. Vous obtiendrez une réponse des personnes qui ont écrit le code.