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