DeterministicESPAsyncWebServer v6.27.1
Zero-allocation, bounded-execution async HTTP server for ESP32
Loading...
Searching...
No Matches
websocket.h File Reference

Layer 6 (Presentation) – WebSocket frame parser and connection pool. More...

Go to the source code of this file.

Classes

struct  WsConn
 WebSocket connection state stored in ws_pool[]. More...
 

Enumerations

enum class  WsOpcode : uint8_t {
  WS_OP_CONTINUATION = 0x0 , WS_OP_TEXT = 0x1 , WS_OP_BINARY = 0x2 , WS_OP_CLOSE = 0x8 ,
  WS_OP_PING = 0x9 , WS_OP_PONG = 0xA
}
 WebSocket frame opcodes. More...
 
enum class  WsCloseCode : uint16_t {
  WS_CLOSE_NORMAL = 1000 , WS_CLOSE_GOING_AWAY = 1001 , WS_CLOSE_PROTOCOL = 1002 , WS_CLOSE_UNSUPPORTED = 1003 ,
  WS_CLOSE_INVALID_PAYLOAD = 1007 , WS_CLOSE_TOO_BIG = 1009
}
 WebSocket close status codes (RFC 6455 §7.4.1). More...
 
enum class  WsParseState : uint8_t {
  WS_HEADER1 , WS_HEADER2 , WS_LEN16_HI , WS_LEN16_LO ,
  WS_LEN64 , WS_MASK0 , WS_MASK1 , WS_MASK2 ,
  WS_MASK3 , WS_PAYLOAD , WS_FRAME_READY , WS_CLOSED ,
  WS_ERROR
}
 States of the WebSocket frame parser. More...
 

Functions

void ws_init ()
 Initialize all WebSocket pool slots to inactive.
 
bool ws_active (uint8_t ws_id)
 True if ws_id is a valid, in-use WebSocket slot. Use this instead of reaching into ws_pool[ws_id].active from another module.
 
const char * ws_payload (uint8_t ws_id)
 The NUL-terminated reassembled message payload for ws_id, or nullptr if the slot is out of range / inactive. Use this instead of reaching into ws_pool[ws_id].buf.
 
WsConnws_alloc (uint8_t slot_id)
 Allocate a WsConn slot and bind it to a TCP slot.
 
WsConnws_find (uint8_t slot_id)
 Find the WsConn for a given TCP slot, or nullptr if none.
 
void ws_free (uint8_t slot_id)
 Free the WsConn associated with a TCP slot.
 
void ws_parse (WsConn *ws)
 Drain the ring buffer for slot_id and feed bytes to the WS parser.
 
void ws_feed_byte (WsConn *ws, uint8_t byte)
 Feed one already-plaintext byte through the WS frame state machine.
 
void ws_reset_frame (WsConn *ws)
 Reset the frame parser back to WsParseState::WS_HEADER1, ready for the next frame.
 
bool ws_send_frame (WsConn *ws, WsOpcode opcode, const uint8_t *payload, uint16_t len)
 Send a WebSocket frame to the client.
 
void ws_set_frag_size (uint16_t bytes)
 Set the outbound fragmentation size (RFC 6455 sec 5.4), in payload bytes; 0 = off.
 
void ws_close (WsConn *ws, WsCloseCode code)
 Send a Close frame and mark the slot WsParseState::WS_CLOSED.
 

Variables

WsConn ws_pool [MAX_WS_CONNS]
 Pool of WebSocket connection state, one per MAX_WS_CONNS.
 

Detailed Description

Layer 6 (Presentation) – WebSocket frame parser and connection pool.

Implements RFC 6455 framing with a fixed-size payload buffer per slot. Connections are tracked in ws_pool[MAX_WS_CONNS]; each entry maps to one TCP slot in conn_pool[] via slot_id.

Frame format (client to server)

