Improve docs coverage and refresh profiling parser artifacts
Add domain-specific XML documentation across src server components to satisfy CommentChecker, and update dotTrace parsing outputs used for diagnostics.
This commit is contained in:
@@ -9,12 +9,26 @@ public sealed class ExternalAuthCalloutAuthenticator : IAuthenticator
|
||||
private readonly IExternalAuthClient _client;
|
||||
private readonly TimeSpan _timeout;
|
||||
|
||||
/// <summary>
|
||||
/// Creates an authenticator that delegates user validation to the external auth callout subject.
|
||||
/// This mirrors the NATS external authorization flow used for centralized policy decisions.
|
||||
/// </summary>
|
||||
/// <param name="client">Client used to publish authorization requests and receive decisions.</param>
|
||||
/// <param name="timeout">Maximum time to wait for an authorization decision.</param>
|
||||
public ExternalAuthCalloutAuthenticator(IExternalAuthClient client, TimeSpan timeout)
|
||||
{
|
||||
_client = client;
|
||||
_timeout = timeout;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Authenticates a client by calling the external authorization service and mapping the decision
|
||||
/// into a local identity/account context for the accepted connection.
|
||||
/// </summary>
|
||||
/// <param name="context">Connection authentication inputs received from the CONNECT payload.</param>
|
||||
/// <returns>
|
||||
/// An <see cref="AuthResult"/> when the callout allows the connection; otherwise <see langword="null"/>.
|
||||
/// </returns>
|
||||
public AuthResult? Authenticate(ClientAuthContext context)
|
||||
{
|
||||
using var cts = new CancellationTokenSource(_timeout);
|
||||
|
||||
@@ -99,9 +99,17 @@ public sealed class AccountLimits
|
||||
|
||||
public sealed class AccountJetStreamLimits
|
||||
{
|
||||
/// <summary>
|
||||
/// Maximum number of streams the account can create in JetStream.
|
||||
/// This limit protects cluster resources in multi-tenant deployments.
|
||||
/// </summary>
|
||||
[JsonPropertyName("max_streams")]
|
||||
public int MaxStreams { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Optional JetStream service tier label assigned to the account (for example, dev or prod).
|
||||
/// Tier is used for policy and placement decisions in operator-managed environments.
|
||||
/// </summary>
|
||||
[JsonPropertyName("tier")]
|
||||
public string? Tier { get; set; }
|
||||
}
|
||||
|
||||
@@ -15,6 +15,14 @@ internal static class JwtConnectionTypes
|
||||
Standard, Websocket, Leafnode, LeafnodeWs, Mqtt, MqttWs, InProcess,
|
||||
];
|
||||
|
||||
/// <summary>
|
||||
/// Converts raw JWT allowed connection type values into normalized server constants
|
||||
/// and tracks whether any unknown connection types were supplied by policy.
|
||||
/// </summary>
|
||||
/// <param name="values">Allowed connection type values from user JWT claims.</param>
|
||||
/// <returns>
|
||||
/// A set of valid normalized types and a flag indicating whether unknown values were present.
|
||||
/// </returns>
|
||||
public static (HashSet<string> Valid, bool HasUnknown) Convert(IEnumerable<string>? values)
|
||||
{
|
||||
var valid = new HashSet<string>(StringComparer.Ordinal);
|
||||
|
||||
@@ -223,11 +223,11 @@ public sealed class JwtToken
|
||||
/// </summary>
|
||||
public sealed class JwtHeader
|
||||
{
|
||||
[System.Text.Json.Serialization.JsonPropertyName("alg")]
|
||||
/// <summary>JWT signing algorithm identifier (typically <c>ed25519-nkey</c> for NATS).</summary>
|
||||
[System.Text.Json.Serialization.JsonPropertyName("alg")]
|
||||
public string? Algorithm { get; set; }
|
||||
|
||||
[System.Text.Json.Serialization.JsonPropertyName("typ")]
|
||||
/// <summary>JWT type marker (typically <c>JWT</c>).</summary>
|
||||
[System.Text.Json.Serialization.JsonPropertyName("typ")]
|
||||
public string? Type { get; set; }
|
||||
}
|
||||
|
||||
@@ -12,12 +12,26 @@ public sealed class JwtAuthenticator : IAuthenticator
|
||||
private readonly string[] _trustedKeys;
|
||||
private readonly IAccountResolver _resolver;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a JWT authenticator that trusts the provided operator keys and resolves account JWTs
|
||||
/// from the configured resolver source.
|
||||
/// </summary>
|
||||
/// <param name="trustedKeys">Trusted operator/signing keys allowed to issue account JWTs.</param>
|
||||
/// <param name="resolver">Resolver used to fetch account claims by account public key.</param>
|
||||
public JwtAuthenticator(string[] trustedKeys, IAccountResolver resolver)
|
||||
{
|
||||
_trustedKeys = trustedKeys;
|
||||
_resolver = resolver;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Authenticates a client using NATS JWT flow: decode user claims, resolve account claims,
|
||||
/// verify trust/signatures/revocations, and derive connection permissions and limits.
|
||||
/// </summary>
|
||||
/// <param name="context">CONNECT options and nonce/signature context for the client.</param>
|
||||
/// <returns>
|
||||
/// Populated authentication result when JWT policy allows the connection; otherwise <see langword="null"/>.
|
||||
/// </returns>
|
||||
public AuthResult? Authenticate(ClientAuthContext context)
|
||||
{
|
||||
var jwt = context.Opts.JWT;
|
||||
|
||||
@@ -18,6 +18,12 @@ public sealed class NKeyAuthenticator(IEnumerable<NKeyUser> nkeyUsers) : IAuthen
|
||||
u => u,
|
||||
StringComparer.Ordinal);
|
||||
|
||||
/// <summary>
|
||||
/// Authenticates a client by verifying its nonce signature with the presented NKey public key
|
||||
/// and returning the mapped account and permission context for that key.
|
||||
/// </summary>
|
||||
/// <param name="context">CONNECT payload plus server nonce used for signature verification.</param>
|
||||
/// <returns><see cref="AuthResult"/> for a valid NKey user; otherwise <see langword="null"/>.</returns>
|
||||
public AuthResult? Authenticate(ClientAuthContext context)
|
||||
{
|
||||
var clientNkey = context.Opts.Nkey;
|
||||
|
||||
@@ -2,6 +2,12 @@ namespace NATS.Server.Auth;
|
||||
|
||||
public sealed class ProxyAuthenticator(ProxyAuthOptions options) : IAuthenticator
|
||||
{
|
||||
/// <summary>
|
||||
/// Authenticates a client from a trusted proxy identity prefix and maps it to the configured account.
|
||||
/// This supports edge proxies that perform upstream auth and pass a canonical user principal.
|
||||
/// </summary>
|
||||
/// <param name="context">Client credentials and connection metadata from CONNECT.</param>
|
||||
/// <returns><see cref="AuthResult"/> when proxy-auth rules match; otherwise <see langword="null"/>.</returns>
|
||||
public AuthResult? Authenticate(ClientAuthContext context)
|
||||
{
|
||||
if (!options.Enabled)
|
||||
|
||||
@@ -14,12 +14,22 @@ public sealed class SimpleUserPasswordAuthenticator : IAuthenticator
|
||||
private readonly byte[] _expectedUsername;
|
||||
private readonly string _serverPassword;
|
||||
|
||||
/// <summary>
|
||||
/// Creates an authenticator for a single configured user credential pair from server options.
|
||||
/// </summary>
|
||||
/// <param name="username">Expected username for incoming client connections.</param>
|
||||
/// <param name="password">Expected password (plain or bcrypt hash) for that username.</param>
|
||||
public SimpleUserPasswordAuthenticator(string username, string password)
|
||||
{
|
||||
_expectedUsername = Encoding.UTF8.GetBytes(username);
|
||||
_serverPassword = password;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Authenticates the configured single user using constant-time comparisons and optional bcrypt verification.
|
||||
/// </summary>
|
||||
/// <param name="context">Client-provided username/password from CONNECT.</param>
|
||||
/// <returns><see cref="AuthResult"/> on successful validation; otherwise <see langword="null"/>.</returns>
|
||||
public AuthResult? Authenticate(ClientAuthContext context)
|
||||
{
|
||||
var clientUsername = context.Opts.Username;
|
||||
|
||||
@@ -7,11 +7,20 @@ public sealed class TokenAuthenticator : IAuthenticator
|
||||
{
|
||||
private readonly byte[] _expectedToken;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a token authenticator for deployments that use shared bearer tokens.
|
||||
/// </summary>
|
||||
/// <param name="token">Server-configured token value clients must present in CONNECT.</param>
|
||||
public TokenAuthenticator(string token)
|
||||
{
|
||||
_expectedToken = Encoding.UTF8.GetBytes(token);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Authenticates the client using constant-time token comparison to avoid timing leakage.
|
||||
/// </summary>
|
||||
/// <param name="context">Client connection options containing the presented token.</param>
|
||||
/// <returns><see cref="AuthResult"/> for a matching token; otherwise <see langword="null"/>.</returns>
|
||||
public AuthResult? Authenticate(ClientAuthContext context)
|
||||
{
|
||||
var clientToken = context.Opts.Token;
|
||||
|
||||
@@ -13,6 +13,10 @@ public sealed class UserPasswordAuthenticator : IAuthenticator
|
||||
{
|
||||
private readonly Dictionary<string, User> _users;
|
||||
|
||||
/// <summary>
|
||||
/// Creates an authenticator for a configured user set and builds a fast lookup by username.
|
||||
/// </summary>
|
||||
/// <param name="users">Configured users with account mappings and permission scopes.</param>
|
||||
public UserPasswordAuthenticator(IEnumerable<User> users)
|
||||
{
|
||||
_users = new Dictionary<string, User>(StringComparer.Ordinal);
|
||||
@@ -20,6 +24,11 @@ public sealed class UserPasswordAuthenticator : IAuthenticator
|
||||
_users[user.Username] = user;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Authenticates a username/password client and returns account, permissions, and connection expiry metadata.
|
||||
/// </summary>
|
||||
/// <param name="context">Client CONNECT credentials.</param>
|
||||
/// <returns><see cref="AuthResult"/> when credentials match; otherwise <see langword="null"/>.</returns>
|
||||
public AuthResult? Authenticate(ClientAuthContext context)
|
||||
{
|
||||
var username = context.Opts.Username;
|
||||
|
||||
@@ -28,6 +28,11 @@ public enum ClientClosedReason
|
||||
|
||||
public static class ClientClosedReasonExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Converts an internal close reason enum into the human-readable text exposed by monitoring endpoints.
|
||||
/// </summary>
|
||||
/// <param name="reason">Internal close classification captured at disconnect time.</param>
|
||||
/// <returns>Display string used in `/connz` and related operational diagnostics.</returns>
|
||||
public static string ToReasonString(this ClientClosedReason reason) => reason switch
|
||||
{
|
||||
ClientClosedReason.None => "",
|
||||
|
||||
@@ -17,6 +17,12 @@ public enum ClientKind
|
||||
|
||||
public static class ClientKindExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Indicates whether a client kind represents internal server infrastructure traffic
|
||||
/// rather than an external end-user connection.
|
||||
/// </summary>
|
||||
/// <param name="kind">Connection kind being evaluated.</param>
|
||||
/// <returns><see langword="true"/> for internal kinds such as system and JetStream.</returns>
|
||||
public static bool IsInternal(this ClientKind kind) =>
|
||||
kind is ClientKind.System or ClientKind.JetStream or ClientKind.Account;
|
||||
}
|
||||
|
||||
@@ -63,6 +63,12 @@ public sealed class NatsConfLexer
|
||||
_ilstart = 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tokenizes a NATS configuration document into lexical tokens consumed by the config parser.
|
||||
/// The lexer preserves Go-compatible token rules for production config parity.
|
||||
/// </summary>
|
||||
/// <param name="input">Raw configuration text in NATS conf syntax.</param>
|
||||
/// <returns>Ordered token stream including error tokens when malformed input is encountered.</returns>
|
||||
public static IReadOnlyList<Token> Tokenize(string input)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(input);
|
||||
|
||||
@@ -11,11 +11,19 @@ public sealed class AdaptiveReadBuffer
|
||||
private int _target = 4096;
|
||||
private int _consecutiveShortReads;
|
||||
|
||||
/// <summary>
|
||||
/// Current target buffer size used for the next socket read operation.
|
||||
/// </summary>
|
||||
public int CurrentSize => Math.Clamp(_target, 512, 64 * 1024);
|
||||
|
||||
/// <summary>Number of consecutive short reads since last full read or grow.</summary>
|
||||
public int ConsecutiveShortReads => _consecutiveShortReads;
|
||||
|
||||
/// <summary>
|
||||
/// Updates adaptive sizing state using the number of bytes returned by the latest read.
|
||||
/// Full reads bias toward growth for throughput, repeated short reads bias toward shrink to save memory.
|
||||
/// </summary>
|
||||
/// <param name="bytesRead">Byte count returned by the transport read.</param>
|
||||
public void RecordRead(int bytesRead)
|
||||
{
|
||||
if (bytesRead <= 0)
|
||||
|
||||
@@ -2,6 +2,13 @@ namespace NATS.Server.Imports;
|
||||
|
||||
public sealed class ServiceLatency
|
||||
{
|
||||
/// <summary>
|
||||
/// Percentage of service requests to sample for latency telemetry export.
|
||||
/// </summary>
|
||||
public int SamplingPercentage { get; init; } = 100;
|
||||
|
||||
/// <summary>
|
||||
/// Subject where sampled service latency observations are published.
|
||||
/// </summary>
|
||||
public string Subject { get; init; } = string.Empty;
|
||||
}
|
||||
|
||||
@@ -2,5 +2,8 @@ namespace NATS.Server.Imports;
|
||||
|
||||
public sealed class StreamExport
|
||||
{
|
||||
/// <summary>
|
||||
/// Authorization rules that govern which remote accounts may import this stream export.
|
||||
/// </summary>
|
||||
public ExportAuth Auth { get; init; } = new();
|
||||
}
|
||||
|
||||
@@ -2,6 +2,12 @@ namespace NATS.Server.JetStream.Api.Handlers;
|
||||
|
||||
public static class AccountApiHandlers
|
||||
{
|
||||
/// <summary>
|
||||
/// Builds JetStream account usage info for `$JS.API.INFO`, including current stream and consumer counts.
|
||||
/// </summary>
|
||||
/// <param name="streams">Stream manager for the account context.</param>
|
||||
/// <param name="consumers">Consumer manager for the account context.</param>
|
||||
/// <returns>API response payload containing JetStream account statistics.</returns>
|
||||
public static JetStreamApiResponse HandleInfo(StreamManager streams, ConsumerManager consumers)
|
||||
{
|
||||
return new JetStreamApiResponse
|
||||
|
||||
@@ -7,6 +7,13 @@ public static class DirectApiHandlers
|
||||
{
|
||||
private const string Prefix = JetStreamApiSubjects.DirectGet;
|
||||
|
||||
/// <summary>
|
||||
/// Handles direct message fetch requests by stream and sequence for `$JS.API.DIRECT.GET.*`.
|
||||
/// </summary>
|
||||
/// <param name="subject">API subject containing the target stream token.</param>
|
||||
/// <param name="payload">JSON payload with the requested sequence number.</param>
|
||||
/// <param name="streamManager">Stream manager used to read the stored message.</param>
|
||||
/// <returns>Direct message response or a not-found/validation error response.</returns>
|
||||
public static JetStreamApiResponse HandleGet(string subject, ReadOnlySpan<byte> payload, StreamManager streamManager)
|
||||
{
|
||||
var streamName = ExtractTrailingToken(subject, Prefix);
|
||||
|
||||
@@ -2,7 +2,14 @@ namespace NATS.Server.JetStream.Api;
|
||||
|
||||
public sealed class JetStreamApiError
|
||||
{
|
||||
/// <summary>
|
||||
/// Numeric JetStream API error code returned to clients for programmatic handling.
|
||||
/// </summary>
|
||||
public int Code { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Human-readable description of the API error condition.
|
||||
/// </summary>
|
||||
public string Description { get; init; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -4,11 +4,20 @@ public sealed class AssetPlacementPlanner
|
||||
{
|
||||
private readonly int _nodes;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a planner that allocates stream/consumer replicas across the available cluster node count.
|
||||
/// </summary>
|
||||
/// <param name="nodes">Number of eligible JetStream nodes in the placement domain.</param>
|
||||
public AssetPlacementPlanner(int nodes)
|
||||
{
|
||||
_nodes = Math.Max(nodes, 1);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Produces a deterministic list of node slots for replica placement capped by available nodes.
|
||||
/// </summary>
|
||||
/// <param name="replicas">Requested replica count from stream or consumer configuration.</param>
|
||||
/// <returns>Ordered node slot identifiers selected for placement.</returns>
|
||||
public IReadOnlyList<int> PlanReplicas(int replicas)
|
||||
{
|
||||
var count = Math.Min(Math.Max(replicas, 1), _nodes);
|
||||
|
||||
@@ -25,6 +25,7 @@ internal static class MetaSnapshotCodec
|
||||
/// Encodes <paramref name="assignments"/> into the versioned, S2-compressed binary format.
|
||||
/// Go reference: jetstream_cluster.go:2075 encodeMetaSnapshot.
|
||||
/// </summary>
|
||||
/// <param name="assignments">Current stream placement assignments to persist into the meta snapshot.</param>
|
||||
public static byte[] Encode(Dictionary<string, StreamAssignment> assignments)
|
||||
{
|
||||
var json = JsonSerializer.SerializeToUtf8Bytes(assignments, SerializerOptions);
|
||||
@@ -40,6 +41,7 @@ internal static class MetaSnapshotCodec
|
||||
/// Decodes a versioned, S2-compressed binary snapshot into a stream assignment map.
|
||||
/// Go reference: jetstream_cluster.go:2100 decodeMetaSnapshot.
|
||||
/// </summary>
|
||||
/// <param name="data">Versioned binary snapshot payload received from replicated state.</param>
|
||||
/// <exception cref="InvalidOperationException">
|
||||
/// Thrown when <paramref name="data"/> is too short or contains an unrecognised version.
|
||||
/// </exception>
|
||||
|
||||
@@ -12,6 +12,12 @@ public sealed class DeliveryInterestTracker
|
||||
private int _subscriberCount;
|
||||
private DateTime? _lastUnsubscribeUtc;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a tracker for consumer delivery-subject interest with an optional inactivity timeout override.
|
||||
/// </summary>
|
||||
/// <param name="inactiveTimeout">
|
||||
/// Duration with zero subscribers before the consumer is considered inactive for cleanup.
|
||||
/// </param>
|
||||
public DeliveryInterestTracker(TimeSpan? inactiveTimeout = null)
|
||||
{
|
||||
_inactiveTimeout = inactiveTimeout ?? TimeSpan.FromSeconds(30);
|
||||
|
||||
@@ -2877,6 +2877,7 @@ public sealed class FileStore : IStreamStore, IAsyncDisposable, IDisposable
|
||||
/// background flush loop's deferred sync queue).
|
||||
/// Used by <see cref="FileStore.RotateBlock"/> to avoid synchronous fsync on the hot path.
|
||||
/// </summary>
|
||||
/// <param name="blockId">Identifier of the message block whose pending cache entry should be removed.</param>
|
||||
public void EvictBlockNoSync(int blockId)
|
||||
{
|
||||
_entries.TryRemove(blockId, out _);
|
||||
|
||||
@@ -10,6 +10,11 @@ namespace NATS.Server.Monitoring;
|
||||
/// </summary>
|
||||
public sealed class ConnzHandler(NatsServer server)
|
||||
{
|
||||
/// <summary>
|
||||
/// Handles `/connz` monitor requests, applying filters/sorting/pagination over open and closed connections.
|
||||
/// </summary>
|
||||
/// <param name="ctx">HTTP request context containing query parameters for connection diagnostics.</param>
|
||||
/// <returns>Connection report payload compatible with NATS monitoring clients.</returns>
|
||||
public Connz HandleConnz(HttpContext ctx)
|
||||
{
|
||||
var opts = ParseQueryParams(ctx);
|
||||
|
||||
@@ -9,6 +9,11 @@ namespace NATS.Server.Monitoring;
|
||||
/// </summary>
|
||||
public sealed class SubszHandler(NatsServer server)
|
||||
{
|
||||
/// <summary>
|
||||
/// Handles `/subsz` monitor requests, returning subscription totals and optional per-subscription detail.
|
||||
/// </summary>
|
||||
/// <param name="ctx">HTTP request context containing account/subject/paging filter parameters.</param>
|
||||
/// <returns>Subscription diagnostics payload for operational introspection.</returns>
|
||||
public Subsz HandleSubsz(HttpContext ctx)
|
||||
{
|
||||
var opts = ParseQueryParams(ctx);
|
||||
|
||||
@@ -18,6 +18,10 @@ public sealed class MqttStreamInitializer
|
||||
private volatile bool _initialized;
|
||||
private readonly Lock _initLock = new();
|
||||
|
||||
/// <summary>
|
||||
/// Creates an initializer that provisions account-scoped MQTT persistence streams on demand.
|
||||
/// </summary>
|
||||
/// <param name="streamManager">JetStream manager used to create and query internal MQTT streams.</param>
|
||||
public MqttStreamInitializer(StreamManager streamManager)
|
||||
{
|
||||
_streamManager = streamManager;
|
||||
|
||||
@@ -2,6 +2,13 @@ namespace NATS.Server.Protocol;
|
||||
|
||||
public sealed class ClientCommandMatrix
|
||||
{
|
||||
/// <summary>
|
||||
/// Validates whether a protocol operation is permitted for a given connection kind
|
||||
/// to enforce route/gateway/leaf command boundaries.
|
||||
/// </summary>
|
||||
/// <param name="kind">Kind of connection issuing the command.</param>
|
||||
/// <param name="op">Protocol operation verb (for example RS+, A-, LMSG).</param>
|
||||
/// <returns><see langword="true"/> when the command is allowed for that connection kind.</returns>
|
||||
public bool IsAllowed(global::NATS.Server.ClientKind kind, string? op)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(op))
|
||||
|
||||
@@ -27,6 +27,7 @@ public readonly record struct RaftMembershipChange(RaftMembershipChangeType Type
|
||||
/// Parses a log entry command string back into a membership change.
|
||||
/// Returns null if the command is not a membership change.
|
||||
/// </summary>
|
||||
/// <param name="command">Serialized membership command from a replicated RAFT log entry.</param>
|
||||
public static RaftMembershipChange? TryParse(string command)
|
||||
{
|
||||
var colonIndex = command.IndexOf(':');
|
||||
|
||||
@@ -31,6 +31,7 @@ public sealed class RaftSnapshotCheckpoint
|
||||
/// <summary>
|
||||
/// Adds a chunk of snapshot data for streaming assembly.
|
||||
/// </summary>
|
||||
/// <param name="chunk">One snapshot payload fragment received during InstallSnapshot transfer.</param>
|
||||
public void AddChunk(byte[] chunk) => _chunks.Add(chunk);
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -6,6 +6,11 @@ namespace NATS.Server.Raft;
|
||||
/// </summary>
|
||||
public static class RaftStateExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Returns the canonical state label used in RAFT diagnostics and status output.
|
||||
/// </summary>
|
||||
/// <param name="state">Current RAFT node state.</param>
|
||||
/// <returns>Stable, human-readable state name.</returns>
|
||||
public static string String(this RaftState state) =>
|
||||
state switch
|
||||
{
|
||||
|
||||
@@ -66,7 +66,10 @@ public sealed class SnapshotChunkEnumerator : IEnumerable<byte[]>
|
||||
/// </summary>
|
||||
public int ChunkCount => _data.Length == 0 ? 1 : ((_data.Length + _chunkSize - 1) / _chunkSize);
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>
|
||||
/// Enumerates snapshot chunks in transmission order for InstallSnapshot streaming.
|
||||
/// </summary>
|
||||
/// <returns>Sequence of byte arrays, each no larger than the configured chunk size.</returns>
|
||||
public IEnumerator<byte[]> GetEnumerator()
|
||||
{
|
||||
if (_data.Length == 0)
|
||||
@@ -86,5 +89,6 @@ public sealed class SnapshotChunkEnumerator : IEnumerable<byte[]>
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => GetEnumerator();
|
||||
}
|
||||
|
||||
@@ -8,6 +8,14 @@ public sealed record RemoteSubscription(
|
||||
int QueueWeight = 1,
|
||||
bool IsRemoval = false)
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a tombstone record that propagates remote subscription removal across route links.
|
||||
/// </summary>
|
||||
/// <param name="subject">Subject being removed from remote interest tracking.</param>
|
||||
/// <param name="queue">Optional queue group associated with the removed subscription.</param>
|
||||
/// <param name="routeId">Route identifier where the removal originated.</param>
|
||||
/// <param name="account">Account namespace for the subscription removal.</param>
|
||||
/// <returns>A <see cref="RemoteSubscription"/> marked as a removal operation.</returns>
|
||||
public static RemoteSubscription Removal(string subject, string? queue, string routeId, string account = "$G")
|
||||
=> new(subject, queue, routeId, account, QueueWeight: 1, IsRemoval: true);
|
||||
}
|
||||
|
||||
@@ -2,6 +2,11 @@ namespace NATS.Server.Subscriptions;
|
||||
|
||||
internal readonly record struct RoutedSubKey(string RouteId, string Account, string Subject, string? Queue)
|
||||
{
|
||||
/// <summary>
|
||||
/// Projects a remote subscription announcement into the canonical routed-subscription key shape.
|
||||
/// </summary>
|
||||
/// <param name="sub">Remote subscription metadata received from a route peer.</param>
|
||||
/// <returns>Deduplication key used by routed subscription indexes.</returns>
|
||||
public static RoutedSubKey FromRemoteSubscription(RemoteSubscription sub)
|
||||
=> new(sub.RouteId, sub.Account, sub.Subject, sub.Queue);
|
||||
}
|
||||
|
||||
@@ -12,6 +12,18 @@ public static class TlsConnectionWrapper
|
||||
{
|
||||
private const byte TlsRecordMarker = 0x16;
|
||||
|
||||
/// <summary>
|
||||
/// Negotiates client transport security according to server TLS mode (required, mixed, or TLS-first),
|
||||
/// including INFO preface behavior and optional pinned certificate validation.
|
||||
/// </summary>
|
||||
/// <param name="socket">Accepted client socket.</param>
|
||||
/// <param name="networkStream">Base transport stream over the accepted socket.</param>
|
||||
/// <param name="options">Server TLS and protocol options controlling negotiation mode.</param>
|
||||
/// <param name="sslOptions">TLS server authentication options, or <see langword="null"/> when TLS is disabled.</param>
|
||||
/// <param name="serverInfo">Server INFO payload template to send before/after TLS handshake.</param>
|
||||
/// <param name="logger">Logger used for TLS negotiation diagnostics.</param>
|
||||
/// <param name="ct">Cancellation token for connection startup and handshake operations.</param>
|
||||
/// <returns>Negotiated stream and a flag indicating whether INFO was already written.</returns>
|
||||
public static async Task<(Stream stream, bool infoAlreadySent)> NegotiateAsync(
|
||||
Socket socket,
|
||||
Stream networkStream,
|
||||
|
||||
@@ -15,6 +15,12 @@ public static class WebSocketOptionsValidator
|
||||
"Sec-WebSocket-Protocol",
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Validates websocket listener settings against NATS auth, TLS, and header constraints
|
||||
/// to fail fast on incompatible runtime configuration.
|
||||
/// </summary>
|
||||
/// <param name="options">Server options containing websocket and global auth/TLS configuration.</param>
|
||||
/// <returns>Validation result with error details when the websocket configuration is invalid.</returns>
|
||||
public static WebSocketOptionsValidationResult Validate(NatsOptions options)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(options);
|
||||
|
||||
@@ -61,6 +61,11 @@ public static class WsConstants
|
||||
// Decompression trailer appended before decompressing (RFC 7692 Section 7.2.2)
|
||||
public static readonly byte[] DecompressTrailer = [0x00, 0x00, 0xff, 0xff];
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether an opcode is a WebSocket control frame (close, ping, or pong).
|
||||
/// </summary>
|
||||
/// <param name="opcode">RFC 6455 opcode from the incoming frame header.</param>
|
||||
/// <returns><see langword="true"/> when the frame must follow control-frame size/fragmentation rules.</returns>
|
||||
public static bool IsControlFrame(int opcode) => opcode >= CloseMessage;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user