WSLAY_EVENT_CONTEXT_FREE(3)

πŸ’– WSLAY_EVENT_CONTEXT_FREE β€” the wslay(3) girlies' guide to event contexts πŸ’–

okay bestie, so this is a page from wslay(3), and it's literally the origin story of your event-based API context. no cap, it's the setup page: you init a context, you hand it your callbacks, you vibe, and when you're done you free it. let's get into it 🫢


✨ NAME ✨

wslay_event_context_free β€” Initialize an event-based API context

(yes the NAME says "free" but the page is really about init + free, the whole glow-up arc, we move πŸ’…)


🌸 SYNOPSIS 🌸

#include <wslay/wslay.h>

int wslay_event_context_server_init(wslay_event_context_ptr *ctx, const
struct wslay_event_callbacks *callbacks, void *user_data)

int wslay_event_context_client_init(wslay_event_context_ptr *ctx, const
struct wslay_event_callbacks *callbacks, void *user_data)

void wslay_event_context_free(wslay_event_context_ptr ctx)

πŸ¦‹ DESCRIPTION πŸ¦‹

so here's the tea β˜• β€” wslay_event_context_server_init() sets you up an event-based API context for WebSocket server use. and wslay_event_context_client_init() does the exact same thing but for WebSocket client use. if they come back successful, ctx will point to a structure that holds, like, every resource you need to process WebSocket protocol transfers. it's giving "your whole setup, pre-assembled, main character energy" πŸ‘‘

callbacks is a pointer to wslay_event_callbacks, which is defined like this:

struct wslay_event_callbacks {
    wslay_event_recv_callback                recv_callback;
    wslay_event_send_callback                send_callback;
    wslay_event_genmask_callback             genmask_callback;
    wslay_event_on_frame_recv_start_callback on_frame_recv_start_callback;
    wslay_event_on_frame_recv_chunk_callback on_frame_recv_chunk_callback;
    wslay_event_on_frame_recv_end_callback   on_frame_recv_end_callback;
    wslay_event_on_msg_recv_callback         on_msg_recv_callback;
};

now let's meet the whole friend group one by one, they're all iconic in their own way 🌈

  • typedef ssize_t (*wslay_event_recv_callback)(wslay_event_context_ptr ctx, uint8_t *buf, size_t len, int flags, void *user_data) β€” so recv_callback gets invoked by wslay_event_recv() when it's like "okay i need more data from the peer". your implementation has to read at most len bytes from the peer, stash them in buf, and return the number of bytes you read. flags is always 0 in this version, so don't go looking for lore there πŸ™ˆ

if something goes wrong, return -1 and set the error code WSLAY_ERR_CALLBACK_FAILURE using wslay_event_set_error(). also important: the whole wslay event-based API assumes non-blocking I/O. if your error's cause is EAGAIN or EWOULDBLOCK, set WSLAY_ERR_WOULDBLOCK instead. this matters SO much because it's literally how you tell wslay_event_recv() to stop receiving further data and return. don't skip that part, it's the plot twist πŸ”₯

  • typedef ssize_t (*wslay_event_send_callback)(wslay_event_context_ptr ctx, const uint8_t *data, size_t len, int flags, void *user_data) β€” send_callback gets invoked by wslay_event_send() when it wants to send more data to the peer. your implementation must send at most len bytes to the peer and return how many bytes you actually sent. flags is the bitwise OR of zero or more of this flag:
    • WSLAY_MSG_MORE β€” there is more data to send

it provides some hints to tune performance and behaviour, iykyk ✨

