aboutsummaryrefslogtreecommitdiff
path: root/src/lib/request_resume.c
blob: be513f5365b22cf3936890a638b3a98dc4c8f0f2 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
/*
  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."));
#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."));
#endif
    }
  }
  return ret;
}


/* end of request_resume.c */