using System.Text; namespace NATS.Server.Protocol; public readonly struct NatsHeaders { public int Status { get; init; } public string Description { get; init; } public Dictionary Headers { get; init; } public static readonly NatsHeaders Invalid = new() { Status = -1, Description = string.Empty, Headers = new() }; } public static class NatsHeaderParser { private static readonly byte[] CrLf = "\r\n"u8.ToArray(); private static readonly byte[] Prefix = "NATS/1.0"u8.ToArray(); public static NatsHeaders Parse(ReadOnlySpan data) { if (data.Length < Prefix.Length) return NatsHeaders.Invalid; if (!data[..Prefix.Length].SequenceEqual(Prefix)) return NatsHeaders.Invalid; int pos = Prefix.Length; int status = 0; string description = string.Empty; // Parse status line: NATS/1.0[ status[ description]]\r\n int lineEnd = data[pos..].IndexOf(CrLf); if (lineEnd < 0) return NatsHeaders.Invalid; var statusLine = data[pos..(pos + lineEnd)]; pos += lineEnd + 2; // skip \r\n if (statusLine.Length > 0) { int si = 0; while (si < statusLine.Length && statusLine[si] == (byte)' ') si++; int numStart = si; while (si < statusLine.Length && statusLine[si] >= (byte)'0' && statusLine[si] <= (byte)'9') si++; if (si > numStart) { status = int.Parse(Encoding.ASCII.GetString(statusLine[numStart..si])); while (si < statusLine.Length && statusLine[si] == (byte)' ') si++; if (si < statusLine.Length) description = Encoding.ASCII.GetString(statusLine[si..]); } } // Parse key-value headers until empty line var headers = new Dictionary>(StringComparer.OrdinalIgnoreCase); while (pos < data.Length) { var remaining = data[pos..]; if (remaining.Length >= 2 && remaining[0] == (byte)'\r' && remaining[1] == (byte)'\n') break; lineEnd = remaining.IndexOf(CrLf); if (lineEnd < 0) break; var headerLine = remaining[..lineEnd]; pos += lineEnd + 2; int colon = headerLine.IndexOf((byte)':'); if (colon < 0) continue; var key = Encoding.ASCII.GetString(headerLine[..colon]).Trim(); var value = Encoding.ASCII.GetString(headerLine[(colon + 1)..]).Trim(); if (!headers.TryGetValue(key, out var values)) { values = []; headers[key] = values; } values.Add(value); } var result = new Dictionary(headers.Count, StringComparer.OrdinalIgnoreCase); foreach (var (k, v) in headers) result[k] = v.ToArray(); return new NatsHeaders { Status = status, Description = description, Headers = result, }; } }