diff options
Diffstat (limited to 'src/microhttpd/test_upgrade_large.c')
-rw-r--r-- | src/microhttpd/test_upgrade_large.c | 1386 |
1 files changed, 1386 insertions, 0 deletions
diff --git a/src/microhttpd/test_upgrade_large.c b/src/microhttpd/test_upgrade_large.c new file mode 100644 index 00000000..6f2a1b94 --- /dev/null +++ b/src/microhttpd/test_upgrade_large.c | |||
@@ -0,0 +1,1386 @@ | |||
1 | /* | ||
2 | This file is part of libmicrohttpd | ||
3 | Copyright (C) 2016, 2019 Christian Grothoff | ||
4 | |||
5 | libmicrohttpd is free software; you can redistribute it and/or modify | ||
6 | it under the terms of the GNU General Public License as published | ||
7 | by the Free Software Foundation; either version 3, or (at your | ||
8 | option) any later version. | ||
9 | |||
10 | libmicrohttpd is distributed in the hope that it will be useful, but | ||
11 | WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
13 | General Public License for more details. | ||
14 | |||
15 | You should have received a copy of the GNU General Public License | ||
16 | along with libmicrohttpd; see the file COPYING. If not, write to the | ||
17 | Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, | ||
18 | Boston, MA 02110-1301, USA. | ||
19 | */ | ||
20 | |||
21 | /** | ||
22 | * @file test_upgrade_large.c | ||
23 | * @brief Testcase for libmicrohttpd upgrading a connection, | ||
24 | * modified to test the "large" corner case reported | ||
25 | * by Viet on the mailinglist in 6'2019 | ||
26 | * @author Christian Grothoff | ||
27 | * @author Karlson2k (Evgeny Grin) | ||
28 | */ | ||
29 | |||
30 | #include "mhd_options.h" | ||
31 | #include <stdlib.h> | ||
32 | #include <string.h> | ||
33 | #include <stdio.h> | ||
34 | #include <pthread.h> | ||
35 | #include <stdlib.h> | ||
36 | #include <stddef.h> | ||
37 | #ifndef WINDOWS | ||
38 | #include <unistd.h> | ||
39 | #endif | ||
40 | #ifdef HAVE_STDBOOL_H | ||
41 | #include <stdbool.h> | ||
42 | #endif /* HAVE_STDBOOL_H */ | ||
43 | |||
44 | #include "mhd_sockets.h" | ||
45 | #ifdef HAVE_NETINET_IP_H | ||
46 | #include <netinet/ip.h> | ||
47 | #endif /* HAVE_NETINET_IP_H */ | ||
48 | |||
49 | #include "platform.h" | ||
50 | #include "microhttpd.h" | ||
51 | |||
52 | #include "test_helpers.h" | ||
53 | |||
54 | #define LARGE_STRING "HelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHello" | ||
55 | |||
56 | #define LARGE_REPLY_STRING "WorldWorldWorldWorldWorldWorldWorldWorldWorldWorldWorldWorldWorldWorldWorldWorldWorldWorldWorldWorldWorldWorldWorldWorldWorldWorldWorldWorldWorldWorldWorldWorldWorldWorldWorldWorldWorldWorldWorldWorld" | ||
57 | |||
58 | #ifdef HTTPS_SUPPORT | ||
59 | #include <gnutls/gnutls.h> | ||
60 | #include "../testcurl/https/tls_test_keys.h" | ||
61 | |||
62 | #if defined(HAVE_FORK) && defined(HAVE_WAITPID) | ||
63 | #include <sys/types.h> | ||
64 | #include <sys/wait.h> | ||
65 | #endif /* HAVE_FORK && HAVE_WAITPID */ | ||
66 | #endif /* HTTPS_SUPPORT */ | ||
67 | |||
68 | static int verbose = 0; | ||
69 | |||
70 | static int kicker[2] = {-1, -1} ; | ||
71 | |||
72 | enum tls_tool | ||
73 | { | ||
74 | TLS_CLI_NO_TOOL = 0, | ||
75 | TLS_CLI_GNUTLS, | ||
76 | TLS_CLI_OPENSSL, | ||
77 | TLS_LIB_GNUTLS | ||
78 | }; | ||
79 | |||
80 | enum tls_tool use_tls_tool; | ||
81 | |||
82 | #if defined(HTTPS_SUPPORT) && defined(HAVE_FORK) && defined(HAVE_WAITPID) | ||
83 | /** | ||
84 | * Fork child that connects via GnuTLS-CLI to our @a port. Allows us to | ||
85 | * talk to our port over a socket in @a sp without having to worry | ||
86 | * about TLS. | ||
87 | * | ||
88 | * @param location where the socket is returned | ||
89 | * @return -1 on error, otherwise PID of TLS child process | ||
90 | */ | ||
91 | static pid_t | ||
92 | gnutlscli_connect (int *sock, | ||
93 | uint16_t port) | ||
94 | { | ||
95 | pid_t chld; | ||
96 | int sp[2]; | ||
97 | char destination[30]; | ||
98 | |||
99 | if (0 != socketpair (AF_UNIX, | ||
100 | SOCK_STREAM, | ||
101 | 0, | ||
102 | sp)) | ||
103 | return -1; | ||
104 | chld = fork (); | ||
105 | if (0 != chld) | ||
106 | { | ||
107 | *sock = sp[1]; | ||
108 | MHD_socket_close_chk_ (sp[0]); | ||
109 | return chld; | ||
110 | } | ||
111 | MHD_socket_close_chk_ (sp[1]); | ||
112 | (void) close (0); | ||
113 | (void) close (1); | ||
114 | if (-1 == dup2 (sp[0], 0)) | ||
115 | abort (); | ||
116 | if (-1 == dup2 (sp[0], 1)) | ||
117 | abort (); | ||
118 | MHD_socket_close_chk_ (sp[0]); | ||
119 | if (TLS_CLI_GNUTLS == use_tls_tool) | ||
120 | { | ||
121 | snprintf (destination, | ||
122 | sizeof(destination), | ||
123 | "%u", | ||
124 | (unsigned int) port); | ||
125 | execlp ("gnutls-cli", | ||
126 | "gnutls-cli", | ||
127 | "--insecure", | ||
128 | "-p", | ||
129 | destination, | ||
130 | "127.0.0.1", | ||
131 | (char *) NULL); | ||
132 | } | ||
133 | else if (TLS_CLI_OPENSSL == use_tls_tool) | ||
134 | { | ||
135 | snprintf (destination, | ||
136 | sizeof(destination), | ||
137 | "127.0.0.1:%u", | ||
138 | (unsigned int) port); | ||
139 | execlp ("openssl", | ||
140 | "openssl", | ||
141 | "s_client", | ||
142 | "-connect", | ||
143 | destination, | ||
144 | "-verify", | ||
145 | "1", | ||
146 | (char *) NULL); | ||
147 | } | ||
148 | _exit (1); | ||
149 | } | ||
150 | #endif /* HTTPS_SUPPORT && HAVE_FORK && HAVE_WAITPID */ | ||
151 | |||
152 | |||
153 | /** | ||
154 | * Wrapper structure for plain&TLS sockets | ||
155 | */ | ||
156 | struct wr_socket | ||
157 | { | ||
158 | /** | ||
159 | * Real network socket | ||
160 | */ | ||
161 | MHD_socket fd; | ||
162 | |||
163 | /** | ||
164 | * Type of this socket | ||
165 | */ | ||
166 | enum wr_type | ||
167 | { | ||
168 | wr_invalid = 0, | ||
169 | wr_plain = 1, | ||
170 | wr_tls = 2 | ||
171 | } t; | ||
172 | #ifdef HTTPS_SUPPORT | ||
173 | /** | ||
174 | * TLS credentials | ||
175 | */ | ||
176 | gnutls_certificate_credentials_t tls_crd; | ||
177 | |||
178 | /** | ||
179 | * TLS session. | ||
180 | */ | ||
181 | gnutls_session_t tls_s; | ||
182 | |||
183 | /** | ||
184 | * TLS handshake already succeed? | ||
185 | */ | ||
186 | bool tls_connected; | ||
187 | #endif | ||
188 | }; | ||
189 | |||
190 | |||
191 | /** | ||
192 | * Get underlying real socket. | ||
193 | * @return FD of real socket | ||
194 | */ | ||
195 | #define wr_fd(s) ((s)->fd) | ||
196 | |||
197 | |||
198 | /** | ||
199 | * Create wr_socket with plain TCP underlying socket | ||
200 | * @return created socket on success, NULL otherwise | ||
201 | */ | ||
202 | static struct wr_socket * | ||
203 | wr_create_plain_sckt(void) | ||
204 | { | ||
205 | struct wr_socket *s = malloc(sizeof(struct wr_socket)); | ||
206 | if (NULL == s) | ||
207 | return NULL; | ||
208 | s->t = wr_plain; | ||
209 | s->fd = socket (AF_INET, SOCK_STREAM, IPPROTO_TCP); | ||
210 | if (MHD_INVALID_SOCKET != s->fd) | ||
211 | return s; | ||
212 | free(s); | ||
213 | return NULL; | ||
214 | } | ||
215 | |||
216 | |||
217 | /** | ||
218 | * Create wr_socket with TLS TCP underlying socket | ||
219 | * @return created socket on success, NULL otherwise | ||
220 | */ | ||
221 | static struct wr_socket * | ||
222 | wr_create_tls_sckt(void) | ||
223 | { | ||
224 | #ifdef HTTPS_SUPPORT | ||
225 | struct wr_socket *s = malloc(sizeof(struct wr_socket)); | ||
226 | if (NULL == s) | ||
227 | return NULL; | ||
228 | s->t = wr_tls; | ||
229 | s->tls_connected = 0; | ||
230 | s->fd = socket (AF_INET, SOCK_STREAM, IPPROTO_TCP); | ||
231 | if (MHD_INVALID_SOCKET != s->fd) | ||
232 | { | ||
233 | if (GNUTLS_E_SUCCESS == gnutls_init (&(s->tls_s), GNUTLS_CLIENT)) | ||
234 | { | ||
235 | if (GNUTLS_E_SUCCESS == gnutls_set_default_priority (s->tls_s)) | ||
236 | { | ||
237 | if (GNUTLS_E_SUCCESS == gnutls_certificate_allocate_credentials (&(s->tls_crd))) | ||
238 | { | ||
239 | if (GNUTLS_E_SUCCESS == gnutls_credentials_set (s->tls_s, GNUTLS_CRD_CERTIFICATE, s->tls_crd)) | ||
240 | { | ||
241 | #if GNUTLS_VERSION_NUMBER+0 >= 0x030109 | ||
242 | gnutls_transport_set_int (s->tls_s, (int)(s->fd)); | ||
243 | #else /* GnuTLS before 3.1.9 */ | ||
244 | gnutls_transport_set_ptr (s->tls_s, (gnutls_transport_ptr_t)(intptr_t)(s->fd)); | ||
245 | #endif /* GnuTLS before 3.1.9 */ | ||
246 | return s; | ||
247 | } | ||
248 | gnutls_certificate_free_credentials (s->tls_crd); | ||
249 | } | ||
250 | } | ||
251 | gnutls_deinit (s->tls_s); | ||
252 | } | ||
253 | (void)MHD_socket_close_ (s->fd); | ||
254 | } | ||
255 | free(s); | ||
256 | #endif /* HTTPS_SUPPORT */ | ||
257 | return NULL; | ||
258 | } | ||
259 | |||
260 | |||
261 | /** | ||
262 | * Create wr_socket with plain TCP underlying socket | ||
263 | * from already created TCP socket. | ||
264 | * @param plain_sk real TCP socket | ||
265 | * @return created socket on success, NULL otherwise | ||
266 | */ | ||
267 | static struct wr_socket * | ||
268 | wr_create_from_plain_sckt(MHD_socket plain_sk) | ||
269 | { | ||
270 | struct wr_socket *s = malloc(sizeof(struct wr_socket)); | ||
271 | |||
272 | if (NULL == s) | ||
273 | return NULL; | ||
274 | s->t = wr_plain; | ||
275 | s->fd = plain_sk; | ||
276 | return s; | ||
277 | } | ||
278 | |||
279 | |||
280 | /** | ||
281 | * Connect socket to specified address. | ||
282 | * @param s socket to use | ||
283 | * @param addr address to connect | ||
284 | * @param length of sturcture pointed by @a addr | ||
285 | * @return zero on success, -1 otherwise. | ||
286 | */ | ||
287 | static int | ||
288 | wr_connect(struct wr_socket *s, | ||
289 | const struct sockaddr *addr, | ||
290 | int length) | ||
291 | { | ||
292 | if (0 != connect (s->fd, addr, length)) | ||
293 | return -1; | ||
294 | if (wr_plain == s->t) | ||
295 | return 0; | ||
296 | #ifdef HTTPS_SUPPORT | ||
297 | if (wr_tls == s->t) | ||
298 | { | ||
299 | /* Do not try handshake here as | ||
300 | * it require processing on MHD side and | ||
301 | * when testing with "external" polling, | ||
302 | * test will call MHD processing only | ||
303 | * after return from wr_connect(). */ | ||
304 | s->tls_connected = 0; | ||
305 | return 0; | ||
306 | } | ||
307 | #endif /* HTTPS_SUPPORT */ | ||
308 | return -1; | ||
309 | } | ||
310 | |||
311 | #ifdef HTTPS_SUPPORT | ||
312 | /* Only to be called from wr_send() and wr_recv() ! */ | ||
313 | static bool | ||
314 | wr_handshake(struct wr_socket *s) | ||
315 | { | ||
316 | int res = gnutls_handshake (s->tls_s); | ||
317 | if (GNUTLS_E_SUCCESS == res) | ||
318 | s->tls_connected = true; | ||
319 | else if (GNUTLS_E_AGAIN == res) | ||
320 | MHD_socket_set_error_ (MHD_SCKT_EAGAIN_); | ||
321 | else | ||
322 | MHD_socket_set_error_ (MHD_SCKT_ECONNABORTED_); /* hard error */ | ||
323 | return s->tls_connected; | ||
324 | } | ||
325 | #endif /* HTTPS_SUPPORT */ | ||
326 | |||
327 | |||
328 | /** | ||
329 | * Send data to remote by socket. | ||
330 | * @param s the socket to use | ||
331 | * @param buf the buffer with data to send | ||
332 | * @param len the length of data in @a buf | ||
333 | * @return number of bytes were sent if succeed, | ||
334 | * -1 if failed. Use #MHD_socket_get_error_() | ||
335 | * to get socket error. | ||
336 | */ | ||
337 | static ssize_t | ||
338 | wr_send (struct wr_socket *s, | ||
339 | const void *buf, | ||
340 | size_t len) | ||
341 | { | ||
342 | if (wr_plain == s->t) | ||
343 | return MHD_send_(s->fd, buf, len); | ||
344 | #ifdef HTTPS_SUPPORT | ||
345 | if (wr_tls == s->t) | ||
346 | { | ||
347 | ssize_t ret; | ||
348 | if (!s->tls_connected && !wr_handshake (s)) | ||
349 | return -1; | ||
350 | |||
351 | ret = gnutls_record_send (s->tls_s, buf, len); | ||
352 | if (ret > 0) | ||
353 | return ret; | ||
354 | if (GNUTLS_E_AGAIN == ret) | ||
355 | MHD_socket_set_error_ (MHD_SCKT_EAGAIN_); | ||
356 | else | ||
357 | MHD_socket_set_error_ (MHD_SCKT_ECONNABORTED_); /* hard error */ | ||
358 | } | ||
359 | #endif /* HTTPS_SUPPORT */ | ||
360 | return -1; | ||
361 | } | ||
362 | |||
363 | |||
364 | /** | ||
365 | * Receive data from remote by socket. | ||
366 | * @param s the socket to use | ||
367 | * @param buf the buffer to store received data | ||
368 | * @param len the length of @a buf | ||
369 | * @return number of bytes were received if succeed, | ||
370 | * -1 if failed. Use #MHD_socket_get_error_() | ||
371 | * to get socket error. | ||
372 | */ | ||
373 | static ssize_t | ||
374 | wr_recv (struct wr_socket *s, | ||
375 | void *buf, | ||
376 | size_t len) | ||
377 | { | ||
378 | if (wr_plain == s->t) | ||
379 | return MHD_recv_ (s->fd, buf, len); | ||
380 | #ifdef HTTPS_SUPPORT | ||
381 | if (wr_tls == s->t) | ||
382 | { | ||
383 | ssize_t ret; | ||
384 | if (!s->tls_connected && !wr_handshake (s)) | ||
385 | return -1; | ||
386 | |||
387 | ret = gnutls_record_recv (s->tls_s, buf, len); | ||
388 | if (ret > 0) | ||
389 | return ret; | ||
390 | if (GNUTLS_E_AGAIN == ret) | ||
391 | MHD_socket_set_error_ (MHD_SCKT_EAGAIN_); | ||
392 | else | ||
393 | MHD_socket_set_error_ (MHD_SCKT_ECONNABORTED_); /* hard error */ | ||
394 | } | ||
395 | #endif /* HTTPS_SUPPORT */ | ||
396 | return -1; | ||
397 | } | ||
398 | |||
399 | |||
400 | /** | ||
401 | * Close socket and release allocated resourced | ||
402 | * @param s the socket to close | ||
403 | * @return zero on succeed, -1 otherwise | ||
404 | */ | ||
405 | static int | ||
406 | wr_close (struct wr_socket *s) | ||
407 | { | ||
408 | int ret = (MHD_socket_close_(s->fd)) ? 0 : -1; | ||
409 | #ifdef HTTPS_SUPPORT | ||
410 | if (wr_tls == s->t) | ||
411 | { | ||
412 | gnutls_deinit (s->tls_s); | ||
413 | gnutls_certificate_free_credentials (s->tls_crd); | ||
414 | } | ||
415 | #endif /* HTTPS_SUPPORT */ | ||
416 | free (s); | ||
417 | return ret; | ||
418 | } | ||
419 | |||
420 | |||
421 | /** | ||
422 | * Thread we use to run the interaction with the upgraded socket. | ||
423 | */ | ||
424 | static pthread_t pt; | ||
425 | |||
426 | /** | ||
427 | * Will be set to the upgraded socket. | ||
428 | */ | ||
429 | static struct wr_socket *usock; | ||
430 | |||
431 | /** | ||
432 | * Thread we use to run the interaction with the upgraded socket. | ||
433 | */ | ||
434 | static pthread_t pt_client; | ||
435 | |||
436 | /** | ||
437 | * Flag set to 1 once the test is finished. | ||
438 | */ | ||
439 | static volatile bool done; | ||
440 | |||
441 | |||
442 | /** | ||
443 | * Callback used by MHD to notify the application about completed | ||
444 | * requests. Frees memory. | ||
445 | * | ||
446 | * @param cls client-defined closure | ||
447 | * @param connection connection handle | ||
448 | * @param con_cls value as set by the last call to | ||
449 | * the #MHD_AccessHandlerCallback | ||
450 | * @param toe reason for request termination | ||
451 | */ | ||
452 | static void | ||
453 | notify_completed_cb (void *cls, | ||
454 | struct MHD_Connection *connection, | ||
455 | void **con_cls, | ||
456 | enum MHD_RequestTerminationCode toe) | ||
457 | { | ||
458 | pthread_t* ppth = *con_cls; | ||
459 | |||
460 | (void) cls; | ||
461 | (void) connection; /* Unused. Silent compiler warning. */ | ||
462 | if ( (toe != MHD_REQUEST_TERMINATED_COMPLETED_OK) && | ||
463 | (toe != MHD_REQUEST_TERMINATED_CLIENT_ABORT) && | ||
464 | (toe != MHD_REQUEST_TERMINATED_DAEMON_SHUTDOWN) ) | ||
465 | abort (); | ||
466 | if (! pthread_equal (**((pthread_t**)con_cls), | ||
467 | pthread_self ())) | ||
468 | abort (); | ||
469 | if (NULL != ppth) | ||
470 | free (*con_cls); | ||
471 | *con_cls = NULL; | ||
472 | } | ||
473 | |||
474 | |||
475 | /** | ||
476 | * Logging callback. | ||
477 | * | ||
478 | * @param cls logging closure (NULL) | ||
479 | * @param uri access URI | ||
480 | * @param connection connection handle | ||
481 | * @return #TEST_PTR | ||
482 | */ | ||
483 | static void * | ||
484 | log_cb (void *cls, | ||
485 | const char *uri, | ||
486 | struct MHD_Connection *connection) | ||
487 | { | ||
488 | pthread_t *ppth; | ||
489 | |||
490 | (void) cls; | ||
491 | (void) connection; /* Unused. Silent compiler warning. */ | ||
492 | if (0 != strcmp (uri, | ||
493 | "/")) | ||
494 | abort (); | ||
495 | ppth = malloc (sizeof (pthread_t)); | ||
496 | if (NULL == ppth) | ||
497 | abort(); | ||
498 | *ppth = pthread_self (); | ||
499 | return (void *) ppth; | ||
500 | } | ||
501 | |||
502 | |||
503 | /** | ||
504 | * Function to check that MHD properly notifies about starting | ||
505 | * and stopping. | ||
506 | * | ||
507 | * @param cls client-defined closure | ||
508 | * @param connection connection handle | ||
509 | * @param socket_context socket-specific pointer where the | ||
510 | * client can associate some state specific | ||
511 | * to the TCP connection; note that this is | ||
512 | * different from the "con_cls" which is per | ||
513 | * HTTP request. The client can initialize | ||
514 | * during #MHD_CONNECTION_NOTIFY_STARTED and | ||
515 | * cleanup during #MHD_CONNECTION_NOTIFY_CLOSED | ||
516 | * and access in the meantime using | ||
517 | * #MHD_CONNECTION_INFO_SOCKET_CONTEXT. | ||
518 | * @param toe reason for connection notification | ||
519 | * @see #MHD_OPTION_NOTIFY_CONNECTION | ||
520 | * @ingroup request | ||
521 | */ | ||
522 | static void | ||
523 | notify_connection_cb (void *cls, | ||
524 | struct MHD_Connection *connection, | ||
525 | void **socket_context, | ||
526 | enum MHD_ConnectionNotificationCode toe) | ||
527 | { | ||
528 | static int started; | ||
529 | |||
530 | (void) cls; | ||
531 | (void) connection; /* Unused. Silent compiler warning. */ | ||
532 | switch (toe) | ||
533 | { | ||
534 | case MHD_CONNECTION_NOTIFY_STARTED: | ||
535 | if (MHD_NO != started) | ||
536 | abort (); | ||
537 | started = MHD_YES; | ||
538 | *socket_context = &started; | ||
539 | break; | ||
540 | case MHD_CONNECTION_NOTIFY_CLOSED: | ||
541 | if (MHD_YES != started) | ||
542 | abort (); | ||
543 | if (&started != *socket_context) | ||
544 | abort (); | ||
545 | *socket_context = NULL; | ||
546 | started = MHD_NO; | ||
547 | break; | ||
548 | } | ||
549 | } | ||
550 | |||
551 | |||
552 | /** | ||
553 | * Change socket to blocking. | ||
554 | * | ||
555 | * @param fd the socket to manipulate | ||
556 | * @return non-zero if succeeded, zero otherwise | ||
557 | */ | ||
558 | static void | ||
559 | make_blocking (MHD_socket fd) | ||
560 | { | ||
561 | #if defined(MHD_POSIX_SOCKETS) | ||
562 | int flags; | ||
563 | |||
564 | flags = fcntl (fd, F_GETFL); | ||
565 | if (-1 == flags) | ||
566 | return; | ||
567 | if ((flags & ~O_NONBLOCK) != flags) | ||
568 | if (-1 == fcntl (fd, F_SETFL, flags & ~O_NONBLOCK)) | ||
569 | abort (); | ||
570 | #elif defined(MHD_WINSOCK_SOCKETS) | ||
571 | unsigned long flags = 1; | ||
572 | |||
573 | ioctlsocket (fd, FIONBIO, &flags); | ||
574 | #endif /* MHD_WINSOCK_SOCKETS */ | ||
575 | } | ||
576 | |||
577 | |||
578 | static void | ||
579 | kick_select () | ||
580 | { | ||
581 | if (-1 != kicker[1]) | ||
582 | { | ||
583 | write (kicker[1], "K", 1); | ||
584 | fprintf (stderr, "KICKING\n"); | ||
585 | } | ||
586 | } | ||
587 | |||
588 | |||
589 | static void | ||
590 | send_all (struct wr_socket *sock, | ||
591 | const char *text) | ||
592 | { | ||
593 | size_t len = strlen (text); | ||
594 | ssize_t ret; | ||
595 | size_t off; | ||
596 | |||
597 | make_blocking (wr_fd (sock)); | ||
598 | for (off = 0; off < len; off += ret) | ||
599 | { | ||
600 | ret = wr_send (sock, | ||
601 | &text[off], | ||
602 | len - off); | ||
603 | kick_select (); | ||
604 | if (0 > ret) | ||
605 | { | ||
606 | if (MHD_SCKT_ERR_IS_EAGAIN_ (MHD_socket_get_error_ ())) | ||
607 | { | ||
608 | ret = 0; | ||
609 | continue; | ||
610 | } | ||
611 | abort (); | ||
612 | } | ||
613 | } | ||
614 | } | ||
615 | |||
616 | |||
617 | /** | ||
618 | * Read character-by-character until we | ||
619 | * get '\r\n\r\n'. | ||
620 | */ | ||
621 | static void | ||
622 | recv_hdr (struct wr_socket *sock) | ||
623 | { | ||
624 | unsigned int i; | ||
625 | char next; | ||
626 | char c; | ||
627 | ssize_t ret; | ||
628 | |||
629 | make_blocking (wr_fd (sock)); | ||
630 | next = '\r'; | ||
631 | i = 0; | ||
632 | while (i < 4) | ||
633 | { | ||
634 | ret = wr_recv (sock, | ||
635 | &c, | ||
636 | 1); | ||
637 | kick_select (); | ||
638 | if (0 > ret) | ||
639 | { | ||
640 | if (MHD_SCKT_ERR_IS_EAGAIN_ (MHD_socket_get_error_ ())) | ||
641 | continue; | ||
642 | abort (); | ||
643 | } | ||
644 | if (0 == ret) | ||
645 | continue; | ||
646 | if (c == next) | ||
647 | { | ||
648 | i++; | ||
649 | if (next == '\r') | ||
650 | next = '\n'; | ||
651 | else | ||
652 | next = '\r'; | ||
653 | continue; | ||
654 | } | ||
655 | if (c == '\r') | ||
656 | { | ||
657 | i = 1; | ||
658 | next = '\n'; | ||
659 | continue; | ||
660 | } | ||
661 | i = 0; | ||
662 | next = '\r'; | ||
663 | } | ||
664 | } | ||
665 | |||
666 | |||
667 | static void | ||
668 | recv_all (struct wr_socket *sock, | ||
669 | const char *text) | ||
670 | { | ||
671 | size_t len = strlen (text); | ||
672 | char buf[len]; | ||
673 | ssize_t ret; | ||
674 | size_t off; | ||
675 | |||
676 | make_blocking (wr_fd (sock)); | ||
677 | for (off = 0; off < len; off += ret) | ||
678 | { | ||
679 | ret = wr_recv (sock, | ||
680 | &buf[off], | ||
681 | len - off); | ||
682 | if (0 > ret) | ||
683 | { | ||
684 | if (MHD_SCKT_ERR_IS_EAGAIN_ (MHD_socket_get_error_ ())) | ||
685 | { | ||
686 | ret = 0; | ||
687 | continue; | ||
688 | } | ||
689 | abort (); | ||
690 | } | ||
691 | } | ||
692 | if (0 != strncmp (text, buf, len)) | ||
693 | abort(); | ||
694 | } | ||
695 | |||
696 | |||
697 | /** | ||
698 | * Main function for the thread that runs the interaction with | ||
699 | * the upgraded socket. | ||
700 | * | ||
701 | * @param cls the handle for the upgrade | ||
702 | */ | ||
703 | static void * | ||
704 | run_usock (void *cls) | ||
705 | { | ||
706 | struct MHD_UpgradeResponseHandle *urh = cls; | ||
707 | |||
708 | send_all (usock, | ||
709 | LARGE_STRING); | ||
710 | recv_all (usock, | ||
711 | LARGE_REPLY_STRING); | ||
712 | send_all (usock, | ||
713 | "Finished"); | ||
714 | MHD_upgrade_action (urh, | ||
715 | MHD_UPGRADE_ACTION_CLOSE); | ||
716 | free (usock); | ||
717 | usock = NULL; | ||
718 | return NULL; | ||
719 | } | ||
720 | |||
721 | |||
722 | /** | ||
723 | * Main function for the thread that runs the client-side of the | ||
724 | * interaction with the upgraded socket. | ||
725 | * | ||
726 | * @param cls the client socket | ||
727 | */ | ||
728 | static void * | ||
729 | run_usock_client (void *cls) | ||
730 | { | ||
731 | struct wr_socket *sock = cls; | ||
732 | |||
733 | send_all (sock, | ||
734 | "GET / HTTP/1.1\r\nConnection: Upgrade\r\n\r\n"); | ||
735 | recv_hdr (sock); | ||
736 | recv_all (sock, | ||
737 | LARGE_STRING); | ||
738 | send_all (sock, | ||
739 | LARGE_REPLY_STRING); | ||
740 | recv_all (sock, | ||
741 | "Finished"); | ||
742 | wr_close (sock); | ||
743 | done = true; | ||
744 | return NULL; | ||
745 | } | ||
746 | |||
747 | |||
748 | /** | ||
749 | * Function called after a protocol "upgrade" response was sent | ||
750 | * successfully and the socket should now be controlled by some | ||
751 | * protocol other than HTTP. | ||
752 | * | ||
753 | * Any data already received on the socket will be made available in | ||
754 | * @e extra_in. This can happen if the application sent extra data | ||
755 | * before MHD send the upgrade response. The application should | ||
756 | * treat data from @a extra_in as if it had read it from the socket. | ||
757 | * | ||
758 | * Note that the application must not close() @a sock directly, | ||
759 | * but instead use #MHD_upgrade_action() for special operations | ||
760 | * on @a sock. | ||
761 | * | ||
762 | * Except when in 'thread-per-connection' mode, implementations | ||
763 | * of this function should never block (as it will still be called | ||
764 | * from within the main event loop). | ||
765 | * | ||
766 | * @param cls closure, whatever was given to #MHD_create_response_for_upgrade(). | ||
767 | * @param connection original HTTP connection handle, | ||
768 | * giving the function a last chance | ||
769 | * to inspect the original HTTP request | ||
770 | * @param con_cls last value left in `con_cls` of the `MHD_AccessHandlerCallback` | ||
771 | * @param extra_in if we happened to have read bytes after the | ||
772 | * HTTP header already (because the client sent | ||
773 | * more than the HTTP header of the request before | ||
774 | * we sent the upgrade response), | ||
775 | * these are the extra bytes already read from @a sock | ||
776 | * by MHD. The application should treat these as if | ||
777 | * it had read them from @a sock. | ||
778 | * @param extra_in_size number of bytes in @a extra_in | ||
779 | * @param sock socket to use for bi-directional communication | ||
780 | * with the client. For HTTPS, this may not be a socket | ||
781 | * that is directly connected to the client and thus certain | ||
782 | * operations (TCP-specific setsockopt(), getsockopt(), etc.) | ||
783 | * may not work as expected (as the socket could be from a | ||
784 | * socketpair() or a TCP-loopback). The application is expected | ||
785 | * to perform read()/recv() and write()/send() calls on the socket. | ||
786 | * The application may also call shutdown(), but must not call | ||
787 | * close() directly. | ||
788 | * @param urh argument for #MHD_upgrade_action()s on this @a connection. | ||
789 | * Applications must eventually use this callback to (indirectly) | ||
790 | * perform the close() action on the @a sock. | ||
791 | */ | ||
792 | static void | ||
793 | upgrade_cb (void *cls, | ||
794 | struct MHD_Connection *connection, | ||
795 | void *con_cls, | ||
796 | const char *extra_in, | ||
797 | size_t extra_in_size, | ||
798 | MHD_socket sock, | ||
799 | struct MHD_UpgradeResponseHandle *urh) | ||
800 | { | ||
801 | (void) cls; | ||
802 | (void) connection; | ||
803 | (void) con_cls; | ||
804 | (void) extra_in; /* Unused. Silent compiler warning. */ | ||
805 | |||
806 | usock = wr_create_from_plain_sckt (sock); | ||
807 | if (0 != extra_in_size) | ||
808 | abort (); | ||
809 | if (0 != pthread_create (&pt, | ||
810 | NULL, | ||
811 | &run_usock, | ||
812 | urh)) | ||
813 | abort (); | ||
814 | } | ||
815 | |||
816 | |||
817 | /** | ||
818 | * A client has requested the given url using the given method | ||
819 | * (#MHD_HTTP_METHOD_GET, #MHD_HTTP_METHOD_PUT, | ||
820 | * #MHD_HTTP_METHOD_DELETE, #MHD_HTTP_METHOD_POST, etc). The callback | ||
821 | * must call MHD callbacks to provide content to give back to the | ||
822 | * client and return an HTTP status code (i.e. #MHD_HTTP_OK, | ||
823 | * #MHD_HTTP_NOT_FOUND, etc.). | ||
824 | * | ||
825 | * @param cls argument given together with the function | ||
826 | * pointer when the handler was registered with MHD | ||
827 | * @param url the requested url | ||
828 | * @param method the HTTP method used (#MHD_HTTP_METHOD_GET, | ||
829 | * #MHD_HTTP_METHOD_PUT, etc.) | ||
830 | * @param version the HTTP version string (i.e. | ||
831 | * #MHD_HTTP_VERSION_1_1) | ||
832 | * @param upload_data the data being uploaded (excluding HEADERS, | ||
833 | * for a POST that fits into memory and that is encoded | ||
834 | * with a supported encoding, the POST data will NOT be | ||
835 | * given in upload_data and is instead available as | ||
836 | * part of #MHD_get_connection_values; very large POST | ||
837 | * data *will* be made available incrementally in | ||
838 | * @a upload_data) | ||
839 | * @param upload_data_size set initially to the size of the | ||
840 | * @a upload_data provided; the method must update this | ||
841 | * value to the number of bytes NOT processed; | ||
842 | * @param con_cls pointer that the callback can set to some | ||
843 | * address and that will be preserved by MHD for future | ||
844 | * calls for this request; since the access handler may | ||
845 | * be called many times (i.e., for a PUT/POST operation | ||
846 | * with plenty of upload data) this allows the application | ||
847 | * to easily associate some request-specific state. | ||
848 | * If necessary, this state can be cleaned up in the | ||
849 | * global #MHD_RequestCompletedCallback (which | ||
850 | * can be set with the #MHD_OPTION_NOTIFY_COMPLETED). | ||
851 | * Initially, `*con_cls` will be NULL. | ||
852 | * @return #MHD_YES if the connection was handled successfully, | ||
853 | * #MHD_NO if the socket must be closed due to a serios | ||
854 | * error while handling the request | ||
855 | */ | ||
856 | static int | ||
857 | ahc_upgrade (void *cls, | ||
858 | struct MHD_Connection *connection, | ||
859 | const char *url, | ||
860 | const char *method, | ||
861 | const char *version, | ||
862 | const char *upload_data, | ||
863 | size_t *upload_data_size, | ||
864 | void **con_cls) | ||
865 | { | ||
866 | struct MHD_Response *resp; | ||
867 | int ret; | ||
868 | (void) cls; | ||
869 | (void) url; | ||
870 | (void) method; /* Unused. Silent compiler warning. */ | ||
871 | (void) version; | ||
872 | (void) upload_data; | ||
873 | (void) upload_data_size; /* Unused. Silent compiler warning. */ | ||
874 | |||
875 | if (NULL == *con_cls) | ||
876 | abort (); | ||
877 | if (! pthread_equal (**((pthread_t**)con_cls), pthread_self ())) | ||
878 | abort (); | ||
879 | resp = MHD_create_response_for_upgrade (&upgrade_cb, | ||
880 | NULL); | ||
881 | MHD_add_response_header (resp, | ||
882 | MHD_HTTP_HEADER_UPGRADE, | ||
883 | "Hello World Protocol"); | ||
884 | ret = MHD_queue_response (connection, | ||
885 | MHD_HTTP_SWITCHING_PROTOCOLS, | ||
886 | resp); | ||
887 | MHD_destroy_response (resp); | ||
888 | return ret; | ||
889 | } | ||
890 | |||
891 | |||
892 | /** | ||
893 | * Run the MHD external event loop using select. | ||
894 | * | ||
895 | * @param daemon daemon to run it for | ||
896 | */ | ||
897 | static void | ||
898 | run_mhd_select_loop (struct MHD_Daemon *daemon) | ||
899 | { | ||
900 | fd_set rs; | ||
901 | fd_set ws; | ||
902 | fd_set es; | ||
903 | MHD_socket max_fd; | ||
904 | MHD_UNSIGNED_LONG_LONG to; | ||
905 | struct timeval tv; | ||
906 | char drain[128]; | ||
907 | |||
908 | if (0 != pipe (kicker)) | ||
909 | abort (); | ||
910 | while (! done) | ||
911 | { | ||
912 | FD_ZERO (&rs); | ||
913 | FD_ZERO (&ws); | ||
914 | FD_ZERO (&es); | ||
915 | max_fd = -1; | ||
916 | to = 1000; | ||
917 | |||
918 | FD_SET (kicker[0], &rs); | ||
919 | if (MHD_YES != | ||
920 | MHD_get_fdset (daemon, | ||
921 | &rs, | ||
922 | &ws, | ||
923 | &es, | ||
924 | &max_fd)) | ||
925 | abort (); | ||
926 | (void) MHD_get_timeout (daemon, | ||
927 | &to); | ||
928 | if (1000 < to) | ||
929 | to = 1000; | ||
930 | tv.tv_sec = to / 1000; | ||
931 | tv.tv_usec = 1000 * (to % 1000); | ||
932 | if (0 > MHD_SYS_select_ (max_fd + 1, | ||
933 | &rs, | ||
934 | &ws, | ||
935 | &es, | ||
936 | &tv)) | ||
937 | abort (); | ||
938 | if (FD_ISSET (kicker[0], &rs)) | ||
939 | (void) read (kicker[0], drain, sizeof (drain)); | ||
940 | MHD_run_from_select (daemon, | ||
941 | &rs, | ||
942 | &ws, | ||
943 | &es); | ||
944 | } | ||
945 | close (kicker[0]); | ||
946 | close (kicker[1]); | ||
947 | kicker[0] = -1; | ||
948 | kicker[1] = -1; | ||
949 | } | ||
950 | |||
951 | #ifdef HAVE_POLL | ||
952 | |||
953 | /** | ||
954 | * Run the MHD external event loop using select. | ||
955 | * | ||
956 | * @param daemon daemon to run it for | ||
957 | */ | ||
958 | static void | ||
959 | run_mhd_poll_loop (struct MHD_Daemon *daemon) | ||
960 | { | ||
961 | (void)daemon; /* Unused. Silent compiler warning. */ | ||
962 | abort (); /* currently not implementable with existing MHD API */ | ||
963 | } | ||
964 | #endif /* HAVE_POLL */ | ||
965 | |||
966 | |||
967 | #ifdef EPOLL_SUPPORT | ||
968 | /** | ||
969 | * Run the MHD external event loop using select. | ||
970 | * | ||
971 | * @param daemon daemon to run it for | ||
972 | */ | ||
973 | static void | ||
974 | run_mhd_epoll_loop (struct MHD_Daemon *daemon) | ||
975 | { | ||
976 | const union MHD_DaemonInfo *di; | ||
977 | MHD_socket ep; | ||
978 | fd_set rs; | ||
979 | MHD_UNSIGNED_LONG_LONG to; | ||
980 | struct timeval tv; | ||
981 | int ret; | ||
982 | char drain[128]; | ||
983 | |||
984 | di = MHD_get_daemon_info (daemon, | ||
985 | MHD_DAEMON_INFO_EPOLL_FD); | ||
986 | ep = di->listen_fd; | ||
987 | if (0 != pipe (kicker)) | ||
988 | abort (); | ||
989 | while (! done) | ||
990 | { | ||
991 | FD_ZERO (&rs); | ||
992 | to = 1000; | ||
993 | FD_SET (kicker[0], &rs); | ||
994 | FD_SET (ep, &rs); | ||
995 | (void) MHD_get_timeout (daemon, | ||
996 | &to); | ||
997 | if (1000 < to) | ||
998 | to = 1000; | ||
999 | tv.tv_sec = to / 1000; | ||
1000 | tv.tv_usec = 1000 * (to % 1000); | ||
1001 | ret = select (ep + 1, | ||
1002 | &rs, | ||
1003 | NULL, | ||
1004 | NULL, | ||
1005 | &tv); | ||
1006 | if ( (-1 == ret) && | ||
1007 | (EAGAIN != errno) && | ||
1008 | (EINTR != errno) ) | ||
1009 | abort (); | ||
1010 | if (FD_ISSET (kicker[0], &rs)) | ||
1011 | (void) read (kicker[0], drain, sizeof (drain)); | ||
1012 | MHD_run (daemon); | ||
1013 | } | ||
1014 | close (kicker[0]); | ||
1015 | close (kicker[1]); | ||
1016 | kicker[0] = -1; | ||
1017 | kicker[1] = -1; | ||
1018 | } | ||
1019 | #endif /* EPOLL_SUPPORT */ | ||
1020 | |||
1021 | /** | ||
1022 | * Run the MHD external event loop using select. | ||
1023 | * | ||
1024 | * @param daemon daemon to run it for | ||
1025 | */ | ||
1026 | static void | ||
1027 | run_mhd_loop (struct MHD_Daemon *daemon, | ||
1028 | int flags) | ||
1029 | { | ||
1030 | if (0 == (flags & (MHD_USE_POLL | MHD_USE_EPOLL))) | ||
1031 | run_mhd_select_loop (daemon); | ||
1032 | #ifdef HAVE_POLL | ||
1033 | else if (0 != (flags & MHD_USE_POLL)) | ||
1034 | run_mhd_poll_loop (daemon); | ||
1035 | #endif /* HAVE_POLL */ | ||
1036 | #if EPOLL_SUPPORT | ||
1037 | else if (0 != (flags & MHD_USE_EPOLL)) | ||
1038 | run_mhd_epoll_loop (daemon); | ||
1039 | #endif | ||
1040 | else | ||
1041 | abort (); | ||
1042 | } | ||
1043 | |||
1044 | |||
1045 | static bool test_tls; | ||
1046 | |||
1047 | /** | ||
1048 | * Test upgrading a connection. | ||
1049 | * | ||
1050 | * @param flags which event loop style should be tested | ||
1051 | * @param pool size of the thread pool, 0 to disable | ||
1052 | */ | ||
1053 | static int | ||
1054 | test_upgrade (int flags, | ||
1055 | unsigned int pool) | ||
1056 | { | ||
1057 | struct MHD_Daemon *d = NULL; | ||
1058 | struct wr_socket *sock; | ||
1059 | struct sockaddr_in sa; | ||
1060 | const union MHD_DaemonInfo *real_flags; | ||
1061 | const union MHD_DaemonInfo *dinfo; | ||
1062 | #if defined(HTTPS_SUPPORT) && defined(HAVE_FORK) && defined(HAVE_WAITPID) | ||
1063 | pid_t pid = -1; | ||
1064 | #endif /* HTTPS_SUPPORT && HAVE_FORK && HAVE_WAITPID */ | ||
1065 | |||
1066 | done = false; | ||
1067 | |||
1068 | if (! test_tls) | ||
1069 | d = MHD_start_daemon (flags | MHD_USE_ERROR_LOG | MHD_ALLOW_UPGRADE, | ||
1070 | MHD_is_feature_supported(MHD_FEATURE_AUTODETECT_BIND_PORT) ? | ||
1071 | 0 : 1090, | ||
1072 | NULL, NULL, | ||
1073 | &ahc_upgrade, NULL, | ||
1074 | MHD_OPTION_CONNECTION_MEMORY_LIMIT, 512, | ||
1075 | MHD_OPTION_URI_LOG_CALLBACK, &log_cb, NULL, | ||
1076 | MHD_OPTION_NOTIFY_COMPLETED, ¬ify_completed_cb, NULL, | ||
1077 | MHD_OPTION_NOTIFY_CONNECTION, ¬ify_connection_cb, NULL, | ||
1078 | MHD_OPTION_THREAD_POOL_SIZE, pool, | ||
1079 | MHD_OPTION_END); | ||
1080 | #ifdef HTTPS_SUPPORT | ||
1081 | else | ||
1082 | d = MHD_start_daemon (flags | MHD_USE_ERROR_LOG | MHD_ALLOW_UPGRADE | MHD_USE_TLS, | ||
1083 | MHD_is_feature_supported(MHD_FEATURE_AUTODETECT_BIND_PORT) ? | ||
1084 | 0 : 1090, | ||
1085 | NULL, NULL, | ||
1086 | &ahc_upgrade, NULL, | ||
1087 | MHD_OPTION_CONNECTION_MEMORY_LIMIT, 512, | ||
1088 | MHD_OPTION_URI_LOG_CALLBACK, &log_cb, NULL, | ||
1089 | MHD_OPTION_NOTIFY_COMPLETED, ¬ify_completed_cb, NULL, | ||
1090 | MHD_OPTION_NOTIFY_CONNECTION, ¬ify_connection_cb, NULL, | ||
1091 | MHD_OPTION_HTTPS_MEM_KEY, srv_signed_key_pem, | ||
1092 | MHD_OPTION_HTTPS_MEM_CERT, srv_signed_cert_pem, | ||
1093 | MHD_OPTION_THREAD_POOL_SIZE, pool, | ||
1094 | MHD_OPTION_END); | ||
1095 | #endif /* HTTPS_SUPPORT */ | ||
1096 | if (NULL == d) | ||
1097 | return 2; | ||
1098 | real_flags = MHD_get_daemon_info (d, | ||
1099 | MHD_DAEMON_INFO_FLAGS); | ||
1100 | if (NULL == real_flags) | ||
1101 | abort (); | ||
1102 | dinfo = MHD_get_daemon_info (d, | ||
1103 | MHD_DAEMON_INFO_BIND_PORT); | ||
1104 | if ( (NULL == dinfo) || | ||
1105 | (0 == dinfo->port) ) | ||
1106 | abort (); | ||
1107 | if (!test_tls || TLS_LIB_GNUTLS == use_tls_tool) | ||
1108 | { | ||
1109 | sock = test_tls ? wr_create_tls_sckt () : wr_create_plain_sckt (); | ||
1110 | if (NULL == sock) | ||
1111 | abort (); | ||
1112 | sa.sin_family = AF_INET; | ||
1113 | sa.sin_port = htons (dinfo->port); | ||
1114 | sa.sin_addr.s_addr = htonl (INADDR_LOOPBACK); | ||
1115 | if (0 != wr_connect (sock, | ||
1116 | (struct sockaddr *) &sa, | ||
1117 | sizeof (sa))) | ||
1118 | abort (); | ||
1119 | } | ||
1120 | else | ||
1121 | { | ||
1122 | #if defined(HTTPS_SUPPORT) && defined(HAVE_FORK) && defined(HAVE_WAITPID) | ||
1123 | MHD_socket tls_fork_sock; | ||
1124 | uint16_t port; | ||
1125 | |||
1126 | /* make address sanitizer happy */ | ||
1127 | memcpy (&port, | ||
1128 | dinfo /* ->port */, | ||
1129 | sizeof (port)); | ||
1130 | if (-1 == (pid = gnutlscli_connect (&tls_fork_sock, | ||
1131 | port))) | ||
1132 | { | ||
1133 | MHD_stop_daemon (d); | ||
1134 | return 4; | ||
1135 | } | ||
1136 | |||
1137 | sock = wr_create_from_plain_sckt (tls_fork_sock); | ||
1138 | if (NULL == sock) | ||
1139 | abort (); | ||
1140 | #else /* !HTTPS_SUPPORT || !HAVE_FORK || !HAVE_WAITPID */ | ||
1141 | abort (); | ||
1142 | #endif /* !HTTPS_SUPPORT || !HAVE_FORK || !HAVE_WAITPID */ | ||
1143 | } | ||
1144 | |||
1145 | if (0 != pthread_create (&pt_client, | ||
1146 | NULL, | ||
1147 | &run_usock_client, | ||
1148 | sock)) | ||
1149 | abort (); | ||
1150 | if (0 == (flags & MHD_USE_INTERNAL_POLLING_THREAD) ) | ||
1151 | { | ||
1152 | enum MHD_FLAG flags; | ||
1153 | |||
1154 | /* make address sanitizer happy */ | ||
1155 | memcpy (&flags, | ||
1156 | real_flags /* ->flags */, | ||
1157 | sizeof (flags)); | ||
1158 | run_mhd_loop (d, flags); | ||
1159 | } | ||
1160 | pthread_join (pt_client, | ||
1161 | NULL); | ||
1162 | pthread_join (pt, | ||
1163 | NULL); | ||
1164 | #if defined(HTTPS_SUPPORT) && defined(HAVE_FORK) && defined(HAVE_WAITPID) | ||
1165 | if (test_tls && TLS_LIB_GNUTLS != use_tls_tool) | ||
1166 | waitpid (pid, NULL, 0); | ||
1167 | #endif /* HTTPS_SUPPORT && HAVE_FORK && HAVE_WAITPID */ | ||
1168 | MHD_stop_daemon (d); | ||
1169 | return 0; | ||
1170 | } | ||
1171 | |||
1172 | |||
1173 | int | ||
1174 | main (int argc, | ||
1175 | char *const *argv) | ||
1176 | { | ||
1177 | int error_count = 0; | ||
1178 | int res; | ||
1179 | |||
1180 | use_tls_tool = TLS_CLI_NO_TOOL; | ||
1181 | test_tls = has_in_name(argv[0], "_tls"); | ||
1182 | |||
1183 | verbose = 1; | ||
1184 | if (has_param(argc, argv, "-q") || | ||
1185 | has_param(argc, argv, "--quiet")) | ||
1186 | verbose = 0; | ||
1187 | |||
1188 | if (test_tls) | ||
1189 | { | ||
1190 | #ifdef HTTPS_SUPPORT | ||
1191 | if (has_param(argc, argv, "--use-gnutls-cli")) | ||
1192 | use_tls_tool = TLS_CLI_GNUTLS; | ||
1193 | else if (has_param(argc, argv, "--use-openssl")) | ||
1194 | use_tls_tool = TLS_CLI_OPENSSL; | ||
1195 | else if (has_param(argc, argv, "--use-gnutls-lib")) | ||
1196 | use_tls_tool = TLS_LIB_GNUTLS; | ||
1197 | #if defined(HAVE_FORK) && defined(HAVE_WAITPID) | ||
1198 | else if (0 == system ("gnutls-cli --version 1> /dev/null 2> /dev/null")) | ||
1199 | use_tls_tool = TLS_CLI_GNUTLS; | ||
1200 | else if (0 == system ("openssl version 1> /dev/null 2> /dev/null")) | ||
1201 | use_tls_tool = TLS_CLI_OPENSSL; | ||
1202 | #endif /* HAVE_FORK && HAVE_WAITPID */ | ||
1203 | else | ||
1204 | use_tls_tool = TLS_LIB_GNUTLS; /* Should be available as MHD use it. */ | ||
1205 | if (verbose) | ||
1206 | { | ||
1207 | switch (use_tls_tool) | ||
1208 | { | ||
1209 | case TLS_CLI_GNUTLS: | ||
1210 | printf ("GnuTLS-CLI will be used for testing.\n"); | ||
1211 | break; | ||
1212 | case TLS_CLI_OPENSSL: | ||
1213 | printf ("Command line version of OpenSSL will be used for testing.\n"); | ||
1214 | break; | ||
1215 | case TLS_LIB_GNUTLS: | ||
1216 | printf ("GnuTLS library will be used for testing.\n"); | ||
1217 | break; | ||
1218 | default: | ||
1219 | abort (); | ||
1220 | } | ||
1221 | } | ||
1222 | if ( (TLS_LIB_GNUTLS == use_tls_tool) && | ||
1223 | (GNUTLS_E_SUCCESS != gnutls_global_init()) ) | ||
1224 | abort (); | ||
1225 | |||
1226 | #else /* ! HTTPS_SUPPORT */ | ||
1227 | fprintf (stderr, "HTTPS support was disabled by configure.\n"); | ||
1228 | return 77; | ||
1229 | #endif /* ! HTTPS_SUPPORT */ | ||
1230 | } | ||
1231 | |||
1232 | /* run tests */ | ||
1233 | if (verbose) | ||
1234 | printf ("Starting HTTP \"Upgrade\" tests with %s connections.\n", | ||
1235 | test_tls ? "TLS" : "plain"); | ||
1236 | /* try external select */ | ||
1237 | res = test_upgrade (0, | ||
1238 | 0); | ||
1239 | error_count += res; | ||
1240 | if (res) | ||
1241 | fprintf (stderr, | ||
1242 | "FAILED: Upgrade with external select, return code %d.\n", | ||
1243 | res); | ||
1244 | else if (verbose) | ||
1245 | printf ("PASSED: Upgrade with external select.\n"); | ||
1246 | |||
1247 | /* Try external auto */ | ||
1248 | res = test_upgrade (MHD_USE_AUTO, | ||
1249 | 0); | ||
1250 | error_count += res; | ||
1251 | if (res) | ||
1252 | fprintf (stderr, | ||
1253 | "FAILED: Upgrade with external 'auto', return code %d.\n", | ||
1254 | res); | ||
1255 | else if (verbose) | ||
1256 | printf ("PASSED: Upgrade with external 'auto'.\n"); | ||
1257 | |||
1258 | #ifdef EPOLL_SUPPORT | ||
1259 | res = test_upgrade (MHD_USE_EPOLL, | ||
1260 | 0); | ||
1261 | error_count += res; | ||
1262 | if (res) | ||
1263 | fprintf (stderr, | ||
1264 | "FAILED: Upgrade with external select with EPOLL, return code %d.\n", | ||
1265 | res); | ||
1266 | else if (verbose) | ||
1267 | printf ("PASSED: Upgrade with external select with EPOLL.\n"); | ||
1268 | #endif | ||
1269 | |||
1270 | /* Test thread-per-connection */ | ||
1271 | res = test_upgrade (MHD_USE_INTERNAL_POLLING_THREAD | MHD_USE_THREAD_PER_CONNECTION, | ||
1272 | 0); | ||
1273 | error_count += res; | ||
1274 | if (res) | ||
1275 | fprintf (stderr, | ||
1276 | "FAILED: Upgrade with thread per connection, return code %d.\n", | ||
1277 | res); | ||
1278 | else if (verbose) | ||
1279 | printf ("PASSED: Upgrade with thread per connection.\n"); | ||
1280 | |||
1281 | res = test_upgrade (MHD_USE_AUTO | MHD_USE_INTERNAL_POLLING_THREAD | MHD_USE_THREAD_PER_CONNECTION, | ||
1282 | 0); | ||
1283 | error_count += res; | ||
1284 | if (res) | ||
1285 | fprintf (stderr, | ||
1286 | "FAILED: Upgrade with thread per connection and 'auto', return code %d.\n", | ||
1287 | res); | ||
1288 | else if (verbose) | ||
1289 | printf ("PASSED: Upgrade with thread per connection and 'auto'.\n"); | ||
1290 | #ifdef HAVE_POLL | ||
1291 | res = test_upgrade (MHD_USE_INTERNAL_POLLING_THREAD | MHD_USE_THREAD_PER_CONNECTION | MHD_USE_POLL, | ||
1292 | 0); | ||
1293 | error_count += res; | ||
1294 | if (res) | ||
1295 | fprintf (stderr, | ||
1296 | "FAILED: Upgrade with thread per connection and poll, return code %d.\n", | ||
1297 | res); | ||
1298 | else if (verbose) | ||
1299 | printf ("PASSED: Upgrade with thread per connection and poll.\n"); | ||
1300 | #endif /* HAVE_POLL */ | ||
1301 | |||
1302 | /* Test different event loops, with and without thread pool */ | ||
1303 | res = test_upgrade (MHD_USE_INTERNAL_POLLING_THREAD, | ||
1304 | 0); | ||
1305 | error_count += res; | ||
1306 | if (res) | ||
1307 | fprintf (stderr, | ||
1308 | "FAILED: Upgrade with internal select, return code %d.\n", | ||
1309 | res); | ||
1310 | else if (verbose) | ||
1311 | printf ("PASSED: Upgrade with internal select.\n"); | ||
1312 | res = test_upgrade (MHD_USE_INTERNAL_POLLING_THREAD, | ||
1313 | 2); | ||
1314 | error_count += res; | ||
1315 | if (res) | ||
1316 | fprintf (stderr, | ||
1317 | "FAILED: Upgrade with internal select with thread pool, return code %d.\n", | ||
1318 | res); | ||
1319 | else if (verbose) | ||
1320 | printf ("PASSED: Upgrade with internal select with thread pool.\n"); | ||
1321 | res = test_upgrade (MHD_USE_AUTO | MHD_USE_INTERNAL_POLLING_THREAD, | ||
1322 | 0); | ||
1323 | error_count += res; | ||
1324 | if (res) | ||
1325 | fprintf (stderr, | ||
1326 | "FAILED: Upgrade with internal 'auto' return code %d.\n", | ||
1327 | res); | ||
1328 | else if (verbose) | ||
1329 | printf ("PASSED: Upgrade with internal 'auto'.\n"); | ||
1330 | res = test_upgrade (MHD_USE_AUTO | MHD_USE_INTERNAL_POLLING_THREAD, | ||
1331 | 2); | ||
1332 | error_count += res; | ||
1333 | if (res) | ||
1334 | fprintf (stderr, | ||
1335 | "FAILED: Upgrade with internal 'auto' with thread pool, return code %d.\n", | ||
1336 | res); | ||
1337 | else if (verbose) | ||
1338 | printf ("PASSED: Upgrade with internal 'auto' with thread pool.\n"); | ||
1339 | #ifdef HAVE_POLL | ||
1340 | res = test_upgrade (MHD_USE_POLL_INTERNAL_THREAD, | ||
1341 | 0); | ||
1342 | error_count += res; | ||
1343 | if (res) | ||
1344 | fprintf (stderr, | ||
1345 | "FAILED: Upgrade with internal poll, return code %d.\n", | ||
1346 | res); | ||
1347 | else if (verbose) | ||
1348 | printf ("PASSED: Upgrade with internal poll.\n"); | ||
1349 | res = test_upgrade (MHD_USE_POLL_INTERNAL_THREAD, | ||
1350 | 2); | ||
1351 | if (res) | ||
1352 | fprintf (stderr, | ||
1353 | "FAILED: Upgrade with internal poll with thread pool, return code %d.\n", | ||
1354 | res); | ||
1355 | else if (verbose) | ||
1356 | printf ("PASSED: Upgrade with internal poll with thread pool.\n"); | ||
1357 | #endif | ||
1358 | #ifdef EPOLL_SUPPORT | ||
1359 | res = test_upgrade (MHD_USE_EPOLL_INTERNAL_THREAD, | ||
1360 | 0); | ||
1361 | if (res) | ||
1362 | fprintf (stderr, | ||
1363 | "FAILED: Upgrade with internal epoll, return code %d.\n", | ||
1364 | res); | ||
1365 | else if (verbose) | ||
1366 | printf ("PASSED: Upgrade with internal epoll.\n"); | ||
1367 | res = test_upgrade (MHD_USE_EPOLL_INTERNAL_THREAD, | ||
1368 | 2); | ||
1369 | if (res) | ||
1370 | fprintf (stderr, | ||
1371 | "FAILED: Upgrade with internal epoll, return code %d.\n", | ||
1372 | res); | ||
1373 | else if (verbose) | ||
1374 | printf ("PASSED: Upgrade with internal epoll.\n"); | ||
1375 | #endif | ||
1376 | /* report result */ | ||
1377 | if (0 != error_count) | ||
1378 | fprintf (stderr, | ||
1379 | "Error (code: %u)\n", | ||
1380 | error_count); | ||
1381 | #ifdef HTTPS_SUPPORT | ||
1382 | if (test_tls && (TLS_LIB_GNUTLS == use_tls_tool)) | ||
1383 | gnutls_global_deinit(); | ||
1384 | #endif /* HTTPS_SUPPORT */ | ||
1385 | return error_count != 0; /* 0 == pass */ | ||
1386 | } | ||