0 1 2 3
0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7
+-+-+-+-+-------+-+-------------+-------------------------------+
|F|R|R|R| opcode|M| payload len | extended payload length |
|I|S|S|S| (4) |A| (7) | (16/64) |
|N|V|V|V| |S| +-------------------------------+
| |1|2|3| |K| | |
+-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - -+
| extended payload length continued, if payload len == 127 |
+ - - - - - - - - - - - - - - -+-------------------------------+
| | masking key, if MASK set |
+-------------------------------+-------------------------------+
| masking key (continued) | payload data |
+-------------------------------- - - - - - - - - - - - - - - -+
: payload data continued :
+---------------------------------------------------------------+

State machine

WsParseState::WS_HEADER1 -- read FIN + opcode byte
WsParseState::WS_HEADER2 -- read MASK + 7-bit payload length
WsParseState::WS_LEN16_HI -- read extended 16-bit length high byte
WsParseState::WS_LEN16_LO -- read extended 16-bit length low byte
WsParseState::WS_LEN64 -- consume 8-byte 64-bit length (reject; too large)
WsParseState::WS_MASK0..3 -- read 4-byte masking key
WsParseState::WS_PAYLOAD -- accumulate payload bytes (unmasked)
WsParseState::WS_FRAME_READY -- complete frame waiting for dispatch
WsParseState::WS_CLOSED -- connection closed; slot may be recycled
WsParseState::WS_ERROR -- protocol error; close frame sent
@ WS_PAYLOAD
Accumulating payload bytes.
@ WS_FRAME_READY
Complete frame ready for dispatch.
@ WS_LEN16_LO
Reading extended 16-bit length, low byte.
@ WS_ERROR
Protocol error; close frame has been queued.
@ WS_HEADER2
Awaiting second header byte (MASK, 7-bit length).
@ WS_LEN64
Consuming 8-byte 64-bit length (always rejected).
@ WS_LEN16_HI
Reading extended 16-bit length, high byte.
@ WS_MASK0
Reading masking key byte 0.
@ WS_CLOSED
Connection closed; slot may be recycled.
@ WS_HEADER1
Awaiting first header byte (FIN, RSV, opcode).

Limitations

  • A reassembled message must fit in WS_FRAME_SIZE bytes; larger closes 1009.
  • RSV bits must be zero (no extensions supported).

Fragmentation (RFC 6455 §5.4) Fragmented data messages are reassembled into buf across continuation frames; the message is delivered only when the FIN frame arrives. Control frames (ping/pong/close) may be interleaved between fragments and are handled immediately without disturbing the partial message.

Author
Douglas Quigg (dstroy0)
Date
2026

Definition in file websocket.h.

Enumeration Type Documentation

◆ WsOpcode

enum class WsOpcode : uint8_t
strong

WebSocket frame opcodes.

Enumerator
WS_OP_CONTINUATION 

Continuation frame (data-message fragment; reassembled into buf).

WS_OP_TEXT 

UTF-8 text payload.

WS_OP_BINARY 

Binary payload.

WS_OP_CLOSE 

Connection close.

WS_OP_PING 

Ping (auto-ponged by the library).

WS_OP_PONG 

Pong (echoed ping; ignored by library).

Definition at line 71 of file websocket.h.

◆ WsCloseCode

enum class WsCloseCode : uint16_t
strong

WebSocket close status codes (RFC 6455 §7.4.1).

Enumerator
WS_CLOSE_NORMAL 

Normal closure.

WS_CLOSE_GOING_AWAY 

Endpoint going away.

WS_CLOSE_PROTOCOL 

Protocol error.

WS_CLOSE_UNSUPPORTED 

Received a data type the endpoint cannot accept (RFC 6455).

WS_CLOSE_INVALID_PAYLOAD 

Text message that is not valid UTF-8 (RFC 6455 8.1).

WS_CLOSE_TOO_BIG 

Payload too large for WS_FRAME_SIZE.

Definition at line 82 of file websocket.h.

◆ WsParseState

enum class WsParseState : uint8_t
strong

States of the WebSocket frame parser.

Enumerator
WS_HEADER1 

Awaiting first header byte (FIN, RSV, opcode).

WS_HEADER2 

Awaiting second header byte (MASK, 7-bit length).

WS_LEN16_HI 

