blob: 52dc23771c01542ee7deb29b6a21ce8912564206 [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/request_resume.c
* @brief implementation of MHD_request_resume()
* @author Christian Grothoff
*/
#include "internal.h"
#include "connection_close.h"
/**
* Resume handling of network data for suspended request. It is
* safe to resume a suspended request at any time. Calling this
* function on a request that was not previously suspended will
* result in undefined behavior.
*
* If you are using this function in ``external'' select mode, you must
* make sure to run #MHD_run() afterwards (before again calling
* #MHD_get_fdset(), as otherwise the change may not be reflected in
* the set returned by #MHD_get_fdset() and you may end up with a
* request that is stuck until the next network activity.
*
* @param request the request to resume
*/
void
MHD_request_resume (struct MHD_Request *request)
{
struct MHD_Daemon *daemon = request->daemon;
if (daemon->disallow_suspend_resume)
MHD_PANIC (_ (
"Cannot resume connections without enabling MHD_ALLOW_SUSPEND_RESUME!\n"));
MHD_mutex_lock_chk_ (&daemon->cleanup_connection_mutex);
request->connection->resuming = true;
daemon->resuming = true;
MHD_mutex_unlock_chk_ (&daemon->cleanup_connection_mutex);
if ( (MHD_ITC_IS_VALID_ (daemon->itc)) &&
(! MHD_itc_activate_ (daemon->itc,
"r")) )
{
#ifdef HAVE_MESSAGES
MHD_DLOG (daemon,
MHD_SC_ITC_USE_FAILED,
_ (
"Failed to signal resume via inter-thread communication channel.\n"));
#endif
}
}
/**
* Run through the suspended connections and move any that are no
* longer suspended back to the active state.
*
* @remark To be called only from thread that process
* daemon's select()/poll()/etc.
*
* @param daemon daemon context
* @return true if a connection was actually resumed
*/
bool
MHD_resume_suspended_connections_ (struct MHD_Daemon *daemon)
/* FIXME: rename connections -> requests? */
{
struct MHD_Connection *pos;
struct MHD_Connection *prev = NULL;
bool ret;
const bool used_thr_p_c = (MHD_TM_THREAD_PER_CONNECTION ==
daemon->threading_mode);
mhd_assert (NULL == daemon->worker_pool);
ret = false;
MHD_mutex_lock_chk_ (&daemon->cleanup_connection_mutex);
if (daemon->resuming)
{
prev = daemon->suspended_connections_tail;
/* During shutdown check for resuming is forced. */
mhd_assert ((NULL != prev) || (daemon->shutdown));
}
daemon->resuming = false;
while (NULL != (pos = prev))
{
#ifdef UPGRADE_SUPPORT
struct MHD_UpgradeResponseHandle *const urh = pos->request.urh;
#else /* ! UPGRADE_SUPPORT */
static const void *const urh = NULL;
#endif /* ! UPGRADE_SUPPORT */
prev = pos->prev;
if ( (! pos->resuming)
#ifdef UPGRADE_SUPPORT
|| ( (NULL != urh) &&
( (! urh->was_closed) ||
(! urh->clean_ready) ) )
#endif /* UPGRADE_SUPPORT */
)
continue;
ret = true;
mhd_assert (pos->suspended);
DLL_remove (daemon->suspended_connections_head,
daemon->suspended_connections_tail,
pos);
pos->suspended = false;
if (NULL == urh)
{
DLL_insert (daemon->connections_head,
daemon->connections_tail,
pos);
if (! used_thr_p_c)
{
/* Reset timeout timer on resume. */
if (0 != pos->connection_timeout)
pos->last_activity = MHD_monotonic_sec_counter ();
if (pos->connection_timeout == daemon->connection_default_timeout)
XDLL_insert (daemon->normal_timeout_head,
daemon->normal_timeout_tail,
pos);
else
XDLL_insert (daemon->manual_timeout_head,
daemon->manual_timeout_tail,
pos);
}
#ifdef EPOLL_SUPPORT
if (MHD_ELS_EPOLL == daemon->event_loop_syscall)
{
if (0 != (pos->epoll_state & MHD_EPOLL_STATE_IN_EREADY_EDLL))
MHD_PANIC ("Resumed connection was already in EREADY set.\n");
/* we always mark resumed connections as ready, as we
might have missed the edge poll event during suspension */
EDLL_insert (daemon->eready_head,
daemon->eready_tail,
pos);
pos->epoll_state |= MHD_EPOLL_STATE_IN_EREADY_EDLL \
| MHD_EPOLL_STATE_READ_READY
| MHD_EPOLL_STATE_WRITE_READY;
pos->epoll_state &= ~MHD_EPOLL_STATE_SUSPENDED;
}
#endif
}
#ifdef UPGRADE_SUPPORT
else
{
struct MHD_Response *response = pos->request.response;
/* Data forwarding was finished (for TLS connections) AND
* application was closed upgraded connection.
* Insert connection into cleanup list. */
if ( (NULL != response) &&
(MHD_TM_THREAD_PER_CONNECTION != daemon->threading_mode) &&
(NULL != response->termination_cb) )
response->termination_cb (response->termination_cb_cls,
MHD_REQUEST_TERMINATED_COMPLETED_OK,
&pos->request.client_context);
DLL_insert (daemon->cleanup_head,
daemon->cleanup_tail,
pos);
}
#endif /* UPGRADE_SUPPORT */
pos->resuming = false;
}
MHD_mutex_unlock_chk_ (&daemon->cleanup_connection_mutex);
if ( (used_thr_p_c) &&
(ret) )
{ /* Wake up suspended connections. */
if (! MHD_itc_activate_ (daemon->itc,
"w"))
{
#ifdef HAVE_MESSAGES
MHD_DLOG (daemon,
MHD_SC_ITC_USE_FAILED,
_ (
"Failed to signal resume of connection via inter-thread communication channel.\n"));
#endif
}
}
return ret;
}
/* end of request_resume.c */