libmicrohttpd

HTTP/1.x server C library (MHD 1.x, stable)
Log | Files | Refs | Submodules | README | LICENSE

commit a104a2a46a8d3ef1787f7171295af3acc63c4912
parent 6450f55f0c18d7a4b2fac829d9eff5d567348d19
Author: Christian Grothoff <christian@grothoff.org>
Date:   Fri, 16 Feb 2018 06:48:12 +0100

implementing daemon_close_all_connections

Diffstat:
Msrc/include/microhttpd2.h | 6++++++
Msrc/lib/Makefile.am | 1+
Asrc/lib/daemon_close_all_connections.c | 229+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/lib/daemon_close_all_connections.h | 42++++++++++++++++++++++++++++++++++++++++++
Msrc/lib/daemon_destroy.c | 2+-
Msrc/lib/daemon_start.c | 3++-
6 files changed, 281 insertions(+), 2 deletions(-)

diff --git a/src/include/microhttpd2.h b/src/include/microhttpd2.h @@ -729,6 +729,12 @@ enum MHD_StatusCode * some reason. */ MHD_SC_IP_COUNTER_FAILURE = 50052, + + /** + * Application violated our API by calling shutdown + * while having an upgrade connection still open. + */ + MHD_SC_SHUTDOWN_WITH_OPEN_UPGRADED_CONNECTION = 50053, }; diff --git a/src/lib/Makefile.am b/src/lib/Makefile.am @@ -64,6 +64,7 @@ libmicrohttpd_la_SOURCES = \ connection_info.c \ connection_options.c \ connection_update_last_activity.c connection_update_last_activity.h \ + daemon_close_all_connections.c daemon_close_all_connections.h \ daemon_create.c \ daemon_destroy.c \ daemon_epoll.c daemon_epoll.h \ diff --git a/src/lib/daemon_close_all_connections.c b/src/lib/daemon_close_all_connections.c @@ -0,0 +1,229 @@ +/* + This file is part of libmicrohttpd + Copyright (C) 2007-2018 Daniel Pittman and Christian Grothoff + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +/** + * @file lib/daemon_close_all_connections.c + * @brief function to close all connections open at a daemon + * @author Christian Grothoff + */ +#include "internal.h" +#include "connection_close.h" +#include "connection_finish_forward.h" +#include "daemon_close_all_connections.h" +#include "upgrade_process.h" + + +/** + * Close the given connection, remove it from all of its + * DLLs and move it into the cleanup queue. + * @remark To be called only from thread that + * process daemon's select()/poll()/etc. + * + * @param pos connection to move to cleanup + */ +static void +close_connection (struct MHD_Connection *pos) +{ + struct MHD_Daemon *daemon = pos->daemon; + + if (MHD_TM_THREAD_PER_CONNECTION == daemon->threading_model) + { + MHD_connection_mark_closed_ (pos); + return; /* must let thread to do the rest */ + } + MHD_connection_close_ (pos, + MHD_REQUEST_TERMINATED_DAEMON_SHUTDOWN); + + MHD_mutex_lock_chk_ (&daemon->cleanup_connection_mutex); + + mhd_assert (! pos->suspended); + mhd_assert (! pos->resuming); + if (pos->connection_timeout == + pos->daemon->connection_default_timeout) + XDLL_remove (daemon->normal_timeout_head, + daemon->normal_timeout_tail, + pos); + else + XDLL_remove (daemon->manual_timeout_head, + daemon->manual_timeout_tail, + pos); + DLL_remove (daemon->connections_head, + daemon->connections_tail, + pos); + DLL_insert (daemon->cleanup_head, + daemon->cleanup_tail, + pos); + + MHD_mutex_unlock_chk_ (&daemon->cleanup_connection_mutex); +} + + +/** + * Close all connections for the daemon. Must only be called when + * MHD_Daemon::shutdown was set to true. + * + * @remark To be called only from thread that process daemon's + * select()/poll()/etc. + * + * @param daemon daemon to close down + */ +void +MHD_daemon_close_all_connections_ (struct MHD_Daemon *daemon) +{ + struct MHD_Connection *pos; + const bool used_thr_p_c = (MHD_TM_THREAD_PER_CONNECTION == daemon->threading_model); +#ifdef UPGRADE_SUPPORT + const bool upg_allowed = (! daemon->disallow_upgrade); +#endif /* UPGRADE_SUPPORT */ +#if defined(HTTPS_SUPPORT) && defined(UPGRADE_SUPPORT) + struct MHD_UpgradeResponseHandle *urh; + struct MHD_UpgradeResponseHandle *urhn; + const bool used_tls = (NULL != daemon->tls_api); + + mhd_assert (NULL == daemon->worker_pool); + mhd_assert (daemon->shutdown); + /* give upgraded HTTPS connections a chance to finish */ + /* 'daemon->urh_head' is not used in thread-per-connection mode. */ + for (urh = daemon->urh_tail; NULL != urh; urh = urhn) + { + urhn = urh->prev; + /* call generic forwarding function for passing data + with chance to detect that application is done. */ + MHD_upgrade_response_handle_process_ (urh); + MHD_connection_finish_forward_ (urh->connection); + urh->clean_ready = true; + /* Resuming will move connection to cleanup list. */ + MHD_resume_connection (urh->connection); + } +#endif /* HTTPS_SUPPORT && UPGRADE_SUPPORT */ + + /* Give suspended connections a chance to resume to avoid + running into the check for there not being any suspended + connections left in case of a tight race with a recently + resumed connection. */ + if (! daemon->disallow_suspend_resume) + { + daemon->resuming = true; /* Force check for pending resume. */ + resume_suspended_connections (daemon); + } + /* first, make sure all threads are aware of shutdown; need to + traverse DLLs in peace... */ + MHD_mutex_lock_chk_ (&daemon->cleanup_connection_mutex); +#ifdef UPGRADE_SUPPORT + if (upg_allowed) + { + struct MHD_Connection * susp; + + susp = daemon->suspended_connections_tail; + while (NULL != susp) + { + if (NULL == susp->request.urh) /* "Upgraded" connection? */ + MHD_PANIC (_("MHD_stop_daemon() called while we have suspended connections.\n")); +#ifdef HTTPS_SUPPORT + else if (used_tls && + used_thr_p_c && + (! susp->request.urh->clean_ready) ) + shutdown (susp->request.urh->app.socket, + SHUT_RDWR); /* Wake thread by shutdown of app socket. */ +#endif /* HTTPS_SUPPORT */ + else + { +#ifdef HAVE_MESSAGES + if (! susp->request.urh->was_closed) + MHD_DLOG (daemon, + MHD_SC_SHUTDOWN_WITH_OPEN_UPGRADED_CONNECTION, + _("Initiated daemon shutdown while \"upgraded\" connection was not closed.\n")); +#endif + susp->request.urh->was_closed = true; + /* If thread-per-connection is used, connection's thread + * may still processing "upgrade" (exiting). */ + if (! used_thr_p_c) + MHD_connection_finish_forward_ (susp); + /* Do not use MHD_resume_connection() as mutex is + * already locked. */ + susp->resuming = true; + daemon->resuming = true; + } + susp = susp->prev; + } + } + else /* This 'else' is combined with next 'if' */ +#endif /* UPGRADE_SUPPORT */ + if (NULL != daemon->suspended_connections_head) + MHD_PANIC (_("MHD_stop_daemon() called while we have suspended connections.\n")); + for (pos = daemon->connections_tail; NULL != pos; pos = pos->prev) + { + shutdown (pos->socket_fd, + SHUT_RDWR); +#if MHD_WINSOCK_SOCKETS + if ( (used_thr_p_c) && + (MHD_ITC_IS_VALID_(daemon->itc)) && + (! MHD_itc_activate_ (daemon->itc, + "e")) ) + MHD_PANIC (_("Failed to signal shutdown via inter-thread communication channel")); +#endif + } + + /* now, collect per-connection threads */ + if (used_thr_p_c) + { + pos = daemon->connections_tail; + while (NULL != pos) + { + if (! pos->thread_joined) + { + MHD_mutex_unlock_chk_ (&daemon->cleanup_connection_mutex); + if (! MHD_join_thread_ (pos->pid.handle)) + MHD_PANIC (_("Failed to join a thread\n")); + MHD_mutex_lock_chk_ (&daemon->cleanup_connection_mutex); + pos->thread_joined = true; + /* The thread may have concurrently modified the DLL, + need to restart from the beginning */ + pos = daemon->connections_tail; + continue; + } + pos = pos->prev; + } + } + MHD_mutex_unlock_chk_ (&daemon->cleanup_connection_mutex); + +#ifdef UPGRADE_SUPPORT + /* Finished threads with "upgraded" connections need to be moved + * to cleanup list by resume_suspended_connections(). */ + /* "Upgraded" connections that were not closed explicitly by + * application should be moved to cleanup list too. */ + if (upg_allowed) + { + daemon->resuming = true; /* Force check for pending resume. */ + resume_suspended_connections (daemon); + } +#endif /* UPGRADE_SUPPORT */ + + /* now that we're alone, move everyone to cleanup */ + while (NULL != (pos = daemon->connections_tail)) + { + if ( (used_thr_p_c) && + (! pos->thread_joined) ) + MHD_PANIC (_("Failed to join a thread\n")); + close_connection (pos); + } + MHD_cleanup_connections (daemon); +} + +/* end of daemon_close_all_connections.c */ diff --git a/src/lib/daemon_close_all_connections.h b/src/lib/daemon_close_all_connections.h @@ -0,0 +1,42 @@ +/* + This file is part of libmicrohttpd + Copyright (C) 2007-2018 Daniel Pittman and Christian Grothoff + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +/** + * @file lib/daemon_close_all_connections.h + * @brief function to close all connections open at a daemon + * @author Christian Grothoff + */ +#ifndef DAEMON_CLOSE_ALL_CONNECTIONS_H +#define DAEMON_CLOSE_ALL_CONNECTIONS_H + + +/** + * Close all connections for the daemon. Must only be called when + * MHD_Daemon::shutdown was set to true. + * + * @remark To be called only from thread that process daemon's + * select()/poll()/etc. + * + * @param daemon daemon to close down + */ +void +MHD_daemon_close_all_connections_ (struct MHD_Daemon *daemon) + MHD_NONNULL (1); + +#endif diff --git a/src/lib/daemon_destroy.c b/src/lib/daemon_destroy.c @@ -147,7 +147,7 @@ MHD_daemon_destroy (struct MHD_Daemon *daemon) { /* No internal threads are used for polling sockets (external event loop) */ - close_all_connections (daemon); + MHD_daemon_close_all_connections_ (daemon); } if (MHD_ITC_IS_VALID_ (daemon->itc)) MHD_itc_destroy_chk_ (daemon->itc); diff --git a/src/lib/daemon_start.c b/src/lib/daemon_start.c @@ -23,6 +23,7 @@ * @author Christian Grothoff */ #include "internal.h" +#include "daemon_close_all_connections.h" #include "daemon_select.h" @@ -639,7 +640,7 @@ MHD_polling_thread (void *cls) /* Resume any pending for resume connections, join * all connection's threads (if any) and finally cleanup * everything. */ - close_all_connections (daemon); + MHD_daemon_close_all_connections_ (daemon); return (MHD_THRD_RTRN_TYPE_)0; }