ascii-chat 0.11.33
Video chat in your terminal
Loading...
Searching...
No Matches
lib/network/websocket/client.c
Go to the documentation of this file.
1
13#include <ascii-chat/common.h>
14#include <ascii-chat/log/log.h>
20
21#include <string.h>
22#include <ascii-chat/atomic.h>
23
28 if (!name) {
29 log_error("WebSocket client name is required");
30 return NULL;
31 }
32
34 if (!client) {
35 log_error("Failed to allocate websocket_client_t");
36 return NULL;
37 }
38
39 // Zero-initialize all fields
40 memset(client, 0, sizeof(*client));
41
42 // Initialize connection state
43 atomic_store_bool(&client->connection_active, false);
44 atomic_store_bool(&client->connection_lost, false);
45 client->transport = NULL;
46 client->my_client_id = 0;
47 client->encryption_enabled = false;
48 atomic_store_bool(&client->should_reconnect, false);
49
50 // Initialize thread-safe mutex for packet transmission
51 if (mutex_init(&client->send_mutex, "client_send") != 0) {
52 log_error("Failed to initialize send_mutex");
53 SAFE_FREE(client);
54 return NULL;
55 }
56
57 // Register WebSocket client with debug naming system
58 NAMED_REGISTER_WEBSOCKET(client, name, NULL);
59
60 // Register atomic fields for sync state monitoring - descriptive names showing control purpose
61 NAMED_REGISTER_ATOMIC(&client->connection_active, "connection_is_active", (uintptr_t)(const void *)(client));
62 NAMED_REGISTER_ATOMIC(&client->connection_lost, "connection_was_lost", (uintptr_t)(const void *)(client));
63 NAMED_REGISTER_ATOMIC(&client->should_reconnect, "needs_reconnection_attempt", (uintptr_t)(const void *)(client));
64
65 log_debug("WebSocket client created");
66
67 return client;
68}
69
74 if (!client_ptr || !*client_ptr) {
75 return; // No-op if NULL
76 }
77
78 websocket_client_t *client = *client_ptr;
79
80 log_debug("Destroying WebSocket client");
81
82 // Note: Do NOT destroy the transport. The transport is created and owned by the
83 // connection layer (acip_websocket_client_transport_create). The WebSocket client
84 // is just a thin wrapper that holds a reference. The transport will be destroyed
85 // by the connection context or server connection cleanup code.
86 client->transport = NULL;
87
88 // Unregister atomic fields
89 NAMED_UNREGISTER(&client->connection_active);
90 NAMED_UNREGISTER(&client->connection_lost);
91 NAMED_UNREGISTER(&client->should_reconnect);
92
93 NAMED_UNREGISTER(client);
94
95 // Destroy mutex
96 mutex_destroy(&client->send_mutex);
97
98 SAFE_FREE(*client_ptr);
99}
100
105 if (!client) {
106 return false;
107 }
108 return atomic_load_bool(&client->connection_active);
109}
110
115 if (!client) {
116 return false;
117 }
118 return atomic_load_bool(&client->connection_lost);
119}
120
125 if (!client) {
126 return;
127 }
128 atomic_store_bool(&client->connection_lost, true);
129 atomic_store_bool(&client->connection_active, false);
130 log_debug("WebSocket connection marked as lost");
131}
132
137 if (!client) {
138 return;
139 }
140
141 log_debug("Closing WebSocket client");
142
143 if (client->transport) {
144 acip_transport_close(client->transport);
145 }
146
147 atomic_store_bool(&client->connection_active, false);
148}
149
154 if (!client) {
155 return;
156 }
157
158 log_debug("Shutting down WebSocket client");
159
160 // Force close the transport
161 if (client->transport) {
162 acip_transport_close(client->transport);
163 }
164
165 atomic_store_bool(&client->connection_active, false);
166 atomic_store_bool(&client->connection_lost, true);
167}
168
178 struct crypto_context_t *crypto_ctx) {
179 if (!client || !url) {
180 log_error("Invalid arguments to websocket_client_connect");
181 return NULL;
182 }
183
184 log_info("Connecting WebSocket client to %s", url);
185
186 // Store URL for reference
187 SAFE_STRNCPY(client->url, url, sizeof(client->url));
188
189 // Create transport using ACIP layer
190 acip_transport_t *transport = acip_websocket_client_transport_create("client", url, crypto_ctx);
191 if (!transport) {
192 log_error("Failed to create WebSocket transport");
193 atomic_store_bool(&client->connection_lost, true);
194 return NULL;
195 }
196
197 // Derive client ID from URL hash using FNV-1a (proper implementation with 64-bit arithmetic)
198 // This provides a stable, unique ID per connection URL without undefined behavior
199 client->my_client_id = fnv1a_hash_string(url);
200
201 // Update registration name now that we have a client ID
202 {
203 char client_name[64];
204 SAFE_SNPRINTF(client_name, sizeof(client_name), "websocket_client_%u", client->my_client_id);
205 named_update_name((uintptr_t)client, client_name);
206 }
207
208 // Mark encryption as enabled if crypto context provided
209 client->encryption_enabled = (crypto_ctx != NULL);
210
211 // Store transport and mark as active
212 client->transport = transport;
213 atomic_store_bool(&client->connection_active, true);
214 atomic_store_bool(&client->connection_lost, false);
215
216 log_info("WebSocket client connected to %s (ID: %u)", url, client->my_client_id);
217
218 return transport;
219}
220
225 if (!client) {
226 return NULL;
227 }
228 return client->transport;
229}
230
237int websocket_client_send_packet(websocket_client_t *client, packet_type_t type, const void *data, size_t len) {
238 if (!client) {
239 return SET_ERRNO(ERROR_INVALID_PARAM, "NULL client");
240 }
241
242 if (!atomic_load_bool(&client->connection_active)) {
243 return SET_ERRNO(ERROR_NETWORK, "Connection not active");
244 }
245
246 if (!client->transport) {
247 return SET_ERRNO(ERROR_NETWORK, "No active transport");
248 }
249
250 // Acquire send mutex for thread-safe transmission
251 mutex_lock(&client->send_mutex);
252
253 // Send packet through transport with client ID
254 asciichat_error_t result = packet_send_via_transport(client->transport, type, data, len, client->my_client_id);
255
256 mutex_unlock(&client->send_mutex);
257
258 if (result != ASCIICHAT_OK) {
259 log_debug("Failed to send packet type %d: %s", type, asciichat_error_string(result));
260 return -1;
261 }
262
263 return 0;
264}
265
272 if (!client)
273 return -1;
274 return websocket_client_send_packet(client, PACKET_TYPE_PING, NULL, 0);
275}
276
283 if (!client)
284 return -1;
285 return websocket_client_send_packet(client, PACKET_TYPE_PONG, NULL, 0);
286}
287
292 return client ? client->my_client_id : 0;
293}
294
299 if (!client)
300 return false;
301 return client->encryption_enabled;
302}
Platform abstraction layer umbrella header providing unified cross-platform API.
void atomic_store_bool(atomic_t *a, bool value)
Atomically store a boolean value.
Definition atomic.c:177
bool atomic_load_bool(atomic_t *a)
Atomically load a boolean value.
Definition atomic.c:169
⚛️ Atomic operations abstraction layer with debug tracking
⚙️ Common definitions, error codes, macros, and types shared throughout the application
Named object registry for debugging — log identifiable resource names.
#️⃣ FNV-1a Hash Function Implementation
unsigned int uint32_t
Definition common.h:58
#define SAFE_STRNCPY(dst, src, size)
Definition common.h:414
#define SAFE_FREE(ptr)
Definition common.h:376
#define SAFE_MALLOC(size, cast)
Definition common.h:264
#define SAFE_SNPRINTF(buffer, buffer_size,...)
Definition common.h:492
#define NAMED_REGISTER_ATOMIC(a, name, parent_ptr)
Register an atomic_t with automatic format specifier.
#define NAMED_UNREGISTER(ptr)
Unregister a pointer.
#define NAMED_REGISTER_WEBSOCKET(websocket, name, parent_ptr)
Register a WebSocket connection with automatic format specifier.
const char * named_update_name(uintptr_t key, const char *new_base_name)
Update the registered name for a resource with a new base name.
#define SET_ERRNO(code, context_msg,...)
Set error code with custom context message and log it, returning the error code.
asciichat_error_t
Error and exit codes - unified status values (0-255)
Definition error_codes.h:49
@ ERROR_NETWORK
Definition error_codes.h:77
@ ASCIICHAT_OK
Definition error_codes.h:51
@ ERROR_INVALID_PARAM
#define log_error(...)
Log an ERROR message.
Definition log/log.h:587
#define log_info(...)
Log an INFO message.
Definition log/log.h:561
#define log_debug(...)
Log a DEBUG message.
Definition log/log.h:548
packet_type_t
Network protocol packet type enumeration.
Definition packet.h:286
@ PACKET_TYPE_PONG
Keepalive pong response.
Definition packet.h:385
@ PACKET_TYPE_PING
Keepalive ping packet.
Definition packet.h:383
#define mutex_lock(mutex)
Lock a mutex (with debug tracking in debug builds)
int mutex_init(mutex_t *mutex, const char *name)
Initialize a mutex with a name.
Definition threading.c:16
#define mutex_unlock(mutex)
Unlock a mutex (with debug tracking in debug builds)
int mutex_destroy(mutex_t *mutex)
Destroy a mutex.
Definition threading.c:22
WebSocket client abstraction for ascii-chat connections.
int websocket_client_send_pong(websocket_client_t *client)
Send pong frame (keepalive response)
int websocket_client_send_packet(websocket_client_t *client, packet_type_t type, const void *data, size_t len)
Send a packet through WebSocket connection (thread-safe)
bool websocket_client_is_active(const websocket_client_t *client)
Check if connection is currently active.
void websocket_client_shutdown(websocket_client_t *client)
Shutdown connection forcefully.
uint32_t websocket_client_get_id(const websocket_client_t *client)
Get the client's unique ID.
acip_transport_t * websocket_client_connect(websocket_client_t *client, const char *url, struct crypto_context_t *crypto_ctx)
Establish WebSocket connection to server.
websocket_client_t * websocket_client_create(const char *name)
Create and initialize named WebSocket client.
bool websocket_client_is_lost(const websocket_client_t *client)
Check if connection was lost.
void websocket_client_close(websocket_client_t *client)
Close connection gracefully.
void websocket_client_destroy(websocket_client_t **client_ptr)
Destroy WebSocket client and free resources.
acip_transport_t * websocket_client_get_transport(const websocket_client_t *client)
Get active transport instance.
bool websocket_client_is_encrypted(const websocket_client_t *client)
Check if encryption is enabled for this connection.
void websocket_client_signal_lost(websocket_client_t *client)
Signal that connection was lost.
int websocket_client_send_ping(websocket_client_t *client)
Send ping frame (keepalive heartbeat)
📝 Logging API with multiple log levels and terminal output control
struct websocket_client_t websocket_client_t
asciichat_error_t packet_send_via_transport(acip_transport_t *transport, packet_type_t type, const void *payload, size_t payload_len, uint32_t client_id)
Send packet via transport with proper header (exported for generic wrappers)
Definition send.c:41
ACIP shared/bidirectional packet sending functions.
Transport instance structure.
Definition transport.h:214
Cryptographic context structure.
Transport abstraction layer for ACIP protocol.
acip_transport_t * acip_websocket_client_transport_create(const char *name, const char *url, crypto_context_t *crypto_ctx)
Create WebSocket client transport.