19#if PC_ENABLE_WEBSOCKET
30#if PC_ENABLE_WEBSOCKET
32static const char WS_MAGIC[] =
"258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
39#if PC_ENABLE_WEBSOCKET
47static const size_t WS_MAX_KEY_LEN = 64;
49static bool ws_accept_key(
const char *client_key,
char *out)
51 size_t key_len = strnlen(client_key, WS_MAX_KEY_LEN + 1);
56 if (key_len > WS_MAX_KEY_LEN)
69 size_t magic_len =
sizeof(WS_MAGIC) - 1;
70 char concat[WS_MAX_KEY_LEN +
sizeof(WS_MAGIC)];
71 memcpy(concat, client_key, key_len);
72 memcpy(concat + key_len, WS_MAGIC, magic_len);
75 pc_sha1((
const uint8_t *)concat, key_len + magic_len, digest);
93void ws_send_version_required(uint8_t slot_id)
95 if (!pc_conn_active(slot_id))
101 static const char resp[] =
"HTTP/1.1 426 Upgrade Required\r\n"
102 "Sec-WebSocket-Version: 13\r\n"
103 "Content-Length: 0\r\n"
104 "Connection: close\r\n\r\n";
113bool ws_do_upgrade(uint8_t slot_id,
HttpReq *req, WsConnectHandler on_connect)
122 if (!ws_accept_key(client_key, accept))
127 if (!pc_conn_active(slot_id))
134#if PC_ENABLE_WS_DEFLATE
139 bool pmd = ws_ext && strstr(ws_ext,
"permessage-deflate");
140 pc_sb sb_hdr = {hdr,
sizeof(hdr), 0,
true};
143 "HTTP/1.1 101 Switching Protocols\r\nUpgrade: websocket\r\nConnection: Upgrade\r\nSec-WebSocket-Accept: ");
146 pc_sb_put(&sb_hdr, pmd ?
"Sec-WebSocket-Extensions: permessage-deflate; client_no_context_takeover; "
147 "server_no_context_takeover\r\n"
152 pc_sb sb_hdr2 = {hdr,
sizeof(hdr), 0,
true};
155 "HTTP/1.1 101 Switching Protocols\r\nUpgrade: websocket\r\nConnection: Upgrade\r\nSec-WebSocket-Accept: ");
175#if PC_ENABLE_WS_DEFLATE
180 on_connect(ws->
ws_id);
195bool pc_sse_do_upgrade(uint8_t slot_id,
HttpReq *req, SseConnectHandler on_connect)
197 if (!pc_conn_active(slot_id))
202 static const char SSE_HDR[] =
"HTTP/1.1 200 OK\r\n"
203 "Content-Type: text/event-stream\r\n"
204 "Cache-Control: no-cache\r\n"
205 "Connection: keep-alive\r\n\r\n";
207 pc_conn_send(slot_id, SSE_HDR, (u16_t)(
sizeof(SSE_HDR) - 1));
214 strncpy(path, req->
path,
sizeof(path) - 1);
215 path[
sizeof(path) - 1] =
'\0';
238#if PC_ENABLE_WEBSOCKET
239void PC::ws_send_text(uint8_t ws_id,
const char *text)
250 uint16_t len = (uint16_t)strnlen(text, 0xFFFF);
256 if (pc_conn_active(ws->
slot_id))
264void PC::ws_send_binary(uint8_t ws_id,
const uint8_t *data, uint16_t len)
279 if (pc_conn_active(ws->
slot_id))
287void PC::ws_disconnect(uint8_t ws_id)
295 if (pc_conn_active(ws->
slot_id))
308void PC::pc_sse_send(uint8_t pc_sse_id,
const char *data,
const char *event,
const char *
id)
319 if (pc_conn_active(sse->
slot_id))
327void PC::pc_sse_broadcast(
const char *path,
const char *data,
const char *event,
const char *
id)
344 if (pc_conn_active(sse->
slot_id))
size_t pc_base64_decode(const char *src, uint8_t *dst, size_t dst_cap)
Decode a null-terminated Base64 string.
void pc_base64_encode(const uint8_t *src, size_t src_len, char *dst)
Encode src_len bytes of src as Base64.
const char * http_get_header(const HttpReq *req, const char *key)
Look up a header value by name (case-insensitive).
void http_reset(uint8_t slot_id)
Reset the HTTP parser for a connection slot.
Layer 7 (Application) - public HTTP routing API.
#define WS_HDR_BUF_SIZE
Stack buffer for the HTTP 101 Switching Protocols response sent during the WebSocket handshake.
#define MAX_PATH_LEN
Maximum URL path length (including leading /).
Library-private declarations shared between protocore.cpp and the src/server/*.cpp request-handler tr...
PC_CRYPTO_HOT void pc_sha1(const uint8_t *data, size_t len, uint8_t digest[PC_SHA1_DIGEST_LEN])
Compute a SHA-1 digest over an arbitrary byte buffer.
SHA-1 (FIPS 180-4) - one-shot digest.
#define PC_SHA1_DIGEST_LEN
SHA-1 digest length in bytes.
SseConn * pc_sse_alloc(uint8_t slot_id, const char *path)
Allocate an SseConn and bind it to a TCP slot.
bool pc_sse_write(SseConn *sse, const char *data, const char *event, const char *id)
Write one SSE event record to a client.
SseConn pc_sse_pool[MAX_SSE_CONNS]
Pool of SSE connection state, one per MAX_SSE_CONNS.
Layer 6 (Presentation) – Server-Sent Events connection pool.
Bounded no-heap string builder that fails closed on overflow (one shared copy).
size_t pc_sb_finish(pc_sb *b)
NUL-terminate and return the built length, or 0 if the build overflowed.
void pc_sb_put(pc_sb *b, const char *s)
Append NUL-terminated s; leaves the buffer untouched and clears ok if it would not fit.
Fully-parsed HTTP/1.1 request.
char path[MAX_PATH_LEN]
URL path, null-terminated; no query string.
SSE connection state stored in pc_sse_pool[].
uint8_t pc_sse_id
Index into pc_sse_pool[] (set at init).
uint8_t slot_id
Owning TCP slot in conn_pool[].
WebSocket connection state stored in ws_pool[].
uint8_t ws_id
Index into ws_pool[] (set at init).
uint8_t slot_id
Owning TCP slot in conn_pool[].
WsParseState parse_state
Current frame parser state.
Bump-append target; ok latches false once an append would overflow cap.
void pc_conn_abort_slot(uint8_t slot)
Hard-abort connection slot (RST) for a fatal condition. The transport owns the teardown order: free t...
void pc_conn_flush(uint8_t slot)
Flush queued bytes / finish the send on slot (TLS-aware).
bool pc_conn_send(uint8_t slot, const void *data, u16_t len)
Send len bytes on connection slot (copies data; TLS-aware).
void pc_conn_begin_close(uint8_t slot_id)
Begin a graceful close that dwells in ConnState::CONN_CLOSING until the peer ACKs.
Layer 4 (Transport) - TCP connection pool, ring buffers, and lwIP integration.
bool ws_send_frame(WsConn *ws, WsOpcode opcode, const uint8_t *payload, uint16_t len)
Send a WebSocket frame to the client.
void ws_close(WsConn *ws, WsCloseCode code)
Send a Close frame and mark the slot WsParseState::WS_CLOSED.
WsConn * ws_alloc(uint8_t slot_id)
Allocate a WsConn slot and bind it to a TCP slot.
WsConn ws_pool[MAX_WS_CONNS]
Pool of WebSocket connection state, one per MAX_WS_CONNS.
Layer 6 (Presentation) – WebSocket frame parser and connection pool.
@ WS_CLOSE_NORMAL
Normal closure.
@ WS_ERROR
Protocol error; close frame has been queued.
@ WS_CLOSED
Connection closed; slot may be recycled.
@ WS_OP_TEXT
UTF-8 text payload.
@ WS_OP_BINARY
Binary payload.