feat(batch24): complete leaf nodes implementation and verification

This commit is contained in:
Joseph Doherty
2026-03-01 01:31:57 -05:00
parent ff7e674ec4
commit 3e9ad16033
36 changed files with 3427 additions and 21 deletions

View File

@@ -1064,18 +1064,130 @@ public static class ProtocolParser
// =====================================================================
/// <summary>
/// Parses leaf MSG arguments. Same format as routed MSG args.
/// Stub — will be fully implemented with leaf node support.
/// Parses leaf MSG arguments:
/// <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>
public static Exception? ProcessLeafMsgArgs(ParseContext c, byte[] arg) =>
ProcessRoutedMsgArgs(c, arg);
public static Exception? ProcessLeafMsgArgs(ParseContext c, byte[] 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>
/// Parses leaf HMSG arguments. Same format as routed header MSG args.
/// Stub — will be fully implemented with leaf node support.
/// Parses leaf HMSG arguments:
/// <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>
public static Exception? ProcessLeafHeaderMsgArgs(ParseContext c, byte[] arg) =>
ProcessRoutedHeaderMsgArgs(c, arg);
public static Exception? ProcessLeafHeaderMsgArgs(ParseContext c, byte[] 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>
/// Parses LMSG arguments (origin cluster routed messages).