1 /*
2 This file is part of libmicrohttpd
3 Copyright (C) 2007-2018 Daniel Pittman and Christian Grothoff
4
5 This library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Lesser General Public
7 License as published by the Free Software Foundation; either
8 version 2.1 of the License, or (at your option) any later version.
9
10 This library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public
16 License along with this library; if not, write to the Free Software
17 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18 */
19
20 /**
21 * @file lib/request_resume.c
22 * @brief implementation of MHD_request_resume()
23 * @author Christian Grothoff
24 */
25 #include "internal.h"
26 #include "connection_close.h"
27
28 /**
29 * Resume handling of network data for suspended request. It is
30 * safe to resume a suspended request at any time. Calling this
31 * function on a request that was not previously suspended will
32 * result in undefined behavior.
33 *
34 * If you are using this function in ``external'' select mode, you must
35 * make sure to run #MHD_run() afterwards (before again calling
36 * #MHD_get_fdset(), as otherwise the change may not be reflected in
37 * the set returned by #MHD_get_fdset() and you may end up with a
38 * request that is stuck until the next network activity.
39 *
40 * @param request the request to resume
41 */
42 void
MHD_request_resume(struct MHD_Request * request)43 MHD_request_resume (struct MHD_Request *request)
44 {
45 struct MHD_Daemon *daemon = request->daemon;
46
47 if (daemon->disallow_suspend_resume)
48 MHD_PANIC (_ (
49 "Cannot resume connections without enabling MHD_ALLOW_SUSPEND_RESUME!\n"));
50 MHD_mutex_lock_chk_ (&daemon->cleanup_connection_mutex);
51 request->connection->resuming = true;
52 daemon->resuming = true;
53 MHD_mutex_unlock_chk_ (&daemon->cleanup_connection_mutex);
54 if ( (MHD_ITC_IS_VALID_ (daemon->itc)) &&
55 (! MHD_itc_activate_ (daemon->itc,
56 "r")) )
57 {
58 #ifdef HAVE_MESSAGES
59 MHD_DLOG (daemon,
60 MHD_SC_ITC_USE_FAILED,
61 _ (
62 "Failed to signal resume via inter-thread communication channel.\n"));
63 #endif
64 }
65 }
66
67
68 /**
69 * Run through the suspended connections and move any that are no
70 * longer suspended back to the active state.
71 *
72 * @remark To be called only from thread that process
73 * daemon's select()/poll()/etc.
74 *
75 * @param daemon daemon context
76 * @return true if a connection was actually resumed
77 */
78 bool
MHD_resume_suspended_connections_(struct MHD_Daemon * daemon)79 MHD_resume_suspended_connections_ (struct MHD_Daemon *daemon)
80 /* FIXME: rename connections -> requests? */
81 {
82 struct MHD_Connection *pos;
83 struct MHD_Connection *prev = NULL;
84 bool ret;
85 const bool used_thr_p_c = (MHD_TM_THREAD_PER_CONNECTION ==
86 daemon->threading_mode);
87
88 mhd_assert (NULL == daemon->worker_pool);
89 ret = false;
90 MHD_mutex_lock_chk_ (&daemon->cleanup_connection_mutex);
91
92 if (daemon->resuming)
93 {
94 prev = daemon->suspended_connections_tail;
95 /* During shutdown check for resuming is forced. */
96 mhd_assert ((NULL != prev) || (daemon->shutdown));
97 }
98
99 daemon->resuming = false;
100
101 while (NULL != (pos = prev))
102 {
103 #ifdef UPGRADE_SUPPORT
104 struct MHD_UpgradeResponseHandle *const urh = pos->request.urh;
105 #else /* ! UPGRADE_SUPPORT */
106 static const void *const urh = NULL;
107 #endif /* ! UPGRADE_SUPPORT */
108 prev = pos->prev;
109 if ( (! pos->resuming)
110 #ifdef UPGRADE_SUPPORT
111 || ( (NULL != urh) &&
112 ( (! urh->was_closed) ||
113 (! urh->clean_ready) ) )
114 #endif /* UPGRADE_SUPPORT */
115 )
116 continue;
117 ret = true;
118 mhd_assert (pos->suspended);
119 DLL_remove (daemon->suspended_connections_head,
120 daemon->suspended_connections_tail,
121 pos);
122 pos->suspended = false;
123 if (NULL == urh)
124 {
125 DLL_insert (daemon->connections_head,
126 daemon->connections_tail,
127 pos);
128 if (! used_thr_p_c)
129 {
130 /* Reset timeout timer on resume. */
131 if (0 != pos->connection_timeout)
132 pos->last_activity = MHD_monotonic_sec_counter ();
133
134 if (pos->connection_timeout == daemon->connection_default_timeout)
135 XDLL_insert (daemon->normal_timeout_head,
136 daemon->normal_timeout_tail,
137 pos);
138 else
139 XDLL_insert (daemon->manual_timeout_head,
140 daemon->manual_timeout_tail,
141 pos);
142 }
143 #ifdef EPOLL_SUPPORT
144 if (MHD_ELS_EPOLL == daemon->event_loop_syscall)
145 {
146 if (0 != (pos->epoll_state & MHD_EPOLL_STATE_IN_EREADY_EDLL))
147 MHD_PANIC ("Resumed connection was already in EREADY set.\n");
148 /* we always mark resumed connections as ready, as we
149 might have missed the edge poll event during suspension */
150 EDLL_insert (daemon->eready_head,
151 daemon->eready_tail,
152 pos);
153 pos->epoll_state |= MHD_EPOLL_STATE_IN_EREADY_EDLL \
154 | MHD_EPOLL_STATE_READ_READY
155 | MHD_EPOLL_STATE_WRITE_READY;
156 pos->epoll_state &= ~MHD_EPOLL_STATE_SUSPENDED;
157 }
158 #endif
159 }
160 #ifdef UPGRADE_SUPPORT
161 else
162 {
163 struct MHD_Response *response = pos->request.response;
164
165 /* Data forwarding was finished (for TLS connections) AND
166 * application was closed upgraded connection.
167 * Insert connection into cleanup list. */
168 if ( (NULL != response) &&
169 (MHD_TM_THREAD_PER_CONNECTION != daemon->threading_mode) &&
170 (NULL != response->termination_cb) )
171 response->termination_cb (response->termination_cb_cls,
172 MHD_REQUEST_TERMINATED_COMPLETED_OK,
173 &pos->request.client_context);
174 DLL_insert (daemon->cleanup_head,
175 daemon->cleanup_tail,
176 pos);
177
178 }
179 #endif /* UPGRADE_SUPPORT */
180 pos->resuming = false;
181 }
182 MHD_mutex_unlock_chk_ (&daemon->cleanup_connection_mutex);
183 if ( (used_thr_p_c) &&
184 (ret) )
185 { /* Wake up suspended connections. */
186 if (! MHD_itc_activate_ (daemon->itc,
187 "w"))
188 {
189 #ifdef HAVE_MESSAGES
190 MHD_DLOG (daemon,
191 MHD_SC_ITC_USE_FAILED,
192 _ (
193 "Failed to signal resume of connection via inter-thread communication channel.\n"));
194 #endif
195 }
196 }
197 return ret;
198 }
199
200
201 /* end of request_resume.c */
202