diff options
author | Christian Grothoff <christian@grothoff.org> | 2016-09-03 22:32:29 +0000 |
---|---|---|
committer | Christian Grothoff <christian@grothoff.org> | 2016-09-03 22:32:29 +0000 |
commit | 352a07b01018a4379f4fd83b8277730f044afede (patch) | |
tree | e2e7624f083b3ae8a9adee3f9c36e216bc2a885e | |
parent | 6fa19ae05f226451db78316919908c44d6444ac4 (diff) | |
download | libmicrohttpd-352a07b01018a4379f4fd83b8277730f044afede.tar.gz libmicrohttpd-352a07b01018a4379f4fd83b8277730f044afede.zip |
-first, crazy-pants version of test and implementation of HTTPS upgrade; FLUSH API still needs to change dramatically, neither the ioctl nor the busy waiting are really acceptable here
-rw-r--r-- | src/microhttpd/Makefile.am | 9 | ||||
-rw-r--r-- | src/microhttpd/connection.c | 29 | ||||
-rw-r--r-- | src/microhttpd/daemon.c | 26 | ||||
-rw-r--r-- | src/microhttpd/mhd_sockets.c | 17 | ||||
-rw-r--r-- | src/microhttpd/response.c | 65 | ||||
-rw-r--r-- | src/microhttpd/test_upgrade.c | 11 | ||||
-rw-r--r-- | src/microhttpd/test_upgrade_ssl.c | 472 |
7 files changed, 590 insertions, 39 deletions
diff --git a/src/microhttpd/Makefile.am b/src/microhttpd/Makefile.am index 9a32f598..b60ff81e 100644 --- a/src/microhttpd/Makefile.am +++ b/src/microhttpd/Makefile.am | |||
@@ -146,6 +146,10 @@ check_PROGRAMS = \ | |||
146 | test_daemon \ | 146 | test_daemon \ |
147 | test_upgrade | 147 | test_upgrade |
148 | 148 | ||
149 | if ENABLE_HTTPS | ||
150 | check_PROGRAMS += test_upgrade_ssl | ||
151 | endif | ||
152 | |||
149 | if HAVE_POSTPROCESSOR | 153 | if HAVE_POSTPROCESSOR |
150 | check_PROGRAMS += \ | 154 | check_PROGRAMS += \ |
151 | test_postprocessor \ | 155 | test_postprocessor \ |
@@ -171,6 +175,11 @@ test_upgrade_SOURCES = \ | |||
171 | test_upgrade_LDADD = \ | 175 | test_upgrade_LDADD = \ |
172 | $(top_builddir)/src/microhttpd/libmicrohttpd.la | 176 | $(top_builddir)/src/microhttpd/libmicrohttpd.la |
173 | 177 | ||
178 | test_upgrade_ssl_SOURCES = \ | ||
179 | test_upgrade_ssl.c | ||
180 | test_upgrade_ssl_LDADD = \ | ||
181 | $(top_builddir)/src/microhttpd/libmicrohttpd.la | ||
182 | |||
174 | test_postprocessor_SOURCES = \ | 183 | test_postprocessor_SOURCES = \ |
175 | test_postprocessor.c | 184 | test_postprocessor.c |
176 | test_postprocessor_CPPFLAGS = \ | 185 | test_postprocessor_CPPFLAGS = \ |
diff --git a/src/microhttpd/connection.c b/src/microhttpd/connection.c index 0c21bc57..16477538 100644 --- a/src/microhttpd/connection.c +++ b/src/microhttpd/connection.c | |||
@@ -2354,7 +2354,8 @@ MHD_connection_handle_write (struct MHD_Connection *connection) | |||
2354 | (void) MHD_mutex_unlock_ (&response->mutex); | 2354 | (void) MHD_mutex_unlock_ (&response->mutex); |
2355 | if (ret < 0) | 2355 | if (ret < 0) |
2356 | { | 2356 | { |
2357 | if (MHD_SCKT_ERR_IS_EINTR_ (err) || MHD_SCKT_ERR_IS_EAGAIN_ (err)) | 2357 | if (MHD_SCKT_ERR_IS_EINTR_ (err) || |
2358 | MHD_SCKT_ERR_IS_EAGAIN_ (err)) | ||
2358 | return MHD_YES; | 2359 | return MHD_YES; |
2359 | #ifdef HAVE_MESSAGES | 2360 | #ifdef HAVE_MESSAGES |
2360 | MHD_DLOG (connection->daemon, | 2361 | MHD_DLOG (connection->daemon, |
@@ -2515,13 +2516,17 @@ MHD_connection_handle_idle (struct MHD_Connection *connection) | |||
2515 | } | 2516 | } |
2516 | break; | 2517 | break; |
2517 | } | 2518 | } |
2518 | if (MHD_NO == parse_initial_message_line (connection, line, line_len)) | 2519 | if (MHD_NO == parse_initial_message_line (connection, |
2519 | CONNECTION_CLOSE_ERROR (connection, NULL); | 2520 | line, |
2521 | line_len)) | ||
2522 | CONNECTION_CLOSE_ERROR (connection, | ||
2523 | NULL); | ||
2520 | else | 2524 | else |
2521 | connection->state = MHD_CONNECTION_URL_RECEIVED; | 2525 | connection->state = MHD_CONNECTION_URL_RECEIVED; |
2522 | continue; | 2526 | continue; |
2523 | case MHD_CONNECTION_URL_RECEIVED: | 2527 | case MHD_CONNECTION_URL_RECEIVED: |
2524 | line = get_next_header_line (connection, NULL); | 2528 | line = get_next_header_line (connection, |
2529 | NULL); | ||
2525 | if (NULL == line) | 2530 | if (NULL == line) |
2526 | { | 2531 | { |
2527 | if (MHD_CONNECTION_URL_RECEIVED != connection->state) | 2532 | if (MHD_CONNECTION_URL_RECEIVED != connection->state) |
@@ -2539,7 +2544,8 @@ MHD_connection_handle_idle (struct MHD_Connection *connection) | |||
2539 | connection->state = MHD_CONNECTION_HEADERS_RECEIVED; | 2544 | connection->state = MHD_CONNECTION_HEADERS_RECEIVED; |
2540 | continue; | 2545 | continue; |
2541 | } | 2546 | } |
2542 | if (MHD_NO == process_header_line (connection, line)) | 2547 | if (MHD_NO == process_header_line (connection, |
2548 | line)) | ||
2543 | { | 2549 | { |
2544 | transmit_error_response (connection, | 2550 | transmit_error_response (connection, |
2545 | MHD_HTTP_BAD_REQUEST, | 2551 | MHD_HTTP_BAD_REQUEST, |
@@ -2645,7 +2651,8 @@ MHD_connection_handle_idle (struct MHD_Connection *connection) | |||
2645 | } | 2651 | } |
2646 | break; | 2652 | break; |
2647 | case MHD_CONNECTION_BODY_RECEIVED: | 2653 | case MHD_CONNECTION_BODY_RECEIVED: |
2648 | line = get_next_header_line (connection, NULL); | 2654 | line = get_next_header_line (connection, |
2655 | NULL); | ||
2649 | if (NULL == line) | 2656 | if (NULL == line) |
2650 | { | 2657 | { |
2651 | if (connection->state != MHD_CONNECTION_BODY_RECEIVED) | 2658 | if (connection->state != MHD_CONNECTION_BODY_RECEIVED) |
@@ -2689,7 +2696,9 @@ MHD_connection_handle_idle (struct MHD_Connection *connection) | |||
2689 | break; | 2696 | break; |
2690 | } | 2697 | } |
2691 | if (MHD_NO == | 2698 | if (MHD_NO == |
2692 | process_broken_line (connection, line, MHD_FOOTER_KIND)) | 2699 | process_broken_line (connection, |
2700 | line, | ||
2701 | MHD_FOOTER_KIND)) | ||
2693 | continue; | 2702 | continue; |
2694 | if (0 == line[0]) | 2703 | if (0 == line[0]) |
2695 | { | 2704 | { |
@@ -3057,17 +3066,17 @@ MHD_get_connection_info (struct MHD_Connection *connection, | |||
3057 | { | 3066 | { |
3058 | #if HTTPS_SUPPORT | 3067 | #if HTTPS_SUPPORT |
3059 | case MHD_CONNECTION_INFO_CIPHER_ALGO: | 3068 | case MHD_CONNECTION_INFO_CIPHER_ALGO: |
3060 | if (connection->tls_session == NULL) | 3069 | if (NULL == connection->tls_session) |
3061 | return NULL; | 3070 | return NULL; |
3062 | connection->cipher = gnutls_cipher_get (connection->tls_session); | 3071 | connection->cipher = gnutls_cipher_get (connection->tls_session); |
3063 | return (const union MHD_ConnectionInfo *) &connection->cipher; | 3072 | return (const union MHD_ConnectionInfo *) &connection->cipher; |
3064 | case MHD_CONNECTION_INFO_PROTOCOL: | 3073 | case MHD_CONNECTION_INFO_PROTOCOL: |
3065 | if (connection->tls_session == NULL) | 3074 | if (NULL == connection->tls_session) |
3066 | return NULL; | 3075 | return NULL; |
3067 | connection->protocol = gnutls_protocol_get_version (connection->tls_session); | 3076 | connection->protocol = gnutls_protocol_get_version (connection->tls_session); |
3068 | return (const union MHD_ConnectionInfo *) &connection->protocol; | 3077 | return (const union MHD_ConnectionInfo *) &connection->protocol; |
3069 | case MHD_CONNECTION_INFO_GNUTLS_SESSION: | 3078 | case MHD_CONNECTION_INFO_GNUTLS_SESSION: |
3070 | if (connection->tls_session == NULL) | 3079 | if (NULL == connection->tls_session) |
3071 | return NULL; | 3080 | return NULL; |
3072 | return (const union MHD_ConnectionInfo *) &connection->tls_session; | 3081 | return (const union MHD_ConnectionInfo *) &connection->tls_session; |
3073 | #endif | 3082 | #endif |
diff --git a/src/microhttpd/daemon.c b/src/microhttpd/daemon.c index a298a1a4..62cd3ce8 100644 --- a/src/microhttpd/daemon.c +++ b/src/microhttpd/daemon.c | |||
@@ -659,19 +659,19 @@ urh_to_fdset (struct MHD_UpgradeResponseHandle *urh, | |||
659 | max_fd, | 659 | max_fd, |
660 | fd_setsize)) ) | 660 | fd_setsize)) ) |
661 | return MHD_NO; | 661 | return MHD_NO; |
662 | if ( (0 != (MHD_EPOLL_STATE_WRITE_READY & urh->mhd.celi)) && | 662 | if ( (0 == (MHD_EPOLL_STATE_WRITE_READY & urh->mhd.celi)) && |
663 | (! MHD_add_to_fd_set_ (urh->mhd.socket, | 663 | (! MHD_add_to_fd_set_ (urh->mhd.socket, |
664 | ws, | 664 | ws, |
665 | max_fd, | 665 | max_fd, |
666 | fd_setsize)) ) | 666 | fd_setsize)) ) |
667 | return MHD_NO; | 667 | return MHD_NO; |
668 | if ( (0 != (MHD_EPOLL_STATE_READ_READY & urh->app.celi)) && | 668 | if ( (0 == (MHD_EPOLL_STATE_READ_READY & urh->app.celi)) && |
669 | (! MHD_add_to_fd_set_ (urh->connection->socket_fd, | 669 | (! MHD_add_to_fd_set_ (urh->connection->socket_fd, |
670 | rs, | 670 | rs, |
671 | max_fd, | 671 | max_fd, |
672 | fd_setsize)) ) | 672 | fd_setsize)) ) |
673 | return MHD_NO; | 673 | return MHD_NO; |
674 | if ( (0 != (MHD_EPOLL_STATE_WRITE_READY & urh->app.celi)) && | 674 | if ( (0 == (MHD_EPOLL_STATE_WRITE_READY & urh->app.celi)) && |
675 | (! MHD_add_to_fd_set_ (urh->connection->socket_fd, | 675 | (! MHD_add_to_fd_set_ (urh->connection->socket_fd, |
676 | ws, | 676 | ws, |
677 | max_fd, | 677 | max_fd, |
@@ -2639,10 +2639,17 @@ MHD_select (struct MHD_Daemon *daemon, | |||
2639 | may_block = MHD_NO; | 2639 | may_block = MHD_NO; |
2640 | 2640 | ||
2641 | /* single-threaded, go over everything */ | 2641 | /* single-threaded, go over everything */ |
2642 | if (MHD_NO == MHD_get_fdset2 (daemon, &rs, &ws, &es, &maxsock, FD_SETSIZE)) | 2642 | if (MHD_NO == |
2643 | MHD_get_fdset2 (daemon, | ||
2644 | &rs, | ||
2645 | &ws, | ||
2646 | &es, | ||
2647 | &maxsock, | ||
2648 | FD_SETSIZE)) | ||
2643 | { | 2649 | { |
2644 | #ifdef HAVE_MESSAGES | 2650 | #ifdef HAVE_MESSAGES |
2645 | MHD_DLOG (daemon, "Could not obtain daemon fdsets"); | 2651 | MHD_DLOG (daemon, |
2652 | "Could not obtain daemon fdsets"); | ||
2646 | #endif | 2653 | #endif |
2647 | err_state = MHD_YES; | 2654 | err_state = MHD_YES; |
2648 | } | 2655 | } |
@@ -2657,7 +2664,8 @@ MHD_select (struct MHD_Daemon *daemon, | |||
2657 | FD_SETSIZE)) ) | 2664 | FD_SETSIZE)) ) |
2658 | { | 2665 | { |
2659 | #ifdef HAVE_MESSAGES | 2666 | #ifdef HAVE_MESSAGES |
2660 | MHD_DLOG (daemon, "Could not add listen socket to fdset"); | 2667 | MHD_DLOG (daemon, |
2668 | "Could not add listen socket to fdset"); | ||
2661 | #endif | 2669 | #endif |
2662 | return MHD_NO; | 2670 | return MHD_NO; |
2663 | } | 2671 | } |
@@ -2726,7 +2734,11 @@ MHD_select (struct MHD_Daemon *daemon, | |||
2726 | timeout.tv_sec = (_MHD_TIMEVAL_TV_SEC_TYPE)(ltimeout / 1000); | 2734 | timeout.tv_sec = (_MHD_TIMEVAL_TV_SEC_TYPE)(ltimeout / 1000); |
2727 | tv = &timeout; | 2735 | tv = &timeout; |
2728 | } | 2736 | } |
2729 | num_ready = MHD_SYS_select_ (maxsock + 1, &rs, &ws, &es, tv); | 2737 | num_ready = MHD_SYS_select_ (maxsock + 1, |
2738 | &rs, | ||
2739 | &ws, | ||
2740 | &es, | ||
2741 | tv); | ||
2730 | if (MHD_YES == daemon->shutdown) | 2742 | if (MHD_YES == daemon->shutdown) |
2731 | return MHD_NO; | 2743 | return MHD_NO; |
2732 | if (num_ready < 0) | 2744 | if (num_ready < 0) |
diff --git a/src/microhttpd/mhd_sockets.c b/src/microhttpd/mhd_sockets.c index 1023017f..1187a2cd 100644 --- a/src/microhttpd/mhd_sockets.c +++ b/src/microhttpd/mhd_sockets.c | |||
@@ -337,16 +337,21 @@ MHD_add_to_fd_set_ (MHD_socket fd, | |||
337 | MHD_socket *max_fd, | 337 | MHD_socket *max_fd, |
338 | unsigned int fd_setsize) | 338 | unsigned int fd_setsize) |
339 | { | 339 | { |
340 | if (NULL == set || MHD_INVALID_SOCKET == fd) | 340 | if ( (NULL == set) || |
341 | (MHD_INVALID_SOCKET == fd) ) | ||
341 | return 0; | 342 | return 0; |
342 | if (!MHD_SCKT_FD_FITS_FDSET_SETSIZE_(fd, set, fd_setsize)) | 343 | if (! MHD_SCKT_FD_FITS_FDSET_SETSIZE_ (fd, |
344 | set, | ||
345 | fd_setsize)) | ||
343 | return 0; | 346 | return 0; |
344 | MHD_SCKT_ADD_FD_TO_FDSET_SETSIZE_(fd, set, fd_setsize); | 347 | MHD_SCKT_ADD_FD_TO_FDSET_SETSIZE_(fd, |
348 | set, | ||
349 | fd_setsize); | ||
345 | if ( (NULL != max_fd) && | 350 | if ( (NULL != max_fd) && |
346 | ((fd > *max_fd) || (MHD_INVALID_SOCKET == *max_fd)) ) | 351 | ( (fd > *max_fd) || |
352 | (MHD_INVALID_SOCKET == *max_fd) ) ) | ||
347 | *max_fd = fd; | 353 | *max_fd = fd; |
348 | 354 | return ! 0; | |
349 | return !0; | ||
350 | } | 355 | } |
351 | 356 | ||
352 | 357 | ||
diff --git a/src/microhttpd/response.c b/src/microhttpd/response.c index ca729765..aa79196d 100644 --- a/src/microhttpd/response.c +++ b/src/microhttpd/response.c | |||
@@ -16,7 +16,6 @@ | |||
16 | License along with this library; if not, write to the Free Software | 16 | License along with this library; if not, write to the Free Software |
17 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | 17 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
18 | */ | 18 | */ |
19 | |||
20 | /** | 19 | /** |
21 | * @file response.c | 20 | * @file response.c |
22 | * @brief Methods for managing response objects | 21 | * @brief Methods for managing response objects |
@@ -34,6 +33,7 @@ | |||
34 | #include "connection.h" | 33 | #include "connection.h" |
35 | #include "memorypool.h" | 34 | #include "memorypool.h" |
36 | 35 | ||
36 | #include <sys/ioctl.h> | ||
37 | 37 | ||
38 | #if defined(_WIN32) && defined(MHD_W32_MUTEX_) | 38 | #if defined(_WIN32) && defined(MHD_W32_MUTEX_) |
39 | #ifndef WIN32_LEAN_AND_MEAN | 39 | #ifndef WIN32_LEAN_AND_MEAN |
@@ -202,7 +202,9 @@ MHD_get_response_headers (struct MHD_Response *response, | |||
202 | numHeaders++; | 202 | numHeaders++; |
203 | if ((NULL != iterator) && | 203 | if ((NULL != iterator) && |
204 | (MHD_YES != iterator (iterator_cls, | 204 | (MHD_YES != iterator (iterator_cls, |
205 | pos->kind, pos->header, pos->value))) | 205 | pos->kind, |
206 | pos->header, | ||
207 | pos->value))) | ||
206 | break; | 208 | break; |
207 | } | 209 | } |
208 | return numHeaders; | 210 | return numHeaders; |
@@ -337,16 +339,23 @@ file_reader (void *cls, | |||
337 | return MHD_CONTENT_READER_END_WITH_ERROR; /* seek to required position is not possible */ | 339 | return MHD_CONTENT_READER_END_WITH_ERROR; /* seek to required position is not possible */ |
338 | 340 | ||
339 | #if defined(HAVE_LSEEK64) | 341 | #if defined(HAVE_LSEEK64) |
340 | if (lseek64 (response->fd, offset64, SEEK_SET) != offset64) | 342 | if (lseek64 (response->fd, |
343 | offset64, | ||
344 | SEEK_SET) != offset64) | ||
341 | return MHD_CONTENT_READER_END_WITH_ERROR; /* can't seek to required position */ | 345 | return MHD_CONTENT_READER_END_WITH_ERROR; /* can't seek to required position */ |
342 | #elif defined(HAVE___LSEEKI64) | 346 | #elif defined(HAVE___LSEEKI64) |
343 | if (_lseeki64 (response->fd, offset64, SEEK_SET) != offset64) | 347 | if (_lseeki64 (response->fd, |
348 | offset64, | ||
349 | SEEK_SET) != offset64) | ||
344 | return MHD_CONTENT_READER_END_WITH_ERROR; /* can't seek to required position */ | 350 | return MHD_CONTENT_READER_END_WITH_ERROR; /* can't seek to required position */ |
345 | #else /* !HAVE___LSEEKI64 */ | 351 | #else /* !HAVE___LSEEKI64 */ |
346 | if (sizeof(off_t) < sizeof(uint64_t) && offset64 > (uint64_t)INT32_MAX) | 352 | if ( (sizeof(off_t) < sizeof (uint64_t)) && |
353 | (offset64 > (uint64_t)INT32_MAX) ) | ||
347 | return MHD_CONTENT_READER_END_WITH_ERROR; /* seek to required position is not possible */ | 354 | return MHD_CONTENT_READER_END_WITH_ERROR; /* seek to required position is not possible */ |
348 | 355 | ||
349 | if (lseek (response->fd, (off_t)offset64, SEEK_SET) != (off_t)offset64) | 356 | if (lseek (response->fd, |
357 | (off_t) offset64, | ||
358 | SEEK_SET) != (off_t) offset64) | ||
350 | return MHD_CONTENT_READER_END_WITH_ERROR; /* can't seek to required position */ | 359 | return MHD_CONTENT_READER_END_WITH_ERROR; /* can't seek to required position */ |
351 | #endif | 360 | #endif |
352 | 361 | ||
@@ -354,12 +363,16 @@ file_reader (void *cls, | |||
354 | if (max > SSIZE_MAX) | 363 | if (max > SSIZE_MAX) |
355 | max = SSIZE_MAX; | 364 | max = SSIZE_MAX; |
356 | 365 | ||
357 | n = read (response->fd, buf, max); | 366 | n = read (response->fd, |
367 | buf, | ||
368 | max); | ||
358 | #else /* _WIN32 */ | 369 | #else /* _WIN32 */ |
359 | if (max > INT32_MAX) | 370 | if (max > INT32_MAX) |
360 | max = INT32_MAX; | 371 | max = INT32_MAX; |
361 | 372 | ||
362 | n = read (response->fd, buf, (unsigned int)max); | 373 | n = read (response->fd, |
374 | buf, | ||
375 | (unsigned int)max); | ||
363 | #endif /* _WIN32 */ | 376 | #endif /* _WIN32 */ |
364 | 377 | ||
365 | if (0 == n) | 378 | if (0 == n) |
@@ -438,8 +451,10 @@ MHD_create_response_from_fd_at_offset64 (uint64_t size, | |||
438 | struct MHD_Response *response; | 451 | struct MHD_Response *response; |
439 | 452 | ||
440 | #if !defined(HAVE___LSEEKI64) && !defined(HAVE_LSEEK64) | 453 | #if !defined(HAVE___LSEEKI64) && !defined(HAVE_LSEEK64) |
441 | if (sizeof(uint64_t) > sizeof(off_t) && | 454 | if ( (sizeof(uint64_t) > sizeof(off_t)) && |
442 | (size > (uint64_t)INT32_MAX || offset > (uint64_t)INT32_MAX || (size + offset) >= (uint64_t)INT32_MAX)) | 455 | ( (size > (uint64_t)INT32_MAX) || |
456 | (offset > (uint64_t)INT32_MAX) || | ||
457 | ((size + offset) >= (uint64_t)INT32_MAX) ) ) | ||
443 | return NULL; | 458 | return NULL; |
444 | #endif | 459 | #endif |
445 | if ( ((int64_t)size < 0) || | 460 | if ( ((int64_t)size < 0) || |
@@ -530,7 +545,9 @@ MHD_create_response_from_data (size_t size, | |||
530 | return NULL; | 545 | return NULL; |
531 | if (NULL == (response = malloc (sizeof (struct MHD_Response)))) | 546 | if (NULL == (response = malloc (sizeof (struct MHD_Response)))) |
532 | return NULL; | 547 | return NULL; |
533 | memset (response, 0, sizeof (struct MHD_Response)); | 548 | memset (response, |
549 | 0, | ||
550 | sizeof (struct MHD_Response)); | ||
534 | response->fd = -1; | 551 | response->fd = -1; |
535 | if (! MHD_mutex_init_ (&response->mutex)) | 552 | if (! MHD_mutex_init_ (&response->mutex)) |
536 | { | 553 | { |
@@ -675,8 +692,30 @@ MHD_upgrade_action (struct MHD_UpgradeResponseHandle *urh, | |||
675 | /* FIXME: not implemented */ | 692 | /* FIXME: not implemented */ |
676 | return MHD_NO; | 693 | return MHD_NO; |
677 | case MHD_UPGRADE_ACTION_FLUSH: | 694 | case MHD_UPGRADE_ACTION_FLUSH: |
678 | /* FIXME: not implemented */ | 695 | #if HTTPS_SUPPORT |
679 | return MHD_NO; | 696 | if (0 != (daemon->options & MHD_USE_SSL)) |
697 | { | ||
698 | int avail; | ||
699 | |||
700 | /* First, check that our pipe is empty, to be sure we do | ||
701 | have it all in the buffer. */ | ||
702 | if ( (0 == | ||
703 | #if WINDOWS | ||
704 | ioctlsocket | ||
705 | #else | ||
706 | ioctl | ||
707 | #endif | ||
708 | (urh->mhd.socket, | ||
709 | FIONREAD, | ||
710 | &avail)) && | ||
711 | (0 != avail) ) | ||
712 | return MHD_NO; | ||
713 | /* then, refuse 'flush' unless our buffer is empty */ | ||
714 | if (0 != urh->out_buffer_off) | ||
715 | return MHD_NO; | ||
716 | } | ||
717 | #endif | ||
718 | return MHD_YES; | ||
680 | default: | 719 | default: |
681 | /* we don't understand this one */ | 720 | /* we don't understand this one */ |
682 | return MHD_NO; | 721 | return MHD_NO; |
diff --git a/src/microhttpd/test_upgrade.c b/src/microhttpd/test_upgrade.c index 2288d927..469dc581 100644 --- a/src/microhttpd/test_upgrade.c +++ b/src/microhttpd/test_upgrade.c | |||
@@ -298,7 +298,9 @@ test_upgrade_internal_select () | |||
298 | sa.sin_family = AF_INET; | 298 | sa.sin_family = AF_INET; |
299 | sa.sin_port = htons (1080); | 299 | sa.sin_port = htons (1080); |
300 | sa.sin_addr.s_addr = htonl (INADDR_LOOPBACK); | 300 | sa.sin_addr.s_addr = htonl (INADDR_LOOPBACK); |
301 | if (0 != connect (sock, (struct sockaddr *) &sa, sizeof (sa))) | 301 | if (0 != connect (sock, |
302 | (struct sockaddr *) &sa, | ||
303 | sizeof (sa))) | ||
302 | abort (); | 304 | abort (); |
303 | send_all (sock, | 305 | send_all (sock, |
304 | "GET / HTTP/1.1\r\nConnection: Upgrade\r\n\r\n"); | 306 | "GET / HTTP/1.1\r\nConnection: Upgrade\r\n\r\n"); |
@@ -316,12 +318,15 @@ test_upgrade_internal_select () | |||
316 | 318 | ||
317 | 319 | ||
318 | int | 320 | int |
319 | main (int argc, char *const *argv) | 321 | main (int argc, |
322 | char *const *argv) | ||
320 | { | 323 | { |
321 | int errorCount = 0; | 324 | int errorCount = 0; |
322 | 325 | ||
323 | errorCount += test_upgrade_internal_select (); | 326 | errorCount += test_upgrade_internal_select (); |
324 | if (errorCount != 0) | 327 | if (errorCount != 0) |
325 | fprintf (stderr, "Error (code: %u)\n", errorCount); | 328 | fprintf (stderr, |
329 | "Error (code: %u)\n", | ||
330 | errorCount); | ||
326 | return errorCount != 0; /* 0 == pass */ | 331 | return errorCount != 0; /* 0 == pass */ |
327 | } | 332 | } |
diff --git a/src/microhttpd/test_upgrade_ssl.c b/src/microhttpd/test_upgrade_ssl.c new file mode 100644 index 00000000..5c663dd9 --- /dev/null +++ b/src/microhttpd/test_upgrade_ssl.c | |||
@@ -0,0 +1,472 @@ | |||
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_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 | |||
33 | #ifndef WINDOWS | ||
34 | #include <unistd.h> | ||
35 | #endif | ||
36 | |||
37 | #include <sys/socket.h> | ||
38 | #include <sys/wait.h> | ||
39 | #include <netinet/in.h> | ||
40 | #include <netinet/ip.h> | ||
41 | #include <pthread.h> | ||
42 | #include "mhd_sockets.h" | ||
43 | |||
44 | #include "../testcurl/https/tls_test_keys.h" | ||
45 | |||
46 | |||
47 | /** | ||
48 | * Thread we use to run the interaction with the upgraded socket. | ||
49 | */ | ||
50 | static pthread_t pt; | ||
51 | |||
52 | /** | ||
53 | * Will be set to the upgraded socket. | ||
54 | */ | ||
55 | static MHD_socket usock; | ||
56 | |||
57 | /** | ||
58 | * Fork child that connects via OpenSSL to our @a port. Allows us to | ||
59 | * talk to our port over a socket in @a sp without having to worry | ||
60 | * about TLS. | ||
61 | * | ||
62 | * @param location where the socket is returned | ||
63 | * @return -1 on error, otherwise PID of SSL child process | ||
64 | */ | ||
65 | static pid_t | ||
66 | openssl_connect (int *sock, | ||
67 | uint16_t port) | ||
68 | { | ||
69 | pid_t chld; | ||
70 | int sp[2]; | ||
71 | char destination[30]; | ||
72 | |||
73 | if (0 != socketpair (AF_UNIX, | ||
74 | SOCK_STREAM, | ||
75 | 0, | ||
76 | sp)) | ||
77 | return -1; | ||
78 | chld = fork (); | ||
79 | if (0 != chld) | ||
80 | { | ||
81 | *sock = sp[1]; | ||
82 | MHD_socket_close_ (sp[0]); | ||
83 | return chld; | ||
84 | } | ||
85 | MHD_socket_close_ (sp[1]); | ||
86 | (void) close (0); | ||
87 | (void) close (1); | ||
88 | dup2 (sp[0], 0); | ||
89 | dup2 (sp[0], 1); | ||
90 | close (sp[0]); | ||
91 | sprintf (destination, | ||
92 | "localhost:%u", | ||
93 | (unsigned int) port); | ||
94 | execlp ("openssl", | ||
95 | "openssl", | ||
96 | "s_client", | ||
97 | "-connect", | ||
98 | destination, | ||
99 | "-verify", | ||
100 | "0", | ||
101 | // "-quiet", | ||
102 | (char *) NULL); | ||
103 | _exit (1); | ||
104 | } | ||
105 | |||
106 | |||
107 | /** | ||
108 | * Change itc FD options to be non-blocking. | ||
109 | * | ||
110 | * @param fd the FD to manipulate | ||
111 | * @return non-zero if succeeded, zero otherwise | ||
112 | */ | ||
113 | static void | ||
114 | make_blocking (MHD_socket fd) | ||
115 | { | ||
116 | int flags; | ||
117 | |||
118 | flags = fcntl (fd, F_GETFL); | ||
119 | if (-1 == flags) | ||
120 | return; | ||
121 | if ((flags & ~O_NONBLOCK) != flags) | ||
122 | fcntl (fd, F_SETFL, flags & ~O_NONBLOCK); | ||
123 | } | ||
124 | |||
125 | |||
126 | static void | ||
127 | send_all (MHD_socket sock, | ||
128 | const char *text) | ||
129 | { | ||
130 | size_t len = strlen (text); | ||
131 | ssize_t ret; | ||
132 | |||
133 | make_blocking (sock); | ||
134 | for (size_t off = 0; off < len; off += ret) | ||
135 | { | ||
136 | ret = write (sock, | ||
137 | &text[off], | ||
138 | len - off); | ||
139 | if (-1 == ret) | ||
140 | { | ||
141 | if (EAGAIN == errno) | ||
142 | { | ||
143 | ret = 0; | ||
144 | continue; | ||
145 | } | ||
146 | abort (); | ||
147 | } | ||
148 | } | ||
149 | } | ||
150 | |||
151 | |||
152 | /** | ||
153 | * Read character-by-character until we | ||
154 | * get '\r\n\r\n'. | ||
155 | */ | ||
156 | static void | ||
157 | recv_hdr (MHD_socket sock) | ||
158 | { | ||
159 | unsigned int i; | ||
160 | char next; | ||
161 | char c; | ||
162 | ssize_t ret; | ||
163 | |||
164 | make_blocking (sock); | ||
165 | next = '\r'; | ||
166 | i = 0; | ||
167 | while (i < 4) | ||
168 | { | ||
169 | ret = read (sock, | ||
170 | &c, | ||
171 | 1); | ||
172 | if (0 == ret) | ||
173 | abort (); /* this is fatal */ | ||
174 | if (-1 == ret) | ||
175 | { | ||
176 | if (EAGAIN == errno) | ||
177 | { | ||
178 | ret = 0; | ||
179 | continue; | ||
180 | } | ||
181 | abort (); | ||
182 | } | ||
183 | if (0 == ret) | ||
184 | continue; | ||
185 | if (c == next) | ||
186 | { | ||
187 | i++; | ||
188 | if (next == '\r') | ||
189 | next = '\n'; | ||
190 | else | ||
191 | next = '\r'; | ||
192 | continue; | ||
193 | } | ||
194 | if (c == '\r') | ||
195 | { | ||
196 | i = 1; | ||
197 | next = '\n'; | ||
198 | continue; | ||
199 | } | ||
200 | i = 0; | ||
201 | next = '\r'; | ||
202 | } | ||
203 | } | ||
204 | |||
205 | |||
206 | static void | ||
207 | recv_all (MHD_socket sock, | ||
208 | const char *text) | ||
209 | { | ||
210 | size_t len = strlen (text); | ||
211 | char buf[len]; | ||
212 | ssize_t ret; | ||
213 | |||
214 | make_blocking (sock); | ||
215 | for (size_t off = 0; off < len; off += ret) | ||
216 | { | ||
217 | ret = read (sock, | ||
218 | &buf[off], | ||
219 | len - off); | ||
220 | if (0 == ret) | ||
221 | abort (); /* this is fatal */ | ||
222 | if (-1 == ret) | ||
223 | { | ||
224 | if (EAGAIN == errno) | ||
225 | { | ||
226 | ret = 0; | ||
227 | continue; | ||
228 | } | ||
229 | abort (); | ||
230 | } | ||
231 | } | ||
232 | if (0 != strncmp (text, buf, len)) | ||
233 | abort(); | ||
234 | } | ||
235 | |||
236 | |||
237 | /** | ||
238 | * Main function for the thread that runs the interaction with | ||
239 | * the upgraded socket. | ||
240 | * | ||
241 | * @param cls the handle for the upgrade | ||
242 | */ | ||
243 | static void * | ||
244 | run_usock (void *cls) | ||
245 | { | ||
246 | struct MHD_UpgradeResponseHandle *urh = cls; | ||
247 | |||
248 | fprintf (stderr, | ||
249 | "Sending `Hello'\n"); | ||
250 | send_all (usock, | ||
251 | "Hello"); | ||
252 | fprintf (stderr, | ||
253 | "Receiving `World'\n"); | ||
254 | recv_all (usock, | ||
255 | "World"); | ||
256 | fprintf (stderr, | ||
257 | "Sending `Finished'\n"); | ||
258 | send_all (usock, | ||
259 | "Finished"); | ||
260 | fprintf (stderr, | ||
261 | "Closing socket\n"); | ||
262 | while (MHD_NO == | ||
263 | MHD_upgrade_action (urh, | ||
264 | MHD_UPGRADE_ACTION_FLUSH)) | ||
265 | usleep (1000); | ||
266 | MHD_upgrade_action (urh, | ||
267 | MHD_UPGRADE_ACTION_CLOSE); | ||
268 | fprintf (stderr, | ||
269 | "Thread terminating\n"); | ||
270 | return NULL; | ||
271 | } | ||
272 | |||
273 | |||
274 | /** | ||
275 | * Function called after a protocol "upgrade" response was sent | ||
276 | * successfully and the socket should now be controlled by some | ||
277 | * protocol other than HTTP. | ||
278 | * | ||
279 | * Any data received on the socket will be made available in | ||
280 | * 'data_in'. The function should update 'data_in_size' to | ||
281 | * reflect the number of bytes consumed from 'data_in' (the remaining | ||
282 | * bytes will be made available in the next call to the handler). | ||
283 | * | ||
284 | * Any data that should be transmitted on the socket should be | ||
285 | * stored in 'data_out'. '*data_out_size' is initially set to | ||
286 | * the available buffer space in 'data_out'. It should be set to | ||
287 | * the number of bytes stored in 'data_out' (which can be zero). | ||
288 | * | ||
289 | * The return value is a BITMASK that indicates how the function | ||
290 | * intends to interact with the event loop. It can request to be | ||
291 | * notified for reading, writing, request to UNCORK the send buffer | ||
292 | * (which MHD is allowed to ignore, if it is not possible to uncork on | ||
293 | * the local platform), to wait for the 'external' select loop to | ||
294 | * trigger another round. It is also possible to specify "no events" | ||
295 | * to terminate the connection; in this case, the | ||
296 | * #MHD_RequestCompletedCallback will be called and all resources of | ||
297 | * the connection will be released. | ||
298 | * | ||
299 | * Except when in 'thread-per-connection' mode, implementations | ||
300 | * of this function should never block (as it will still be called | ||
301 | * from within the main event loop). | ||
302 | * | ||
303 | * @param cls closure, whatever was given to #MHD_create_response_for_upgrade(). | ||
304 | * @param connection original HTTP connection handle, | ||
305 | * giving the function a last chance | ||
306 | * to inspect the original HTTP request | ||
307 | * @param con_cls last value left in `*con_cls` in the `MHD_AccessHandlerCallback` | ||
308 | * @param extra_in if we happened to have read bytes after the | ||
309 | * HTTP header already (because the client sent | ||
310 | * more than the HTTP header of the request before | ||
311 | * we sent the upgrade response), | ||
312 | * these are the extra bytes already read from @a sock | ||
313 | * by MHD. The application should treat these as if | ||
314 | * it had read them from @a sock. | ||
315 | * @param extra_in_size number of bytes in @a extra_in | ||
316 | * @param sock socket to use for bi-directional communication | ||
317 | * with the client. For HTTPS, this may not be a socket | ||
318 | * that is directly connected to the client and thus certain | ||
319 | * operations (TCP-specific setsockopt(), getsockopt(), etc.) | ||
320 | * may not work as expected (as the socket could be from a | ||
321 | * socketpair() or a TCP-loopback) | ||
322 | * @param urh argument for #MHD_upgrade_action()s on this @a connection. | ||
323 | * Applications must eventually use this function to perform the | ||
324 | * close() action on the @a sock. | ||
325 | */ | ||
326 | static void | ||
327 | upgrade_cb (void *cls, | ||
328 | struct MHD_Connection *connection, | ||
329 | void *con_cls, | ||
330 | const char *extra_in, | ||
331 | size_t extra_in_size, | ||
332 | MHD_socket sock, | ||
333 | struct MHD_UpgradeResponseHandle *urh) | ||
334 | { | ||
335 | usock = sock; | ||
336 | if (0 != extra_in_size) | ||
337 | abort (); | ||
338 | pthread_create (&pt, | ||
339 | NULL, | ||
340 | &run_usock, | ||
341 | urh); | ||
342 | } | ||
343 | |||
344 | |||
345 | /** | ||
346 | * A client has requested the given url using the given method | ||
347 | * (#MHD_HTTP_METHOD_GET, #MHD_HTTP_METHOD_PUT, | ||
348 | * #MHD_HTTP_METHOD_DELETE, #MHD_HTTP_METHOD_POST, etc). The callback | ||
349 | * must call MHD callbacks to provide content to give back to the | ||
350 | * client and return an HTTP status code (i.e. #MHD_HTTP_OK, | ||
351 | * #MHD_HTTP_NOT_FOUND, etc.). | ||
352 | * | ||
353 | * @param cls argument given together with the function | ||
354 | * pointer when the handler was registered with MHD | ||
355 | * @param url the requested url | ||
356 | * @param method the HTTP method used (#MHD_HTTP_METHOD_GET, | ||
357 | * #MHD_HTTP_METHOD_PUT, etc.) | ||
358 | * @param version the HTTP version string (i.e. | ||
359 | * #MHD_HTTP_VERSION_1_1) | ||
360 | * @param upload_data the data being uploaded (excluding HEADERS, | ||
361 | * for a POST that fits into memory and that is encoded | ||
362 | * with a supported encoding, the POST data will NOT be | ||
363 | * given in upload_data and is instead available as | ||
364 | * part of #MHD_get_connection_values; very large POST | ||
365 | * data *will* be made available incrementally in | ||
366 | * @a upload_data) | ||
367 | * @param upload_data_size set initially to the size of the | ||
368 | * @a upload_data provided; the method must update this | ||
369 | * value to the number of bytes NOT processed; | ||
370 | * @param con_cls pointer that the callback can set to some | ||
371 | * address and that will be preserved by MHD for future | ||
372 | * calls for this request; since the access handler may | ||
373 | * be called many times (i.e., for a PUT/POST operation | ||
374 | * with plenty of upload data) this allows the application | ||
375 | * to easily associate some request-specific state. | ||
376 | * If necessary, this state can be cleaned up in the | ||
377 | * global #MHD_RequestCompletedCallback (which | ||
378 | * can be set with the #MHD_OPTION_NOTIFY_COMPLETED). | ||
379 | * Initially, `*con_cls` will be NULL. | ||
380 | * @return #MHD_YES if the connection was handled successfully, | ||
381 | * #MHD_NO if the socket must be closed due to a serios | ||
382 | * error while handling the request | ||
383 | */ | ||
384 | static int | ||
385 | ahc_upgrade (void *cls, | ||
386 | struct MHD_Connection *connection, | ||
387 | const char *url, | ||
388 | const char *method, | ||
389 | const char *version, | ||
390 | const char *upload_data, | ||
391 | size_t *upload_data_size, | ||
392 | void **con_cls) | ||
393 | { | ||
394 | struct MHD_Response *resp; | ||
395 | int ret; | ||
396 | |||
397 | resp = MHD_create_response_for_upgrade (&upgrade_cb, | ||
398 | NULL); | ||
399 | MHD_add_response_header (resp, | ||
400 | MHD_HTTP_HEADER_UPGRADE, | ||
401 | "Hello World Protocol"); | ||
402 | ret = MHD_queue_response (connection, | ||
403 | MHD_HTTP_SWITCHING_PROTOCOLS, | ||
404 | resp); | ||
405 | MHD_destroy_response (resp); | ||
406 | return ret; | ||
407 | } | ||
408 | |||
409 | |||
410 | static int | ||
411 | test_upgrade_internal_select () | ||
412 | { | ||
413 | struct MHD_Daemon *d; | ||
414 | MHD_socket sock; | ||
415 | pid_t pid; | ||
416 | |||
417 | d = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY | MHD_USE_DEBUG | MHD_USE_SUSPEND_RESUME | MHD_USE_TLS, | ||
418 | 1080, | ||
419 | NULL, NULL, | ||
420 | &ahc_upgrade, NULL, | ||
421 | MHD_OPTION_HTTPS_MEM_KEY, srv_signed_key_pem, | ||
422 | MHD_OPTION_HTTPS_MEM_CERT, srv_signed_cert_pem, | ||
423 | MHD_OPTION_END); | ||
424 | if (NULL == d) | ||
425 | return 2; | ||
426 | if (-1 == (pid = openssl_connect (&sock, 1080))) | ||
427 | { | ||
428 | MHD_stop_daemon (d); | ||
429 | return 4; | ||
430 | } | ||
431 | |||
432 | send_all (sock, | ||
433 | "GET / HTTP/1.1\r\nConnection: Upgrade\r\n\r\n"); | ||
434 | recv_hdr (sock); | ||
435 | recv_all (sock, | ||
436 | "Hello"); | ||
437 | fprintf (stderr, | ||
438 | "Received `Hello'\n"); | ||
439 | send_all (sock, | ||
440 | "World"); | ||
441 | fprintf (stderr, | ||
442 | "Sent `World'\n"); | ||
443 | recv_all (sock, | ||
444 | "Finished"); | ||
445 | fprintf (stderr, | ||
446 | "Received `Finished'\n"); | ||
447 | MHD_socket_close_ (sock); | ||
448 | pthread_join (pt, | ||
449 | NULL); | ||
450 | fprintf (stderr, | ||
451 | "Joined helper thread\n"); | ||
452 | waitpid (pid, NULL, 0); | ||
453 | MHD_stop_daemon (d); | ||
454 | return 0; | ||
455 | } | ||
456 | |||
457 | |||
458 | int | ||
459 | main (int argc, | ||
460 | char *const *argv) | ||
461 | { | ||
462 | int errorCount = 0; | ||
463 | |||
464 | if (0 != system ("openssl version 1> /dev/null")) | ||
465 | return 77; /* openssl not available, can't run the test */ | ||
466 | errorCount += test_upgrade_internal_select (); | ||
467 | if (errorCount != 0) | ||
468 | fprintf (stderr, | ||
469 | "Error (code: %u)\n", | ||
470 | errorCount); | ||
471 | return errorCount != 0; /* 0 == pass */ | ||
472 | } | ||