sgcWebSockets .NET 2026.8.0 Latest
- 2026.8.0: 2026 August
- NewA graceful STOMP Disconnect now waits until the broker confirms it with a receipt, so nothing is lost when closing. The new DisconnectTimeout option controls how long to wait (10 seconds by default, 0 returns immediately as before).
- NewThe WebSocket server now limits how many control frames a client may send each second (100), so it cannot be flooded with pings.
- NewThe SChannel server can now check the certificate of the clients that connect to it. Set SSLOptions.VerifyCertificate to True and the client certificate goes through the same checks a client applies to a server certificate, the chain and the dates, without the host name check. SSLOptions.VerifyCertificate_Options.FailIfNoCertificate decides what happens when a client sends none, rejecting the connection when True and letting it in when False (False by default). Until now the server never asked the client for a certificate.
- FixedFixed TLSOptions.Version being ignored by the SChannel IO handler. On Windows 11 and Windows Server 2022 it was not read at all, on Windows 10 asking for tls1_3 silently gave TLS 1.2, and leaving it undefined switched SSL 3.0, TLS 1.0 and TLS 1.1 back on. Version is now honoured on both paths, the negotiated version is checked when the handshake completes and the connection fails if it falls outside what was asked for, SSL 3.0 is never requested, and asking for tls1_3 where the platform cannot provide it fails with a message saying why.
- FixedFixed the SChannel IO handler losing every TLS setting on the connections opened on the side, such as an HTTP redirect to another host, and on the client IO handler a SChannel server builds for the connections it opens itself. The clones came back empty, so they ran with VerifyCertificate off and no cipher list, TLS version or ALPN list, and nothing reported it. They now carry the full configuration.
- FixedFixed the SChannel connections after a TLS renegotiation: a new certificate was accepted with no check of the chain or the host name, and the record sizes of the original cipher were kept, so everything sent and received was laid out with the wrong sizes. The full check now runs again whenever the certificate is not the one already accepted, the connection is dropped when it does not pass, and the sizes are read again on every handshake.
- FixedFixed a client using the SChannel IO handler never noticing that the connection was gone when the other end dropped it without a TLS close notification, as happens when a proxy restarts. OnDisconnect never fired, Connected stayed True and the reconnect and WatchDog machinery never ran.
- FixedFixed a client using the SChannel IO handler never completing a TLS 1.3 connection. The session ticket the server sends straight after the handshake left the client waiting for data the server had already sent, so the connection never opened and neither OnConnect nor OnError ever fired.
- FixedThe TLS handshake and every read of the SChannel IO handler are now bounded. The handshake honours ConnectTimeout instead of covering only the TCP connect, so a peer that accepts the socket and then goes silent no longer blocks the thread for ever, and a peer that sends a record a few bytes at a time can no longer hold the reader past the read deadline.
- FixedFixed a startup race in the SChannel IO handler where two connections opened at the same time could see initialization as complete before it actually was, an intermittent access violation. A single connection's handshake error could also unload the shared SSPI library while other SChannel connections were still using it.
- FixedA connection using the SChannel IO handler now sends the TLS close notification before closing, on clients and servers, so a normal disconnect is no longer seen by the other end as a connection cut short, which several exchanges log or rate-limit. It is sent only when it can go out immediately, so closing is never delayed.
- FixedFixed every exchange WebSocket API resending its whole subscription list in one burst after reconnecting, on a connection a few milliseconds old, so an exchange that limits messages per second closed it straight away and the cycle repeated. The replay is now paced. It affects Binance, Bitfinex, Bitget, Bitmex, Bitstamp, Bybit, Cex, CexPlus, Coinbase, CryptoCom, Deribit, GateIO, Huobi, Kraken, Kucoin, MEXC and OKX.
- FixedFixed memory leaks and random crashes when stopping the IOCP/EPOLL server engine while it was busy, and a buffer lost on every message processed with worker threads, a leak that grew with traffic.
- FixedFixed the IOCP and EPOLL servers leaking connections and handles, and crashing, on several cleanup paths: a client dropping the line right after being accepted (a port scan or aborted TLS handshake), a client aborting while a read was pending, a connection released twice when the server was stopped mid-cleanup, an aborted connection consuming an accept slot that was never returned, and a use-after-free with worker threads enabled.
- FixedFixed HTTP keep-alive not working on the IOCP and EPOLL servers. The connection was closed after each request, so the server filled with sockets in TIME_WAIT. Connections now stay open between requests, as with the classic thread engine.
- FixedFixed a memory leak in the Linux (EPOLL) server where every HTTP request leaked pieces of the parsed request, growing with request size until the server ran out of memory. The classic thread engine was not affected.
- FixedFixed the EPOLL server (Linux) sharing one connection queue across all its worker threads, where one or two threads did almost all the work while the rest stayed idle. Each worker now has its own queue.
- FixedFixed the TLS IOCP and EPOLL servers writing past the end of their read buffer when a client sent several encrypted records together, which could overwrite adjacent memory and crash the server, and crashing when a connection could not be set up, for example at the connection limit or when a client dropped during the handshake. The plain TCP servers already handled this correctly.
- FixedFixed an "invalid pointer operation" crash in clients using ConnectTimeout, when the connection attempt finished as its helper thread was being disposed of.
- FixedThe WebSocket server now validates the opening handshake as the standard requires. A missing or non-13 version is refused, a handshake that does not finish within ten seconds is closed, and the number and length of headers are limited. A configured allowed-origins list is now also applied when a client sends no origin at all.
- FixedThe WebSocket connection now rejects invalid frames instead of accepting them: data frames masked by a server, frames setting reserved bits no active extension defines, and close messages whose reason text carried an invalid close code.
- FixedThe WebSocket client now builds its handshake key with a secure random generator, and the server's user name and password check now takes the same time whether or not the password is close to correct, so the response time no longer leaks a hint.
- FixedWhen a client closes a WebSocket connection, the server now sends its own close message back before disconnecting, completing the closing handshake instead of just dropping the connection. This is now the default.
- FixedFixed the MQTT client building invalid packets that brokers rejected: a CONNECT with a user name but no password (or the reverse), and any MQTT 5 packet carrying a larger block of properties. Also fixed it getting stuck after a broken packet on plain TCP, and two threads publishing at once picking the same packet identifier.
- FixedFixed the MQTT 5 client misreading broker replies and reading past the end of a packet. A CONNACK Maximum QoS was read from the wrong byte, SUBSCRIBE and UNSUBSCRIBE replies with a long reason text reported the wrong QoS levels, and a truncated packet made it hand whatever followed in memory to the application as property values. Lengths and properties are now checked against the packet before being read.
- FixedThe MQTT 5 client now honours what the broker tells it: it uses the Keep Alive returned in the CONNACK including for its first ping, passes the real reason code and server reference to OnMQTTDisconnect, raises OnMQTTAuth on an authentication challenge, and delivers a message arriving with only a Topic Alias under its real topic.
- FixedFixed several problems with MQTT QoS 2 publishing. The client now sends the correct follow-up confirmation instead of re-sending the original message, discards a message the broker rejected instead of retrying forever, flags re-sent messages as duplicates, and retries on a sensible schedule instead of on every timer tick.
- FixedSTOMP messages are now delivered exactly as the broker sent them. Multi-line bodies lost their line breaks and an invisible end-of-frame character was left at the end. The client now uses content-length to read the body, so it can contain any character including line breaks and binary zeros, and header values with special characters are escaped following the STOMP 1.1 and 1.2 rules.
- FixedFixed STOMP frames being lost. Several frames packed into one WebSocket message are all processed now, a frame split across two messages is reassembled, and frames received as binary WebSocket messages are no longer ignored. A malformed frame now fires OnSTOMPError and closes the connection, as the specification requires.
- FixedSTOMP ACK and NACK now send the headers required by the negotiated version: id for 1.2, message-id plus subscription for 1.1, message-id for 1.0. NACK is no longer sent on STOMP 1.0. Heart-beats now start only after the server confirms the connection and use the agreed intervals, closing the connection if the server goes silent so the WatchDog can reconnect.
- FixedSTOMP fixes for ActiveMQ: the message priority header was sent without its colon separator so priority was ignored, and unsubscribing did not detect whether the subscription was durable.
- FixedFixed the STOMP client reading far outside a frame when a broker sent a very large content-length, because the check was done with 32 bit maths and overflowed. A body larger than the maximum frame size is now refused, and a frame is limited to 1024 headers.
- FixedThe AMQP 1.0 client is now protected against a bad broker: reading past the end of its buffer when a frame arrives in small pieces, a frame declaring an invalid header size, messages nested too deeply, a small message crafted to expand into a huge amount of memory, arrays whose items are not text symbols, and a text or symbol field declaring four gigabytes out of a small frame.
- FixedThe default maximum frame size for AMQP 0.9.1 and 1.0 is now 1 MB instead of practically unlimited, so a broker cannot make the client hold an enormous frame in memory. You can still raise it. The AMQP 0.9.1 client now also closes the connection with "not implemented" on an unrecognized command, as the specification requires.
- FixedHTTP/2 header compression is now protected against crafted headers: a small block expanding into a huge amount of memory, a length wrapping around to a negative value, and a read one byte past the end of a block. A malformed compressed header now closes the connection cleanly with the correct error, and continuation frames must belong to the stream they started on.
- FixedThe HTTP/2 connection now rejects abuse that could crash it or exhaust memory and CPU: a DATA frame for a stream never opened, a flood of PRIORITY frames building unlimited hidden streams, reuse of a stream number, a flood of empty continuation frames, confusion after a stream reset, and an error code that could read outside a fixed internal table.
- FixedFixed HTTP/2 memory growth where reset streams were never cleaned up. The client now also checks that a response's declared length matches what arrived, and rejects conflicting duplicate content-length headers.
- FixedFixed a misleading error when a TLS connection failed, for example "error:00000006:lib(0):func(0):EVP lib". The real reason reported by OpenSSL was discarded before the exception was raised, it is now shown.
- FixedImproved the errors reported when loading certificates and using newer algorithms. ML-KEM-768 explains that it needs OpenSSL 3.5 or later and shows the version found, a failed legacy provider reports which provider could not be loaded and where it was searched, and a PKCS#12 file using an old algorithm such as RC2 40-bit explains how to enable the legacy provider.
- FixedFixed a pending OpenSSL error being left behind when the certificate file contained the certificate and private key together, which could affect a later call, and real errors while reading the certificate chain being ignored. The OpenSSL options to disable old TLS versions, compression and renegotiation and to prefer the server cipher order were also ignored with OpenSSL 1.1 or later, and are now applied.
- FixedFixed the OAuth2 server sending the authorization code to whatever address the request asked for. The redirect address was never compared with the one registered, so a crafted link could deliver a user's authorization code to somebody else's site. It must now match exactly.
- FixedThe OAuth2 server now cleans the application name and requested scopes before showing them on the sign-in page and strips line breaks from the values used to build the redirect address, closing two ways crafted text could run script or add headers. Also fixed it using memory it had already released when the sign-in page ended up empty.
- FixedFixed the WebAuthn server trusting the FIDO metadata file without checking it. With no root certificate set the check was skipped entirely, so a forged file could make the server accept a fake authenticator. The file is now refused when there is nothing to check it against, and the download verifies the server certificate, using the Windows certificate store with no setup.
- FixedFixed the WebAuthn server reading past the end of the certificate extension it examines when a device registers. The decoder checked nothing, so a truncated or deeply nested extension could make the server read unrelated memory and hand it back, run out of stack, or stop. Every field is now checked, nesting is limited and malformed extensions are refused.
- FixedFixed the limit on the number of response headers in the HTTP client never being applied, so a server could send an endless stream of headers until the client ran out of memory. The limit is now enforced.
- FixedFixed the MCP server writing its internal connection id as a message on the event stream a client opens with GET, which clients such as VS Code GitHub Copilot reported as "Failed to parse message". The session id is still returned in the mcp-session-id header. This also covers the HTTP.sys server.
- FixedFixed the MCP server checking where a request came from only when it carried a header browsers never send, so the check never ran for the case it was meant to stop. A web page the user visited could reach an MCP server on their own machine, list its tools, run them and read the results. The origin is now checked on every request, including the browser's preliminary check. If your MCP client runs in a browser, set ValidateOrigin to False.
- FixedFixed the Files protocol deleting and writing files outside the folder it was given. The incoming file name was used almost as it came, and on Windows forward slashes were kept, so a peer could send a name such as ../../../file and reach anywhere on the disk. The delete side had no protection at all. Names are now reduced to a plain file name and checked, on server and client.
- FixedFixed seven STUN attributes reading past the end of the packet when a peer declared a length shorter than the field carried, which could hand unrelated memory to the application or stop the client. It affects the two ICE control attributes, the reservation token, requested address family, requested transport, even port flag, address error code and ICMP attribute.
- FixedFixed the Binance Spot user data stream, which stopped working when Binance retired the listenKey endpoints it was built on. Account, order and balance updates now come from the Binance WebSocket API, over a second connection the component opens by itself and renews after a reconnection. The events arrive in the same shape, so your existing handlers keep working. The new subscription is signed, so Binance.ApiSecret must now be set as well as Binance.ApiKey. Binance.us and Futures still use a listenKey, so Binance.ListenKeyOnDisconnect now applies only to those two. When a private stream cannot be opened, the error now reports the status the server returned and the message Binance sent back, instead of only "ListenKey cannot be empty.".
- FixedFixed the OKX keepalive, it now sends the ping text message the exchange requires instead of a WebSocket ping, and reconnects when no pong comes back. OnOKXSubscribed and OnOKXUnsubscribed now fire on the subscription reply, they never did before.
- FixedFixed the KuCoin keepalive, it now sends the ping message the exchange requires instead of a WebSocket ping, and the pingTimeout returned when the connection opens is used to reconnect when no pong comes back.
- FixedFixed random crashes in the OnException event of the TCP and HTTP/2 components. The exception was destroyed by the thread that raised it before the event ran, so the handler read freed memory and reported a wrong class name. It now receives a valid copy.