Files
natsdotnet/gaps/protocol.md
Joseph Doherty c30e67a69d Fix E2E test gaps and add comprehensive E2E + parity test suites
- 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
2026-03-12 14:09:23 -04:00

289 lines
27 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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:57134 | 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:2734 | 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 |