GNU libmicrohttpd 0.9.77
Loading...
Searching...
No Matches
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)
37void
38MHD_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 }
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)
#define MHD_SCKT_ERR_IS_LOW_RESOURCES_(err)
#define MHD_socket_strerr_(err)
#define MHD_socket_get_error_()
#define MHD_SCKT_ERR_IS_EINTR_(err)
#define MHD_SCKT_SEND_MAX_SIZE_
#define MHD_recv_(s, b, l)
#define MHD_send_(s, b, l)
#define _(String)
Definition mhd_options.h:42
MHD internal shared structures.
#define SSIZE_MAX
Definition mhd_limits.h:113
#define MHD_UNSIGNED_LONG_LONG
Definition microhttpd.h:311
#define MHD_UNSIGNED_LONG_LONG_PRINTF
Definition microhttpd.h:325
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)
enum MHD_Bool(* check_record_pending)(void *cls, struct MHD_TLS_ConnectionState *cs)
const char *(* strerror)(void *cls, int ec)
ssize_t(* send)(void *cls, struct MHD_TLS_ConnectionState *cs, const void *buf, size_t buf_size)
function to process upgrade activity (over TLS)