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.
7.2 KiB
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.gofor tests) - Current state:
- Batch status is
pending. - Batch is not ready (
batch readycurrently 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).
- Batch status is
Goals
- Port all 37 Batch 26 websocket features from
server/websocket.gowith behaviorally equivalent logic. - Port and verify all 86 Batch 26 mapped tests with no fake/stub assertions.
- Remove websocket-specific stubs (
NotImplementedException, placeholder comments, always-false flags) in runtime paths touched by this batch. - Keep implementation idiomatic C# while preserving protocol correctness (frame parsing, masking, compression, upgrade validation, close/status handling).
Non-Goals
- Rewriting unrelated subsystems (MQTT, full leaf routing internals, non-websocket JetStream internals).
- Performance tuning beyond parity with Go logic and existing .NET architecture.
- 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.
Approach B (Recommended): Split by Concern with Partial Integration
- Description: Keep websocket-focused primitives in
WebSocket/and wire server/client integration through partial class methods inNatsServerandClientConnection. - 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.
Recommended Design (Approach B)
1) Component Boundaries
- Frame/read-path primitives:
WsReadInfostate machine (init,Read,ReadByte,NextCBuf,Decompress,Unmask).- Static helpers for
WsGet,WsIsControlFrame, frame-header creation/fill, masking utilities, close payload creation.
- Client websocket behavior:
ClientConnectionwebsocket methods (WsRead, control frame handling/enqueue, close mapping, outbound collapse).IsWebSocket()no longer hardcoded false once websocket state exists on connection.
- 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).
- Handshake validation and response (
- 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
- Upgrade flow:
- HTTP request validation -> origin check -> extension negotiation -> connection hijack -> 101 response -> websocket context creation.
- Inbound frame flow:
- Header decode -> mask enforcement -> payload size handling -> optional control-frame handling -> optional decompression -> payload slices returned for parser.
- Outbound frame flow:
- Pending buffers -> optional compression -> fragmentation rules (browser/safari constraints) -> optional masking -> queued frames + close frame semantics.
3) Error Handling Model
- Protocol violations map to websocket close frames with RFC status codes, then close/read termination behavior.
- Handshake failures return HTTP status + server-side error logging.
- Decompression limit violations map to payload errors (max payload).
- Any runtime branch that cannot be implemented due dependency/runtime gap is marked
deferredwith 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.csdotnet/src/ZB.MOM.NatsNet.Server/ClientConnection.csdotnet/src/ZB.MOM.NatsNet.Server/NatsServer.Listeners.csdotnet/src/ZB.MOM.NatsNet.Server/NatsServer.Lifecycle.csdotnet/src/ZB.MOM.NatsNet.Server/NatsServer.Init.csdotnet/src/ZB.MOM.NatsNet.Server/NatsServer.cs
- Tests:
dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/WebSocketHandlerTests.Impltests.csdotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/JetStreamFileStoreTests.Impltests.csdotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/NatsConsumerTests.Impltests.csdotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/LeafNodeHandlerTests.Impltests.csdotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/ConcurrencyTests1.Impltests.csdotnet/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.csdotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/PublishBenchmarks.Impltests.csdotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/LeafNodeProxyTests.Impltests.cs
5) Test Strategy
- Use strict ID-driven mapping from PortTracker for all 86 tests.
- For websocket functional tests, port assertions against frame semantics, handshake negotiation, header behavior, compression behavior, and outbound framing.
- 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_aordeferredwith explicit reason and evidence.
- Add anti-stub checks to prevent placeholder tests passing as real.
6) Risks and Mitigations
- Risk: Dependency batches 16/18 incomplete.
- Mitigation: strict preflight gate, do not start batch until dependencies are complete.
- Risk: Silent stub regressions in broad ImplBacklog files.
- Mitigation: mandatory grep-based stub scans and per-ID evidence before status updates.
- Risk: Frame/compression edge-case mismatch.
- Mitigation: prioritize low-level frame tests first, then server/upgrade paths.
- Risk: Benchmarks misclassified as verified tests.
- Mitigation: explicit benchmark decision gate (real deterministic test vs
n_a/deferredwith reason).
- Mitigation: explicit benchmark decision gate (real deterministic test vs
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.