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