Session 21 (402 features, IDs 3195-3387, 584-792): - JetStream/StreamTypes.cs: StreamInfo, ConsumerInfo, SequenceInfo, JSPubAckResponse, WaitQueue, ClusterInfo, PeerInfo, message types, ConsumerAction enum, CreateConsumerRequest, PriorityGroupState - JetStream/NatsStream.cs: NatsStream class (stub methods, IDisposable) - JetStream/NatsConsumer.cs: NatsConsumer class (stub methods, IDisposable) - Updated JetStreamApiTypes.cs: removed duplicate StreamInfo/ConsumerInfo stubs Session 22 (153 features, IDs 2252-2404): - Mqtt/MqttConstants.cs: all MQTT protocol constants, packet types, flags - Mqtt/MqttTypes.cs: MqttSession, MqttSubscription, MqttWill, MqttJsa, MqttAccountSessionManager, MqttHandler and supporting types - Mqtt/MqttHandler.cs: per-client MQTT state, MqttServerExtensions stubs Session 23 (97 features, IDs 3506-3543, 2443-2501): - WebSocket/WebSocketConstants.cs: WsOpCode enum, frame bits, close codes - WebSocket/WebSocketTypes.cs: WsReadInfo, SrvWebsocket (replaces stub), WebSocketHandler stubs - Auth/Ocsp/OcspTypes.cs: OcspMode, OcspMonitor (replaces stub), IOcspResponseCache (replaces stub), NoOpCache, LocalDirCache All features (3503 complete, 0 not_started). Phase 6 now at 58.9%.
336 lines
16 KiB
C#
336 lines
16 KiB
C#
// Copyright 2012-2026 The NATS Authors
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
//
|
|
// Adapted from server/server.go in the NATS server Go source.
|
|
|
|
using System.Collections.Concurrent;
|
|
using System.Threading.Channels;
|
|
using Microsoft.Extensions.Logging;
|
|
using Microsoft.Extensions.Logging.Abstractions;
|
|
using ZB.MOM.NatsNet.Server.Auth;
|
|
using ZB.MOM.NatsNet.Server.Auth.Ocsp;
|
|
using ZB.MOM.NatsNet.Server.Internal;
|
|
using ZB.MOM.NatsNet.Server.Internal.DataStructures;
|
|
using ZB.MOM.NatsNet.Server.WebSocket;
|
|
|
|
namespace ZB.MOM.NatsNet.Server;
|
|
|
|
/// <summary>
|
|
/// The core NATS server class.
|
|
/// Mirrors Go <c>Server</c> struct in server/server.go.
|
|
/// Session 09: initialization, configuration, and account management.
|
|
/// Sessions 10-23 add further capabilities as partial class files.
|
|
/// </summary>
|
|
public sealed partial class NatsServer : INatsServer
|
|
{
|
|
// =========================================================================
|
|
// Build-time stamps (mirrors package-level vars in server.go)
|
|
// =========================================================================
|
|
|
|
/// <summary>
|
|
/// Binary-stamped trusted operator keys (space-separated NKey public keys).
|
|
/// In Go this is a package-level var that can be overridden at build time.
|
|
/// In .NET it can be set before constructing any server instance.
|
|
/// Mirrors Go package-level <c>trustedKeys</c> var.
|
|
/// </summary>
|
|
public static string StampedTrustedKeys { get; set; } = string.Empty;
|
|
|
|
// =========================================================================
|
|
// Atomic counters (mirrors fields accessed with atomic operations)
|
|
// =========================================================================
|
|
|
|
private ulong _gcid; // global client id counter
|
|
private long _pinnedAccFail; // pinned-account auth failures
|
|
private int _activeAccounts; // number of active accounts
|
|
|
|
// =========================================================================
|
|
// Stats (embedded Go structs: stats, scStats, staleStats)
|
|
// =========================================================================
|
|
|
|
private readonly ServerStats _stats = new();
|
|
private readonly SlowConsumerStats _scStats = new();
|
|
private readonly InternalStaleStats _staleStats = new();
|
|
|
|
// =========================================================================
|
|
// Core identity
|
|
// =========================================================================
|
|
|
|
// kp / xkp are NKey keypairs — represented as byte arrays here.
|
|
// Full crypto operations deferred to auth session.
|
|
private byte[]? _kpSeed; // server NKey seed
|
|
private string _pub = string.Empty; // server public key (server ID)
|
|
private byte[]? _xkpSeed; // x25519 key seed
|
|
private string _xpub = string.Empty; // x25519 public key
|
|
|
|
// =========================================================================
|
|
// Server info (wire protocol)
|
|
// =========================================================================
|
|
|
|
private readonly ReaderWriterLockSlim _mu = new(LockRecursionPolicy.SupportsRecursion);
|
|
private readonly ReaderWriterLockSlim _reloadMu = new(LockRecursionPolicy.SupportsRecursion);
|
|
internal ServerInfo _info = new();
|
|
private string _configFile = string.Empty;
|
|
|
|
// =========================================================================
|
|
// Options (protected by _optsMu)
|
|
// =========================================================================
|
|
|
|
private readonly ReaderWriterLockSlim _optsMu = new(LockRecursionPolicy.NoRecursion);
|
|
private ServerOptions _opts;
|
|
|
|
// =========================================================================
|
|
// Running / shutdown state
|
|
// =========================================================================
|
|
|
|
private int _running; // 1 = running, 0 = not (Interlocked)
|
|
private int _shutdown; // 1 = shutting down
|
|
private readonly CancellationTokenSource _quitCts = new();
|
|
private readonly TaskCompletionSource _startupComplete = new(TaskCreationOptions.RunContinuationsAsynchronously);
|
|
private readonly TaskCompletionSource _shutdownComplete = new(TaskCreationOptions.RunContinuationsAsynchronously);
|
|
private Task? _quitTask;
|
|
|
|
// =========================================================================
|
|
// Listeners (forward-declared stubs — fully wired in session 10)
|
|
// =========================================================================
|
|
|
|
private System.Net.Sockets.TcpListener? _listener;
|
|
private Exception? _listenerErr;
|
|
|
|
// HTTP monitoring listener
|
|
private System.Net.Sockets.TcpListener? _http;
|
|
|
|
// Route listener
|
|
private System.Net.Sockets.TcpListener? _routeListener;
|
|
private Exception? _routeListenerErr;
|
|
|
|
// Gateway listener
|
|
private System.Net.Sockets.TcpListener? _gatewayListener;
|
|
private Exception? _gatewayListenerErr;
|
|
|
|
// Leaf-node listener
|
|
private System.Net.Sockets.TcpListener? _leafNodeListener;
|
|
private Exception? _leafNodeListenerErr;
|
|
|
|
// Profiling listener
|
|
private System.Net.Sockets.TcpListener? _profiler;
|
|
|
|
// Accept-loop done channel — each accept loop sends true when it exits.
|
|
private readonly System.Threading.Channels.Channel<bool> _done =
|
|
System.Threading.Channels.Channel.CreateUnbounded<bool>();
|
|
|
|
// Lame-duck channel — created in lameDuckMode, receives one signal per accept loop.
|
|
private System.Threading.Channels.Channel<bool>? _ldmCh;
|
|
|
|
// The no-auth user that only the system account can use (auth session).
|
|
private string _sysAccOnlyNoAuthUser = string.Empty;
|
|
|
|
// =========================================================================
|
|
// Accounts
|
|
// =========================================================================
|
|
|
|
private Account? _gacc; // global account
|
|
private Account? _sysAccAtomic; // system account (atomic)
|
|
private readonly ConcurrentDictionary<string, Account> _accounts = new(StringComparer.Ordinal);
|
|
private readonly ConcurrentDictionary<string, Account> _tmpAccounts = new(StringComparer.Ordinal);
|
|
private IAccountResolver? _accResolver;
|
|
private InternalState? _sys; // system messaging state
|
|
|
|
// =========================================================================
|
|
// Client/route/leaf tracking
|
|
// =========================================================================
|
|
|
|
private readonly Dictionary<ulong, ClientConnection> _clients = [];
|
|
private readonly Dictionary<ulong, ClientConnection> _leafs = [];
|
|
private Dictionary<string, List<ClientConnection>> _routes = [];
|
|
private int _routesPoolSize = 1;
|
|
private bool _routesReject;
|
|
private int _routesNoPool;
|
|
private Dictionary<string, Dictionary<string, ClientConnection>>? _accRoutes;
|
|
private readonly ConcurrentDictionary<string, object?> _accRouteByHash = new(StringComparer.Ordinal);
|
|
private Channel<struct_>? _accAddedCh; // stub
|
|
private string _accAddedReqId = string.Empty;
|
|
|
|
// =========================================================================
|
|
// User / nkey maps
|
|
// =========================================================================
|
|
|
|
private Dictionary<string, Auth.User>? _users;
|
|
private Dictionary<string, Auth.NkeyUser>? _nkeys;
|
|
|
|
// =========================================================================
|
|
// Connection tracking
|
|
// =========================================================================
|
|
|
|
private ulong _totalClients;
|
|
private ClosedRingBuffer _closed = new(0);
|
|
private DateTime _start;
|
|
private DateTime _configTime;
|
|
|
|
// =========================================================================
|
|
// Goroutine / WaitGroup tracking
|
|
// =========================================================================
|
|
|
|
private readonly object _grMu = new();
|
|
private bool _grRunning;
|
|
private readonly Dictionary<ulong, ClientConnection> _grTmpClients = [];
|
|
private readonly WaitGroup _grWg = new();
|
|
|
|
// =========================================================================
|
|
// Cluster name (separate lock)
|
|
// =========================================================================
|
|
|
|
private readonly ReaderWriterLockSlim _cnMu = new(LockRecursionPolicy.NoRecursion);
|
|
private string _cn = string.Empty;
|
|
private ServerInfo _routeInfo = new();
|
|
private bool _leafNoCluster;
|
|
private bool _leafNodeEnabled;
|
|
private bool _leafDisableConnect;
|
|
private bool _ldm;
|
|
|
|
// =========================================================================
|
|
// Trusted keys
|
|
// =========================================================================
|
|
|
|
private List<string>? _trustedKeys;
|
|
private HashSet<string> _strictSigningKeyUsage = [];
|
|
|
|
// =========================================================================
|
|
// Monitoring / stats endpoint
|
|
// =========================================================================
|
|
|
|
private string _httpBasePath = string.Empty;
|
|
private readonly Dictionary<string, ulong> _httpReqStats = [];
|
|
|
|
// =========================================================================
|
|
// Client connect URLs
|
|
// =========================================================================
|
|
|
|
private readonly List<string> _clientConnectUrls = [];
|
|
private readonly RefCountedUrlSet _clientConnectUrlsMap = new();
|
|
|
|
// =========================================================================
|
|
// Gateway / Websocket / MQTT / OCSP stubs
|
|
// =========================================================================
|
|
|
|
private readonly SrvGateway _gateway = new();
|
|
private readonly SrvWebsocket _websocket = new();
|
|
private readonly SrvMqtt _mqtt = new();
|
|
private OcspMonitor[]? _ocsps;
|
|
private bool _ocspPeerVerify;
|
|
private IOcspResponseCache? _ocsprc;
|
|
|
|
// =========================================================================
|
|
// Gateway reply map (stub — session 16)
|
|
// =========================================================================
|
|
|
|
private readonly SubscriptionIndex _gwLeafSubs;
|
|
|
|
// =========================================================================
|
|
// NUID event ID generator
|
|
// =========================================================================
|
|
|
|
// Replaced by actual NUID in session 10. Use Guid for now.
|
|
private string NextEventId() => Guid.NewGuid().ToString("N");
|
|
|
|
// =========================================================================
|
|
// Various stubs
|
|
// =========================================================================
|
|
|
|
private readonly List<string> _leafRemoteCfgs = []; // stub — session 15
|
|
private readonly List<object> _proxiesKeyPairs = []; // stub — session 09 (proxies)
|
|
private readonly Dictionary<string, Dictionary<ulong, ClientConnection>> _proxiedConns = [];
|
|
private long _cproto; // count of INFO-capable clients
|
|
private readonly ConcurrentDictionary<string, object?> _nodeToInfo = new(StringComparer.Ordinal);
|
|
private readonly ConcurrentDictionary<string, object?> _raftNodes = new(StringComparer.Ordinal);
|
|
private readonly Dictionary<string, string> _routesToSelf = [];
|
|
private INetResolver? _routeResolver;
|
|
private readonly ConcurrentDictionary<string, object?> _rateLimitLogging = new();
|
|
private readonly Channel<TimeSpan> _rateLimitLoggingCh;
|
|
private RateCounter? _connRateCounter;
|
|
|
|
// GW reply map expiration
|
|
private readonly ConcurrentDictionary<string, object?> _gwrm = new();
|
|
|
|
// Catchup bytes
|
|
private readonly ReaderWriterLockSlim _gcbMu = new(LockRecursionPolicy.NoRecursion);
|
|
private long _gcbOut;
|
|
private long _gcbOutMax;
|
|
private readonly Channel<struct_>? _gcbKick; // stub
|
|
|
|
// Sync-out semaphore
|
|
private readonly SemaphoreSlim _syncOutSem;
|
|
private const int MaxConcurrentSyncRequests = 16;
|
|
|
|
// =========================================================================
|
|
// Logging
|
|
// =========================================================================
|
|
|
|
private ILogger _logger = NullLogger.Instance;
|
|
private int _traceEnabled;
|
|
private int _debugEnabled;
|
|
private int _traceSysAcc;
|
|
|
|
// =========================================================================
|
|
// INatsServer implementation
|
|
// =========================================================================
|
|
|
|
/// <inheritdoc/>
|
|
public ulong NextClientId() => Interlocked.Increment(ref _gcid);
|
|
|
|
/// <inheritdoc/>
|
|
public ServerOptions Options => GetOpts();
|
|
|
|
/// <inheritdoc/>
|
|
public bool TraceEnabled => Interlocked.CompareExchange(ref _traceEnabled, 0, 0) != 0;
|
|
|
|
/// <inheritdoc/>
|
|
public bool TraceSysAcc => Interlocked.CompareExchange(ref _traceSysAcc, 0, 0) != 0;
|
|
|
|
/// <inheritdoc/>
|
|
public ILogger Logger => _logger;
|
|
|
|
/// <inheritdoc/>
|
|
public void DecActiveAccounts() => Interlocked.Decrement(ref _activeAccounts);
|
|
|
|
/// <inheritdoc/>
|
|
public void IncActiveAccounts() => Interlocked.Increment(ref _activeAccounts);
|
|
|
|
// =========================================================================
|
|
// Logging helpers (mirrors Go s.Debugf / s.Noticef / s.Warnf / s.Errorf)
|
|
// =========================================================================
|
|
|
|
internal void Debugf(string fmt, params object?[] args) => _logger.LogDebug(fmt, args);
|
|
internal void Noticef(string fmt, params object?[] args) => _logger.LogInformation(fmt, args);
|
|
internal void Warnf(string fmt, params object?[] args) => _logger.LogWarning(fmt, args);
|
|
internal void Errorf(string fmt, params object?[] args) => _logger.LogError(fmt, args);
|
|
internal void Fatalf(string fmt, params object?[] args) => _logger.LogCritical(fmt, args);
|
|
|
|
// =========================================================================
|
|
// Constructor
|
|
// =========================================================================
|
|
|
|
/// <summary>
|
|
/// Direct constructor — do not call directly; use <see cref="NewServer(ServerOptions)"/>.
|
|
/// </summary>
|
|
private NatsServer(ServerOptions opts)
|
|
{
|
|
_opts = opts;
|
|
_gwLeafSubs = SubscriptionIndex.NewSublistWithCache();
|
|
_rateLimitLoggingCh = Channel.CreateBounded<TimeSpan>(1);
|
|
_syncOutSem = new SemaphoreSlim(MaxConcurrentSyncRequests, MaxConcurrentSyncRequests);
|
|
}
|
|
}
|
|
|
|
// Placeholder struct for stub channel types
|
|
internal readonly struct struct_ { }
|