- Fix pull consumer fetch: send original stream subject in HMSG (not inbox) so NATS client distinguishes data messages from control messages - Fix MaxAge expiry: add background timer in StreamManager for periodic pruning - Fix JetStream wire format: Go-compatible anonymous objects with string enums, proper offset-based pagination for stream/consumer list APIs - Add 42 E2E black-box tests (core messaging, auth, TLS, accounts, JetStream) - Add ~1000 parity tests across all subsystems (gaps closure) - Update gap inventory docs to reflect implementation status
289 lines
27 KiB
Markdown
289 lines
27 KiB
Markdown
# Protocol — Gap Analysis
|
||
|
||
> This file tracks what has and hasn't been ported from Go to .NET for the **Protocol** module.
|
||
> See [stillmissing.md](stillmissing.md) for the full LOC comparison across all modules.
|
||
|
||
## LLM Instructions: How to Analyze This Category
|
||
|
||
### Step 1: Read the Go Reference Files
|
||
|
||
Read each Go source file listed below. For every file:
|
||
|
||
1. Extract all **exported types** (structs, interfaces, type aliases)
|
||
2. Extract all **exported methods** on those types (receiver functions)
|
||
3. Extract all **exported standalone functions**
|
||
4. Note **key constants, enums, and protocol states**
|
||
5. Note **important unexported helpers** that implement core logic (functions >20 lines)
|
||
6. Pay attention to **concurrency patterns** (goroutines, mutexes, channels) — these map to different .NET patterns
|
||
|
||
### Step 2: Read the .NET Implementation Files
|
||
|
||
Read all `.cs` files in the .NET directories listed below. For each Go symbol found in Step 1:
|
||
|
||
1. Search for a matching type, method, or function in .NET
|
||
2. If found, compare the behavior: does it handle the same edge cases? Same error paths?
|
||
3. If partially implemented, note what's missing
|
||
4. If not found, note it as MISSING
|
||
|
||
### Step 3: Cross-Reference Tests
|
||
|
||
Compare Go test functions against .NET test methods:
|
||
|
||
1. For each Go `Test*` function, check if a corresponding .NET `[Fact]` or `[Theory]` exists
|
||
2. Note which test scenarios are covered and which are missing
|
||
3. Check the parity DB (`docs/test_parity.db`) for existing mappings:
|
||
```bash
|
||
sqlite3 docs/test_parity.db "SELECT go_test, dotnet_test, confidence FROM test_mappings tm JOIN go_tests gt ON tm.go_test_id=gt.rowid JOIN dotnet_tests dt ON tm.dotnet_test_id=dt.rowid WHERE gt.go_file LIKE '%PATTERN%'"
|
||
```
|
||
|
||
### Step 4: Classify Each Item
|
||
|
||
Use these status values:
|
||
|
||
| Status | Meaning |
|
||
|--------|---------|
|
||
| **PORTED** | Equivalent exists in .NET with matching behavior |
|
||
| **PARTIAL** | .NET implementation exists but is incomplete (missing edge cases, error handling, or features) |
|
||
| **MISSING** | No .NET equivalent found — needs to be ported |
|
||
| **NOT_APPLICABLE** | Go-specific pattern that doesn't apply to .NET (build tags, platform-specific goroutine tricks, etc.) |
|
||
| **DEFERRED** | Intentionally skipped for now (document why) |
|
||
|
||
### Step 5: Fill In the Gap Inventory
|
||
|
||
Add rows to the Gap Inventory table below. Group by Go source file. Include the Go file and line number so a porting LLM can jump directly to the reference implementation.
|
||
|
||
### Key Porting Notes for Protocol
|
||
|
||
- The parser is a **byte-by-byte state machine**. In .NET, use `System.IO.Pipelines` for zero-copy parsing with `ReadOnlySequence<byte>`.
|
||
- Control line limit: 4096 bytes. Default max payload: 1MB.
|
||
- Extended protocol: `HPUB/HMSG` (headers), `RPUB/RMSG` (routes) — check if these are implemented.
|
||
- Fuzz tests in Go may map to `[Theory]` tests with randomized inputs in .NET.
|
||
|
||
---
|
||
|
||
## Go Reference Files (Source)
|
||
|
||
- `golang/nats-server/server/parser.go` — Protocol state machine (PUB, SUB, UNSUB, CONNECT, INFO, PING/PONG, HPUB/HMSG, RPUB/RMSG)
|
||
- `golang/nats-server/server/proto.go` — Wire-level protocol writing (sendProto, sendInfo, etc.)
|
||
- `golang/nats-server/server/const.go` — Protocol constants, limits (control line 4096, default max payload 1MB)
|
||
|
||
## Go Reference Files (Tests)
|
||
|
||
- `golang/nats-server/server/parser_test.go`
|
||
- `golang/nats-server/server/parser_fuzz_test.go`
|
||
- `golang/nats-server/server/server_fuzz_test.go`
|
||
- `golang/nats-server/server/subject_fuzz_test.go`
|
||
- `golang/nats-server/server/split_test.go`
|
||
|
||
## .NET Implementation Files (Source)
|
||
|
||
- `src/NATS.Server/Protocol/NatsParser.cs` — State machine
|
||
- `src/NATS.Server/Protocol/NatsProtocol.cs` — Wire-level writing
|
||
- `src/NATS.Server/Protocol/NatsHeaderParser.cs`
|
||
- `src/NATS.Server/Protocol/ClientCommandMatrix.cs`
|
||
- `src/NATS.Server/Protocol/MessageTraceContext.cs`
|
||
- `src/NATS.Server/Protocol/ProxyProtocol.cs`
|
||
|
||
## .NET Implementation Files (Tests)
|
||
|
||
- `tests/NATS.Server.Tests/Protocol/`
|
||
|
||
---
|
||
|
||
## Gap Inventory
|
||
|
||
<!-- After analysis, fill in this table. Group rows by Go source file. -->
|
||
|
||
### golang/nats-server/server/parser.go
|
||
|
||
| Go Symbol | Go File:Line | Status | .NET Equivalent | Notes |
|
||
|-----------|:-------------|--------|:----------------|-------|
|
||
| `parserState` (type) | parser.go:24 | PORTED | `src/NATS.Server/Protocol/NatsParser.cs` | Go uses an `iota` int type; .NET uses `enum CommandType` + internal awaiting-payload state |
|
||
| `parseState` (struct) | parser.go:25 | PARTIAL | `src/NATS.Server/Protocol/NatsParser.cs:37` | `.NET NatsParser` carries equivalent per-command state via fields (`_awaitingPayload`, `_expectedPayloadSize`, etc.). Missing: `argBuf`/`msgBuf` split-buffer accumulation fields, `scratch` fixed-size buffer, `header` lazy-parse field. The .NET parser relies on `System.IO.Pipelines` buffering rather than explicit accumulator fields. |
|
||
| `pubArg` (struct) | parser.go:37 | PARTIAL | `src/NATS.Server/Protocol/NatsParser.cs:21` | `ParsedCommand` covers `subject`, `reply`, `size`, `hdr`. Missing: `origin`, `account`, `pacache`, `mapped`, `queues`, `szb`, `hdb`, `psi`, `trace`, `delivered` — clustering/routing/JetStream fields not yet needed for core client protocol. |
|
||
| `OP_START` … `INFO_ARG` (parser state constants) | parser.go:57–134 | PARTIAL | `src/NATS.Server/Protocol/NatsParser.cs:104` | All CLIENT-facing states implemented (PUB, HPUB, SUB, UNSUB, CONNECT, INFO, PING, PONG, +OK, -ERR). MISSING states: `OP_A`/`ASUB_ARG`/`AUSUB_ARG` (A+/A- for gateways), `OP_R`/`OP_RS`/`OP_L`/`OP_LS` (RMSG/LMSG/RS+/RS-/LS+/LS-), `OP_M`/`MSG_ARG`/`HMSG_ARG` (routing MSG/HMSG). See `ClientCommandMatrix.cs` for partial routing opcode routing. |
|
||
| `client.parse()` | parser.go:136 | PARTIAL | `src/NATS.Server/Protocol/NatsParser.cs:69` | Core CLIENT-facing parse loop ported as `NatsParser.TryParse()` using `ReadOnlySequence<byte>` + `SequenceReader`. Missing: byte-by-byte incremental state transitions (Go uses byte-by-byte state machine; .NET scans for `\r\n` on each call), auth-set check before non-CONNECT op, MQTT dispatch (`c.mqttParse`), gateway in-CONNECT gating, ROUTER/GATEWAY/LEAF protocol dispatch (RMSG, LMSG, RS+, RS-, A+, A-). |
|
||
| `protoSnippet()` | parser.go:1236 | PORTED | `src/NATS.Server/Protocol/NatsParser.cs:206` | Added Go-style quoted snippet helper (`ProtoSnippet(start,max,buffer)`) and default overload, with parity tests in `tests/NATS.Server.Tests/Protocol/ProtocolParserSnippetGapParityTests.cs`. |
|
||
| `client.overMaxControlLineLimit()` | parser.go:1251 | PARTIAL | `src/NATS.Server/Protocol/NatsParser.cs:83` | .NET now emits structured max-control-line errors with snippet context (`snip=...`). Missing: kind-check (Go only enforces for `CLIENT`) and explicit connection-close side effect (`closeConnection(MaxControlLineExceeded)`) at parser layer. |
|
||
| `client.clonePubArg()` | parser.go:1267 | MISSING | — | Split-buffer scenario: clones pubArg and re-processes when payload spans two reads. Not needed in .NET because `System.IO.Pipelines` handles buffering, but there is no explicit equivalent. |
|
||
| `parseState.getHeader()` | parser.go:1297 | PARTIAL | `src/NATS.Server/Protocol/NatsHeaderParser.cs:25` | Go lazily parses `http.Header` from the raw message buffer. .NET has `NatsHeaderParser.Parse()` which parses NATS/1.0 headers. Missing: lazy evaluation on the parsed command (header is not cached on `ParsedCommand`). |
|
||
|
||
### golang/nats-server/server/proto.go
|
||
|
||
*Note: This Go file (`proto.go`) implements a **protobuf wire-format** scanner/encoder (field tags, varints, length-delimited bytes) used internally for NATS's binary internal protocol (e.g. JetStream metadata encoding). It is unrelated to the NATS text protocol.*
|
||
|
||
| Go Symbol | Go File:Line | Status | .NET Equivalent | Notes |
|
||
|-----------|:-------------|--------|:----------------|-------|
|
||
| `errProtoInsufficient` | proto.go:24 | PORTED | `src/NATS.Server/Protocol/ProtoWire.cs:5` | Added sentinel-equivalent error constant and `ProtoWireException` usage for insufficient varint/bytes payloads. |
|
||
| `errProtoOverflow` | proto.go:25 | PORTED | `src/NATS.Server/Protocol/ProtoWire.cs:6` | Added overflow sentinel-equivalent error constant for invalid 10-byte varint tails. |
|
||
| `errProtoInvalidFieldNumber` | proto.go:26 | PORTED | `src/NATS.Server/Protocol/ProtoWire.cs:7` | Added invalid field-number sentinel-equivalent error constant used by tag scanning. |
|
||
| `protoScanField()` | proto.go:28 | PORTED | `src/NATS.Server/Protocol/ProtoWire.cs:9` | Added field scanner that composes tag + value scanning and returns total consumed size. |
|
||
| `protoScanTag()` | proto.go:42 | PORTED | `src/NATS.Server/Protocol/ProtoWire.cs:16` | Added tag scanner with Go-equivalent field number validation (`1..int32`). |
|
||
| `protoScanFieldValue()` | proto.go:61 | PORTED | `src/NATS.Server/Protocol/ProtoWire.cs:26` | Added wire-type scanner for varint/fixed32/fixed64/length-delimited forms. |
|
||
| `protoScanVarint()` | proto.go:77 | PORTED | `src/NATS.Server/Protocol/ProtoWire.cs:38` | Added 10-byte max varint scanner with insufficient/overflow parity errors. |
|
||
| `protoScanBytes()` | proto.go:179 | PORTED | `src/NATS.Server/Protocol/ProtoWire.cs:65` | Added length-delimited scanner that validates the declared size against remaining payload bytes. |
|
||
| `protoEncodeVarint()` | proto.go:190 | PORTED | `src/NATS.Server/Protocol/ProtoWire.cs:74` | Added varint encoder and round-trip coverage in parity tests. |
|
||
|
||
### golang/nats-server/server/const.go
|
||
|
||
| Go Symbol | Go File:Line | Status | .NET Equivalent | Notes |
|
||
|-----------|:-------------|--------|:----------------|-------|
|
||
| `Command` (type) | const.go:23 | NOT_APPLICABLE | — | Go string type alias for signal commands (stop/quit/reload). Managed by OS signal handling; not applicable to .NET server lifecycle which uses `CancellationToken`. |
|
||
| `CommandStop`, `CommandQuit`, `CommandReopen`, `CommandReload` | const.go:27–34 | NOT_APPLICABLE | — | OS signal-based server control commands. .NET uses `CancellationToken` + `IHostedService` lifecycle. |
|
||
| `gitCommit`, `serverVersion` (build vars) | const.go:39 | NOT_APPLICABLE | — | Go linker-injected build vars. Equivalent handled by .NET assembly info / `AssemblyInformationalVersionAttribute`. |
|
||
| `formatRevision()` | const.go:47 | NOT_APPLICABLE | — | Formats a 7-char VCS commit hash for display. Go-specific build info pattern; not needed in .NET. |
|
||
| `init()` (build info) | const.go:54 | NOT_APPLICABLE | — | Reads `debug.BuildInfo` at startup to extract VCS revision. Not applicable to .NET. |
|
||
| `VERSION = "2.14.0-dev"` | const.go:69 | PARTIAL | `src/NATS.Server/Protocol/NatsProtocol.cs:11` | .NET has `Version = "0.1.0"`. The version string is present but does not match Go's version. |
|
||
| `PROTO = 1` | const.go:76 | PORTED | `src/NATS.Server/Protocol/NatsProtocol.cs:12` | `ProtoVersion = 1` |
|
||
| `DEFAULT_PORT = 4222` | const.go:79 | PORTED | `src/NATS.Server/Protocol/NatsProtocol.cs:10` | `DefaultPort = 4222` |
|
||
| `RANDOM_PORT = -1` | const.go:83 | NOT_APPLICABLE | — | Used in Go test helpers to request a random port. .NET tests use `GetFreePort()` pattern. |
|
||
| `DEFAULT_HOST = "0.0.0.0"` | const.go:86 | PORTED | `src/NATS.Server/Protocol/NatsProtocol.cs:11` | Added `NatsProtocol.DefaultHost` and wired `NatsOptions.Host` default to it. |
|
||
| `MAX_CONTROL_LINE_SIZE = 4096` | const.go:91 | PORTED | `src/NATS.Server/Protocol/NatsProtocol.cs:7` | `MaxControlLineSize = 4096` |
|
||
| `MAX_PAYLOAD_SIZE = 1MB` | const.go:95 | PORTED | `src/NATS.Server/Protocol/NatsProtocol.cs:8` | `MaxPayloadSize = 1024 * 1024` |
|
||
| `MAX_PAYLOAD_MAX_SIZE = 8MB` | const.go:99 | PORTED | `src/NATS.Server/Protocol/NatsProtocol.cs:9` | Added `NatsProtocol.MaxPayloadMaxSize` (8MB threshold constant). |
|
||
| `MAX_PENDING_SIZE = 64MB` | const.go:103 | PORTED | `src/NATS.Server/Protocol/NatsProtocol.cs:9` | `MaxPendingSize = 64 * 1024 * 1024` |
|
||
| `DEFAULT_MAX_CONNECTIONS = 64K` | const.go:106 | PORTED | `src/NATS.Server/Protocol/NatsProtocol.cs:13` | Added `NatsProtocol.DefaultMaxConnections` and wired `NatsOptions.MaxConnections`. |
|
||
| `TLS_TIMEOUT = 2s` | const.go:109 | PORTED | `src/NATS.Server/Protocol/NatsProtocol.cs:18`, `src/NATS.Server/NatsOptions.cs:102` | Added protocol default and wired TLS timeout default in options. |
|
||
| `DEFAULT_TLS_HANDSHAKE_FIRST_FALLBACK_DELAY = 50ms` | const.go:114 | PORTED | `src/NATS.Server/Protocol/NatsProtocol.cs:19`, `src/NATS.Server/NatsOptions.cs:104` | Added protocol default and wired `TlsHandshakeFirstFallback` default in options. |
|
||
| `AUTH_TIMEOUT = 2s` | const.go:118 | PORTED | `src/NATS.Server/Protocol/NatsProtocol.cs:20`, `src/NATS.Server/NatsOptions.cs:49` | Added protocol default and wired `AuthTimeout` default in options. |
|
||
| `DEFAULT_PING_INTERVAL = 2min` | const.go:122 | PORTED | `src/NATS.Server/Protocol/NatsProtocol.cs:21`, `src/NATS.Server/NatsOptions.cs:19` | Added protocol default and wired `PingInterval` default in options. |
|
||
| `DEFAULT_PING_MAX_OUT = 2` | const.go:125 | PORTED | `src/NATS.Server/Protocol/NatsProtocol.cs:14`, `src/NATS.Server/NatsOptions.cs:20` | Added protocol default and wired `MaxPingsOut` default in options. |
|
||
| `CR_LF = "\r\n"` | const.go:128 | PORTED | `src/NATS.Server/Protocol/NatsProtocol.cs:15` | `CrLf` byte array. |
|
||
| `LEN_CR_LF = 2` | const.go:131 | PORTED | Implicit in .NET (`+ 2` literals in parser). | Used as literal `2` in `TryReadPayload`. |
|
||
| `DEFAULT_FLUSH_DEADLINE = 10s` | const.go:134 | PORTED | `src/NATS.Server/Protocol/NatsProtocol.cs:22`, `src/NATS.Server/NatsOptions.cs:18` | Added protocol default and wired `WriteDeadline` default in options. |
|
||
| `DEFAULT_HTTP_PORT = 8222` | const.go:137 | PORTED | `src/NATS.Server/Protocol/NatsProtocol.cs:15` | Added `NatsProtocol.DefaultHttpPort` constant and parity assertions in protocol constants tests. |
|
||
| `DEFAULT_HTTP_BASE_PATH = "/"` | const.go:140 | PORTED | `src/NATS.Server/Protocol/NatsProtocol.cs:16` | Added `NatsProtocol.DefaultHttpBasePath` constant and parity assertions in protocol constants tests. |
|
||
| `ACCEPT_MIN_SLEEP = 10ms` | const.go:143 | PORTED | `src/NATS.Server/Protocol/NatsProtocol.cs:23`, `src/NATS.Server/NatsServer.cs:94` | Added protocol default and wired accept-loop backoff minimum in server. |
|
||
| `ACCEPT_MAX_SLEEP = 1s` | const.go:146 | PORTED | `src/NATS.Server/Protocol/NatsProtocol.cs:24`, `src/NATS.Server/NatsServer.cs:95` | Added protocol default and wired accept-loop backoff maximum in server. |
|
||
| `DEFAULT_ROUTE_CONNECT = 1s` | const.go:149 | PORTED | `src/NATS.Server/Protocol/NatsProtocol.cs:31` | Added `NatsProtocol.DefaultRouteConnect` constant. |
|
||
| `DEFAULT_ROUTE_CONNECT_MAX = 30s` | const.go:152 | PORTED | `src/NATS.Server/Protocol/NatsProtocol.cs:32` | Added `NatsProtocol.DefaultRouteConnectMax` constant. |
|
||
| `DEFAULT_ROUTE_RECONNECT = 1s` | const.go:155 | PORTED | `src/NATS.Server/Protocol/NatsProtocol.cs:33` | Added `NatsProtocol.DefaultRouteReconnect` constant. |
|
||
| `DEFAULT_ROUTE_DIAL = 1s` | const.go:158 | PORTED | `src/NATS.Server/Protocol/NatsProtocol.cs:34` | Added `NatsProtocol.DefaultRouteDial` constant. |
|
||
| `DEFAULT_ROUTE_POOL_SIZE = 3` | const.go:161 | PORTED | `src/NATS.Server/Protocol/NatsProtocol.cs:17` | Added `NatsProtocol.DefaultRoutePoolSize` constant. |
|
||
| `DEFAULT_LEAF_NODE_RECONNECT = 1s` | const.go:164 | PORTED | `src/NATS.Server/Protocol/NatsProtocol.cs:35` | Added `NatsProtocol.DefaultLeafNodeReconnect` constant. |
|
||
| `DEFAULT_LEAF_TLS_TIMEOUT = 2s` | const.go:167 | PORTED | `src/NATS.Server/Protocol/NatsProtocol.cs:36` | Added `NatsProtocol.DefaultLeafTlsTimeout` constant. |
|
||
| `PROTO_SNIPPET_SIZE = 32` | const.go:170 | PORTED | `src/NATS.Server/Protocol/NatsProtocol.cs:9`, `src/NATS.Server/Protocol/NatsParser.cs:222` | Added snippet-size constant and wired parser default `ProtoSnippet` overload to it. |
|
||
| `MAX_CONTROL_LINE_SNIPPET_SIZE = 128` | const.go:172 | PORTED | `src/NATS.Server/Protocol/NatsProtocol.cs:8`, `src/NATS.Server/Protocol/NatsParser.cs:85` | Added max control-line snippet size constant and wired control-line violation errors to use it. |
|
||
| `MAX_MSG_ARGS = 4` | const.go:175 | NOT_APPLICABLE | — | Used in Go's manual arg-split loop. .NET uses `SplitArgs()` with stack-allocated ranges. |
|
||
| `MAX_RMSG_ARGS = 6` | const.go:178 | NOT_APPLICABLE | — | Used in RMSG parsing. RMSG not yet ported. |
|
||
| `MAX_HMSG_ARGS = 7` | const.go:180 | NOT_APPLICABLE | — | Used in HMSG parsing. HMSG routing not yet ported. |
|
||
| `MAX_PUB_ARGS = 3` | const.go:183 | NOT_APPLICABLE | — | Used in PUB arg splitting. .NET uses dynamic `SplitArgs`. |
|
||
| `MAX_HPUB_ARGS = 4` | const.go:186 | NOT_APPLICABLE | — | Used in HPUB arg splitting. .NET uses dynamic `SplitArgs`. |
|
||
| `MAX_RSUB_ARGS = 6` | const.go:189 | NOT_APPLICABLE | — | Used in RS+/LS+ subscription arg splitting. Not yet ported. |
|
||
| `DEFAULT_MAX_CLOSED_CLIENTS = 10000` | const.go:192 | PORTED | `src/NATS.Server/Protocol/NatsProtocol.cs:15`, `src/NATS.Server/NatsOptions.cs:89` | Added protocol default and wired closed-client ring size default in options. |
|
||
| `DEFAULT_LAME_DUCK_DURATION = 2min` | const.go:196 | PORTED | `src/NATS.Server/Protocol/NatsProtocol.cs:25`, `src/NATS.Server/NatsOptions.cs:59` | Added protocol default and wired lame-duck duration default in options. |
|
||
| `DEFAULT_LAME_DUCK_GRACE_PERIOD = 10s` | const.go:200 | PORTED | `src/NATS.Server/Protocol/NatsProtocol.cs:26`, `src/NATS.Server/NatsOptions.cs:60` | Added protocol default and wired lame-duck grace period default in options. |
|
||
| `DEFAULT_LEAFNODE_INFO_WAIT = 1s` | const.go:203 | PORTED | `src/NATS.Server/Protocol/NatsProtocol.cs:37` | Added `NatsProtocol.DefaultLeafNodeInfoWait` constant. |
|
||
| `DEFAULT_LEAFNODE_PORT = 7422` | const.go:206 | PORTED | `src/NATS.Server/Protocol/NatsProtocol.cs:18` | Added `NatsProtocol.DefaultLeafNodePort` constant. |
|
||
| `DEFAULT_CONNECT_ERROR_REPORTS = 3600` | const.go:214 | PORTED | `src/NATS.Server/Protocol/NatsProtocol.cs:16`, `src/NATS.Server/NatsOptions.cs:86` | Added protocol default and wired `ConnectErrorReports` default in options. |
|
||
| `DEFAULT_RECONNECT_ERROR_REPORTS = 1` | const.go:220 | PORTED | `src/NATS.Server/Protocol/NatsProtocol.cs:17`, `src/NATS.Server/NatsOptions.cs:87` | Added protocol default and wired `ReconnectErrorReports` default in options. |
|
||
| `DEFAULT_RTT_MEASUREMENT_INTERVAL = 1h` | const.go:224 | PORTED | `src/NATS.Server/Protocol/NatsProtocol.cs:38` | Added `NatsProtocol.DefaultRttMeasurementInterval` constant. |
|
||
| `DEFAULT_ALLOW_RESPONSE_MAX_MSGS = 1` | const.go:228 | PORTED | `src/NATS.Server/Protocol/NatsProtocol.cs:24` | Added `NatsProtocol.DefaultAllowResponseMaxMsgs` constant. |
|
||
| `DEFAULT_ALLOW_RESPONSE_EXPIRATION = 2min` | const.go:232 | PORTED | `src/NATS.Server/Protocol/NatsProtocol.cs:39` | Added `NatsProtocol.DefaultAllowResponseExpiration` constant. |
|
||
| `DEFAULT_SERVICE_EXPORT_RESPONSE_THRESHOLD = 2min` | const.go:237 | PORTED | `src/NATS.Server/Protocol/NatsProtocol.cs:40` | Added `NatsProtocol.DefaultServiceExportResponseThreshold` constant. |
|
||
| `DEFAULT_SERVICE_LATENCY_SAMPLING = 100` | const.go:241 | PORTED | `src/NATS.Server/Protocol/NatsProtocol.cs:25` | Added `NatsProtocol.DefaultServiceLatencySampling` constant. |
|
||
| `DEFAULT_SYSTEM_ACCOUNT = "$SYS"` | const.go:244 | PORTED | `src/NATS.Server/Protocol/NatsProtocol.cs:26`, `src/NATS.Server/Auth/Account.cs:10` | Added protocol-level constant; existing account model uses the same value (`$SYS`). |
|
||
| `DEFAULT_GLOBAL_ACCOUNT = "$G"` | const.go:247 | PORTED | `src/NATS.Server/Protocol/NatsProtocol.cs:27` | Added `NatsProtocol.DefaultGlobalAccount` constant. |
|
||
| `DEFAULT_ACCOUNT_FETCH_TIMEOUT = 1900ms` | const.go:250 | PORTED | `src/NATS.Server/Protocol/NatsProtocol.cs:41` | Added `NatsProtocol.DefaultAccountFetchTimeout` constant. |
|
||
|
||
### .NET-Only Additions (no Go counterpart in the three source files)
|
||
|
||
| .NET Symbol | .NET File:Line | Status | Notes |
|
||
|-------------|:---------------|--------|-------|
|
||
| `NatsHeaderParser` | `src/NATS.Server/Protocol/NatsHeaderParser.cs:20` | PORTED | Parses `NATS/1.0` header blocks. Go uses `http.Header`/`textproto` lazily from `getHeader()` on `parseState`. .NET provides an eager standalone parser. |
|
||
| `NatsHeaders` (struct) | `src/NATS.Server/Protocol/NatsHeaderParser.cs:6` | PORTED | Structured result for parsed NATS headers (status, description, key-value map). No direct Go counterpart — Go uses `http.Header` directly. |
|
||
| `ClientCommandMatrix` | `src/NATS.Server/Protocol/ClientCommandMatrix.cs:4` | PORTED | Encodes which inter-server opcodes (RS+, RS-, RMSG, A+, A-, LS+, LS-, LMSG) are allowed per client kind. Corresponds to the `switch c.kind` / `switch c.op` dispatch inside `parse()`. |
|
||
| `MessageTraceContext` | `src/NATS.Server/Protocol/MessageTraceContext.cs:3` | PORTED | Captures client name/lang/version/headers from CONNECT options for trace logging. Equivalent to fields inside Go's `client` struct populated during `processConnect`. |
|
||
| `ProxyProtocolParser` | `src/NATS.Server/Protocol/ProxyProtocol.cs:48` | PORTED | PROXY protocol v1/v2 pure-parser. Corresponds to `client_proxyproto.go` (not in the three listed source files, but closely related to the protocol module). |
|
||
| `ProxyAddress`, `ProxyParseResult`, `ProxyParseResultKind` | `src/NATS.Server/Protocol/ProxyProtocol.cs:11` | PORTED | Supporting types for PROXY protocol parsing. |
|
||
| `ServerInfo` (class) | `src/NATS.Server/Protocol/NatsProtocol.cs:39` | PORTED | Wire-format INFO message JSON model. Corresponds to Go's `Info` struct in `server.go`. |
|
||
| `ClientOptions` (class) | `src/NATS.Server/Protocol/NatsProtocol.cs:98` | PARTIAL | Wire-format CONNECT options JSON model. Missing: `nkey`, `sig`, `jwt` fields present but auth handling not implemented server-side. |
|
||
|
||
---
|
||
|
||
## Keeping This File Updated
|
||
|
||
After porting work is completed:
|
||
|
||
1. **Update status**: Change `MISSING → PORTED` or `PARTIAL → PORTED` for each item completed
|
||
2. **Add .NET path**: Fill in the ".NET Equivalent" column with the actual file:line
|
||
3. **Re-count LOC**: Update the LOC numbers in `stillmissing.md`:
|
||
```bash
|
||
# Re-count .NET source LOC for this module
|
||
find src/NATS.Server/Protocol/ -name '*.cs' -type f -exec cat {} + | wc -l
|
||
# Re-count .NET test LOC for this module
|
||
find tests/NATS.Server.Tests/Protocol/ -name '*.cs' -type f -exec cat {} + | wc -l
|
||
```
|
||
4. **Add a changelog entry** below with date and summary of what was ported
|
||
5. **Update the parity DB** if new test mappings were created:
|
||
```bash
|
||
sqlite3 docs/test_parity.db "INSERT INTO test_mappings (go_test_id, dotnet_test_id, confidence, notes) VALUES (?, ?, 'manual', 'ported in YYYY-MM-DD session')"
|
||
```
|
||
|
||
## Test Cross-Reference Summary
|
||
|
||
### Go test file: `parser_test.go`
|
||
|
||
| Go Test | Status | .NET Equivalent |
|
||
|---------|--------|-----------------|
|
||
| `TestParsePing` | PARTIAL | `tests/NATS.Server.Tests/ParserTests.cs: Parse_PING` — covers full `PING\r\n`; missing byte-by-byte incremental state assertions |
|
||
| `TestParsePong` | PARTIAL | `tests/NATS.Server.Tests/ParserTests.cs: Parse_PONG` — covers full `PONG\r\n`; missing `ping.out` counter decrement test |
|
||
| `TestParseConnect` | PORTED | `tests/NATS.Server.Tests/ParserTests.cs: Parse_CONNECT` |
|
||
| `TestParseSub` | PORTED | `tests/NATS.Server.Tests/ParserTests.cs: Parse_SUB_without_queue`, `Parse_SUB_with_queue` |
|
||
| `TestParsePub` | PARTIAL | `tests/NATS.Server.Tests/ParserTests.cs: Parse_PUB_with_payload`, `Parse_PUB_with_reply` — missing overflow payload error scenario |
|
||
| `TestParsePubSizeOverflow` | PORTED | `tests/NATS.Server.Tests/ParserTests.cs: Parse_pub_size_overflow_fails` — explicit oversized PUB payload-size argument test now asserts parser rejection (`Invalid payload size`) |
|
||
| `TestParsePubArg` | PORTED | `tests/NATS.Server.Tests/ParserTests.cs: Parse_PUB_argument_variations` (Theory) |
|
||
| `TestParsePubBadSize` | PARTIAL | `tests/NATS.Server.Tests/ParserTests.cs: Parse_malformed_protocol_fails` covers some bad args; missing specific `mpay` (max payload per-client) test |
|
||
| `TestParseHeaderPub` | PORTED | `tests/NATS.Server.Tests/ParserTests.cs: Parse_HPUB` |
|
||
| `TestParseHeaderPubArg` | MISSING | No .NET Theory equivalent for the 32 HPUB argument variations with mixed spaces/tabs |
|
||
| `TestParseRoutedHeaderMsg` (HMSG) | MISSING | No .NET equivalent — ROUTER/GATEWAY HMSG parsing not yet ported |
|
||
| `TestParseRouteMsg` (RMSG) | MISSING | No .NET equivalent — ROUTER RMSG parsing not yet ported |
|
||
| `TestParseMsgSpace` | MISSING | No .NET equivalent — MSG opcode for routes not yet ported |
|
||
| `TestShouldFail` | PARTIAL | `tests/NATS.Server.Tests/ParserTests.cs: Parse_malformed_protocol_fails` — covers subset; documented behavioral differences for byte-by-byte vs prefix-scan parser |
|
||
| `TestProtoSnippet` | PORTED | `tests/NATS.Server.Tests/Protocol/ProtocolParserSnippetGapParityTests.cs: ProtoSnippet_*` validates Go-style snippet behavior and parser error context wiring. |
|
||
| `TestParseOK` | PORTED | `tests/NATS.Server.Tests/ParserTests.cs: Parse_case_insensitive` includes +OK (via `ParsedCommand.Simple`) |
|
||
| `TestMaxControlLine` | PARTIAL | `tests/NATS.Server.Tests/ParserTests.cs: Parse_exceeding_max_control_line_fails` — covers basic enforcement; missing per-client-kind bypass (LEAF/ROUTER/GATEWAY exempt) |
|
||
|
||
### Go test file: `split_test.go`
|
||
|
||
| Go Test | Status | .NET Equivalent |
|
||
|---------|--------|-----------------|
|
||
| `TestSplitBufferSubOp` | PARTIAL | `tests/NATS.Server.Tests/ParserTests.cs` — `System.IO.Pipelines` handles split buffers transparently; no explicit split-state test |
|
||
| `TestSplitBufferUnsubOp` | PARTIAL | Same as above |
|
||
| `TestSplitBufferPubOp` … `TestSplitBufferPubOp5` | PARTIAL | `Parse_PUB_with_payload` covers basic case; no multi-chunk split test |
|
||
| `TestSplitConnectArg` | PARTIAL | No explicit argBuf accumulation test |
|
||
| `TestSplitDanglingArgBuf` | NOT_APPLICABLE | .NET parser has no `argBuf` — pipeline buffering makes this moot |
|
||
| `TestSplitRoutedMsgArg` | MISSING | RMSG not yet ported |
|
||
| `TestSplitBufferMsgOp` | MISSING | RMSG not yet ported |
|
||
| `TestSplitBufferLeafMsgArg` | MISSING | LMSG (leaf) not yet ported |
|
||
|
||
### Go test file: `parser_fuzz_test.go`
|
||
|
||
| Go Test | Status | .NET Equivalent |
|
||
|---------|--------|-----------------|
|
||
| `FuzzParser` | MISSING | No .NET fuzz test. Could be approximated with property-based testing (`[Theory]` with random inputs via `FsCheck` or `Bogus`). |
|
||
|
||
### Go test file: `server_fuzz_test.go`
|
||
|
||
| Go Test | Status | .NET Equivalent |
|
||
|---------|--------|-----------------|
|
||
| `FuzzServerTLS` | MISSING | TLS-first handshake and TLS fuzzing not yet implemented in .NET server. |
|
||
|
||
### Go test file: `subject_fuzz_test.go`
|
||
|
||
| Go Test | Status | .NET Equivalent |
|
||
|---------|--------|-----------------|
|
||
| `FuzzSubjectsCollide` | MISSING | `SubjectsCollide()` function not yet ported. .NET has `SubjectMatch.IsValidSubject()` and wildcard matching but not a `SubjectsCollide` API. |
|
||
|
||
---
|
||
|
||
## Change Log
|
||
|
||
| Date | Change | By |
|
||
|------|--------|----|
|
||
| 2026-02-26 | Added parser overflow parity test (`Parse_pub_size_overflow_fails`) and reclassified `TestParsePubSizeOverflow` from MISSING to PORTED. | codex |
|
||
| 2026-02-25 | File created with LLM analysis instructions | auto |
|
||
| 2026-02-25 | Full gap inventory populated: parser.go, proto.go, const.go; test cross-reference for all 5 Go test files | claude-sonnet-4-6 |
|
||
| 2026-02-25 | Executed protocol defaults parity batch: introduced missing const/default surfaces in `NatsProtocol`, wired `NatsOptions` and accept-loop defaults, added targeted tests (`ProtocolDefaultConstantsGapParityTests`), and reclassified 16 const.go rows from MISSING to PORTED | codex |
|
||
| 2026-02-25 | Executed protocol proto-wire parity batch: added `ProtoWire` scanners/encoder and parity tests (`ProtoWireParityTests`), and reclassified all 9 `proto.go` rows from MISSING to PORTED | codex |
|