From 494418abace48baff79c679a25a4e5aa034589a3 Mon Sep 17 00:00:00 2001 From: Andy Green Date: Wed, 2 Mar 2016 09:17:22 +0800 Subject: [PATCH] add explicit parent child wsi relationships wsi can have a full tree relationship with each other using linked lists. closing the parent ensures the children are closed first. Convert cgi to use this instead of his cgi-specific sub-wsi management. Signed-off-by: Andy Green --- changelog | 7 +++++ lib/client-handshake.c | 20 +++++++++----- lib/libwebsockets.c | 64 ++++++++++++++++++++++++++++++++++++--------- lib/libwebsockets.h | 10 +++++++ lib/private-libwebsockets.h | 4 ++- lib/service.c | 8 +++--- test-server/lws-cgi-test.sh | 2 ++ 7 files changed, 91 insertions(+), 24 deletions(-) diff --git a/changelog b/changelog index 9be1268..8ce86c9 100644 --- a/changelog +++ b/changelog @@ -149,6 +149,13 @@ There are 4 new related callbacks LWS_CALLBACK_RECEIVE_CLIENT_HTTP = 46, LWS_CALLBACK_COMPLETED_CLIENT_HTTP = 47, +6) struct lws_client_connect_info has a new member + + const char *parent_wsi + +if non-NULL, the client wsi is set to be a child of parent_wsi. This ensures +if parent_wsi closes, then the client child is closed just before. + v1.7.0 ====== diff --git a/lib/client-handshake.c b/lib/client-handshake.c index d1baa0e..ba7a44a 100644 --- a/lib/client-handshake.c +++ b/lib/client-handshake.c @@ -407,7 +407,12 @@ lws_client_connect_via_info(struct lws_client_connect_info *i) if (wsi && !wsi->user_space && i->userdata) { wsi->user_space_externally_allocated = 1; wsi->user_space = i->userdata; - } + } else + /* if we stay in http, we can assign the user space now, + * otherwise do it after the protocol negotiated + */ + if (i->method) + lws_ensure_user_space(wsi); #ifdef LWS_OPENSSL_SUPPORT wsi->use_ssl = i->ssl_connection; @@ -417,11 +422,6 @@ lws_client_connect_via_info(struct lws_client_connect_info *i) goto bail; } #endif - wsi->protocol = &i->context->protocols[0]; - if (wsi && !wsi->user_space && i->userdata) { - wsi->user_space_externally_allocated = 1; - wsi->user_space = i->userdata; - } /* 2) stash the things from connect_info that we can't process without * an ah. Because if no ah, we will go on the ah waiting list and @@ -469,6 +469,14 @@ lws_client_connect_via_info(struct lws_client_connect_info *i) if (lws_header_table_attach(wsi, 0)) lwsl_debug("%s: went on ah wait list\n", __func__); + if (i->parent_wsi) { + lwsl_info("%s: created child %p of parent %p\n", __func__, + wsi, i->parent_wsi); + wsi->parent = i->parent_wsi; + wsi->sibling_list = i->parent_wsi->child_list; + i->parent_wsi->child_list = wsi; + } + return wsi; bail: diff --git a/lib/libwebsockets.c b/lib/libwebsockets.c index 056921e..72cc2fd 100644 --- a/lib/libwebsockets.c +++ b/lib/libwebsockets.c @@ -142,10 +142,11 @@ lws_set_timeout(struct lws *wsi, enum pending_timeout reason, int secs) void lws_close_free_wsi(struct lws *wsi, enum lws_close_status reason) { - struct lws_context *context; struct lws_context_per_thread *pt; - int n, m, ret; + struct lws **pwsi, *wsi1, *wsi2; + struct lws_context *context; struct lws_tokens eff_buf; + int n, m, ret; if (!wsi) return; @@ -153,24 +154,31 @@ lws_close_free_wsi(struct lws *wsi, enum lws_close_status reason) context = wsi->context; pt = &context->pt[(int)wsi->tsi]; + /* if we have children, close them first */ + if (wsi->child_list) { + wsi2 = wsi->child_list; + while (wsi2) { + lwsl_notice("%s: closing %p: close child %p\n", + __func__, wsi, wsi2); + wsi1 = wsi2->sibling_list; + lws_close_free_wsi(wsi2, reason); + wsi2 = wsi1; + } + } + #ifdef LWS_WITH_CGI if (wsi->mode == LWSCM_CGI) { /* we are not a network connection, but a handler for CGI io */ - assert(wsi->master); - assert(wsi->master->cgi); - assert(wsi->master->cgi->stdwsi[(int)wsi->cgi_channel] == wsi); + if (wsi->parent && wsi->parent->cgi) /* end the binding between us and master */ - wsi->master->cgi->stdwsi[(int)wsi->cgi_channel] = NULL; - wsi->master = NULL; + wsi->parent->cgi->stdwsi[(int)wsi->cgi_channel] = NULL; wsi->socket_is_permanently_unusable = 1; goto just_kill_connection; } if (wsi->cgi) { - /* we have a cgi going, we must kill it and close the - * related stdin/out/err wsis first - */ + /* we have a cgi going, we must kill it */ wsi->cgi->being_closed = 1; lws_cgi_kill(wsi); } @@ -327,6 +335,22 @@ lws_close_free_wsi(struct lws *wsi, enum lws_close_status reason) } just_kill_connection: + if (wsi->parent) { + /* detach ourselves from parent's child list */ + pwsi = &wsi->parent->child_list; + while (*pwsi) { + if (*pwsi == wsi) { + lwsl_notice("%s: detach %p from parent %p\n", + __func__, wsi, wsi->parent); + *pwsi = wsi->sibling_list; + break; + } + pwsi = &(*pwsi)->sibling_list; + } + if (*pwsi) + lwsl_err("%s: failed to detach from parent\n", + __func__); + } #if LWS_POSIX /* @@ -1182,6 +1206,18 @@ lws_wsi_user(struct lws *wsi) return wsi->user_space; } +LWS_VISIBLE LWS_EXTERN struct lws * +lws_get_parent(const struct lws *wsi) +{ + return wsi->parent; +} + +LWS_VISIBLE LWS_EXTERN struct lws * +lws_get_child(const struct lws *wsi) +{ + return wsi->child_list; +} + LWS_VISIBLE LWS_EXTERN void lws_close_reason(struct lws *wsi, enum lws_close_status status, unsigned char *buf, size_t len) @@ -1480,9 +1516,11 @@ lws_cgi(struct lws *wsi, char * const *exec_array, int timeout_secs) } for (n = 0; n < 3; n++) { - cgi->stdwsi[n]->master = wsi; if (insert_wsi_socket_into_fds(wsi->context, cgi->stdwsi[n])) goto bail3; + cgi->stdwsi[n]->parent = wsi; + cgi->stdwsi[n]->sibling_list = wsi->child_list; + wsi->child_list = cgi->stdwsi[n]; } lws_change_pollfd(cgi->stdwsi[LWS_STDIN], LWS_POLLIN, LWS_POLLOUT); @@ -1496,6 +1534,8 @@ lws_cgi(struct lws *wsi, char * const *exec_array, int timeout_secs) lws_set_timeout(wsi, PENDING_TIMEOUT_CGI, timeout_secs); + + /* add us to the pt list of active cgis */ cgi->cgi_list = pt->cgi_list; pt->cgi_list = cgi; @@ -1633,8 +1673,6 @@ lws_cgi_kill(struct lws *wsi) if (wsi->cgi->pipe_fds[n][!!(n == 0)] >= 0) { close(wsi->cgi->pipe_fds[n][!!(n == 0)]); wsi->cgi->pipe_fds[n][!!(n == 0)] = -1; - - lws_close_free_wsi(wsi->cgi->stdwsi[n], 0); } } diff --git a/lib/libwebsockets.h b/lib/libwebsockets.h index f38fc56..3126e84 100644 --- a/lib/libwebsockets.h +++ b/lib/libwebsockets.h @@ -1410,6 +1410,9 @@ struct lws_context_creation_info { * @client_exts: array of extensions that may be used on connection * @method: if non-NULL, do this http method instead of ws[s] upgrade. * use "GET" to be a simple http client connection + * @parent_wsi: if another wsi is responsible for this connection, give it here. + * this is used to make sure if the parent closes so do any + * child connections first. */ struct lws_client_connect_info { @@ -1425,6 +1428,7 @@ struct lws_client_connect_info { void *userdata; const struct lws_extension *client_exts; const char *method; + struct lws *parent_wsi; /* Add new things just above here ---^ * This is part of the ABI, don't needlessly break compatibility @@ -1847,6 +1851,12 @@ lws_get_context(const struct lws *wsi); LWS_VISIBLE LWS_EXTERN int LWS_WARN_UNUSED_RESULT lws_get_count_threads(struct lws_context *context); +LWS_VISIBLE LWS_EXTERN struct lws * LWS_WARN_UNUSED_RESULT +lws_get_parent(const struct lws *wsi); + +LWS_VISIBLE LWS_EXTERN struct lws * LWS_WARN_UNUSED_RESULT +lws_get_child(const struct lws *wsi); + #ifdef LWS_WITH_CGI enum lws_enum_stdinouterr { LWS_STDIN = 0, diff --git a/lib/private-libwebsockets.h b/lib/private-libwebsockets.h index 4a9a92b..c93f688 100644 --- a/lib/private-libwebsockets.h +++ b/lib/private-libwebsockets.h @@ -1075,9 +1075,11 @@ struct lws { /* pointers */ struct lws_context *context; + struct lws *parent; /* points to parent, if any */ + struct lws *child_list; /* points to first child */ + struct lws *sibling_list; /* subsequent children at same level */ #ifdef LWS_WITH_CGI struct lws_cgi *cgi; /* wsi being cgi master have one of these */ - struct lws *master; /* for stdin/out/err wsi to point to cgi master */ #endif const struct lws_protocols *protocol; struct lws *timeout_list; diff --git a/lib/service.c b/lib/service.c index d27e4e0..bec262d 100644 --- a/lib/service.c +++ b/lib/service.c @@ -871,12 +871,12 @@ handle_pending: } args.ch = wsi->cgi_channel; - args.stdwsi = &wsi->master->cgi->stdwsi[0]; + args.stdwsi = &wsi->parent->cgi->stdwsi[0]; if (user_callback_handle_rxflow( - wsi->master->protocol->callback, - wsi->master, LWS_CALLBACK_CGI, - wsi->master->user_space, + wsi->parent->protocol->callback, + wsi->parent, LWS_CALLBACK_CGI, + wsi->parent->user_space, (void *)&args, 0)) return 1; diff --git a/test-server/lws-cgi-test.sh b/test-server/lws-cgi-test.sh index 90c804e..2f5df11 100755 --- a/test-server/lws-cgi-test.sh +++ b/test-server/lws-cgi-test.sh @@ -8,6 +8,8 @@ echo "REQUEST_METHOD=$REQUEST_METHOD" if [ "$REQUEST_METHOD" = "POST" ] ; then read line echo "read=\"$line\"" +else + cat /proc/meminfo fi echo "done" -- 2.7.4