1#ifndef VWS_WEBSOCKET_DECLARE
2#define VWS_WEBSOCKET_DECLARE
3
4#include <stdint.h>
5#include <stddef.h>
6
7#include <openssl/ssl.h>
8#include <openssl/err.h>
9#include <openssl/crypto.h>
10
11#include <stdbool.h>
12
13#include "vws.h"
14#include "socket.h"
15#include "util/sc_queue.h"
16
17#ifdef __cplusplus
18extern "C" {
19#endif
20
21//------------------------------------------------------------------------------
22// Frame
23//------------------------------------------------------------------------------
24
25/** @brief Defines the states of a WebSocket frame. */
26typedef enum
27{
28 /** Not all of the frame data has been received or processed. */
29 FRAME_INCOMPLETE,
30
31 /** All of the frame data has been received and processed. */
32 FRAME_COMPLETE,
33
34 /** There was an error in processing the frame data. */
35 FRAME_ERROR
36
37} fs_t;
38
39/** @brief Defines the types of WebSocket frames */
40typedef enum
41{
42 /** A continuation frame, data message split across multiple frames. */
43 CONTINUATION_FRAME = 0x0,
44
45 /** A text data frame. */
46 TEXT_FRAME = 0x1,
47
48 /** A binary data frame. */
49 BINARY_FRAME = 0x2,
50
51 /** A close control frame. */
52 CLOSE_FRAME = 0x8,
53
54 /** A ping control frame. */
55 PING_FRAME = 0x9,
56
57 /** A pong control frame, in response to a ping. */
58 PONG_FRAME = 0xA
59
60} frame_type_t;
61
62/**
63 * @brief Represents a Websocket frame.
64 */
65typedef struct vws_frame
66{
67 /**< Final frame in the message (1) or not (0). */
68 unsigned char fin;
69
70 /**< Defines the interpretation of the payload data. */
71 unsigned char opcode;
72
73 /**< Defines whether the payload is masked. */
74 unsigned char mask;
75
76 /**< Position of the data in the frame. */
77 unsigned int offset;
78
79 /**< The size of the payload data. */
80 unsigned long long size;
81
82 /**< The payload data for the frame. */
83 unsigned char* data;
84
85} vws_frame;
86
87/**
88 * @brief Instantiates a new websocket frame given some data, size, and opcode.
89 *
90 * @param data The data to be contained in the frame.
91 * @param s The size of the data.
92 * @param oc The opcode for the frame.
93 * @return Returns a pointer to the new frame.
94 */
95vws_frame* vws_frame_new(ucstr data, size_t s, unsigned char oc);
96
97/**
98 * @brief Frees memory associated with a vws_frame object.
99 *
100 * @param frame The vws_frame object to free.
101 * @return void
102 *
103 * @ingroup FrameFunctions
104 */
105void vws_frame_free(vws_frame* frame);
106
107/**
108 * @brief Serializes a vws_frame into a buffer that can be sent over the
109 * network.
110 *
111 * @param f The vws_frame to serialize.
112 * @return A pointer to the serialized buffer.
113 *
114 * @ingroup FrameFunctions
115 */
116vws_buffer* vws_serialize(vws_frame* f);
117
118/**
119 * @brief Deserializes raw network data into a vws_frame, updating consumed
120 * with the number of bytes processed.
121 *
122 * @param data The raw network data.
123 * @param size The size of the data.
124 * @param f The vws_frame to deserialize into.
125 * @param consumed Pointer to the number of bytes consumed during
126 * deserialization.
127 * @return The status of the deserialization process, 0 if successful, an error
128 * code otherwise.
129 *
130 * @ingroup FrameFunctions
131 */
132fs_t vws_deserialize(ucstr data, size_t size, vws_frame* f, size_t* consumed);
133
134/**
135 * @brief Generates a close frame for a WebSocket connection.
136 *
137 * @return A pointer to the generated close frame.
138 *
139 * @ingroup FrameFunctions
140 */
141vws_buffer* vws_generate_close_frame();
142
143/**
144 * @brief Generates a pong frame in response to a received ping frame.
145 *
146 * @param ping_data The data from the received ping frame.
147 * @param s The size of the ping data.
148 * @return A pointer to the generated pong frame.
149 *
150 * @ingroup FrameFunctions
151 */
152vws_buffer* vws_generate_pong_frame(ucstr ping_data, size_t s);
153
154/**
155 * @brief Dumps the contents of a WebSocket frame for debugging purposes.
156 *
157 * @param data The data of the WebSocket frame.
158 * @param size The size of the WebSocket frame.
159 *
160 * @ingroup TraceFunctions
161 */
162void vws_dump_websocket_frame(const unsigned char* data, size_t size);
163
164//------------------------------------------------------------------------------
165// Message
166//------------------------------------------------------------------------------
167
168/**
169 * @brief Represents a websocket message, which contains opcode and data.
170 */
171typedef struct vws_msg
172{
173 /**< Defines the interpretation of the payload data. */
174 unsigned char opcode;
175
176 /**< The payload data for the message. */
177 vws_buffer* data;
178} vws_msg;
179
180/**
181 * @brief Instantiates a new websocket message.
182 *
183 * @return Returns a pointer to the new message.
184 */
185vws_msg* vws_msg_new();
186
187/**
188 * @brief Deallocates a websocket message.
189 *
190 * @param msg The message to be deallocated.
191 */
192void vws_msg_free(vws_msg* msg);
193
194//------------------------------------------------------------------------------
195// Connection
196//------------------------------------------------------------------------------
197
198struct vws_cnx;
199
200/**
201 * @brief Callback for frame processing
202 * @param cnx The connection instance
203 * @param frame The frame to process
204 */
205typedef void (*vws_process_frame)(struct vws_cnx* cnx, vws_frame* frame);
206
207typedef struct vws_url_data
208{
209 char* href;
210 char* protocol;
211 char* host;
212 char* auth;
213 char* hostname;
214 char* pathname;
215 char* search;
216 char* path;
217 char* hash;
218 char* query;
219 char* port;
220} vws_url_data;
221
222/**
223 * @defgroup ConnectionFunctions
224 *
225 * @brief Functions that manage WebSocket connections
226 *
227 */
228
229/**
230 * @brief Callback for disconnect. This is called after socket disconnect. This
231 * allows additional logic to be hooked into disconnect process.
232 * @param cnx The connection instance
233 * @return Returns true on successful reconnect, false otherwise.
234 */
235typedef void (*vws_cnx_disconnect)(struct vws_cnx* cnx);
236
237/**
238 * @brief A WebSocket connection.
239 */
240typedef struct vws_cnx
241{
242 /**< Base class */
243 struct vws_socket base;
244
245 /**< Connection state flags. */
246 uint64_t flags;
247
248 /**< The URL of the websocket server. */
249 vws_url_data* url;
250
251 /**< The websocket key. */
252 char* key;
253
254 /**< Queue for incoming frames. */
255 struct sc_queue_ptr queue;
256
257 /**< Frame processing callback. */
258 vws_process_frame process;
259
260 /**< Disconnect callback. */
261 vws_cnx_disconnect disconnect;
262
263 /**< User-defined data associated with the connection */
264 char* data;
265
266} vws_cnx;
267
268/**
269 * @brief Generates a WebSocket accept key from input.
270 *
271 * @param key The input key
272 * @return Returns the accept key on the heap. Caller MUST call free() on return
273 * value
274 *
275 * @ingroup ConnectionFunctions
276 */
277cstr vws_accept_key(cstr key);
278
279/**
280 * @brief Connects to a specified host URL.
281 *
282 * @param c The websocket connection.
283 * @param uri The URL of the host to connect to.
284 * @return Returns true if the connection is successful, false otherwise.
285 *
286 * @ingroup ConnectionFunctions
287 */
288bool vws_connect(vws_cnx* c, cstr uri);
289
290/**
291 * @brief Attempts to reconnects based on previous URL. If no previous
292 * connection was made, this function does nothing and returns false.
293 *
294 * @param c The websocket connection.
295 * @return Returns true if the connection is successful, false otherwise.
296 *
297 * @ingroup ConnectionFunctions
298 */
299bool vws_reconnect(vws_cnx* c);
300
301/**
302 * @brief Closes the connection to the host.
303 *
304 * @param c The websocket connection.
305 *
306 * @ingroup ConnectionFunctions
307 */
308void vws_disconnect(vws_cnx* c);
309
310/**
311 * @brief Allocates a new websocket connection.
312 *
313 * @return A pointer to the new connection instance.
314 *
315 * @ingroup ConnectionFunctions
316 */
317vws_cnx* vws_cnx_new();
318
319/**
320 * @brief Deallocates a websocket connection.
321 *
322 * @param c The websocket connection.
323 *
324 * @ingroup ConnectionFunctions
325 */
326void vws_cnx_free(vws_cnx* c);
327
328/**
329 * @brief Checks connection state
330 *
331 * @param c The websocket connection.
332 * @return Returns true if connection is established, false otherwise. If false,
333 * sets vrtql.e to VE_SOCKET.
334 *
335 * @ingroup ConnectionFunctions
336 */
337bool vws_cnx_is_connected(vws_cnx* c);
338
339/**
340 * @brief Sets the connection to server mode
341 *
342 * @param c The websocket connection.
343 * @return Returns void.
344 *
345 * @ingroup ConnectionFunctions
346 */
347void vws_cnx_set_server_mode(vws_cnx* c);
348
349/**
350 * @brief Processes incoming data from a Socket.
351 *
352 * This function parses the data in the socket buffer and attempts to extract
353 * complete WebSocket frames. It processes as many frames as possible and
354 * calls the appropriate processing function for each frame. The consumed
355 * data is then drained from the buffer.
356 *
357 * @param c The WebSocket connection.
358 * @return The total number of bytes consumed from the socket buffer.
359 * If no complete frame is available or an error occurs, it returns 0.
360 *
361 * @ingroup ConnectionFunctions
362 */
363ssize_t vws_cnx_ingress(vws_cnx* c);
364
365//------------------------------------------------------------------------------
366// Messaging API
367//------------------------------------------------------------------------------
368
369/**
370 * @brief Sends a TEXT frame
371 *
372 * @param c The connection.
373 * @param string The text to send.
374
375 * @return Returns the number of bytes sent or -1 on error. In the case of
376 * error, check vws.e for details, especially for VE_SOCKET.
377 */
378ssize_t vws_frame_send_text(vws_cnx* c, cstr string);
379
380/**
381 * @brief Sends a BINARY frame
382 *
383 * @param c The connection.
384 * @param string The data to send.
385 * @param size The size of the data in bytes.
386 * @return Returns the number of bytes sent or -1 on error. In the case of
387 * error, check vws.e for details, especially for VE_SOCKET.
388 */
389ssize_t vws_frame_send_binary(vws_cnx* c, ucstr string, size_t size);
390
391/**
392 * @brief Sends custom frame containing data
393 *
394 * @param c The connection.
395 * @param dataThe data to send.
396 * @param size The size of the data in bytes.
397 * @param oc The websocket opcode defining the frame type.
398 * @return Returns the number of bytes sent or -1 on error. In the case of
399 * error, check vws.e for details, especially for VE_SOCKET.
400 */
401ssize_t vws_frame_send_data(vws_cnx* c, ucstr data, size_t size, int oc);
402
403/**
404 * @brief Sends a prebuilt websocket frame. This function will take ownership of
405 * the frame and deallocate it for the caller.
406 *
407 * @param c The connection.
408 * @param frame The frame to send. This function will take ownership of the
409 * frame and deallocate it for the caller.
410 * @return Returns the number of bytes sent or -1 on error. In the case of
411 * error, check vws.e for details, especially for VE_SOCKET.
412 */
413ssize_t vws_frame_send(vws_cnx* c, vws_frame* frame);
414
415/**
416 * @brief Sends a TEXT message
417 *
418 * @param c The connection.
419 * @param string The text to send.
420 * @return Returns the number of bytes sent or -1 on error. In the case of
421 * error, check vws.e for details, especially for VE_SOCKET.
422 */
423ssize_t vws_msg_send_text(vws_cnx* c, cstr string);
424
425/**
426 * @brief Sends a BINARY message
427 *
428 * @param c The connection.
429 * @param string The data to send.
430 * @param size The size of the data in bytes.
431 * @return Returns the number of bytes sent or -1 on error. In the case of
432 * error, check vws.e for details, especially for VE_SOCKET.
433 */
434ssize_t vws_msg_send_binary(vws_cnx* c, ucstr string, size_t size);
435
436/**
437 * @brief Sends custom message containing
438 *
439 * @param c The connection.
440 * @param dataThe data to send.
441 * @param size The size of the data in bytes.
442 * @param oc The websocket opcode defining the frame type.
443 * @return Returns the number of bytes sent or -1 on error. In the case of
444 * error, check vws.e for details, especially for VE_SOCKET.
445 */
446ssize_t vws_msg_send_data(vws_cnx* c, ucstr data, size_t size, int oc);
447
448/**
449 * @brief Receives a websocket message from the connection. If there are no
450 * messages in queue, it will call socket_wait_for_frame().
451 *
452 * @param c The connection.
453 * @return Returns the most recent websocket message or NULL if the socket
454 * timed out without receiving a full message. If NULL, check for
455 * socket error (vws.e.code == VE_SOCKET or vws_cnx_is_connected()).
456 */
457vws_msg* vws_msg_recv(vws_cnx* c);
458
459/**
460 * @brief Removes and returns the first complete message from the connection's
461 * message queue.
462 *
463 * @param c The vws_cnx representing the WebSocket connection.
464 * @return A pointer to the popped vws_msg object, or NULL if the queue is
465 * empty.
466 *
467 * @ingroup MessageFunctions
468 */
469vws_msg* vws_msg_pop(vws_cnx* c);
470
471/**
472 * @brief Receives a websocket frame from the connection. If there are no
473 * frames in queue, it will call socket_wait_for_frame().
474 *
475 * @param c The connection.
476 * @return Returns the most recent websocket frame or NULL if the socket timed
477 * out without receiving a full frame. If NULL, check for
478 * socket error (vws.e.code == VE_SOCKET or vws_cnx_is_connected()).
479 */
480vws_frame* vws_frame_recv(vws_cnx* c);
481
482#ifdef __cplusplus
483}
484#endif
485
486#endif /* VWS_WEBSOCKET_DECLARE */
487