18#if DETWS_ENABLE_WEBSOCKET
29#if DETWS_ENABLE_WEBSOCKET
31static const char WS_MAGIC[] =
"258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
38#if DETWS_ENABLE_WEBSOCKET
46static const size_t WS_MAX_KEY_LEN = 64;
48static bool ws_accept_key(
const char *client_key,
char *out)
50 size_t key_len = strnlen(client_key, WS_MAX_KEY_LEN + 1);
51 if (key_len > WS_MAX_KEY_LEN)
63 size_t magic_len =
sizeof(WS_MAGIC) - 1;
64 char concat[WS_MAX_KEY_LEN +
sizeof(WS_MAGIC)];
65 memcpy(concat, client_key, key_len);
66 memcpy(concat + key_len, WS_MAGIC, magic_len);
69 sha1((
const uint8_t *)concat, key_len + magic_len, digest);
87void ws_send_version_required(uint8_t slot_id)
89 if (!det_conn_active(slot_id))
95 static const char resp[] =
"HTTP/1.1 426 Upgrade Required\r\n"
96 "Sec-WebSocket-Version: 13\r\n"
97 "Content-Length: 0\r\n"
98 "Connection: close\r\n\r\n";
107bool ws_do_upgrade(uint8_t slot_id,
HttpReq *req, WsConnectHandler on_connect)
114 if (!ws_accept_key(client_key, accept))
117 if (!det_conn_active(slot_id))
122#if DETWS_ENABLE_WS_DEFLATE
127 bool pmd = ws_ext && strstr(ws_ext,
"permessage-deflate");
128 hlen = snprintf(hdr,
sizeof(hdr),
129 "HTTP/1.1 101 Switching Protocols\r\n"
130 "Upgrade: websocket\r\n"
131 "Connection: Upgrade\r\n"
132 "Sec-WebSocket-Accept: %s\r\n"
136 pmd ?
"Sec-WebSocket-Extensions: permessage-deflate; client_no_context_takeover; "
137 "server_no_context_takeover\r\n"
140 hlen = snprintf(hdr,
sizeof(hdr),
141 "HTTP/1.1 101 Switching Protocols\r\n"
142 "Upgrade: websocket\r\n"
143 "Connection: Upgrade\r\n"
144 "Sec-WebSocket-Accept: %s\r\n\r\n",
162#if DETWS_ENABLE_WS_DEFLATE
166 on_connect(ws->
ws_id);
180bool sse_do_upgrade(uint8_t slot_id,
HttpReq *req, SseConnectHandler on_connect)
182 if (!det_conn_active(slot_id))
185 static const char SSE_HDR[] =
"HTTP/1.1 200 OK\r\n"
186 "Content-Type: text/event-stream\r\n"
187 "Cache-Control: no-cache\r\n"
188 "Connection: keep-alive\r\n\r\n";
190 det_conn_send(slot_id, SSE_HDR, (u16_t)(
sizeof(SSE_HDR) - 1));
197 strncpy(path, req->
path,
sizeof(path) - 1);
198 path[
sizeof(path) - 1] =
'\0';
219#if DETWS_ENABLE_WEBSOCKET
220void DetWebServer::ws_send_text(uint8_t ws_id,
const char *text)
227 uint16_t len = (uint16_t)strnlen(text, 0xFFFF);
230 if (det_conn_active(ws->
slot_id))
235void DetWebServer::ws_send_binary(uint8_t ws_id,
const uint8_t *data, uint16_t len)
244 if (det_conn_active(ws->
slot_id))
249void DetWebServer::ws_disconnect(uint8_t ws_id)
255 if (det_conn_active(ws->
slot_id))
266void DetWebServer::sse_send(uint8_t sse_id,
const char *data,
const char *event,
const char *
id)
273 if (det_conn_active(sse->
slot_id))
278void DetWebServer::sse_broadcast(
const char *path,
const char *data,
const char *event,
const char *
id)
284 if (strcmp(
sse_pool[i].path, path) != 0)
289 if (det_conn_active(sse->
slot_id))
#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 /).
#define MAX_SSE_CONNS
Maximum simultaneous SSE connections.
#define MAX_WS_CONNS
Maximum simultaneous WebSocket connections.
void base64_encode(const uint8_t *src, size_t src_len, char *dst)
Encode src_len bytes of src as Base64.
size_t base64_decode(const char *src, uint8_t *dst, size_t dst_cap)
Decode a null-terminated Base64 string.
Layer 7 (Application) - public HTTP routing API.
Library-private declarations shared between dwserver.cpp and the src/server/*.cpp request-handler tra...
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.
void sha1(const uint8_t *data, size_t len, uint8_t digest[SHA1_DIGEST_LEN])
Compute a SHA-1 digest over an arbitrary byte buffer.
Software SHA-1 implementation - no platform dependencies.
#define SHA1_DIGEST_LEN
SHA-1 digest length in bytes.
bool sse_write(SseConn *sse, const char *data, const char *event, const char *id)
Write one SSE event record to a client.
SseConn sse_pool[MAX_SSE_CONNS]
Pool of SSE connection state, one per MAX_SSE_CONNS.
SseConn * sse_alloc(uint8_t slot_id, const char *path)
Allocate an SseConn and bind it to a TCP slot.
Layer 6 (Presentation) – Server-Sent Events connection pool.
Fully-parsed HTTP/1.1 request.
char path[MAX_PATH_LEN]
URL path, null-terminated; no query string.
SSE connection state stored in sse_pool[].
uint8_t sse_id
Index into 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.
bool det_conn_send(uint8_t slot, const void *data, u16_t len)
Send len bytes on connection slot (copies data; TLS-aware).
void det_conn_flush(uint8_t slot)
Flush queued bytes / finish the send on slot (TLS-aware).
void det_conn_begin_close(uint8_t slot_id)
Begin a graceful close that dwells in ConnState::CONN_CLOSING until the peer ACKs.
void det_conn_abort_slot(uint8_t slot)
Hard-abort connection slot (RST) for a fatal condition. The transport owns the teardown order: free t...
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.