143 lines
4.5 KiB
C++
143 lines
4.5 KiB
C++
/**
|
|
* @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 <string.h>
|
|
|
|
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<uint8>(payloadLen);
|
|
return 2u;
|
|
} else if (payloadLen < 65536u) {
|
|
buf[1] = 126u;
|
|
buf[2] = static_cast<uint8>((payloadLen >> 8u) & 0xFFu);
|
|
buf[3] = static_cast<uint8>(payloadLen & 0xFFu);
|
|
return 4u;
|
|
} else {
|
|
buf[1] = 127u;
|
|
for (int i = 7; i >= 0; i--) {
|
|
buf[2 + i] = static_cast<uint8>(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<uint64>(buf[1] & 0x7Fu);
|
|
|
|
if (plen == 126u) {
|
|
if (bufLen < 4u) { return false; }
|
|
plen = (static_cast<uint64>(buf[2]) << 8u) | static_cast<uint64>(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<uint64>(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_ */
|