aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEvgeny Grin (Karlson2k) <k2k@narod.ru>2016-11-05 13:43:44 +0300
committerEvgeny Grin (Karlson2k) <k2k@narod.ru>2016-11-07 14:39:09 +0300
commit9ea89671719e23e5bcff40b17a3c0fe66c047cc3 (patch)
tree7d4b77138644aeb68b39fd1afb5c511cc1a833ef
parent8b10b7801c17f9d589cdb4b5131e5621ee088827 (diff)
downloadlibmicrohttpd-9ea89671719e23e5bcff40b17a3c0fe66c047cc3.tar.gz
libmicrohttpd-9ea89671719e23e5bcff40b17a3c0fe66c047cc3.zip
Combine upgrade tests into single source file, port to platforms without fork()
-rw-r--r--configure.ac42
-rw-r--r--src/microhttpd/Makefile.am30
-rw-r--r--src/microhttpd/test_upgrade.c1062
-rw-r--r--src/microhttpd/test_upgrade_common.c562
-rw-r--r--src/microhttpd/test_upgrade_ssl.c236
5 files changed, 1109 insertions, 823 deletions
diff --git a/configure.ac b/configure.ac
index c8a7c3f8..7a74e240 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1173,6 +1173,48 @@ AS_VAR_IF([[enable_httpupgrade]],[["yes"]],
1173AM_CONDITIONAL([ENABLE_UPGRADE], [[test "x$enable_httpupgrade" = "xyes"]]) 1173AM_CONDITIONAL([ENABLE_UPGRADE], [[test "x$enable_httpupgrade" = "xyes"]])
1174AC_MSG_RESULT([[$enable_httpupgrade]]) 1174AC_MSG_RESULT([[$enable_httpupgrade]])
1175 1175
1176# Check for fork() and waitpid(). They are used for tests.
1177AC_MSG_CHECKING([[for fork()]])
1178AC_LINK_IFELSE(
1179 [
1180 AC_LANG_PROGRAM(
1181 [[
1182#ifdef HAVE_SYS_TYPES_H
1183#include <sys/types.h>
1184#endif
1185#ifdef HAVE_UNISTD_H
1186#include <unistd.h>
1187#endif
1188 ]], [[
1189 pid_t p = fork ();
1190 if (0 == p)
1191 return 1;
1192 ]])
1193 ], [
1194 AC_DEFINE([[HAVE_FORK]], [[1]], [Define to 1 if you have the usable `fork' function.])
1195 AC_MSG_RESULT([[yes]])
1196
1197 AC_MSG_CHECKING([[for waitpid()]])
1198 AC_LINK_IFELSE(
1199 [
1200 AC_LANG_PROGRAM(
1201 [[
1202#include <sys/wait.h>
1203 ]], [[
1204 pid_t p = fork ();
1205 if (0 == p)
1206 return 1;
1207 waitpid (p, (void*)0, 0)
1208 ]])
1209 ], [
1210 AC_DEFINE([[HAVE_WAITPID]], [[1]], [Define to 1 if you have the usable `waitpid' function.])
1211 AC_MSG_RESULT([[yes]])
1212 ],[
1213 AC_MSG_RESULT([[no]])
1214 ])
1215],[
1216 AC_MSG_RESULT([[no]])
1217])
1176 1218
1177 1219
1178MHD_LIB_LDFLAGS="$MHD_LIB_LDFLAGS -export-dynamic -no-undefined" 1220MHD_LIB_LDFLAGS="$MHD_LIB_LDFLAGS -export-dynamic -no-undefined"
diff --git a/src/microhttpd/Makefile.am b/src/microhttpd/Makefile.am
index 81dc95c2..cc48d11d 100644
--- a/src/microhttpd/Makefile.am
+++ b/src/microhttpd/Makefile.am
@@ -152,10 +152,12 @@ check_PROGRAMS = \
152 test_shutdown_poll \ 152 test_shutdown_poll \
153 test_daemon 153 test_daemon
154 154
155if HAVE_POSIX_THREADS
155if ENABLE_UPGRADE 156if ENABLE_UPGRADE
156 check_PROGRAMS += test_upgrade 157 check_PROGRAMS += test_upgrade
157if ENABLE_HTTPS 158if ENABLE_HTTPS
158 check_PROGRAMS += test_upgrade_ssl 159 check_PROGRAMS += test_upgrade_tls
160endif
159endif 161endif
160endif 162endif
161 163
@@ -180,14 +182,28 @@ test_daemon_LDADD = \
180 $(top_builddir)/src/microhttpd/libmicrohttpd.la 182 $(top_builddir)/src/microhttpd/libmicrohttpd.la
181 183
182test_upgrade_SOURCES = \ 184test_upgrade_SOURCES = \
183 test_upgrade.c 185 test_upgrade.c test_helpers.h mhd_sockets.h
186test_upgrade_CPPFLAGS = \
187 $(AM_CPPFLAGS) $(GNUTLS_CPPFLAGS)
188test_upgrade_CFLAGS = \
189 $(AM_CFLAGS) $(PTHREAD_CFLAGS)
190test_upgrade_LDFLAGS = \
191 $(GNUTLS_LDFLAGS)
184test_upgrade_LDADD = \ 192test_upgrade_LDADD = \
185 $(top_builddir)/src/microhttpd/libmicrohttpd.la 193 $(top_builddir)/src/microhttpd/libmicrohttpd.la \
194 $(PTHREAD_LIBS)
186 195
187test_upgrade_ssl_SOURCES = \ 196test_upgrade_tls_SOURCES = \
188 test_upgrade_ssl.c 197 test_upgrade.c test_helpers.h mhd_sockets.h
189test_upgrade_ssl_LDADD = \ 198test_upgrade_tls_CPPFLAGS = \
190 $(top_builddir)/src/microhttpd/libmicrohttpd.la 199 $(AM_CPPFLAGS) $(GNUTLS_CPPFLAGS)
200test_upgrade_tls_CFLAGS = \
201 $(AM_CFLAGS) $(PTHREAD_CFLAGS)
202test_upgrade_tls_LDFLAGS = \
203 $(GNUTLS_LDFLAGS)
204test_upgrade_tls_LDADD = \
205 $(top_builddir)/src/microhttpd/libmicrohttpd.la \
206 $(PTHREAD_LIBS)
191 207
192test_postprocessor_SOURCES = \ 208test_postprocessor_SOURCES = \
193 test_postprocessor.c 209 test_postprocessor.c
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}
diff --git a/src/microhttpd/test_upgrade_common.c b/src/microhttpd/test_upgrade_common.c
deleted file mode 100644
index a96ffcdb..00000000
--- a/src/microhttpd/test_upgrade_common.c
+++ /dev/null
@@ -1,562 +0,0 @@
1/*
2 This file is part of libmicrohttpd
3 Copyright (C) 2016 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 2, 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_common.c
23 * @brief Shared logic for testcases for libmicrohttpd upgrading a connection
24 * @author Christian Grothoff
25 */
26
27#include "mhd_sockets.h"
28
29/**
30 * Thread we use to run the interaction with the upgraded socket.
31 */
32static pthread_t pt;
33
34/**
35 * Will be set to the upgraded socket.
36 */
37static MHD_socket usock;
38
39/**
40 * Thread we use to run the interaction with the upgraded socket.
41 */
42static pthread_t pt_client;
43
44/**
45 * Flag set to 1 once the test is finished.
46 */
47static int done;
48
49
50static void
51notify_completed_cb (void *cls,
52 struct MHD_Connection *connection,
53 void **con_cls,
54 enum MHD_RequestTerminationCode toe)
55{
56 if ( (toe != MHD_REQUEST_TERMINATED_COMPLETED_OK) &&
57 (toe != MHD_REQUEST_TERMINATED_CLIENT_ABORT) &&
58 (toe != MHD_REQUEST_TERMINATED_DAEMON_SHUTDOWN) )
59 abort ();
60 if (((long) *con_cls) != (long) pthread_self ())
61 abort ();
62 *con_cls = NULL;
63}
64
65
66/**
67 * Logging callback.
68 *
69 * @param cls logging closure (NULL)
70 * @param uri access URI
71 * @param connection connection handle
72 * @return #TEST_PTR
73 */
74static void *
75log_cb (void *cls,
76 const char *uri,
77 struct MHD_Connection *connection)
78{
79 if (0 != strcmp (uri,
80 "/"))
81 abort ();
82 return (void *) (long) pthread_self ();
83}
84
85
86/**
87 * Function to check that MHD properly notifies about starting
88 * and stopping.
89 *
90 * @param cls client-defined closure
91 * @param connection connection handle
92 * @param socket_context socket-specific pointer where the
93 * client can associate some state specific
94 * to the TCP connection; note that this is
95 * different from the "con_cls" which is per
96 * HTTP request. The client can initialize
97 * during #MHD_CONNECTION_NOTIFY_STARTED and
98 * cleanup during #MHD_CONNECTION_NOTIFY_CLOSED
99 * and access in the meantime using
100 * #MHD_CONNECTION_INFO_SOCKET_CONTEXT.
101 * @param toe reason for connection notification
102 * @see #MHD_OPTION_NOTIFY_CONNECTION
103 * @ingroup request
104 */
105static void
106notify_connection_cb (void *cls,
107 struct MHD_Connection *connection,
108 void **socket_context,
109 enum MHD_ConnectionNotificationCode toe)
110{
111 static int started;
112
113 switch (toe)
114 {
115 case MHD_CONNECTION_NOTIFY_STARTED:
116 if (MHD_NO != started)
117 abort ();
118 started = MHD_YES;
119 *socket_context = &started;
120 break;
121 case MHD_CONNECTION_NOTIFY_CLOSED:
122 if (MHD_YES != started)
123 abort ();
124 if (&started != *socket_context)
125 abort ();
126 *socket_context = NULL;
127 started = MHD_NO;
128 break;
129 }
130}
131
132
133/**
134 * Change socket to blocking.
135 *
136 * @param fd the socket to manipulate
137 * @return non-zero if succeeded, zero otherwise
138 */
139static void
140make_blocking (MHD_socket fd)
141{
142#if defined(MHD_POSIX_SOCKETS)
143 int flags;
144
145 flags = fcntl (fd, F_GETFL);
146 if (-1 == flags)
147 return;
148 if ((flags & ~O_NONBLOCK) != flags)
149 if (-1 == fcntl (fd, F_SETFL, flags & ~O_NONBLOCK))
150 abort ();
151#elif defined(MHD_WINSOCK_SOCKETS)
152 unsigned long flags = 1;
153
154 ioctlsocket (fd, FIONBIO, &flags);
155#endif /* MHD_WINSOCK_SOCKETS */
156
157}
158
159
160static void
161send_all (MHD_socket sock,
162 const char *text)
163{
164 size_t len = strlen (text);
165 ssize_t ret;
166
167 make_blocking (sock);
168 for (size_t off = 0; off < len; off += ret)
169 {
170 ret = MHD_send_ (sock,
171 &text[off],
172 len - off);
173 if (0 > ret)
174 {
175 if (MHD_SCKT_ERR_IS_EAGAIN_ (MHD_socket_get_error_ ()))
176 {
177 ret = 0;
178 continue;
179 }
180 abort ();
181 }
182 }
183}
184
185
186/**
187 * Read character-by-character until we
188 * get '\r\n\r\n'.
189 */
190static void
191recv_hdr (MHD_socket sock)
192{
193 unsigned int i;
194 char next;
195 char c;
196 ssize_t ret;
197
198 make_blocking (sock);
199 next = '\r';
200 i = 0;
201 while (i < 4)
202 {
203 ret = MHD_recv_ (sock,
204 &c,
205 1);
206 if (0 > ret)
207 {
208 if (MHD_SCKT_ERR_IS_EAGAIN_ (MHD_socket_get_error_ ()))
209 continue;
210 abort ();
211 }
212 if (0 == ret)
213 continue;
214 if (c == next)
215 {
216 i++;
217 if (next == '\r')
218 next = '\n';
219 else
220 next = '\r';
221 continue;
222 }
223 if (c == '\r')
224 {
225 i = 1;
226 next = '\n';
227 continue;
228 }
229 i = 0;
230 next = '\r';
231 }
232}
233
234
235static void
236recv_all (MHD_socket sock,
237 const char *text)
238{
239 size_t len = strlen (text);
240 char buf[len];
241 ssize_t ret;
242
243 make_blocking (sock);
244 for (size_t off = 0; off < len; off += ret)
245 {
246 ret = MHD_recv_ (sock,
247 &buf[off],
248 len - off);
249 if (0 > ret)
250 {
251 if (MHD_SCKT_ERR_IS_EAGAIN_ (MHD_socket_get_error_ ()))
252 {
253 ret = 0;
254 continue;
255 }
256 abort ();
257 }
258 }
259 if (0 != strncmp (text, buf, len))
260 abort();
261}
262
263
264/**
265 * Main function for the thread that runs the interaction with
266 * the upgraded socket.
267 *
268 * @param cls the handle for the upgrade
269 */
270static void *
271run_usock (void *cls)
272{
273 struct MHD_UpgradeResponseHandle *urh = cls;
274
275 send_all (usock,
276 "Hello");
277 recv_all (usock,
278 "World");
279 send_all (usock,
280 "Finished");
281 MHD_upgrade_action (urh,
282 MHD_UPGRADE_ACTION_CLOSE);
283 return NULL;
284}
285
286
287/**
288 * Main function for the thread that runs the client-side of the
289 * interaction with the upgraded socket.
290 *
291 * @param cls the client socket
292 */
293static void *
294run_usock_client (void *cls)
295{
296 MHD_socket *sock = cls;
297
298 send_all (*sock,
299 "GET / HTTP/1.1\r\nConnection: Upgrade\r\n\r\n");
300 recv_hdr (*sock);
301 recv_all (*sock,
302 "Hello");
303 send_all (*sock,
304 "World");
305 recv_all (*sock,
306 "Finished");
307 MHD_socket_close_chk_ (*sock);
308 done = 1;
309 return NULL;
310}
311
312
313/**
314 * Function called after a protocol "upgrade" response was sent
315 * successfully and the socket should now be controlled by some
316 * protocol other than HTTP.
317 *
318 * Any data already received on the socket will be made available in
319 * @e extra_in. This can happen if the application sent extra data
320 * before MHD send the upgrade response. The application should
321 * treat data from @a extra_in as if it had read it from the socket.
322 *
323 * Note that the application must not close() @a sock directly,
324 * but instead use #MHD_upgrade_action() for special operations
325 * on @a sock.
326 *
327 * Except when in 'thread-per-connection' mode, implementations
328 * of this function should never block (as it will still be called
329 * from within the main event loop).
330 *
331 * @param cls closure, whatever was given to #MHD_create_response_for_upgrade().
332 * @param connection original HTTP connection handle,
333 * giving the function a last chance
334 * to inspect the original HTTP request
335 * @param con_cls last value left in `con_cls` of the `MHD_AccessHandlerCallback`
336 * @param extra_in if we happened to have read bytes after the
337 * HTTP header already (because the client sent
338 * more than the HTTP header of the request before
339 * we sent the upgrade response),
340 * these are the extra bytes already read from @a sock
341 * by MHD. The application should treat these as if
342 * it had read them from @a sock.
343 * @param extra_in_size number of bytes in @a extra_in
344 * @param sock socket to use for bi-directional communication
345 * with the client. For HTTPS, this may not be a socket
346 * that is directly connected to the client and thus certain
347 * operations (TCP-specific setsockopt(), getsockopt(), etc.)
348 * may not work as expected (as the socket could be from a
349 * socketpair() or a TCP-loopback). The application is expected
350 * to perform read()/recv() and write()/send() calls on the socket.
351 * The application may also call shutdown(), but must not call
352 * close() directly.
353 * @param urh argument for #MHD_upgrade_action()s on this @a connection.
354 * Applications must eventually use this callback to (indirectly)
355 * perform the close() action on the @a sock.
356 */
357static void
358upgrade_cb (void *cls,
359 struct MHD_Connection *connection,
360 void *con_cls,
361 const char *extra_in,
362 size_t extra_in_size,
363 MHD_socket sock,
364 struct MHD_UpgradeResponseHandle *urh)
365{
366 usock = sock;
367 if (0 != extra_in_size)
368 abort ();
369 if (0 != pthread_create (&pt,
370 NULL,
371 &run_usock,
372 urh))
373 abort ();
374}
375
376
377/**
378 * A client has requested the given url using the given method
379 * (#MHD_HTTP_METHOD_GET, #MHD_HTTP_METHOD_PUT,
380 * #MHD_HTTP_METHOD_DELETE, #MHD_HTTP_METHOD_POST, etc). The callback
381 * must call MHD callbacks to provide content to give back to the
382 * client and return an HTTP status code (i.e. #MHD_HTTP_OK,
383 * #MHD_HTTP_NOT_FOUND, etc.).
384 *
385 * @param cls argument given together with the function
386 * pointer when the handler was registered with MHD
387 * @param url the requested url
388 * @param method the HTTP method used (#MHD_HTTP_METHOD_GET,
389 * #MHD_HTTP_METHOD_PUT, etc.)
390 * @param version the HTTP version string (i.e.
391 * #MHD_HTTP_VERSION_1_1)
392 * @param upload_data the data being uploaded (excluding HEADERS,
393 * for a POST that fits into memory and that is encoded
394 * with a supported encoding, the POST data will NOT be
395 * given in upload_data and is instead available as
396 * part of #MHD_get_connection_values; very large POST
397 * data *will* be made available incrementally in
398 * @a upload_data)
399 * @param upload_data_size set initially to the size of the
400 * @a upload_data provided; the method must update this
401 * value to the number of bytes NOT processed;
402 * @param con_cls pointer that the callback can set to some
403 * address and that will be preserved by MHD for future
404 * calls for this request; since the access handler may
405 * be called many times (i.e., for a PUT/POST operation
406 * with plenty of upload data) this allows the application
407 * to easily associate some request-specific state.
408 * If necessary, this state can be cleaned up in the
409 * global #MHD_RequestCompletedCallback (which
410 * can be set with the #MHD_OPTION_NOTIFY_COMPLETED).
411 * Initially, `*con_cls` will be NULL.
412 * @return #MHD_YES if the connection was handled successfully,
413 * #MHD_NO if the socket must be closed due to a serios
414 * error while handling the request
415 */
416static int
417ahc_upgrade (void *cls,
418 struct MHD_Connection *connection,
419 const char *url,
420 const char *method,
421 const char *version,
422 const char *upload_data,
423 size_t *upload_data_size,
424 void **con_cls)
425{
426 struct MHD_Response *resp;
427 int ret;
428
429 if (((long) *con_cls) != (long) pthread_self ())
430 abort ();
431 resp = MHD_create_response_for_upgrade (&upgrade_cb,
432 NULL);
433 MHD_add_response_header (resp,
434 MHD_HTTP_HEADER_UPGRADE,
435 "Hello World Protocol");
436 ret = MHD_queue_response (connection,
437 MHD_HTTP_SWITCHING_PROTOCOLS,
438 resp);
439 MHD_destroy_response (resp);
440 return ret;
441}
442
443
444/**
445 * Run the MHD external event loop using select.
446 *
447 * @param daemon daemon to run it for
448 */
449static void
450run_mhd_select_loop (struct MHD_Daemon *daemon)
451{
452 fd_set rs;
453 fd_set ws;
454 fd_set es;
455 MHD_socket max_fd;
456 MHD_UNSIGNED_LONG_LONG to;
457 struct timeval tv;
458
459 while (! done)
460 {
461 FD_ZERO (&rs);
462 FD_ZERO (&ws);
463 FD_ZERO (&es);
464 max_fd = -1;
465 to = 1000;
466
467 if (MHD_YES !=
468 MHD_get_fdset (daemon,
469 &rs,
470 &ws,
471 &es,
472 &max_fd))
473 abort ();
474 (void) MHD_get_timeout (daemon,
475 &to);
476 if (1000 < to)
477 to = 1000;
478 tv.tv_sec = to / 1000;
479 tv.tv_usec = 1000 * (to % 1000);
480 if (0 > MHD_SYS_select_ (max_fd + 1,
481 &rs,
482 &ws,
483 &es,
484 &tv))
485 abort ();
486 MHD_run_from_select (daemon,
487 &rs,
488 &ws,
489 &es);
490 }
491}
492
493
494/**
495 * Run the MHD external event loop using select.
496 *
497 * @param daemon daemon to run it for
498 */
499static void
500run_mhd_poll_loop (struct MHD_Daemon *daemon)
501{
502 abort (); /* currently not implementable with existing MHD API */
503}
504
505
506/**
507 * Run the MHD external event loop using select.
508 *
509 * @param daemon daemon to run it for
510 */
511static void
512run_mhd_epoll_loop (struct MHD_Daemon *daemon)
513{
514 const union MHD_DaemonInfo *di;
515 MHD_socket ep;
516 fd_set rs;
517 MHD_UNSIGNED_LONG_LONG to;
518 struct timeval tv;
519
520 di = MHD_get_daemon_info (daemon,
521 MHD_DAEMON_INFO_EPOLL_FD);
522 ep = di->listen_fd;
523 while (! done)
524 {
525 FD_ZERO (&rs);
526 to = 1000;
527
528 FD_SET (ep, &rs);
529 (void) MHD_get_timeout (daemon,
530 &to);
531 if (1000 < to)
532 to = 1000;
533 tv.tv_sec = to / 1000;
534 tv.tv_usec = 1000 * (to % 1000);
535 select (ep + 1,
536 &rs,
537 NULL,
538 NULL,
539 &tv);
540 MHD_run (daemon);
541 }
542}
543
544
545/**
546 * Run the MHD external event loop using select.
547 *
548 * @param daemon daemon to run it for
549 */
550static void
551run_mhd_loop (struct MHD_Daemon *daemon,
552 int flags)
553{
554 if (0 != (flags & MHD_USE_POLL))
555 run_mhd_poll_loop (daemon);
556#if EPOLL_SUPPORT
557 else if (0 != (flags & MHD_USE_EPOLL))
558 run_mhd_epoll_loop (daemon);
559#endif
560 else
561 run_mhd_select_loop (daemon);
562}
diff --git a/src/microhttpd/test_upgrade_ssl.c b/src/microhttpd/test_upgrade_ssl.c
deleted file mode 100644
index bb3d2c3b..00000000
--- a/src/microhttpd/test_upgrade_ssl.c
+++ /dev/null
@@ -1,236 +0,0 @@
1/*
2 This file is part of libmicrohttpd
3 Copyright (C) 2016 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_ssl.c
23 * @brief Testcase for libmicrohttpd upgrading a connection
24 * @author Christian Grothoff
25 */
26
27#include "platform.h"
28#include "microhttpd.h"
29#include <stdlib.h>
30#include <string.h>
31#include <stdio.h>
32#include <sys/types.h>
33#include <sys/wait.h>
34
35#ifndef WINDOWS
36#include <unistd.h>
37#endif
38
39#include <pthread.h>
40#include "mhd_sockets.h"
41#ifdef HAVE_NETINET_IP_H
42#include <netinet/ip.h>
43#endif /* HAVE_NETINET_IP_H */
44#include "mhd_sockets.h"
45#include "test_upgrade_common.c"
46
47#include "../testcurl/https/tls_test_keys.h"
48
49
50enum tls_cli_tool
51{
52 TLS_CLI_NO_TOOL = 0,
53 TLS_CLI_GNUTLS,
54 TLS_CLI_OPENSSL
55};
56
57enum tls_cli_tool use_tool;
58
59/**
60 * Fork child that connects via OpenSSL to our @a port. Allows us to
61 * talk to our port over a socket in @a sp without having to worry
62 * about TLS.
63 *
64 * @param location where the socket is returned
65 * @return -1 on error, otherwise PID of SSL child process
66 */
67static pid_t
68openssl_connect (int *sock,
69 uint16_t port)
70{
71 pid_t chld;
72 int sp[2];
73 char destination[30];
74
75 if (0 != socketpair (AF_UNIX,
76 SOCK_STREAM,
77 0,
78 sp))
79 return -1;
80 chld = fork ();
81 if (0 != chld)
82 {
83 *sock = sp[1];
84 MHD_socket_close_chk_ (sp[0]);
85 return chld;
86 }
87 MHD_socket_close_chk_ (sp[1]);
88 (void) close (0);
89 (void) close (1);
90 dup2 (sp[0], 0);
91 dup2 (sp[0], 1);
92 MHD_socket_close_chk_ (sp[0]);
93 if (TLS_CLI_GNUTLS == use_tool)
94 {
95 snprintf (destination,
96 sizeof(destination),
97 "%u",
98 (unsigned int) port);
99 execlp ("gnutls-cli",
100 "gnutls-cli",
101 "--insecure",
102 "-p",
103 destination,
104 "localhost",
105 (char *) NULL);
106 }
107 else if (TLS_CLI_OPENSSL == use_tool)
108 {
109 snprintf (destination,
110 sizeof(destination),
111 "localhost:%u",
112 (unsigned int) port);
113 execlp ("openssl",
114 "openssl",
115 "s_client",
116 "-connect",
117 destination,
118 "-verify",
119 "0",
120 (char *) NULL);
121 }
122 _exit (1);
123}
124
125
126/**
127 * Test upgrading a connection.
128 *
129 * @param flags which event loop style should be tested
130 * @param pool size of the thread pool, 0 to disable
131 */
132static int
133test_upgrade (int flags,
134 unsigned int pool)
135{
136 struct MHD_Daemon *d;
137 MHD_socket sock;
138 pid_t pid;
139
140 done = 0;
141
142 d = MHD_start_daemon (flags | MHD_USE_DEBUG | MHD_USE_UPGRADE | MHD_USE_TLS,
143 1080,
144 NULL, NULL,
145 &ahc_upgrade, NULL,
146 MHD_OPTION_URI_LOG_CALLBACK, &log_cb, NULL,
147 MHD_OPTION_NOTIFY_COMPLETED, &notify_completed_cb, NULL,
148 MHD_OPTION_NOTIFY_CONNECTION, &notify_connection_cb, NULL,
149 MHD_OPTION_HTTPS_MEM_KEY, srv_signed_key_pem,
150 MHD_OPTION_HTTPS_MEM_CERT, srv_signed_cert_pem,
151 MHD_OPTION_THREAD_POOL_SIZE, pool,
152 MHD_OPTION_END);
153 if (NULL == d)
154 return 2;
155 if (-1 == (pid = openssl_connect (&sock, 1080)))
156 {
157 MHD_stop_daemon (d);
158 return 4;
159 }
160
161 pthread_create (&pt_client,
162 NULL,
163 &run_usock_client,
164 &sock);
165 if (0 == (flags & (MHD_USE_SELECT_INTERNALLY |
166 MHD_USE_THREAD_PER_CONNECTION)) )
167 run_mhd_loop (d, flags);
168 pthread_join (pt_client,
169 NULL);
170 if (0 == (flags & (MHD_USE_SELECT_INTERNALLY |
171 MHD_USE_THREAD_PER_CONNECTION)) )
172 run_mhd_loop (d, flags);
173 pthread_join (pt,
174 NULL);
175 waitpid (pid,
176 NULL,
177 0);
178 MHD_stop_daemon (d);
179 return 0;
180}
181
182
183int
184main (int argc,
185 char *const *argv)
186{
187 int error_count = 0;
188
189 use_tool = TLS_CLI_NO_TOOL;
190 if (0 == system ("gnutls-cli --version 1> /dev/null"))
191 use_tool = TLS_CLI_GNUTLS;
192 else if (0 == system ("openssl version 1> /dev/null"))
193 use_tool = TLS_CLI_OPENSSL;
194 else
195 return 77; /* not possible to test */
196
197 /* try external select */
198 error_count += test_upgrade (0,
199 0);
200#ifdef EPOLL_SUPPORT
201 error_count += test_upgrade (MHD_USE_EPOLL | MHD_USE_TLS,
202 0);
203#endif
204
205 /* Test thread-per-connection */
206 error_count += test_upgrade (MHD_USE_THREAD_PER_CONNECTION,
207 0);
208 error_count += test_upgrade (MHD_USE_THREAD_PER_CONNECTION | MHD_USE_POLL,
209 0);
210
211 /* Test different event loops, with and without thread pool */
212 error_count += test_upgrade (MHD_USE_SELECT_INTERNALLY,
213 0);
214 error_count += test_upgrade (MHD_USE_SELECT_INTERNALLY,
215 2);
216#ifdef HAVE_POLL
217 error_count += test_upgrade (MHD_USE_POLL_INTERNALLY,
218 0);
219 error_count += test_upgrade (MHD_USE_POLL_INTERNALLY,
220 2);
221#endif
222#ifdef EPOLL_SUPPORT
223 error_count += test_upgrade (MHD_USE_EPOLL_INTERNALLY |
224 MHD_USE_TLS,
225 0);
226 error_count += test_upgrade (MHD_USE_EPOLL_INTERNALLY |
227 MHD_USE_TLS,
228 2);
229#endif
230 /* report result */
231 if (0 != error_count)
232 fprintf (stderr,
233 "Error (code: %u)\n",
234 error_count);
235 return error_count != 0; /* 0 == pass */
236}