24#define WS_GUID "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
31#define CWIST_WS_MAX_PAYLOAD_BYTES ((uint64_t)(16u * 1024u * 1024u))
37#define CWIST_WS_MAX_MESSAGE_BYTES ((size_t)(64u * 1024u * 1024u))
45static char *ws_strcasestr(
const char *
haystack,
const char *
needle) {
50 while (*h && *
n &&
tolower((
unsigned char)*h) ==
tolower((
unsigned char)*
n)) {
72 if (!req || !res)
return false;
120 if (!req || client_fd < 0)
return NULL;
123 if (!res)
return NULL;
134 if (
err.error.err_i16 != 0)
return NULL;
139 if (!ws)
return NULL;
156static ssize_t read_exact(
int fd,
void *buf,
size_t len) {
158 while (total < len) {
160 if (
n <= 0)
return -1;
182 if (len == 0)
return true;
191 if (!
nb)
return false;
222 if (read_exact(ws->
fd,
head, 2) < 0)
return NULL;
224 bool fin = (
head[0] & 0x80) != 0;
236 if (payload_len == 126) {
238 if (read_exact(ws->
fd, &
len16, 2) < 0)
return NULL;
240 }
else if (payload_len == 127) {
242 if (read_exact(ws->
fd, &
len64, 8) < 0)
return NULL;
262 if (payload_len > 0) {
264 if (!payload)
return NULL;
265 if (read_exact(ws->
fd, payload, payload_len) < 0) {
270 payload[payload_len] =
'\0';
292 if (!ws_frag_append(ws, payload, (
size_t)payload_len)) {
313 if (!ws_frag_append(ws, payload, (
size_t)payload_len)) {
335 if (payload_len == 1) {
354 frame->opcode = opcode;
355 frame->payload = payload;
372static int ws_send_all(
int fd,
const uint8_t *buf,
size_t len) {
378 ssize_t n = send(fd, buf + off, len - off, 0);
384 if (
n == 0)
return -1;
400 if (!ws || ws->
is_closed || (!data && len > 0))
return -1;
405 head[0] = 0x80 | (opcode & 0x0F);
409 }
else if (len < 65536) {
411 head[2] = (len >> 8) & 0xFF;
412 head[3] = len & 0xFF;
416 for (
int i = 0;
i < 8;
i++) {
417 head[2 +
i] = (len >> ((7 -
i) * 8)) & 0xFF;
424 if (ws_send_all(ws->
fd,
head, head_len) != 0)
return -1;
426 if (ws_send_all(ws->
fd, data, len) != 0)
return -1;
454 for (
size_t i = 0;
i <
msg.count &&
rc == 0;
i++) {
469 if (!out_len)
return NULL;
Memory allocation wrappers backed by libttak.
void cwist_free(void *ptr)
Free memory obtained via cwist_alloc/cwist_strdup/etc.
void * cwist_alloc(size_t size)
Allocate zeroed memory tracked by libttak.
cwist_error_t cwist_sstring_assign(cwist_sstring *str, const char *data)
Assign a C string.
cwist_error_t cwist_http_send_response(int client_fd, cwist_http_response *res)
Send a response over a socket.
char * cwist_http_header_get(cwist_http_header_node *head, const char *key)
Find a header value by key.
cwist_error_t cwist_http_header_add(cwist_http_header_node **head, const char *key, const char *value)
Add a header to the list.
void cwist_http_response_destroy(cwist_http_response *res)
Destroy an HTTP response object.
cwist_http_response * cwist_http_response_create(void)
Create a new HTTP response object.
HTTP Protocol Definitions and Helpers.
WebSocket upgrade and frame handling.
@ CWIST_WS_FRAME_CONTINUATION
TCP-like sequenced message fragmentation and reassembly.
bool cwist_seq_assembler_is_complete(const cwist_seq_assembler_t *a)
Return true when every expected chunk has been received.
bool cwist_seq_chunk_parse(const uint8_t *data, size_t len, cwist_seq_chunk_t *out)
Parse a sequence chunk from raw bytes.
cwist_seq_assembler_t * cwist_seq_assembler_create(void)
Create an empty assembler.
void cwist_seq_assembler_destroy(cwist_seq_assembler_t *a)
Destroy an assembler and its internal buffer.
void cwist_seq_message_free(cwist_seq_message_t *msg)
Free all buffers allocated by cwist_seq_split().
bool cwist_seq_assembler_get_data(cwist_seq_assembler_t *a, const uint8_t **out_data, size_t *out_len)
Borrow the assembled message when complete.
bool cwist_seq_assembler_feed(cwist_seq_assembler_t *a, const cwist_seq_chunk_t *chunk)
Feed one parsed chunk into the assembler.
struct cwist_seq_assembler cwist_seq_assembler_t
Opaque stateful assembler that reorders chunks and detects duplicates.
bool cwist_seq_split(const uint8_t *data, size_t len, uint16_t chunk_payload_size, cwist_seq_message_t *out)
Split data into sequenced chunks.
Integrated error structure used throughout the system.
cwist_http_header_node * headers
HTTP Response Object. Supports standard string body or Zero-Copy pointer body.
cwist_sstring * status_text
e.g., "OK"
cwist_http_status_t status_code
cwist_http_header_node * headers
Parsed view of one sequenced chunk.
Split a message into sequenced chunks.
cwist_ws_opcode_t frag_opcode
cwist_websocket * cwist_websocket_upgrade(cwist_http_request *req, int client_fd)
Upgrade an HTTP request to a WebSocket connection.
int cwist_websocket_send_sequenced(cwist_websocket *ws, const uint8_t *data, size_t len, uint16_t chunk_payload_size)
Send a large payload as sequenced binary frames.
cwist_ws_frame * cwist_websocket_receive(cwist_websocket *ws)
Receive the next complete WebSocket message from a connected client.
void cwist_websocket_close(cwist_websocket *ws)
Initiate a close handshake for the WebSocket wrapper.
int cwist_websocket_send(cwist_websocket *ws, cwist_ws_opcode_t opcode, const uint8_t *data, size_t len)
Send a single FIN-terminated WebSocket frame to the peer.
uint8_t * cwist_websocket_receive_sequenced(cwist_websocket *ws, size_t *out_len)
Receive and reassemble a sequenced binary message.
#define CWIST_WS_MAX_MESSAGE_BYTES
bool cwist_websocket_upgrade_response(cwist_http_request *req, cwist_http_response *res)
Validate the upgrade request and fill a 101 Switching Protocols response.
void cwist_websocket_frame_destroy(cwist_ws_frame *frame)
Release a frame and its owned payload buffer.
#define CWIST_WS_MAX_PAYLOAD_BYTES
void cwist_websocket_destroy(cwist_websocket *ws)
Destroy the WebSocket wrapper without closing the underlying socket.
char * base64_encode(const uint8_t *data, size_t input_length, size_t *output_length)
Base64-encode binary data for WebSocket handshake header generation.
void sha1(const uint8_t *data, size_t len, uint8_t *hash)
Compute a SHA-1 digest for the provided input bytes.
Internal cryptographic helpers used during the WebSocket handshake.