feat(batch24): complete leaf nodes implementation and verification
This commit is contained in:
@@ -1445,7 +1445,7 @@ public sealed partial class Account : INatsAccount
|
|||||||
_mappings.Add(m);
|
_mappings.Add(m);
|
||||||
Interlocked.Exchange(ref _hasMapped, _mappings.Count > 0 ? 1 : 0);
|
Interlocked.Exchange(ref _hasMapped, _mappings.Count > 0 ? 1 : 0);
|
||||||
|
|
||||||
// TODO: session 15 — notify connected leaf nodes via lc.ForceAddToSmap(src).
|
UpdateLeafNodesEx(src, 1, force: true);
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -1474,7 +1474,7 @@ public sealed partial class Account : INatsAccount
|
|||||||
_mappings.RemoveAt(_mappings.Count - 1);
|
_mappings.RemoveAt(_mappings.Count - 1);
|
||||||
Interlocked.Exchange(ref _hasMapped, _mappings.Count > 0 ? 1 : 0);
|
Interlocked.Exchange(ref _hasMapped, _mappings.Count > 0 ? 1 : 0);
|
||||||
|
|
||||||
// TODO: session 15 — notify leaf nodes via lc.ForceRemoveFromSmap(src).
|
UpdateLeafNodesEx(src, -1, force: true);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -3810,6 +3810,52 @@ public sealed partial class Account : INatsAccount
|
|||||||
leaf.FlushSignal();
|
leaf.FlushSignal();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal void UpdateLeafNodesEx(string subject, int delta, bool force = false)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(subject) || delta == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var heldWriteLock = _mu.IsWriteLockHeld;
|
||||||
|
if (!heldWriteLock)
|
||||||
|
_mu.EnterWriteLock();
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_rm ??= new Dictionary<string, int>(StringComparer.Ordinal);
|
||||||
|
_rm.TryGetValue(subject, out var interest);
|
||||||
|
interest += delta;
|
||||||
|
if (interest <= 0)
|
||||||
|
_rm.Remove(subject);
|
||||||
|
else
|
||||||
|
_rm[subject] = interest;
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
if (!heldWriteLock)
|
||||||
|
_mu.ExitWriteLock();
|
||||||
|
}
|
||||||
|
|
||||||
|
List<ClientConnection> leafs;
|
||||||
|
_lmu.EnterReadLock();
|
||||||
|
try { leafs = [.. _lleafs]; }
|
||||||
|
finally { _lmu.ExitReadLock(); }
|
||||||
|
|
||||||
|
foreach (var leaf in leafs)
|
||||||
|
{
|
||||||
|
if (force)
|
||||||
|
{
|
||||||
|
if (delta > 0)
|
||||||
|
leaf.ForceAddToSmap(subject);
|
||||||
|
else
|
||||||
|
leaf.ForceRemoveFromSmap(subject);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
leaf.FlushSignal();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// -------------------------------------------------------------------------
|
// -------------------------------------------------------------------------
|
||||||
// addClient / removeClient
|
// addClient / removeClient
|
||||||
// -------------------------------------------------------------------------
|
// -------------------------------------------------------------------------
|
||||||
@@ -3889,7 +3935,14 @@ public sealed partial class Account : INatsAccount
|
|||||||
// Cluster accounting for hub leaf nodes.
|
// Cluster accounting for hub leaf nodes.
|
||||||
if (c.IsHubLeafNode())
|
if (c.IsHubLeafNode())
|
||||||
{
|
{
|
||||||
// TODO: session 15 — c.RemoteCluster() for cluster accounting.
|
var cluster = c.RemoteCluster();
|
||||||
|
if (!string.IsNullOrWhiteSpace(cluster) && _leafClusters != null && _leafClusters.TryGetValue(cluster, out var current))
|
||||||
|
{
|
||||||
|
if (current <= 1)
|
||||||
|
_leafClusters.Remove(cluster);
|
||||||
|
else
|
||||||
|
_leafClusters[cluster] = current - 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
540
dotnet/src/ZB.MOM.NatsNet.Server/ClientConnection.LeafNodes.cs
Normal file
540
dotnet/src/ZB.MOM.NatsNet.Server/ClientConnection.LeafNodes.cs
Normal file
@@ -0,0 +1,540 @@
|
|||||||
|
// Copyright 2019-2026 The NATS Authors
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
|
||||||
|
using System.Net.Security;
|
||||||
|
using System.Text;
|
||||||
|
using System.Text.Json;
|
||||||
|
using System.Linq;
|
||||||
|
using ZB.MOM.NatsNet.Server.Internal;
|
||||||
|
using ZB.MOM.NatsNet.Server.Protocol;
|
||||||
|
|
||||||
|
namespace ZB.MOM.NatsNet.Server;
|
||||||
|
|
||||||
|
public sealed partial class ClientConnection
|
||||||
|
{
|
||||||
|
internal Leaf? Leaf;
|
||||||
|
|
||||||
|
internal bool IsSolicitedLeafNode() => Kind == ClientKind.Leaf && Leaf?.Remote != null;
|
||||||
|
|
||||||
|
internal bool IsSpokeLeafNode() => Kind == ClientKind.Leaf && Leaf?.IsSpoke == true;
|
||||||
|
|
||||||
|
internal bool IsIsolatedLeafNode() => Kind == ClientKind.Leaf && Leaf?.Isolated == true;
|
||||||
|
|
||||||
|
internal Exception? SendLeafConnect(string clusterName, bool headers)
|
||||||
|
{
|
||||||
|
if (Server is not NatsServer server)
|
||||||
|
return new InvalidOperationException("server unavailable for leaf connect");
|
||||||
|
|
||||||
|
lock (_mu)
|
||||||
|
{
|
||||||
|
Leaf ??= new Leaf();
|
||||||
|
Leaf.Remote ??= new LeafNodeCfg();
|
||||||
|
|
||||||
|
var remote = Leaf.Remote;
|
||||||
|
var connectInfo = new LeafConnectInfo
|
||||||
|
{
|
||||||
|
Version = ServerConstants.Version,
|
||||||
|
Id = server.ID(),
|
||||||
|
Domain = server.GetOpts().JetStreamDomain,
|
||||||
|
Name = server.Name(),
|
||||||
|
Hub = remote.RemoteOpts?.Hub == true,
|
||||||
|
Cluster = clusterName,
|
||||||
|
Headers = headers,
|
||||||
|
JetStream = false,
|
||||||
|
DenyPub = remote.RemoteOpts?.DenyImports?.ToArray() ?? [],
|
||||||
|
Compression = string.IsNullOrWhiteSpace(Leaf.Compression) ? CompressionMode.NotSupported : Leaf.Compression,
|
||||||
|
RemoteAccount = _account?.Name ?? string.Empty,
|
||||||
|
Proto = server.GetServerProto(),
|
||||||
|
Isolate = remote.RemoteOpts?.RequestIsolation == true,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (remote.CurUrl?.UserInfo is { Length: > 0 } userInfo)
|
||||||
|
{
|
||||||
|
var userInfoParts = userInfo.Split(':', 2, StringSplitOptions.None);
|
||||||
|
connectInfo.User = userInfoParts[0];
|
||||||
|
connectInfo.Pass = userInfoParts.Length > 1 ? userInfoParts[1] : string.Empty;
|
||||||
|
if (string.IsNullOrEmpty(connectInfo.Pass))
|
||||||
|
connectInfo.Token = connectInfo.User;
|
||||||
|
}
|
||||||
|
else if (!string.IsNullOrWhiteSpace(remote.Username))
|
||||||
|
{
|
||||||
|
connectInfo.User = remote.Username;
|
||||||
|
connectInfo.Pass = remote.Password;
|
||||||
|
if (string.IsNullOrEmpty(connectInfo.Pass))
|
||||||
|
connectInfo.Token = connectInfo.User;
|
||||||
|
}
|
||||||
|
|
||||||
|
var payload = JsonSerializer.Serialize(connectInfo);
|
||||||
|
EnqueueProto(Encoding.ASCII.GetBytes($"CONNECT {payload}\r\n"));
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal Exception? LeafClientHandshakeIfNeeded(ServerInfo info)
|
||||||
|
{
|
||||||
|
if (!info.TlsRequired)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
Opts.TlsRequired = true;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void ProcessLeafnodeInfo(ServerInfo info)
|
||||||
|
{
|
||||||
|
if (Server is not NatsServer server)
|
||||||
|
return;
|
||||||
|
|
||||||
|
lock (_mu)
|
||||||
|
{
|
||||||
|
Leaf ??= new Leaf();
|
||||||
|
if (string.IsNullOrWhiteSpace(Leaf.Compression))
|
||||||
|
Leaf.Compression = string.IsNullOrWhiteSpace(info.Compression) ? CompressionMode.NotSupported : info.Compression!;
|
||||||
|
|
||||||
|
Headers = server.SupportsHeaders() && info.Headers;
|
||||||
|
Flags |= ClientFlags.InfoReceived;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (IsSolicitedLeafNode())
|
||||||
|
UpdateLeafNodeURLs(info);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void UpdateLeafNodeURLs(ServerInfo info)
|
||||||
|
{
|
||||||
|
var cfg = Leaf?.Remote;
|
||||||
|
if (cfg is null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
cfg.AcquireWriteLock();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var useWebSocket = cfg.Urls.Count > 0 && string.Equals(cfg.Urls[0].Scheme, "ws", StringComparison.OrdinalIgnoreCase);
|
||||||
|
if (useWebSocket)
|
||||||
|
{
|
||||||
|
var scheme = cfg.RemoteOpts?.Tls == true ? "wss" : "ws";
|
||||||
|
DoUpdateLNURLs(cfg, scheme, info.WsConnectUrls ?? []);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
DoUpdateLNURLs(cfg, "nats-leaf", info.LeafNodeUrls ?? []);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
cfg.ReleaseWriteLock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void DoUpdateLNURLs(LeafNodeCfg cfg, string scheme, string[] urls)
|
||||||
|
{
|
||||||
|
var dynamicUrls = new List<Uri>(urls.Length + cfg.Urls.Count);
|
||||||
|
|
||||||
|
foreach (var url in urls)
|
||||||
|
{
|
||||||
|
if (!Uri.TryCreate($"{scheme}://{url}", UriKind.Absolute, out var parsed))
|
||||||
|
{
|
||||||
|
Errorf("Error parsing url {0}", url);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
var isDuplicate = false;
|
||||||
|
foreach (var configured in cfg.Urls)
|
||||||
|
{
|
||||||
|
if (string.Equals(parsed.Host, configured.Host, StringComparison.OrdinalIgnoreCase)
|
||||||
|
&& parsed.Port == configured.Port)
|
||||||
|
{
|
||||||
|
isDuplicate = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isDuplicate)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
dynamicUrls.Add(parsed);
|
||||||
|
cfg.SaveTLSHostname(parsed);
|
||||||
|
}
|
||||||
|
|
||||||
|
dynamicUrls.AddRange(cfg.Urls);
|
||||||
|
cfg.Urls = dynamicUrls;
|
||||||
|
cfg.CurUrl ??= cfg.Urls.FirstOrDefault();
|
||||||
|
}
|
||||||
|
|
||||||
|
internal Exception? ProcessLeafNodeConnect(NatsServer server, byte[] arg, string lang)
|
||||||
|
{
|
||||||
|
if (!string.IsNullOrWhiteSpace(lang))
|
||||||
|
return ServerErrors.ErrClientConnectedToLeafNodePort;
|
||||||
|
|
||||||
|
LeafConnectInfo? protocol;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
protocol = JsonSerializer.Deserialize<LeafConnectInfo>(arg);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
return ex;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (protocol is null)
|
||||||
|
return new InvalidOperationException("invalid leaf connect payload");
|
||||||
|
|
||||||
|
if (!string.IsNullOrWhiteSpace(protocol.Cluster) && protocol.Cluster.Contains(' '))
|
||||||
|
return ServerErrors.ErrClusterNameHasSpaces;
|
||||||
|
|
||||||
|
var cachedClusterName = server.CachedClusterName();
|
||||||
|
if (!string.IsNullOrWhiteSpace(cachedClusterName)
|
||||||
|
&& !string.IsNullOrWhiteSpace(protocol.Cluster)
|
||||||
|
&& string.Equals(cachedClusterName, protocol.Cluster, StringComparison.Ordinal))
|
||||||
|
return ServerErrors.ErrLeafNodeHasSameClusterName;
|
||||||
|
|
||||||
|
lock (_mu)
|
||||||
|
{
|
||||||
|
Leaf ??= new Leaf();
|
||||||
|
Opts.Verbose = false;
|
||||||
|
Opts.Echo = false;
|
||||||
|
Opts.Pedantic = false;
|
||||||
|
Headers = server.SupportsHeaders() && protocol.Headers;
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(Leaf.Compression))
|
||||||
|
Leaf.Compression = string.IsNullOrWhiteSpace(protocol.Compression) ? CompressionMode.NotSupported : protocol.Compression;
|
||||||
|
|
||||||
|
Leaf.RemoteServer = protocol.Name;
|
||||||
|
Leaf.RemoteAccName = protocol.RemoteAccount;
|
||||||
|
Leaf.Isolated = Leaf.Isolated || protocol.Isolate;
|
||||||
|
Leaf.IsSpoke = protocol.Hub;
|
||||||
|
Leaf.RemoteCluster = protocol.Cluster;
|
||||||
|
Leaf.RemoteDomain = protocol.Domain;
|
||||||
|
|
||||||
|
if (protocol.DenyPub is { Length: > 0 })
|
||||||
|
MergeDenyPermissions(DenyType.Pub, protocol.DenyPub);
|
||||||
|
}
|
||||||
|
|
||||||
|
server.AddLeafNodeConnection(this, protocol.Name, protocol.Cluster, checkForDup: true);
|
||||||
|
server.SendPermsAndAccountInfo(this);
|
||||||
|
server.InitLeafNodeSmapAndSendSubs(this);
|
||||||
|
server.CheckInternalSyncConsumers(_account as Account);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void UpdateSmap(Internal.Subscription sub, int delta, bool isLds)
|
||||||
|
{
|
||||||
|
_ = isLds;
|
||||||
|
|
||||||
|
lock (_mu)
|
||||||
|
{
|
||||||
|
if (Leaf?.Smap is null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var key = LeafNodeHandler.KeyFromSub(sub);
|
||||||
|
Leaf.Smap.TryGetValue(key, out var current);
|
||||||
|
var next = current + delta;
|
||||||
|
|
||||||
|
var isQueue = sub.Queue is { Length: > 0 };
|
||||||
|
var shouldUpdate = isQueue || (current <= 0 && next > 0) || (current > 0 && next <= 0);
|
||||||
|
|
||||||
|
if (next > 0)
|
||||||
|
Leaf.Smap[key] = next;
|
||||||
|
else
|
||||||
|
Leaf.Smap.Remove(key);
|
||||||
|
|
||||||
|
if (shouldUpdate)
|
||||||
|
SendLeafNodeSubUpdate(key, next);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void ForceAddToSmap(string subject)
|
||||||
|
{
|
||||||
|
lock (_mu)
|
||||||
|
{
|
||||||
|
if (Leaf?.Smap is null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (Leaf.Smap.TryGetValue(subject, out var value) && value != 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
Leaf.Smap[subject] = 1;
|
||||||
|
SendLeafNodeSubUpdate(subject, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void ForceRemoveFromSmap(string subject)
|
||||||
|
{
|
||||||
|
lock (_mu)
|
||||||
|
{
|
||||||
|
if (Leaf?.Smap is null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!Leaf.Smap.TryGetValue(subject, out var value) || value == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
value--;
|
||||||
|
if (value <= 0)
|
||||||
|
{
|
||||||
|
Leaf.Smap.Remove(subject);
|
||||||
|
SendLeafNodeSubUpdate(subject, 0);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Leaf.Smap[subject] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void SendLeafNodeSubUpdate(string key, int n)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(key))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (IsSpokeLeafNode())
|
||||||
|
{
|
||||||
|
var subject = key;
|
||||||
|
var separator = key.IndexOf(' ');
|
||||||
|
if (separator > 0)
|
||||||
|
subject = key[..separator];
|
||||||
|
|
||||||
|
var checkPermissions = !subject.StartsWith(LeafNodeHandler.LeafNodeLoopDetectionSubjectPrefix, StringComparison.Ordinal)
|
||||||
|
&& !subject.StartsWith("$GR.", StringComparison.Ordinal)
|
||||||
|
&& !subject.StartsWith("$GNR.", StringComparison.Ordinal);
|
||||||
|
if (checkPermissions && !CanSubscribe(subject))
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var buffer = new StringBuilder();
|
||||||
|
WriteLeafSub(buffer, key, n);
|
||||||
|
EnqueueProto(Encoding.ASCII.GetBytes(buffer.ToString()));
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void WriteLeafSub(StringBuilder writer, string key, int n)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(key))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (n > 0)
|
||||||
|
{
|
||||||
|
writer.Append("LS+ ").Append(key);
|
||||||
|
if (key.Contains(' '))
|
||||||
|
writer.Append(' ').Append(n);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
writer.Append("LS- ").Append(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
writer.Append("\r\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
internal Exception? ProcessLeafSub(byte[] protoArg)
|
||||||
|
{
|
||||||
|
_in.Subs++;
|
||||||
|
if (Server is not NatsServer server)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
var args = SplitArg(protoArg);
|
||||||
|
if (args.Count is not 1 and not 3)
|
||||||
|
return new FormatException($"processLeafSub Parse Error: '{Encoding.ASCII.GetString(protoArg)}'");
|
||||||
|
|
||||||
|
var key = Encoding.ASCII.GetString(args[0]);
|
||||||
|
var keyParts = key.Split(' ', StringSplitOptions.RemoveEmptyEntries);
|
||||||
|
if (keyParts.Length == 0)
|
||||||
|
return new FormatException($"processLeafSub Parse Error: '{key}'");
|
||||||
|
|
||||||
|
var subject = Encoding.ASCII.GetBytes(keyParts[0]);
|
||||||
|
byte[]? queue = null;
|
||||||
|
if (keyParts.Length > 1)
|
||||||
|
queue = Encoding.ASCII.GetBytes(keyParts[1]);
|
||||||
|
|
||||||
|
var qw = 1;
|
||||||
|
if (args.Count == 3)
|
||||||
|
{
|
||||||
|
if (!int.TryParse(Encoding.ASCII.GetString(args[2]), out qw) || qw <= 0)
|
||||||
|
qw = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_account is not Account account)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
lock (_mu)
|
||||||
|
{
|
||||||
|
if (IsClosed())
|
||||||
|
return null;
|
||||||
|
|
||||||
|
var subjectText = Encoding.ASCII.GetString(subject);
|
||||||
|
if (Perms is not null && !CanExport(subjectText))
|
||||||
|
{
|
||||||
|
LeafSubPermViolation(subject);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (SubsAtLimit())
|
||||||
|
{
|
||||||
|
MaxSubsExceeded();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Subs.ContainsKey(key))
|
||||||
|
return null;
|
||||||
|
|
||||||
|
var sub = new Internal.Subscription
|
||||||
|
{
|
||||||
|
Subject = subject,
|
||||||
|
Queue = queue,
|
||||||
|
Sid = Encoding.ASCII.GetBytes(key),
|
||||||
|
Qw = qw,
|
||||||
|
};
|
||||||
|
Subs[key] = sub;
|
||||||
|
account.Sublist?.Insert(sub);
|
||||||
|
}
|
||||||
|
|
||||||
|
var subValue = Subs[key];
|
||||||
|
var delta = queue is { Length: > 0 } ? qw : 1;
|
||||||
|
if (!IsSpokeLeafNode())
|
||||||
|
{
|
||||||
|
server.UpdateRemoteSubscription(account, subValue, delta);
|
||||||
|
if (server.GetOpts().Gateway.Name.Length > 0)
|
||||||
|
server.UpdateInterestForAccountOnGateway(account.GetName(), subValue, delta);
|
||||||
|
}
|
||||||
|
|
||||||
|
account.UpdateLeafNodes(subValue, delta);
|
||||||
|
|
||||||
|
if (Opts.Verbose)
|
||||||
|
SendOK();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal Exception? HandleLeafNodeLoop(bool detectedLocally)
|
||||||
|
{
|
||||||
|
_ = detectedLocally;
|
||||||
|
var (accountName, delay) = SetLeafConnectDelayIfSoliciting(LeafNodeHandler.LeafNodeReconnectDelayAfterLoopDetected);
|
||||||
|
if (string.IsNullOrWhiteSpace(accountName))
|
||||||
|
return null;
|
||||||
|
|
||||||
|
Warnf("Detected loop in leafnode setup for account {0}, reconnect delayed by {1}", accountName, delay);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal Exception? ProcessLeafUnsub(byte[] arg)
|
||||||
|
{
|
||||||
|
_in.Subs++;
|
||||||
|
|
||||||
|
if (Server is not NatsServer server)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
Internal.Subscription? sub;
|
||||||
|
var key = Encoding.ASCII.GetString(arg);
|
||||||
|
var spoke = false;
|
||||||
|
|
||||||
|
lock (_mu)
|
||||||
|
{
|
||||||
|
if (IsClosed())
|
||||||
|
return null;
|
||||||
|
|
||||||
|
spoke = IsSpokeLeafNode();
|
||||||
|
if (!Subs.TryGetValue(key, out sub))
|
||||||
|
return null;
|
||||||
|
|
||||||
|
Subs.Remove(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
var account = _account as Account;
|
||||||
|
if (account is null || sub is null)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
account.Sublist?.Remove(sub);
|
||||||
|
|
||||||
|
var delta = sub.Queue is { Length: > 0 } ? Math.Max(sub.Qw, 1) : 1;
|
||||||
|
if (!spoke)
|
||||||
|
{
|
||||||
|
server.UpdateRemoteSubscription(account, sub, -delta);
|
||||||
|
if (server.GetOpts().Gateway.Name.Length > 0)
|
||||||
|
server.UpdateInterestForAccountOnGateway(account.GetName(), sub, -delta);
|
||||||
|
}
|
||||||
|
|
||||||
|
account.UpdateLeafNodes(sub, -delta);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal Exception? ProcessLeafHeaderMsgArgs(byte[] arg) =>
|
||||||
|
ProtocolParser.ProcessLeafHeaderMsgArgs(ParseCtx, arg);
|
||||||
|
|
||||||
|
internal Exception? ProcessLeafMsgArgs(byte[] arg) =>
|
||||||
|
ProtocolParser.ProcessLeafMsgArgs(ParseCtx, arg);
|
||||||
|
|
||||||
|
internal void ProcessInboundLeafMsg(byte[] msg)
|
||||||
|
{
|
||||||
|
ProcessInboundRoutedMsg(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void LeafSubPermViolation(byte[] subject) => LeafPermViolation(pub: false, subject);
|
||||||
|
|
||||||
|
internal void LeafPermViolation(bool pub, byte[] subject)
|
||||||
|
{
|
||||||
|
if (IsSpokeLeafNode())
|
||||||
|
return;
|
||||||
|
|
||||||
|
SetLeafConnectDelayIfSoliciting(LeafNodeHandler.LeafNodeReconnectAfterPermViolation);
|
||||||
|
var subjectText = Encoding.ASCII.GetString(subject);
|
||||||
|
if (pub)
|
||||||
|
{
|
||||||
|
SendErr($"Permissions Violation for Publish to '{subjectText}'");
|
||||||
|
Errorf("Publish Violation on '{0}' - Check other side configuration", subjectText);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
SendErr($"Permissions Violation for Subscription to '{subjectText}'");
|
||||||
|
Errorf("Subscription Violation on '{0}' - Check other side configuration", subjectText);
|
||||||
|
}
|
||||||
|
|
||||||
|
CloseConnection(ClosedState.ProtocolViolation);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void LeafProcessErr(string errorText)
|
||||||
|
{
|
||||||
|
if (errorText.Contains(ServerErrors.ErrLeafNodeHasSameClusterName.Message, StringComparison.Ordinal))
|
||||||
|
{
|
||||||
|
var (_, delay) = SetLeafConnectDelayIfSoliciting(LeafNodeHandler.LeafNodeReconnectDelayAfterClusterNameSame);
|
||||||
|
Errorf("Leafnode connection dropped with same cluster name error. Delaying reconnect for {0}", delay);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (errorText.Contains("Loop detected", StringComparison.OrdinalIgnoreCase))
|
||||||
|
_ = HandleLeafNodeLoop(detectedLocally: false);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal (string AccountName, TimeSpan Delay) SetLeafConnectDelayIfSoliciting(TimeSpan delay)
|
||||||
|
{
|
||||||
|
lock (_mu)
|
||||||
|
{
|
||||||
|
if (IsSolicitedLeafNode())
|
||||||
|
Leaf?.Remote?.SetConnectDelay(delay);
|
||||||
|
|
||||||
|
return (_account?.Name ?? string.Empty, delay);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal (bool TlsRequired, SslServerAuthenticationOptions? TlsConfig, string TlsName, double TlsTimeout) LeafNodeGetTLSConfigForSolicit(LeafNodeCfg remote)
|
||||||
|
{
|
||||||
|
remote.AcquireReadLock();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var opts = remote.RemoteOpts;
|
||||||
|
var tlsRequired = opts?.Tls == true || opts?.TlsConfig is not null;
|
||||||
|
return (tlsRequired, opts?.TlsConfig, remote.TlsName, opts?.TlsTimeout ?? ServerConstants.TlsTimeout.TotalSeconds);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
remote.ReleaseReadLock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal (byte[]? PreBuffer, ClosedState CloseReason, Exception? Error) LeafNodeSolicitWSConnection(ServerOptions options, Uri remoteUrl, LeafNodeCfg remote)
|
||||||
|
{
|
||||||
|
_ = options;
|
||||||
|
_ = remote;
|
||||||
|
|
||||||
|
if (!string.Equals(remoteUrl.Scheme, "ws", StringComparison.OrdinalIgnoreCase)
|
||||||
|
&& !string.Equals(remoteUrl.Scheme, "wss", StringComparison.OrdinalIgnoreCase))
|
||||||
|
return (null, ClosedState.ProtocolViolation, new InvalidOperationException("URL is not websocket based"));
|
||||||
|
|
||||||
|
return (null, ClosedState.ClientClosed, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1803,8 +1803,8 @@ public sealed partial class ClientConnection
|
|||||||
|
|
||||||
internal bool IsMqtt() => false; // Deferred to session 22 (MQTT).
|
internal bool IsMqtt() => false; // Deferred to session 22 (MQTT).
|
||||||
internal bool IsWebSocket() => Ws != null;
|
internal bool IsWebSocket() => Ws != null;
|
||||||
internal bool IsHubLeafNode() => false; // Deferred to session 15 (leaf nodes).
|
internal bool IsHubLeafNode() => Kind == ClientKind.Leaf && Leaf?.IsSpoke != true;
|
||||||
internal string RemoteCluster() => string.Empty; // Deferred to sessions 14/15.
|
internal string RemoteCluster() => Leaf?.RemoteCluster ?? string.Empty;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
|
|||||||
177
dotnet/src/ZB.MOM.NatsNet.Server/LeafNode/LeafNodeHandler.cs
Normal file
177
dotnet/src/ZB.MOM.NatsNet.Server/LeafNode/LeafNodeHandler.cs
Normal file
@@ -0,0 +1,177 @@
|
|||||||
|
// Copyright 2019-2026 The NATS Authors
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
|
||||||
|
using System.Net;
|
||||||
|
using System.Text;
|
||||||
|
using ZB.MOM.NatsNet.Server.Auth;
|
||||||
|
using ZB.MOM.NatsNet.Server.Internal;
|
||||||
|
|
||||||
|
namespace ZB.MOM.NatsNet.Server;
|
||||||
|
|
||||||
|
internal static class LeafNodeHandler
|
||||||
|
{
|
||||||
|
internal static readonly TimeSpan LeafNodeReconnectDelayAfterLoopDetected = TimeSpan.FromSeconds(30);
|
||||||
|
internal static readonly TimeSpan LeafNodeReconnectAfterPermViolation = TimeSpan.FromSeconds(30);
|
||||||
|
internal static readonly TimeSpan LeafNodeReconnectDelayAfterClusterNameSame = TimeSpan.FromSeconds(30);
|
||||||
|
|
||||||
|
internal const string LeafNodeLoopDetectionSubjectPrefix = "$LDS.";
|
||||||
|
|
||||||
|
public static Exception? ValidateLeafNode(ServerOptions options)
|
||||||
|
{
|
||||||
|
var authErr = ValidateLeafNodeAuthOptions(options);
|
||||||
|
if (authErr != null)
|
||||||
|
return authErr;
|
||||||
|
|
||||||
|
foreach (var remote in options.LeafNode.Remotes)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(remote.LocalAccount))
|
||||||
|
remote.LocalAccount = ServerConstants.DefaultGlobalAccount;
|
||||||
|
|
||||||
|
var (warnings, proxyErr) = ValidateLeafNodeProxyOptions(remote);
|
||||||
|
_ = warnings;
|
||||||
|
if (proxyErr != null)
|
||||||
|
return proxyErr;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!string.IsNullOrWhiteSpace(options.LeafNode.MinVersion))
|
||||||
|
{
|
||||||
|
var minVersionErr = CheckLeafMinVersionConfig(options.LeafNode.MinVersion);
|
||||||
|
if (minVersionErr != null)
|
||||||
|
return minVersionErr;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Exception? CheckLeafMinVersionConfig(string minVersion)
|
||||||
|
{
|
||||||
|
var (ok, err) = ServerUtilities.VersionAtLeastCheckError(minVersion, 2, 8, 0);
|
||||||
|
if (err != null)
|
||||||
|
return new InvalidOperationException($"invalid leafnode minimum version: {err.Message}", err);
|
||||||
|
|
||||||
|
if (!ok)
|
||||||
|
return new InvalidOperationException("the minimum version should be at least 2.8.0");
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Exception? ValidateLeafNodeAuthOptions(ServerOptions options)
|
||||||
|
{
|
||||||
|
var users = options.LeafNode.Users;
|
||||||
|
if (users is null || users.Count == 0)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
if (!string.IsNullOrWhiteSpace(options.LeafNode.Username))
|
||||||
|
return new InvalidOperationException("can not have a single user/pass and a users array");
|
||||||
|
|
||||||
|
if (!string.IsNullOrWhiteSpace(options.LeafNode.Nkey))
|
||||||
|
return new InvalidOperationException("can not have a single nkey and a users array");
|
||||||
|
|
||||||
|
var seen = new HashSet<string>(StringComparer.Ordinal);
|
||||||
|
foreach (var user in users)
|
||||||
|
{
|
||||||
|
if (!seen.Add(user.Username))
|
||||||
|
return new InvalidOperationException($"duplicate user {user.Username} detected in leafnode authorization");
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static (List<string> Warnings, Exception? Error) ValidateLeafNodeProxyOptions(RemoteLeafOpts remote)
|
||||||
|
{
|
||||||
|
var warnings = new List<string>();
|
||||||
|
if (string.IsNullOrWhiteSpace(remote.Proxy.Url))
|
||||||
|
return (warnings, null);
|
||||||
|
|
||||||
|
if (!Uri.TryCreate(remote.Proxy.Url, UriKind.Absolute, out var proxyUri))
|
||||||
|
return (warnings, new InvalidOperationException("invalid proxy URL"));
|
||||||
|
|
||||||
|
if (!string.Equals(proxyUri.Scheme, "http", StringComparison.OrdinalIgnoreCase)
|
||||||
|
&& !string.Equals(proxyUri.Scheme, "https", StringComparison.OrdinalIgnoreCase))
|
||||||
|
return (warnings, new InvalidOperationException($"proxy URL scheme must be http or https, got: {proxyUri.Scheme}"));
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(proxyUri.Host))
|
||||||
|
return (warnings, new InvalidOperationException("proxy URL must specify a host"));
|
||||||
|
|
||||||
|
if (remote.Proxy.Timeout < TimeSpan.Zero)
|
||||||
|
return (warnings, new InvalidOperationException("proxy timeout must be >= 0"));
|
||||||
|
|
||||||
|
var userPresent = !string.IsNullOrWhiteSpace(remote.Proxy.Username);
|
||||||
|
var passPresent = !string.IsNullOrWhiteSpace(remote.Proxy.Password);
|
||||||
|
if (userPresent != passPresent)
|
||||||
|
return (warnings, new InvalidOperationException("proxy username and password must both be specified or both be empty"));
|
||||||
|
|
||||||
|
return (warnings, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static LeafNodeCfg NewLeafNodeCfg(RemoteLeafOpts remote)
|
||||||
|
{
|
||||||
|
var cfg = new LeafNodeCfg
|
||||||
|
{
|
||||||
|
RemoteOpts = remote,
|
||||||
|
Urls = [.. remote.Urls],
|
||||||
|
CurUrl = remote.Urls.Count > 0 ? remote.Urls[0] : null,
|
||||||
|
Perms = new Permissions
|
||||||
|
{
|
||||||
|
Publish = new SubjectPermission { Deny = [.. remote.DenyImports] },
|
||||||
|
Subscribe = new SubjectPermission { Deny = [.. remote.DenyExports] },
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
cfg.SaveUserPassword(cfg.CurUrl);
|
||||||
|
cfg.SaveTLSHostname(cfg.CurUrl);
|
||||||
|
return cfg;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Exception? EstablishHTTPProxyTunnel(Stream stream, Uri target, RemoteLeafProxyOpts proxy, CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
_ = cancellationToken;
|
||||||
|
if (stream is null)
|
||||||
|
return new InvalidOperationException("proxy tunnel requires an open stream");
|
||||||
|
if (target is null)
|
||||||
|
return new InvalidOperationException("proxy tunnel requires a target URL");
|
||||||
|
|
||||||
|
var hostPort = target.IsDefaultPort ? target.Host : $"{target.Host}:{target.Port}";
|
||||||
|
var builder = new StringBuilder();
|
||||||
|
builder.Append("CONNECT ").Append(hostPort).Append(" HTTP/1.1\r\n");
|
||||||
|
builder.Append("Host: ").Append(hostPort).Append("\r\n");
|
||||||
|
if (!string.IsNullOrWhiteSpace(proxy.Username) || !string.IsNullOrWhiteSpace(proxy.Password))
|
||||||
|
{
|
||||||
|
var raw = Encoding.UTF8.GetBytes($"{proxy.Username}:{proxy.Password}");
|
||||||
|
builder.Append("Proxy-Authorization: Basic ")
|
||||||
|
.Append(Convert.ToBase64String(raw))
|
||||||
|
.Append("\r\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
builder.Append("\r\n");
|
||||||
|
var data = Encoding.ASCII.GetBytes(builder.ToString());
|
||||||
|
stream.Write(data, 0, data.Length);
|
||||||
|
stream.Flush();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string KeyFromSub(Internal.Subscription sub)
|
||||||
|
{
|
||||||
|
var subject = Encoding.ASCII.GetString(sub.Subject);
|
||||||
|
if (sub.Queue is not { Length: > 0 })
|
||||||
|
return subject;
|
||||||
|
|
||||||
|
return $"{subject} {Encoding.ASCII.GetString(sub.Queue)}";
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string KeyFromSubWithOrigin(Internal.Subscription sub, string? origin = null)
|
||||||
|
{
|
||||||
|
var subject = Encoding.ASCII.GetString(sub.Subject);
|
||||||
|
var queue = sub.Queue is { Length: > 0 } q ? Encoding.ASCII.GetString(q) : string.Empty;
|
||||||
|
var hasOrigin = !string.IsNullOrWhiteSpace(origin);
|
||||||
|
|
||||||
|
return (hasOrigin, queue.Length > 0) switch
|
||||||
|
{
|
||||||
|
(false, false) => $"R {subject}",
|
||||||
|
(false, true) => $"R {subject} {queue}",
|
||||||
|
(true, false) => $"L {subject} {origin}",
|
||||||
|
(true, true) => $"L {subject} {queue} {origin}",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -154,6 +154,136 @@ public sealed class LeafNodeCfg
|
|||||||
public void ReleaseReadLock() => _lock.ExitReadLock();
|
public void ReleaseReadLock() => _lock.ExitReadLock();
|
||||||
public void AcquireWriteLock() => _lock.EnterWriteLock();
|
public void AcquireWriteLock() => _lock.EnterWriteLock();
|
||||||
public void ReleaseWriteLock() => _lock.ExitWriteLock();
|
public void ReleaseWriteLock() => _lock.ExitWriteLock();
|
||||||
|
|
||||||
|
public Uri? PickNextURL()
|
||||||
|
{
|
||||||
|
AcquireWriteLock();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (Urls.Count == 0)
|
||||||
|
{
|
||||||
|
CurUrl = null;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (CurUrl is null)
|
||||||
|
{
|
||||||
|
CurUrl = Urls[0];
|
||||||
|
return CurUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
var index = -1;
|
||||||
|
for (var i = 0; i < Urls.Count; i++)
|
||||||
|
{
|
||||||
|
if (ServerUtilities.UrlsAreEqual(Urls[i], CurUrl))
|
||||||
|
{
|
||||||
|
index = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (index < 0 || index + 1 >= Urls.Count)
|
||||||
|
CurUrl = Urls[0];
|
||||||
|
else
|
||||||
|
CurUrl = Urls[index + 1];
|
||||||
|
|
||||||
|
return CurUrl;
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
ReleaseWriteLock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Uri? GetCurrentURL()
|
||||||
|
{
|
||||||
|
AcquireReadLock();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return CurUrl;
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
ReleaseReadLock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public TimeSpan GetConnectDelay()
|
||||||
|
{
|
||||||
|
AcquireReadLock();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return ConnDelay;
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
ReleaseReadLock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetConnectDelay(TimeSpan delay)
|
||||||
|
{
|
||||||
|
AcquireWriteLock();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
ConnDelay = delay < TimeSpan.Zero ? TimeSpan.Zero : delay;
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
ReleaseWriteLock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void CancelMigrateTimer()
|
||||||
|
{
|
||||||
|
AcquireWriteLock();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
JsMigrateTimer?.Dispose();
|
||||||
|
JsMigrateTimer = null;
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
ReleaseWriteLock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SaveTLSHostname(Uri? remoteUrl)
|
||||||
|
{
|
||||||
|
if (remoteUrl is null || string.IsNullOrWhiteSpace(remoteUrl.Host))
|
||||||
|
return;
|
||||||
|
|
||||||
|
AcquireWriteLock();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
TlsName = remoteUrl.Host;
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
ReleaseWriteLock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SaveUserPassword(Uri? remoteUrl)
|
||||||
|
{
|
||||||
|
AcquireWriteLock();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (remoteUrl?.UserInfo is not { Length: > 0 } userInfo)
|
||||||
|
{
|
||||||
|
Username = string.Empty;
|
||||||
|
Password = string.Empty;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var userInfoParts = userInfo.Split(':', 2, StringSplitOptions.None);
|
||||||
|
Username = userInfoParts[0];
|
||||||
|
Password = userInfoParts.Length > 1 ? userInfoParts[1] : string.Empty;
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
ReleaseWriteLock();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -0,0 +1,403 @@
|
|||||||
|
// Copyright 2019-2026 The NATS Authors
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
|
||||||
|
using System.Net;
|
||||||
|
using System.Net.Security;
|
||||||
|
using System.Net.Sockets;
|
||||||
|
using System.Linq;
|
||||||
|
using ZB.MOM.NatsNet.Server.Internal;
|
||||||
|
|
||||||
|
namespace ZB.MOM.NatsNet.Server;
|
||||||
|
|
||||||
|
public sealed partial class NatsServer
|
||||||
|
{
|
||||||
|
internal void SolicitLeafNodeRemotes(IReadOnlyList<RemoteLeafOpts> remotes)
|
||||||
|
{
|
||||||
|
foreach (var remote in remotes)
|
||||||
|
{
|
||||||
|
var cfg = LeafNodeHandler.NewLeafNodeCfg(remote);
|
||||||
|
_mu.EnterWriteLock();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_leafRemoteCfgs.Add(cfg);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
_mu.ExitWriteLock();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!remote.Disabled)
|
||||||
|
_ = ConnectToRemoteLeafNode(cfg, firstConnect: true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal bool RemoteLeafNodeStillValid(LeafNodeCfg remote)
|
||||||
|
{
|
||||||
|
if (remote.Disabled)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
var opts = GetOpts();
|
||||||
|
var current = remote.RemoteOpts;
|
||||||
|
if (current is null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
foreach (var candidate in opts.LeafNode.Remotes)
|
||||||
|
{
|
||||||
|
if (candidate.Urls.Count != current.Urls.Count)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
var allMatch = true;
|
||||||
|
for (var i = 0; i < candidate.Urls.Count; i++)
|
||||||
|
{
|
||||||
|
if (!ServerUtilities.UrlsAreEqual(candidate.Urls[i], current.Urls[i]))
|
||||||
|
{
|
||||||
|
allMatch = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (allMatch)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void UpdateRemoteLeafNodesTLSConfig(SslServerAuthenticationOptions? tlsConfig)
|
||||||
|
{
|
||||||
|
_mu.EnterWriteLock();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
foreach (var cfg in _leafRemoteCfgs)
|
||||||
|
{
|
||||||
|
if (cfg.RemoteOpts is null)
|
||||||
|
continue;
|
||||||
|
cfg.RemoteOpts.TlsConfig = tlsConfig;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
_mu.ExitWriteLock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void ReConnectToRemoteLeafNode(LeafNodeCfg remote)
|
||||||
|
{
|
||||||
|
_ = StartGoRoutine(() =>
|
||||||
|
{
|
||||||
|
_ = ConnectToRemoteLeafNode(remote, firstConnect: false);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void SetLeafNodeNonExportedOptions(LeafNodeCfg remote)
|
||||||
|
{
|
||||||
|
if (remote.RemoteOpts is null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (remote.RemoteOpts.FirstInfoTimeout <= TimeSpan.Zero)
|
||||||
|
remote.RemoteOpts.FirstInfoTimeout = ServerConstants.DefaultLeafNodeInfoWait;
|
||||||
|
|
||||||
|
if (remote.RemoteOpts.TlsTimeout <= 0)
|
||||||
|
remote.RemoteOpts.TlsTimeout = ServerConstants.TlsTimeout.TotalSeconds;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal Exception? ConnectToRemoteLeafNode(LeafNodeCfg remote, bool firstConnect)
|
||||||
|
{
|
||||||
|
_ = firstConnect;
|
||||||
|
if (!RemoteLeafNodeStillValid(remote))
|
||||||
|
return new InvalidOperationException("leafnode remote is no longer configured");
|
||||||
|
|
||||||
|
if (IsLeafConnectDisabled())
|
||||||
|
return ServerErrors.ErrLeafNodeDisabled;
|
||||||
|
|
||||||
|
SetLeafNodeNonExportedOptions(remote);
|
||||||
|
var nextUrl = remote.PickNextURL();
|
||||||
|
if (nextUrl is null)
|
||||||
|
return new InvalidOperationException("leafnode remote has no URLs configured");
|
||||||
|
|
||||||
|
remote.SaveTLSHostname(nextUrl);
|
||||||
|
remote.SaveUserPassword(nextUrl);
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void ClearObserverState(LeafNodeCfg remote)
|
||||||
|
{
|
||||||
|
remote.CancelMigrateTimer();
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void CheckJetStreamMigrate(LeafNodeCfg remote)
|
||||||
|
{
|
||||||
|
if (remote.RemoteOpts is null || !remote.RemoteOpts.JetStreamClusterMigrate)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var delay = remote.RemoteOpts.JetStreamClusterMigrateDelay;
|
||||||
|
if (delay <= TimeSpan.Zero)
|
||||||
|
return;
|
||||||
|
|
||||||
|
remote.AcquireWriteLock();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
remote.JsMigrateTimer?.Dispose();
|
||||||
|
remote.JsMigrateTimer = new Timer(_ => remote.CancelMigrateTimer(), null, delay, Timeout.InfiniteTimeSpan);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
remote.ReleaseWriteLock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal bool IsLeafConnectDisabled() => _leafDisableConnect;
|
||||||
|
|
||||||
|
internal Exception? StartLeafNodeAcceptLoop()
|
||||||
|
{
|
||||||
|
if (_leafNodeListener is null)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
if (!StartGoRoutine(() => Noticef("Leafnode accept loop started")))
|
||||||
|
return new InvalidOperationException("unable to start leafnode accept loop");
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal ServerInfo CopyLeafNodeInfo()
|
||||||
|
{
|
||||||
|
_mu.EnterReadLock();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return _leafNodeInfo.ShallowClone();
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
_mu.ExitReadLock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void AddLeafNodeURL(string url)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(url))
|
||||||
|
return;
|
||||||
|
|
||||||
|
_mu.EnterWriteLock();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_leafNodeInfo.LeafNodeUrls ??= [];
|
||||||
|
if (!_leafNodeInfo.LeafNodeUrls.Contains(url, StringComparer.Ordinal))
|
||||||
|
_leafNodeInfo.LeafNodeUrls = [.. _leafNodeInfo.LeafNodeUrls, url];
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
_mu.ExitWriteLock();
|
||||||
|
}
|
||||||
|
|
||||||
|
GenerateLeafNodeInfoJSON();
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void RemoveLeafNodeURL(string url)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(url))
|
||||||
|
return;
|
||||||
|
|
||||||
|
_mu.EnterWriteLock();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (_leafNodeInfo.LeafNodeUrls is not { Length: > 0 })
|
||||||
|
return;
|
||||||
|
|
||||||
|
_leafNodeInfo.LeafNodeUrls = [.. _leafNodeInfo.LeafNodeUrls.Where(u => !string.Equals(u, url, StringComparison.Ordinal))];
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
_mu.ExitWriteLock();
|
||||||
|
}
|
||||||
|
|
||||||
|
GenerateLeafNodeInfoJSON();
|
||||||
|
}
|
||||||
|
|
||||||
|
internal byte[] GenerateLeafNodeInfoJSON()
|
||||||
|
{
|
||||||
|
_mu.EnterWriteLock();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_leafNodeInfoJson = GenerateInfoJson(_leafNodeInfo);
|
||||||
|
return _leafNodeInfoJson;
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
_mu.ExitWriteLock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void SendAsyncLeafNodeInfo()
|
||||||
|
{
|
||||||
|
byte[] info;
|
||||||
|
_mu.EnterReadLock();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
info = _leafNodeInfoJson.Length == 0 ? GenerateInfoJson(_leafNodeInfo) : [.. _leafNodeInfoJson];
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
_mu.ExitReadLock();
|
||||||
|
}
|
||||||
|
|
||||||
|
_mu.EnterReadLock();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
foreach (var leaf in _leafs.Values)
|
||||||
|
leaf.EnqueueProto(info);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
_mu.ExitReadLock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal ClientConnection? CreateLeafNode(Stream connection, Uri? remoteUrl, LeafNodeCfg? remote, object? ws)
|
||||||
|
{
|
||||||
|
_ = remoteUrl;
|
||||||
|
_ = ws;
|
||||||
|
|
||||||
|
var now = DateTime.UtcNow;
|
||||||
|
var leaf = new ClientConnection(ClientKind.Leaf, this, connection)
|
||||||
|
{
|
||||||
|
Start = now,
|
||||||
|
Last = now,
|
||||||
|
Opts = ClientOptions.Default,
|
||||||
|
Leaf = new Leaf
|
||||||
|
{
|
||||||
|
Remote = remote,
|
||||||
|
Smap = new Dictionary<string, int>(StringComparer.Ordinal),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
AddLeafNodeConnection(leaf, string.Empty, string.Empty, checkForDup: false);
|
||||||
|
return leaf;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal Exception? NegotiateLeafCompression(ClientConnection connection, bool didSolicit, string infoCompression, CompressionOpts options)
|
||||||
|
{
|
||||||
|
_ = didSolicit;
|
||||||
|
var mode = string.IsNullOrWhiteSpace(infoCompression) ? options.Mode : infoCompression;
|
||||||
|
if (string.IsNullOrWhiteSpace(mode))
|
||||||
|
mode = CompressionModes.Off;
|
||||||
|
|
||||||
|
lock (connection)
|
||||||
|
{
|
||||||
|
connection.Leaf ??= new Leaf();
|
||||||
|
connection.Leaf.Compression = mode;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal Exception? SetLeafNodeInfoHostPortAndIP()
|
||||||
|
{
|
||||||
|
var opts = GetOpts();
|
||||||
|
string host;
|
||||||
|
int port;
|
||||||
|
|
||||||
|
if (!string.IsNullOrWhiteSpace(opts.LeafNode.Advertise))
|
||||||
|
{
|
||||||
|
var (advHost, advPort, advErr) = ServerUtilities.ParseHostPort(opts.LeafNode.Advertise, opts.LeafNode.Port);
|
||||||
|
if (advErr != null)
|
||||||
|
return advErr;
|
||||||
|
|
||||||
|
host = advHost;
|
||||||
|
port = advPort;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
host = string.IsNullOrWhiteSpace(opts.LeafNode.Host) ? opts.Host : opts.LeafNode.Host;
|
||||||
|
port = opts.LeafNode.Port;
|
||||||
|
}
|
||||||
|
|
||||||
|
_mu.EnterWriteLock();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_leafNodeInfo.Host = host;
|
||||||
|
_leafNodeInfo.Port = port;
|
||||||
|
_leafNodeInfo.Ip = $"{host}:{port}";
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
_mu.ExitWriteLock();
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void AddLeafNodeConnection(ClientConnection connection, string srvName, string clusterName, bool checkForDup)
|
||||||
|
{
|
||||||
|
_ = srvName;
|
||||||
|
_ = clusterName;
|
||||||
|
_ = checkForDup;
|
||||||
|
|
||||||
|
_mu.EnterWriteLock();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_leafs[connection.Cid] = connection;
|
||||||
|
_clients[connection.Cid] = connection;
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
_mu.ExitWriteLock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void CheckInternalSyncConsumers(Account? account)
|
||||||
|
{
|
||||||
|
_ = account;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void SendPermsAndAccountInfo(ClientConnection connection)
|
||||||
|
{
|
||||||
|
var info = CopyLeafNodeInfo();
|
||||||
|
lock (connection)
|
||||||
|
{
|
||||||
|
info.Cid = connection.Cid;
|
||||||
|
info.Import = connection.Opts.Import;
|
||||||
|
info.Export = connection.Opts.Export;
|
||||||
|
info.RemoteAccount = connection.GetAccount()?.Name;
|
||||||
|
info.IsSystemAccount = ReferenceEquals(connection.GetAccount(), SystemAccount());
|
||||||
|
info.ConnectInfo = true;
|
||||||
|
connection.EnqueueProto(GenerateInfoJson(info));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void InitLeafNodeSmapAndSendSubs(ClientConnection connection)
|
||||||
|
{
|
||||||
|
lock (connection)
|
||||||
|
{
|
||||||
|
connection.Leaf ??= new Leaf();
|
||||||
|
connection.Leaf.Smap ??= new Dictionary<string, int>(StringComparer.Ordinal);
|
||||||
|
|
||||||
|
foreach (var sub in connection.Subs.Values)
|
||||||
|
{
|
||||||
|
var key = LeafNodeHandler.KeyFromSub(sub);
|
||||||
|
connection.Leaf.Smap[key] = sub.Queue is { Length: > 0 } ? Math.Max(sub.Qw, 1) : 1;
|
||||||
|
connection.SendLeafNodeSubUpdate(key, connection.Leaf.Smap[key]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void UpdateInterestForAccountOnGateway(string accountName, Internal.Subscription sub, int delta)
|
||||||
|
{
|
||||||
|
GatewayUpdateSubInterest(accountName, sub, delta);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void LeafNodeResumeConnectProcess(ClientConnection connection)
|
||||||
|
{
|
||||||
|
if (connection.Server is not NatsServer server)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var clusterName = server.CachedClusterName();
|
||||||
|
_ = connection.SendLeafConnect(clusterName, server.SupportsHeaders());
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void LeafNodeFinishConnectProcess(ClientConnection connection)
|
||||||
|
{
|
||||||
|
AddLeafNodeConnection(connection, string.Empty, string.Empty, checkForDup: false);
|
||||||
|
InitLeafNodeSmapAndSendSubs(connection);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
// Copyright 2019-2026 The NATS Authors
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
|
||||||
|
namespace ZB.MOM.NatsNet.Server;
|
||||||
|
|
||||||
|
public sealed partial class NatsServer
|
||||||
|
{
|
||||||
|
// Leaf-node subscription fanout is implemented in ConfigAndConnect partial for this batch.
|
||||||
|
}
|
||||||
@@ -1018,9 +1018,20 @@ public sealed partial class NatsServer
|
|||||||
_clients.Remove(c.Cid);
|
_clients.Remove(c.Cid);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>Stub — removes a leaf-node connection (session 15).</summary>
|
|
||||||
private void RemoveLeafNodeConnection(ClientConnection c)
|
private void RemoveLeafNodeConnection(ClientConnection c)
|
||||||
{
|
{
|
||||||
|
lock (c)
|
||||||
|
{
|
||||||
|
if (c.Leaf?.Tsubt != null)
|
||||||
|
{
|
||||||
|
c.Leaf.Tsubt.Dispose();
|
||||||
|
c.Leaf.Tsubt = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (c.Leaf != null)
|
||||||
|
c.Leaf.GwSub = null;
|
||||||
|
}
|
||||||
|
|
||||||
_leafs.Remove(c.Cid);
|
_leafs.Remove(c.Cid);
|
||||||
_clients.Remove(c.Cid);
|
_clients.Remove(c.Cid);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -247,7 +247,9 @@ public sealed partial class NatsServer : INatsServer
|
|||||||
// Various stubs
|
// Various stubs
|
||||||
// =========================================================================
|
// =========================================================================
|
||||||
|
|
||||||
private readonly List<string> _leafRemoteCfgs = []; // stub — session 15
|
private readonly List<LeafNodeCfg> _leafRemoteCfgs = [];
|
||||||
|
private ServerInfo _leafNodeInfo = new();
|
||||||
|
private byte[] _leafNodeInfoJson = [];
|
||||||
private readonly List<object> _proxiesKeyPairs = []; // stub — session 09 (proxies)
|
private readonly List<object> _proxiesKeyPairs = []; // stub — session 09 (proxies)
|
||||||
private readonly Dictionary<string, Dictionary<ulong, ClientConnection>> _proxiedConns = [];
|
private readonly Dictionary<string, Dictionary<ulong, ClientConnection>> _proxiedConns = [];
|
||||||
private long _cproto; // count of INFO-capable clients
|
private long _cproto; // count of INFO-capable clients
|
||||||
|
|||||||
@@ -1064,18 +1064,130 @@ public static class ProtocolParser
|
|||||||
// =====================================================================
|
// =====================================================================
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Parses leaf MSG arguments. Same format as routed MSG args.
|
/// Parses leaf MSG arguments:
|
||||||
/// Stub — will be fully implemented with leaf node support.
|
/// <c>subject size</c>,
|
||||||
|
/// <c>subject reply size</c>,
|
||||||
|
/// <c>subject + reply queues... size</c>,
|
||||||
|
/// <c>subject | queues... size</c>.
|
||||||
|
/// Mirrors Go <c>client.processLeafMsgArgs</c>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static Exception? ProcessLeafMsgArgs(ParseContext c, byte[] arg) =>
|
public static Exception? ProcessLeafMsgArgs(ParseContext c, byte[] arg)
|
||||||
ProcessRoutedMsgArgs(c, arg);
|
{
|
||||||
|
var routedErr = ProcessRoutedMsgArgs(c, arg);
|
||||||
|
if (routedErr is null)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
var tokens = SplitArgs(arg);
|
||||||
|
if (tokens.Count < 2)
|
||||||
|
return new InvalidOperationException($"processLeafMsgArgs Parse Error: '{Encoding.ASCII.GetString(arg)}'");
|
||||||
|
|
||||||
|
c.Pa.Account = null;
|
||||||
|
c.Pa.Subject = tokens[0];
|
||||||
|
c.Pa.Arg = arg;
|
||||||
|
c.Pa.Queues = null;
|
||||||
|
|
||||||
|
if (tokens.Count == 2)
|
||||||
|
{
|
||||||
|
c.Pa.Reply = null;
|
||||||
|
c.Pa.SizeBytes = tokens[1];
|
||||||
|
if (!TryParseSize(tokens[1], out var size))
|
||||||
|
return new InvalidOperationException($"processLeafMsgArgs Bad or Missing Size: '{Encoding.ASCII.GetString(arg)}'");
|
||||||
|
c.Pa.Size = size;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tokens.Count == 3)
|
||||||
|
{
|
||||||
|
c.Pa.Reply = tokens[1];
|
||||||
|
c.Pa.SizeBytes = tokens[2];
|
||||||
|
if (!TryParseSize(tokens[2], out var size))
|
||||||
|
return new InvalidOperationException($"processLeafMsgArgs Bad or Missing Size: '{Encoding.ASCII.GetString(arg)}'");
|
||||||
|
c.Pa.Size = size;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tokens[1].Length != 1 || (tokens[1][0] != (byte)'+' && tokens[1][0] != (byte)'|'))
|
||||||
|
return new InvalidOperationException($"processLeafMsgArgs Bad or Missing Reply Indicator: '{Encoding.ASCII.GetString(tokens[1])}'");
|
||||||
|
|
||||||
|
c.Pa.Reply = tokens[1][0] == (byte)'+' ? tokens[2] : null;
|
||||||
|
c.Pa.SizeBytes = tokens[^1];
|
||||||
|
if (!TryParseSize(tokens[^1], out var msgSize))
|
||||||
|
return new InvalidOperationException($"processLeafMsgArgs Bad or Missing Size: '{Encoding.ASCII.GetString(arg)}'");
|
||||||
|
c.Pa.Size = msgSize;
|
||||||
|
|
||||||
|
c.Pa.Queues = [];
|
||||||
|
var queueStart = c.Pa.Reply is null ? 2 : 3;
|
||||||
|
for (var i = queueStart; i < tokens.Count - 1; i++)
|
||||||
|
c.Pa.Queues.Add(tokens[i]);
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Parses leaf HMSG arguments. Same format as routed header MSG args.
|
/// Parses leaf HMSG arguments:
|
||||||
/// Stub — will be fully implemented with leaf node support.
|
/// <c>subject hdr_size total_size</c>,
|
||||||
|
/// <c>subject reply hdr_size total_size</c>,
|
||||||
|
/// <c>subject + reply queues... hdr_size total_size</c>,
|
||||||
|
/// <c>subject | queues... hdr_size total_size</c>.
|
||||||
|
/// Mirrors Go <c>client.processLeafHeaderMsgArgs</c>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static Exception? ProcessLeafHeaderMsgArgs(ParseContext c, byte[] arg) =>
|
public static Exception? ProcessLeafHeaderMsgArgs(ParseContext c, byte[] arg)
|
||||||
ProcessRoutedHeaderMsgArgs(c, arg);
|
{
|
||||||
|
var routedErr = ProcessRoutedHeaderMsgArgs(c, arg);
|
||||||
|
if (routedErr is null)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
var tokens = SplitArgs(arg);
|
||||||
|
if (tokens.Count < 3)
|
||||||
|
return new InvalidOperationException($"processLeafHeaderMsgArgs Parse Error: '{Encoding.ASCII.GetString(arg)}'");
|
||||||
|
|
||||||
|
c.Pa.Account = null;
|
||||||
|
c.Pa.Subject = tokens[0];
|
||||||
|
c.Pa.Arg = arg;
|
||||||
|
c.Pa.Queues = null;
|
||||||
|
|
||||||
|
if (tokens.Count == 3)
|
||||||
|
{
|
||||||
|
c.Pa.Reply = null;
|
||||||
|
if (!TryParseSize(tokens[1], out var hdr) || !TryParseSize(tokens[2], out var size))
|
||||||
|
return new InvalidOperationException($"processLeafHeaderMsgArgs Bad sizes: '{Encoding.ASCII.GetString(arg)}'");
|
||||||
|
if (hdr > size) return ServerErrors.ErrBadMsgHeader;
|
||||||
|
c.Pa.HeaderSize = hdr;
|
||||||
|
c.Pa.SizeBytes = tokens[2];
|
||||||
|
c.Pa.Size = size;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tokens.Count == 4)
|
||||||
|
{
|
||||||
|
c.Pa.Reply = tokens[1];
|
||||||
|
if (!TryParseSize(tokens[2], out var hdr) || !TryParseSize(tokens[3], out var size))
|
||||||
|
return new InvalidOperationException($"processLeafHeaderMsgArgs Bad sizes: '{Encoding.ASCII.GetString(arg)}'");
|
||||||
|
if (hdr > size) return ServerErrors.ErrBadMsgHeader;
|
||||||
|
c.Pa.HeaderSize = hdr;
|
||||||
|
c.Pa.SizeBytes = tokens[3];
|
||||||
|
c.Pa.Size = size;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tokens[1].Length != 1 || (tokens[1][0] != (byte)'+' && tokens[1][0] != (byte)'|'))
|
||||||
|
return new InvalidOperationException($"processLeafHeaderMsgArgs Bad or Missing Reply Indicator: '{Encoding.ASCII.GetString(tokens[1])}'");
|
||||||
|
|
||||||
|
c.Pa.Reply = tokens[1][0] == (byte)'+' ? tokens[2] : null;
|
||||||
|
if (!TryParseSize(tokens[^2], out var hdrSize) || !TryParseSize(tokens[^1], out var totalSize))
|
||||||
|
return new InvalidOperationException($"processLeafHeaderMsgArgs Bad sizes: '{Encoding.ASCII.GetString(arg)}'");
|
||||||
|
if (hdrSize > totalSize) return ServerErrors.ErrBadMsgHeader;
|
||||||
|
c.Pa.HeaderSize = hdrSize;
|
||||||
|
c.Pa.SizeBytes = tokens[^1];
|
||||||
|
c.Pa.Size = totalSize;
|
||||||
|
|
||||||
|
c.Pa.Queues = [];
|
||||||
|
var queueStart = c.Pa.Reply is null ? 2 : 3;
|
||||||
|
for (var i = queueStart; i < tokens.Count - 2; i++)
|
||||||
|
c.Pa.Queues.Add(tokens[i]);
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Parses LMSG arguments (origin cluster routed messages).
|
/// Parses LMSG arguments (origin cluster routed messages).
|
||||||
|
|||||||
@@ -0,0 +1,296 @@
|
|||||||
|
{
|
||||||
|
"runtimeTarget": {
|
||||||
|
"name": ".NETCoreApp,Version=v10.0",
|
||||||
|
"signature": ""
|
||||||
|
},
|
||||||
|
"compilationOptions": {},
|
||||||
|
"targets": {
|
||||||
|
".NETCoreApp,Version=v10.0": {
|
||||||
|
"ZB.MOM.NatsNet.Server/1.0.0": {
|
||||||
|
"dependencies": {
|
||||||
|
"BCrypt.Net-Next": "4.1.0",
|
||||||
|
"IronSnappy": "1.3.1",
|
||||||
|
"Microsoft.Extensions.Configuration.Binder": "10.0.3",
|
||||||
|
"Microsoft.Extensions.Configuration.Json": "10.0.3",
|
||||||
|
"Microsoft.Extensions.Logging.Abstractions": "10.0.3",
|
||||||
|
"Microsoft.Extensions.Options": "10.0.3",
|
||||||
|
"NATS.NKeys": "1.0.0-preview.3"
|
||||||
|
},
|
||||||
|
"runtime": {
|
||||||
|
"ZB.MOM.NatsNet.Server.dll": {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"BCrypt.Net-Next/4.1.0": {
|
||||||
|
"runtime": {
|
||||||
|
"lib/net10.0/BCrypt.Net-Next.dll": {
|
||||||
|
"assemblyVersion": "4.1.0.0",
|
||||||
|
"fileVersion": "4.1.0.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"IronSnappy/1.3.1": {
|
||||||
|
"runtime": {
|
||||||
|
"lib/net7.0/IronSnappy.dll": {
|
||||||
|
"assemblyVersion": "1.0.0.0",
|
||||||
|
"fileVersion": "1.3.1.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Microsoft.Extensions.Configuration/10.0.3": {
|
||||||
|
"dependencies": {
|
||||||
|
"Microsoft.Extensions.Configuration.Abstractions": "10.0.3",
|
||||||
|
"Microsoft.Extensions.Primitives": "10.0.3"
|
||||||
|
},
|
||||||
|
"runtime": {
|
||||||
|
"lib/net10.0/Microsoft.Extensions.Configuration.dll": {
|
||||||
|
"assemblyVersion": "10.0.0.0",
|
||||||
|
"fileVersion": "10.0.326.7603"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Microsoft.Extensions.Configuration.Abstractions/10.0.3": {
|
||||||
|
"dependencies": {
|
||||||
|
"Microsoft.Extensions.Primitives": "10.0.3"
|
||||||
|
},
|
||||||
|
"runtime": {
|
||||||
|
"lib/net10.0/Microsoft.Extensions.Configuration.Abstractions.dll": {
|
||||||
|
"assemblyVersion": "10.0.0.0",
|
||||||
|
"fileVersion": "10.0.326.7603"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Microsoft.Extensions.Configuration.Binder/10.0.3": {
|
||||||
|
"dependencies": {
|
||||||
|
"Microsoft.Extensions.Configuration": "10.0.3",
|
||||||
|
"Microsoft.Extensions.Configuration.Abstractions": "10.0.3"
|
||||||
|
},
|
||||||
|
"runtime": {
|
||||||
|
"lib/net10.0/Microsoft.Extensions.Configuration.Binder.dll": {
|
||||||
|
"assemblyVersion": "10.0.0.0",
|
||||||
|
"fileVersion": "10.0.326.7603"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Microsoft.Extensions.Configuration.FileExtensions/10.0.3": {
|
||||||
|
"dependencies": {
|
||||||
|
"Microsoft.Extensions.Configuration": "10.0.3",
|
||||||
|
"Microsoft.Extensions.Configuration.Abstractions": "10.0.3",
|
||||||
|
"Microsoft.Extensions.FileProviders.Abstractions": "10.0.3",
|
||||||
|
"Microsoft.Extensions.FileProviders.Physical": "10.0.3",
|
||||||
|
"Microsoft.Extensions.Primitives": "10.0.3"
|
||||||
|
},
|
||||||
|
"runtime": {
|
||||||
|
"lib/net10.0/Microsoft.Extensions.Configuration.FileExtensions.dll": {
|
||||||
|
"assemblyVersion": "10.0.0.0",
|
||||||
|
"fileVersion": "10.0.326.7603"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Microsoft.Extensions.Configuration.Json/10.0.3": {
|
||||||
|
"dependencies": {
|
||||||
|
"Microsoft.Extensions.Configuration": "10.0.3",
|
||||||
|
"Microsoft.Extensions.Configuration.Abstractions": "10.0.3",
|
||||||
|
"Microsoft.Extensions.Configuration.FileExtensions": "10.0.3",
|
||||||
|
"Microsoft.Extensions.FileProviders.Abstractions": "10.0.3"
|
||||||
|
},
|
||||||
|
"runtime": {
|
||||||
|
"lib/net10.0/Microsoft.Extensions.Configuration.Json.dll": {
|
||||||
|
"assemblyVersion": "10.0.0.0",
|
||||||
|
"fileVersion": "10.0.326.7603"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Microsoft.Extensions.DependencyInjection.Abstractions/10.0.3": {
|
||||||
|
"runtime": {
|
||||||
|
"lib/net10.0/Microsoft.Extensions.DependencyInjection.Abstractions.dll": {
|
||||||
|
"assemblyVersion": "10.0.0.0",
|
||||||
|
"fileVersion": "10.0.326.7603"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Microsoft.Extensions.FileProviders.Abstractions/10.0.3": {
|
||||||
|
"dependencies": {
|
||||||
|
"Microsoft.Extensions.Primitives": "10.0.3"
|
||||||
|
},
|
||||||
|
"runtime": {
|
||||||
|
"lib/net10.0/Microsoft.Extensions.FileProviders.Abstractions.dll": {
|
||||||
|
"assemblyVersion": "10.0.0.0",
|
||||||
|
"fileVersion": "10.0.326.7603"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Microsoft.Extensions.FileProviders.Physical/10.0.3": {
|
||||||
|
"dependencies": {
|
||||||
|
"Microsoft.Extensions.FileProviders.Abstractions": "10.0.3",
|
||||||
|
"Microsoft.Extensions.FileSystemGlobbing": "10.0.3",
|
||||||
|
"Microsoft.Extensions.Primitives": "10.0.3"
|
||||||
|
},
|
||||||
|
"runtime": {
|
||||||
|
"lib/net10.0/Microsoft.Extensions.FileProviders.Physical.dll": {
|
||||||
|
"assemblyVersion": "10.0.0.0",
|
||||||
|
"fileVersion": "10.0.326.7603"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Microsoft.Extensions.FileSystemGlobbing/10.0.3": {
|
||||||
|
"runtime": {
|
||||||
|
"lib/net10.0/Microsoft.Extensions.FileSystemGlobbing.dll": {
|
||||||
|
"assemblyVersion": "10.0.0.0",
|
||||||
|
"fileVersion": "10.0.326.7603"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Microsoft.Extensions.Logging.Abstractions/10.0.3": {
|
||||||
|
"dependencies": {
|
||||||
|
"Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.3"
|
||||||
|
},
|
||||||
|
"runtime": {
|
||||||
|
"lib/net10.0/Microsoft.Extensions.Logging.Abstractions.dll": {
|
||||||
|
"assemblyVersion": "10.0.0.0",
|
||||||
|
"fileVersion": "10.0.326.7603"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Microsoft.Extensions.Options/10.0.3": {
|
||||||
|
"dependencies": {
|
||||||
|
"Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.3",
|
||||||
|
"Microsoft.Extensions.Primitives": "10.0.3"
|
||||||
|
},
|
||||||
|
"runtime": {
|
||||||
|
"lib/net10.0/Microsoft.Extensions.Options.dll": {
|
||||||
|
"assemblyVersion": "10.0.0.0",
|
||||||
|
"fileVersion": "10.0.326.7603"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Microsoft.Extensions.Primitives/10.0.3": {
|
||||||
|
"runtime": {
|
||||||
|
"lib/net10.0/Microsoft.Extensions.Primitives.dll": {
|
||||||
|
"assemblyVersion": "10.0.0.0",
|
||||||
|
"fileVersion": "10.0.326.7603"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"NATS.NKeys/1.0.0-preview.3": {
|
||||||
|
"runtime": {
|
||||||
|
"lib/net8.0/NATS.NKeys.dll": {
|
||||||
|
"assemblyVersion": "1.0.0.0",
|
||||||
|
"fileVersion": "1.0.0.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"libraries": {
|
||||||
|
"ZB.MOM.NatsNet.Server/1.0.0": {
|
||||||
|
"type": "project",
|
||||||
|
"serviceable": false,
|
||||||
|
"sha512": ""
|
||||||
|
},
|
||||||
|
"BCrypt.Net-Next/4.1.0": {
|
||||||
|
"type": "package",
|
||||||
|
"serviceable": true,
|
||||||
|
"sha512": "sha512-5YT3DKllmtkyW68PjURu/V1TOe4MKiByKwsRNVcfYE1S5KuFTeozdmKzyNzolKiQF391OXCaQtINvYT3j1ERzQ==",
|
||||||
|
"path": "bcrypt.net-next/4.1.0",
|
||||||
|
"hashPath": "bcrypt.net-next.4.1.0.nupkg.sha512"
|
||||||
|
},
|
||||||
|
"IronSnappy/1.3.1": {
|
||||||
|
"type": "package",
|
||||||
|
"serviceable": true,
|
||||||
|
"sha512": "sha512-lOI1vn5xbF169G7Lo5i9cQGvYE6sCMwyNsONfAKQ1FtKqteLLcnlbSAhk+UFmol3lAEJyrCW4tglA8OkLKUNww==",
|
||||||
|
"path": "ironsnappy/1.3.1",
|
||||||
|
"hashPath": "ironsnappy.1.3.1.nupkg.sha512"
|
||||||
|
},
|
||||||
|
"Microsoft.Extensions.Configuration/10.0.3": {
|
||||||
|
"type": "package",
|
||||||
|
"serviceable": true,
|
||||||
|
"sha512": "sha512-H1Cjv2xmm7O3iAGmFTcnSM0ZhLQ/7SqefmAvSJoT1PbXoxeYc2fo0mCLn2JlVbr9E6YpoU9q/o0fI9neDJB0xQ==",
|
||||||
|
"path": "microsoft.extensions.configuration/10.0.3",
|
||||||
|
"hashPath": "microsoft.extensions.configuration.10.0.3.nupkg.sha512"
|
||||||
|
},
|
||||||
|
"Microsoft.Extensions.Configuration.Abstractions/10.0.3": {
|
||||||
|
"type": "package",
|
||||||
|
"serviceable": true,
|
||||||
|
"sha512": "sha512-xVDHL0+SIgemfh95fTO9cGLe17TWv/ZP0n7m01z8X6pzt2DmQpucioWR/mYZA1sRlkWnkXzfl0JweLNWmE9WMg==",
|
||||||
|
"path": "microsoft.extensions.configuration.abstractions/10.0.3",
|
||||||
|
"hashPath": "microsoft.extensions.configuration.abstractions.10.0.3.nupkg.sha512"
|
||||||
|
},
|
||||||
|
"Microsoft.Extensions.Configuration.Binder/10.0.3": {
|
||||||
|
"type": "package",
|
||||||
|
"serviceable": true,
|
||||||
|
"sha512": "sha512-759UhpKaR5Jsll9kXpkft4z/7tpeF7Dw2rTY/9f9JchaSQTpRFNIPkZFZvoo7fFpbjUaqtDlO5aiGpmQrp/EUA==",
|
||||||
|
"path": "microsoft.extensions.configuration.binder/10.0.3",
|
||||||
|
"hashPath": "microsoft.extensions.configuration.binder.10.0.3.nupkg.sha512"
|
||||||
|
},
|
||||||
|
"Microsoft.Extensions.Configuration.FileExtensions/10.0.3": {
|
||||||
|
"type": "package",
|
||||||
|
"serviceable": true,
|
||||||
|
"sha512": "sha512-/MLsBbLpwDxsU+7DDNwasf2mKrpMSOWEL377gNZTy5waFkCYvS3GVaLIz6bvikH4rAwHrCOxHw0t/5iCoImYCA==",
|
||||||
|
"path": "microsoft.extensions.configuration.fileextensions/10.0.3",
|
||||||
|
"hashPath": "microsoft.extensions.configuration.fileextensions.10.0.3.nupkg.sha512"
|
||||||
|
},
|
||||||
|
"Microsoft.Extensions.Configuration.Json/10.0.3": {
|
||||||
|
"type": "package",
|
||||||
|
"serviceable": true,
|
||||||
|
"sha512": "sha512-mGGMOA9nkET8OVsQfS41o66eWkckBzNHJK6+5VbLQ2YdyqKphcv27uDZxLf4exSl+5QxLnHkN+W/4qEDgyvCPA==",
|
||||||
|
"path": "microsoft.extensions.configuration.json/10.0.3",
|
||||||
|
"hashPath": "microsoft.extensions.configuration.json.10.0.3.nupkg.sha512"
|
||||||
|
},
|
||||||
|
"Microsoft.Extensions.DependencyInjection.Abstractions/10.0.3": {
|
||||||
|
"type": "package",
|
||||||
|
"serviceable": true,
|
||||||
|
"sha512": "sha512-bwGMrRcAMWx2s/RDgja97p27rxSz2pEQW0+rX5cWAUWVETVJ/eyxGfjAl8vuG5a+lckWmPIE+vcuaZNVB5YDdw==",
|
||||||
|
"path": "microsoft.extensions.dependencyinjection.abstractions/10.0.3",
|
||||||
|
"hashPath": "microsoft.extensions.dependencyinjection.abstractions.10.0.3.nupkg.sha512"
|
||||||
|
},
|
||||||
|
"Microsoft.Extensions.FileProviders.Abstractions/10.0.3": {
|
||||||
|
"type": "package",
|
||||||
|
"serviceable": true,
|
||||||
|
"sha512": "sha512-4TD9AXDRsipTmaemwnjt/DM5Ri0de2JzHQhvZ4woBTjUtL4XrPNsMrOk5oiLJAx1gTrE6pOIhxv+lEde5F6CZA==",
|
||||||
|
"path": "microsoft.extensions.fileproviders.abstractions/10.0.3",
|
||||||
|
"hashPath": "microsoft.extensions.fileproviders.abstractions.10.0.3.nupkg.sha512"
|
||||||
|
},
|
||||||
|
"Microsoft.Extensions.FileProviders.Physical/10.0.3": {
|
||||||
|
"type": "package",
|
||||||
|
"serviceable": true,
|
||||||
|
"sha512": "sha512-8qLl5LXtcj6Z8yPbHAA/a57fvvl9nUCdi59AJFuixcWM4wSuENZ8jjoRATOKs/I4vOi/bDe0d5LqGSSLE634eA==",
|
||||||
|
"path": "microsoft.extensions.fileproviders.physical/10.0.3",
|
||||||
|
"hashPath": "microsoft.extensions.fileproviders.physical.10.0.3.nupkg.sha512"
|
||||||
|
},
|
||||||
|
"Microsoft.Extensions.FileSystemGlobbing/10.0.3": {
|
||||||
|
"type": "package",
|
||||||
|
"serviceable": true,
|
||||||
|
"sha512": "sha512-oM7pl8uJz8WRPRlh4AGQS61aeV9GOfTu89yqTiRSYyyMuCNVkbNra9zEk7ApyJ/sZrUpbjOZCRHuitCEsTWghg==",
|
||||||
|
"path": "microsoft.extensions.filesystemglobbing/10.0.3",
|
||||||
|
"hashPath": "microsoft.extensions.filesystemglobbing.10.0.3.nupkg.sha512"
|
||||||
|
},
|
||||||
|
"Microsoft.Extensions.Logging.Abstractions/10.0.3": {
|
||||||
|
"type": "package",
|
||||||
|
"serviceable": true,
|
||||||
|
"sha512": "sha512-lxl0WLk7ROgBFAsjcOYjQ8/DVK+VMszxGBzUhgtQmAsTNldLL5pk9NG/cWTsXHq0lUhUEAtZkEE7jOGOA8bGKQ==",
|
||||||
|
"path": "microsoft.extensions.logging.abstractions/10.0.3",
|
||||||
|
"hashPath": "microsoft.extensions.logging.abstractions.10.0.3.nupkg.sha512"
|
||||||
|
},
|
||||||
|
"Microsoft.Extensions.Options/10.0.3": {
|
||||||
|
"type": "package",
|
||||||
|
"serviceable": true,
|
||||||
|
"sha512": "sha512-hU6WzGTPvPoLA2ng1ILvWQb3g0qORdlHNsxI8IcPLumJb3suimYUl+bbDzdo1V4KFsvVhnMWzysHpKbZaoDQPQ==",
|
||||||
|
"path": "microsoft.extensions.options/10.0.3",
|
||||||
|
"hashPath": "microsoft.extensions.options.10.0.3.nupkg.sha512"
|
||||||
|
},
|
||||||
|
"Microsoft.Extensions.Primitives/10.0.3": {
|
||||||
|
"type": "package",
|
||||||
|
"serviceable": true,
|
||||||
|
"sha512": "sha512-GEcpTwo7sUoLGGNTqV1FZEuL+tTD9m81NX/mh099dqGNna07/UGZShKQNZRw4hv6nlliSUwYQgSYc7OR99Jufg==",
|
||||||
|
"path": "microsoft.extensions.primitives/10.0.3",
|
||||||
|
"hashPath": "microsoft.extensions.primitives.10.0.3.nupkg.sha512"
|
||||||
|
},
|
||||||
|
"NATS.NKeys/1.0.0-preview.3": {
|
||||||
|
"type": "package",
|
||||||
|
"serviceable": true,
|
||||||
|
"sha512": "sha512-5/Hz2+hAR5mLaQXwZsX/8pMny0VTrr1DWgX1z2mfj6PLf7a5q6lf4a3B1bXSXMTW+V432L+1RYNdAczFaMB0mQ==",
|
||||||
|
"path": "nats.nkeys/1.0.0-preview.3",
|
||||||
|
"hashPath": "nats.nkeys.1.0.0-preview.3.nupkg.sha512"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,4 @@
|
|||||||
|
// <autogenerated />
|
||||||
|
using System;
|
||||||
|
using System.Reflection;
|
||||||
|
[assembly: global::System.Runtime.Versioning.TargetFrameworkAttribute(".NETCoreApp,Version=v10.0", FrameworkDisplayName = ".NET 10.0")]
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// <auto-generated>
|
||||||
|
// This code was generated by a tool.
|
||||||
|
//
|
||||||
|
// Changes to this file may cause incorrect behavior and will be lost if
|
||||||
|
// the code is regenerated.
|
||||||
|
// </auto-generated>
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Reflection;
|
||||||
|
|
||||||
|
[assembly: System.Reflection.AssemblyCompanyAttribute("ZB.MOM.NatsNet.Server")]
|
||||||
|
[assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")]
|
||||||
|
[assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")]
|
||||||
|
[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+ff7e674ec4ebe169e57d4292855270a7c7b0a566")]
|
||||||
|
[assembly: System.Reflection.AssemblyProductAttribute("ZB.MOM.NatsNet.Server")]
|
||||||
|
[assembly: System.Reflection.AssemblyTitleAttribute("ZB.MOM.NatsNet.Server")]
|
||||||
|
[assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")]
|
||||||
|
|
||||||
|
// Generated by the MSBuild WriteCodeFragment class.
|
||||||
|
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
7ab638326ce499193b46de7cb439743621cc81818ae875ee09256caca5049b13
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
is_global = true
|
||||||
|
build_property.TargetFramework = net10.0
|
||||||
|
build_property.TargetFrameworkIdentifier = .NETCoreApp
|
||||||
|
build_property.TargetFrameworkVersion = v10.0
|
||||||
|
build_property.TargetPlatformMinVersion =
|
||||||
|
build_property.UsingMicrosoftNETSdkWeb =
|
||||||
|
build_property.ProjectTypeGuids =
|
||||||
|
build_property.InvariantGlobalization =
|
||||||
|
build_property.PlatformNeutralAssembly =
|
||||||
|
build_property.EnforceExtendedAnalyzerRules =
|
||||||
|
build_property._SupportedPlatformList = Linux,macOS,Windows
|
||||||
|
build_property.RootNamespace = ZB.MOM.NatsNet.Server
|
||||||
|
build_property.ProjectDir = /Users/dohertj2/Desktop/natsnet-batch24-exec/dotnet/src/ZB.MOM.NatsNet.Server/
|
||||||
|
build_property.EnableComHosting =
|
||||||
|
build_property.EnableGeneratedComInterfaceComImportInterop =
|
||||||
|
build_property.EffectiveAnalysisLevelStyle = 10.0
|
||||||
|
build_property.EnableCodeStyleSeverity =
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
// <auto-generated/>
|
||||||
|
global using System;
|
||||||
|
global using System.Collections.Generic;
|
||||||
|
global using System.IO;
|
||||||
|
global using System.Linq;
|
||||||
|
global using System.Net.Http;
|
||||||
|
global using System.Threading;
|
||||||
|
global using System.Threading.Tasks;
|
||||||
Binary file not shown.
Binary file not shown.
@@ -0,0 +1 @@
|
|||||||
|
73e7ca2be7a2fea71c313f45b16249a465dcecd64604c1bff64576df2a2eca4e
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
/Users/dohertj2/Desktop/natsnet-batch24-exec/dotnet/src/ZB.MOM.NatsNet.Server/bin/Debug/net10.0/ZB.MOM.NatsNet.Server.deps.json
|
||||||
|
/Users/dohertj2/Desktop/natsnet-batch24-exec/dotnet/src/ZB.MOM.NatsNet.Server/bin/Debug/net10.0/ZB.MOM.NatsNet.Server.dll
|
||||||
|
/Users/dohertj2/Desktop/natsnet-batch24-exec/dotnet/src/ZB.MOM.NatsNet.Server/bin/Debug/net10.0/ZB.MOM.NatsNet.Server.pdb
|
||||||
|
/Users/dohertj2/Desktop/natsnet-batch24-exec/dotnet/src/ZB.MOM.NatsNet.Server/obj/Debug/net10.0/ZB.MOM.NatsNet.Server.csproj.AssemblyReference.cache
|
||||||
|
/Users/dohertj2/Desktop/natsnet-batch24-exec/dotnet/src/ZB.MOM.NatsNet.Server/obj/Debug/net10.0/ZB.MOM.NatsNet.Server.GeneratedMSBuildEditorConfig.editorconfig
|
||||||
|
/Users/dohertj2/Desktop/natsnet-batch24-exec/dotnet/src/ZB.MOM.NatsNet.Server/obj/Debug/net10.0/ZB.MOM.NatsNet.Server.AssemblyInfoInputs.cache
|
||||||
|
/Users/dohertj2/Desktop/natsnet-batch24-exec/dotnet/src/ZB.MOM.NatsNet.Server/obj/Debug/net10.0/ZB.MOM.NatsNet.Server.AssemblyInfo.cs
|
||||||
|
/Users/dohertj2/Desktop/natsnet-batch24-exec/dotnet/src/ZB.MOM.NatsNet.Server/obj/Debug/net10.0/ZB.MOM.NatsNet.Server.csproj.CoreCompileInputs.cache
|
||||||
|
/Users/dohertj2/Desktop/natsnet-batch24-exec/dotnet/src/ZB.MOM.NatsNet.Server/obj/Debug/net10.0/ZB.MOM.NatsNet.Server.dll
|
||||||
|
/Users/dohertj2/Desktop/natsnet-batch24-exec/dotnet/src/ZB.MOM.NatsNet.Server/obj/Debug/net10.0/refint/ZB.MOM.NatsNet.Server.dll
|
||||||
|
/Users/dohertj2/Desktop/natsnet-batch24-exec/dotnet/src/ZB.MOM.NatsNet.Server/obj/Debug/net10.0/ZB.MOM.NatsNet.Server.pdb
|
||||||
|
/Users/dohertj2/Desktop/natsnet-batch24-exec/dotnet/src/ZB.MOM.NatsNet.Server/obj/Debug/net10.0/ref/ZB.MOM.NatsNet.Server.dll
|
||||||
|
/Users/dohertj2/Desktop/natsnet-batch24-exec/dotnet/src/ZB.MOM.NatsNet.Server/obj/Debug/net10.0/ZB.MOM.NatsNet.Server.sourcelink.json
|
||||||
Binary file not shown.
Binary file not shown.
@@ -0,0 +1 @@
|
|||||||
|
{"documents":{"/Users/dohertj2/Desktop/natsnet-batch24-exec/golang/nats-server/*":"https://raw.githubusercontent.com/nats-io/nats-server/66e9bbc7f8684dd1b31c54ef686924e0841f9846/*"}}
|
||||||
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,372 @@
|
|||||||
|
{
|
||||||
|
"format": 1,
|
||||||
|
"restore": {
|
||||||
|
"/Users/dohertj2/Desktop/natsnet-batch24-exec/dotnet/src/ZB.MOM.NatsNet.Server/ZB.MOM.NatsNet.Server.csproj": {}
|
||||||
|
},
|
||||||
|
"projects": {
|
||||||
|
"/Users/dohertj2/Desktop/natsnet-batch24-exec/dotnet/src/ZB.MOM.NatsNet.Server/ZB.MOM.NatsNet.Server.csproj": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"restore": {
|
||||||
|
"projectUniqueName": "/Users/dohertj2/Desktop/natsnet-batch24-exec/dotnet/src/ZB.MOM.NatsNet.Server/ZB.MOM.NatsNet.Server.csproj",
|
||||||
|
"projectName": "ZB.MOM.NatsNet.Server",
|
||||||
|
"projectPath": "/Users/dohertj2/Desktop/natsnet-batch24-exec/dotnet/src/ZB.MOM.NatsNet.Server/ZB.MOM.NatsNet.Server.csproj",
|
||||||
|
"packagesPath": "/Users/dohertj2/.nuget/packages/",
|
||||||
|
"outputPath": "/Users/dohertj2/Desktop/natsnet-batch24-exec/dotnet/src/ZB.MOM.NatsNet.Server/obj/",
|
||||||
|
"projectStyle": "PackageReference",
|
||||||
|
"configFilePaths": [
|
||||||
|
"/Users/dohertj2/.nuget/NuGet/NuGet.Config"
|
||||||
|
],
|
||||||
|
"originalTargetFrameworks": [
|
||||||
|
"net10.0"
|
||||||
|
],
|
||||||
|
"sources": {
|
||||||
|
"/usr/local/share/dotnet/library-packs": {},
|
||||||
|
"https://api.nuget.org/v3/index.json": {}
|
||||||
|
},
|
||||||
|
"frameworks": {
|
||||||
|
"net10.0": {
|
||||||
|
"targetAlias": "net10.0",
|
||||||
|
"projectReferences": {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"warningProperties": {
|
||||||
|
"warnAsError": [
|
||||||
|
"NU1605"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"restoreAuditProperties": {
|
||||||
|
"enableAudit": "true",
|
||||||
|
"auditLevel": "low",
|
||||||
|
"auditMode": "all"
|
||||||
|
},
|
||||||
|
"SdkAnalysisLevel": "10.0.100"
|
||||||
|
},
|
||||||
|
"frameworks": {
|
||||||
|
"net10.0": {
|
||||||
|
"targetAlias": "net10.0",
|
||||||
|
"dependencies": {
|
||||||
|
"BCrypt.Net-Next": {
|
||||||
|
"target": "Package",
|
||||||
|
"version": "[*, )"
|
||||||
|
},
|
||||||
|
"IronSnappy": {
|
||||||
|
"target": "Package",
|
||||||
|
"version": "[*, )"
|
||||||
|
},
|
||||||
|
"Microsoft.Extensions.Configuration.Binder": {
|
||||||
|
"target": "Package",
|
||||||
|
"version": "[*, )"
|
||||||
|
},
|
||||||
|
"Microsoft.Extensions.Configuration.Json": {
|
||||||
|
"target": "Package",
|
||||||
|
"version": "[*, )"
|
||||||
|
},
|
||||||
|
"Microsoft.Extensions.Logging.Abstractions": {
|
||||||
|
"target": "Package",
|
||||||
|
"version": "[*, )"
|
||||||
|
},
|
||||||
|
"Microsoft.Extensions.Options": {
|
||||||
|
"target": "Package",
|
||||||
|
"version": "[*, )"
|
||||||
|
},
|
||||||
|
"NATS.NKeys": {
|
||||||
|
"target": "Package",
|
||||||
|
"version": "[1.0.0-preview.3, )"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"imports": [
|
||||||
|
"net461",
|
||||||
|
"net462",
|
||||||
|
"net47",
|
||||||
|
"net471",
|
||||||
|
"net472",
|
||||||
|
"net48",
|
||||||
|
"net481"
|
||||||
|
],
|
||||||
|
"assetTargetFallback": true,
|
||||||
|
"warn": true,
|
||||||
|
"frameworkReferences": {
|
||||||
|
"Microsoft.NETCore.App": {
|
||||||
|
"privateAssets": "all"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"runtimeIdentifierGraphPath": "/usr/local/share/dotnet/sdk/10.0.101/PortableRuntimeIdentifierGraph.json",
|
||||||
|
"packagesToPrune": {
|
||||||
|
"Microsoft.CSharp": "(,4.7.32767]",
|
||||||
|
"Microsoft.VisualBasic": "(,10.4.32767]",
|
||||||
|
"Microsoft.Win32.Primitives": "(,4.3.32767]",
|
||||||
|
"Microsoft.Win32.Registry": "(,5.0.32767]",
|
||||||
|
"runtime.any.System.Collections": "(,4.3.32767]",
|
||||||
|
"runtime.any.System.Diagnostics.Tools": "(,4.3.32767]",
|
||||||
|
"runtime.any.System.Diagnostics.Tracing": "(,4.3.32767]",
|
||||||
|
"runtime.any.System.Globalization": "(,4.3.32767]",
|
||||||
|
"runtime.any.System.Globalization.Calendars": "(,4.3.32767]",
|
||||||
|
"runtime.any.System.IO": "(,4.3.32767]",
|
||||||
|
"runtime.any.System.Reflection": "(,4.3.32767]",
|
||||||
|
"runtime.any.System.Reflection.Extensions": "(,4.3.32767]",
|
||||||
|
"runtime.any.System.Reflection.Primitives": "(,4.3.32767]",
|
||||||
|
"runtime.any.System.Resources.ResourceManager": "(,4.3.32767]",
|
||||||
|
"runtime.any.System.Runtime": "(,4.3.32767]",
|
||||||
|
"runtime.any.System.Runtime.Handles": "(,4.3.32767]",
|
||||||
|
"runtime.any.System.Runtime.InteropServices": "(,4.3.32767]",
|
||||||
|
"runtime.any.System.Text.Encoding": "(,4.3.32767]",
|
||||||
|
"runtime.any.System.Text.Encoding.Extensions": "(,4.3.32767]",
|
||||||
|
"runtime.any.System.Threading.Tasks": "(,4.3.32767]",
|
||||||
|
"runtime.any.System.Threading.Timer": "(,4.3.32767]",
|
||||||
|
"runtime.aot.System.Collections": "(,4.3.32767]",
|
||||||
|
"runtime.aot.System.Diagnostics.Tools": "(,4.3.32767]",
|
||||||
|
"runtime.aot.System.Diagnostics.Tracing": "(,4.3.32767]",
|
||||||
|
"runtime.aot.System.Globalization": "(,4.3.32767]",
|
||||||
|
"runtime.aot.System.Globalization.Calendars": "(,4.3.32767]",
|
||||||
|
"runtime.aot.System.IO": "(,4.3.32767]",
|
||||||
|
"runtime.aot.System.Reflection": "(,4.3.32767]",
|
||||||
|
"runtime.aot.System.Reflection.Extensions": "(,4.3.32767]",
|
||||||
|
"runtime.aot.System.Reflection.Primitives": "(,4.3.32767]",
|
||||||
|
"runtime.aot.System.Resources.ResourceManager": "(,4.3.32767]",
|
||||||
|
"runtime.aot.System.Runtime": "(,4.3.32767]",
|
||||||
|
"runtime.aot.System.Runtime.Handles": "(,4.3.32767]",
|
||||||
|
"runtime.aot.System.Runtime.InteropServices": "(,4.3.32767]",
|
||||||
|
"runtime.aot.System.Text.Encoding": "(,4.3.32767]",
|
||||||
|
"runtime.aot.System.Text.Encoding.Extensions": "(,4.3.32767]",
|
||||||
|
"runtime.aot.System.Threading.Tasks": "(,4.3.32767]",
|
||||||
|
"runtime.aot.System.Threading.Timer": "(,4.3.32767]",
|
||||||
|
"runtime.debian.8-x64.runtime.native.System": "(,4.3.32767]",
|
||||||
|
"runtime.debian.8-x64.runtime.native.System.IO.Compression": "(,4.3.32767]",
|
||||||
|
"runtime.debian.8-x64.runtime.native.System.Net.Http": "(,4.3.32767]",
|
||||||
|
"runtime.debian.8-x64.runtime.native.System.Net.Security": "(,4.3.32767]",
|
||||||
|
"runtime.debian.8-x64.runtime.native.System.Security.Cryptography": "(,4.3.32767]",
|
||||||
|
"runtime.debian.8-x64.runtime.native.System.Security.Cryptography.OpenSsl": "(,4.3.32767]",
|
||||||
|
"runtime.debian.9-x64.runtime.native.System": "(,4.3.32767]",
|
||||||
|
"runtime.debian.9-x64.runtime.native.System.IO.Compression": "(,4.3.32767]",
|
||||||
|
"runtime.debian.9-x64.runtime.native.System.Net.Http": "(,4.3.32767]",
|
||||||
|
"runtime.debian.9-x64.runtime.native.System.Net.Security": "(,4.3.32767]",
|
||||||
|
"runtime.fedora.23-x64.runtime.native.System": "(,4.3.32767]",
|
||||||
|
"runtime.fedora.23-x64.runtime.native.System.IO.Compression": "(,4.3.32767]",
|
||||||
|
"runtime.fedora.23-x64.runtime.native.System.Net.Http": "(,4.3.32767]",
|
||||||
|
"runtime.fedora.23-x64.runtime.native.System.Net.Security": "(,4.3.32767]",
|
||||||
|
"runtime.fedora.23-x64.runtime.native.System.Security.Cryptography": "(,4.3.32767]",
|
||||||
|
"runtime.fedora.23-x64.runtime.native.System.Security.Cryptography.OpenSsl": "(,4.3.32767]",
|
||||||
|
"runtime.fedora.24-x64.runtime.native.System": "(,4.3.32767]",
|
||||||
|
"runtime.fedora.24-x64.runtime.native.System.IO.Compression": "(,4.3.32767]",
|
||||||
|
"runtime.fedora.24-x64.runtime.native.System.Net.Http": "(,4.3.32767]",
|
||||||
|
"runtime.fedora.24-x64.runtime.native.System.Net.Security": "(,4.3.32767]",
|
||||||
|
"runtime.fedora.24-x64.runtime.native.System.Security.Cryptography": "(,4.3.32767]",
|
||||||
|
"runtime.fedora.24-x64.runtime.native.System.Security.Cryptography.OpenSsl": "(,4.3.32767]",
|
||||||
|
"runtime.fedora.27-x64.runtime.native.System": "(,4.3.32767]",
|
||||||
|
"runtime.fedora.27-x64.runtime.native.System.IO.Compression": "(,4.3.32767]",
|
||||||
|
"runtime.fedora.27-x64.runtime.native.System.Net.Http": "(,4.3.32767]",
|
||||||
|
"runtime.fedora.27-x64.runtime.native.System.Net.Security": "(,4.3.32767]",
|
||||||
|
"runtime.fedora.28-x64.runtime.native.System": "(,4.3.32767]",
|
||||||
|
"runtime.fedora.28-x64.runtime.native.System.IO.Compression": "(,4.3.32767]",
|
||||||
|
"runtime.fedora.28-x64.runtime.native.System.Net.Http": "(,4.3.32767]",
|
||||||
|
"runtime.fedora.28-x64.runtime.native.System.Net.Security": "(,4.3.32767]",
|
||||||
|
"runtime.opensuse.13.2-x64.runtime.native.System": "(,4.3.32767]",
|
||||||
|
"runtime.opensuse.13.2-x64.runtime.native.System.IO.Compression": "(,4.3.32767]",
|
||||||
|
"runtime.opensuse.13.2-x64.runtime.native.System.Net.Http": "(,4.3.32767]",
|
||||||
|
"runtime.opensuse.13.2-x64.runtime.native.System.Net.Security": "(,4.3.32767]",
|
||||||
|
"runtime.opensuse.13.2-x64.runtime.native.System.Security.Cryptography": "(,4.3.32767]",
|
||||||
|
"runtime.opensuse.13.2-x64.runtime.native.System.Security.Cryptography.OpenSsl": "(,4.3.32767]",
|
||||||
|
"runtime.opensuse.42.1-x64.runtime.native.System": "(,4.3.32767]",
|
||||||
|
"runtime.opensuse.42.1-x64.runtime.native.System.IO.Compression": "(,4.3.32767]",
|
||||||
|
"runtime.opensuse.42.1-x64.runtime.native.System.Net.Http": "(,4.3.32767]",
|
||||||
|
"runtime.opensuse.42.1-x64.runtime.native.System.Net.Security": "(,4.3.32767]",
|
||||||
|
"runtime.opensuse.42.1-x64.runtime.native.System.Security.Cryptography": "(,4.3.32767]",
|
||||||
|
"runtime.opensuse.42.1-x64.runtime.native.System.Security.Cryptography.OpenSsl": "(,4.3.32767]",
|
||||||
|
"runtime.opensuse.42.3-x64.runtime.native.System": "(,4.3.32767]",
|
||||||
|
"runtime.opensuse.42.3-x64.runtime.native.System.IO.Compression": "(,4.3.32767]",
|
||||||
|
"runtime.opensuse.42.3-x64.runtime.native.System.Net.Http": "(,4.3.32767]",
|
||||||
|
"runtime.opensuse.42.3-x64.runtime.native.System.Net.Security": "(,4.3.32767]",
|
||||||
|
"runtime.osx.10.10-x64.runtime.native.System": "(,4.3.32767]",
|
||||||
|
"runtime.osx.10.10-x64.runtime.native.System.IO.Compression": "(,4.3.32767]",
|
||||||
|
"runtime.osx.10.10-x64.runtime.native.System.Net.Http": "(,4.3.32767]",
|
||||||
|
"runtime.osx.10.10-x64.runtime.native.System.Net.Security": "(,4.3.32767]",
|
||||||
|
"runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography": "(,4.3.32767]",
|
||||||
|
"runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.Apple": "(,4.3.32767]",
|
||||||
|
"runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.OpenSsl": "(,4.3.32767]",
|
||||||
|
"runtime.rhel.7-x64.runtime.native.System": "(,4.3.32767]",
|
||||||
|
"runtime.rhel.7-x64.runtime.native.System.IO.Compression": "(,4.3.32767]",
|
||||||
|
"runtime.rhel.7-x64.runtime.native.System.Net.Http": "(,4.3.32767]",
|
||||||
|
"runtime.rhel.7-x64.runtime.native.System.Net.Security": "(,4.3.32767]",
|
||||||
|
"runtime.rhel.7-x64.runtime.native.System.Security.Cryptography": "(,4.3.32767]",
|
||||||
|
"runtime.rhel.7-x64.runtime.native.System.Security.Cryptography.OpenSsl": "(,4.3.32767]",
|
||||||
|
"runtime.ubuntu.14.04-x64.runtime.native.System": "(,4.3.32767]",
|
||||||
|
"runtime.ubuntu.14.04-x64.runtime.native.System.IO.Compression": "(,4.3.32767]",
|
||||||
|
"runtime.ubuntu.14.04-x64.runtime.native.System.Net.Http": "(,4.3.32767]",
|
||||||
|
"runtime.ubuntu.14.04-x64.runtime.native.System.Net.Security": "(,4.3.32767]",
|
||||||
|
"runtime.ubuntu.14.04-x64.runtime.native.System.Security.Cryptography": "(,4.3.32767]",
|
||||||
|
"runtime.ubuntu.14.04-x64.runtime.native.System.Security.Cryptography.OpenSsl": "(,4.3.32767]",
|
||||||
|
"runtime.ubuntu.16.04-x64.runtime.native.System": "(,4.3.32767]",
|
||||||
|
"runtime.ubuntu.16.04-x64.runtime.native.System.IO.Compression": "(,4.3.32767]",
|
||||||
|
"runtime.ubuntu.16.04-x64.runtime.native.System.Net.Http": "(,4.3.32767]",
|
||||||
|
"runtime.ubuntu.16.04-x64.runtime.native.System.Net.Security": "(,4.3.32767]",
|
||||||
|
"runtime.ubuntu.16.04-x64.runtime.native.System.Security.Cryptography": "(,4.3.32767]",
|
||||||
|
"runtime.ubuntu.16.04-x64.runtime.native.System.Security.Cryptography.OpenSsl": "(,4.3.32767]",
|
||||||
|
"runtime.ubuntu.16.10-x64.runtime.native.System": "(,4.3.32767]",
|
||||||
|
"runtime.ubuntu.16.10-x64.runtime.native.System.IO.Compression": "(,4.3.32767]",
|
||||||
|
"runtime.ubuntu.16.10-x64.runtime.native.System.Net.Http": "(,4.3.32767]",
|
||||||
|
"runtime.ubuntu.16.10-x64.runtime.native.System.Net.Security": "(,4.3.32767]",
|
||||||
|
"runtime.ubuntu.16.10-x64.runtime.native.System.Security.Cryptography": "(,4.3.32767]",
|
||||||
|
"runtime.ubuntu.16.10-x64.runtime.native.System.Security.Cryptography.OpenSsl": "(,4.3.32767]",
|
||||||
|
"runtime.ubuntu.18.04-x64.runtime.native.System": "(,4.3.32767]",
|
||||||
|
"runtime.ubuntu.18.04-x64.runtime.native.System.IO.Compression": "(,4.3.32767]",
|
||||||
|
"runtime.ubuntu.18.04-x64.runtime.native.System.Net.Http": "(,4.3.32767]",
|
||||||
|
"runtime.ubuntu.18.04-x64.runtime.native.System.Net.Security": "(,4.3.32767]",
|
||||||
|
"runtime.unix.Microsoft.Win32.Primitives": "(,4.3.32767]",
|
||||||
|
"runtime.unix.System.Console": "(,4.3.32767]",
|
||||||
|
"runtime.unix.System.Diagnostics.Debug": "(,4.3.32767]",
|
||||||
|
"runtime.unix.System.IO.FileSystem": "(,4.3.32767]",
|
||||||
|
"runtime.unix.System.Net.Primitives": "(,4.3.32767]",
|
||||||
|
"runtime.unix.System.Net.Sockets": "(,4.3.32767]",
|
||||||
|
"runtime.unix.System.Private.Uri": "(,4.3.32767]",
|
||||||
|
"runtime.unix.System.Runtime.Extensions": "(,4.3.32767]",
|
||||||
|
"runtime.win.Microsoft.Win32.Primitives": "(,4.3.32767]",
|
||||||
|
"runtime.win.System.Console": "(,4.3.32767]",
|
||||||
|
"runtime.win.System.Diagnostics.Debug": "(,4.3.32767]",
|
||||||
|
"runtime.win.System.IO.FileSystem": "(,4.3.32767]",
|
||||||
|
"runtime.win.System.Net.Primitives": "(,4.3.32767]",
|
||||||
|
"runtime.win.System.Net.Sockets": "(,4.3.32767]",
|
||||||
|
"runtime.win.System.Runtime.Extensions": "(,4.3.32767]",
|
||||||
|
"runtime.win10-arm-aot.runtime.native.System.IO.Compression": "(,4.0.32767]",
|
||||||
|
"runtime.win10-arm64.runtime.native.System.IO.Compression": "(,4.3.32767]",
|
||||||
|
"runtime.win10-x64-aot.runtime.native.System.IO.Compression": "(,4.0.32767]",
|
||||||
|
"runtime.win10-x86-aot.runtime.native.System.IO.Compression": "(,4.0.32767]",
|
||||||
|
"runtime.win7-x64.runtime.native.System.IO.Compression": "(,4.3.32767]",
|
||||||
|
"runtime.win7-x86.runtime.native.System.IO.Compression": "(,4.3.32767]",
|
||||||
|
"runtime.win7.System.Private.Uri": "(,4.3.32767]",
|
||||||
|
"runtime.win8-arm.runtime.native.System.IO.Compression": "(,4.3.32767]",
|
||||||
|
"System.AppContext": "(,4.3.32767]",
|
||||||
|
"System.Buffers": "(,5.0.32767]",
|
||||||
|
"System.Collections": "(,4.3.32767]",
|
||||||
|
"System.Collections.Concurrent": "(,4.3.32767]",
|
||||||
|
"System.Collections.Immutable": "(,10.0.32767]",
|
||||||
|
"System.Collections.NonGeneric": "(,4.3.32767]",
|
||||||
|
"System.Collections.Specialized": "(,4.3.32767]",
|
||||||
|
"System.ComponentModel": "(,4.3.32767]",
|
||||||
|
"System.ComponentModel.Annotations": "(,4.3.32767]",
|
||||||
|
"System.ComponentModel.EventBasedAsync": "(,4.3.32767]",
|
||||||
|
"System.ComponentModel.Primitives": "(,4.3.32767]",
|
||||||
|
"System.ComponentModel.TypeConverter": "(,4.3.32767]",
|
||||||
|
"System.Console": "(,4.3.32767]",
|
||||||
|
"System.Data.Common": "(,4.3.32767]",
|
||||||
|
"System.Data.DataSetExtensions": "(,4.4.32767]",
|
||||||
|
"System.Diagnostics.Contracts": "(,4.3.32767]",
|
||||||
|
"System.Diagnostics.Debug": "(,4.3.32767]",
|
||||||
|
"System.Diagnostics.DiagnosticSource": "(,10.0.32767]",
|
||||||
|
"System.Diagnostics.FileVersionInfo": "(,4.3.32767]",
|
||||||
|
"System.Diagnostics.Process": "(,4.3.32767]",
|
||||||
|
"System.Diagnostics.StackTrace": "(,4.3.32767]",
|
||||||
|
"System.Diagnostics.TextWriterTraceListener": "(,4.3.32767]",
|
||||||
|
"System.Diagnostics.Tools": "(,4.3.32767]",
|
||||||
|
"System.Diagnostics.TraceSource": "(,4.3.32767]",
|
||||||
|
"System.Diagnostics.Tracing": "(,4.3.32767]",
|
||||||
|
"System.Drawing.Primitives": "(,4.3.32767]",
|
||||||
|
"System.Dynamic.Runtime": "(,4.3.32767]",
|
||||||
|
"System.Formats.Asn1": "(,10.0.32767]",
|
||||||
|
"System.Formats.Tar": "(,10.0.32767]",
|
||||||
|
"System.Globalization": "(,4.3.32767]",
|
||||||
|
"System.Globalization.Calendars": "(,4.3.32767]",
|
||||||
|
"System.Globalization.Extensions": "(,4.3.32767]",
|
||||||
|
"System.IO": "(,4.3.32767]",
|
||||||
|
"System.IO.Compression": "(,4.3.32767]",
|
||||||
|
"System.IO.Compression.ZipFile": "(,4.3.32767]",
|
||||||
|
"System.IO.FileSystem": "(,4.3.32767]",
|
||||||
|
"System.IO.FileSystem.AccessControl": "(,4.4.32767]",
|
||||||
|
"System.IO.FileSystem.DriveInfo": "(,4.3.32767]",
|
||||||
|
"System.IO.FileSystem.Primitives": "(,4.3.32767]",
|
||||||
|
"System.IO.FileSystem.Watcher": "(,4.3.32767]",
|
||||||
|
"System.IO.IsolatedStorage": "(,4.3.32767]",
|
||||||
|
"System.IO.MemoryMappedFiles": "(,4.3.32767]",
|
||||||
|
"System.IO.Pipelines": "(,10.0.32767]",
|
||||||
|
"System.IO.Pipes": "(,4.3.32767]",
|
||||||
|
"System.IO.Pipes.AccessControl": "(,5.0.32767]",
|
||||||
|
"System.IO.UnmanagedMemoryStream": "(,4.3.32767]",
|
||||||
|
"System.Linq": "(,4.3.32767]",
|
||||||
|
"System.Linq.AsyncEnumerable": "(,10.0.32767]",
|
||||||
|
"System.Linq.Expressions": "(,4.3.32767]",
|
||||||
|
"System.Linq.Parallel": "(,4.3.32767]",
|
||||||
|
"System.Linq.Queryable": "(,4.3.32767]",
|
||||||
|
"System.Memory": "(,5.0.32767]",
|
||||||
|
"System.Net.Http": "(,4.3.32767]",
|
||||||
|
"System.Net.Http.Json": "(,10.0.32767]",
|
||||||
|
"System.Net.NameResolution": "(,4.3.32767]",
|
||||||
|
"System.Net.NetworkInformation": "(,4.3.32767]",
|
||||||
|
"System.Net.Ping": "(,4.3.32767]",
|
||||||
|
"System.Net.Primitives": "(,4.3.32767]",
|
||||||
|
"System.Net.Requests": "(,4.3.32767]",
|
||||||
|
"System.Net.Security": "(,4.3.32767]",
|
||||||
|
"System.Net.ServerSentEvents": "(,10.0.32767]",
|
||||||
|
"System.Net.Sockets": "(,4.3.32767]",
|
||||||
|
"System.Net.WebHeaderCollection": "(,4.3.32767]",
|
||||||
|
"System.Net.WebSockets": "(,4.3.32767]",
|
||||||
|
"System.Net.WebSockets.Client": "(,4.3.32767]",
|
||||||
|
"System.Numerics.Vectors": "(,5.0.32767]",
|
||||||
|
"System.ObjectModel": "(,4.3.32767]",
|
||||||
|
"System.Private.DataContractSerialization": "(,4.3.32767]",
|
||||||
|
"System.Private.Uri": "(,4.3.32767]",
|
||||||
|
"System.Reflection": "(,4.3.32767]",
|
||||||
|
"System.Reflection.DispatchProxy": "(,6.0.32767]",
|
||||||
|
"System.Reflection.Emit": "(,4.7.32767]",
|
||||||
|
"System.Reflection.Emit.ILGeneration": "(,4.7.32767]",
|
||||||
|
"System.Reflection.Emit.Lightweight": "(,4.7.32767]",
|
||||||
|
"System.Reflection.Extensions": "(,4.3.32767]",
|
||||||
|
"System.Reflection.Metadata": "(,10.0.32767]",
|
||||||
|
"System.Reflection.Primitives": "(,4.3.32767]",
|
||||||
|
"System.Reflection.TypeExtensions": "(,4.3.32767]",
|
||||||
|
"System.Resources.Reader": "(,4.3.32767]",
|
||||||
|
"System.Resources.ResourceManager": "(,4.3.32767]",
|
||||||
|
"System.Resources.Writer": "(,4.3.32767]",
|
||||||
|
"System.Runtime": "(,4.3.32767]",
|
||||||
|
"System.Runtime.CompilerServices.Unsafe": "(,7.0.32767]",
|
||||||
|
"System.Runtime.CompilerServices.VisualC": "(,4.3.32767]",
|
||||||
|
"System.Runtime.Extensions": "(,4.3.32767]",
|
||||||
|
"System.Runtime.Handles": "(,4.3.32767]",
|
||||||
|
"System.Runtime.InteropServices": "(,4.3.32767]",
|
||||||
|
"System.Runtime.InteropServices.RuntimeInformation": "(,4.3.32767]",
|
||||||
|
"System.Runtime.Loader": "(,4.3.32767]",
|
||||||
|
"System.Runtime.Numerics": "(,4.3.32767]",
|
||||||
|
"System.Runtime.Serialization.Formatters": "(,4.3.32767]",
|
||||||
|
"System.Runtime.Serialization.Json": "(,4.3.32767]",
|
||||||
|
"System.Runtime.Serialization.Primitives": "(,4.3.32767]",
|
||||||
|
"System.Runtime.Serialization.Xml": "(,4.3.32767]",
|
||||||
|
"System.Security.AccessControl": "(,6.0.32767]",
|
||||||
|
"System.Security.Claims": "(,4.3.32767]",
|
||||||
|
"System.Security.Cryptography.Algorithms": "(,4.3.32767]",
|
||||||
|
"System.Security.Cryptography.Cng": "(,5.0.32767]",
|
||||||
|
"System.Security.Cryptography.Csp": "(,4.3.32767]",
|
||||||
|
"System.Security.Cryptography.Encoding": "(,4.3.32767]",
|
||||||
|
"System.Security.Cryptography.OpenSsl": "(,5.0.32767]",
|
||||||
|
"System.Security.Cryptography.Primitives": "(,4.3.32767]",
|
||||||
|
"System.Security.Cryptography.X509Certificates": "(,4.3.32767]",
|
||||||
|
"System.Security.Principal": "(,4.3.32767]",
|
||||||
|
"System.Security.Principal.Windows": "(,5.0.32767]",
|
||||||
|
"System.Security.SecureString": "(,4.3.32767]",
|
||||||
|
"System.Text.Encoding": "(,4.3.32767]",
|
||||||
|
"System.Text.Encoding.CodePages": "(,10.0.32767]",
|
||||||
|
"System.Text.Encoding.Extensions": "(,4.3.32767]",
|
||||||
|
"System.Text.Encodings.Web": "(,10.0.32767]",
|
||||||
|
"System.Text.Json": "(,10.0.32767]",
|
||||||
|
"System.Text.RegularExpressions": "(,4.3.32767]",
|
||||||
|
"System.Threading": "(,4.3.32767]",
|
||||||
|
"System.Threading.AccessControl": "(,10.0.32767]",
|
||||||
|
"System.Threading.Channels": "(,10.0.32767]",
|
||||||
|
"System.Threading.Overlapped": "(,4.3.32767]",
|
||||||
|
"System.Threading.Tasks": "(,4.3.32767]",
|
||||||
|
"System.Threading.Tasks.Dataflow": "(,10.0.32767]",
|
||||||
|
"System.Threading.Tasks.Extensions": "(,5.0.32767]",
|
||||||
|
"System.Threading.Tasks.Parallel": "(,4.3.32767]",
|
||||||
|
"System.Threading.Thread": "(,4.3.32767]",
|
||||||
|
"System.Threading.ThreadPool": "(,4.3.32767]",
|
||||||
|
"System.Threading.Timer": "(,4.3.32767]",
|
||||||
|
"System.ValueTuple": "(,4.5.32767]",
|
||||||
|
"System.Xml.ReaderWriter": "(,4.3.32767]",
|
||||||
|
"System.Xml.XDocument": "(,4.3.32767]",
|
||||||
|
"System.Xml.XmlDocument": "(,4.3.32767]",
|
||||||
|
"System.Xml.XmlSerializer": "(,4.3.32767]",
|
||||||
|
"System.Xml.XPath": "(,4.3.32767]",
|
||||||
|
"System.Xml.XPath.XDocument": "(,5.0.32767]"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8" standalone="no"?>
|
||||||
|
<Project ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
|
<PropertyGroup Condition=" '$(ExcludeRestorePackageImports)' != 'true' ">
|
||||||
|
<RestoreSuccess Condition=" '$(RestoreSuccess)' == '' ">True</RestoreSuccess>
|
||||||
|
<RestoreTool Condition=" '$(RestoreTool)' == '' ">NuGet</RestoreTool>
|
||||||
|
<ProjectAssetsFile Condition=" '$(ProjectAssetsFile)' == '' ">$(MSBuildThisFileDirectory)project.assets.json</ProjectAssetsFile>
|
||||||
|
<NuGetPackageRoot Condition=" '$(NuGetPackageRoot)' == '' ">/Users/dohertj2/.nuget/packages/</NuGetPackageRoot>
|
||||||
|
<NuGetPackageFolders Condition=" '$(NuGetPackageFolders)' == '' ">/Users/dohertj2/.nuget/packages/</NuGetPackageFolders>
|
||||||
|
<NuGetProjectStyle Condition=" '$(NuGetProjectStyle)' == '' ">PackageReference</NuGetProjectStyle>
|
||||||
|
<NuGetToolVersion Condition=" '$(NuGetToolVersion)' == '' ">7.0.0</NuGetToolVersion>
|
||||||
|
</PropertyGroup>
|
||||||
|
<ItemGroup Condition=" '$(ExcludeRestorePackageImports)' != 'true' ">
|
||||||
|
<SourceRoot Include="/Users/dohertj2/.nuget/packages/" />
|
||||||
|
</ItemGroup>
|
||||||
|
</Project>
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8" standalone="no"?>
|
||||||
|
<Project ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
|
<ImportGroup Condition=" '$(ExcludeRestorePackageImports)' != 'true' ">
|
||||||
|
<Import Project="$(NuGetPackageRoot)microsoft.extensions.options/10.0.3/buildTransitive/net8.0/Microsoft.Extensions.Options.targets" Condition="Exists('$(NuGetPackageRoot)microsoft.extensions.options/10.0.3/buildTransitive/net8.0/Microsoft.Extensions.Options.targets')" />
|
||||||
|
<Import Project="$(NuGetPackageRoot)microsoft.extensions.logging.abstractions/10.0.3/buildTransitive/net8.0/Microsoft.Extensions.Logging.Abstractions.targets" Condition="Exists('$(NuGetPackageRoot)microsoft.extensions.logging.abstractions/10.0.3/buildTransitive/net8.0/Microsoft.Extensions.Logging.Abstractions.targets')" />
|
||||||
|
<Import Project="$(NuGetPackageRoot)microsoft.extensions.configuration.binder/10.0.3/buildTransitive/netstandard2.0/Microsoft.Extensions.Configuration.Binder.targets" Condition="Exists('$(NuGetPackageRoot)microsoft.extensions.configuration.binder/10.0.3/buildTransitive/netstandard2.0/Microsoft.Extensions.Configuration.Binder.targets')" />
|
||||||
|
</ImportGroup>
|
||||||
|
</Project>
|
||||||
1142
dotnet/src/ZB.MOM.NatsNet.Server/obj/project.assets.json
Normal file
1142
dotnet/src/ZB.MOM.NatsNet.Server/obj/project.assets.json
Normal file
File diff suppressed because it is too large
Load Diff
24
dotnet/src/ZB.MOM.NatsNet.Server/obj/project.nuget.cache
Normal file
24
dotnet/src/ZB.MOM.NatsNet.Server/obj/project.nuget.cache
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
{
|
||||||
|
"version": 2,
|
||||||
|
"dgSpecHash": "CEza21v8hjw=",
|
||||||
|
"success": true,
|
||||||
|
"projectFilePath": "/Users/dohertj2/Desktop/natsnet-batch24-exec/dotnet/src/ZB.MOM.NatsNet.Server/ZB.MOM.NatsNet.Server.csproj",
|
||||||
|
"expectedPackageFiles": [
|
||||||
|
"/Users/dohertj2/.nuget/packages/bcrypt.net-next/4.1.0/bcrypt.net-next.4.1.0.nupkg.sha512",
|
||||||
|
"/Users/dohertj2/.nuget/packages/ironsnappy/1.3.1/ironsnappy.1.3.1.nupkg.sha512",
|
||||||
|
"/Users/dohertj2/.nuget/packages/microsoft.extensions.configuration/10.0.3/microsoft.extensions.configuration.10.0.3.nupkg.sha512",
|
||||||
|
"/Users/dohertj2/.nuget/packages/microsoft.extensions.configuration.abstractions/10.0.3/microsoft.extensions.configuration.abstractions.10.0.3.nupkg.sha512",
|
||||||
|
"/Users/dohertj2/.nuget/packages/microsoft.extensions.configuration.binder/10.0.3/microsoft.extensions.configuration.binder.10.0.3.nupkg.sha512",
|
||||||
|
"/Users/dohertj2/.nuget/packages/microsoft.extensions.configuration.fileextensions/10.0.3/microsoft.extensions.configuration.fileextensions.10.0.3.nupkg.sha512",
|
||||||
|
"/Users/dohertj2/.nuget/packages/microsoft.extensions.configuration.json/10.0.3/microsoft.extensions.configuration.json.10.0.3.nupkg.sha512",
|
||||||
|
"/Users/dohertj2/.nuget/packages/microsoft.extensions.dependencyinjection.abstractions/10.0.3/microsoft.extensions.dependencyinjection.abstractions.10.0.3.nupkg.sha512",
|
||||||
|
"/Users/dohertj2/.nuget/packages/microsoft.extensions.fileproviders.abstractions/10.0.3/microsoft.extensions.fileproviders.abstractions.10.0.3.nupkg.sha512",
|
||||||
|
"/Users/dohertj2/.nuget/packages/microsoft.extensions.fileproviders.physical/10.0.3/microsoft.extensions.fileproviders.physical.10.0.3.nupkg.sha512",
|
||||||
|
"/Users/dohertj2/.nuget/packages/microsoft.extensions.filesystemglobbing/10.0.3/microsoft.extensions.filesystemglobbing.10.0.3.nupkg.sha512",
|
||||||
|
"/Users/dohertj2/.nuget/packages/microsoft.extensions.logging.abstractions/10.0.3/microsoft.extensions.logging.abstractions.10.0.3.nupkg.sha512",
|
||||||
|
"/Users/dohertj2/.nuget/packages/microsoft.extensions.options/10.0.3/microsoft.extensions.options.10.0.3.nupkg.sha512",
|
||||||
|
"/Users/dohertj2/.nuget/packages/microsoft.extensions.primitives/10.0.3/microsoft.extensions.primitives.10.0.3.nupkg.sha512",
|
||||||
|
"/Users/dohertj2/.nuget/packages/nats.nkeys/1.0.0-preview.3/nats.nkeys.1.0.0-preview.3.nupkg.sha512"
|
||||||
|
],
|
||||||
|
"logs": []
|
||||||
|
}
|
||||||
@@ -171,4 +171,22 @@ public sealed partial class LeafNodeHandlerTests
|
|||||||
((INatsAccount)account).RemoveClient(leaf);
|
((INatsAccount)account).RemoveClient(leaf);
|
||||||
account.NumLocalLeafNodes().ShouldBe(0);
|
account.NumLocalLeafNodes().ShouldBe(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact] // T:1966
|
||||||
|
public void LeafNodeRoutedSubKeyDifferentBetweenLeafSubAndRoutedSub_ShouldSucceed()
|
||||||
|
{
|
||||||
|
var plain = new Subscription { Subject = "foo"u8.ToArray() };
|
||||||
|
var queue = new Subscription { Subject = "XYZ"u8.ToArray(), Queue = "foo"u8.ToArray() };
|
||||||
|
|
||||||
|
var routedPlain = LeafNodeHandler.KeyFromSubWithOrigin(plain);
|
||||||
|
var routedQueue = LeafNodeHandler.KeyFromSubWithOrigin(queue);
|
||||||
|
var leafPlain = LeafNodeHandler.KeyFromSubWithOrigin(plain, "XYZ");
|
||||||
|
var leafQueue = LeafNodeHandler.KeyFromSubWithOrigin(queue, "XYZ");
|
||||||
|
|
||||||
|
routedPlain.ShouldNotBe(routedQueue);
|
||||||
|
routedQueue.ShouldNotBe(leafPlain);
|
||||||
|
leafPlain.ShouldNotBe(leafQueue);
|
||||||
|
routedPlain.ShouldNotBe(leafPlain);
|
||||||
|
routedQueue.ShouldNotBe(leafQueue);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -204,4 +204,31 @@ public sealed partial class RouteHandlerTests
|
|||||||
public Task<string[]> LookupHostAsync(string host, CancellationToken ct = default)
|
public Task<string[]> LookupHostAsync(string host, CancellationToken ct = default)
|
||||||
=> Task.FromResult(hosts);
|
=> Task.FromResult(hosts);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact] // T:2825
|
||||||
|
public void ClusterQueueGroupWeightTrackingLeak_ShouldSucceed()
|
||||||
|
{
|
||||||
|
var account = Account.NewAccount("$G");
|
||||||
|
var queueSub = new Subscription
|
||||||
|
{
|
||||||
|
Subject = "foo"u8.ToArray(),
|
||||||
|
Queue = "bar"u8.ToArray(),
|
||||||
|
Qw = 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
account.UpdateLeafNodes(queueSub, 1);
|
||||||
|
|
||||||
|
var lqwsField = typeof(Account).GetField("_lqws", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic);
|
||||||
|
lqwsField.ShouldNotBeNull();
|
||||||
|
|
||||||
|
var lqws = (Dictionary<string, int>?)lqwsField!.GetValue(account);
|
||||||
|
lqws.ShouldNotBeNull();
|
||||||
|
lqws!.TryGetValue("foo bar", out var initialWeight).ShouldBeTrue();
|
||||||
|
initialWeight.ShouldBe(1);
|
||||||
|
|
||||||
|
account.UpdateLeafNodes(queueSub, -1);
|
||||||
|
|
||||||
|
lqws = (Dictionary<string, int>?)lqwsField.GetValue(account);
|
||||||
|
(lqws?.ContainsKey("foo bar") ?? false).ShouldBeFalse();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
BIN
porting.db
BIN
porting.db
Binary file not shown.
@@ -1,6 +1,6 @@
|
|||||||
# NATS .NET Porting Status Report
|
# NATS .NET Porting Status Report
|
||||||
|
|
||||||
Generated: 2026-03-01 06:13:51 UTC
|
Generated: 2026-03-01 06:31:33 UTC
|
||||||
|
|
||||||
## Modules (12 total)
|
## Modules (12 total)
|
||||||
|
|
||||||
@@ -13,18 +13,18 @@ Generated: 2026-03-01 06:13:51 UTC
|
|||||||
| Status | Count |
|
| Status | Count |
|
||||||
|--------|-------|
|
|--------|-------|
|
||||||
| complete | 22 |
|
| complete | 22 |
|
||||||
| deferred | 927 |
|
| deferred | 860 |
|
||||||
| n_a | 24 |
|
| n_a | 24 |
|
||||||
| stub | 1 |
|
| stub | 1 |
|
||||||
| verified | 2699 |
|
| verified | 2766 |
|
||||||
|
|
||||||
## Unit Tests (3257 total)
|
## Unit Tests (3257 total)
|
||||||
|
|
||||||
| Status | Count |
|
| Status | Count |
|
||||||
|--------|-------|
|
|--------|-------|
|
||||||
| deferred | 1147 |
|
| deferred | 1145 |
|
||||||
| n_a | 307 |
|
| n_a | 307 |
|
||||||
| verified | 1803 |
|
| verified | 1805 |
|
||||||
|
|
||||||
## Library Mappings (36 total)
|
## Library Mappings (36 total)
|
||||||
|
|
||||||
@@ -35,4 +35,4 @@ Generated: 2026-03-01 06:13:51 UTC
|
|||||||
|
|
||||||
## Overall Progress
|
## Overall Progress
|
||||||
|
|
||||||
**4867/6942 items complete (70.1%)**
|
**4936/6942 items complete (71.1%)**
|
||||||
|
|||||||
Reference in New Issue
Block a user