aboutsummaryrefslogtreecommitdiff
path: root/src/microhttpd/test_upgrade_large.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/microhttpd/test_upgrade_large.c')
-rw-r--r--src/microhttpd/test_upgrade_large.c1386
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
68static int verbose = 0;
69
70static int kicker[2] = {-1, -1} ;
71
72enum tls_tool
73{
74 TLS_CLI_NO_TOOL = 0,
75 TLS_CLI_GNUTLS,
76 TLS_CLI_OPENSSL,
77 TLS_LIB_GNUTLS
78};
79
80enum 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 */
91static pid_t
92gnutlscli_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 */
156struct 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 */
202static struct wr_socket *
203wr_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 */
221static struct wr_socket *
222wr_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 */
267static struct wr_socket *
268wr_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 */
287static int
288wr_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() ! */
313static bool
314wr_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 */
337static ssize_t
338wr_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 */
373static ssize_t
374wr_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 */
405static int
406wr_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 */
424static pthread_t pt;
425
426/**
427 * Will be set to the upgraded socket.
428 */
429static struct wr_socket *usock;
430
431/**
432 * Thread we use to run the interaction with the upgraded socket.
433 */
434static pthread_t pt_client;
435
436/**
437 * Flag set to 1 once the test is finished.
438 */
439static 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 */
452static void
453notify_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 */
483static void *
484log_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 */
522static void
523notify_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 */
558static void
559make_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
578static void
579kick_select ()
580{
581 if (-1 != kicker[1])
582 {
583 write (kicker[1], "K", 1);
584 fprintf (stderr, "KICKING\n");
585 }
586}
587
588
589static void
590send_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 */
621static void
622recv_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
667static void
668recv_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 */
703static void *
704run_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 */
728static void *
729run_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 */
792static void
793upgrade_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 */
856static int
857ahc_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 */
897static void
898run_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 */
958static void
959run_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 */
973static void
974run_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 */
1026static void
1027run_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
1045static 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 */
1053static int
1054test_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, &notify_completed_cb, NULL,
1077 MHD_OPTION_NOTIFY_CONNECTION, &notify_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, &notify_completed_cb, NULL,
1090 MHD_OPTION_NOTIFY_CONNECTION, &notify_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
1173int
1174main (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}