| /* |
| 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 */ |