GNU libmicrohttpd  0.9.65
upgrade_process.c
Go to the documentation of this file.
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 */
24 #include "internal.h"
25 #include "upgrade_process.h"
26 
27 
28 #if defined(HTTPS_SUPPORT) && defined(UPGRADE_SUPPORT)
29 
37 void
38 MHD_upgrade_response_handle_process_ (struct MHD_UpgradeResponseHandle *urh)
39 {
40  /* Help compiler to optimize:
41  * pointers to 'connection' and 'daemon' are not changed
42  * during this processing, so no need to chain dereference
43  * each time. */
44  struct MHD_Connection * const connection = urh->connection;
45  struct MHD_Daemon * const daemon = connection->daemon;
46  /* Prevent data races: use same value of 'was_closed' throughout
47  * this function. If 'was_closed' changed externally in the middle
48  * of processing - it will be processed on next iteration. */
49  bool was_closed;
50  struct MHD_TLS_Plugin *tls = daemon->tls_api;
51 
52  if (daemon->shutdown)
53  {
54  /* Daemon shutting down, application will not receive any more data. */
55 #ifdef HAVE_MESSAGES
56  if (! urh->was_closed)
57  {
58  MHD_DLOG (daemon,
59  MHD_SC_DAEMON_ALREADY_SHUTDOWN,
60  _("Initiated daemon shutdown while \"upgraded\" connection was not closed.\n"));
61  }
62 #endif
63  urh->was_closed = true;
64  }
65  was_closed = urh->was_closed;
66  if (was_closed)
67  {
68  /* Application was closed connections: no more data
69  * can be forwarded to application socket. */
70  if (0 < urh->in_buffer_used)
71  {
72 #ifdef HAVE_MESSAGES
73  MHD_DLOG (daemon,
74  MHD_SC_UPGRADE_FORWARD_INCOMPLETE,
75  _("Failed to forward to application " MHD_UNSIGNED_LONG_LONG_PRINTF \
76  " bytes of data received from remote side: application shut down socket\n"),
77  (MHD_UNSIGNED_LONG_LONG) urh->in_buffer_used);
78 #endif
79 
80  }
81  /* If application signaled MHD about socket closure then
82  * check for any pending data even if socket is not marked
83  * as 'ready' (signal may arrive after poll()/select()).
84  * Socketpair for forwarding is always in non-blocking mode
85  * so no risk that recv() will block the thread. */
86  if (0 != urh->out_buffer_size)
87  urh->mhd.celi |= MHD_EPOLL_STATE_READ_READY;
88  /* Discard any data received form remote. */
89  urh->in_buffer_used = 0;
90  /* Do not try to push data to application. */
91  urh->mhd.celi &= ~MHD_EPOLL_STATE_WRITE_READY;
92  /* Reading from remote client is not required anymore. */
93  urh->in_buffer_size = 0;
94  urh->app.celi &= ~MHD_EPOLL_STATE_READ_READY;
95  connection->tls_read_ready = false;
96  }
97 
98  /* On some platforms (W32, possibly Darwin) failed send() (send() will always
99  * fail after remote disconnect was detected) may discard data in system
100  * buffers received by system but not yet read by recv().
101  * So, before trying send() on any socket, recv() must be performed at first
102  * otherwise last part of incoming data may be lost. */
103 
104  /* If disconnect or error was detected - try to read from socket
105  * to dry data possibly pending is system buffers. */
106  if (0 != (MHD_EPOLL_STATE_ERROR & urh->app.celi))
107  urh->app.celi |= MHD_EPOLL_STATE_READ_READY;
108  if (0 != (MHD_EPOLL_STATE_ERROR & urh->mhd.celi))
109  urh->mhd.celi |= MHD_EPOLL_STATE_READ_READY;
110 
111  /*
112  * handle reading from remote TLS client
113  */
114  if ( ( (0 != (MHD_EPOLL_STATE_READ_READY & urh->app.celi)) ||
115  (connection->tls_read_ready) ) &&
116  (urh->in_buffer_used < urh->in_buffer_size) )
117  {
118  ssize_t res;
119  size_t buf_size;
120 
121  buf_size = urh->in_buffer_size - urh->in_buffer_used;
122  if (buf_size > SSIZE_MAX)
123  buf_size = SSIZE_MAX;
124 
125  connection->tls_read_ready = false;
126  res = tls->recv (tls->cls,
127  connection->tls_cs,
128  &urh->in_buffer[urh->in_buffer_used],
129  buf_size);
130  if (0 >= res)
131  {
132  // FIXME: define GNUTLS-independent error codes!
133  if (GNUTLS_E_INTERRUPTED != res)
134  {
135  urh->app.celi &= ~MHD_EPOLL_STATE_READ_READY;
136  if (GNUTLS_E_AGAIN != res)
137  {
138  /* Unrecoverable error on socket was detected or
139  * socket was disconnected/shut down. */
140  /* Stop trying to read from this TLS socket. */
141  urh->in_buffer_size = 0;
142  }
143  }
144  }
145  else /* 0 < res */
146  {
147  urh->in_buffer_used += res;
148  if (buf_size > (size_t)res)
149  urh->app.celi &= ~MHD_EPOLL_STATE_READ_READY;
150  else if (0 < tls->check_record_pending (tls->cls,
151  connection->tls_cs))
152  connection->tls_read_ready = true;
153  }
154  if (MHD_EPOLL_STATE_ERROR ==
156  {
157  /* Unrecoverable error on socket was detected and all
158  * pending data was read from system buffers. */
159  /* Stop trying to read from this TLS socket. */
160  urh->in_buffer_size = 0;
161  }
162  }
163 
164  /*
165  * handle reading from application
166  */
167  if ( (0 != (MHD_EPOLL_STATE_READ_READY & urh->mhd.celi)) &&
168  (urh->out_buffer_used < urh->out_buffer_size) )
169  {
170  ssize_t res;
171  size_t buf_size;
172 
173  buf_size = urh->out_buffer_size - urh->out_buffer_used;
174  if (buf_size > MHD_SCKT_SEND_MAX_SIZE_)
175  buf_size = MHD_SCKT_SEND_MAX_SIZE_;
176 
177  res = MHD_recv_ (urh->mhd.socket,
178  &urh->out_buffer[urh->out_buffer_used],
179  buf_size);
180  if (0 >= res)
181  {
182  const int err = MHD_socket_get_error_ ();
183  if ((0 == res) ||
184  ((! MHD_SCKT_ERR_IS_EINTR_ (err)) &&
186  {
187  urh->mhd.celi &= ~MHD_EPOLL_STATE_READ_READY;
188  if ((0 == res) ||
189  (was_closed) ||
190  (0 != (MHD_EPOLL_STATE_ERROR & urh->mhd.celi)) ||
191  (! MHD_SCKT_ERR_IS_EAGAIN_ (err)))
192  {
193  /* Socket disconnect/shutdown was detected;
194  * Application signaled about closure of 'upgraded' socket;
195  * or persistent / unrecoverable error. */
196  /* Do not try to pull more data from application. */
197  urh->out_buffer_size = 0;
198  }
199  }
200  }
201  else /* 0 < res */
202  {
203  urh->out_buffer_used += res;
204  if (buf_size > (size_t)res)
205  urh->mhd.celi &= ~MHD_EPOLL_STATE_READ_READY;
206  }
207  if ( (0 == (MHD_EPOLL_STATE_READ_READY & urh->mhd.celi)) &&
208  ( (0 != (MHD_EPOLL_STATE_ERROR & urh->mhd.celi)) ||
209  (was_closed) ) )
210  {
211  /* Unrecoverable error on socket was detected and all
212  * pending data was read from system buffers. */
213  /* Do not try to pull more data from application. */
214  urh->out_buffer_size = 0;
215  }
216  }
217 
218  /*
219  * handle writing to remote HTTPS client
220  */
221  if ( (0 != (MHD_EPOLL_STATE_WRITE_READY & urh->app.celi)) &&
222  (urh->out_buffer_used > 0) )
223  {
224  ssize_t res;
225  size_t data_size;
226 
227  data_size = urh->out_buffer_used;
228  if (data_size > SSIZE_MAX)
229  data_size = SSIZE_MAX;
230 
231  res = tls->send (tls->cls,
232  connection->tls_cs,
233  urh->out_buffer,
234  data_size);
235  if (0 >= res)
236  {
237  // FIXME: define GNUTLS-independent error codes!
238  if (GNUTLS_E_INTERRUPTED != res)
239  {
240  urh->app.celi &= ~MHD_EPOLL_STATE_WRITE_READY;
241  if (GNUTLS_E_INTERRUPTED != res)
242  {
243  /* TLS connection shut down or
244  * persistent / unrecoverable error. */
245 #ifdef HAVE_MESSAGES
246  MHD_DLOG (daemon,
247  MHD_SC_UPGRADE_FORWARD_INCOMPLETE,
248  _("Failed to forward to remote client " MHD_UNSIGNED_LONG_LONG_PRINTF \
249  " bytes of data received from application: %s\n"),
250  (MHD_UNSIGNED_LONG_LONG) urh->out_buffer_used,
251  tls->strerror (tls->cls,
252  res));
253 #endif
254  /* Discard any data unsent to remote. */
255  urh->out_buffer_used = 0;
256  /* Do not try to pull more data from application. */
257  urh->out_buffer_size = 0;
258  urh->mhd.celi &= ~MHD_EPOLL_STATE_READ_READY;
259  }
260  }
261  }
262  else /* 0 < res */
263  {
264  const size_t next_out_buffer_used = urh->out_buffer_used - res;
265  if (0 != next_out_buffer_used)
266  {
267  memmove (urh->out_buffer,
268  &urh->out_buffer[res],
269  next_out_buffer_used);
270  if (data_size > (size_t)res)
271  urh->app.celi &= ~MHD_EPOLL_STATE_WRITE_READY;
272  }
273  urh->out_buffer_used = next_out_buffer_used;
274  }
275  if ( (0 == urh->out_buffer_used) &&
276  (0 != (MHD_EPOLL_STATE_ERROR & urh->app.celi)) )
277  {
278  /* Unrecoverable error on socket was detected and all
279  * pending data was sent to remote. */
280  /* Do not try to send to remote anymore. */
281  urh->app.celi &= ~MHD_EPOLL_STATE_WRITE_READY;
282  /* Do not try to pull more data from application. */
283  urh->out_buffer_size = 0;
284  urh->mhd.celi &= ~MHD_EPOLL_STATE_READ_READY;
285  }
286  }
287 
288  /*
289  * handle writing to application
290  */
291  if ( (0 != (MHD_EPOLL_STATE_WRITE_READY & urh->mhd.celi)) &&
292  (urh->in_buffer_used > 0) )
293  {
294  ssize_t res;
295  size_t data_size;
296 
297  data_size = urh->in_buffer_used;
298  if (data_size > MHD_SCKT_SEND_MAX_SIZE_)
299  data_size = MHD_SCKT_SEND_MAX_SIZE_;
300 
301  res = MHD_send_ (urh->mhd.socket,
302  urh->in_buffer,
303  data_size);
304  if (0 >= res)
305  {
306  const int err = MHD_socket_get_error_ ();
307  if ( (! MHD_SCKT_ERR_IS_EINTR_ (err)) &&
309  {
310  urh->mhd.celi &= ~MHD_EPOLL_STATE_WRITE_READY;
311  if (! MHD_SCKT_ERR_IS_EAGAIN_ (err))
312  {
313  /* Socketpair connection shut down or
314  * persistent / unrecoverable error. */
315 #ifdef HAVE_MESSAGES
316  MHD_DLOG (daemon,
317  MHD_SC_UPGRADE_FORWARD_INCOMPLETE,
318  _("Failed to forward to application " MHD_UNSIGNED_LONG_LONG_PRINTF \
319  " bytes of data received from remote side: %s\n"),
320  (MHD_UNSIGNED_LONG_LONG) urh->in_buffer_used,
321  MHD_socket_strerr_ (err));
322 #endif
323  /* Discard any data received form remote. */
324  urh->in_buffer_used = 0;
325  /* Reading from remote client is not required anymore. */
326  urh->in_buffer_size = 0;
327  urh->app.celi &= ~MHD_EPOLL_STATE_READ_READY;
328  connection->tls_read_ready = false;
329  }
330  }
331  }
332  else /* 0 < res */
333  {
334  const size_t next_in_buffer_used = urh->in_buffer_used - res;
335  if (0 != next_in_buffer_used)
336  {
337  memmove (urh->in_buffer,
338  &urh->in_buffer[res],
339  next_in_buffer_used);
340  if (data_size > (size_t)res)
341  urh->mhd.celi &= ~MHD_EPOLL_STATE_WRITE_READY;
342  }
343  urh->in_buffer_used = next_in_buffer_used;
344  }
345  if ( (0 == urh->in_buffer_used) &&
346  (0 != (MHD_EPOLL_STATE_ERROR & urh->mhd.celi)) )
347  {
348  /* Do not try to push data to application. */
349  urh->mhd.celi &= ~MHD_EPOLL_STATE_WRITE_READY;
350  /* Reading from remote client is not required anymore. */
351  urh->in_buffer_size = 0;
352  urh->app.celi &= ~MHD_EPOLL_STATE_READ_READY;
353  connection->tls_read_ready = false;
354  }
355  }
356 
357  /* Check whether data is present in TLS buffers
358  * and incoming forward buffer have some space. */
359  if ( (connection->tls_read_ready) &&
360  (urh->in_buffer_used < urh->in_buffer_size) &&
361  (MHD_TM_THREAD_PER_CONNECTION != daemon->threading_mode) )
362  daemon->data_already_pending = true;
363 
364  if ( (daemon->shutdown) &&
365  ( (0 != urh->out_buffer_size) ||
366  (0 != urh->out_buffer_used) ) )
367  {
368  /* Daemon shutting down, discard any remaining forward data. */
369 #ifdef HAVE_MESSAGES
370  if (0 < urh->out_buffer_used)
371  MHD_DLOG (daemon,
372  MHD_SC_UPGRADE_FORWARD_INCOMPLETE,
373  _("Failed to forward to remote client " MHD_UNSIGNED_LONG_LONG_PRINTF \
374  " bytes of data received from application: daemon shut down\n"),
375  (MHD_UNSIGNED_LONG_LONG) urh->out_buffer_used);
376 #endif
377  /* Discard any data unsent to remote. */
378  urh->out_buffer_used = 0;
379  /* Do not try to sent to remote anymore. */
380  urh->app.celi &= ~MHD_EPOLL_STATE_WRITE_READY;
381  /* Do not try to pull more data from application. */
382  urh->out_buffer_size = 0;
383  urh->mhd.celi &= ~MHD_EPOLL_STATE_READ_READY;
384  }
385 }
386 #endif /* HTTPS_SUPPORT && UPGRADE_SUPPORT */
387 
388 /* end of upgrade_process.c */
#define SSIZE_MAX
Definition: mhd_limits.h:111
#define MHD_send_(s, b, l)
Definition: mhd_sockets.h:259
ssize_t(* send)(void *cls, struct MHD_TLS_ConnectionState *cs, const void *buf, size_t buf_size)
bool data_already_pending
Definition: internal.h:1497
#define MHD_socket_get_error_()
Definition: mhd_sockets.h:507
#define MHD_socket_strerr_(err)
Definition: mhd_sockets.h:525
#define MHD_SCKT_ERR_IS_EAGAIN_(err)
Definition: mhd_sockets.h:622
internal shared structures
#define MHD_UNSIGNED_LONG_LONG
Definition: microhttpd.h:283
struct MHD_Daemon * daemon
Definition: internal.h:672
const char *(* strerror)(void *cls, int ec)
#define MHD_SCKT_SEND_MAX_SIZE_
Definition: mhd_sockets.h:220
ssize_t(* recv)(void *cls, struct MHD_TLS_ConnectionState *cs, void *buf, size_t buf_size)
bool tls_read_ready
Definition: internal.h:766
#define MHD_recv_(s, b, l)
Definition: mhd_sockets.h:270
#define _(String)
Definition: mhd_options.h:42
#define MHD_UNSIGNED_LONG_LONG_PRINTF
Definition: microhttpd.h:296
enum MHD_ThreadingMode threading_mode
Definition: internal.h:1414
volatile bool shutdown
Definition: internal.h:1523
#define MHD_SCKT_ERR_IS_EINTR_(err)
Definition: mhd_sockets.h:613
function to process upgrade activity (over TLS)
enum MHD_Bool(* check_record_pending)(void *cls, struct MHD_TLS_ConnectionState *cs)
#define MHD_SCKT_ERR_IS_LOW_RESOURCES_(err)
Definition: mhd_sockets.h:633