// Copyright Epic Games, Inc. All Rights Reserved. #pragma once #include #include #include #include #include #include #include #include namespace zen { /** * Result of attempting to parse a single WebSocket frame from a byte buffer */ struct WsFrameParseResult { bool IsValid = false; // true if a complete frame was successfully parsed size_t BytesConsumed = 0; // number of bytes consumed from the input buffer WebSocketOpcode Opcode = WebSocketOpcode::kText; bool Fin = false; std::vector Payload; }; /** * RFC 6455 WebSocket frame codec * * Provides static helpers for parsing client-to-server frames (which are * always masked) and building server-to-client frames (which are never masked). */ struct WsFrameCodec { /** * Try to parse one complete frame from the front of the buffer. * * Returns a result with IsValid == false and BytesConsumed == 0 when * there is not enough data yet. The caller should accumulate more data * and retry. */ static WsFrameParseResult TryParseFrame(const uint8_t* Data, size_t Size); /** * Build a server-to-client frame (no masking) */ static std::vector BuildFrame(WebSocketOpcode Opcode, std::span Payload); /** * Build a close frame with a status code and optional reason string */ static std::vector BuildCloseFrame(uint16_t Code, std::string_view Reason = {}); /** * Build a client-to-server frame (with masking per RFC 6455) */ static std::vector BuildMaskedFrame(WebSocketOpcode Opcode, std::span Payload); /** * Build a masked close frame with status code and optional reason */ static std::vector BuildMaskedCloseFrame(uint16_t Code, std::string_view Reason = {}); /** * Compute the Sec-WebSocket-Accept value per RFC 6455 section 4.2.2 * * accept = Base64(SHA1(clientKey + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11")) */ static std::string ComputeAcceptKey(std::string_view ClientKey); }; } // namespace zen