aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristian Grothoff <christian@grothoff.org>2016-09-03 22:32:29 +0000
committerChristian Grothoff <christian@grothoff.org>2016-09-03 22:32:29 +0000
commit352a07b01018a4379f4fd83b8277730f044afede (patch)
treee2e7624f083b3ae8a9adee3f9c36e216bc2a885e
parent6fa19ae05f226451db78316919908c44d6444ac4 (diff)
downloadlibmicrohttpd-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.am9
-rw-r--r--src/microhttpd/connection.c29
-rw-r--r--src/microhttpd/daemon.c26
-rw-r--r--src/microhttpd/mhd_sockets.c17
-rw-r--r--src/microhttpd/response.c65
-rw-r--r--src/microhttpd/test_upgrade.c11
-rw-r--r--src/microhttpd/test_upgrade_ssl.c472
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
149if ENABLE_HTTPS
150 check_PROGRAMS += test_upgrade_ssl
151endif
152
149if HAVE_POSTPROCESSOR 153if HAVE_POSTPROCESSOR
150check_PROGRAMS += \ 154check_PROGRAMS += \
151 test_postprocessor \ 155 test_postprocessor \
@@ -171,6 +175,11 @@ test_upgrade_SOURCES = \
171test_upgrade_LDADD = \ 175test_upgrade_LDADD = \
172 $(top_builddir)/src/microhttpd/libmicrohttpd.la 176 $(top_builddir)/src/microhttpd/libmicrohttpd.la
173 177
178test_upgrade_ssl_SOURCES = \
179 test_upgrade_ssl.c
180test_upgrade_ssl_LDADD = \
181 $(top_builddir)/src/microhttpd/libmicrohttpd.la
182
174test_postprocessor_SOURCES = \ 183test_postprocessor_SOURCES = \
175 test_postprocessor.c 184 test_postprocessor.c
176test_postprocessor_CPPFLAGS = \ 185test_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
318int 320int
319main (int argc, char *const *argv) 321main (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 */
50static pthread_t pt;
51
52/**
53 * Will be set to the upgraded socket.
54 */
55static 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 */
65static pid_t
66openssl_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 */
113static void
114make_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
126static void
127send_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 */
156static void
157recv_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
206static void
207recv_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 */
243static void *
244run_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 */
326static void
327upgrade_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 */
384static int
385ahc_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
410static int
411test_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
458int
459main (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}