feat: add WebSocket protocol constants (RFC 6455)

Port WsConstants from golang/nats-server/server/websocket.go lines 41-106.
Includes opcodes, frame header bits, close status codes, compression
constants, header names, path routing, and the WsClientKind enum.
This commit is contained in:
Joseph Doherty
2026-02-23 04:33:04 -05:00
parent 708e1b4168
commit 72f60054ed
2 changed files with 131 additions and 0 deletions

View File

@@ -0,0 +1,78 @@
namespace NATS.Server.WebSocket;
/// <summary>
/// WebSocket protocol constants (RFC 6455).
/// Ported from golang/nats-server/server/websocket.go lines 41-106.
/// </summary>
public static class WsConstants
{
// Opcodes (RFC 6455 Section 5.2)
public const int TextMessage = 1;
public const int BinaryMessage = 2;
public const int CloseMessage = 8;
public const int PingMessage = 9;
public const int PongMessage = 10;
public const int ContinuationFrame = 0;
// Frame header bits
public const byte FinalBit = 0x80; // 1 << 7
public const byte Rsv1Bit = 0x40; // 1 << 6 (compression, RFC 7692)
public const byte Rsv2Bit = 0x20; // 1 << 5
public const byte Rsv3Bit = 0x10; // 1 << 4
public const byte MaskBit = 0x80; // 1 << 7 (in second byte)
// Frame size limits
public const int MaxFrameHeaderSize = 14;
public const int MaxControlPayloadSize = 125;
public const int FrameSizeForBrowsers = 4096;
public const int CompressThreshold = 64;
public const int CloseStatusSize = 2;
// Close status codes (RFC 6455 Section 11.7)
public const int CloseStatusNormalClosure = 1000;
public const int CloseStatusGoingAway = 1001;
public const int CloseStatusProtocolError = 1002;
public const int CloseStatusUnsupportedData = 1003;
public const int CloseStatusNoStatusReceived = 1005;
public const int CloseStatusInvalidPayloadData = 1007;
public const int CloseStatusPolicyViolation = 1008;
public const int CloseStatusMessageTooBig = 1009;
public const int CloseStatusInternalSrvError = 1011;
public const int CloseStatusTlsHandshake = 1015;
// Compression constants (RFC 7692)
public const string PmcExtension = "permessage-deflate";
public const string PmcSrvNoCtx = "server_no_context_takeover";
public const string PmcCliNoCtx = "client_no_context_takeover";
public static readonly string PmcReqHeaderValue = $"{PmcExtension}; {PmcSrvNoCtx}; {PmcCliNoCtx}";
public static readonly string PmcFullResponse = $"Sec-WebSocket-Extensions: {PmcExtension}; {PmcSrvNoCtx}; {PmcCliNoCtx}\r\n";
// Header names
public const string NoMaskingHeader = "Nats-No-Masking";
public const string NoMaskingValue = "true";
public static readonly string NoMaskingFullResponse = $"{NoMaskingHeader}: {NoMaskingValue}\r\n";
public const string XForwardedForHeader = "X-Forwarded-For";
// Path routing
public const string ClientPath = "/";
public const string LeafNodePath = "/leafnode";
public const string MqttPath = "/mqtt";
// WebSocket GUID (RFC 6455 Section 1.3)
public static readonly byte[] WsGuid = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"u8.ToArray();
// Compression trailer (RFC 7692 Section 7.2.2)
public static readonly byte[] CompressLastBlock = [0x00, 0x00, 0xff, 0xff, 0x01, 0x00, 0x00, 0xff, 0xff];
// Decompression trailer appended before decompressing
public static readonly byte[] DecompressTrailer = [0x00, 0x00, 0xff, 0xff];
public static bool IsControlFrame(int opcode) => opcode >= CloseMessage;
}
public enum WsClientKind
{
Client,
Leaf,
Mqtt,
}