This patch gets deflate-stream working with x-google-mux.
It adds a clean veto system where are extension can veto the proposal
of any extension when opening a new connection. x-google-mux uses that
in its callback to defeat any use of deflate-stream on mux children.
However deflate stream is allowed on the parent connection and works
transparently now alongside x-google-mux.
Signed-off-by: Andy Green <andy@warmcat.com>
1) To enable it, reconfigure with --enable-x-google-mux
- 2) It conflicts with deflate-stream, use the -u switch on
- the test client to disable deflate-stream
-
- 3) It deviates from the google standard by sending full
+ 2) It deviates from the google standard by sending full
headers in the addchannel subcommand rather than just
changed ones from original connect
- 4) Quota is not implemented yet
-
- 5) Close of subchannel is not really implemented yet
-
- 6) Google opcode 0xf is changed to 0x7 to account for
- v7 protocol changes to opcode layout
+ 3) Quota is not implemented yet
- However despite those caveats, in fact it can run the
- test client reliably over one socket (both dumb-increment
- and lws-mirror-protocol), you can open a browser on the
- same test server too and see the circles, etc.
+However despite those caveats, in fact it can run the
+test client reliably over one socket (both dumb-increment
+and lws-mirror-protocol), you can open a browser on the
+same test server too and see the circles, etc.
+It also works compatibly with deflate-stream automatically.
-2011-05-23 Andy Green <andy@warmcat.com>
+2011-05-28 Andy Green <andy@warmcat.com>
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)
{
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(
/* send the request to the server */
- n = lws_issue_raw(wsi, &send_buf[LWS_SEND_BUFFER_PRE_PADDING],
+ 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;
wsi->xor_mask = xor_no_mask;
child_conn = lws_get_extension_user_matching_ext(wsi_child,
this_ext);
+ if (!child_conn) {
+ fprintf(stderr, "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;
* afterwards
*/
if (conn->block_subchannel - MUX_REAL_CHILD_INDEX_OFFSET >= conn->highest_child_subchannel) {
- fprintf(stderr, "Illegal subchannel\n");
+ fprintf(stderr, "Illegal subchannel %d\n", conn->block_subchannel);
return -1;
}
} else
wsi_child = conn->wsi_children[conn->block_subchannel - MUX_REAL_CHILD_INDEX_OFFSET];
+ if (!wsi_child) {
+ fprintf(stderr, "Bad subchannel %d\n", conn->block_subchannel);
+ return -1;
+ }
+
switch (wsi_child->mode) {
/* client receives something */
* recurse to allow nesting
*/
- lws_issue_raw(wsi_parent, basepin, (pin - basepin) + len);
+ lws_issue_raw_ext_access(wsi_parent, basepin, (pin - basepin) + len);
return 1; /* handled */
memcpy(pb, in, len);
pb += len;
- lws_issue_raw(wsi->extension_handles, &send_buf[LWS_SEND_BUFFER_PRE_PADDING],
+ lws_issue_raw_ext_access(wsi->extension_handles, &send_buf[LWS_SEND_BUFFER_PRE_PADDING],
pb - &send_buf[LWS_SEND_BUFFER_PRE_PADDING]);
}
break;
+ case LWS_EXT_CALLBACK_CHECK_OK_TO_PROPOSE_EXTENSION:
+
+ /* disallow deflate-stream if we are a mux child connection */
+
+ if (strcmp(in, "deflate-stream") == 0 &&
+ client_handshake_generation_is_for_mux_child) {
+
+ fprintf(stderr, "mux banned deflate-stream on child connection\n");
+ return 1; /* disallow */
+ }
+ break;
+
default:
break;
}
char *p = pkt;
int n;
struct libwebsocket_extension *ext;
+ struct libwebsocket_extension *ext1;
int ext_count = 0;
unsigned char buf[LWS_SEND_BUFFER_PRE_PADDING + 1 + MAX_BROADCAST_PAYLOAD +
LWS_SEND_BUFFER_POST_PADDING];
while (ext && ext->callback) {
n = 0;
+ ext1 = context->extensions;
+ while (ext1 && ext1->callback) {
+
+ n |= ext1->callback(context, ext1, wsi,
+ LWS_EXT_CALLBACK_CHECK_OK_TO_PROPOSE_EXTENSION,
+ NULL, (char *)ext->name, 0);
+
+ ext1++;
+ }
+
+ if (n) {
+
+ /* an extension vetos us */
+ fprintf(stderr, "ext %s vetoed\n", (char *)ext->name);
+ ext++;
+ continue;
+ }
+
n = context->protocols[0].callback(context, wsi,
LWS_CALLBACK_CLIENT_CONFIRM_EXTENSION_SUPPORTED,
wsi->user_space, (char *)ext->name, 0);
issue_hdr:
+ puts(pkt);
+
/* done with these now */
free(wsi->c_path);
LWS_EXT_CALLBACK_CONSTRUCT,
LWS_EXT_CALLBACK_CLIENT_CONSTRUCT,
LWS_EXT_CALLBACK_CHECK_OK_TO_REALLY_CLOSE,
+ LWS_EXT_CALLBACK_CHECK_OK_TO_PROPOSE_EXTENSION,
LWS_EXT_CALLBACK_DESTROY,
LWS_EXT_CALLBACK_DESTROY_ANY_WSI_CLOSING,
LWS_EXT_CALLBACK_ANY_WSI_ESTABLISHED,
return 0;
}
+int
+lws_issue_raw_ext_access(struct libwebsocket *wsi,
+ unsigned char *buf, size_t len)
+{
+ int ret;
+ struct lws_tokens eff_buf;
+ int m;
+ int n;
+
+ eff_buf.token = (char *)buf;
+ eff_buf.token_len = len;
+
+ /*
+ * while we have original buf to spill ourselves, or extensions report
+ * more in their pipeline
+ */
+
+ ret = 1;
+ while (ret == 1) {
+
+ /* default to nobody has more to spill */
+
+ ret = 0;
+
+ /* show every extension the new incoming data */
+
+ for (n = 0; n < wsi->count_active_extensions; n++) {
+ m = wsi->active_extensions[n]->callback(
+ wsi->protocol->owning_server,
+ wsi->active_extensions[n], wsi,
+ LWS_EXT_CALLBACK_PACKET_TX_PRESEND,
+ wsi->active_extensions_user[n], &eff_buf, 0);
+ if (m < 0) {
+ fprintf(stderr, "Extension reports fatal error\n");
+ return -1;
+ }
+ if (m)
+ /*
+ * at least one extension told us he has more
+ * to spill, so we will go around again after
+ */
+ ret = 1;
+ }
+
+ /* assuming they left us something to send, send it */
+
+ if (eff_buf.token_len)
+ if (lws_issue_raw(wsi, (unsigned char *)eff_buf.token,
+ eff_buf.token_len))
+ return -1;
+
+ /* we used up what we had */
+
+ eff_buf.token = NULL;
+ eff_buf.token_len = 0;
+
+ /*
+ * Did that leave the pipe choked?
+ */
+
+ if (!lws_send_pipe_choked(wsi))
+ /* no we could add more */
+ continue;
+
+ fprintf(stderr, "choked\n");
+
+ /*
+ * Yes, he's choked. Don't spill the rest now get a callback
+ * when he is ready to send and take care of it there
+ */
+ libwebsocket_callback_on_writable(
+ wsi->protocol->owning_server, wsi);
+ wsi->extension_data_pending = 1;
+ ret = 0;
+ }
+
+ debug("written %d bytes to client\n", eff_buf.token_len);
+
+ return 0;
+}
+
/**
* libwebsocket_write() - Apply protocol then write data to client
* @wsi: Websocket instance (available from user callback)
size_t len, enum libwebsocket_write_protocol protocol)
{
int n;
- int m;
int pre = 0;
int post = 0;
int shift = 7;
- struct lws_tokens eff_buf;
- int ret;
int masked7 = wsi->mode == LWS_CONNMODE_WS_CLIENT && wsi->xor_mask != xor_no_mask;
unsigned char *dropmask = NULL;
unsigned char is_masked_bit = 0;
* callback returns 1 in case it wants to spill more buffers
*/
- eff_buf.token = (char *)buf - pre;
- eff_buf.token_len = len + pre + post;
-
- /*
- * while we have original buf to spill ourselves, or extensions report
- * more in their pipeline
- */
-
- ret = 1;
- while (ret == 1) {
-
- /* default to nobody has more to spill */
-
- ret = 0;
-
- /* show every extension the new incoming data */
-
- for (n = 0; n < wsi->count_active_extensions; n++) {
- m = wsi->active_extensions[n]->callback(
- wsi->protocol->owning_server,
- wsi->active_extensions[n], wsi,
- LWS_EXT_CALLBACK_PACKET_TX_PRESEND,
- wsi->active_extensions_user[n], &eff_buf, 0);
- if (m < 0) {
- fprintf(stderr, "Extension reports fatal error\n");
- return -1;
- }
- if (m)
- /*
- * at least one extension told us he has more
- * to spill, so we will go around again after
- */
- ret = 1;
- }
-
- /* assuming they left us something to send, send it */
-
- if (eff_buf.token_len)
- if (lws_issue_raw(wsi, (unsigned char *)eff_buf.token,
- eff_buf.token_len))
- return -1;
-
- /* we used up what we had */
-
- eff_buf.token = NULL;
- eff_buf.token_len = 0;
-
- /*
- * Did that leave the pipe choked?
- */
-
- if (!lws_send_pipe_choked(wsi))
- /* no we could add more */
- continue;
-
- fprintf(stderr, "choked\n");
-
- /*
- * Yes, he's choked. Don't spill the rest now get a callback
- * when he is ready to send and take care of it there
- */
- libwebsocket_callback_on_writable(
- wsi->protocol->owning_server, wsi);
- wsi->extension_data_pending = 1;
- ret = 0;
- }
-
- debug("written %d bytes to client\n", eff_buf.token_len);
-
- return 0;
+ return lws_issue_raw_ext_access(wsi, buf - pre, len + pre + post);
}
extern int
libwebsocket_rx_sm(struct libwebsocket *wsi, unsigned char c);
+extern int
+lws_issue_raw_ext_access(struct libwebsocket *wsi,
+ unsigned char *buf, size_t len);
+
#ifndef LWS_OPENSSL_SUPPORT
unsigned char *