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