extensions fix deflate stream vs mux and veto it on mux children

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>
This commit is contained in:
Andy Green 2011-05-28 10:19:19 +01:00
parent 5555c98728
commit 0922650b53
6 changed files with 144 additions and 92 deletions

View file

@ -263,25 +263,18 @@ It has the following notes:
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
3) 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
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>

View file

@ -5,6 +5,7 @@
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)
{
@ -96,8 +97,10 @@ static int lws_ext_x_google_mux__send_addchannel(
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(
@ -122,7 +125,7 @@ static int lws_ext_x_google_mux__send_addchannel(
/* 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;
@ -443,6 +446,10 @@ bail2:
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;
@ -526,7 +533,7 @@ bail2:
* 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;
}
@ -541,6 +548,11 @@ bail2:
} 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 */
@ -1078,7 +1090,7 @@ handle_additions:
* 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 */
@ -1144,7 +1156,7 @@ handle_additions:
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]);
@ -1190,6 +1202,18 @@ handle_additions:
}
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;
}

View file

@ -784,6 +784,7 @@ libwebsockets_generate_client_handshake(struct libwebsocket_context *context,
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];
@ -957,6 +958,24 @@ libwebsockets_generate_client_handshake(struct libwebsocket_context *context,
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);
@ -1010,6 +1029,8 @@ libwebsockets_generate_client_handshake(struct libwebsocket_context *context,
issue_hdr:
puts(pkt);
/* done with these now */
free(wsi->c_path);

View file

@ -83,6 +83,7 @@ enum libwebsocket_extension_callback_reasons {
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,

View file

@ -1450,6 +1450,87 @@ int lws_issue_raw(struct libwebsocket *wsi, unsigned char *buf, size_t len)
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)
@ -1479,12 +1560,9 @@ int libwebsocket_write(struct libwebsocket *wsi, unsigned char *buf,
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;
@ -1780,76 +1858,7 @@ send_raw:
* 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);
}

View file

@ -415,6 +415,10 @@ lws_client_interpret_server_handshake(struct libwebsocket_context *context,
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 *