Reading extended 16-bit length, high byte.

WS_LEN16_LO 

Reading extended 16-bit length, low byte.

WS_LEN64 

Consuming 8-byte 64-bit length (always rejected).

WS_MASK0 

Reading masking key byte 0.

WS_MASK1 

Reading masking key byte 1.

WS_MASK2 

Reading masking key byte 2.

WS_MASK3 

Reading masking key byte 3.

WS_PAYLOAD 

Accumulating payload bytes.

WS_FRAME_READY 

Complete frame ready for dispatch.

WS_CLOSED 

Connection closed; slot may be recycled.

WS_ERROR 

Protocol error; close frame has been queued.

Definition at line 97 of file websocket.h.

Function Documentation

◆ ws_init()

void ws_init ( )

Initialize all WebSocket pool slots to inactive.

Called once from DetWebServer::begin().

Definition at line 31 of file websocket.cpp.

References MAX_WS_CONNS, WsConn::ws_id, and ws_pool.

Referenced by DetWebServer::begin(), and DetWebServer::stop().

◆ ws_active()

bool ws_active ( uint8_t  ws_id)

True if ws_id is a valid, in-use WebSocket slot. Use this instead of reaching into ws_pool[ws_id].active from another module.

Definition at line 40 of file websocket.cpp.

References WsConn::active, MAX_WS_CONNS, and ws_pool.

◆ ws_payload()

const char * ws_payload ( uint8_t  ws_id)

The NUL-terminated reassembled message payload for ws_id, or nullptr if the slot is out of range / inactive. Use this instead of reaching into ws_pool[ws_id].buf.

Definition at line 45 of file websocket.cpp.

References MAX_WS_CONNS, and ws_pool.

◆ ws_alloc()

WsConn * ws_alloc ( uint8_t  slot_id)

Allocate a WsConn slot and bind it to a TCP slot.

Parameters
slot_idTCP connection slot that just completed an upgrade.
Returns
Pointer to the allocated WsConn, or nullptr if the pool is full.

Definition at line 50 of file websocket.cpp.

References WsConn::active, MAX_WS_CONNS, WsConn::parse_state, WsConn::slot_id, WS_HEADER1, WsConn::ws_id, and ws_pool.

◆ ws_find()

WsConn * ws_find ( uint8_t  slot_id)

Find the WsConn for a given TCP slot, or nullptr if none.

Parameters
slot_idTCP connection slot index.

Definition at line 67 of file websocket.cpp.

References MAX_WS_CONNS, and ws_pool.

Referenced by http_parse(), and DetWebServer::http_poll_slot().

◆ ws_free()

void ws_free ( uint8_t  slot_id)

Free the WsConn associated with a TCP slot.

Parameters
slot_idTCP connection slot index.

Definition at line 77 of file websocket.cpp.

References MAX_WS_CONNS, WsConn::ws_id, and ws_pool.

Referenced by DetWebServer::http_poll_slot().

◆ ws_parse()

void ws_parse ( WsConn ws)

Drain the ring buffer for slot_id and feed bytes to the WS parser.

Stops when the ring buffer is empty or the parser reaches a terminal state (WsParseState::WS_FRAME_READY, WsParseState::WS_CLOSED, WsParseState::WS_ERROR).

Parameters
wsWebSocket connection to drain into.

Definition at line 345 of file websocket.cpp.

References WsConn::parse_state, WsConn::slot_id, WS_CLOSED, WS_ERROR, ws_feed_byte(), and WS_FRAME_READY.

Referenced by DetWebServer::http_poll_slot().

◆ ws_feed_byte()

void ws_feed_byte ( WsConn ws,
uint8_t  byte 
)

Feed one already-plaintext byte through the WS frame state machine.

The per-byte core shared by ws_parse() (which reads the plaintext rx ring) and the TLS receive path (which decrypts ciphertext and feeds the plaintext here). Callers must stop feeding once parse_state reaches a terminal state (WsParseState::WS_FRAME_READY / WsParseState::WS_CLOSED / WsParseState::WS_ERROR) and dispatch/reset before continuing.

