31#include <ascii-chat/network/acip/transport.h>
32#include <ascii-chat/network/packet.h>
33#include <ascii-chat/crypto/crypto.h>
34#include <ascii-chat/util/endian.h>
35#include <ascii-chat/network/crc32.h>
36#include <ascii-chat/log/logging.h>
37#include <ascii-chat/ringbuffer.h>
38#include <ascii-chat/buffer_pool.h>
39#include <ascii-chat/platform/mutex.h>
40#include <ascii-chat/platform/cond.h>
41#include <ascii-chat/debug/memory.h>
42#include <libwebsockets.h>
53#define WEBSOCKET_RECV_QUEUE_SIZE 4096
61#define WEBSOCKET_SEND_QUEUE_SIZE 256
64#include <ascii-chat/network/websocket/internal.h>
67static int websocket_callback(
struct lws *wsi,
enum lws_callback_reasons reason,
void *user,
void *in,
size_t len);
79static void *websocket_service_thread(
void *arg) {
80 websocket_transport_data_t *ws_data = (websocket_transport_data_t *)arg;
82 log_debug(
"WebSocket service thread started");
84 while (ws_data->service_running) {
87 int result = lws_service(ws_data->context, 50);
89 log_error(
"lws_service error: %d", result);
94 log_debug(
"WebSocket service thread exiting");
108static int websocket_callback(
struct lws *wsi,
enum lws_callback_reasons reason,
void *user,
void *in,
size_t len) {
109 websocket_transport_data_t *ws_data = (websocket_transport_data_t *)user;
112 case LWS_CALLBACK_CLIENT_ESTABLISHED:
113 log_info(
"WebSocket connection established");
115 mutex_lock(&ws_data->state_mutex);
116 ws_data->is_connected =
true;
117 mutex_unlock(&ws_data->state_mutex);
121 case LWS_CALLBACK_CLIENT_RECEIVE: {
123 if (!ws_data || !in || len == 0) {
127 bool is_first = lws_is_first_fragment(wsi);
128 bool is_final = lws_is_final_fragment(wsi);
130 log_dev_every(4500000,
"WebSocket fragment: %zu bytes (first=%d, final=%d)", len, is_first, is_final);
137 websocket_recv_msg_t msg;
140 log_error(
"Failed to allocate buffer for fragment (%zu bytes)", len);
144 memcpy(msg.data, in, len);
146 msg.first = is_first;
147 msg.final = is_final;
149 mutex_lock(&ws_data->recv_mutex);
153 log_warn(
"WebSocket receive queue full - dropping fragment (len=%zu, first=%d, final=%d)", len, is_first,
156 mutex_unlock(&ws_data->recv_mutex);
161 cond_signal(&ws_data->recv_cond);
162 mutex_unlock(&ws_data->recv_mutex);
166 case LWS_CALLBACK_CLIENT_CLOSED:
167 case LWS_CALLBACK_CLOSED:
168 log_info(
"WebSocket connection closed");
170 mutex_lock(&ws_data->state_mutex);
171 ws_data->is_connected =
false;
172 mutex_unlock(&ws_data->state_mutex);
175 cond_broadcast(&ws_data->recv_cond);
179 case LWS_CALLBACK_CLIENT_CONNECTION_ERROR:
180 log_error(
"WebSocket connection error: %s", in ? (
const char *)in :
"unknown");
182 mutex_lock(&ws_data->state_mutex);
183 ws_data->is_connected =
false;
184 mutex_unlock(&ws_data->state_mutex);
187 cond_broadcast(&ws_data->recv_cond);
191 case LWS_CALLBACK_CLIENT_WRITEABLE:
207static asciichat_error_t websocket_send(acip_transport_t *transport,
const void *data,
size_t len) {
208 websocket_transport_data_t *ws_data = (websocket_transport_data_t *)transport->impl_data;
210 mutex_lock(&ws_data->state_mutex);
211 bool connected = ws_data->is_connected;
212 mutex_unlock(&ws_data->state_mutex);
214 log_dev_every(1000000,
"websocket_send: is_connected=%d, wsi=%p, send_len=%zu", connected, (
void *)ws_data->wsi, len);
217 log_error(
"WebSocket send called but transport NOT connected! wsi=%p, len=%zu", (
void *)ws_data->wsi, len);
218 return SET_ERRNO(ERROR_NETWORK,
"WebSocket transport not connected (wsi=%p)", (
void *)ws_data->wsi);
222 const void *send_data = data;
223 size_t send_len = len;
224 uint8_t *encrypted_packet = NULL;
225 size_t encrypted_packet_size = 0;
227 if (len >=
sizeof(packet_header_t) && transport->crypto_ctx &&
crypto_is_ready(transport->crypto_ctx)) {
228 const packet_header_t *header = (
const packet_header_t *)data;
229 uint16_t packet_type = NET_TO_HOST_U16(header->type);
231 if (!packet_is_handshake_type((packet_type_t)packet_type)) {
233 size_t ciphertext_size = len + CRYPTO_NONCE_SIZE + CRYPTO_MAC_SIZE;
235 uint8_t *ciphertext = SAFE_MALLOC(ciphertext_size, uint8_t *);
237 return SET_ERRNO(ERROR_MEMORY,
"Failed to allocate ciphertext buffer for WebSocket");
240 size_t ciphertext_len;
241 crypto_result_t result =
242 crypto_encrypt(transport->crypto_ctx, data, len, ciphertext, ciphertext_size, &ciphertext_len);
243 if (result != CRYPTO_OK) {
244 SAFE_FREE(ciphertext);
249 size_t total_encrypted_size =
sizeof(packet_header_t) + ciphertext_len;
251 if (!encrypted_packet) {
252 SAFE_FREE(ciphertext);
253 return SET_ERRNO(ERROR_MEMORY,
"Failed to allocate encrypted packet buffer");
256 packet_header_t encrypted_header;
257 encrypted_header.magic = HOST_TO_NET_U64(PACKET_MAGIC);
258 encrypted_header.type = HOST_TO_NET_U16(PACKET_TYPE_ENCRYPTED);
259 encrypted_header.length = HOST_TO_NET_U32((uint32_t)ciphertext_len);
260 encrypted_header.crc32 = HOST_TO_NET_U32(asciichat_crc32(ciphertext, ciphertext_len));
261 encrypted_header.client_id = 0;
263 memcpy(encrypted_packet, &encrypted_header,
sizeof(encrypted_header));
264 memcpy(encrypted_packet +
sizeof(encrypted_header), ciphertext, ciphertext_len);
265 SAFE_FREE(ciphertext);
267 send_data = encrypted_packet;
268 send_len = total_encrypted_size;
269 encrypted_packet_size = total_encrypted_size;
271 log_dev_every(1000000,
"WebSocket: encrypted packet (original type %d as PACKET_TYPE_ENCRYPTED, %zu bytes)",
272 packet_type, send_len);
279 size_t required_size = LWS_PRE + send_len;
280 uint8_t *send_buffer = SAFE_MALLOC(required_size, uint8_t *);
282 if (encrypted_packet)
284 return SET_ERRNO(ERROR_MEMORY,
"Failed to allocate WebSocket send buffer");
288 memcpy(send_buffer + LWS_PRE, send_data, send_len);
292 if (!ws_data->owns_context) {
294 websocket_recv_msg_t msg;
295 msg.data = SAFE_MALLOC(send_len, uint8_t *);
297 SAFE_FREE(send_buffer);
298 if (encrypted_packet)
300 return SET_ERRNO(ERROR_MEMORY,
"Failed to allocate send queue buffer");
302 memcpy(msg.data, send_data, send_len);
307 mutex_lock(&ws_data->send_mutex);
311 mutex_unlock(&ws_data->send_mutex);
312 log_error(
"WebSocket server send queue FULL - cannot queue %zu byte message for wsi=%p", send_len,
313 (
void *)ws_data->wsi);
315 SAFE_FREE(send_buffer);
316 if (encrypted_packet)
318 return SET_ERRNO(ERROR_NETWORK,
"Send queue full (cannot queue %zu bytes)", send_len);
320 mutex_unlock(&ws_data->send_mutex);
327 log_dev_every(1000000,
">>> FRAME QUEUED: %zu bytes for wsi=%p (send_len=%zu)", send_len, (
void *)ws_data->wsi,
330 struct lws_context *ctx = lws_get_context(ws_data->wsi);
331 lws_cancel_service(ctx);
333 log_dev_every(1000000,
"Server-side WebSocket: queued %zu bytes, cancel_service sent for wsi=%p", send_len,
334 (
void *)ws_data->wsi);
335 SAFE_FREE(send_buffer);
336 if (encrypted_packet)
343 const size_t FRAGMENT_SIZE = 4096;
346 while (offset < send_len) {
347 size_t chunk_size = (send_len - offset > FRAGMENT_SIZE) ? FRAGMENT_SIZE : (send_len - offset);
348 int is_start = (offset == 0);
349 int is_end = (offset + chunk_size >= send_len);
352 enum lws_write_protocol flags = lws_write_ws_flags(LWS_WRITE_BINARY, is_start, is_end);
355 flags = (
enum lws_write_protocol)((
int)flags | LWS_WRITE_BUFLIST);
358 int written = lws_write(ws_data->wsi, send_buffer + LWS_PRE + offset, chunk_size, flags);
361 SAFE_FREE(send_buffer);
362 if (encrypted_packet)
364 return SET_ERRNO(ERROR_NETWORK,
"WebSocket write failed on fragment at offset %zu", offset);
367 if ((
size_t)written != chunk_size) {
368 SAFE_FREE(send_buffer);
369 if (encrypted_packet)
371 return SET_ERRNO(ERROR_NETWORK,
"WebSocket partial write: %d/%zu bytes at offset %zu", written, chunk_size,
375 offset += chunk_size;
376 log_dev_every(1000000,
"WebSocket sent fragment %zu bytes (offset %zu/%zu)", chunk_size, offset, send_len);
379 log_dev_every(1000000,
"WebSocket sent complete message: %zu bytes in fragments", send_len);
380 SAFE_FREE(send_buffer);
381 if (encrypted_packet)
386static asciichat_error_t websocket_recv(acip_transport_t *transport,
void **buffer,
size_t *out_len,
387 void **out_allocated_buffer) {
388 websocket_transport_data_t *ws_data = (websocket_transport_data_t *)transport->impl_data;
391 mutex_lock(&ws_data->state_mutex);
392 bool connected = ws_data->is_connected;
393 mutex_unlock(&ws_data->state_mutex);
396 return SET_ERRNO(ERROR_NETWORK,
"Connection not established");
399 mutex_lock(&ws_data->recv_mutex);
406 uint8_t *assembled_buffer = NULL;
407 size_t assembled_size = 0;
408 size_t assembled_capacity = 0;
410 int fragment_count = 0;
411 const uint64_t MAX_REASSEMBLY_TIME_NS = 100 * 1000000ULL;
416 uint64_t elapsed_ns =
time_get_ns() - assembly_start_ns;
417 if (elapsed_ns > MAX_REASSEMBLY_TIME_NS) {
420 if (assembled_buffer) {
423 mutex_unlock(&ws_data->recv_mutex);
425 if (assembled_size > 0) {
426 log_dev_every(4500000,
427 "🔄 WEBSOCKET_RECV: Reassembly timeout after %llums (have %zu bytes, expecting final fragment)",
428 (
unsigned long long)(elapsed_ns / 1000000ULL), assembled_size);
430 return SET_ERRNO(ERROR_NETWORK,
"Fragment reassembly timeout - no data from network");
434 mutex_lock(&ws_data->state_mutex);
435 bool still_connected = ws_data->is_connected;
436 mutex_unlock(&ws_data->state_mutex);
438 if (!still_connected) {
439 if (assembled_buffer) {
442 mutex_unlock(&ws_data->recv_mutex);
443 return SET_ERRNO(ERROR_NETWORK,
"Connection closed while reassembling fragments");
447 cond_timedwait(&ws_data->recv_cond, &ws_data->recv_mutex, 1 * 1000000ULL);
451 websocket_recv_msg_t frag;
454 if (assembled_buffer) {
457 mutex_unlock(&ws_data->recv_mutex);
458 return SET_ERRNO(ERROR_NETWORK,
"Failed to read fragment from queue");
462 log_warn(
"[WS_REASSEMBLE] Fragment #%d: %zu bytes, first=%d, final=%d, assembled_so_far=%zu", fragment_count,
463 frag.len, frag.first, frag.final, assembled_size);
466 if (assembled_size == 0 && !frag.first) {
467 log_error(
"[WS_REASSEMBLE] ERROR: Expected first=1 for first fragment, got first=%d", frag.first);
469 if (assembled_buffer) {
472 mutex_unlock(&ws_data->recv_mutex);
473 return SET_ERRNO(ERROR_NETWORK,
"Protocol error: continuation fragment without first fragment");
477 size_t required_size = assembled_size + frag.len;
478 if (required_size > assembled_capacity) {
479 size_t new_capacity = (assembled_capacity == 0) ? 8192 : (assembled_capacity * 3 / 2);
480 if (new_capacity < required_size) {
481 new_capacity = required_size;
486 log_error(
"[WS_REASSEMBLE] Failed to allocate reassembly buffer (%zu bytes)", new_capacity);
488 if (assembled_buffer) {
491 mutex_unlock(&ws_data->recv_mutex);
492 return SET_ERRNO(ERROR_MEMORY,
"Failed to allocate fragment reassembly buffer");
496 if (assembled_size > 0) {
497 memcpy(new_buffer, assembled_buffer, assembled_size);
501 if (assembled_buffer) {
505 assembled_buffer = new_buffer;
506 assembled_capacity = new_capacity;
510 memcpy(assembled_buffer + assembled_size, frag.data, frag.len);
511 assembled_size += frag.len;
519 log_info(
"[WS_REASSEMBLE] Complete message: %zu bytes in %d fragments", assembled_size, fragment_count);
520 *buffer = assembled_buffer;
521 *out_len = assembled_size;
522 *out_allocated_buffer = assembled_buffer;
523 mutex_unlock(&ws_data->recv_mutex);
530 mutex_unlock(&ws_data->recv_mutex);
533 *buffer = assembled_buffer;
534 *out_len = assembled_size;
535 *out_allocated_buffer = assembled_buffer;
537 log_info_every(LOG_RATE_DEFAULT,
"[WS_TIMING] websocket_recv dequeued %zu bytes (from %d fragments) at t=%llu",
538 assembled_size, fragment_count, (
unsigned long long)
time_get_ns());
542static asciichat_error_t websocket_close(acip_transport_t *transport) {
543 websocket_transport_data_t *ws_data = (websocket_transport_data_t *)transport->impl_data;
545 mutex_lock(&ws_data->state_mutex);
547 if (!ws_data->is_connected) {
548 mutex_unlock(&ws_data->state_mutex);
549 log_debug(
"websocket_close: Already closed (is_connected=false), wsi=%p", (
void *)ws_data->wsi);
553 log_info(
"websocket_close: Setting is_connected=false, wsi=%p", (
void *)ws_data->wsi);
554 ws_data->is_connected =
false;
555 mutex_unlock(&ws_data->state_mutex);
559 log_debug(
"websocket_close: Calling lws_close_reason for wsi=%p", (
void *)ws_data->wsi);
560 lws_close_reason(ws_data->wsi, LWS_CLOSE_STATUS_NORMAL, NULL, 0);
564 cond_broadcast(&ws_data->recv_cond);
566 log_debug(
"WebSocket transport closed");
570static acip_transport_type_t websocket_get_type(acip_transport_t *transport) {
572 return ACIP_TRANSPORT_WEBSOCKET;
575static socket_t websocket_get_socket(acip_transport_t *transport) {
577 return INVALID_SOCKET_VALUE;
580static bool websocket_is_connected(acip_transport_t *transport) {
581 websocket_transport_data_t *ws_data = (websocket_transport_data_t *)transport->impl_data;
583 mutex_lock(&ws_data->state_mutex);
584 bool connected = ws_data->is_connected;
585 mutex_unlock(&ws_data->state_mutex);
601static void websocket_destroy_impl(acip_transport_t *transport) {
602 if (!transport || !transport->impl_data) {
606 websocket_transport_data_t *ws_data = (websocket_transport_data_t *)transport->impl_data;
609 if (ws_data->service_running) {
610 log_debug(
"Stopping WebSocket service thread");
611 ws_data->service_running =
false;
613 log_debug(
"WebSocket service thread stopped");
623 if (ws_data->context && ws_data->owns_context) {
624 lws_context_destroy(ws_data->context);
625 ws_data->context = NULL;
629 if (ws_data->recv_queue) {
630 mutex_lock(&ws_data->recv_mutex);
632 websocket_recv_msg_t msg;
639 mutex_unlock(&ws_data->recv_mutex);
641 ws_data->recv_queue = NULL;
643 if (ws_data->send_queue) {
645 websocket_recv_msg_t msg;
652 ws_data->send_queue = NULL;
657 if (ws_data->send_buffer) {
658 SAFE_FREE(ws_data->send_buffer);
659 ws_data->send_buffer = NULL;
664 cond_destroy(&ws_data->recv_cond);
669 transport->impl_data = NULL;
674 log_debug(
"Destroyed WebSocket transport resources");
681static const acip_transport_methods_t websocket_methods = {
682 .send = websocket_send,
683 .recv = websocket_recv,
684 .close = websocket_close,
685 .get_type = websocket_get_type,
686 .get_socket = websocket_get_socket,
687 .is_connected = websocket_is_connected,
688 .destroy_impl = websocket_destroy_impl,
704 SET_ERRNO(ERROR_INVALID_PARAM,
"url is required");
710 const char *protocol_end = strstr(url,
"://");
712 SET_ERRNO(ERROR_INVALID_PARAM,
"Invalid WebSocket URL format (missing ://)");
716 bool use_ssl = (strncmp(url,
"wss://", 6) == 0);
717 const char *host_start = protocol_end + 3;
720 const char *port_start = strchr(host_start,
':');
721 const char *path_start = strchr(host_start,
'/');
723 char host[256] = {0};
724 int port = use_ssl ? 443 : 27226;
725 char path[256] =
"/";
727 if (port_start && (!path_start || port_start < path_start)) {
729 size_t host_len = port_start - host_start;
730 if (host_len >=
sizeof(host)) {
731 SET_ERRNO(ERROR_INVALID_PARAM,
"Host name too long");
734 memcpy(host, host_start, host_len);
735 host[host_len] =
'\0';
738 port = atoi(port_start + 1);
739 if (port <= 0 || port > 65535) {
740 SET_ERRNO(ERROR_INVALID_PARAM,
"Invalid port number");
745 size_t host_len = path_start ? (size_t)(path_start - host_start) : strlen(host_start);
746 if (host_len >=
sizeof(host)) {
747 SET_ERRNO(ERROR_INVALID_PARAM,
"Host name too long");
750 memcpy(host, host_start, host_len);
751 host[host_len] =
'\0';
756 SAFE_STRNCPY(path, path_start,
sizeof(path) - 1);
757 path[
sizeof(path) - 1] =
'\0';
760 log_info(
"Connecting to WebSocket: %s (host=%s, port=%d, path=%s, ssl=%d)", url, host, port, path, use_ssl);
763 acip_transport_t *transport = SAFE_MALLOC(
sizeof(acip_transport_t), acip_transport_t *);
765 SET_ERRNO(ERROR_MEMORY,
"Failed to allocate WebSocket transport");
770 websocket_transport_data_t *ws_data =
771 SAFE_CALLOC(1,
sizeof(websocket_transport_data_t), websocket_transport_data_t *);
773 SAFE_FREE(transport);
774 SET_ERRNO(ERROR_MEMORY,
"Failed to allocate WebSocket transport data");
780 if (!ws_data->recv_queue) {
782 SAFE_FREE(transport);
783 SET_ERRNO(ERROR_MEMORY,
"Failed to create receive queue");
791 SAFE_FREE(transport);
792 SET_ERRNO(ERROR_INTERNAL,
"Failed to initialize recv mutex");
796 if (cond_init(&ws_data->recv_cond) != 0) {
800 SAFE_FREE(transport);
801 SET_ERRNO(ERROR_INTERNAL,
"Failed to initialize recv condition variable");
806 cond_destroy(&ws_data->recv_cond);
810 SAFE_FREE(transport);
811 SET_ERRNO(ERROR_INTERNAL,
"Failed to initialize state mutex");
816 ws_data->send_buffer_capacity = LWS_PRE + 8192;
817 ws_data->send_buffer = SAFE_MALLOC(ws_data->send_buffer_capacity, uint8_t *);
818 if (!ws_data->send_buffer) {
820 cond_destroy(&ws_data->recv_cond);
824 SAFE_FREE(transport);
825 SET_ERRNO(ERROR_MEMORY,
"Failed to allocate send buffer");
831 static struct lws_protocols client_protocols[] = {
832 {
"acip", websocket_callback, 0, 4096, 0, NULL, 0}, {NULL, NULL, 0, 0, 0, NULL, 0}
835 struct lws_context_creation_info info;
836 memset(&info, 0,
sizeof(info));
837 info.port = CONTEXT_PORT_NO_LISTEN;
838 info.protocols = client_protocols;
839 info.gid = (gid_t)-1;
840 info.uid = (uid_t)-1;
841 info.options = LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT;
843 ws_data->context = lws_create_context(&info);
844 if (!ws_data->context) {
845 SAFE_FREE(ws_data->send_buffer);
847 cond_destroy(&ws_data->recv_cond);
851 SAFE_FREE(transport);
852 SET_ERRNO(ERROR_NETWORK,
"Failed to create libwebsockets context");
857 log_debug(
"Initiating WebSocket connection to %s:%d%s", host, port, path);
858 struct lws_client_connect_info connect_info;
859 memset(&connect_info, 0,
sizeof(connect_info));
860 connect_info.context = ws_data->context;
861 connect_info.address = host;
862 connect_info.port = port;
863 connect_info.path = path;
864 connect_info.host = host;
865 connect_info.origin = host;
866 connect_info.protocol =
"acip";
867 connect_info.ssl_connection = use_ssl ? LCCSCF_USE_SSL : 0;
868 connect_info.userdata = ws_data;
870 log_debug(
"Calling lws_client_connect_via_info...");
871 ws_data->wsi = lws_client_connect_via_info(&connect_info);
872 log_debug(
"lws_client_connect_via_info returned: %p", (
void *)ws_data->wsi);
874 lws_context_destroy(ws_data->context);
875 SAFE_FREE(ws_data->send_buffer);
877 cond_destroy(&ws_data->recv_cond);
881 SAFE_FREE(transport);
882 SET_ERRNO(ERROR_NETWORK,
"Failed to connect to WebSocket server");
886 ws_data->is_connected =
false;
887 ws_data->owns_context =
true;
890 transport->methods = &websocket_methods;
891 transport->crypto_ctx = crypto_ctx;
892 transport->impl_data = ws_data;
896 log_debug(
"Waiting for WebSocket connection to establish...");
897 int timeout_ms = 5000;
899 while (!ws_data->is_connected && elapsed_ms < timeout_ms) {
901 int result = lws_service(ws_data->context, 50);
903 log_error(
"lws_service error during connection: %d", result);
904 lws_context_destroy(ws_data->context);
905 SAFE_FREE(ws_data->send_buffer);
907 cond_destroy(&ws_data->recv_cond);
911 SAFE_FREE(transport);
912 SET_ERRNO(ERROR_NETWORK,
"WebSocket connection failed");
918 if (!ws_data->is_connected) {
919 log_error(
"WebSocket connection timeout after %d ms", elapsed_ms);
920 lws_context_destroy(ws_data->context);
921 SAFE_FREE(ws_data->send_buffer);
923 cond_destroy(&ws_data->recv_cond);
927 SAFE_FREE(transport);
928 SET_ERRNO(ERROR_NETWORK,
"WebSocket connection timeout");
932 log_info(
"WebSocket connection established (crypto: %s)", crypto_ctx ?
"enabled" :
"disabled");
935 ws_data->service_running =
true;
937 log_error(
"Failed to create WebSocket service thread");
938 ws_data->service_running =
false;
939 lws_context_destroy(ws_data->context);
940 SAFE_FREE(ws_data->send_buffer);
942 cond_destroy(&ws_data->recv_cond);
946 SAFE_FREE(transport);
947 SET_ERRNO(ERROR_INTERNAL,
"Failed to create service thread");
951 log_debug(
"WebSocket service thread started for client transport");
968 SET_ERRNO(ERROR_INVALID_PARAM,
"Invalid wsi parameter");
973 acip_transport_t *transport = SAFE_CALLOC(1,
sizeof(acip_transport_t), acip_transport_t *);
975 SET_ERRNO(ERROR_MEMORY,
"Failed to allocate WebSocket transport");
980 websocket_transport_data_t *ws_data =
981 SAFE_CALLOC(1,
sizeof(websocket_transport_data_t), websocket_transport_data_t *);
983 SAFE_FREE(transport);
984 SET_ERRNO(ERROR_MEMORY,
"Failed to allocate WebSocket transport data");
990 if (!ws_data->recv_queue) {
992 SAFE_FREE(transport);
993 SET_ERRNO(ERROR_MEMORY,
"Failed to create receive queue");
999 if (!ws_data->send_queue) {
1002 SAFE_FREE(transport);
1003 SET_ERRNO(ERROR_MEMORY,
"Failed to create send queue");
1012 SAFE_FREE(transport);
1013 SET_ERRNO(ERROR_NETWORK,
"Failed to initialize recv mutex");
1017 if (cond_init(&ws_data->recv_cond) != 0) {
1022 SAFE_FREE(transport);
1023 SET_ERRNO(ERROR_NETWORK,
"Failed to initialize recv condition variable");
1028 cond_destroy(&ws_data->recv_cond);
1033 SAFE_FREE(transport);
1034 SET_ERRNO(ERROR_NETWORK,
"Failed to initialize send mutex");
1038 if (
mutex_init(&ws_data->state_mutex) != 0) {
1040 cond_destroy(&ws_data->recv_cond);
1045 SAFE_FREE(transport);
1046 SET_ERRNO(ERROR_NETWORK,
"Failed to initialize state mutex");
1051 size_t initial_capacity = 4096 + LWS_PRE;
1052 ws_data->send_buffer = SAFE_MALLOC(initial_capacity, uint8_t *);
1053 if (!ws_data->send_buffer) {
1056 cond_destroy(&ws_data->recv_cond);
1061 SAFE_FREE(transport);
1062 SET_ERRNO(ERROR_MEMORY,
"Failed to allocate send buffer");
1065 ws_data->send_buffer_capacity = initial_capacity;
1069 ws_data->context = lws_get_context(wsi);
1070 ws_data->owns_context =
false;
1071 ws_data->is_connected =
true;
1072 log_debug(
"Server transport created: is_connected=true, wsi=%p", (
void *)wsi);
1075 transport->methods = &websocket_methods;
1076 transport->crypto_ctx = crypto_ctx;
1077 transport->impl_data = ws_data;
1079 log_info(
"Created WebSocket server transport (crypto: %s)", crypto_ctx ?
"enabled" :
"disabled");
void buffer_pool_free(buffer_pool_t *pool, void *data, size_t size)
void * buffer_pool_alloc(buffer_pool_t *pool, size_t size)
crypto_result_t crypto_encrypt(crypto_context_t *ctx, const uint8_t *plaintext, size_t plaintext_len, uint8_t *ciphertext_out, size_t ciphertext_out_size, size_t *ciphertext_len_out)
const char * crypto_result_to_string(crypto_result_t result)
bool crypto_is_ready(const crypto_context_t *ctx)
ringbuffer_t * ringbuffer_create(size_t element_size, size_t capacity)
void ringbuffer_destroy(ringbuffer_t *rb)
bool ringbuffer_is_empty(const ringbuffer_t *rb)
bool ringbuffer_read(ringbuffer_t *rb, void *data)
bool ringbuffer_write(ringbuffer_t *rb, const void *data)
int mutex_init(mutex_t *mutex)
int asciichat_thread_create(asciichat_thread_t *thread, void *(*start_routine)(void *), void *arg)
int asciichat_thread_join(asciichat_thread_t *thread, void **retval)
int mutex_destroy(mutex_t *mutex)
uint64_t time_get_ns(void)
acip_transport_t * acip_websocket_server_transport_create(struct lws *wsi, crypto_context_t *crypto_ctx)
Create WebSocket server transport from existing connection.
#define WEBSOCKET_SEND_QUEUE_SIZE
Maximum send queue size (messages buffered for server-side sending)
acip_transport_t * acip_websocket_client_transport_create(const char *url, crypto_context_t *crypto_ctx)
Create WebSocket client transport.
#define WEBSOCKET_RECV_QUEUE_SIZE
Maximum receive queue size (messages buffered before recv())