| /* |
| 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_cleanup.h" |
| #include "connection_close.h" |
| #include "connection_finish_forward.h" |
| #include "daemon_close_all_connections.h" |
| #include "request_resume.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_mode) |
| { |
| 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_mode); |
| #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_request_resume (&urh->connection->request); |
| } |
| #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. */ |
| MHD_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.\n")); |
| #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. */ |
| MHD_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_connection_cleanup_ (daemon); |
| } |
| |
| |
| /* end of daemon_close_all_connections.c */ |