Parameters
wsWebSocket connection.
byteNext plaintext byte of the client frame stream.

Definition at line 364 of file websocket.cpp.

References WsConn::buf, conn_pool, WsConn::ctl_buf, WsConn::fin, WsConn::fragmenting, WsConn::len64_count, WsConn::mask_key, WsConn::masked, WsConn::msg_len, WsConn::msg_opcode, WsConn::opcode, WsConn::parse_state, WsConn::payload_idx, WsConn::payload_len, WsConn::slot_id, ws_close(), WS_CLOSE_PROTOCOL, WS_CLOSE_TOO_BIG, WS_ERROR, WS_FRAME_SIZE, WS_HEADER1, WS_HEADER2, WS_LEN16_HI, WS_LEN16_LO, WS_LEN64, WS_MASK0, WS_MASK1, WS_MASK2, WS_MASK3, WS_OP_BINARY, WS_OP_CLOSE, WS_OP_CONTINUATION, WS_OP_PING, WS_OP_PONG, WS_OP_TEXT, and WS_PAYLOAD.

Referenced by DetWebServer::http_poll_slot(), and ws_parse().

◆ ws_reset_frame()

void ws_reset_frame ( WsConn ws)

Reset the frame parser back to WsParseState::WS_HEADER1, ready for the next frame.

Does not change ws->active or ws->slot_id.

Parameters
wsWebSocket connection to reset.

Definition at line 105 of file websocket.cpp.

References WsConn::buf, WsConn::ctl_buf, WsConn::fragmenting, WsConn::msg_len, WsConn::msg_opcode, and WS_OP_TEXT.

Referenced by DetWebServer::http_poll_slot().

◆ ws_send_frame()

bool ws_send_frame ( WsConn ws,
WsOpcode  opcode,
const uint8_t *  payload,
uint16_t  len 
)

Send a WebSocket frame to the client.

Builds the header (no masking – server-to-client frames are never masked) and hands both to the transport layer (det_conn_send()). The caller is responsible for flushing afterwards (det_conn_flush()).

Parameters
wsWebSocket connection.
opcodeFrame opcode (WsOpcode::WS_OP_TEXT, WsOpcode::WS_OP_BINARY, WsOpcode::WS_OP_PONG, etc.).
payloadPayload bytes (may be nullptr for zero-length frames).
lenPayload length in bytes.
Returns
true on success, false if the TCP slot is not active.

Definition at line 159 of file websocket.cpp.

References conn_pool, WsCtx::frag_size, scratch_alloc(), WsConn::slot_id, WS_OP_BINARY, WS_OP_CONTINUATION, and WS_OP_TEXT.

Referenced by ws_close().

◆ ws_set_frag_size()

void ws_set_frag_size ( uint16_t  bytes)

Set the outbound fragmentation size (RFC 6455 sec 5.4), in payload bytes; 0 = off.

A runtime override of DETWS_WS_FRAG_SIZE. When >0, a data message longer than bytes is split into that-sized frames by ws_send_frame() (see DETWS_WS_FRAG_SIZE). Applies to all connections.

Definition at line 128 of file websocket.cpp.

References WsCtx::frag_size.

◆ ws_close()

void ws_close ( WsConn ws,
WsCloseCode  code 
)

Send a Close frame and mark the slot WsParseState::WS_CLOSED.

Parameters
wsWebSocket connection.
codeClose status code (e.g. WsCloseCode::WS_CLOSE_NORMAL).

Definition at line 221 of file websocket.cpp.

References det_conn_flush(), WsConn::parse_state, WsConn::slot_id, WS_CLOSED, WS_OP_CLOSE, and ws_send_frame().

Referenced by ws_feed_byte().

Variable Documentation

◆ ws_pool

WsConn ws_pool[MAX_WS_CONNS]
extern

Pool of WebSocket connection state, one per MAX_WS_CONNS.

Definition at line 29 of file websocket.cpp.

Referenced by ws_active(), ws_alloc(), ws_find(), ws_free(), ws_init(), and ws_payload().