aboutsummaryrefslogtreecommitdiff
path: root/src/microhttpd/test_upgrade.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/microhttpd/test_upgrade.c')
-rw-r--r--src/microhttpd/test_upgrade.c1062
1 files changed, 1044 insertions, 18 deletions
diff --git a/src/microhttpd/test_upgrade.c b/src/microhttpd/test_upgrade.c
index 72583451..e9836461 100644
--- a/src/microhttpd/test_upgrade.c
+++ b/src/microhttpd/test_upgrade.c
@@ -22,30 +22,961 @@
22 * @file test_upgrade.c 22 * @file test_upgrade.c
23 * @brief Testcase for libmicrohttpd upgrading a connection 23 * @brief Testcase for libmicrohttpd upgrading a connection
24 * @author Christian Grothoff 24 * @author Christian Grothoff
25 * @author Karlson2k (Evgeny Grin)
25 */ 26 */
26 27
27#include "platform.h"
28#include "microhttpd.h"
29#include <stdlib.h> 28#include <stdlib.h>
30#include <string.h> 29#include <string.h>
31#include <stdio.h> 30#include <stdio.h>
32#include <pthread.h> 31#include <pthread.h>
33 32#include <stdlib.h>
33#include <stddef.h>
34#ifndef WINDOWS 34#ifndef WINDOWS
35#include <unistd.h> 35#include <unistd.h>
36#endif 36#endif
37#include "mhd_options.h"
38#ifdef HAVE_STDBOOL_H
39#include <stdbool.h>
40#endif /* HAVE_STDBOOL_H */
37 41
38#include "mhd_sockets.h" 42#include "mhd_sockets.h"
39#ifdef HAVE_NETINET_IP_H 43#ifdef HAVE_NETINET_IP_H
40#include <netinet/ip.h> 44#include <netinet/ip.h>
41#endif /* HAVE_NETINET_IP_H */ 45#endif /* HAVE_NETINET_IP_H */
42 46
43static int verbose = 0; 47#include "platform.h"
48#include "microhttpd.h"
49
44#include "test_helpers.h" 50#include "test_helpers.h"
45#include "test_upgrade_common.c" 51
52#ifdef HTTPS_SUPPORT
53#include <gnutls/gnutls.h>
54#include "../testcurl/https/tls_test_keys.h"
55
56#if defined(HAVE_FORK) && defined(HAVE_WAITPID)
57#include <sys/types.h>
58#include <sys/wait.h>
59#endif /* HAVE_FORK && HAVE_WAITPID */
60#endif /* HTTPS_SUPPORT */
61
62static int verbose = 0;
63
64enum tls_tool
65{
66 TLS_CLI_NO_TOOL = 0,
67 TLS_CLI_GNUTLS,
68 TLS_CLI_OPENSSL,
69 TLS_LIB_GNUTLS
70};
71
72enum tls_tool use_tls_tool;
73
74#if defined(HTTPS_SUPPORT) && defined(HAVE_FORK) && defined(HAVE_WAITPID)
75/**
76 * Fork child that connects via GnuTLS-CLI to our @a port. Allows us to
77 * talk to our port over a socket in @a sp without having to worry
78 * about TLS.
79 *
80 * @param location where the socket is returned
81 * @return -1 on error, otherwise PID of TLS child process
82 */
83static pid_t
84gnutlscli_connect (int *sock,
85 uint16_t port)
86{
87 pid_t chld;
88 int sp[2];
89 char destination[30];
90
91 if (0 != socketpair (AF_UNIX,
92 SOCK_STREAM,
93 0,
94 sp))
95 return -1;
96 chld = fork ();
97 if (0 != chld)
98 {
99 *sock = sp[1];
100 MHD_socket_close_chk_ (sp[0]);
101 return chld;
102 }
103 MHD_socket_close_chk_ (sp[1]);
104 (void) close (0);
105 (void) close (1);
106 dup2 (sp[0], 0);
107 dup2 (sp[0], 1);
108 MHD_socket_close_chk_ (sp[0]);
109 if (TLS_CLI_GNUTLS == use_tls_tool)
110 {
111 snprintf (destination,
112 sizeof(destination),
113 "%u",
114 (unsigned int) port);
115 execlp ("gnutls-cli",
116 "gnutls-cli",
117 "--insecure",
118 "-p",
119 destination,
120 "localhost",
121 (char *) NULL);
122 }
123 else if (TLS_CLI_OPENSSL == use_tls_tool)
124 {
125 snprintf (destination,
126 sizeof(destination),
127 "localhost:%u",
128 (unsigned int) port);
129 execlp ("openssl",
130 "openssl",
131 "s_client",
132 "-connect",
133 destination,
134 "-verify",
135 "0",
136 (char *) NULL);
137 }
138 _exit (1);
139}
140#endif /* HTTPS_SUPPORT && HAVE_FORK && HAVE_WAITPID */
141
142
143/**
144 * Wrapper structure for plain&TLS sockets
145 */
146struct wr_socket_strc
147{
148 /**
149 * Real network socket
150 */
151 MHD_socket fd;
152
153 /**
154 * Type of this socket
155 */
156 enum wr_type
157 {
158 wr_invalid = 0,
159 wr_plain = 1,
160 wr_tls = 2
161 } t;
162#ifdef HTTPS_SUPPORT
163 /**
164 * TLS credentials
165 */
166 gnutls_certificate_credentials_t tls_crd;
167
168 /**
169 * TLS session.
170 */
171 gnutls_session_t tls_s;
172
173 /**
174 * TLS handshake already succeed?
175 */
176 bool tls_connected;
177#endif
178};
179
180
181/**
182 * Pseudo type for plain&TLS sockets
183 */
184typedef struct wr_socket_strc* wr_socket;
185
186
187/**
188 * Invalid value of wr_socket
189 */
190#define WR_BAD (NULL)
191
192
193/**
194 * Get underlying real socket.
195 * @return FD of real socket
196 */
197#define wr_fd(s) ((s)->fd)
198
199
200/**
201 * Create wr_socket with plain TCP underlying socket
202 * @return created socket on success, WR_BAD otherwise
203 */
204static wr_socket wr_create_plain_sckt(void)
205{
206 wr_socket s = (wr_socket)malloc(sizeof(struct wr_socket_strc));
207 if (WR_BAD == s)
208 return WR_BAD;
209 s->t = wr_plain;
210 s->fd = socket (AF_INET, SOCK_STREAM, IPPROTO_TCP);
211 if (MHD_INVALID_SOCKET != s->fd)
212 return s;
213 free(s);
214 return WR_BAD;
215}
46 216
47 217
48/** 218/**
219 * Create wr_socket with TLS TCP underlying socket
220 * @return created socket on success, WR_BAD otherwise
221 */
222static wr_socket wr_create_tls_sckt(void)
223{
224#ifdef HTTPS_SUPPORT
225 wr_socket s = (wr_socket)malloc(sizeof(struct wr_socket_strc));
226 if (WR_BAD == s)
227 return WR_BAD;
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 gnutls_transport_set_int (s->tls_s, (int )(s->fd));
242 return s;
243 }
244 gnutls_certificate_free_credentials (s->tls_crd);
245 }
246 }
247 gnutls_deinit (s->tls_s);
248 }
249 (void)MHD_socket_close_ (s->fd);
250 }
251 free(s);
252#endif /* HTTPS_SUPPORT */
253 return WR_BAD;
254}
255
256
257/**
258 * Create wr_socket with plain TCP underlying socket
259 * from already created TCP socket.
260 * @param plain_sk real TCP socket
261 * @return created socket on success, WR_BAD otherwise
262 */
263static wr_socket wr_create_from_plain_sckt(MHD_socket plain_sk)
264{
265 wr_socket s = (wr_socket)malloc(sizeof(struct wr_socket_strc));
266 if (WR_BAD == s)
267 return WR_BAD;
268 s->t = wr_plain;
269 s->fd = plain_sk;
270 return s;
271}
272
273
274/**
275 * Connect socket to specified address.
276 * @param s socket to use
277 * @param addr address to connect
278 * @param length of sturcture pointed by @a addr
279 * @return zero on success, -1 otherwise.
280 */
281static int wr_connect(wr_socket s, struct sockaddr * addr, int length)
282{
283 if (0 != connect(s->fd, addr, length))
284 return -1;
285 if (wr_plain == s->t)
286 return 0;
287#ifdef HTTPS_SUPPORT
288 if (wr_tls == s->t)
289 {
290 s->tls_connected = 0;
291 return 0;
292 /* Do not try handshake here as
293 * it require processing on MHD side and
294 * when testing with "external" polling,
295 * test will call MHD processing only
296 * after return from wr_connect(). */
297 /*
298 int res = gnutls_handshake (s->tls_s);
299 if (GNUTLS_E_SUCCESS == res)
300 {
301 s->tls_connected = !0;
302 return 0;
303 }
304 if (GNUTLS_E_AGAIN == res)
305 return 0;
306 */
307 }
308#endif /* HTTPS_SUPPORT */
309 return -1;
310}
311
312#ifdef HTTPS_SUPPORT
313/* Only to be called from wr_send() and wr_recv() ! */
314static bool wr_handshake(wr_socket s)
315{
316 int res = gnutls_handshake (s->tls_s);
317 if (GNUTLS_E_SUCCESS == res)
318 s->tls_connected = !0;
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 wr_send(wr_socket s, const void *buf, size_t len)
338{
339 if (wr_plain == s->t)
340 return MHD_send_(s->fd, buf, len);
341#ifdef HTTPS_SUPPORT
342 if (wr_tls == s->t)
343 {
344 ssize_t ret;
345 if (!s->tls_connected && !wr_handshake (s))
346 return -1;
347
348 ret = gnutls_record_send (s->tls_s, buf, len);
349 if (ret > 0)
350 return ret;
351 if (GNUTLS_E_AGAIN == ret)
352 MHD_socket_set_error_ (MHD_SCKT_EAGAIN_);
353 else
354 MHD_socket_set_error_ (MHD_SCKT_ECONNABORTED_); /* hard error */
355 }
356#endif /* HTTPS_SUPPORT */
357 return -1;
358}
359
360
361/**
362 * Receive data from remote by socket.
363 * @param s the socket to use
364 * @param buf the buffer to store received data
365 * @param len the length of @a buf
366 * @return number of bytes were received if succeed,
367 * -1 if failed. Use #MHD_socket_get_error_()
368 * to get socket error.
369 */
370static ssize_t wr_recv(wr_socket s, void *buf, size_t len)
371{
372 if (wr_plain == s->t)
373 return MHD_recv_ (s->fd, buf, len);
374#ifdef HTTPS_SUPPORT
375 if (wr_tls == s->t)
376 {
377 ssize_t ret;
378 if (!s->tls_connected && !wr_handshake (s))
379 return -1;
380
381 ret = gnutls_record_recv (s->tls_s, buf, len);
382 if (ret > 0)
383 return ret;
384 if (GNUTLS_E_AGAIN == ret)
385 MHD_socket_set_error_ (MHD_SCKT_EAGAIN_);
386 else
387 MHD_socket_set_error_ (MHD_SCKT_ECONNABORTED_); /* hard error */
388 }
389#endif /* HTTPS_SUPPORT */
390 return -1;
391}
392
393
394/**
395 * Perform shutdown of TCP socket for plain sockets or
396 * shutdown of TLS layer for TLS sockets.
397 * @param s the socket to shutdown
398 * @param how SHUT_WR or SHUT_RDWR
399 * @return zero on succeed, -1 otherwise
400 */
401static int wr_shutdown(wr_socket s, int how)
402{
403 if (wr_plain == s->t)
404 return shutdown (s->fd, how);
405#ifdef HTTPS_SUPPORT
406 if (wr_tls == s->t)
407 {
408 ssize_t ret;
409 if (SHUT_WR == how)
410 ret = gnutls_bye (s->tls_s, GNUTLS_SHUT_WR);
411 else
412 ret = gnutls_bye (s->tls_s, GNUTLS_SHUT_RDWR);
413
414 if (GNUTLS_E_SUCCESS == ret)
415 return 0;
416 }
417#endif /* HTTPS_SUPPORT */
418 return -1;
419}
420
421
422/**
423 * Close socket and release allocated resourced
424 * @param s the socket to close
425 * @return zero on succeed, -1 otherwise
426 */
427static int wr_close(wr_socket s)
428{
429 int ret = (MHD_socket_close_(s->fd)) ? 0 : -1;
430#ifdef HTTPS_SUPPORT
431 if (wr_tls == s->t)
432 {
433 gnutls_deinit (s->tls_s);
434 gnutls_certificate_free_credentials (s->tls_crd);
435 }
436#endif /* HTTPS_SUPPORT */
437 free(s);
438 return ret;
439}
440
441
442/**
443 * Thread we use to run the interaction with the upgraded socket.
444 */
445static pthread_t pt;
446
447/**
448 * Will be set to the upgraded socket.
449 */
450static wr_socket usock;
451
452/**
453 * Thread we use to run the interaction with the upgraded socket.
454 */
455static pthread_t pt_client;
456
457/**
458 * Flag set to 1 once the test is finished.
459 */
460static int done;
461
462
463static void
464notify_completed_cb (void *cls,
465 struct MHD_Connection *connection,
466 void **con_cls,
467 enum MHD_RequestTerminationCode toe)
468{
469 if ( (toe != MHD_REQUEST_TERMINATED_COMPLETED_OK) &&
470 (toe != MHD_REQUEST_TERMINATED_CLIENT_ABORT) &&
471 (toe != MHD_REQUEST_TERMINATED_DAEMON_SHUTDOWN) )
472 abort ();
473 if (((long) *con_cls) != (long) pthread_self ())
474 abort ();
475 *con_cls = NULL;
476}
477
478
479/**
480 * Logging callback.
481 *
482 * @param cls logging closure (NULL)
483 * @param uri access URI
484 * @param connection connection handle
485 * @return #TEST_PTR
486 */
487static void *
488log_cb (void *cls,
489 const char *uri,
490 struct MHD_Connection *connection)
491{
492 if (0 != strcmp (uri,
493 "/"))
494 abort ();
495 return (void *) (long) pthread_self ();
496}
497
498
499/**
500 * Function to check that MHD properly notifies about starting
501 * and stopping.
502 *
503 * @param cls client-defined closure
504 * @param connection connection handle
505 * @param socket_context socket-specific pointer where the
506 * client can associate some state specific
507 * to the TCP connection; note that this is
508 * different from the "con_cls" which is per
509 * HTTP request. The client can initialize
510 * during #MHD_CONNECTION_NOTIFY_STARTED and
511 * cleanup during #MHD_CONNECTION_NOTIFY_CLOSED
512 * and access in the meantime using
513 * #MHD_CONNECTION_INFO_SOCKET_CONTEXT.
514 * @param toe reason for connection notification
515 * @see #MHD_OPTION_NOTIFY_CONNECTION
516 * @ingroup request
517 */
518static void
519notify_connection_cb (void *cls,
520 struct MHD_Connection *connection,
521 void **socket_context,
522 enum MHD_ConnectionNotificationCode toe)
523{
524 static int started;
525
526 switch (toe)
527 {
528 case MHD_CONNECTION_NOTIFY_STARTED:
529 if (MHD_NO != started)
530 abort ();
531 started = MHD_YES;
532 *socket_context = &started;
533 break;
534 case MHD_CONNECTION_NOTIFY_CLOSED:
535 if (MHD_YES != started)
536 abort ();
537 if (&started != *socket_context)
538 abort ();
539 *socket_context = NULL;
540 started = MHD_NO;
541 break;
542 }
543}
544
545
546/**
547 * Change socket to blocking.
548 *
549 * @param fd the socket to manipulate
550 * @return non-zero if succeeded, zero otherwise
551 */
552static void
553make_blocking (MHD_socket fd)
554{
555#if defined(MHD_POSIX_SOCKETS)
556 int flags;
557
558 flags = fcntl (fd, F_GETFL);
559 if (-1 == flags)
560 return;
561 if ((flags & ~O_NONBLOCK) != flags)
562 if (-1 == fcntl (fd, F_SETFL, flags & ~O_NONBLOCK))
563 abort ();
564#elif defined(MHD_WINSOCK_SOCKETS)
565 unsigned long flags = 1;
566
567 ioctlsocket (fd, FIONBIO, &flags);
568#endif /* MHD_WINSOCK_SOCKETS */
569
570}
571
572
573static void
574send_all (wr_socket sock,
575 const char *text)
576{
577 size_t len = strlen (text);
578 ssize_t ret;
579
580 make_blocking (wr_fd (sock));
581 for (size_t off = 0; off < len; off += ret)
582 {
583 ret = wr_send (sock,
584 &text[off],
585 len - off);
586 if (0 > ret)
587 {
588 if (MHD_SCKT_ERR_IS_EAGAIN_ (MHD_socket_get_error_ ()))
589 {
590 ret = 0;
591 continue;
592 }
593 abort ();
594 }
595 }
596}
597
598
599/**
600 * Read character-by-character until we
601 * get '\r\n\r\n'.
602 */
603static void
604recv_hdr (wr_socket sock)
605{
606 unsigned int i;
607 char next;
608 char c;
609 ssize_t ret;
610
611 make_blocking (wr_fd (sock));
612 next = '\r';
613 i = 0;
614 while (i < 4)
615 {
616 ret = wr_recv (sock,
617 &c,
618 1);
619 if (0 > ret)
620 {
621 if (MHD_SCKT_ERR_IS_EAGAIN_ (MHD_socket_get_error_ ()))
622 continue;
623 abort ();
624 }
625 if (0 == ret)
626 continue;
627 if (c == next)
628 {
629 i++;
630 if (next == '\r')
631 next = '\n';
632 else
633 next = '\r';
634 continue;
635 }
636 if (c == '\r')
637 {
638 i = 1;
639 next = '\n';
640 continue;
641 }
642 i = 0;
643 next = '\r';
644 }
645}
646
647
648static void
649recv_all (wr_socket sock,
650 const char *text)
651{
652 size_t len = strlen (text);
653 char buf[len];
654 ssize_t ret;
655
656 make_blocking (wr_fd (sock));
657 for (size_t off = 0; off < len; off += ret)
658 {
659 ret = wr_recv (sock,
660 &buf[off],
661 len - off);
662 if (0 > ret)
663 {
664 if (MHD_SCKT_ERR_IS_EAGAIN_ (MHD_socket_get_error_ ()))
665 {
666 ret = 0;
667 continue;
668 }
669 abort ();
670 }
671 }
672 if (0 != strncmp (text, buf, len))
673 abort();
674}
675
676
677/**
678 * Main function for the thread that runs the interaction with
679 * the upgraded socket.
680 *
681 * @param cls the handle for the upgrade
682 */
683static void *
684run_usock (void *cls)
685{
686 struct MHD_UpgradeResponseHandle *urh = cls;
687
688 send_all (usock,
689 "Hello");
690 recv_all (usock,
691 "World");
692 send_all (usock,
693 "Finished");
694 MHD_upgrade_action (urh,
695 MHD_UPGRADE_ACTION_CLOSE);
696 return NULL;
697}
698
699
700/**
701 * Main function for the thread that runs the client-side of the
702 * interaction with the upgraded socket.
703 *
704 * @param cls the client socket
705 */
706static void *
707run_usock_client (void *cls)
708{
709 wr_socket *sock = cls;
710
711 send_all (*sock,
712 "GET / HTTP/1.1\r\nConnection: Upgrade\r\n\r\n");
713 recv_hdr (*sock);
714 recv_all (*sock,
715 "Hello");
716 send_all (*sock,
717 "World");
718 recv_all (*sock,
719 "Finished");
720 wr_close (*sock);
721 done = 1;
722 return NULL;
723}
724
725
726/**
727 * Function called after a protocol "upgrade" response was sent
728 * successfully and the socket should now be controlled by some
729 * protocol other than HTTP.
730 *
731 * Any data already received on the socket will be made available in
732 * @e extra_in. This can happen if the application sent extra data
733 * before MHD send the upgrade response. The application should
734 * treat data from @a extra_in as if it had read it from the socket.
735 *
736 * Note that the application must not close() @a sock directly,
737 * but instead use #MHD_upgrade_action() for special operations
738 * on @a sock.
739 *
740 * Except when in 'thread-per-connection' mode, implementations
741 * of this function should never block (as it will still be called
742 * from within the main event loop).
743 *
744 * @param cls closure, whatever was given to #MHD_create_response_for_upgrade().
745 * @param connection original HTTP connection handle,
746 * giving the function a last chance
747 * to inspect the original HTTP request
748 * @param con_cls last value left in `con_cls` of the `MHD_AccessHandlerCallback`
749 * @param extra_in if we happened to have read bytes after the
750 * HTTP header already (because the client sent
751 * more than the HTTP header of the request before
752 * we sent the upgrade response),
753 * these are the extra bytes already read from @a sock
754 * by MHD. The application should treat these as if
755 * it had read them from @a sock.
756 * @param extra_in_size number of bytes in @a extra_in
757 * @param sock socket to use for bi-directional communication
758 * with the client. For HTTPS, this may not be a socket
759 * that is directly connected to the client and thus certain
760 * operations (TCP-specific setsockopt(), getsockopt(), etc.)
761 * may not work as expected (as the socket could be from a
762 * socketpair() or a TCP-loopback). The application is expected
763 * to perform read()/recv() and write()/send() calls on the socket.
764 * The application may also call shutdown(), but must not call
765 * close() directly.
766 * @param urh argument for #MHD_upgrade_action()s on this @a connection.
767 * Applications must eventually use this callback to (indirectly)
768 * perform the close() action on the @a sock.
769 */
770static void
771upgrade_cb (void *cls,
772 struct MHD_Connection *connection,
773 void *con_cls,
774 const char *extra_in,
775 size_t extra_in_size,
776 MHD_socket sock,
777 struct MHD_UpgradeResponseHandle *urh)
778{
779 usock = wr_create_from_plain_sckt (sock);
780 if (0 != extra_in_size)
781 abort ();
782 if (0 != pthread_create (&pt,
783 NULL,
784 &run_usock,
785 urh))
786 abort ();
787}
788
789
790/**
791 * A client has requested the given url using the given method
792 * (#MHD_HTTP_METHOD_GET, #MHD_HTTP_METHOD_PUT,
793 * #MHD_HTTP_METHOD_DELETE, #MHD_HTTP_METHOD_POST, etc). The callback
794 * must call MHD callbacks to provide content to give back to the
795 * client and return an HTTP status code (i.e. #MHD_HTTP_OK,
796 * #MHD_HTTP_NOT_FOUND, etc.).
797 *
798 * @param cls argument given together with the function
799 * pointer when the handler was registered with MHD
800 * @param url the requested url
801 * @param method the HTTP method used (#MHD_HTTP_METHOD_GET,
802 * #MHD_HTTP_METHOD_PUT, etc.)
803 * @param version the HTTP version string (i.e.
804 * #MHD_HTTP_VERSION_1_1)
805 * @param upload_data the data being uploaded (excluding HEADERS,
806 * for a POST that fits into memory and that is encoded
807 * with a supported encoding, the POST data will NOT be
808 * given in upload_data and is instead available as
809 * part of #MHD_get_connection_values; very large POST
810 * data *will* be made available incrementally in
811 * @a upload_data)
812 * @param upload_data_size set initially to the size of the
813 * @a upload_data provided; the method must update this
814 * value to the number of bytes NOT processed;
815 * @param con_cls pointer that the callback can set to some
816 * address and that will be preserved by MHD for future
817 * calls for this request; since the access handler may
818 * be called many times (i.e., for a PUT/POST operation
819 * with plenty of upload data) this allows the application
820 * to easily associate some request-specific state.
821 * If necessary, this state can be cleaned up in the
822 * global #MHD_RequestCompletedCallback (which
823 * can be set with the #MHD_OPTION_NOTIFY_COMPLETED).
824 * Initially, `*con_cls` will be NULL.
825 * @return #MHD_YES if the connection was handled successfully,
826 * #MHD_NO if the socket must be closed due to a serios
827 * error while handling the request
828 */
829static int
830ahc_upgrade (void *cls,
831 struct MHD_Connection *connection,
832 const char *url,
833 const char *method,
834 const char *version,
835 const char *upload_data,
836 size_t *upload_data_size,
837 void **con_cls)
838{
839 struct MHD_Response *resp;
840 int ret;
841
842 if (((long) *con_cls) != (long) pthread_self ())
843 abort ();
844 resp = MHD_create_response_for_upgrade (&upgrade_cb,
845 NULL);
846 MHD_add_response_header (resp,
847 MHD_HTTP_HEADER_UPGRADE,
848 "Hello World Protocol");
849 ret = MHD_queue_response (connection,
850 MHD_HTTP_SWITCHING_PROTOCOLS,
851 resp);
852 MHD_destroy_response (resp);
853 return ret;
854}
855
856
857/**
858 * Run the MHD external event loop using select.
859 *
860 * @param daemon daemon to run it for
861 */
862static void
863run_mhd_select_loop (struct MHD_Daemon *daemon)
864{
865 fd_set rs;
866 fd_set ws;
867 fd_set es;
868 MHD_socket max_fd;
869 MHD_UNSIGNED_LONG_LONG to;
870 struct timeval tv;
871
872 while (! done)
873 {
874 FD_ZERO (&rs);
875 FD_ZERO (&ws);
876 FD_ZERO (&es);
877 max_fd = -1;
878 to = 1000;
879
880 if (MHD_YES !=
881 MHD_get_fdset (daemon,
882 &rs,
883 &ws,
884 &es,
885 &max_fd))
886 abort ();
887 (void) MHD_get_timeout (daemon,
888 &to);
889 if (1000 < to)
890 to = 1000;
891 tv.tv_sec = to / 1000;
892 tv.tv_usec = 1000 * (to % 1000);
893 if (0 > MHD_SYS_select_ (max_fd + 1,
894 &rs,
895 &ws,
896 &es,
897 &tv))
898 abort ();
899 MHD_run_from_select (daemon,
900 &rs,
901 &ws,
902 &es);
903 }
904}
905
906
907/**
908 * Run the MHD external event loop using select.
909 *
910 * @param daemon daemon to run it for
911 */
912static void
913run_mhd_poll_loop (struct MHD_Daemon *daemon)
914{
915 abort (); /* currently not implementable with existing MHD API */
916}
917
918
919/**
920 * Run the MHD external event loop using select.
921 *
922 * @param daemon daemon to run it for
923 */
924static void
925run_mhd_epoll_loop (struct MHD_Daemon *daemon)
926{
927 const union MHD_DaemonInfo *di;
928 MHD_socket ep;
929 fd_set rs;
930 MHD_UNSIGNED_LONG_LONG to;
931 struct timeval tv;
932
933 di = MHD_get_daemon_info (daemon,
934 MHD_DAEMON_INFO_EPOLL_FD);
935 ep = di->listen_fd;
936 while (! done)
937 {
938 FD_ZERO (&rs);
939 to = 1000;
940
941 FD_SET (ep, &rs);
942 (void) MHD_get_timeout (daemon,
943 &to);
944 if (1000 < to)
945 to = 1000;
946 tv.tv_sec = to / 1000;
947 tv.tv_usec = 1000 * (to % 1000);
948 select (ep + 1,
949 &rs,
950 NULL,
951 NULL,
952 &tv);
953 MHD_run (daemon);
954 }
955}
956
957
958/**
959 * Run the MHD external event loop using select.
960 *
961 * @param daemon daemon to run it for
962 */
963static void
964run_mhd_loop (struct MHD_Daemon *daemon,
965 int flags)
966{
967 if (0 != (flags & MHD_USE_POLL))
968 run_mhd_poll_loop (daemon);
969#if EPOLL_SUPPORT
970 else if (0 != (flags & MHD_USE_EPOLL))
971 run_mhd_epoll_loop (daemon);
972#endif
973 else
974 run_mhd_select_loop (daemon);
975}
976
977static bool test_tls;
978
979/**
49 * Test upgrading a connection. 980 * Test upgrading a connection.
50 * 981 *
51 * @param flags which event loop style should be tested 982 * @param flags which event loop style should be tested
@@ -55,13 +986,17 @@ static int
55test_upgrade (int flags, 986test_upgrade (int flags,
56 unsigned int pool) 987 unsigned int pool)
57{ 988{
58 struct MHD_Daemon *d; 989 struct MHD_Daemon *d = NULL;
59 MHD_socket sock; 990 wr_socket sock;
60 struct sockaddr_in sa; 991 struct sockaddr_in sa;
992#if defined(HTTPS_SUPPORT) && defined(HAVE_FORK) && defined(HAVE_WAITPID)
993 pid_t pid = -1;
994#endif /* HTTPS_SUPPORT && HAVE_FORK && HAVE_WAITPID */
61 995
62 done = 0; 996 done = 0;
63 997
64 d = MHD_start_daemon (flags | MHD_USE_DEBUG | MHD_USE_UPGRADE, 998 if (!test_tls)
999 d = MHD_start_daemon (flags | MHD_USE_DEBUG | MHD_USE_UPGRADE,
65 1080, 1000 1080,
66 NULL, NULL, 1001 NULL, NULL,
67 &ahc_upgrade, NULL, 1002 &ahc_upgrade, NULL,
@@ -70,18 +1005,52 @@ test_upgrade (int flags,
70 MHD_OPTION_NOTIFY_CONNECTION, &notify_connection_cb, NULL, 1005 MHD_OPTION_NOTIFY_CONNECTION, &notify_connection_cb, NULL,
71 MHD_OPTION_THREAD_POOL_SIZE, pool, 1006 MHD_OPTION_THREAD_POOL_SIZE, pool,
72 MHD_OPTION_END); 1007 MHD_OPTION_END);
1008#ifdef HTTPS_SUPPORT
1009 else
1010 d = MHD_start_daemon (flags | MHD_USE_DEBUG | MHD_USE_UPGRADE | MHD_USE_TLS,
1011 1080,
1012 NULL, NULL,
1013 &ahc_upgrade, NULL,
1014 MHD_OPTION_URI_LOG_CALLBACK, &log_cb, NULL,
1015 MHD_OPTION_NOTIFY_COMPLETED, &notify_completed_cb, NULL,
1016 MHD_OPTION_NOTIFY_CONNECTION, &notify_connection_cb, NULL,
1017 MHD_OPTION_HTTPS_MEM_KEY, srv_signed_key_pem,
1018 MHD_OPTION_HTTPS_MEM_CERT, srv_signed_cert_pem,
1019 MHD_OPTION_THREAD_POOL_SIZE, pool,
1020 MHD_OPTION_END);
1021#endif /* HTTPS_SUPPORT */
73 if (NULL == d) 1022 if (NULL == d)
74 return 2; 1023 return 2;
75 sock = socket (AF_INET, SOCK_STREAM, IPPROTO_TCP); 1024 if (!test_tls || TLS_LIB_GNUTLS == use_tls_tool)
76 if (MHD_INVALID_SOCKET == sock) 1025 {
77 abort (); 1026 sock = test_tls ? wr_create_tls_sckt () : wr_create_plain_sckt ();
78 sa.sin_family = AF_INET; 1027 if (WR_BAD == sock)
79 sa.sin_port = htons (1080); 1028 abort ();
80 sa.sin_addr.s_addr = htonl (INADDR_LOOPBACK); 1029 sa.sin_family = AF_INET;
81 if (0 != connect (sock, 1030 sa.sin_port = htons (1080);
82 (struct sockaddr *) &sa, 1031 sa.sin_addr.s_addr = htonl (INADDR_LOOPBACK);
83 sizeof (sa))) 1032 if (0 != wr_connect (sock,
84 abort (); 1033 (struct sockaddr *) &sa,
1034 sizeof (sa)))
1035 abort ();
1036 }
1037 else
1038 {
1039#if defined(HTTPS_SUPPORT) && defined(HAVE_FORK) && defined(HAVE_WAITPID)
1040 MHD_socket tls_fork_sock;
1041 if (-1 == (pid = gnutlscli_connect (&tls_fork_sock, 1080)))
1042 {
1043 MHD_stop_daemon (d);
1044 return 4;
1045 }
1046 sock = wr_create_from_plain_sckt (tls_fork_sock);
1047 if (WR_BAD == sock)
1048 abort ();
1049#else /* !HTTPS_SUPPORT || !HAVE_FORK || !HAVE_WAITPID */
1050 abort ();
1051#endif /* !HTTPS_SUPPORT || !HAVE_FORK || !HAVE_WAITPID */
1052 }
1053
85 if (0 != pthread_create (&pt_client, 1054 if (0 != pthread_create (&pt_client,
86 NULL, 1055 NULL,
87 &run_usock_client, 1056 &run_usock_client,
@@ -94,6 +1063,10 @@ test_upgrade (int flags,
94 NULL); 1063 NULL);
95 pthread_join (pt, 1064 pthread_join (pt,
96 NULL); 1065 NULL);
1066#if defined(HTTPS_SUPPORT) && defined(HAVE_FORK) && defined(HAVE_WAITPID)
1067 if (test_tls && TLS_LIB_GNUTLS != use_tls_tool)
1068 waitpid (pid, NULL, 0);
1069#endif /* HTTPS_SUPPORT && HAVE_FORK && HAVE_WAITPID */
97 MHD_stop_daemon (d); 1070 MHD_stop_daemon (d);
98 return 0; 1071 return 0;
99} 1072}
@@ -106,9 +1079,58 @@ main (int argc,
106 int error_count = 0; 1079 int error_count = 0;
107 int res; 1080 int res;
108 1081
1082 use_tls_tool = TLS_CLI_NO_TOOL;
1083 test_tls = has_in_name(argv[0], "_tls");
1084
109 if (has_param(argc, argv, "-v") || has_param(argc, argv, "--verbose")) 1085 if (has_param(argc, argv, "-v") || has_param(argc, argv, "--verbose"))
110 verbose = 1; 1086 verbose = 1;
111 1087
1088 if (test_tls)
1089 {
1090#ifdef HTTPS_SUPPORT
1091 if (has_param(argc, argv, "--use-gnutls-cli"))
1092 use_tls_tool = TLS_CLI_GNUTLS;
1093 else if (has_param(argc, argv, "--use-openssl"))
1094 use_tls_tool = TLS_CLI_OPENSSL;
1095 else if (has_param(argc, argv, "--use-gnutls-lib"))
1096 use_tls_tool = TLS_LIB_GNUTLS;
1097#if defined(HAVE_FORK) && defined(HAVE_WAITPID)
1098 else if (0 == system ("gnutls-cli --version 1> /dev/null"))
1099 use_tls_tool = TLS_CLI_GNUTLS;
1100 else if (0 == system ("openssl version 1> /dev/null"))
1101 use_tls_tool = TLS_CLI_OPENSSL;
1102#endif /* HAVE_FORK && HAVE_WAITPID */
1103 else
1104 use_tls_tool = TLS_LIB_GNUTLS; /* Should be available as MHD use it. */
1105 if (verbose)
1106 {
1107 switch (use_tls_tool)
1108 {
1109 case TLS_CLI_GNUTLS:
1110 printf ("GnuTLS-CLI will be used for testing.\n");
1111 break;
1112 case TLS_CLI_OPENSSL:
1113 printf ("Command line version of OpenSSL will be used for testing.\n");
1114 break;
1115 case TLS_LIB_GNUTLS:
1116 printf ("GnuTLS library will be used for testing.\n");
1117 break;
1118 default:
1119 abort ();
1120 }
1121 }
1122 if (TLS_LIB_GNUTLS == use_tls_tool && GNUTLS_E_SUCCESS != gnutls_global_init())
1123 abort ();
1124
1125#else /* ! HTTPS_SUPPORT */
1126 fprintf (stderr, "HTTPS support was disabled by configure.\n");
1127 return 99;
1128#endif /* ! HTTPS_SUPPORT */
1129 }
1130
1131 /* run tests */
1132 if (verbose)
1133 printf ("Starting HTTP \"Upgrade\" tests with %s connections.\n", test_tls ? "TLS" : "plain");
112 /* try external select */ 1134 /* try external select */
113 res = test_upgrade (0, 1135 res = test_upgrade (0,
114 0); 1136 0);
@@ -194,5 +1216,9 @@ main (int argc,
194 fprintf (stderr, 1216 fprintf (stderr,
195 "Error (code: %u)\n", 1217 "Error (code: %u)\n",
196 error_count); 1218 error_count);
1219#ifdef HTTPS_SUPPORT
1220 if (test_tls && TLS_LIB_GNUTLS == use_tls_tool)
1221 gnutls_global_deinit();
1222#endif /* HTTPS_SUPPORT */
197 return error_count != 0; /* 0 == pass */ 1223 return error_count != 0; /* 0 == pass */
198} 1224}