using System; using ZB.MOM.WW.MxGateway.Contracts; using ZB.MOM.WW.MxGateway.Worker.Bootstrap; namespace ZB.MOM.WW.MxGateway.Worker.Ipc; /// Configuration options for the worker frame protocol. public sealed class WorkerFrameProtocolOptions { /// Default maximum message size in bytes (16 MB). public const int DefaultMaxMessageBytes = 16 * 1024 * 1024; /// /// Upper ceiling the worker will accept for a gateway-negotiated frame maximum /// (GatewayHello.max_frame_bytes). Matches the gateway's own configuration ceiling /// so a nonsensical negotiated value is rejected at the handshake rather than driving an absurd /// per-frame allocation. 256 MiB. /// public const int MaxNegotiableFrameBytes = 256 * 1024 * 1024; /// Initializes a new instance of the WorkerFrameProtocolOptions class from WorkerOptions. /// Worker initialization options. public WorkerFrameProtocolOptions(WorkerOptions options) : this( options?.SessionId ?? throw new ArgumentNullException(nameof(options)), options.ProtocolVersion, options.Nonce, DefaultMaxMessageBytes) { } /// Initializes a new instance of the WorkerFrameProtocolOptions class with default max message bytes. /// Identifier of the session. /// Protocol version. /// Nonce for startup validation. public WorkerFrameProtocolOptions( string sessionId, uint protocolVersion, string nonce) : this( sessionId, protocolVersion, nonce, DefaultMaxMessageBytes) { } /// Initializes a new instance of the WorkerFrameProtocolOptions class with all parameters. /// Identifier of the session. /// Protocol version. /// Nonce for startup validation. /// Maximum message size in bytes. public WorkerFrameProtocolOptions( string sessionId, uint protocolVersion, string nonce, int maxMessageBytes) { if (string.IsNullOrWhiteSpace(sessionId)) { throw new WorkerFrameProtocolException( WorkerFrameProtocolErrorCode.InvalidConfiguration, "Worker frame protocol requires a session id."); } if (protocolVersion == 0) { throw new WorkerFrameProtocolException( WorkerFrameProtocolErrorCode.InvalidConfiguration, "Worker frame protocol requires a non-zero protocol version."); } if (protocolVersion != GatewayContractInfo.WorkerProtocolVersion) { throw new WorkerFrameProtocolException( WorkerFrameProtocolErrorCode.ProtocolVersionMismatch, $"Worker frame protocol version {protocolVersion} is not supported."); } if (string.IsNullOrWhiteSpace(nonce)) { throw new WorkerFrameProtocolException( WorkerFrameProtocolErrorCode.InvalidConfiguration, "Worker frame protocol requires a nonce."); } if (maxMessageBytes <= 0) { throw new WorkerFrameProtocolException( WorkerFrameProtocolErrorCode.InvalidConfiguration, "Worker frame protocol max message size must be greater than zero."); } SessionId = sessionId; ProtocolVersion = protocolVersion; Nonce = nonce; MaxMessageBytes = maxMessageBytes; } /// Gets the session ID for the worker protocol. public string SessionId { get; } /// Gets the protocol version. public uint ProtocolVersion { get; } /// Gets the nonce for startup validation. public string Nonce { get; } /// /// Gets the maximum worker-frame message size in bytes. Initialized from the constructor and /// then adopted once from the gateway-negotiated value during the startup handshake /// (GatewayHello.max_frame_bytes) via , /// before the message loop starts. Not mutated afterwards, so the single-threaded handshake write /// is safe for the reader/writer that share this instance. /// public int MaxMessageBytes { get; private set; } /// /// Adopts the gateway-negotiated frame maximum conveyed in GatewayHello.max_frame_bytes. /// A value of 0 (an older gateway that never set the field) is ignored and the /// constructor default is kept. A value above is rejected. /// /// The gateway-negotiated maximum, or 0 for "keep default". internal void AdoptNegotiatedMaxMessageBytes(uint negotiatedMaxFrameBytes) { if (negotiatedMaxFrameBytes == 0) { return; } if (negotiatedMaxFrameBytes > MaxNegotiableFrameBytes) { throw new WorkerFrameProtocolException( WorkerFrameProtocolErrorCode.InvalidConfiguration, $"GatewayHello negotiated frame maximum {negotiatedMaxFrameBytes} exceeds the worker ceiling " + $"of {MaxNegotiableFrameBytes} bytes."); } MaxMessageBytes = (int)negotiatedMaxFrameBytes; } }