#pragma once #include "httpd.h" /** * Websocket handle */ typedef struct Websock Websock; #define WEBSOCK_FLAG_NONE 0 #define WEBSOCK_FLAG_MORE (1<<0) // Set if the data is not the final data in the message; more follows #define WEBSOCK_FLAG_BIN (1<<1) // Set if the data is binary instead of text #define WEBSOCK_FLAG_CONT (1<<2) // set if this is a continuation frame (after WEBSOCK_FLAG_MORE) /* https://www.rfc-editor.org/rfc/rfc6455.html#section-7.4.1 */ typedef enum websock_close_reason { /// Normal close WS_CLOSE_OK = 1000, /// Server shutting down WS_CLOSE_GONE = 1001, /// Protocol error WS_CLOSE_PROTOCOL_ERR = 1002, /// Data type error (e.g. binary frame on a text socket) WS_CLOSE_DATA_TYPE_ERR = 1003, /// Data format error (e.g. malformed UTF-8) WS_CLOSE_DATA_FORMAT_ERR = 1007, /// Policy violation of some kind WS_CLOSE_POLICY_ERR = 1008, /// Message too big to handle WS_CLOSE_MESSAGE_TOO_BIG = 1009, /// Internal server error WS_CLOSE_SERVER_ERROR = 1011, } websock_close_reason; /** Type for the WS connect callback */ typedef void(*WsConnectedCb)(Websock *ws); /** Type for the WS receive callback */ typedef void(*WsRecvCb)(Websock *ws, uint8_t *data, size_t len, int flags); /** Type for the WS sending done callback */ typedef void(*WsSentCb)(Websock *ws); /** Type for the WS close callback */ typedef void(*WsCloseCb)(Websock *ws); /** Internal websocket struct (opaque) */ typedef struct WebsockPriv WebsockPriv; // TODO convert to container_of and avoid separate malloc for priv struct Websock { /// Pointer to arbitrary user data, put there e.g. during 'recvCb' void *userData; /// Connection pointer HttpdConnData *conn; /// Receive callback - new data; optional, set by user in WsConnectedCb WsRecvCb recvCb; /// Sent callback - last sending finished; optional, set by user in WsConnectedCb WsSentCb sentCb; /// Close callback - socket was closed, either by client or server ; optional, set by user in WsConnectedCb WsCloseCb closeCb; /// Websocket private data WebsockPriv *priv; }; /** * CGI function for a websocket endpoint * * @param connData * @return CGI state */ httpd_cgi_state cgiWebsocket(HttpdConnData *connData); /** * Send data to a websocket * * @param ws - Websocket handle * @param data - data to send * @param len - data len * @param flags - flags, OR'ed WEBSOCK_FLAG_X constants * @return 1 = OK */ int cgiWebsocketSend(Websock *ws, const uint8_t *data, size_t len, int flags); /** * Close a websocket * * @param ws - Websocket handle * @param reason - close reason (2 bytes). Normally 1000 */ void cgiWebsocketClose(Websock *ws, websock_close_reason reason); /** * Websocket endpoint receive handler (used in the CGI route) * * @param connData - connection * @param data - bytes received * @param len - data len * @return CGI state */ httpd_cgi_state cgiWebSocketRecv(HttpdConnData *connData, uint8_t *data, size_t len); /** * Broadcast a message to all open websockets at a given endpoint * * @param resource - websocket path * @param data - bytes received * @param len - data len * @param flags - OR'ed WEBSOCK_FLAG_X constants * @return Returns the amount of connections sent to, -1 if sending failed */ int cgiWebsockBroadcast(const char *resource, const uint8_t *data, size_t len, int flags); /** * Measure websockets backlog (debug telemetry) * * @param resource - websocket path * @param[out] total - the total backlog for the path is stored here * @param[out] max - the max backlog per websock is stored here */ void cgiWebsockMeasureBacklog(const char *resource, size_t *total, size_t *max);