+++ /dev/null
-#include "private-libwebsockets.h"
-#include "extension-x-google-mux.h"
-
-#define MUX_REAL_CHILD_INDEX_OFFSET 2
-
-static int ongoing_subchannel;
-static struct libwebsocket * tag_with_parent = NULL;
-static int client_handshake_generation_is_for_mux_child;
-
-static int lws_addheader_mux_opcode(unsigned char *pb, int len)
-{
- unsigned char *start = pb;
-
- *pb++ = LWS_WS_OPCODE_07__NOSPEC__MUX | 0x80;
- if (len < 126)
- *pb++ = len;
- else {
- if (len > 65535) {
- *pb++ = 127;
- *pb++ = 0;
- *pb++ = 0;
- *pb++ = 0;
- *pb++ = 0;
- *pb++ = (len ) >> 24;
- *pb++ = (len) >> 16;
- *pb++ = (len) >> 8;
- *pb++ = (len) >> 0;
- } else {
- *pb++ = 126;
- *pb++ = (len) >> 8;
- *pb++ = (len) >> 0;
- }
- }
-
- return pb - start;
-}
-
-static int
-lws_mux_subcommand_header(int cmd, int channel, unsigned char *pb, int len)
-{
- unsigned char *start = pb;
-
- if (channel == 0) {
- muxdebug("lws_mux_subcommand_header: given ch 0\n");
- assert(0);
- }
-
- if (channel < 31)
- *pb++ = (channel << 3) | cmd;
- else {
- *pb++ = (31 << 3) | cmd;
- *pb++ = channel >> 8;
- *pb++ = channel;
- }
-
- if (len <= 253)
- *pb++ = len;
- else {
- if (len <= 65535) {
- *pb++ = 254;
- *pb++ = len >> 8;
- *pb++ = len;
- } else {
- *pb++ = 255;
- *pb++ = len >> 24;
- *pb++ = len >> 16;
- *pb++ = len >> 8;
- *pb++ = len;
- }
- }
-
- return pb - start;
-}
-
-static int lws_ext_x_google_mux__send_addchannel(
- struct libwebsocket_context *context,
- struct libwebsocket *wsi,
- struct lws_ext_x_google_mux_conn *parent_conn,
- struct libwebsocket *wsi_child,
- int channel,
- const char *url
-) {
-
- unsigned char send_buf[LWS_SEND_BUFFER_PRE_PADDING + 2048 +
- LWS_SEND_BUFFER_POST_PADDING];
- unsigned char *pb = &send_buf[LWS_SEND_BUFFER_PRE_PADDING];
- char *p;
- char delta_headers[1536];
- int delta_headers_len;
- int subcommand_length;
- int n;
-
- if (channel == 0) {
- muxdebug("lws_ext_x_google_mux__send_addchannel: given ch 0\n");
- assert(0);
- }
-
- wsi_child->ietf_spec_revision = wsi->ietf_spec_revision;
-
- client_handshake_generation_is_for_mux_child = 1;
- p = libwebsockets_generate_client_handshake(context, wsi_child,
- delta_headers);
- client_handshake_generation_is_for_mux_child = 0;
- delta_headers_len = p - delta_headers;
-
- subcommand_length = lws_mux_subcommand_header(
- LWS_EXT_XGM_OPC__ADDCHANNEL, channel, pb, delta_headers_len);
-
- pb += lws_addheader_mux_opcode(pb, subcommand_length + delta_headers_len);
- pb += lws_mux_subcommand_header(LWS_EXT_XGM_OPC__ADDCHANNEL, channel,
- pb, delta_headers_len);
-
-// n = sprintf((char *)pb, "%s\x0d\x0a", url);
-// pb += n;
-
- if (delta_headers_len)
- memcpy(pb, delta_headers, delta_headers_len);
-
- pb += delta_headers_len;
-
- muxdebug("add channel sends %ld\n",
- pb - &send_buf[LWS_SEND_BUFFER_PRE_PADDING]);
-
- parent_conn->defeat_mux_opcode_wrapping = 1;
-
- /* send the request to the server */
-
- n = lws_issue_raw_ext_access(wsi, &send_buf[LWS_SEND_BUFFER_PRE_PADDING],
- pb - &send_buf[LWS_SEND_BUFFER_PRE_PADDING]);
-
- parent_conn->defeat_mux_opcode_wrapping = 0;
-
- return n;
-}
-
-/**
- * lws_extension_x_google_mux_parser(): Parse mux buffer headers coming in
- * from a muxed connection into subchannel
- * specific actions
- * @wsi: muxed websocket instance
- * @conn: x-google-mux private data bound to that @wsi
- * @c: next character in muxed stream
- */
-
-static int
-lws_extension_x_google_mux_parser(struct libwebsocket_context *context,
- struct libwebsocket *wsi,
- struct libwebsocket_extension *this_ext,
- struct lws_ext_x_google_mux_conn *conn, unsigned char c)
-{
- struct libwebsocket *wsi_child = NULL;
- struct libwebsocket_extension *ext;
- struct lws_ext_x_google_mux_conn *child_conn = NULL;
- int n;
- void *v;
-
-// lwsl_debug("XRX: %02X %d %d\n", c, conn->state, conn->length);
-
- /*
- * [ <Channel ID b12.. b8> <Mux Opcode b2..b0> ]
- * [ <Channel ID b7.. b0> ]
- */
-
- switch (conn->state) {
-
- case LWS_EXT_XGM_STATE__MUX_BLOCK_1:
-// lwsl_ext("LWS_EXT_XGM_STATE__MUX_BLOCK_1: opc=%d channel=%d\n", c & 7, c >> 3);
- conn->block_subopcode = (enum lws_ext_x_goole_mux__mux_opcodes)(c & 7);
- conn->block_subchannel = (c >> 3) & 0x1f;
- conn->ignore_cmd = 0;
-
- if (conn->block_subchannel != 31)
- goto interpret;
- else
- conn->state = LWS_EXT_XGM_STATE__MUX_BLOCK_2;
- break;
-
- case LWS_EXT_XGM_STATE__MUX_BLOCK_2:
- conn->block_subchannel = c << 8;
- conn->state = LWS_EXT_XGM_STATE__MUX_BLOCK_3;
- break;
-
- case LWS_EXT_XGM_STATE__MUX_BLOCK_3:
- conn->block_subchannel |= c;
-
-interpret:
-// lwsl_ext("LWS_EXT_XGM_STATE__MUX_BLOCK_3: subchannel=%d\n", conn->block_subchannel);
- ongoing_subchannel = conn->block_subchannel;
-
- /*
- * convert the subchannel index to a child wsi
- */
-
- /* act on the muxing opcode */
-
- switch (conn->block_subopcode) {
- case LWS_EXT_XGM_OPC__DATA:
- conn->state = LWS_EXT_XGM_STATE__DATA;
- break;
- case LWS_EXT_XGM_OPC__ADDCHANNEL:
- conn->state = LWS_EXT_XGM_STATE__ADDCHANNEL_LEN;
- switch (wsi->mode) {
-
- /* client: parse accepted headers returned by server */
-
- case LWS_CONNMODE_WS_CLIENT_WAITING_PROXY_REPLY:
- case LWS_CONNMODE_WS_CLIENT_ISSUE_HANDSHAKE:
- case LWS_CONNMODE_WS_CLIENT_WAITING_SERVER_REPLY:
- case LWS_CONNMODE_WS_CLIENT:
- wsi_child = conn->wsi_children[conn->block_subchannel - MUX_REAL_CHILD_INDEX_OFFSET];
- wsi_child->state = WSI_STATE_HTTP_HEADERS;
- wsi_child->parser_state = WSI_TOKEN_NAME_PART;
- break;
- default:
- wsi_child = libwebsocket_create_new_server_wsi(context);
- conn->wsi_children[conn->block_subchannel - MUX_REAL_CHILD_INDEX_OFFSET] = wsi_child;
- wsi_child->state = WSI_STATE_HTTP_HEADERS;
- wsi_child->parser_state = WSI_TOKEN_NAME_PART;
- wsi_child->extension_handles = wsi;
- muxdebug("MUX LWS_EXT_XGM_OPC__ADDCHANNEL... "
- "created child subchannel %d\n", conn->block_subchannel);
- break;
- }
- break;
- case LWS_EXT_XGM_OPC__DROPCHANNEL:
- conn->state = LWS_EXT_XGM_STATE__MUX_BLOCK_1;
- break;
- case LWS_EXT_XGM_OPC__FLOWCONTROL:
- conn->state = LWS_EXT_XGM_STATE__FLOWCONTROL_1;
- break;
- default:
- lwsl_ext("xgm: unknown subopcode\n");
- return -1;
- }
- break;
-
- case LWS_EXT_XGM_STATE__ADDCHANNEL_LEN:
- switch (c) {
- case 254:
- conn->state = LWS_EXT_XGM_STATE__ADDCHANNEL_LEN16_1;
- break;
- case 255:
- conn->state = LWS_EXT_XGM_STATE__ADDCHANNEL_LEN32_1;
- break;
- default:
- conn->length = c;
- conn->state = LWS_EXT_XGM_STATE__ADDCHANNEL_HEADERS;
- break;
- }
- break;
-
- case LWS_EXT_XGM_STATE__ADDCHANNEL_LEN16_1:
- conn->length = c << 8;
- conn->state = LWS_EXT_XGM_STATE__ADDCHANNEL_LEN16_2;
- break;
-
- case LWS_EXT_XGM_STATE__ADDCHANNEL_LEN16_2:
- conn->length |= c;
- conn->state = LWS_EXT_XGM_STATE__ADDCHANNEL_HEADERS;
- muxdebug("conn->length in mux block is %d\n", conn->length);
- break;
-
- case LWS_EXT_XGM_STATE__ADDCHANNEL_LEN32_1:
- conn->length = c << 24;
- conn->state = LWS_EXT_XGM_STATE__ADDCHANNEL_LEN32_2;
- break;
-
- case LWS_EXT_XGM_STATE__ADDCHANNEL_LEN32_2:
- conn->length |= c << 16;
- conn->state = LWS_EXT_XGM_STATE__ADDCHANNEL_LEN32_3;
- break;
-
- case LWS_EXT_XGM_STATE__ADDCHANNEL_LEN32_3:
- conn->length |= c << 8;
- conn->state = LWS_EXT_XGM_STATE__ADDCHANNEL_LEN32_4;
- break;
-
- case LWS_EXT_XGM_STATE__ADDCHANNEL_LEN32_4:
- conn->length |= c;
- conn->state = LWS_EXT_XGM_STATE__ADDCHANNEL_HEADERS;
- break;
-
- case LWS_EXT_XGM_STATE__ADDCHANNEL_HEADERS:
-
- if (conn->block_subchannel == 0 || conn->block_subchannel >=
- (sizeof(conn->wsi_children) /
- sizeof(conn->wsi_children[0]))) {
- lwsl_ext("Illegal subchannel %d in "
- "LWS_EXT_XGM_STATE__ADDCHANNEL_HEADERS, ignoring",
- conn->block_subchannel);
- conn->ignore_cmd = 1;
- }
-
- if (conn->block_subchannel == 1 && !conn->original_ch1_closed) {
- lwsl_ext("illegal request to add ch1 when it's still live, ignoring\n");
- conn->ignore_cmd = 1;
- }
-
- if (conn->ignore_cmd) {
- if (--conn->length)
- return 0;
- conn->state = LWS_EXT_XGM_STATE__MUX_BLOCK_1;
- return 0;
- }
-
- switch (wsi->mode) {
-
- /* client: parse accepted headers returned by server */
-
- case LWS_CONNMODE_WS_CLIENT_WAITING_PROXY_REPLY:
- case LWS_CONNMODE_WS_CLIENT_ISSUE_HANDSHAKE:
- case LWS_CONNMODE_WS_CLIENT_WAITING_SERVER_REPLY:
- case LWS_CONNMODE_WS_CLIENT_WAITING_EXTENSION_CONNECT:
- case LWS_CONNMODE_WS_CLIENT:
-
- muxdebug("Client LWS_EXT_XGM_STATE__ADDCHANNEL_HEADERS in %c\n", c);
- if (conn->block_subchannel == 1) {
- muxdebug("adding ch1\n");
- wsi_child = wsi;
- child_conn = conn;
- } else
- wsi_child = conn->wsi_children[
- conn->block_subchannel -
- MUX_REAL_CHILD_INDEX_OFFSET];
-
- libwebsocket_parse(wsi_child, c);
-
- if (--conn->length)
- return 0;
-
- /* it's here we create the actual ext conn via callback */
- tag_with_parent = wsi;
- lws_client_interpret_server_handshake(context, wsi_child);
- tag_with_parent = NULL;
-
- //if (wsi->parser_state != WSI_PARSING_COMPLETE)
-// break;
-
- /* client: we received all server's ADD ack */
-
- if (conn->block_subchannel != 1) {
- child_conn = (struct lws_ext_x_google_mux_conn *) lws_get_extension_user_matching_ext(
- wsi_child, this_ext);
- muxdebug("Received server's ADD Channel ACK for "
- "subchannel %d child_conn=%p!\n",
- conn->block_subchannel, (void *)child_conn);
-
- wsi_child->xor_mask = xor_no_mask;
- wsi_child->ietf_spec_revision = wsi->ietf_spec_revision;
-
- wsi_child->mode = LWS_CONNMODE_WS_CLIENT;
- wsi_child->state = WSI_STATE_ESTABLISHED;
- child_conn->state = LWS_EXT_XGM_STATE__MUX_BLOCK_1;
- }
-
- conn->state = LWS_EXT_XGM_STATE__MUX_BLOCK_1;
- child_conn->subchannel = conn->block_subchannel;
-
- /* allocate the per-connection user memory (if any) */
-
- if (wsi_child->protocol->per_session_data_size) {
- wsi_child->user_space = malloc(
- wsi_child->protocol->per_session_data_size);
- if (wsi_child->user_space == NULL) {
- lwsl_ext("Out of memory for "
- "conn user space\n");
- goto bail2;
- }
- } else
- wsi_child->user_space = NULL;
-
- /* clear his proxy connection timeout */
-
- libwebsocket_set_timeout(wsi, NO_PENDING_TIMEOUT, 0);
-
- /* mark him as being alive */
-
- wsi_child->state = WSI_STATE_ESTABLISHED;
- wsi_child->mode = LWS_CONNMODE_WS_CLIENT;
-
- if (wsi_child->protocol)
- muxdebug("mux handshake OK for protocol %s\n",
- wsi_child->protocol->name);
- else
- muxdebug("mux child handshake ends up with no protocol!\n");
-
- /*
- * inform all extensions, not just active ones since they
- * already know
- */
-
- ext = context->extensions;
-
- while (ext && ext->callback) {
- v = NULL;
- for (n = 0; n < wsi_child->count_active_extensions; n++)
- if (wsi_child->active_extensions[n] == ext) {
- v = wsi_child->active_extensions_user[n];
- }
-
- ext->callback(context, ext, wsi_child,
- LWS_EXT_CALLBACK_ANY_WSI_ESTABLISHED, v, NULL, 0);
- ext++;
- }
-
- /* call him back to inform him he is up */
-
- wsi->protocol->callback(context, wsi_child,
- LWS_CALLBACK_CLIENT_ESTABLISHED,
- wsi_child->user_space,
- NULL, 0);
-
- return 0;
-
-bail2:
- exit(1);
-
- /* server: parse proposed changed headers from client */
-
- default:
- break;
- }
-
- /*
- * SERVER
- */
-
- wsi_child = conn->wsi_children[conn->block_subchannel - MUX_REAL_CHILD_INDEX_OFFSET];
-
- muxdebug("Server LWS_EXT_XGM_STATE__ADDCHANNEL_HEADERS in %d\n", conn->length);
-
- libwebsocket_read(context, wsi_child, &c, 1);
-
- if (--conn->length > 0)
- break;
-
- muxdebug("Server LWS_EXT_XGM_STATE__ADDCHANNEL_HEADERS done\n");
-
- /*
- * server: header diffs are all seen, we must process
- * the add action
- */
-
- /* reply with ADDCHANNEL to ack it */
-
- wsi->xor_mask = xor_no_mask;
- child_conn = (struct lws_ext_x_google_mux_conn *)lws_get_extension_user_matching_ext(wsi_child,
- this_ext);
- if (!child_conn) {
- lwsl_ext("wsi_child %p has no child conn!", (void *)wsi_child);
- break;
- }
- child_conn->wsi_parent = wsi;
- conn->sticky_mux_used = 1;
- child_conn->subchannel = conn->block_subchannel;
-
-
- muxdebug("Setting child conn parent to %p\n", (void *)wsi);
-
- wsi_child->mode = LWS_CONNMODE_WS_SERVING;
- wsi_child->state = WSI_STATE_ESTABLISHED;
- wsi_child->lws_rx_parse_state = LWS_RXPS_NEW;
- wsi_child->rx_packet_length = 0;
-
- /* allocate the per-connection user memory (if any) */
-
- if (wsi_child->protocol->per_session_data_size) {
- wsi_child->user_space = malloc(
- wsi_child->protocol->per_session_data_size);
- if (wsi_child->user_space == NULL) {
- lwsl_err("Out of memory for "
- "conn user space\n");
- break;
- }
- } else
- wsi_child->user_space = NULL;
-
-
- conn->wsi_children[conn->block_subchannel -
- MUX_REAL_CHILD_INDEX_OFFSET] = wsi_child;
- if (conn->highest_child_subchannel <=
- conn->block_subchannel - MUX_REAL_CHILD_INDEX_OFFSET)
- conn->highest_child_subchannel =
- conn->block_subchannel -
- MUX_REAL_CHILD_INDEX_OFFSET + 1;
-
-
- /* notify user code that we're ready to roll */
-
- if (wsi_child->protocol->callback)
- wsi_child->protocol->callback(
- wsi_child->protocol->owning_server,
- wsi_child, LWS_CALLBACK_ESTABLISHED,
- wsi_child->user_space, NULL, 0);
-
- muxdebug("setting conn state LWS_EXT_XGM_STATE__MUX_BLOCK_1\n");
- conn->state = LWS_EXT_XGM_STATE__MUX_BLOCK_1;
- break;
-
- case LWS_EXT_XGM_STATE__FLOWCONTROL_1:
- conn->length = c << 24;
- conn->state = LWS_EXT_XGM_STATE__FLOWCONTROL_2;
- break;
-
- case LWS_EXT_XGM_STATE__FLOWCONTROL_2:
- conn->length |= c << 16;
- conn->state = LWS_EXT_XGM_STATE__FLOWCONTROL_3;
- break;
-
- case LWS_EXT_XGM_STATE__FLOWCONTROL_3:
- conn->length |= c << 8;
- conn->state = LWS_EXT_XGM_STATE__FLOWCONTROL_4;
- break;
-
- case LWS_EXT_XGM_STATE__FLOWCONTROL_4:
- conn->length |= c;
- conn->state = LWS_EXT_XGM_STATE__MUX_BLOCK_1;
- break;
-
- case LWS_EXT_XGM_STATE__DATA:
-
-// lwsl_debug("LWS_EXT_XGM_STATE__DATA in\n");
-
- /*
- * we have cooked websocket frame content following just like
- * it went on the wire without mux, including masking and any
- * other extensions (including this guy can himself be another
- * level of channel mux, there's no restriction).
- *
- * We deal with it by just feeding it to the child wsi's rx
- * state machine. The only issue is, we need that state machine
- * to tell us when it ate a full frame, so we watch its state
- * afterwards
- */
- if (conn->block_subchannel - MUX_REAL_CHILD_INDEX_OFFSET >= conn->highest_child_subchannel) {
- lwsl_ext("Illegal subchannel %d\n", conn->block_subchannel);
- return -1;
- }
-
- // lwsl_debug("LWS_EXT_XGM_STATE__DATA: ch %d\n", conn->block_subchannel);
-
- if (conn->block_subchannel == 1) {
- if (conn->original_ch1_closed) {
- lwsl_debug("data sent to closed ch1\n");
- return -1;
- }
- wsi_child = wsi;
- } else
- wsi_child = conn->wsi_children[conn->block_subchannel - MUX_REAL_CHILD_INDEX_OFFSET];
-
- if (!wsi_child) {
- lwsl_ext("Bad subchannel %d\n", conn->block_subchannel);
- return -1;
- }
-
- switch (wsi_child->mode) {
-
- /* client receives something */
-
- case LWS_CONNMODE_WS_CLIENT_WAITING_PROXY_REPLY:
- case LWS_CONNMODE_WS_CLIENT_ISSUE_HANDSHAKE:
- case LWS_CONNMODE_WS_CLIENT_WAITING_SERVER_REPLY:
- case LWS_CONNMODE_WS_CLIENT:
-// lwsl_ext(" client\n");
- if (libwebsocket_client_rx_sm(wsi_child, c) < 0) {
- libwebsocket_close_and_free_session(
- context,
- wsi_child,
- LWS_CLOSE_STATUS_GOINGAWAY);
- }
-
- return 0;
-
- /* server is receiving from client */
-
- default:
-// lwsl_ext(" server\n");
- if (libwebsocket_rx_sm(wsi_child, c) < 0) {
- muxdebug("probs\n");
- libwebsocket_close_and_free_session(
- context,
- wsi_child,
- LWS_CLOSE_STATUS_GOINGAWAY);
- }
- break;
- }
- break;
- }
-
- return 0;
-}
-
-
-
-int lws_extension_callback_x_google_mux(
- struct libwebsocket_context *context,
- struct libwebsocket_extension *ext,
- struct libwebsocket *wsi,
- enum libwebsocket_extension_callback_reasons reason,
- void *user, void *in, size_t len)
-{
- unsigned char send_buf[LWS_SEND_BUFFER_PRE_PADDING + 4096 +
- LWS_SEND_BUFFER_POST_PADDING];
- struct lws_ext_x_google_mux_conn *conn =
- (struct lws_ext_x_google_mux_conn *)user;
- struct lws_ext_x_google_mux_conn *parent_conn;
- struct lws_ext_x_google_mux_conn *child_conn;
- int n;
- struct lws_tokens *eff_buf = (struct lws_tokens *)in;
- unsigned char *p = NULL;
- struct lws_ext_x_google_mux_context *mux_ctx = (struct lws_ext_x_google_mux_context *)
- ext->per_context_private_data;
- struct libwebsocket *wsi_parent;
- struct libwebsocket *wsi_child;
- struct libwebsocket *wsi_temp;
- unsigned char *pin = (unsigned char *)in;
- unsigned char *basepin;
- int m;
- int done = 0;
- unsigned char *pb = &send_buf[LWS_SEND_BUFFER_PRE_PADDING];
- int subcommand_length;
-
- if (eff_buf)
- p = (unsigned char *)eff_buf->token;
-
- switch (reason) {
-
- /* these guys are once per context */
-
- case LWS_EXT_CALLBACK_SERVER_CONTEXT_CONSTRUCT:
- case LWS_EXT_CALLBACK_CLIENT_CONTEXT_CONSTRUCT:
-
- ext->per_context_private_data = malloc(
- sizeof (struct lws_ext_x_google_mux_context));
- if (ext->per_context_private_data == NULL) {
- lwsl_err("Out of memory\n");
- return -1;
- }
- mux_ctx = (struct lws_ext_x_google_mux_context *)
- ext->per_context_private_data;
- mux_ctx->active_conns = 0;
- break;
-
- case LWS_EXT_CALLBACK_SERVER_CONTEXT_DESTRUCT:
- case LWS_EXT_CALLBACK_CLIENT_CONTEXT_DESTRUCT:
-
- if (!mux_ctx)
- break;
- for (n = 0; n < mux_ctx->active_conns; n++)
- if (mux_ctx->wsi_muxconns[n]) {
- libwebsocket_close_and_free_session(
- context, mux_ctx->wsi_muxconns[n],
- LWS_CLOSE_STATUS_GOINGAWAY);
- mux_ctx->wsi_muxconns[n] = NULL;
- }
-
- free(mux_ctx);
- break;
-
- /*
- * channel management
- */
-
- case LWS_EXT_CALLBACK_CAN_PROXY_CLIENT_CONNECTION:
-
- muxdebug("LWS_EXT_CALLBACK_CAN_PROXY_CLIENT_CONNECTION %s:%u\n",
- (char *)in, (unsigned int)len);
-
- /*
- * Does a physcial connection to the same server:port already
- * exist so we can piggyback on it?
- */
-
- for (n = 0; n < mux_ctx->active_conns && !done; n++) {
-
- wsi_parent = mux_ctx->wsi_muxconns[n];
- if (!wsi_parent)
- continue;
-
- muxdebug(" %s / %s\n", wsi_parent->c_address, (char *)in);
- if (strcmp((const char*)wsi_parent->c_address, (const char *)in))
- continue;
- muxdebug(" %u / %u\n", wsi_parent->c_port, (unsigned int)len);
-
- if (wsi_parent->c_port != (unsigned int)len)
- continue;
-
- /*
- * does this potential parent already have an
- * x-google-mux conn associated with him?
- */
-
- parent_conn = NULL;
- for (m = 0; m < wsi_parent->count_active_extensions; m++)
- if (ext == wsi_parent->active_extensions[m])
- parent_conn = (struct lws_ext_x_google_mux_conn *)
- wsi_parent->active_extensions_user[m];
-
- if (parent_conn == NULL) {
-
- /*
- * he doesn't -- see if that's just because it
- * is early in his connection sequence or if we
- * should give up on him
- */
-
- switch (wsi_parent->mode) {
- case LWS_CONNMODE_WS_SERVING:
- case LWS_CONNMODE_WS_CLIENT:
- continue;
- default:
- break;
- }
-
- /*
- * our putative parent is still connecting
- * himself, we have to become a candidate child
- * and find out our final fate when the parent
- * completes connection
- */
-
- wsi->candidate_children_list = wsi_parent->candidate_children_list;
- wsi_parent->candidate_children_list = wsi;
- wsi->mode = LWS_CONNMODE_WS_CLIENT_PENDING_CANDIDATE_CHILD;
-
- done = 1;
- continue;
- }
-
- if (parent_conn->highest_child_subchannel >=
- (sizeof(parent_conn->wsi_children) /
- sizeof(parent_conn->wsi_children[0]))) {
- lwsl_ext("Can't add any more children\n");
- continue;
- }
- /*
- * this established connection will do, bind them
- * from now on child will only operate through parent
- * connection
- */
-
- wsi->candidate_children_list = wsi_parent->candidate_children_list;
- wsi_parent->candidate_children_list = wsi;
- wsi->mode = LWS_CONNMODE_WS_CLIENT_PENDING_CANDIDATE_CHILD;
-
- muxdebug("attaching to existing mux\n");
-
- conn = parent_conn;
- wsi = wsi_parent;
-
- goto handle_additions;
-
- }
-
- /*
- * either way, note the existence of this connection in case
- * he will become a possible mux parent later
- */
-
- mux_ctx->wsi_muxconns[mux_ctx->active_conns++] = wsi;
- if (done)
- return 1;
-
- muxdebug("x-google-mux: unable to mux connection\n");
-
- break;
-
- /* these guys are once per connection */
-
- case LWS_EXT_CALLBACK_CLIENT_CONSTRUCT:
- muxdebug("LWS_EXT_CALLBACK_CLIENT_CONSTRUCT: setting parent = %p\n", (void *)tag_with_parent);
- conn->state = LWS_EXT_XGM_STATE__MUX_BLOCK_1;
- if (conn->block_subchannel != 1)
- conn->wsi_parent = tag_with_parent;
- break;
-
- case LWS_EXT_CALLBACK_CONSTRUCT:
- muxdebug("LWS_EXT_CALLBACK_CONSTRUCT\n");
- conn->state = LWS_EXT_XGM_STATE__MUX_BLOCK_1;
- break;
-
- case LWS_EXT_CALLBACK_CHECK_OK_TO_REALLY_CLOSE:
- muxdebug("LWS_EXT_CALLBACK_CHECK_OK_TO_REALLY_CLOSE\n");
-
- if (conn->subchannel == 1) {
-
- /*
- * special case of original mux parent channel closing
- */
-
- conn->original_ch1_closed = 1;
-
- lwsl_ext("original mux parent channel closing\n");
-
- parent_conn = conn;
- } else {
-
- parent_conn = (struct lws_ext_x_google_mux_conn *)lws_get_extension_user_matching_ext(conn->wsi_parent, ext);
- if (parent_conn == 0) {
- muxdebug("failed to get parent conn\n");
- break;
- }
- }
-
- /* see if that was the end of the mux entirely */
-
- if (!parent_conn->original_ch1_closed)
- break;
-
- done = 0;
- for (n = 0; n < !done && parent_conn->highest_child_subchannel; n++)
- if (parent_conn->wsi_children[n])
- done = 1;
-
- /* if he has children, don't let him close for real! */
-
- if (done) {
- lwsl_ext("VETO closure\n");
- return 1;
- }
-
- /* no children, ch1 is closed, let him destroy himself */
-
- if (conn->subchannel == 1)
- lwsl_ext("ALLOW closure of mux parent\n");
-
- break;
-
- case LWS_EXT_CALLBACK_DESTROY:
- muxdebug("LWS_EXT_CALLBACK_DESTROY\n");
-
- /*
- * remove us from parent if noted in parent
- */
-
- if (conn->subchannel == 1) {
-
- /*
- * special case of original mux parent channel closing
- */
-
- conn->original_ch1_closed = 1;
-
- lwsl_ext("original mux parent channel closing\n");
-
- parent_conn = conn;
- wsi_parent = wsi;
- } else {
-
- wsi_parent = conn->wsi_parent;
- parent_conn = (struct lws_ext_x_google_mux_conn *)lws_get_extension_user_matching_ext(conn->wsi_parent, ext);
- if (parent_conn == 0) {
- muxdebug("failed to get parent conn\n");
- break;
- }
- for (n = 0; n < parent_conn->highest_child_subchannel; n++)
- if (parent_conn->wsi_children[n] == wsi)
- parent_conn->wsi_children[n] = NULL;
- }
-
- /* see if that was the end of the mux entirely */
-
- if (!parent_conn->original_ch1_closed)
- break;
-
- done = 0;
- for (n = 0; n < !done && parent_conn->highest_child_subchannel; n++)
- if (parent_conn->wsi_children[n])
- done = 1;
-
- if (done == 0)
- if (parent_conn != conn)
-
- /*
- * parent closed last and no children left
- * ... and we are not parent already ourselves
- */
-
- libwebsocket_close_and_free_session(context, wsi_parent, LWS_CLOSE_STATUS_NORMAL);
-
- break;
-
- case LWS_EXT_CALLBACK_DESTROY_ANY_WSI_CLOSING:
- muxdebug("LWS_EXT_CALLBACK_DESTROY_ANY_WSI_CLOSING\n");
-
- for (n = 0; n < mux_ctx->active_conns; n++)
- if (mux_ctx->wsi_muxconns[n] == wsi) {
- while (n++ < mux_ctx->active_conns)
- mux_ctx->wsi_muxconns[n - 1] =
- mux_ctx->wsi_muxconns[n];
- mux_ctx->active_conns--;
- return 0;
- }
-
- /*
- * liberate any candidate children otherwise imprisoned
- */
-
- wsi_parent = wsi->candidate_children_list;
- while (wsi_parent) {
- wsi_temp = wsi_parent->candidate_children_list;
- /* let them each connect privately then */
- __libwebsocket_client_connect_2(context, wsi_parent);
- wsi_parent = wsi_temp;
- }
-
- break;
-
- case LWS_EXT_CALLBACK_ANY_WSI_ESTABLISHED:
- muxdebug("LWS_EXT_CALLBACK_ANY_WSI_ESTABLISHED\n");
-
-handle_additions:
- /*
- * did this putative parent get x-google-mux authorized in the
- * end?
- */
-
- if (!conn) {
-
- muxdebug(" Putative parent didn't get mux extension, let them go it alone\n");
-
- /*
- * no, we can't be a parent for mux children. Let
- * them all go it alone
- */
-
- wsi_child = wsi->candidate_children_list;
- while (wsi_child) {
- wsi_temp = wsi_child->candidate_children_list;
- /* let them each connect privately then */
- __libwebsocket_client_connect_2(context, wsi_child);
- wsi_child = wsi_temp;
- }
-
- return 1;
- }
-
- /*
- * we did get mux extension authorized by server, in that case
- * if we have any candidate children let's try to attach them
- * as mux subchannel real children
- */
-
- wsi_child = wsi->candidate_children_list;
- n = 0;
- while (wsi_child) {
-
- wsi_temp = wsi_child->candidate_children_list;
-
- /* find an empty subchannel */
-
- while ((n < (sizeof(conn->wsi_children) / sizeof(conn->wsi_children[0]))) &&
- conn->wsi_children[n] != NULL)
- n++;
-
- if (n >= (sizeof(conn->wsi_children) / sizeof(conn->wsi_children[0]))) {
- /* no room at the inn */
-
- /* let them each connect privately then */
- __libwebsocket_client_connect_2(context, wsi_child);
- wsi_child = wsi_temp;
- continue;
- }
-
- muxdebug(" using mux addchannel action for candidate child\n");
-
- /* pile the children on the parent */
- lws_ext_x_google_mux__send_addchannel(context, wsi,
- conn, wsi_child,
- n + MUX_REAL_CHILD_INDEX_OFFSET, wsi->c_path);
-
- conn->sticky_mux_used = 1;
-
- conn->wsi_children[n] = wsi_child;
- if ((n + 1) > conn->highest_child_subchannel)
- conn->highest_child_subchannel = n + 1;
-
- muxdebug("Setting CHILD LIST entry %d to %p\n",
- n + MUX_REAL_CHILD_INDEX_OFFSET, (void *)wsi_parent);
- wsi_child = wsi_temp;
- }
- wsi->candidate_children_list = NULL;
- return 1;
-
- /*
- * whenever we receive something on a muxed link
- */
-
- case LWS_EXT_CALLBACK_EXTENDED_PAYLOAD_RX:
-
- muxdebug("LWS_EXT_CALLBACK_EXTENDED_PAYLOAD_RX\n");
-
- if (wsi->opcode != LWS_WS_OPCODE_07__NOSPEC__MUX)
- return 0; /* unhandled */
-
- conn->state = LWS_EXT_XGM_STATE__MUX_BLOCK_1;
-
- n = eff_buf->token_len;
- while (n--)
- if (lws_extension_x_google_mux_parser(context, wsi, ext,
- conn, *p++) < 0) {
- return -1;
- }
- return 1; /* handled */
-
- /*
- * when something might need sending on our transport
- */
-
- case LWS_EXT_CALLBACK_PACKET_TX_DO_SEND:
-
- muxdebug("LWS_EXT_CALLBACK_PACKET_TX_DO_SEND: %p, "
- "my subchannel=%d\n",
- (void *)conn->wsi_parent, conn->subchannel);
-
- pin = *((unsigned char **)in);
- basepin = pin;
-
- wsi_parent = conn->wsi_parent;
-
- if (conn->subchannel == 1) {
-
- /*
- * if we weren't 'closed', then we were the original
- * connection that established this link, ie, it's
- * the parent wsi
- */
-
- if (conn->original_ch1_closed) {
- lwsl_ext("Trying to send on dead original ch1\n");
- return 0;
- }
-
- /* send on ourselves */
-
- wsi_parent = wsi;
-
- } else {
-
- /*
- * he's not a child connection of a mux
- */
-
- if (!conn->wsi_parent) {
- // lwsl_ext("conn %p has no parent\n", (void *)conn);
- return 0;
- }
-
- /*
- * get parent / transport mux context
- */
-
- parent_conn = (struct lws_ext_x_google_mux_conn *)lws_get_extension_user_matching_ext(conn->wsi_parent, ext);
- if (parent_conn == 0) {
- muxdebug("failed to get parent conn\n");
- return 0;
- }
-
- /*
- * mux transport is in singular mode, let the caller send it
- * no more muxified than it already is
- */
-
- if (!parent_conn->sticky_mux_used) {
- // lwsl_ext("parent in singular mode\n");
- return 0;
- }
- }
-
- if (!conn->defeat_mux_opcode_wrapping) {
-
- n = 1;
- if (conn->subchannel >= 31)
- n = 3;
-
- /*
- * otherwise we need to take care of the sending action using
- * mux protocol. Prepend the channel + opcode
- */
-
- pin -= lws_addheader_mux_opcode(send_buf, len + n) + n;
- basepin = pin;
- pin += lws_addheader_mux_opcode(pin, len + n);
-
- if (conn->subchannel >= 31) {
- *pin++ = (31 << 3) | LWS_EXT_XGM_OPC__DATA;
- *pin++ = conn->subchannel >> 8;
- *pin++ = conn->subchannel;
- } else
- *pin++ = (conn->subchannel << 3) | LWS_EXT_XGM_OPC__DATA;
- }
-
- /*
- * recurse to allow nesting
- */
-
- lws_issue_raw_ext_access(wsi_parent, basepin, (pin - basepin) + len);
-
- return 1; /* handled */
-
- case LWS_EXT_CALLBACK_1HZ:
- /*
- * if we have children, service their timeouts using the same
- * handler as toplevel guys to allow recursion
- */
- for (n = 0; n < conn->highest_child_subchannel; n++)
- if (conn->wsi_children[n])
- libwebsocket_service_timeout_check(context,
- conn->wsi_children[n], len);
- break;
-
- case LWS_EXT_CALLBACK_REQUEST_ON_WRITEABLE:
- /*
- * if a mux child is asking for callback on writable, we have
- * to pass it up to his parent
- */
-
- muxdebug("LWS_EXT_CALLBACK_REQUEST_ON_WRITEABLE %s\n", wsi->protocol->name);
-
- if (conn->wsi_parent == NULL) {
- muxdebug(" no parent\n");
- break;
- }
-
- if (!conn->awaiting_POLLOUT) {
-
- muxdebug(" !conn->awaiting_POLLOUT\n");
-
- conn->awaiting_POLLOUT = 1;
- parent_conn = NULL;
- for (m = 0; m < conn->wsi_parent->count_active_extensions; m++)
- if (ext == conn->wsi_parent->active_extensions[m])
- parent_conn = (struct lws_ext_x_google_mux_conn *)
- conn->wsi_parent->active_extensions_user[m];
-
- if (parent_conn != NULL) {
- parent_conn->count_children_needing_POLLOUT++;
- muxdebug(" count_children_needing_POLLOUT bumped\n");
- } else
- muxdebug("unable to identify parent conn\n");
- }
- muxdebug(" requesting on parent %p\n", (void *)conn->wsi_parent);
- libwebsocket_callback_on_writable(context, conn->wsi_parent);
-
- return 1;
-
- case LWS_EXT_CALLBACK_HANDSHAKE_REPLY_TX:
-
- muxdebug("LWS_EXT_CALLBACK_HANDSHAKE_REPLY_TX %p\n",
- (void *)wsi->extension_handles);
-
- /* send raw if we're not a child */
-
- if (!wsi->extension_handles)
- return 0;
-
- subcommand_length = lws_mux_subcommand_header(LWS_EXT_XGM_OPC__ADDCHANNEL, ongoing_subchannel, pb, len);
-
- pb += lws_addheader_mux_opcode(pb, subcommand_length + len);
- pb += lws_mux_subcommand_header(LWS_EXT_XGM_OPC__ADDCHANNEL, ongoing_subchannel, pb, len);
- memcpy(pb, in, len);
- pb += len;
-
- lws_issue_raw_ext_access(wsi->extension_handles, &send_buf[LWS_SEND_BUFFER_PRE_PADDING],
- pb - &send_buf[LWS_SEND_BUFFER_PRE_PADDING]);
-
-
- return 1; /* handled */
-
- case LWS_EXT_CALLBACK_IS_WRITEABLE:
- /*
- * we are writable, inform children if any care
- */
- muxdebug("LWS_EXT_CALLBACK_IS_WRITEABLE: %s\n", wsi->protocol->name);
-
- if (!conn->count_children_needing_POLLOUT) {
- muxdebug(" no children need POLLOUT\n");
- return 0;
- }
-
- for (n = 0; n < conn->highest_child_subchannel; n++) {
-
- if (!conn->wsi_children[n])
- continue;
-
- child_conn = NULL;
- for (m = 0; m < conn->wsi_children[n]->count_active_extensions; m++)
- if (ext == conn->wsi_children[n]->active_extensions[m])
- child_conn = (struct lws_ext_x_google_mux_conn *)
- conn->wsi_children[n]->active_extensions_user[m];
-
- if (!child_conn) {
- muxdebug("unable to identify child conn\n");
- continue;
- }
-
- if (!child_conn->awaiting_POLLOUT)
- continue;
-
- child_conn->awaiting_POLLOUT = 0;
- conn->count_children_needing_POLLOUT--;
- lws_handle_POLLOUT_event(context, conn->wsi_children[n], NULL);
- if (!conn->count_children_needing_POLLOUT)
- return 2; /* all handled */
- else
- return 1; /* handled but need more */
- }
- break;
-
- case LWS_EXT_CALLBACK_CHECK_OK_TO_PROPOSE_EXTENSION:
-
- /* disallow deflate-stream if we are a mux child connection */
-
- if (strcmp((const char*)in, "deflate-stream") == 0 &&
- client_handshake_generation_is_for_mux_child) {
-
- muxdebug("mux banned deflate-stream on child connection\n");
- return 1; /* disallow */
- }
- break;
-
- default:
- break;
- }
-
- return 0;
-}