387 lines
34 KiB
Markdown
387 lines
34 KiB
Markdown
# Core Server — Gap Analysis
|
|
|
|
> This file tracks what has and hasn't been ported from Go to .NET for the **Core Server** 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 Core Server
|
|
|
|
- Go's `client.go` is the **largest single file** (~6,700 lines). It contains the readLoop/writeLoop goroutines, dynamic buffer sizing (512→65536), slow consumer detection, and per-client subscription tracking. The .NET equivalent should use `async/await` with `System.IO.Pipelines`.
|
|
- Go's `server.go` manages listeners, accept loops, and shutdown coordination. Map goroutines to `Task`-based async patterns.
|
|
- Go's `opts.go` handles both CLI flags and config file parsing (CLI overrides config). The .NET version separates this into `Configuration/`.
|
|
- `reload.go` implements hot-reload by diffing old vs new config and applying deltas without restart.
|
|
- Platform-specific files (`service_windows.go`, `signal_windows.go`, `signal_wasm.go`) may be NOT_APPLICABLE.
|
|
|
|
---
|
|
|
|
## Go Reference Files (Source)
|
|
|
|
- `golang/nats-server/server/server.go` — Server struct, startup lifecycle, listener management
|
|
- `golang/nats-server/server/client.go` — Connection handling, readLoop/writeLoop, per-client state (~6,700 lines)
|
|
- `golang/nats-server/server/opts.go` — Server options struct, CLI flag parsing, config file loading
|
|
- `golang/nats-server/server/reload.go` — Hot reload on signal, config diffing
|
|
- `golang/nats-server/server/service.go` — OS service/daemon management
|
|
- `golang/nats-server/server/signal.go` — Signal handling (SIGHUP, SIGTERM, etc.)
|
|
- `golang/nats-server/main.go` — Entry point
|
|
|
|
## Go Reference Files (Tests)
|
|
|
|
- `golang/nats-server/server/server_test.go`
|
|
- `golang/nats-server/server/client_test.go`
|
|
- `golang/nats-server/server/opts_test.go`
|
|
- `golang/nats-server/server/reload_test.go`
|
|
- `golang/nats-server/server/signal_test.go`
|
|
- `golang/nats-server/server/test_test.go` (test helpers)
|
|
|
|
## .NET Implementation Files (Source)
|
|
|
|
- `src/NATS.Server/NatsServer.cs` — Server lifecycle
|
|
- `src/NATS.Server/NatsClient.cs` — Per-connection client
|
|
- `src/NATS.Server/NatsOptions.cs` — Server options
|
|
- `src/NATS.Server/ClientFlags.cs`
|
|
- `src/NATS.Server/ClientKind.cs`
|
|
- `src/NATS.Server/ClientClosedReason.cs`
|
|
- `src/NATS.Server/ClientTraceInfo.cs`
|
|
- `src/NATS.Server/ClosedState.cs`
|
|
- `src/NATS.Server/INatsClient.cs`
|
|
- `src/NATS.Server/InternalClient.cs`
|
|
- `src/NATS.Server/ServerStats.cs`
|
|
- `src/NATS.Server/SlowConsumerTracker.cs`
|
|
- `src/NATS.Server/MqttOptions.cs`
|
|
- `src/NATS.Server.Host/Program.cs` — Entry point
|
|
|
|
## .NET Implementation Files (Tests)
|
|
|
|
- `tests/NATS.Server.Tests/` (root-level test files)
|
|
|
|
---
|
|
|
|
## Gap Inventory
|
|
|
|
<!-- After analysis, fill in this table. Group rows by Go source file. -->
|
|
|
|
| Go Symbol | Go File:Line | Status | .NET Equivalent | Notes |
|
|
|-----------|:-------------|--------|:----------------|-------|
|
|
| **golang/nats-server/main.go** | | | | |
|
|
| main() | main.go:97 | PORTED | src/NATS.Server.Host/Program.cs:1 | CLI arg parsing, config load, server create, start, wait |
|
|
| usage() | main.go:92 | PARTIAL | src/NATS.Server.Host/Program.cs | No dedicated --help usage string; CLI flags are handled inline |
|
|
| **golang/nats-server/server/server.go — Types** | | | | |
|
|
| Info struct | server.go:109 | PORTED | src/NATS.Server/Protocol/NatsProtocol.cs:39 (ServerInfo) | Core fields ported; route/gateway/leafnode-specific fields are partial |
|
|
| Server struct | server.go:169 | PARTIAL | src/NATS.Server/NatsServer.cs:31 | Core fields ported (clients, accounts, opts, listener, shutdown). Missing: route pool tracking, gateway internals, OCSP monitors, proxied conns, rate limiting maps |
|
|
| stats struct | server.go:402 | PORTED | src/NATS.Server/ServerStats.cs:6 | All counters present |
|
|
| scStats struct | server.go:413 | PORTED | src/NATS.Server/ServerStats.cs:18-25 | Per-kind slow consumer counters present |
|
|
| staleStats struct | server.go:421 | PORTED | src/NATS.Server/ServerStats.cs:26 | Per-kind stale connection counters present |
|
|
| nodeInfo struct | server.go:387 | NOT_APPLICABLE | — | JetStream cluster-specific; tracked in JetStream module |
|
|
| Ports struct | server.go:4236 | MISSING | — | Not implemented; no /ports output support |
|
|
| Compression constants | server.go:437-446 | MISSING | — | S2 compression mode constants not defined in core server |
|
|
| CompressionOpts struct | server.go:97 (opts.go) | MISSING | — | No compression options type in .NET |
|
|
| **golang/nats-server/server/server.go — Exported Server Methods** | | | | |
|
|
| NewServer() | server.go:716 | PORTED | src/NATS.Server/NatsServer.cs constructor | Options validation, NKey identity, info setup |
|
|
| New() (deprecated) | server.go:698 | NOT_APPLICABLE | — | Deprecated wrapper |
|
|
| NewServerFromConfig() | server.go:703 | PORTED | src/NATS.Server.Host/Program.cs:20-22 | Config file processing then new server |
|
|
| (s) Start() | server.go:2263 | PORTED | src/NATS.Server/NatsServer.cs:502 (StartAsync) | Listener, accept loop, subsystem startup. Async in .NET |
|
|
| (s) Shutdown() | server.go:2583 | PORTED | src/NATS.Server/NatsServer.cs:163 (ShutdownAsync) | Client disconnect, listener close, subsystem teardown |
|
|
| (s) WaitForShutdown() | server.go:2775 | PORTED | src/NATS.Server/NatsServer.cs:150 | Blocks until shutdown complete |
|
|
| (s) AcceptLoop() | server.go:2780 | PORTED | src/NATS.Server/NatsServer.cs:502 (inline in StartAsync) | Accept loop is part of StartAsync |
|
|
| (s) ReadyForConnections() | server.go:4023 | PORTED | src/NATS.Server/NatsServer.cs:148 (WaitForReadyAsync) | Async TaskCompletionSource-based |
|
|
| (s) Running() | server.go:1695 | PORTED | src/NATS.Server/NatsServer.cs:108 (IsShuttingDown inverted) | Derived from shutdown flag |
|
|
| (s) ID() | server.go:4036 | PORTED | src/NATS.Server/NatsServer.cs:101 (ServerId) | — |
|
|
| (s) Name() | server.go:4046 | PORTED | src/NATS.Server/NatsServer.cs:102 (ServerName) | — |
|
|
| (s) NodeName() | server.go:4041 | PORTED | src/NATS.Server/NatsServer.cs:102 | Same as ServerName |
|
|
| (s) ClusterName() | server.go:1017 | PARTIAL | src/NATS.Server/NatsServer.cs:110 (ClusterListen) | Only listen endpoint; no cluster name getter |
|
|
| (s) ClientURL() | server.go:1086 | MISSING | — | No dedicated method to return client connect URL |
|
|
| (s) WebsocketURL() | server.go:1100 | MISSING | — | No dedicated websocket URL getter |
|
|
| (s) NumClients() | server.go:3810 | PORTED | src/NATS.Server/NatsServer.cs:103 (ClientCount) | — |
|
|
| (s) NumRoutes() | server.go:3773 | PARTIAL | src/NATS.Server/ServerStats.cs:14 (Routes field) | Stats counter exists; no lock-safe method like Go |
|
|
| (s) NumRemotes() | server.go:3790 | MISSING | — | — |
|
|
| (s) NumLeafNodes() | server.go:3803 | PARTIAL | src/NATS.Server/ServerStats.cs:16 (Leafs field) | Stats counter; no lock-safe count method |
|
|
| (s) NumSubscriptions() | server.go:3836 | MISSING | — | No aggregated subscription count method |
|
|
| (s) NumSlowConsumers() | server.go:3855 | PORTED | src/NATS.Server/ServerStats.cs:12 | Direct field access |
|
|
| (s) NumSlowConsumersClients() | server.go:3865 | PORTED | src/NATS.Server/ServerStats.cs:18 | — |
|
|
| (s) NumSlowConsumersRoutes() | server.go:3870 | PORTED | src/NATS.Server/ServerStats.cs:19 | — |
|
|
| (s) NumSlowConsumersGateways() | server.go:3875 | PORTED | src/NATS.Server/ServerStats.cs:21 | — |
|
|
| (s) NumSlowConsumersLeafs() | server.go:3880 | PORTED | src/NATS.Server/ServerStats.cs:20 | — |
|
|
| (s) NumStalledClients() | server.go:3860 | PORTED | src/NATS.Server/ServerStats.cs:17 (Stalls) | — |
|
|
| (s) NumStaleConnections() | server.go:3885 | PORTED | src/NATS.Server/ServerStats.cs:13 | — |
|
|
| (s) NumStaleConnectionsClients() | server.go:3890 | PORTED | src/NATS.Server/ServerStats.cs:22 | — |
|
|
| (s) NumStaleConnectionsRoutes() | server.go:3895 | PORTED | src/NATS.Server/ServerStats.cs:23 | — |
|
|
| (s) NumStaleConnectionsGateways() | server.go:3900 | PORTED | src/NATS.Server/ServerStats.cs:24 | — |
|
|
| (s) NumStaleConnectionsLeafs() | server.go:3905 | PORTED | src/NATS.Server/ServerStats.cs:25 | — |
|
|
| (s) GetClient() | server.go:3817 | PORTED | src/NATS.Server/NatsServer.cs:119 (GetClients enumerable) | Enumerable, not by-ID lookup |
|
|
| (s) GetLeafNode() | server.go:3829 | MISSING | — | No leaf node by-CID lookup |
|
|
| (s) ConfigTime() | server.go:3910 | MISSING | — | No config time tracking exposed |
|
|
| (s) Addr() | server.go:3917 | PARTIAL | src/NATS.Server/NatsServer.cs:104 (Port) | Port exposed but not full net.Addr |
|
|
| (s) MonitorAddr() | server.go:3927 | MISSING | — | Monitoring managed by MonitorServer separately |
|
|
| (s) ClusterAddr() | server.go:3937 | PARTIAL | src/NATS.Server/NatsServer.cs:110 (ClusterListen string) | String, not TCPAddr |
|
|
| (s) ProfilerAddr() | server.go:3947 | MISSING | — | No profiler address getter |
|
|
| (s) ActivePeers() | server.go:1577 | MISSING | — | Cluster peer enumeration not in core |
|
|
| (s) NumActiveAccounts() | server.go:1716 | MISSING | — | No active account count method |
|
|
| (s) NumLoadedAccounts() | server.go:1744 | PARTIAL | src/NATS.Server/NatsServer.cs:123 (GetAccounts) | Enumerable, no count method |
|
|
| (s) LookupOrRegisterAccount() | server.go:1749 | PORTED | src/NATS.Server/NatsServer.cs:1260 (GetOrCreateAccount) | — |
|
|
| (s) RegisterAccount() | server.go:1762 | PORTED | src/NATS.Server/NatsServer.cs:1260 | Via GetOrCreateAccount |
|
|
| (s) SetSystemAccount() | server.go:1775 | PORTED | src/NATS.Server/NatsServer.cs constructor | Set during construction |
|
|
| (s) SystemAccount() | server.go:1798 | PORTED | src/NATS.Server/NatsServer.cs:105 | — |
|
|
| (s) GlobalAccount() | server.go:1804 | PORTED | src/NATS.Server/NatsServer.cs:50 (_globalAccount) | — |
|
|
| (s) LookupAccount() | server.go:2106 | PORTED | src/NATS.Server/NatsServer.cs:1260 | Via GetOrCreateAccount |
|
|
| (s) StartProfiler() | server.go:2941 | MISSING | — | No built-in profiler; .NET uses dotnet-trace/counters |
|
|
| (s) StartMonitoring() | server.go:3014 | PORTED | src/NATS.Server/Monitoring/MonitorServer.cs | Separate monitoring server class |
|
|
| (s) StartHTTPMonitoring() | server.go:3003 | PORTED | src/NATS.Server/Monitoring/MonitorServer.cs:140 | — |
|
|
| (s) StartHTTPSMonitoring() | server.go:3009 | PORTED | src/NATS.Server/Monitoring/MonitorServer.cs | HTTPS variant via options |
|
|
| (s) HTTPHandler() | server.go:3207 | PARTIAL | src/NATS.Server/Monitoring/MonitorServer.cs | ASP.NET Kestrel handles routing, not an http.Handler |
|
|
| (s) InProcessConn() | server.go:2876 | MISSING | — | No in-process connection support |
|
|
| (s) LameDuckShutdown() | server.go:4421 | PORTED | src/NATS.Server/NatsServer.cs:239 (LameDuckShutdownAsync) | Full LDM with grace period and duration |
|
|
| (s) DisconnectClientByID() | server.go:4742 | MISSING | — | No per-client disconnect by ID |
|
|
| (s) LDMClientByID() | server.go:4757 | MISSING | — | No per-client lame duck by ID |
|
|
| (s) PortsInfo() | server.go:4247 | MISSING | — | No Ports struct output |
|
|
| (s) String() | server.go:4050 | MISSING | — | No server string representation |
|
|
| PrintAndDie() | server.go:1664 | NOT_APPLICABLE | — | .NET uses exceptions/logging |
|
|
| PrintServerAndExit() | server.go:1670 | NOT_APPLICABLE | — | .NET uses --version flag differently |
|
|
| ProcessCommandLineArgs() | server.go:1678 | PORTED | src/NATS.Server.Host/Program.cs:25-137 | Inline switch-based CLI parsing |
|
|
| **golang/nats-server/server/server.go — Key Unexported Helpers** | | | | |
|
|
| (s) createClient() | server.go:3245 | PORTED | src/NATS.Server/NatsServer.cs:666 (AcceptClientAsync) | Creates NatsClient, runs it |
|
|
| (s) createInternalClient() | server.go:1925 | PORTED | src/NATS.Server/InternalClient.cs:29 | InternalClient class |
|
|
| (s) createInternalSystemClient() | server.go:1910 | PORTED | src/NATS.Server/NatsServer.cs:377 | System internal client creation |
|
|
| (s) acceptConnections() | server.go:2889 | PORTED | src/NATS.Server/NatsServer.cs:502 (StartAsync accept loop) | Inline in StartAsync |
|
|
| (s) removeClient() | server.go:3708 | PORTED | src/NATS.Server/NatsServer.cs RemoveClient() via IMessageRouter | — |
|
|
| (s) saveClosedClient() | server.go:3561 | PORTED | src/NATS.Server/Monitoring/ClosedConnectionRingBuffer.cs | ClosedConnectionRingBuffer |
|
|
| (s) configureAccounts() | server.go:1230 | PARTIAL | src/NATS.Server/NatsServer.cs constructor | Basic account setup; no multi-account import/export wiring |
|
|
| (s) setInfoHostPort() | server.go:2921 | PORTED | src/NATS.Server/NatsServer.cs:496 (BuildCachedInfo) | — |
|
|
| (s) lameDuckMode() | server.go:4428 | PORTED | src/NATS.Server/NatsServer.cs:239 (LameDuckShutdownAsync) | Async version |
|
|
| (s) handleSignals() | server.go (signal.go:37) | PORTED | src/NATS.Server/NatsServer.cs:320 (HandleSignals) | Uses PosixSignalRegistration on .NET |
|
|
| (s) logPorts() | server.go:4332 | PARTIAL | src/NATS.Server/NatsServer.cs | Logs port at startup; no ports file |
|
|
| (s) startGoRoutine() | server.go:4070 | NOT_APPLICABLE | — | .NET uses Task.Run; no goroutine tracking needed |
|
|
| (s) readyForConnections() | server.go:3956 | PORTED | src/NATS.Server/NatsServer.cs:148 (WaitForReadyAsync) | — |
|
|
| (s) getOpts() | server.go:1206 | PORTED | src/NATS.Server/NatsServer.cs:33 (_options field) | Direct field access |
|
|
| (s) isRunning() | server.go:1700 | PORTED | src/NATS.Server/NatsServer.cs:108 | Inverted IsShuttingDown |
|
|
| (s) isShuttingDown() | server.go:2577 | PORTED | src/NATS.Server/NatsServer.cs:108 (IsShuttingDown) | — |
|
|
| (s) updateServerINFOAndSendINFOToClients() | server.go:3622 | MISSING | — | No dynamic INFO update broadcast to existing clients |
|
|
| (s) getConnectURLs() | server.go:4120 | MISSING | — | No connect URL resolution for clustering |
|
|
| (s) getNonLocalIPsIfHostIsIPAny() | server.go:4159 | MISSING | — | IP enumeration for advertise not implemented |
|
|
| (s) portFile() | server.go:4307 | MISSING | — | No ports file creation |
|
|
| (s) logPid() | server.go:1704 | PARTIAL | src/NATS.Server/NatsServer.cs | PID file support present in options but write logic minimal |
|
|
| validateAndNormalizeCompressionOption() | server.go:466 | MISSING | — | No compression option validation |
|
|
| selectCompressionMode() | server.go:559 | MISSING | — | No compression mode negotiation |
|
|
| selectS2AutoModeBasedOnRTT() | server.go:618 | MISSING | — | No RTT-based auto compression |
|
|
| needsCompression() | server.go:549 | MISSING | — | — |
|
|
| s2WriterOptions() | server.go:678 | MISSING | — | — |
|
|
| **golang/nats-server/server/client.go — Types** | | | | |
|
|
| client struct | client.go:259 | PORTED | src/NATS.Server/NatsClient.cs:35 | Core fields: cid, kind, subs, opts, flags, stats. Missing: route/gw/leaf/mqtt sub-structs, perms cache, nonce, pubKey |
|
|
| ClosedState enum | client.go:188 | PORTED | src/NATS.Server/ClosedState.cs:14 | Full enum parity with all values |
|
|
| ClientOpts struct | client.go:661 | PORTED | src/NATS.Server/Protocol/ClientOptions.cs | Echo, Verbose, Pedantic, TLS, JWT, NKey, Headers, NoResponders |
|
|
| clientFlag type | client.go:132 | PORTED | src/NATS.Server/ClientFlags.cs:9 | Core flags present; missing some (infoReceived, noReconnect, expectConnect, compressionNegotiated, didTLSFirst) |
|
|
| WriteTimeoutPolicy | client.go:239 | PORTED | src/NATS.Server/NatsClient.cs:975 | Close and TcpFlush policies |
|
|
| subscription struct | client.go:631 | PORTED | src/NATS.Server/Subscriptions/Subscription.cs | Subject, queue, sid, client, max, messageCount |
|
|
| outbound struct | client.go:349 | PARTIAL | src/NATS.Server/NatsClient.cs:47-48 | Channel-based outbound; missing: net.Buffers, sync.Cond, S2 writer, stall channel |
|
|
| readCache struct | client.go:485 | MISSING | — | No per-client subject lookup cache |
|
|
| perAccountCache struct | client.go:541 | MISSING | — | No per-account L1 cache for routes/gateways |
|
|
| pinfo struct | client.go:343 | PORTED | src/NATS.Server/NatsClient.cs:99 (_pingsOut) | Simplified: no timer struct, uses PeriodicTimer |
|
|
| permissions struct | client.go:430 | PORTED | src/NATS.Server/Auth/ClientPermissions.cs | Pub/sub allow/deny; missing: pcache (permission cache) |
|
|
| routeTarget struct | client.go:458 | MISSING | — | Route message targeting structure |
|
|
| msgDeny struct | client.go:451 | MISSING | — | Deny-based message filtering cache |
|
|
| resp struct | client.go:442 | MISSING | — | Dynamic response permission tracking |
|
|
| CLIENT/ROUTER/GATEWAY/SYSTEM/LEAF/JETSTREAM/ACCOUNT constants | client.go:44-60 | PORTED | src/NATS.Server/ClientKind.cs:8 | All client kinds present |
|
|
| isInternalClient() | client.go:63 | PORTED | src/NATS.Server/ClientKind.cs:20 (IsInternal extension) | — |
|
|
| NON_CLIENT/NATS/MQTT/WS constants | client.go:70-79 | PARTIAL | src/NATS.Server/NatsClient.cs:107 (IsWebSocket) | WebSocket bool exists; no explicit MQTT/NATS/NON_CLIENT sub-type enum |
|
|
| ClientProtoZero/ClientProtoInfo | client.go:82-88 | MISSING | — | Client protocol version constants not defined |
|
|
| **golang/nats-server/server/client.go — Exported Methods** | | | | |
|
|
| (c) String() | client.go:547 | MISSING | — | No formatted string representation |
|
|
| (c) GetNonce() | client.go:557 | PARTIAL | src/NATS.Server/NatsClient.cs:43 (_nonce field) | Field exists but no public getter |
|
|
| (c) GetName() | client.go:565 | PARTIAL | src/NATS.Server/NatsClient.cs:58 (ClientOpts?.Name) | Via ClientOpts property |
|
|
| (c) GetOpts() | client.go:573 | PORTED | src/NATS.Server/NatsClient.cs:58 (ClientOpts) | — |
|
|
| (c) GetTLSConnectionState() | client.go:579 | PORTED | src/NATS.Server/NatsClient.cs:110 (TlsState) | TlsConnectionState type |
|
|
| (c) RemoteAddress() | client.go:822 | PORTED | src/NATS.Server/NatsClient.cs:85-86 (RemoteIp, RemotePort) | Separate IP and port properties |
|
|
| (c) Kind() | client.go:844 | PORTED | src/NATS.Server/NatsClient.cs:57 (Kind property) | — |
|
|
| (c) RegisterUser() | client.go:981 | MISSING | — | User registration on client not implemented |
|
|
| (c) RegisterNkeyUser() | client.go:1018 | MISSING | — | NKey user registration on client not implemented |
|
|
| (c) Account() | client.go:6204 | PORTED | src/NATS.Server/NatsClient.cs:61 (Account property) | — |
|
|
| (c) Error/Errorf/Debugf/Noticef/Tracef/Warnf | client.go:6586-6610 | PORTED | src/NATS.Server/NatsClient.cs:52 (_logger) | Uses ILogger<T> structured logging |
|
|
| (c) RateLimitErrorf/Warnf/Debugf | client.go:6610-6650 | MISSING | — | No rate-limited logging per client |
|
|
| **golang/nats-server/server/client.go — Key Unexported Methods** | | | | |
|
|
| (c) initClient() | client.go:704 | PORTED | src/NATS.Server/NatsClient.cs:116 (constructor) | CID assignment, outbound setup, subs map, trace level |
|
|
| (c) readLoop() | client.go:1358 | PORTED | src/NATS.Server/NatsClient.cs:296 (FillPipeAsync + ProcessCommandsAsync) | System.IO.Pipelines instead of goroutine + byte buffer |
|
|
| (c) writeLoop() | client.go:1274 | PORTED | src/NATS.Server/NatsClient.cs:785 (RunWriteLoopAsync) | Channel-based async write loop |
|
|
| (c) processConnect() | client.go:2167 | PORTED | src/NATS.Server/NatsClient.cs:450 (ProcessConnectAsync) | JSON deserialization, auth, account binding |
|
|
| (c) processPub() | client.go:2822 | PORTED | src/NATS.Server/NatsClient.cs:627 (ProcessPub) | Subject validation, permission check, router dispatch |
|
|
| (c) processHeaderPub() | client.go:2745 | PORTED | src/NATS.Server/NatsClient.cs:660 | HPUB handling with header size split |
|
|
| (c) parseSub() / processSub() | client.go:2898/2927 | PORTED | src/NATS.Server/NatsClient.cs:560 (ProcessSub) | Permission check, limit check, sublist insert |
|
|
| (c) processUnsub() | client.go:3366 | PORTED | src/NATS.Server/NatsClient.cs:605 (ProcessUnsub) | Max messages auto-unsub, sublist remove |
|
|
| (c) processPing() | client.go:2628 | PORTED | src/NATS.Server/NatsClient.cs:414 (Pong case in DispatchCommandAsync) | — |
|
|
| (c) processPong() | client.go:2680 | PORTED | src/NATS.Server/NatsClient.cs:419-428 | RTT calculation, pingsOut reset |
|
|
| (c) queueOutbound() | client.go:2448 | PORTED | src/NATS.Server/NatsClient.cs:140 (QueueOutbound) | Channel-based with pending bytes tracking |
|
|
| (c) flushOutbound() | client.go:1618 | PORTED | src/NATS.Server/NatsClient.cs:785 (RunWriteLoopAsync drain) | Inline in write loop |
|
|
| (c) flushSignal() | client.go:1996 | PORTED | src/NATS.Server/NatsClient.cs:197 (SignalFlushPending) | Interlocked counter |
|
|
| (c) sendPing() | client.go:2577 | PORTED | src/NATS.Server/NatsClient.cs:888 | RTT start tracking, write PING |
|
|
| (c) sendPong() | client.go:2540 | PORTED | src/NATS.Server/NatsClient.cs:397 | Write PONG bytes |
|
|
| (c) sendRTTPing() | client.go:2548 | PORTED | src/NATS.Server/NatsClient.cs:887 | RTT start tick recording |
|
|
| (c) sendErr() | client.go:2608 | PORTED | src/NATS.Server/NatsClient.cs:779 (SendErr) | — |
|
|
| (c) sendOK() | client.go:2619 | PORTED | src/NATS.Server/NatsClient.cs:344 (OkBytes write on Verbose) | — |
|
|
| (c) processPingTimer() | client.go:5537 | PORTED | src/NATS.Server/NatsClient.cs:853 (RunPingTimerAsync) | PeriodicTimer-based, stale detection |
|
|
| (c) closeConnection() | client.go:5868 | PORTED | src/NATS.Server/NatsClient.cs:834 (CloseWithReasonAsync) | Async close with reason |
|
|
| (c) markConnAsClosed() | client.go:1906 | PORTED | src/NATS.Server/NatsClient.cs:902 (MarkClosed) | Skip-flush flag, reason tracking |
|
|
| (c) deliverMsg() | client.go:3620 | PARTIAL | src/NATS.Server/NatsClient.cs:692 (SendMessage) | Basic MSG/HMSG delivery. Missing: echo check, MQTT interop, GW reply mapping, stall wait |
|
|
| (c) processInboundMsg() | client.go:4139 | PARTIAL | src/NATS.Server/NatsServer.cs:938 (ProcessMessage) | Routes to local subs; missing: route/gateway/leaf-specific inbound handlers |
|
|
| (c) processInboundClientMsg() | client.go:4166 | PARTIAL | src/NATS.Server/NatsServer.cs:938 | Core pub path ported; missing: subject mapping, GW reply mapping, NRG prefix check |
|
|
| (c) processMsgResults() | client.go:4932 | PARTIAL | src/NATS.Server/NatsServer.cs:938-1088 | Queue group selection ported; missing: route forwarding, gateway forwarding, queue name collection |
|
|
| (c) stalledWait() | client.go:3575 | PORTED | src/NATS.Server/NatsClient.cs:1017 (StallGate class) | SemaphoreSlim-based stall gate |
|
|
| (c) flushClients() | client.go:1324 | MISSING | — | Post-readLoop batch flush of producer clients (pcd) not implemented |
|
|
| (c) handleWriteTimeout() | client.go:1842 | PARTIAL | src/NATS.Server/NatsClient.cs:807-813 | Slow consumer detection on timeout; missing: retry policy for routes |
|
|
| (c) msgHeader() | client.go:3534 | PORTED | src/NATS.Server/NatsClient.cs:692 (SendMessage) | Inline MSG line construction |
|
|
| (c) msgHeaderForRouteOrLeaf() | client.go:3447 | MISSING | — | RMSG header construction for routes/leafnodes |
|
|
| (c) setPermissions() | client.go:1055 | PORTED | src/NATS.Server/NatsClient.cs:477 | Via ClientPermissions.Build(authResult.Permissions) |
|
|
| (c) pubAllowed() | client.go:4052 | PORTED | src/NATS.Server/Auth/ClientPermissions.cs | IsPublishAllowed() |
|
|
| (c) canSubscribe() | client.go:3230 | PORTED | src/NATS.Server/Auth/ClientPermissions.cs | IsSubscribeAllowed() |
|
|
| (c) setExpiration() | client.go:1243 | PORTED | src/NATS.Server/NatsClient.cs:534-557 | Auth expiry timer via Task.Delay |
|
|
| (c) authTimeout() | client.go:2383 | PORTED | src/NATS.Server/NatsClient.cs:238-253 | Auth timeout Task |
|
|
| (c) authViolation() | client.go:2398 | PORTED | src/NATS.Server/NatsClient.cs:471-473 | Auth violation error + close |
|
|
| (c) maxPayloadViolation() | client.go:2440 | PORTED | src/NATS.Server/NatsClient.cs:633-639 | Max payload check and close |
|
|
| (c) maxSubsExceeded() | client.go:2433 | PORTED | src/NATS.Server/NatsClient.cs:571-576 | — |
|
|
| (c) maxConnExceeded() | client.go:2428 | PARTIAL | src/NATS.Server/NatsServer.cs:610 | Server-level max conn check; no per-client method |
|
|
| (c) subsAtLimit() | client.go:900 | PORTED | src/NATS.Server/NatsClient.cs:571 | MaxSubs check in ProcessSub |
|
|
| (c) applyAccountLimits() | client.go:923 | PARTIAL | src/NATS.Server/NatsClient.cs:488-494 | Account client count check; missing: maxPayload/maxSubs per-account override |
|
|
| (c) registerWithAccount() | client.go:854 | PORTED | src/NATS.Server/NatsClient.cs:480-494 | Account binding during connect |
|
|
| (c) setTraceLevel() | client.go:695 | PORTED | src/NATS.Server/NatsClient.cs:68 (SetTraceMode) | — |
|
|
| (c) clientType() | client.go:599 | PARTIAL | src/NATS.Server/NatsClient.cs:107 (IsWebSocket) | Bool for WS; no MQTT/NATS sub-type dispatch |
|
|
| (c) addShadowSubscriptions() | client.go:3057 | MISSING | — | Account import shadow subscription system |
|
|
| (c) pruneDenyCache() / prunePubPermsCache() / pruneReplyPerms() | client.go:4007-4019 | MISSING | — | Permission cache pruning |
|
|
| (c) trackRemoteReply() / pruneRemoteTracking() | client.go:3915/3956 | MISSING | — | Reply tracking for latency |
|
|
| (c) loadMsgDenyFilter() | client.go:1265 | MISSING | — | Message-level deny filter loading |
|
|
| (c) generateClientInfoJSON() | client.go:2589 | PARTIAL | src/NATS.Server/NatsClient.cs:674 (SendInfo) | Generates INFO JSON; missing: per-client IP inclusion, connect_urls |
|
|
| (c) updateS2AutoCompressionLevel() | client.go:2723 | MISSING | — | RTT-based compression adjustment |
|
|
| (c) addToPCD() | client.go:3905 | MISSING | — | Producer client data batch tracking |
|
|
| (c) collapsePtoNB() | client.go:1608 | NOT_APPLICABLE | — | Go-specific net.Buffers collapse; .NET uses Channel<T> |
|
|
| nbPoolGet/nbPoolPut | client.go:393-423 | NOT_APPLICABLE | — | Go-specific sync.Pool; .NET uses ArrayPool/MemoryPool |
|
|
| **golang/nats-server/server/opts.go — Types** | | | | |
|
|
| Options struct | opts.go:326 | PORTED | src/NATS.Server/NatsOptions.cs:8 | Core fields present. Missing: DontListen, NoLog, NoSigs, ProxyRequired, CustomAuth, CheckConfig, JetStreamTpm, resolverPreloads |
|
|
| ClusterOpts struct | opts.go:64 | PORTED | src/NATS.Server/Configuration/ClusterOptions.cs | Core fields; missing: compression, pool size, pinned accounts, write timeout policy |
|
|
| GatewayOpts struct | opts.go:114 | PORTED | src/NATS.Server/Configuration/GatewayOptions.cs | Core fields; missing: compression, reject unknown, send qsubs buf |
|
|
| RemoteGatewayOpts struct | opts.go:145 | PORTED | src/NATS.Server/Configuration/GatewayOptions.cs:27 | — |
|
|
| LeafNodeOpts struct | opts.go:154 | PORTED | src/NATS.Server/Configuration/LeafNodeOptions.cs:22 | Core fields; missing: compression, isolate subjects, min version |
|
|
| RemoteLeafOpts struct | opts.go:218 | PORTED | src/NATS.Server/Configuration/LeafNodeOptions.cs:7 | — |
|
|
| WebsocketOpts struct | opts.go:518 | PORTED | src/NATS.Server/NatsOptions.cs:138 (WebSocketOptions) | — |
|
|
| MQTTOpts struct | opts.go:613 | PORTED | src/NATS.Server/MqttOptions.cs:8 | — |
|
|
| TLSConfigOpts struct | opts.go:790 | PARTIAL | src/NATS.Server/NatsOptions.cs:96-107 | Flat TLS fields on NatsOptions; no TLSConfigOpts class |
|
|
| OCSPConfig struct | opts.go:823 | PARTIAL | src/NATS.Server/NatsOptions.cs:110 (OcspConfig) | Basic config; missing: full OCSP mode selection |
|
|
| AuthCallout struct | opts.go:308 | MISSING | — | External auth callout configuration |
|
|
| JSLimitOpts struct | opts.go:289 | MISSING | — | Per-account JetStream limit options |
|
|
| JSTpmOpts struct | opts.go:300 | NOT_APPLICABLE | — | TPM (Trusted Platform Module) not applicable to .NET |
|
|
| ProxiesConfig struct | opts.go:832 | MISSING | — | Proxy configuration |
|
|
| PinnedCertSet type | opts.go:59 | PORTED | src/NATS.Server/NatsOptions.cs:106 (TlsPinnedCerts HashSet) | — |
|
|
| **golang/nats-server/server/opts.go — Exported Functions** | | | | |
|
|
| ProcessConfigFile() | opts.go:870 | PORTED | src/NATS.Server/Configuration/ConfigProcessor.cs:15 | Full config file parsing |
|
|
| ConfigureOptions() | opts.go:6023 | PORTED | src/NATS.Server.Host/Program.cs:25-137 | CLI flag parsing inline |
|
|
| MergeOptions() | opts.go:5714 | PORTED | src/NATS.Server/Configuration/ConfigReloader.cs MergeCliOverrides | — |
|
|
| RoutesFromStr() | opts.go:5797 | MISSING | — | Parse comma-separated route URLs |
|
|
| GenTLSConfig() | opts.go:5633 | PARTIAL | src/NATS.Server/Tls/ | TLS setup exists but not as a standalone GenTLSConfig function |
|
|
| PrintTLSHelpAndDie() | opts.go:4886 | NOT_APPLICABLE | — | Go-specific CLI help |
|
|
| NoErrOnUnknownFields() | opts.go:50 | MISSING | — | Config parsing error control |
|
|
| **golang/nats-server/server/opts.go — Exported Options Methods** | | | | |
|
|
| (o) Clone() | opts.go:715 | MISSING | — | Deep copy of Options not implemented |
|
|
| (o) ProcessConfigFile() | opts.go:974 | PORTED | src/NATS.Server/Configuration/ConfigProcessor.cs:17 | — |
|
|
| (o) ProcessConfigString() | opts.go:990 | MISSING | — | Parse config from string |
|
|
| (o) ConfigDigest() | opts.go:1000 | MISSING | — | Config file digest |
|
|
| **golang/nats-server/server/reload.go** | | | | |
|
|
| FlagSnapshot var | reload.go:36 | PORTED | src/NATS.Server/NatsServer.cs:44-46 (_cliSnapshot, _cliFlags) | — |
|
|
| option interface | reload.go:43 | PORTED | src/NATS.Server/Configuration/IConfigChange.cs | IConfigChange with Apply, IsLoggingChange, etc. |
|
|
| noopOption / loggingOption / traceLevelOption | reload.go:77-129 | PORTED | src/NATS.Server/Configuration/IConfigChange.cs:42 (ConfigChange) | Simplified: single ConfigChange class with flags |
|
|
| traceOption / debugOption / logfileOption / etc. | reload.go:131-200+ | PARTIAL | src/NATS.Server/Configuration/ConfigReloader.cs | Diff-based; individual option types not replicated, uses property comparison |
|
|
| (s) Reload() | reload.go:1090 | PORTED | src/NATS.Server/NatsServer.cs:1623 (ReloadConfig) | Reads config file, diffs, applies |
|
|
| (s) ReloadOptions() | reload.go:1111 | PORTED | src/NATS.Server/NatsServer.cs:1633 (ReloadConfigCore) | Config diff and apply |
|
|
| reloadContext struct | reload.go:38 | MISSING | — | Reload context for cluster perms |
|
|
| applyBoolFlags() | reload.go:1185 | PARTIAL | src/NATS.Server/Configuration/ConfigReloader.cs MergeCliOverrides | CLI override merge exists; no bool flag reflection |
|
|
| **golang/nats-server/server/signal.go** | | | | |
|
|
| SetProcessName() | signal.go:32 | NOT_APPLICABLE | — | Unix-specific process name |
|
|
| (s) handleSignals() | signal.go:37 | PORTED | src/NATS.Server/NatsServer.cs:320 (HandleSignals) | PosixSignalRegistration for SIGINT/SIGTERM/SIGHUP/SIGUSR2 |
|
|
| ProcessSignal() | signal.go:89 | NOT_APPLICABLE | — | Unix-specific kill/pgrep signal dispatch |
|
|
| CommandToSignal() | signal.go:145 | NOT_APPLICABLE | — | Unix syscall.Signal mapping |
|
|
| resolvePids() | signal.go:165 | NOT_APPLICABLE | — | Unix-specific pgrep |
|
|
| **golang/nats-server/server/service.go** | | | | |
|
|
| Run() | service.go:20 | PORTED | src/NATS.Server.Host/Program.cs:235 | StartAsync + WaitForShutdown |
|
|
| isWindowsService() | service.go:26 | NOT_APPLICABLE | — | Build-tag guarded; not relevant for .NET |
|
|
|
|
### Summary Counts
|
|
|
|
| Status | Count |
|
|
|--------|-------|
|
|
| PORTED | 123 |
|
|
| PARTIAL | 30 |
|
|
| MISSING | 55 |
|
|
| NOT_APPLICABLE | 14 |
|
|
| DEFERRED | 0 |
|
|
| **Total** | **222** |
|
|
|
|
---
|
|
|
|
## 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/ -maxdepth 1 -name '*.cs' -type f -exec cat {} + | wc -l
|
|
# Re-count .NET test LOC for this module
|
|
find tests/NATS.Server.Tests/ -maxdepth 1 -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')"
|
|
```
|
|
|
|
## Change Log
|
|
|
|
| Date | Change | By |
|
|
|------|--------|----|
|
|
| 2026-02-25 | File created with LLM analysis instructions | auto |
|
|
| 2026-02-25 | Gap inventory populated: 222 symbols analyzed (123 PORTED, 30 PARTIAL, 55 MISSING, 14 NOT_APPLICABLE) across server.go, client.go, opts.go, reload.go, signal.go, service.go, main.go | claude-opus |
|