/** * @file WSFrame.h * @brief WebSocket frame constants, opcode definitions, and frame-building helpers. * * RFC 6455 WebSocket framing layer utilities. */ #ifndef STREAMHUB_WSFRAME_H_ #define STREAMHUB_WSFRAME_H_ #include "CompilerTypes.h" #include namespace StreamHub { using MARTe::uint8; using MARTe::uint16; using MARTe::uint32; using MARTe::uint64; /*---------------------------------------------------------------------------*/ /* Opcodes */ /*---------------------------------------------------------------------------*/ static const uint8 WS_OPCODE_CONTINUATION = 0x00u; static const uint8 WS_OPCODE_TEXT = 0x01u; static const uint8 WS_OPCODE_BINARY = 0x02u; static const uint8 WS_OPCODE_CLOSE = 0x08u; static const uint8 WS_OPCODE_PING = 0x09u; static const uint8 WS_OPCODE_PONG = 0x0Au; static const uint8 WS_FIN_BIT = 0x80u; static const uint8 WS_MASK_BIT = 0x80u; /*---------------------------------------------------------------------------*/ /* Frame encoder (server → client, no masking) */ /*---------------------------------------------------------------------------*/ /** * @brief Encode a WebSocket frame header into @p buf. * @param buf Output buffer (must have at least 10 bytes available). * @param opcode WS_OPCODE_TEXT or WS_OPCODE_BINARY. * @param payloadLen Number of payload bytes that follow the header. * @return Number of header bytes written (2, 4, or 10). */ inline uint32 WSEncodeHeader(uint8 *buf, uint8 opcode, uint64 payloadLen) { buf[0] = WS_FIN_BIT | opcode; if (payloadLen < 126u) { buf[1] = static_cast(payloadLen); return 2u; } else if (payloadLen < 65536u) { buf[1] = 126u; buf[2] = static_cast((payloadLen >> 8u) & 0xFFu); buf[3] = static_cast(payloadLen & 0xFFu); return 4u; } else { buf[1] = 127u; for (int i = 7; i >= 0; i--) { buf[2 + i] = static_cast(payloadLen & 0xFFu); payloadLen >>= 8u; } return 10u; } } /*---------------------------------------------------------------------------*/ /* Frame decoder (client → server, with masking) */ /*---------------------------------------------------------------------------*/ /** * @brief Parsed fields from a received WebSocket frame header. */ struct WSFrameHeader { bool fin; uint8 opcode; bool masked; uint64 payloadLen; uint8 maskKey[4]; uint32 headerSize; ///< Total header byte count (2 + 0|2|8 length + 0|4 mask) }; /** * @brief Try to parse a WebSocket frame header from @p buf (bufLen available bytes). * @param buf Received bytes. * @param bufLen Number of bytes available. * @param hdr Output: parsed header fields. * @return true if a complete header was parsed; false if more bytes are needed. */ inline bool WSParseHeader(const uint8 *buf, uint32 bufLen, WSFrameHeader &hdr) { if (bufLen < 2u) { return false; } hdr.fin = (buf[0] & WS_FIN_BIT) != 0u; hdr.opcode = buf[0] & 0x0Fu; hdr.masked = (buf[1] & WS_MASK_BIT) != 0u; uint32 hdrSize = 2u; uint64 plen = static_cast(buf[1] & 0x7Fu); if (plen == 126u) { if (bufLen < 4u) { return false; } plen = (static_cast(buf[2]) << 8u) | static_cast(buf[3]); hdrSize += 2u; } else if (plen == 127u) { if (bufLen < 10u) { return false; } plen = 0u; for (int i = 0; i < 8; i++) { plen = (plen << 8u) | static_cast(buf[2 + i]); } hdrSize += 8u; } if (hdr.masked) { if (bufLen < hdrSize + 4u) { return false; } hdr.maskKey[0] = buf[hdrSize]; hdr.maskKey[1] = buf[hdrSize + 1u]; hdr.maskKey[2] = buf[hdrSize + 2u]; hdr.maskKey[3] = buf[hdrSize + 3u]; hdrSize += 4u; } else { hdr.maskKey[0] = hdr.maskKey[1] = hdr.maskKey[2] = hdr.maskKey[3] = 0u; } hdr.payloadLen = plen; hdr.headerSize = hdrSize; return true; } /** * @brief XOR-unmask a masked WebSocket payload in place. * @param payload Payload buffer. * @param len Payload length. * @param mask 4-byte masking key. */ inline void WSUnmask(uint8 *payload, uint32 len, const uint8 mask[4]) { for (uint32 i = 0u; i < len; i++) { payload[i] ^= mask[i % 4u]; } } } /* namespace StreamHub */ #endif /* STREAMHUB_WSFRAME_H_ */