blob: 6d19240e0a70bd5c1d33ebfc3a6cbe168ae55333 [file] [log] [blame]
/*
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 */