extensions fix deflate stream vs mux and veto it on mux children
authorAndy Green <andy@warmcat.com>
Sat, 28 May 2011 09:19:19 +0000 (10:19 +0100)
committerAndy Green <andy@warmcat.com>
Sat, 28 May 2011 09:19:19 +0000 (10:19 +0100)
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>
README-test-server
lib/extension-x-google-mux.c
lib/libwebsockets.c
lib/libwebsockets.h
lib/parsers.c
lib/private-libwebsockets.h

index 9c8475f..c140b8b 100644 (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
-    
-    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>
 
index 13bc461..95253b4 100644 (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;
        }
index 0021eb4..90e1fbe 100644 (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);
index 49bf648..2ab918d 100644 (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,
index 3e3ac19..7ad040e 100644 (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);
 }
 
 
index bf2865d..e706bf5 100644 (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 *