if there's an error, return -1 and set the error code WSLAY_ERR_CALLBACK_FAILURE using wslay_event_set_error(). and again, non-blocking I/O is the whole vibe here. if the cause is EAGAIN or EWOULDBLOCK, set WSLAY_ERR_WOULDBLOCK instead. crucial, because that's what tells wslay_event_send() to stop sending data and return. periodt πŸ’…

  • typedef int (*wslay_event_genmask_callback)(wslay_event_context_ptr ctx, uint8_t *buf, size_t len, void *user_data) β€” genmask_callback gets invoked by wslay_event_send() when it wants a new mask key. as described in RFC6455, only the traffic coming from the WebSocket client is masked, so this callback is only needed if an event-based API is initialized for WebSocket client use. your implementation must fill exactly len bytes of data in buf and return 0 on success. if there's an error, return -1 and set the error code WSLAY_ERR_CALLBACK_FAILURE using wslay_event_set_error(). no notes, just precision πŸŽ€
  • typedef void (*wslay_event_on_frame_recv_start_callback)(wslay_event_context_ptr ctx, const struct wslay_event_on_frame_recv_start_arg *arg, void *user_data) β€” on_frame_recv_start_callback gets invoked by wslay_event_recv() when a new frame starts being received. and listen, this one is only invoked once for each frame, so it's like a special occasion, not an every-day thing πŸ’« wslay_event_on_frame_recv_start_arg is defined like this:
struct wslay_event_on_frame_recv_start_arg {
    uint8_t  fin;
    uint8_t  rsv;
    uint8_t  opcode;
    uint64_t payload_length;
};

fin, rsv and opcode are the fin bit, the reserved bits and the opcode of a frame. payload_length is the payload length of a frame. slay.

  • typedef void (*wslay_event_on_frame_recv_chunk_callback)(wslay_event_context_ptr ctx, const struct wslay_event_on_frame_recv_chunk_arg *arg, void *user_data) β€” on_frame_recv_chunk_callback gets invoked by wslay_event_recv() when a chunk of frame payload arrives. wslay_event_on_frame_recv_chunk_arg is defined like this:
struct wslay_event_on_frame_recv_chunk_arg {
    const uint8_t *data;
    size_t         data_length;
};

data points to a chunk of payload data. data_length is the length of a chunk. little bites, very mindful eating energy πŸ“

  • typedef void (*wslay_event_on_frame_recv_end_callback)(wslay_event_context_ptr ctx, void *user_data) β€” on_frame_recv_end_callback gets invoked by wslay_event_recv() when a frame is completely received. that's the confetti moment, the frame is fully here πŸŽ‰
  • typedef void (*wslay_event_on_msg_recv_callback)(wslay_event_context_ptr ctx, const struct wslay_event_on_msg_recv_arg *arg, void *user_data) β€” on_msg_recv_callback gets invoked by wslay_event_recv() when a message is completely received. wslay_event_on_msg_recv_arg is defined like this:
struct wslay_event_on_msg_recv_arg {
    uint8_t        rsv;
    uint8_t        opcode;
    const uint8_t *msg;
    size_t         msg_length;
    uint16_t       status_code;
};

the rsv member and the opcode member are the reserved bits and the opcode of the received message respectively. the rsv member is constructed like this, and this is a formula bestie, copy it exactly:

rsv = (RSV1 << 2) | (RSV2 << 1) | RSV3

the msg member points to the message of the received message. the msg_length member is the length of the message. now here's the niche case: if a message is a close control frame, in other words if opcode == WSLAY_CONNECTION_CLOSE, then status_code is set to the status code in the close control frame. and if no status code is included in the close control frame, status_code is set to 0. zero. nothing. ghosted πŸ’€

okay and then user_data is an arbitrary pointer, which is directly passed to each callback function as the user_data argument. your little personal item, carried everywhere 🫢

when your initialized event-based API context ctx is no longer used, use wslay_event_context_free() to free any resources allocated for ctx. that's the goodbye, be graceful about it ✨


🌟 RETURN VALUE 🌟

wslay_event_context_server_init() and wslay_event_context_client_init() return 0 if it succeeds, or one of these negative error codes if not:

  • WSLAY_ERR_NOMEM β€” Out of memory. the classic. it's giving "the machine said no" 😭

πŸ‘‘ SEE ALSO πŸ‘‘

wslay_event_send(), wslay_event_recv(), wslay_event_set_error()

(the whole squad, go read them next, you'll be obsessed)


πŸ’œ AUTHOR πŸ’œ

Tatsuhiro Tsujikawa


2024, 2015, Tatsuhiro Tsujikawa


and that's the page bestie! this came out of wslay version @PACKAGE_VERSION@, dated Apr 22, 2024 β€” so if you're quoting it in the group chat, cite it properly, we love a source-having queen 🌸 bye pookie, go init that context πŸ’…πŸ¦‹

πŸ“¦ where this page comes from