extensions introduce pre close veto
authorAndy Green <andy@warmcat.com>
Wed, 25 May 2011 20:41:57 +0000 (21:41 +0100)
committerAndy Green <andy@warmcat.com>
Wed, 25 May 2011 20:41:57 +0000 (21:41 +0100)
This establishes a pre-close extension check to allow an extension to
veto a close.  x-google-mux then uses this to stop ch1 going down
(subchannel 1 is the original socket connection that turns into a mux
parent) if it has active mux children; it just marks ch1 as closed in
its conn struct in that case and returns 1 from the callback to veto.

Code is also added to take care of the case ch1 is 'closed', and the
last child is subsequently closed, it actively calls close on the mux
parent then.

Signed-off-by: Andy Green <andy@warmcat.com>
lib/extension-x-google-mux.c
lib/extension-x-google-mux.h
lib/libwebsockets.c
lib/libwebsockets.h

index e8ceec2..4b1a2ab 100644 (file)
@@ -660,8 +660,8 @@ int lws_extension_callback_x_google_mux(
                        }
 
                        if (parent_conn->highest_child_subchannel >=
-                                       sizeof(parent_conn->wsi_children) /
-                                          sizeof(parent_conn->wsi_children[0])) {
+                                       (sizeof(parent_conn->wsi_children) /
+                                          sizeof(parent_conn->wsi_children[0]))) {
                                fprintf(stderr, "Can't add any more children\n");
                                continue;
                        }
