diff options
Diffstat (limited to 'src/microhttpd/test_upgrade.c')
-rw-r--r-- | src/microhttpd/test_upgrade.c | 1062 |
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 | ||
43 | static 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 | |||
62 | static int verbose = 0; | ||
63 | |||
64 | enum tls_tool | ||
65 | { | ||
66 | TLS_CLI_NO_TOOL = 0, | ||
67 | TLS_CLI_GNUTLS, | ||
68 | TLS_CLI_OPENSSL, | ||
69 | TLS_LIB_GNUTLS | ||
70 | }; | ||
71 | |||
72 | enum 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 | */ | ||
83 | static pid_t | ||
84 | gnutlscli_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 | */ | ||
146 | struct 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 | */ | ||
184 | typedef 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 | */ | ||
204 | static 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 | */ | ||
222 | static 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 | */ | ||
263 | static 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 | */ | ||
281 | static 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() ! */ | ||
314 | static 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 | */ | ||
337 | static 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 | */ | ||
370 | static 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 | */ | ||
401 | static 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 | */ | ||
427 | static 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 | */ | ||
445 | static pthread_t pt; | ||
446 | |||
447 | /** | ||
448 | * Will be set to the upgraded socket. | ||
449 | */ | ||
450 | static wr_socket usock; | ||
451 | |||
452 | /** | ||
453 | * Thread we use to run the interaction with the upgraded socket. | ||
454 | */ | ||
455 | static pthread_t pt_client; | ||
456 | |||
457 | /** | ||
458 | * Flag set to 1 once the test is finished. | ||
459 | */ | ||
460 | static int done; | ||
461 | |||
462 | |||
463 | static void | ||
464 | notify_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 | */ | ||
487 | static void * | ||
488 | log_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 | */ | ||
518 | static void | ||
519 | notify_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 | */ | ||
552 | static void | ||
553 | make_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 | |||
573 | static void | ||
574 | send_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 | */ | ||
603 | static void | ||
604 | recv_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 | |||
648 | static void | ||
649 | recv_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 | */ | ||
683 | static void * | ||
684 | run_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 | */ | ||
706 | static void * | ||
707 | run_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 | */ | ||
770 | static void | ||
771 | upgrade_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 | */ | ||
829 | static int | ||
830 | ahc_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 | */ | ||
862 | static void | ||
863 | run_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 | */ | ||
912 | static void | ||
913 | run_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 | */ | ||
924 | static void | ||
925 | run_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 | */ | ||
963 | static void | ||
964 | run_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 | |||
977 | static 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 | |||
55 | test_upgrade (int flags, | 986 | test_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, ¬ify_connection_cb, NULL, | 1005 | MHD_OPTION_NOTIFY_CONNECTION, ¬ify_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, ¬ify_completed_cb, NULL, | ||
1016 | MHD_OPTION_NOTIFY_CONNECTION, ¬ify_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 | } |