sgcWebSockets 2026.5 is a meaty release. Three new infrastructure components turn the WebSocket / HTTP server into a production-grade API gateway: a fully-featured rate limiter, a client-side circuit breaker, and an API key manager with full lifecycle support. On the protocol side there is a new Forex.com client built on a generic Lightstreamer TLCP 2.5 implementation, the MCP Server gains a built-in OAuth 2.1 flow so it can connect directly to browser-based MCP clients such as claude.ai, and the IOCP (Windows) and EPOLL (Linux) I/O engines pick up six measurable performance wins. There is also an early post-quantum primitive: sgcKEM_CreateMLKEM768Keys for ML-KEM-768 keypair generation.
This post walks through the highlights with paste-ready Delphi snippets for the new components.
TsgcWSRateLimiter — drop-in API rate limiting
Drop a TsgcWSRateLimiter next to your server and you have token-bucket, sliding-window or fixed-window rate limiting per IP, per API key, per user or per endpoint pattern, plus daily/monthly quotas and burst protection. The component is thread-safe and persists state to disk (so a server restart does not give every client a fresh allowance).
uses
sgcWebSocket_Server_RateLimiter;
var
oRL: TsgcWSRateLimiter;
oResult: TsgcRateLimitResult;
begin
oRL := TsgcWSRateLimiter.Create(nil);
try
oRL.TokenBucket.Enabled := True;
oRL.TokenBucket.Capacity := 100; // burst size
oRL.TokenBucket.RefillRate := 10; // tokens / interval
oRL.TokenBucket.RefillIntervalMs := 1000;
oRL.PerIP.Enabled := True;
oRL.PerIP.MaxRequests := 60;
oRL.PerIP.WindowSec := 60;
// Consume one token for the current request
oResult := oRL.Consume('ip:' + vClientIP);
if not oResult.Allowed then
RespondHTTP(429, 'Retry-After: ' + IntToStr(oResult.RetryAfterSec));
finally
oRL.Free;
end;
end;
The rule resolver walks PerEndpoint (wildcard-matched), then PerAPIKey, PerUser, and finally PerIP. The OnThrottled, OnQuotaExceeded and OnStateChange events let you log or override every decision.
Demo: Demos\04.WebSocket_Other_Samples\14.RateLimiter — runs a flood-generating client against a server that publishes live stats and reject reasons.
TsgcWSCircuitBreaker — client-side fault isolation
When an upstream API (api.openai.com, a payments gateway, an internal microservice) starts failing or going slow, hanging on TCP timeouts costs you connections, threads and money. TsgcWSCircuitBreaker implements the classic three-state pattern (closed / open / half-open) on top of any TsgcHTTPAPI_client subclass, with a rolling time window, slow-call detection, optional fallback responses, and per-endpoint overrides.
uses
sgcWebSocket_CircuitBreaker;
var
oCB: TsgcWSCircuitBreaker;
begin
oCB := TsgcWSCircuitBreaker.Create(nil);
try
oCB.Thresholds.FailureCount := 5;
oCB.Thresholds.FailureRatePercent := 50;
oCB.Thresholds.SlowCallDurationMs := 2000;
oCB.Thresholds.SlowCallRatePercent := 80;
oCB.TimeWindow.RollingWindowSec := 60;
oCB.Recovery.CooldownSec := 30;
oCB.Recovery.HalfOpenTrialCalls := 3;
oCB.Fallback.Enabled := True;
oCB.Fallback.UseLastSuccess := True;
if oCB.IsCallAllowed('openai') then
try
vResponse := oOpenAI.ChatCompletion(...);
oCB.RecordSuccess('openai');
except
on E: Exception do
begin
oCB.RecordFailure('openai', E.ClassName);
raise;
end;
end
else
vResponse := 'service temporarily unavailable';
finally
oCB.Free;
end;
end;
On Delphi 2009+ a one-liner wrapper is also available: oCB.Execute('openai', procedure begin oOpenAI.ChatCompletion(...) end); — success / failure recording happens automatically.
Demo: Demos\04.WebSocket_Other_Samples\15.CircuitBreaker.
TsgcWSAPIKeyManager — key lifecycle in one component
Most "WebSocket API" servers need API keys: issue, validate, revoke, rotate. TsgcWSAPIKeyManager wraps the full lifecycle with scope-based authorisation, optional hashing at rest, expiration, an audit log, and a built-in rotation grace period so old keys keep working for a configurable window after rotation.
uses
sgcWebSocket_Server_APIKeyManager;
var
oKM: TsgcWSAPIKeyManager;
vKey, vNewKey: string;
begin
oKM := TsgcWSAPIKeyManager.Create(nil);
try
oKM.Generation.Length := 40;
oKM.Generation.Prefix := 'sgc_';
oKM.Hashing.Enabled := True; // store SHA-256 hash, not the plaintext
oKM.Rotation.GracePeriodSec := 86400; // old key valid for 24h after rotation
oKM.Expiration.DefaultTTLSec := 30 * 86400;
// Issue a key for tenant "acme" with read+write scopes, expiring in 7 days
vKey := oKM.IssueKey('acme', ['read', 'write'], 7 * 86400);
// Validate the key on each request, optionally requiring a scope
if not oKM.ValidateKey(vKey, 'read', vClientIP) then
RespondHTTP(401, 'invalid api key');
// Rotate a key (returns the new plaintext; old key remains valid for GracePeriodSec)
oKM.RotateKey(vKey, vNewKey);
finally
oKM.Free;
end;
end;
The component can read the key directly out of the inbound HTTP headers or query string with IsRequestAuthorized, so you do not have to wire the extraction yourself.
Demo: Demos\04.WebSocket_Other_Samples\16.APIKeyManager.
Forex.com client + generic Lightstreamer
The new TsgcWSAPI_Forex client offers unified REST + streaming access to Forex.com (login, ping, market watch, positions, orders, trade history, simulate trade). It is built on a brand-new TsgcWSPClient_Lightstreamer component, a generic Lightstreamer TLCP 2.5 client that implements create_session, bind_session, control (subscribe / unsubscribe) and the LOOP auto-rebind + subscription replay after reconnect. The Lightstreamer client is reusable on its own, so the same code base works with IG Markets and any other Lightstreamer-backed feed.
uses sgcHTTP_API_Forex;
var oFX: TsgcWSAPI_Forex;
begin
oFX := TsgcWSAPI_Forex.Create(nil);
oFX.UserName := 'demo-user';
oFX.Password := 'secret';
oFX.AppKey := 'your-app-key';
oFX.Login;
oFX.SubscribePrices(['EUR/USD', 'GBP/USD', 'XAU/USD']);
oFX.OnPriceUpdate := procedure(const aSymbol: string; aBid, aAsk: Double)
begin
ShowMessage(Format('%s %.5f / %.5f', [aSymbol, aBid, aAsk]));
end;
end;
A full GUI demo lives in Demos\05.Crypto\22.Forex — login, connectivity ping, live market watch, positions, active orders, trade history, stop / limit history and simulate trade, with credentials persisted to sgcForexDemo.ini.
MCP Server — OAuth 2.1 for browser-based MCP clients
The MCP Server can now talk directly to browser-based MCP connectors (such as claude.ai) without an external authorisation server. It auto-publishes the four OAuth discovery / registration endpoints required by the MCP browser-connector spec, runs a PKCE-S256 authorisation flow with an HTML consent screen, and issues refresh tokens:
GET /.well-known/oauth-authorization-server— RFC 8414 metadata.GET /.well-known/oauth-protected-resource— RFC 9728 protected-resource metadata.POST /oauth/register— RFC 7591 dynamic client registration.GET /oauth/authorize— HTML consent form.POST /oauth/token— PKCE S256 + refresh tokens.
CORS is handled inline: OPTIONS preflight returns 204 with full Access-Control-* headers before authentication, every response carries an Access-Control-Allow-Origin reflection of the request origin and a Strict-Transport-Security: max-age=31536000 HSTS header.
IOCP and EPOLL: six tuning wins
The Windows IOCP and Linux EPOLL I/O engines got a focused round of performance and tunability work.
IOCP (Windows)
ThreadAffinity(defaultFalse) onTsgcIndy_IO_Engine— pin engine threads round-robin to logical cores viaSetThreadAffinityMask. Reduces cross-core cache traffic on high-core-count systems.Metrics— a newTsgcIndy_IO_EngineMetricsrecord (AcceptsPosted,AcceptsCompleted,ReadsPosted,ReadsCompleted,ActiveConnections,BytesRead,BytesWritten) updated under a critical section. No more guessing.SendBufferSize,ReceiveBufferSize,TCPNoDelayonTsgcIndy_IOHandler_IO_IOCP— applied inAfterAcceptviasetsockopt, so per-connection tuning no longer requires a customOnConnecthandler.TsgcPerIoDataPoolcapacity raised from 256 to 2048, avoiding theGetMem/FreeMemheap fallback under high connection concurrency. +15–18% WebSocket throughput on loopback benchmarks.
oServer.Bindings.Add.Port := 443;
oServer.IOHandler := TsgcIndy_IOHandler_IO_IOCP.Create(oServer);
with TsgcIndy_IOHandler_IO_IOCP(oServer.IOHandler) do
begin
SendBufferSize := 256 * 1024;
ReceiveBufferSize := 256 * 1024;
TCPNoDelay := True;
Engine.ThreadAffinity := True;
end;
EPOLL (Linux)
AcceptBatchSize,WaitTimeoutMS,HandshakeTimeoutMS— tune the accept loop and the slow-loris guard.- EPOLLOUT-driven write backpressure — when
send()returnsEAGAINon a partial write, the remaining bytes are captured in a per-connection pending buffer and the socket is re-armed withEPOLLIN|EPOLLOUT. The reactor flushes the tail on the nextEPOLLOUTevent instead of dropping the bytes or blocking the worker.
Shared
The IOCP / EPOLL worker pool used to sleep(1) on every iteration including right after a task was processed — effectively capping each worker at ~1,000 ops/s even when the queue was full. The sleep is now skipped while there is work waiting, removing the cap.
HTTP.sys: opt-in high-performance mode
The HTTP.sys server gains a FineTune property with an OperatingMode selector. The default ompClassic preserves the existing behaviour. The new ompHighPerf mode implements the MSDN N-workers × M-pre-posted-async-receives pattern — the recommended architecture for high-throughput HTTP.sys deployments — behind a single property:
oServer := TsgcWebSocketHTTPServer.Create(nil);
oServer.HTTP2Options.SecureOptions.HTTPAPI := True; // use HTTP.sys
oServer.FineTune.OperatingMode := ompHighPerf;
oServer.FineTune.WorkerCount := 8;
oServer.FineTune.PrePostedReceivesPerWorker := 16;
oServer.Active := True;
THttpServerRequest and THttpServerResponse have also been extended with additional fields covering details previously only available through manual parsing.
Post-quantum primitive: ML-KEM-768
A small but forward-looking addition: sgcKEM_CreateMLKEM768Keys generates an ML-KEM-768 (FIPS 203 / Kyber-768) keypair in both PEM and raw-bytes form. ML-KEM is the post-quantum KEM standardised by NIST and on the IETF roadmap for hybrid TLS 1.3 (X25519MLKEM768). This unblocks experimenting with PQ-ready handshakes ahead of the upcoming OpenSSL 3.5 / 3.6 baseline.
uses sgcKEM;
var vPubPEM, vPrivPEM: string; vPubRaw, vPrivRaw: TBytes;
begin
sgcKEM_CreateMLKEM768Keys(vPubPEM, vPrivPEM, vPubRaw, vPrivRaw);
TFile.WriteAllText('mlkem768_pub.pem', vPubPEM);
TFile.WriteAllText('mlkem768_priv.pem', vPrivPEM);
end;
Reliability and conformance fixes
The bug-fix batch closes a long list of MCP, HTTP.sys, IOCP and HTTP/2 issues. Highlights:
- MCP Server — missing or invalid credentials now return
401 Unauthorizedwith aWWW-Authenticate: Bearerheader pointing at the protected-resource metadata, instead of500 Internal Server Error. Required for OAuth discovery by browser-based MCP clients. - MCP Server —
OPTIONSrequests no longer hit the JSON-RPC parser (previously returned500 "Invalid jsonrpc Value"). CORS preflight is now handled before authentication and before the MCP body parser. - MCP Server — tool descriptions, prompt messages and resource contents containing non-ASCII characters no longer break the connection (the JSON body now matches the declared
charset=utf-8). - MCP Server —
tool/prompt/resource/root/template/ completion-ref and completion-argumentnamefields containing non-ASCII characters are now properly emitted and read as JSON\uXXXXescapes rather than raw UTF-16 code points. - HTTP API request body —
TsgcWSComponent_Server.DoHTTPRequestAPIused to callReadStringFromStreamwithout specifying an encoding, defaulting to ASCII and substituting every non-ASCII byte with?before event handlers saw it. Fixed. - HTTP.sys Server — context leak on
ERROR_MORE_DATAinTsgcHTTPServerAPI.DoExecute. The accept loop now grows the buffer and retriesHttpReceiveHttpRequeston the same request id, and releases orphaned contexts on all error paths. - HTTP.sys Server —
TsgcWSConnectionServer_HTTPAPI.DoSendHTTP_Responseno longer setsHTTP_SEND_RESPONSE_FLAG_MORE_DATAon the final response. - IOCP IOHandler —
TsgcIndy_IO_Engine_IOCP_Base.DoStopThreadswas callingWaitForMultipleObjectson an array ofDWORDthread IDs (FThreadsId) instead of thread HANDLEs. Fixed. - IOCP IOHandler — pending I/O operations on sockets are now cancelled on shutdown and on per-socket close.
- IOCP IOHandler — the fragile
Overlapped.Internal = STATUS_PENDINGprobe inDoFreePerIoDatais replaced with an explicitCompleted: Booleanflag onTsgcPerIoData. - HTTP/2 WebBrokerBridge — Invalid pointer operation on
sgcFree(oResponse)inTsgcWSHTTPServer.OnHTTP2RequestEventwhen DataSnap REST handled an HTTP/2HEADERS-only frame.
Upgrading
2026.5 is a drop-in upgrade for existing 2026.4 projects. No interface changes were made to existing components; the new rate-limit / circuit-breaker / API-key components are additive and opt-in. The HTTP.sys high-performance mode is gated behind FineTune.OperatingMode := ompHighPerf — existing code stays on the classic path.
Customers with an active subscription can download the new build from the customer area. Trial users can grab the updated installer at esegece.com/products/sgcwebsockets/sgcwebsockets-download.
Questions, feedback or migration help? Get in touch — you will get a reply from the people who wrote the code.
