blob: 1a3aaf0a15ba49bc8443feb4f75047ad402bcdaa [file] [log] [blame] [raw]
/*
copyright: Boaz segev, 2016-2017
license: MIT
Feel free to copy, use and enjoy according to the license provided.
*/
#ifndef WEBSOCKETS_H
#define WEBSOCKETS_H
#include "http.h"
/* *****************************************************************************
Upgrading from HTTP to Websockets
***************************************************************************** */
/**
The Websocket type is an opaque type used by the websocket API to provide
identify a specific Websocket connection and manage it's internal state.
*/
typedef struct Websocket ws_s;
/**
The protocol / service identifier for `libserver`.
*/
extern char *WEBSOCKET_ID_STR;
/**
The Websocket Handler contains all the settings required for new websocket
connections.
This struct is used for the named agruments in the `websocket_upgrade`
macro.
*/
typedef struct {
/**
The (optional) on_message callback will be called whenever a websocket message
is
received for this connection.
The data received points to the websocket's message buffer and it will be
overwritten once the function exits (it cannot be saved for later, but it can
be copied).
*/
void (*on_message)(ws_s *ws, char *data, size_t size, uint8_t is_text);
/**
The (optional) on_open callback will be called once the websocket connection
is established and before is is registered with `facil`, so no `on_message`
events are raised before `on_open` returns.
*/
void (*on_open)(ws_s *ws);
/**
The (optional) on_ready callback will be after a the underlying socket's
buffer changes it's state from full to available.
If the socket's buffer is never full, the callback is never called.
It should be noted that `libsock` manages the socket's buffer overflow and
implements and augmenting user-land buffer, allowing data to be safely written
to the websocket without worrying over the socket's buffer.
*/
void (*on_ready)(ws_s *ws);
/**
The (optional) on_shutdown callback will be called if a websocket connection
is still open while the server is shutting down (called before `on_close`).
*/
void (*on_shutdown)(ws_s *ws);
/**
The (optional) on_close callback will be called once a websocket connection is
terminated or failed to be established.
*/
void (*on_close)(ws_s *ws);
/** The `http_request_s` to be converted ("upgraded") to a websocket
* connection. Either a request or a response object is required.*/
http_request_s *request;
/**
The (optional) HttpResponse to be used for sending the upgrade response.
Using this object allows cookies to be set before "upgrading" the connection.
The ownership of the response object will remain unchanged - so if you have
created the response object, you should free it.
*/
http_response_s *response;
/**
The maximum websocket message size/buffer (in bytes) for this connection.
*/
size_t max_msg_size;
/** Opaque user data. */
void *udata;
/**
Timeout for the websocket connections, a ping will be sent
whenever the timeout is reached. Connections are only closed when a ping
cannot be sent (the network layer fails). Pongs aren't reviewed.
*/
uint8_t timeout;
} websocket_settings_s;
/** This macro allows easy access to the `websocket_upgrade` function. The macro
* allows the use of named arguments, using the `websocket_settings_s` struct
* members. i.e.:
*
* on_message(ws_s * ws, char * data, size_t size, int is_text) {
* ; // ... this is the websocket on_message callback
* websocket_write(ws, data, size, is_text); // a simple echo example
* }
*
* on_request(http_request_s* request) {
* websocket_upgrade( .request = request, .on_message = on_message);
* }
*
* Returns 0 on sucess and -1 on failure. A response is always sent.
*/
ssize_t websocket_upgrade(websocket_settings_s settings);
#define websocket_upgrade(...) \
websocket_upgrade((websocket_settings_s){__VA_ARGS__})
/* *****************************************************************************
Websocket information
***************************************************************************** */
/** Returns the opaque user data associated with the websocket. */
void *websocket_udata(ws_s *ws);
/**
Sets the opaque user data associated with the websocket.
Returns the old value, if any.
*/
void *websocket_udata_set(ws_s *ws, void *udata);
/**
Returns the underlying socket UUID.
This is only relevant for collecting the protocol object from outside of
websocket events, as the socket shouldn't be written to.
*/
intptr_t websocket_uuid(ws_s *ws);
/**
Counts the number of websocket connections.
*/
size_t websocket_count(void);
/* *****************************************************************************
Websocket Connection Management (write / close)
***************************************************************************** */
/** Writes data to the websocket. Returns -1 on failure (0 on success). */
int websocket_write(ws_s *ws, void *data, size_t size, uint8_t is_text);
/** Closes a websocket connection. */
void websocket_close(ws_s *ws);
/* *****************************************************************************
Websocket Pub/Sub
=================
API for websocket pub/sub that can be used to publish messages across process
boundries.
Supports pub/sub engines (see {pubsub.h}) that can connect to a backend service
such as Redis.
The default pub/sub engine (if `NULL` or unspecified) will publish the messages
to the process cluster (all the processes in `facil_run`).
To publish to a channel, use the API provided in {pubsub.h}.
***************************************************************************** */
/** Pub/sub engine type. Engine documentation is in `pubsub.h` */
typedef struct pubsub_engine_s pubsub_engine_s;
/** Incoming pub/sub messages will be passed along using this data structure. */
typedef struct {
/** the websocket receiving the message. */
ws_s *ws;
/** the pub/sub engine from which where the message originated. */
struct pubsub_engine_s *engine;
/** the Websocket pub/sub subscription ID. */
uintptr_t subscription_id;
/** the channel where the message was published. */
struct {
const char *name;
size_t len;
} channel;
/** the published message. */
struct {
const char *data;
size_t len;
} msg;
/** Pattern matching was used for channel subscription. */
unsigned use_pattern : 1;
/** user opaque data. */
void *udata;
} websocket_pubsub_notification_s;
/** Possible arguments for the {websocket_subscribe} function. */
struct websocket_subscribe_s {
/** the websocket receiving the message. REQUIRED. */
ws_s *ws;
/**
* The pub/sub engine to use.
*
* Default: engine will publish messages throughout the facil process cluster.
*/
struct pubsub_engine_s *engine;
/** the channel where the message was published. */
struct {
const char *name;
size_t len;
} channel;
/**
* The callback that handles pub/sub notifications.
*
* Default: send directly to websocket client.
*/
void (*on_message)(websocket_pubsub_notification_s notification);
/** User opaque data, passed along to the notification. */
void *udata;
/** Use pattern matching for channel subscription. */
unsigned use_pattern : 1;
/**
* When using client forwarding (no `on_message` callback), this indicates if
* messages should be sent to the client as binary blobs, which is the safest
* approach.
*
* Default: tests for UTF-8 data encoding and sends as text if valid UTF-8.
* Messages above ~32Kb are always assumed to be binary.
*/
unsigned force_binary : 1;
/**
* When using client forwarding (no `on_message` callback), this indicates if
* messages should be sent to the client as text.
*
* `force_binary` has precedence.
*
* Default: see above.
*
*/
unsigned force_text : 1;
};
/**
* Subscribes to a channel. See {struct websocket_subscribe_s} for possible
* arguments.
*
* Returns a subscription ID on success and 0 on failure.
*
* All subscriptions are automatically revoked once the websocket is closed.
*
* If the connections subscribes to the same channel more than once, messages
* will be merged. However, another subscription ID will be assigned, since two
* calls to {websocket_unsubscribe} will be required in order to unregister from
* the channel.
*/
uintptr_t websocket_subscribe(struct websocket_subscribe_s args);
#define websocket_subscribe(wbsckt, ...) \
websocket_subscribe((struct websocket_subscribe_s){.ws = wbsckt, __VA_ARGS__})
/**
* Finds an existing subscription (in case the subscription ID wasn't saved).
* See {struct websocket_subscribe_s} for possible arguments.
*
* Returns the existing subscription's ID (if exists) or 0 (no subscription).
*/
uintptr_t websocket_find_sub(struct websocket_subscribe_s args);
#define websocket_find_sub(wbsckt, ...) \
websocket_find_sub((struct websocket_subscribe_s){.ws = wbsckt, __VA_ARGS__})
/**
* Unsubscribes from a channel.
*
* Failures are silent.
*
* All subscriptions are automatically revoked once the websocket is closed. So
* only use this function to unsubscribe while the websocket is open.
*/
void websocket_unsubscribe(ws_s *ws, uintptr_t subscription_id);
/* *****************************************************************************
Websocket Tasks - within a single process scope, NOT and entire cluster
*****************************************************************************
*/
/** The named arguments for `websocket_each` */
struct websocket_each_args_s {
/** The websocket originating the task. It will be excluded for the loop. */
ws_s *origin;
/** The task (function) to be performed. This is required. */
void (*task)(ws_s *ws_target, void *arg);
/** User opaque data to be passed along. */
void *arg;
/** The on_finish callback is always called. Good for cleanup. */
void (*on_finish)(ws_s *origin, void *arg);
};
/**
Performs a task on each websocket connection that shares the same process
(except the originating `ws_s` connection which is allowed to be NULL).
*/
void websocket_each(struct websocket_each_args_s args);
#define websocket_each(...) \
websocket_each((struct websocket_each_args_s){__VA_ARGS__})
/**
The Arguments passed to the `websocket_write_each` function / macro are defined
here, for convinience of calling the function.
*/
struct websocket_write_each_args_s {
/** The originating websocket client will be excluded from the `write`.
* Can be NULL. */
ws_s *origin;
/** The data to be written to the websocket - required(!) */
void *data;
/** The length of the data to be written to the websocket - required(!) */
size_t length;
/** Text mode vs. binary mode. Defaults to binary mode. */
uint8_t is_text;
/** Set to 1 to send the data to websockets where this application is the
* client. Defaults to 0 (the data is sent to all clients where this
* application is the server). */
uint8_t as_client;
/** A filter callback, allowing us to exclude some clients.
* Should return 1 to send data and 0 to exclude. */
uint8_t (*filter)(ws_s *ws_to, void *arg);
/** A callback called once all the data was sent. */
void (*on_finished)(ws_s *ws_origin, void *arg);
/** A user specified argumernt passed to each of the callbacks. */
void *arg;
};
/**
Writes data to each websocket connection that shares the same process
(except the originating `ws_s` connection which is allowed to be NULL).
Accepts a sing `struct websocket_write_each_args_s` argument. See the struct
details for possible arguments.
*/
int websocket_write_each(struct websocket_write_each_args_s args);
#define websocket_write_each(...) \
websocket_write_each((struct websocket_write_each_args_s){__VA_ARGS__})
#endif