@@ -710,6 +710,53 @@ int lws_extension_callback_x_google_mux(
                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;
+
+                       fprintf(stderr, "original mux parent channel closing\n");
+
+                       parent_conn = conn;
+               } else {
+
+                       parent_conn = lws_get_extension_user_matching_ext(conn->wsi_parent, ext);
+                       if (parent_conn == 0) {
+                               fprintf(stderr, "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) {
+                       fprintf(stderr, "VETO closure\n");
+                       return 1;
+               }
+
+               /* no children, ch1 is closed, let him destroy himself */
+
+               if (conn->subchannel == 1)
+                       fprintf(stderr, "ALLOW closure of mux parent\n");
+
+               break;
+
        case LWS_EXT_CALLBACK_DESTROY:
                muxdebug("LWS_EXT_CALLBACK_DESTROY\n");
 
@@ -717,8 +764,21 @@ int lws_extension_callback_x_google_mux(
                 * remove us from parent if noted in parent
                 */
 
-               if (conn->wsi_parent) {
+               if (conn->subchannel == 1) {
+
+                       /*
+                        * special case of original mux parent channel closing
+                        */
+
+                       conn->original_ch1_closed = 1;
+
+                       fprintf(stderr, "original mux parent channel closing\n");
 
+                       parent_conn = conn;
+                       wsi_parent = wsi;
+               } else {
+
+                       wsi_parent = conn->wsi_parent;
                        parent_conn = lws_get_extension_user_matching_ext(conn->wsi_parent, ext);
                        if (parent_conn == 0) {
                                fprintf(stderr, "failed to get parent conn\n");
@@ -729,6 +789,26 @@ int lws_extension_callback_x_google_mux(
                                        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:
@@ -863,33 +943,55 @@ handle_additions:
                pin = *((unsigned char **)in);
                basepin = pin;
 
-               /*
-                * he's not a child connection of a mux
-                */
+               wsi_parent = conn->wsi_parent;
 
-               if (!conn->wsi_parent) {
-//                     fprintf(stderr, "conn %p has no parent\n", (void *)conn);
-                       return 0;
-               }
+               if (conn->subchannel == 1) {
 
-               /*
-                * get parent / transport mux context
-                */
+                       /*
+                        * if we weren't 'closed', then we were the original
+                        * connection that established this link, ie, it's
+                        * the parent wsi
+                        */
 
-               parent_conn = lws_get_extension_user_matching_ext(conn->wsi_parent, ext);
-               if (parent_conn == 0) {
-                       fprintf(stderr, "failed to get parent conn\n");
-                       return 0;
-               }
+                       if (conn->original_ch1_closed) {
+                               fprintf(stderr, "Trying to send on dead original ch1\n");
+                               return 0;
+                       }
 
-               /*
-                * mux transport is in singular mode, let the caller send it
-                * no more muxified than it already is
-                */
+                       /* send on ourselves */
 
-               if (!parent_conn->sticky_mux_used) {
-//                     fprintf(stderr, "parent in singular mode\n");
-                       return 0;
+                       wsi_parent = wsi;
+
+               } else {
+
+                       /*
+                        * he's not a child connection of a mux
+                        */
+
+                       if (!conn->wsi_parent) {
+       //                      fprintf(stderr, "conn %p has no parent\n", (void *)conn);
+                               return 0;
+                       }
+
+                       /*
+                        * get parent / transport mux context
+                        */
+
+                       parent_conn = lws_get_extension_user_matching_ext(conn->wsi_parent, ext);
+                       if (parent_conn == 0) {
+                               fprintf(stderr, "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) {
+       //                      fprintf(stderr, "parent in singular mode\n");
+                               return 0;
+                       }
                }
 
                if (!conn->defeat_mux_opcode_wrapping) {
@@ -919,7 +1021,7 @@ handle_additions:
                 * recurse to allow nesting
                 */
 
-               lws_issue_raw(conn->wsi_parent, basepin, (pin - basepin) + len);
+               lws_issue_raw(wsi_parent, basepin, (pin - basepin) + len);
 
                return 1; /* handled */
 
index 6626bea..bc4b05f 100644 (file)
@@ -84,6 +84,7 @@ struct lws_ext_x_google_mux_conn {
        int count_children_needing_POLLOUT;
        int sticky_mux_used;
        int defeat_mux_opcode_wrapping;
+       int original_ch1_closed;
 };
 
 extern int
index 0a448d9..0021eb4 100644 (file)
@@ -166,6 +166,35 @@ libwebsocket_close_and_free_session(struct libwebsocket_context *context,
        wsi->close_reason = reason;
 
        /*
+        * are his extensions okay with him closing?  Eg he might be a mux
+        * parent and just his ch1 aspect is closing?
+        */
+
+
+       for (n = 0; n < wsi->count_active_extensions; n++) {
+               if (!wsi->active_extensions[n]->callback)
+                       continue;
+
+               m = wsi->active_extensions[n]->callback(context,
+                       wsi->active_extensions[n], wsi,
+                       LWS_EXT_CALLBACK_CHECK_OK_TO_REALLY_CLOSE,
+                                      wsi->active_extensions_user[n], NULL, 0);
+
+               /*
+                * if somebody vetoed actually closing him at this time....
+                * up to the extension to track the attempted close, let's
+                * just bail
+                */
+
+               if (m) {
+                       fprintf(stderr, "extension vetoed close\n");
+                       return;
+               }
+       }
+
+
+
+       /*
         * flush any tx pending from extensions, since we may send close packet
         * if there are problems with send, just nuke the connection
         */
@@ -2145,6 +2174,9 @@ lws_get_extension_user_matching_ext(struct libwebsocket *wsi,
 {
        int n = 0;
 
+       if (wsi == NULL)
+               return NULL;
+
        while (n < wsi->count_active_extensions) {
                if (wsi->active_extensions[n] != ext) {
                        n++;
index 9dfd41c..49bf648 100644 (file)
@@ -82,6 +82,7 @@ enum libwebsocket_extension_callback_reasons {
        LWS_EXT_CALLBACK_CLIENT_CONTEXT_DESTRUCT,
        LWS_EXT_CALLBACK_CONSTRUCT,
        LWS_EXT_CALLBACK_CLIENT_CONSTRUCT,
+       LWS_EXT_CALLBACK_CHECK_OK_TO_REALLY_CLOSE,
        LWS_EXT_CALLBACK_DESTROY,
        LWS_EXT_CALLBACK_DESTROY_ANY_WSI_CLOSING,
        LWS_EXT_CALLBACK_ANY_WSI_ESTABLISHED,