Files
natsnet/docs/plans/2026-02-27-batch-26-websocket-design.md
Joseph Doherty c05d93618e Add batch plans for batches 23-30 (rounds 12-15)
Generated design docs and implementation plans via Codex for:
- Batch 23: Routes
- Batch 24: Leaf Nodes
- Batch 25: Gateways
- Batch 26: WebSocket
- Batch 27: JetStream Core
- Batch 28: JetStream API
- Batch 29: JetStream Batching
- Batch 30: Raft Part 1

All plans include mandatory verification protocol and anti-stub guardrails.
Updated batches.md with file paths and planned status.
2026-02-27 16:33:10 -05:00

7.2 KiB

Batch 26 (WebSocket) Design

Context Snapshot

  • Batch: 26 - WebSocket
  • Scope: 37 features, 86 tests
  • Dependencies: 16 (Client Core first half), 18 (Server Core)
  • Go source: golang/nats-server/server/websocket.go (+ websocket_test.go for tests)
  • Current state:
    • Batch status is pending.
    • Batch is not ready (batch ready currently lists only batches 0, 1, 5, 8).
    • .NET websocket code is largely stubbed in dotnet/src/ZB.MOM.NatsNet.Server/WebSocket/WebSocketTypes.cs.
    • Multiple websocket-related stubs exist outside the websocket folder (ClientConnection, NatsServer.Lifecycle, NatsServer.Listeners).

Goals

  1. Port all 37 Batch 26 websocket features from server/websocket.go with behaviorally equivalent logic.
  2. Port and verify all 86 Batch 26 mapped tests with no fake/stub assertions.
  3. Remove websocket-specific stubs (NotImplementedException, placeholder comments, always-false flags) in runtime paths touched by this batch.
  4. Keep implementation idiomatic C# while preserving protocol correctness (frame parsing, masking, compression, upgrade validation, close/status handling).

Non-Goals

  1. Rewriting unrelated subsystems (MQTT, full leaf routing internals, non-websocket JetStream internals).
  2. Performance tuning beyond parity with Go logic and existing .NET architecture.
  3. Advancing blocked dependencies (Batches 16/18) inside this batch plan.

Approaches Considered

Approach A: Monolithic Port in WebSocketTypes.cs

  • Description: Implement all websocket behavior inside the existing single file.
  • Pros: Minimal file churn.
  • Cons: Hard to review/test, encourages oversized methods, mixes protocol codec, HTTP upgrade, and server lifecycle logic in one place.
  • Description: Keep websocket-focused primitives in WebSocket/ and wire server/client integration through partial class methods in NatsServer and ClientConnection.
  • Pros: Clear boundaries, easier per-feature verification, easier targeted tests, aligns with existing partial-class architecture in NatsServer.
  • Cons: More file edits and some type movement from current stubs.

Approach C: Rely on System.Net.WebSockets High-Level Abstractions

  • Description: Use framework websocket APIs for framing/handshake and adapt behavior.
  • Pros: Less low-level frame code.
  • Cons: Poor fit for NATS semantics (manual handshake headers, no-masking extension, explicit frame control, compression toggles, client/leaf modes). Higher behavior drift risk vs Go.

1) Component Boundaries

  1. Frame/read-path primitives:
    • WsReadInfo state machine (init, Read, ReadByte, NextCBuf, Decompress, Unmask).
    • Static helpers for WsGet, WsIsControlFrame, frame-header creation/fill, masking utilities, close payload creation.
  2. Client websocket behavior:
    • ClientConnection websocket methods (WsRead, control frame handling/enqueue, close mapping, outbound collapse).
    • IsWebSocket() no longer hardcoded false once websocket state exists on connection.
  3. HTTP upgrade + option validation:
    • Handshake validation and response (WsUpgrade, header checks, accept key, compression negotiation, origin checks).
    • Options parsing/validation (ValidateWebsocketOptions, origin/header settings, auth override).
  4. Server lifecycle/listener integration:
    • Start/close websocket listener and include it in readiness checks.
    • Keep server INFO websocket connect URLs updated and propagated.

2) Data Flow

  1. Upgrade flow:
    • HTTP request validation -> origin check -> extension negotiation -> connection hijack -> 101 response -> websocket context creation.
  2. Inbound frame flow:
    • Header decode -> mask enforcement -> payload size handling -> optional control-frame handling -> optional decompression -> payload slices returned for parser.
  3. Outbound frame flow:
    • Pending buffers -> optional compression -> fragmentation rules (browser/safari constraints) -> optional masking -> queued frames + close frame semantics.

3) Error Handling Model

  1. Protocol violations map to websocket close frames with RFC status codes, then close/read termination behavior.
  2. Handshake failures return HTTP status + server-side error logging.
  3. Decompression limit violations map to payload errors (max payload).
  4. Any runtime branch that cannot be implemented due dependency/runtime gap is marked deferred with explicit reason, never stubbed.

4) File-Level Plan

Likely touched files:

  • Source:
    • dotnet/src/ZB.MOM.NatsNet.Server/WebSocket/WebSocketTypes.cs (replace stubs/split responsibilities)
    • dotnet/src/ZB.MOM.NatsNet.Server/WebSocket/WebSocketConstants.cs
    • dotnet/src/ZB.MOM.NatsNet.Server/ClientConnection.cs
    • dotnet/src/ZB.MOM.NatsNet.Server/NatsServer.Listeners.cs
    • dotnet/src/ZB.MOM.NatsNet.Server/NatsServer.Lifecycle.cs
    • dotnet/src/ZB.MOM.NatsNet.Server/NatsServer.Init.cs
    • dotnet/src/ZB.MOM.NatsNet.Server/NatsServer.cs
  • Tests:
    • dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/WebSocketHandlerTests.Impltests.cs
    • dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/JetStreamFileStoreTests.Impltests.cs
    • dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/NatsConsumerTests.Impltests.cs
    • dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/LeafNodeHandlerTests.Impltests.cs
    • dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/ConcurrencyTests1.Impltests.cs
    • dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/ConcurrencyTests2.Impltests.cs
    • New backlog classes likely required by mapping:
      • dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/CoreBenchmarks.Impltests.cs
      • dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/PublishBenchmarks.Impltests.cs
      • dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/LeafNodeProxyTests.Impltests.cs

5) Test Strategy

  1. Use strict ID-driven mapping from PortTracker for all 86 tests.
  2. For websocket functional tests, port assertions against frame semantics, handshake negotiation, header behavior, compression behavior, and outbound framing.
  3. For benchmark-mapped test IDs:
    • If deterministic behavioral assertions can be extracted, port as regular unit tests.
    • If benchmark-only and non-deterministic/perf-only, mark n_a or deferred with explicit reason and evidence.
  4. Add anti-stub checks to prevent placeholder tests passing as real.

6) Risks and Mitigations

  1. Risk: Dependency batches 16/18 incomplete.
    • Mitigation: strict preflight gate, do not start batch until dependencies are complete.
  2. Risk: Silent stub regressions in broad ImplBacklog files.
    • Mitigation: mandatory grep-based stub scans and per-ID evidence before status updates.
  3. Risk: Frame/compression edge-case mismatch.
    • Mitigation: prioritize low-level frame tests first, then server/upgrade paths.
  4. Risk: Benchmarks misclassified as verified tests.
    • Mitigation: explicit benchmark decision gate (real deterministic test vs n_a/deferred with reason).

Design Decision

Proceed with Approach B (split-by-concern websocket port with partial integration into NatsServer/ClientConnection), executed via a strict verification-first implementation plan.