Commit Graph

11 Commits

Author SHA1 Message Date
Joseph Doherty
18a6d0f478 fix: address code review findings for WebSocket implementation
- Convert WsReadInfo from mutable struct to class (prevents silent copy bugs)
- Add handshake timeout enforcement via CancellationToken in WsUpgrade
- Use buffered reading (512 bytes) in ReadHttpRequestAsync instead of byte-at-a-time
- Add IAsyncDisposable to WsConnection for proper async cleanup
- Simplify redundant mask bit check in WsReadInfo
- Remove unused WsGuid and CompressLastBlock dead code from WsConstants
- Document single-reader assumption on WsConnection read-side state
2026-02-23 05:27:36 -05:00
Joseph Doherty
ca88036126 feat: integrate WebSocket accept loop into NatsServer and NatsClient
Add WebSocket listener support to NatsServer alongside the existing TCP
listener. When WebSocketOptions.Port >= 0, the server binds a second
socket, performs HTTP upgrade via WsUpgrade.TryUpgradeAsync, wraps the
connection in WsConnection for transparent frame/deframe, and hands it
to the standard NatsClient pipeline.

Changes:
- NatsClient: add IsWebSocket and WsInfo properties
- NatsServer: add RunWebSocketAcceptLoopAsync and AcceptWebSocketClientAsync,
  WS listener lifecycle in StartAsync/ShutdownAsync/Dispose
- NatsOptions: change WebSocketOptions.Port default from 0 to -1 (disabled)
- WsConnection.ReadAsync: fix premature end-of-stream when ReadFrames
  returns no payloads by looping until data is available
- Add WsIntegration tests (connect, ping, pub/sub over WebSocket)
- Add WsConnection masked frame and end-of-stream unit tests
2026-02-23 05:16:57 -05:00
Joseph Doherty
6d0a4d259e feat: add WsConnection Stream wrapper for transparent framing 2026-02-23 04:58:56 -05:00
Joseph Doherty
fe304dfe01 fix: review fixes for WsReadInfo and WsUpgrade
- WsReadInfo: validate 64-bit frame payload length against maxPayload
  before casting to int (prevents overflow/memory exhaustion)
- WsReadInfo: always send close response per RFC 6455 Section 5.5.1,
  including for empty close frames
- WsUpgrade: restrict no-masking to leaf node connections only (browser
  clients must always mask frames)
2026-02-23 04:55:53 -05:00
Joseph Doherty
1c948b5b0f feat: add WebSocket HTTP upgrade handshake 2026-02-23 04:53:21 -05:00
Joseph Doherty
bd29c529a8 feat: add WebSocket frame reader state machine 2026-02-23 04:51:54 -05:00
Joseph Doherty
1a1aa9d642 fix: use byte-length for close message truncation, add exception-safe disposal
- CreateCloseMessage now operates on UTF-8 byte length (matching Go's
  len(body) behavior) instead of character length, with proper UTF-8
  boundary detection during truncation
- WsCompression.Compress now uses try/finally for exception-safe disposal
  of DeflateStream and MemoryStream
2026-02-23 04:47:57 -05:00
Joseph Doherty
d49bc5b0d7 feat: add WebSocket permessage-deflate compression
Implement WsCompression with Compress/Decompress methods per RFC 7692.
Key .NET adaptation: Flush() without Dispose() on DeflateStream to produce
the correct sync flush marker that can be stripped and re-appended.
2026-02-23 04:42:31 -05:00
Joseph Doherty
8ded10d49b feat: add WebSocket frame writer with masking and close status mapping 2026-02-23 04:40:44 -05:00
Joseph Doherty
6981a38b72 feat: add WebSocket origin checker 2026-02-23 04:35:06 -05:00
Joseph Doherty
72f60054ed feat: add WebSocket protocol constants (RFC 6455)
Port WsConstants from golang/nats-server/server/websocket.go lines 41-106.
Includes opcodes, frame header bits, close status codes, compression
constants, header names, path routing, and the WsClientKind enum.
2026-02-23 04:33:04 -05:00