libmicrohttpd

HTTP/1.x server C library (MHD 1.x, stable)
Log | Files | Refs | Submodules | README | LICENSE

commit 40bf201dc53465be1d2805039ef5963a21e44c08
parent 1aaff72582b6093f0dcf8c187d125e5fca2d2e8b
Author: Christian Grothoff <christian@grothoff.org>
Date:   Mon, 29 Jul 2019 19:23:35 +0200

indentation fixes, adding test_upgrade_large.c test for issue reported by Viet

Diffstat:
Msrc/microhttpd/.gitignore | 2++
Msrc/microhttpd/Makefile.am | 34++++++++++++++++++++++++++++++----
Msrc/microhttpd/daemon.c | 167++++++++++++++++++++++++++++++++++++++++---------------------------------------
Msrc/microhttpd/response.c | 88++++++++++++++++++++++++++++++++++++++++----------------------------------------
Asrc/microhttpd/test_upgrade_large.c | 1353+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
5 files changed, 1513 insertions(+), 131 deletions(-)

diff --git a/src/microhttpd/.gitignore b/src/microhttpd/.gitignore @@ -57,3 +57,5 @@ test_shutdown_poll test_shutdown_select test_md5 test_sha256 +test_upgrade_large +test_upgrade_large_tls diff --git a/src/microhttpd/Makefile.am b/src/microhttpd/Makefile.am @@ -165,17 +165,17 @@ check_PROGRAMS = \ if HAVE_POSIX_THREADS if ENABLE_UPGRADE if USE_POSIX_THREADS - check_PROGRAMS += test_upgrade + check_PROGRAMS += test_upgrade test_upgrade_large endif if USE_W32_THREADS - check_PROGRAMS += test_upgrade + check_PROGRAMS += test_upgrade test_upgrade_large endif if ENABLE_HTTPS if USE_POSIX_THREADS -check_PROGRAMS += test_upgrade_tls +check_PROGRAMS += test_upgrade_tls test_upgrade_large_tls endif if USE_W32_THREADS -check_PROGRAMS += test_upgrade_tls +check_PROGRAMS += test_upgrade_tls test_upgrade_large_tls endif endif endif @@ -230,6 +230,19 @@ test_upgrade_LDADD = \ $(MHD_TLS_LIB_LDFLAGS) $(MHD_TLS_LIBDEPS) \ $(PTHREAD_LIBS) +test_upgrade_large_SOURCES = \ + test_upgrade_large.c test_helpers.h mhd_sockets.h +test_upgrade_large_CPPFLAGS = \ + $(AM_CPPFLAGS) $(MHD_TLS_LIB_CPPFLAGS) +test_upgrade_large_CFLAGS = \ + $(AM_CFLAGS) $(PTHREAD_CFLAGS) $(MHD_TLS_LIB_CFLAGS) +test_upgrade_large_LDFLAGS = \ + $(MHD_TLS_LIB_LDFLAGS) +test_upgrade_large_LDADD = \ + $(top_builddir)/src/microhttpd/libmicrohttpd.la \ + $(MHD_TLS_LIB_LDFLAGS) $(MHD_TLS_LIBDEPS) \ + $(PTHREAD_LIBS) + test_upgrade_tls_SOURCES = \ test_upgrade.c test_helpers.h mhd_sockets.h test_upgrade_tls_CPPFLAGS = \ @@ -243,6 +256,19 @@ test_upgrade_tls_LDADD = \ $(MHD_TLS_LIB_LDFLAGS) $(MHD_TLS_LIBDEPS) \ $(PTHREAD_LIBS) +test_upgrade_large_tls_SOURCES = \ + test_upgrade_large.c test_helpers.h mhd_sockets.h +test_upgrade_large_tls_CPPFLAGS = \ + $(AM_CPPFLAGS) $(MHD_TLS_LIB_CPPFLAGS) +test_upgrade_large_tls_CFLAGS = \ + $(AM_CFLAGS) $(PTHREAD_CFLAGS) $(MHD_TLS_LIB_CFLAGS) +test_upgrade_large_tls_LDFLAGS = \ + $(MHD_TLS_LIB_LDFLAGS) +test_upgrade_large_tls_LDADD = \ + $(top_builddir)/src/microhttpd/libmicrohttpd.la \ + $(MHD_TLS_LIB_LDFLAGS) $(MHD_TLS_LIBDEPS) \ + $(PTHREAD_LIBS) + test_postprocessor_SOURCES = \ test_postprocessor.c test_postprocessor_CPPFLAGS = \ diff --git a/src/microhttpd/daemon.c b/src/microhttpd/daemon.c @@ -4178,28 +4178,29 @@ run_epoll_for_upgrade (struct MHD_Daemon *daemon) struct MHD_UpgradeResponseHandle * prev; num_events = MAX_EVENTS; - while (MAX_EVENTS == num_events) + while (0 != num_events) { unsigned int i; /* update event masks */ num_events = epoll_wait (daemon->epoll_upgrade_fd, - events, + events, MAX_EVENTS, 0); if (-1 == num_events) - { + { const int err = MHD_socket_get_error_ (); + if (MHD_SCKT_ERR_IS_EINTR_ (err)) - return MHD_YES; + return MHD_YES; #ifdef HAVE_MESSAGES MHD_DLOG (daemon, _("Call to epoll_wait failed: %s\n"), MHD_socket_strerr_ (err)); #endif - return MHD_NO; - } + return MHD_NO; + } for (i = 0; i < (unsigned int) num_events; i++) - { + { struct UpgradeEpollHandle * const ueh = events[i].data.ptr; struct MHD_UpgradeResponseHandle * const urh = ueh->urh; bool new_err_state = false; @@ -4217,24 +4218,24 @@ run_epoll_for_upgrade (struct MHD_Daemon *daemon) if ( (0 == (ueh->celi & MHD_EPOLL_STATE_ERROR)) && (0 != (events[i].events & (EPOLLERR | EPOLLPRI))) ) - { + { /* Process new error state only one time * and avoid continuously marking this connection * as 'ready'. */ ueh->celi |= MHD_EPOLL_STATE_ERROR; new_err_state = true; - } + } if (! urh->in_eready_list) { if (new_err_state || - is_urh_ready(urh)) - { - EDLL_insert (daemon->eready_urh_head, - daemon->eready_urh_tail, - urh); - urh->in_eready_list = true; - } + is_urh_ready(urh)) + { + EDLL_insert (daemon->eready_urh_head, + daemon->eready_urh_tail, + urh); + urh->in_eready_list = true; + } } } } @@ -4246,8 +4247,8 @@ run_epoll_for_upgrade (struct MHD_Daemon *daemon) if (! is_urh_ready(pos)) { EDLL_remove (daemon->eready_urh_head, - daemon->eready_urh_tail, - pos); + daemon->eready_urh_tail, + pos); pos->in_eready_list = false; } /* Finished forwarding? */ @@ -4287,7 +4288,7 @@ static const char * const epoll_itc_marker = "itc_marker"; */ static int MHD_epoll (struct MHD_Daemon *daemon, - int may_block) + int may_block) { #if defined(HTTPS_SUPPORT) && defined(UPGRADE_SUPPORT) static const char * const upgrade_marker = "upgrade_ptr"; @@ -4302,7 +4303,7 @@ MHD_epoll (struct MHD_Daemon *daemon, unsigned int i; MHD_socket ls; #if defined(HTTPS_SUPPORT) && defined(UPGRADE_SUPPORT) - int run_upgraded = MHD_NO; + bool run_upgraded = false; #endif /* HTTPS_SUPPORT && UPGRADE_SUPPORT */ if (-1 == daemon->epoll_fd) @@ -4318,31 +4319,31 @@ MHD_epoll (struct MHD_Daemon *daemon, event.events = EPOLLIN; event.data.ptr = daemon; if (0 != epoll_ctl (daemon->epoll_fd, - EPOLL_CTL_ADD, - ls, - &event)) - { + EPOLL_CTL_ADD, + ls, + &event)) + { #ifdef HAVE_MESSAGES MHD_DLOG (daemon, _("Call to epoll_ctl failed: %s\n"), MHD_socket_last_strerr_ ()); #endif - return MHD_NO; - } + return MHD_NO; + } daemon->listen_socket_in_epoll = true; } if ( (daemon->was_quiesced) && (daemon->listen_socket_in_epoll) ) - { - if ( (0 != epoll_ctl (daemon->epoll_fd, - EPOLL_CTL_DEL, - ls, - NULL)) && - (ENOENT != errno) ) /* ENOENT can happen due to race with - #MHD_quiesce_daemon() */ - MHD_PANIC ("Failed to remove listen FD from epoll set\n"); - daemon->listen_socket_in_epoll = false; - } + { + if ( (0 != epoll_ctl (daemon->epoll_fd, + EPOLL_CTL_DEL, + ls, + NULL)) && + (ENOENT != errno) ) /* ENOENT can happen due to race with + #MHD_quiesce_daemon() */ + MHD_PANIC ("Failed to remove listen FD from epoll set\n"); + daemon->listen_socket_in_epoll = false; + } #if defined(HTTPS_SUPPORT) && defined(UPGRADE_SUPPORT) if ( (! daemon->upgrade_fd_in_epoll) && @@ -4351,17 +4352,17 @@ MHD_epoll (struct MHD_Daemon *daemon, event.events = EPOLLIN | EPOLLOUT; event.data.ptr = (void *) upgrade_marker; if (0 != epoll_ctl (daemon->epoll_fd, - EPOLL_CTL_ADD, - daemon->epoll_upgrade_fd, - &event)) - { + EPOLL_CTL_ADD, + daemon->epoll_upgrade_fd, + &event)) + { #ifdef HAVE_MESSAGES MHD_DLOG (daemon, _("Call to epoll_ctl failed: %s\n"), MHD_socket_last_strerr_ ()); #endif - return MHD_NO; - } + return MHD_NO; + } daemon->upgrade_fd_in_epoll = true; } #endif /* HTTPS_SUPPORT && UPGRADE_SUPPORT */ @@ -4373,10 +4374,10 @@ MHD_epoll (struct MHD_Daemon *daemon, /* we're at the connection limit, disable listen socket for event loop for now */ if (0 != epoll_ctl (daemon->epoll_fd, - EPOLL_CTL_DEL, - ls, - NULL)) - MHD_PANIC (_("Failed to remove listen FD from epoll set\n")); + EPOLL_CTL_DEL, + ls, + NULL)) + MHD_PANIC (_("Failed to remove listen FD from epoll set\n")); daemon->listen_socket_in_epoll = false; } @@ -4388,14 +4389,14 @@ MHD_epoll (struct MHD_Daemon *daemon, { if (MHD_YES == MHD_get_timeout (daemon, &timeout_ll)) - { - if (timeout_ll >= (MHD_UNSIGNED_LONG_LONG) INT_MAX) - timeout_ms = INT_MAX; - else - timeout_ms = (int) timeout_ll; - } + { + if (timeout_ll >= (MHD_UNSIGNED_LONG_LONG) INT_MAX) + timeout_ms = INT_MAX; + else + timeout_ms = (int) timeout_ll; + } else - timeout_ms = -1; + timeout_ms = -1; } else timeout_ms = 0; @@ -4414,33 +4415,33 @@ MHD_epoll (struct MHD_Daemon *daemon, { /* update event masks */ num_events = epoll_wait (daemon->epoll_fd, - events, + events, MAX_EVENTS, timeout_ms); if (-1 == num_events) - { + { const int err = MHD_socket_get_error_ (); if (MHD_SCKT_ERR_IS_EINTR_ (err)) - return MHD_YES; + return MHD_YES; #ifdef HAVE_MESSAGES MHD_DLOG (daemon, _("Call to epoll_wait failed: %s\n"), MHD_socket_strerr_ (err)); #endif - return MHD_NO; - } + return MHD_NO; + } for (i=0;i<(unsigned int) num_events;i++) - { + { /* First, check for the values of `ptr` that would indicate that this event is not about a normal connection. */ - if (NULL == events[i].data.ptr) - continue; /* shutdown signal! */ + if (NULL == events[i].data.ptr) + continue; /* shutdown signal! */ #if defined(HTTPS_SUPPORT) && defined(UPGRADE_SUPPORT) if (upgrade_marker == events[i].data.ptr) { /* activity on an upgraded connection, we process those in a separate epoll() */ - run_upgraded = MHD_YES; + run_upgraded = true; continue; } #endif /* HTTPS_SUPPORT && UPGRADE_SUPPORT */ @@ -4451,8 +4452,8 @@ MHD_epoll (struct MHD_Daemon *daemon, MHD_itc_clear_ (daemon->itc); continue; } - if (daemon == events[i].data.ptr) - { + if (daemon == events[i].data.ptr) + { /* Check for error conditions on listen socket. */ /* FIXME: Initiate MHD_quiesce_daemon() to prevent busy waiting? */ if (0 == (events[i].events & (EPOLLERR | EPOLLHUP))) @@ -4467,9 +4468,9 @@ MHD_epoll (struct MHD_Daemon *daemon, (daemon->connections < daemon->connection_limit) && (! daemon->at_limit) ) series_length++; - } + } continue; - } + } /* this is an event relating to a 'normal' connection, remember the event and if appropriate mark the connection as 'eready'. */ @@ -4518,7 +4519,7 @@ MHD_epoll (struct MHD_Daemon *daemon, } #if defined(HTTPS_SUPPORT) && defined(UPGRADE_SUPPORT) - if (MHD_YES == run_upgraded) + if (run_upgraded) run_epoll_for_upgrade (daemon); #endif /* HTTPS_SUPPORT && UPGRADE_SUPPORT */ @@ -4606,22 +4607,22 @@ MHD_run (struct MHD_Daemon *daemon) (0 != (daemon->options & MHD_USE_INTERNAL_POLLING_THREAD)) ) return MHD_NO; if (0 != (daemon->options & MHD_USE_POLL)) - { - MHD_poll (daemon, MHD_NO); - MHD_cleanup_connections (daemon); - } + { + MHD_poll (daemon, MHD_NO); + MHD_cleanup_connections (daemon); + } #ifdef EPOLL_SUPPORT else if (0 != (daemon->options & MHD_USE_EPOLL)) - { - MHD_epoll (daemon, MHD_NO); - MHD_cleanup_connections (daemon); - } + { + MHD_epoll (daemon, MHD_NO); + MHD_cleanup_connections (daemon); + } #endif else - { - MHD_select (daemon, MHD_NO); - /* MHD_select does MHD_cleanup_connections already */ - } + { + MHD_select (daemon, MHD_NO); + /* MHD_select does MHD_cleanup_connections already */ + } return MHD_YES; } @@ -6494,10 +6495,10 @@ thread_failed: if (daemon->upgrade_fd_in_epoll) { if (0 != epoll_ctl (daemon->epoll_fd, - EPOLL_CTL_DEL, - daemon->epoll_upgrade_fd, - NULL)) - MHD_PANIC (_("Failed to remove FD from epoll set\n")); + EPOLL_CTL_DEL, + daemon->epoll_upgrade_fd, + NULL)) + MHD_PANIC (_("Failed to remove FD from epoll set\n")); daemon->upgrade_fd_in_epoll = false; } #endif /* HTTPS_SUPPORT && UPGRADE_SUPPORT */ diff --git a/src/microhttpd/response.c b/src/microhttpd/response.c @@ -80,9 +80,9 @@ */ static int add_response_entry (struct MHD_Response *response, - enum MHD_ValueKind kind, - const char *header, - const char *content) + enum MHD_ValueKind kind, + const char *header, + const char *content) { struct MHD_HTTP_Header *hdr; @@ -195,7 +195,7 @@ MHD_add_response_footer (struct MHD_Response *response, int MHD_del_response_header (struct MHD_Response *response, const char *header, - const char *content) + const char *content) { struct MHD_HTTP_Header *pos; struct MHD_HTTP_Header *prev; @@ -280,7 +280,7 @@ MHD_get_response_headers (struct MHD_Response *response, */ const char * MHD_get_response_header (struct MHD_Response *response, - const char *key) + const char *key) { struct MHD_HTTP_Header *pos; size_t key_size; @@ -558,8 +558,8 @@ free_callback (void *cls) */ struct MHD_Response * MHD_create_response_from_fd_at_offset (size_t size, - int fd, - off_t offset) + int fd, + off_t offset) { return MHD_create_response_from_fd_at_offset64 (size, fd, @@ -627,7 +627,7 @@ MHD_create_response_from_fd_at_offset64 (uint64_t size, */ struct MHD_Response * MHD_create_response_from_fd (size_t size, - int fd) + int fd) { return MHD_create_response_from_fd_at_offset64 (size, fd, @@ -732,8 +732,8 @@ MHD_create_response_from_data (size_t size, */ struct MHD_Response * MHD_create_response_from_buffer (size_t size, - void *buffer, - enum MHD_ResponseMemoryMode mode) + void *buffer, + enum MHD_ResponseMemoryMode mode) { return MHD_create_response_from_data (size, buffer, @@ -754,15 +754,15 @@ MHD_create_response_from_buffer (size_t size, */ _MHD_EXTERN struct MHD_Response * MHD_create_response_from_buffer_with_free_callback (size_t size, - void *buffer, - MHD_ContentReaderFreeCallback crfc) + void *buffer, + MHD_ContentReaderFreeCallback crfc) { struct MHD_Response *r; r = MHD_create_response_from_data (size, - buffer, - MHD_YES, - MHD_NO); + buffer, + MHD_YES, + MHD_NO); if (NULL == r) return r; r->crfc = crfc; @@ -997,17 +997,17 @@ MHD_response_execute_upgrade_ (struct MHD_Response *response, EPOLL_CTL_ADD, connection->socket_fd, &event)) - { + { #ifdef HAVE_MESSAGES - MHD_DLOG (daemon, - _("Call to epoll_ctl failed: %s\n"), - MHD_socket_last_strerr_ ()); + MHD_DLOG (daemon, + _("Call to epoll_ctl failed: %s\n"), + MHD_socket_last_strerr_ ()); #endif - MHD_socket_close_chk_ (sv[0]); - MHD_socket_close_chk_ (sv[1]); - free (urh); - return MHD_NO; - } + MHD_socket_close_chk_ (sv[0]); + MHD_socket_close_chk_ (sv[1]); + free (urh); + return MHD_NO; + } /* Second, add our end of the UNIX socketpair() */ event.events = EPOLLIN | EPOLLOUT | EPOLLPRI | EPOLLET; @@ -1016,28 +1016,28 @@ MHD_response_execute_upgrade_ (struct MHD_Response *response, EPOLL_CTL_ADD, urh->mhd.socket, &event)) - { - event.events = EPOLLIN | EPOLLOUT | EPOLLPRI; - event.data.ptr = &urh->app; - if (0 != epoll_ctl (daemon->epoll_upgrade_fd, - EPOLL_CTL_DEL, - connection->socket_fd, - &event)) - MHD_PANIC (_("Error cleaning up while handling epoll error")); + { + event.events = EPOLLIN | EPOLLOUT | EPOLLPRI; + event.data.ptr = &urh->app; + if (0 != epoll_ctl (daemon->epoll_upgrade_fd, + EPOLL_CTL_DEL, + connection->socket_fd, + &event)) + MHD_PANIC (_("Error cleaning up while handling epoll error")); #ifdef HAVE_MESSAGES - MHD_DLOG (daemon, - _("Call to epoll_ctl failed: %s\n"), - MHD_socket_last_strerr_ ()); + MHD_DLOG (daemon, + _("Call to epoll_ctl failed: %s\n"), + MHD_socket_last_strerr_ ()); #endif - MHD_socket_close_chk_ (sv[0]); - MHD_socket_close_chk_ (sv[1]); - free (urh); - return MHD_NO; - } - EDLL_insert (daemon->eready_urh_head, - daemon->eready_urh_tail, - urh); - urh->in_eready_list = true; + MHD_socket_close_chk_ (sv[0]); + MHD_socket_close_chk_ (sv[1]); + free (urh); + return MHD_NO; + } + EDLL_insert (daemon->eready_urh_head, + daemon->eready_urh_tail, + urh); + urh->in_eready_list = true; } #endif /* EPOLL_SUPPORT */ if (0 == (daemon->options & MHD_USE_THREAD_PER_CONNECTION) ) diff --git a/src/microhttpd/test_upgrade_large.c b/src/microhttpd/test_upgrade_large.c @@ -0,0 +1,1353 @@ +/* + This file is part of libmicrohttpd + Copyright (C) 2016, 2019 Christian Grothoff + + libmicrohttpd is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 3, or (at your + option) any later version. + + libmicrohttpd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with libmicrohttpd; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +/** + * @file test_upgrade_large.c + * @brief Testcase for libmicrohttpd upgrading a connection, + * modified to test the "large" corner case reported + * by Viet on the mailinglist in 6'2019 + * @author Christian Grothoff + * @author Karlson2k (Evgeny Grin) + */ + +#include "mhd_options.h" +#include <stdlib.h> +#include <string.h> +#include <stdio.h> +#include <pthread.h> +#include <stdlib.h> +#include <stddef.h> +#ifndef WINDOWS +#include <unistd.h> +#endif +#ifdef HAVE_STDBOOL_H +#include <stdbool.h> +#endif /* HAVE_STDBOOL_H */ + +#include "mhd_sockets.h" +#ifdef HAVE_NETINET_IP_H +#include <netinet/ip.h> +#endif /* HAVE_NETINET_IP_H */ + +#include "platform.h" +#include "microhttpd.h" + +#include "test_helpers.h" + +#define LARGE_STRING "HelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHelloHello" + +#define LARGE_REPLY_STRING "WorldWorldWorldWorldWorldWorldWorldWorldWorldWorldWorldWorldWorldWorldWorldWorldWorldWorldWorldWorldWorldWorldWorldWorldWorldWorldWorldWorldWorldWorldWorldWorldWorldWorldWorldWorldWorldWorldWorldWorld" + +#ifdef HTTPS_SUPPORT +#include <gnutls/gnutls.h> +#include "../testcurl/https/tls_test_keys.h" + +#if defined(HAVE_FORK) && defined(HAVE_WAITPID) +#include <sys/types.h> +#include <sys/wait.h> +#endif /* HAVE_FORK && HAVE_WAITPID */ +#endif /* HTTPS_SUPPORT */ + +static int verbose = 0; + +enum tls_tool +{ + TLS_CLI_NO_TOOL = 0, + TLS_CLI_GNUTLS, + TLS_CLI_OPENSSL, + TLS_LIB_GNUTLS +}; + +enum tls_tool use_tls_tool; + +#if defined(HTTPS_SUPPORT) && defined(HAVE_FORK) && defined(HAVE_WAITPID) +/** + * Fork child that connects via GnuTLS-CLI to our @a port. Allows us to + * talk to our port over a socket in @a sp without having to worry + * about TLS. + * + * @param location where the socket is returned + * @return -1 on error, otherwise PID of TLS child process + */ +static pid_t +gnutlscli_connect (int *sock, + uint16_t port) +{ + pid_t chld; + int sp[2]; + char destination[30]; + + if (0 != socketpair (AF_UNIX, + SOCK_STREAM, + 0, + sp)) + return -1; + chld = fork (); + if (0 != chld) + { + *sock = sp[1]; + MHD_socket_close_chk_ (sp[0]); + return chld; + } + MHD_socket_close_chk_ (sp[1]); + (void) close (0); + (void) close (1); + if (-1 == dup2 (sp[0], 0)) + abort (); + if (-1 == dup2 (sp[0], 1)) + abort (); + MHD_socket_close_chk_ (sp[0]); + if (TLS_CLI_GNUTLS == use_tls_tool) + { + snprintf (destination, + sizeof(destination), + "%u", + (unsigned int) port); + execlp ("gnutls-cli", + "gnutls-cli", + "--insecure", + "-p", + destination, + "127.0.0.1", + (char *) NULL); + } + else if (TLS_CLI_OPENSSL == use_tls_tool) + { + snprintf (destination, + sizeof(destination), + "127.0.0.1:%u", + (unsigned int) port); + execlp ("openssl", + "openssl", + "s_client", + "-connect", + destination, + "-verify", + "1", + (char *) NULL); + } + _exit (1); +} +#endif /* HTTPS_SUPPORT && HAVE_FORK && HAVE_WAITPID */ + + +/** + * Wrapper structure for plain&TLS sockets + */ +struct wr_socket +{ + /** + * Real network socket + */ + MHD_socket fd; + + /** + * Type of this socket + */ + enum wr_type + { + wr_invalid = 0, + wr_plain = 1, + wr_tls = 2 + } t; +#ifdef HTTPS_SUPPORT + /** + * TLS credentials + */ + gnutls_certificate_credentials_t tls_crd; + + /** + * TLS session. + */ + gnutls_session_t tls_s; + + /** + * TLS handshake already succeed? + */ + bool tls_connected; +#endif +}; + + +/** + * Get underlying real socket. + * @return FD of real socket + */ +#define wr_fd(s) ((s)->fd) + + +/** + * Create wr_socket with plain TCP underlying socket + * @return created socket on success, NULL otherwise + */ +static struct wr_socket * +wr_create_plain_sckt(void) +{ + struct wr_socket *s = malloc(sizeof(struct wr_socket)); + if (NULL == s) + return NULL; + s->t = wr_plain; + s->fd = socket (AF_INET, SOCK_STREAM, IPPROTO_TCP); + if (MHD_INVALID_SOCKET != s->fd) + return s; + free(s); + return NULL; +} + + +/** + * Create wr_socket with TLS TCP underlying socket + * @return created socket on success, NULL otherwise + */ +static struct wr_socket * +wr_create_tls_sckt(void) +{ +#ifdef HTTPS_SUPPORT + struct wr_socket *s = malloc(sizeof(struct wr_socket)); + if (NULL == s) + return NULL; + s->t = wr_tls; + s->tls_connected = 0; + s->fd = socket (AF_INET, SOCK_STREAM, IPPROTO_TCP); + if (MHD_INVALID_SOCKET != s->fd) + { + if (GNUTLS_E_SUCCESS == gnutls_init (&(s->tls_s), GNUTLS_CLIENT)) + { + if (GNUTLS_E_SUCCESS == gnutls_set_default_priority (s->tls_s)) + { + if (GNUTLS_E_SUCCESS == gnutls_certificate_allocate_credentials (&(s->tls_crd))) + { + if (GNUTLS_E_SUCCESS == gnutls_credentials_set (s->tls_s, GNUTLS_CRD_CERTIFICATE, s->tls_crd)) + { +#if GNUTLS_VERSION_NUMBER+0 >= 0x030109 + gnutls_transport_set_int (s->tls_s, (int)(s->fd)); +#else /* GnuTLS before 3.1.9 */ + gnutls_transport_set_ptr (s->tls_s, (gnutls_transport_ptr_t)(intptr_t)(s->fd)); +#endif /* GnuTLS before 3.1.9 */ + return s; + } + gnutls_certificate_free_credentials (s->tls_crd); + } + } + gnutls_deinit (s->tls_s); + } + (void)MHD_socket_close_ (s->fd); + } + free(s); +#endif /* HTTPS_SUPPORT */ + return NULL; +} + + +/** + * Create wr_socket with plain TCP underlying socket + * from already created TCP socket. + * @param plain_sk real TCP socket + * @return created socket on success, NULL otherwise + */ +static struct wr_socket * +wr_create_from_plain_sckt(MHD_socket plain_sk) +{ + struct wr_socket *s = malloc(sizeof(struct wr_socket)); + + if (NULL == s) + return NULL; + s->t = wr_plain; + s->fd = plain_sk; + return s; +} + + +/** + * Connect socket to specified address. + * @param s socket to use + * @param addr address to connect + * @param length of sturcture pointed by @a addr + * @return zero on success, -1 otherwise. + */ +static int +wr_connect(struct wr_socket *s, + const struct sockaddr *addr, + int length) +{ + if (0 != connect (s->fd, addr, length)) + return -1; + if (wr_plain == s->t) + return 0; +#ifdef HTTPS_SUPPORT + if (wr_tls == s->t) + { + /* Do not try handshake here as + * it require processing on MHD side and + * when testing with "external" polling, + * test will call MHD processing only + * after return from wr_connect(). */ + s->tls_connected = 0; + return 0; + } +#endif /* HTTPS_SUPPORT */ + return -1; +} + +#ifdef HTTPS_SUPPORT +/* Only to be called from wr_send() and wr_recv() ! */ +static bool +wr_handshake(struct wr_socket *s) +{ + int res = gnutls_handshake (s->tls_s); + if (GNUTLS_E_SUCCESS == res) + s->tls_connected = true; + else if (GNUTLS_E_AGAIN == res) + MHD_socket_set_error_ (MHD_SCKT_EAGAIN_); + else + MHD_socket_set_error_ (MHD_SCKT_ECONNABORTED_); /* hard error */ + return s->tls_connected; +} +#endif /* HTTPS_SUPPORT */ + + +/** + * Send data to remote by socket. + * @param s the socket to use + * @param buf the buffer with data to send + * @param len the length of data in @a buf + * @return number of bytes were sent if succeed, + * -1 if failed. Use #MHD_socket_get_error_() + * to get socket error. + */ +static ssize_t +wr_send (struct wr_socket *s, + const void *buf, + size_t len) +{ + if (wr_plain == s->t) + return MHD_send_(s->fd, buf, len); +#ifdef HTTPS_SUPPORT + if (wr_tls == s->t) + { + ssize_t ret; + if (!s->tls_connected && !wr_handshake (s)) + return -1; + + ret = gnutls_record_send (s->tls_s, buf, len); + if (ret > 0) + return ret; + if (GNUTLS_E_AGAIN == ret) + MHD_socket_set_error_ (MHD_SCKT_EAGAIN_); + else + MHD_socket_set_error_ (MHD_SCKT_ECONNABORTED_); /* hard error */ + } +#endif /* HTTPS_SUPPORT */ + return -1; +} + + +/** + * Receive data from remote by socket. + * @param s the socket to use + * @param buf the buffer to store received data + * @param len the length of @a buf + * @return number of bytes were received if succeed, + * -1 if failed. Use #MHD_socket_get_error_() + * to get socket error. + */ +static ssize_t +wr_recv (struct wr_socket *s, + void *buf, + size_t len) +{ + if (wr_plain == s->t) + return MHD_recv_ (s->fd, buf, len); +#ifdef HTTPS_SUPPORT + if (wr_tls == s->t) + { + ssize_t ret; + if (!s->tls_connected && !wr_handshake (s)) + return -1; + + ret = gnutls_record_recv (s->tls_s, buf, len); + if (ret > 0) + return ret; + if (GNUTLS_E_AGAIN == ret) + MHD_socket_set_error_ (MHD_SCKT_EAGAIN_); + else + MHD_socket_set_error_ (MHD_SCKT_ECONNABORTED_); /* hard error */ + } +#endif /* HTTPS_SUPPORT */ + return -1; +} + + +/** + * Close socket and release allocated resourced + * @param s the socket to close + * @return zero on succeed, -1 otherwise + */ +static int +wr_close (struct wr_socket *s) +{ + int ret = (MHD_socket_close_(s->fd)) ? 0 : -1; +#ifdef HTTPS_SUPPORT + if (wr_tls == s->t) + { + gnutls_deinit (s->tls_s); + gnutls_certificate_free_credentials (s->tls_crd); + } +#endif /* HTTPS_SUPPORT */ + free (s); + return ret; +} + + +/** + * Thread we use to run the interaction with the upgraded socket. + */ +static pthread_t pt; + +/** + * Will be set to the upgraded socket. + */ +static struct wr_socket *usock; + +/** + * Thread we use to run the interaction with the upgraded socket. + */ +static pthread_t pt_client; + +/** + * Flag set to 1 once the test is finished. + */ +static volatile bool done; + + +/** + * Callback used by MHD to notify the application about completed + * requests. Frees memory. + * + * @param cls client-defined closure + * @param connection connection handle + * @param con_cls value as set by the last call to + * the #MHD_AccessHandlerCallback + * @param toe reason for request termination + */ +static void +notify_completed_cb (void *cls, + struct MHD_Connection *connection, + void **con_cls, + enum MHD_RequestTerminationCode toe) +{ + pthread_t* ppth = *con_cls; + + (void) cls; + (void) connection; /* Unused. Silent compiler warning. */ + if ( (toe != MHD_REQUEST_TERMINATED_COMPLETED_OK) && + (toe != MHD_REQUEST_TERMINATED_CLIENT_ABORT) && + (toe != MHD_REQUEST_TERMINATED_DAEMON_SHUTDOWN) ) + abort (); + if (! pthread_equal (**((pthread_t**)con_cls), + pthread_self ())) + abort (); + if (NULL != ppth) + free (*con_cls); + *con_cls = NULL; +} + + +/** + * Logging callback. + * + * @param cls logging closure (NULL) + * @param uri access URI + * @param connection connection handle + * @return #TEST_PTR + */ +static void * +log_cb (void *cls, + const char *uri, + struct MHD_Connection *connection) +{ + pthread_t *ppth; + + (void) cls; + (void) connection; /* Unused. Silent compiler warning. */ + if (0 != strcmp (uri, + "/")) + abort (); + ppth = malloc (sizeof (pthread_t)); + if (NULL == ppth) + abort(); + *ppth = pthread_self (); + return (void *) ppth; +} + + +/** + * Function to check that MHD properly notifies about starting + * and stopping. + * + * @param cls client-defined closure + * @param connection connection handle + * @param socket_context socket-specific pointer where the + * client can associate some state specific + * to the TCP connection; note that this is + * different from the "con_cls" which is per + * HTTP request. The client can initialize + * during #MHD_CONNECTION_NOTIFY_STARTED and + * cleanup during #MHD_CONNECTION_NOTIFY_CLOSED + * and access in the meantime using + * #MHD_CONNECTION_INFO_SOCKET_CONTEXT. + * @param toe reason for connection notification + * @see #MHD_OPTION_NOTIFY_CONNECTION + * @ingroup request + */ +static void +notify_connection_cb (void *cls, + struct MHD_Connection *connection, + void **socket_context, + enum MHD_ConnectionNotificationCode toe) +{ + static int started; + + (void) cls; + (void) connection; /* Unused. Silent compiler warning. */ + switch (toe) + { + case MHD_CONNECTION_NOTIFY_STARTED: + if (MHD_NO != started) + abort (); + started = MHD_YES; + *socket_context = &started; + break; + case MHD_CONNECTION_NOTIFY_CLOSED: + if (MHD_YES != started) + abort (); + if (&started != *socket_context) + abort (); + *socket_context = NULL; + started = MHD_NO; + break; + } +} + + +/** + * Change socket to blocking. + * + * @param fd the socket to manipulate + * @return non-zero if succeeded, zero otherwise + */ +static void +make_blocking (MHD_socket fd) +{ +#if defined(MHD_POSIX_SOCKETS) + int flags; + + flags = fcntl (fd, F_GETFL); + if (-1 == flags) + return; + if ((flags & ~O_NONBLOCK) != flags) + if (-1 == fcntl (fd, F_SETFL, flags & ~O_NONBLOCK)) + abort (); +#elif defined(MHD_WINSOCK_SOCKETS) + unsigned long flags = 1; + + ioctlsocket (fd, FIONBIO, &flags); +#endif /* MHD_WINSOCK_SOCKETS */ + +} + + +static void +send_all (struct wr_socket *sock, + const char *text) +{ + size_t len = strlen (text); + ssize_t ret; + size_t off; + + make_blocking (wr_fd (sock)); + for (off = 0; off < len; off += ret) + { + ret = wr_send (sock, + &text[off], + len - off); + if (0 > ret) + { + if (MHD_SCKT_ERR_IS_EAGAIN_ (MHD_socket_get_error_ ())) + { + ret = 0; + continue; + } + abort (); + } + } +} + + +/** + * Read character-by-character until we + * get '\r\n\r\n'. + */ +static void +recv_hdr (struct wr_socket *sock) +{ + unsigned int i; + char next; + char c; + ssize_t ret; + + make_blocking (wr_fd (sock)); + next = '\r'; + i = 0; + while (i < 4) + { + ret = wr_recv (sock, + &c, + 1); + if (0 > ret) + { + if (MHD_SCKT_ERR_IS_EAGAIN_ (MHD_socket_get_error_ ())) + continue; + abort (); + } + if (0 == ret) + continue; + if (c == next) + { + i++; + if (next == '\r') + next = '\n'; + else + next = '\r'; + continue; + } + if (c == '\r') + { + i = 1; + next = '\n'; + continue; + } + i = 0; + next = '\r'; + } +} + + +static void +recv_all (struct wr_socket *sock, + const char *text) +{ + size_t len = strlen (text); + char buf[len]; + ssize_t ret; + size_t off; + + make_blocking (wr_fd (sock)); + for (off = 0; off < len; off += ret) + { + ret = wr_recv (sock, + &buf[off], + len - off); + if (0 > ret) + { + if (MHD_SCKT_ERR_IS_EAGAIN_ (MHD_socket_get_error_ ())) + { + ret = 0; + continue; + } + abort (); + } + } + if (0 != strncmp (text, buf, len)) + abort(); +} + + +/** + * Main function for the thread that runs the interaction with + * the upgraded socket. + * + * @param cls the handle for the upgrade + */ +static void * +run_usock (void *cls) +{ + struct MHD_UpgradeResponseHandle *urh = cls; + + send_all (usock, + LARGE_STRING); + recv_all (usock, + LARGE_REPLY_STRING); + send_all (usock, + "Finished"); + MHD_upgrade_action (urh, + MHD_UPGRADE_ACTION_CLOSE); + free (usock); + usock = NULL; + return NULL; +} + + +/** + * Main function for the thread that runs the client-side of the + * interaction with the upgraded socket. + * + * @param cls the client socket + */ +static void * +run_usock_client (void *cls) +{ + struct wr_socket *sock = cls; + + send_all (sock, + "GET / HTTP/1.1\r\nConnection: Upgrade\r\n\r\n"); + recv_hdr (sock); + recv_all (sock, + LARGE_STRING); + send_all (sock, + LARGE_REPLY_STRING); + recv_all (sock, + "Finished"); + wr_close (sock); + done = true; + return NULL; +} + + +/** + * Function called after a protocol "upgrade" response was sent + * successfully and the socket should now be controlled by some + * protocol other than HTTP. + * + * Any data already received on the socket will be made available in + * @e extra_in. This can happen if the application sent extra data + * before MHD send the upgrade response. The application should + * treat data from @a extra_in as if it had read it from the socket. + * + * Note that the application must not close() @a sock directly, + * but instead use #MHD_upgrade_action() for special operations + * on @a sock. + * + * Except when in 'thread-per-connection' mode, implementations + * of this function should never block (as it will still be called + * from within the main event loop). + * + * @param cls closure, whatever was given to #MHD_create_response_for_upgrade(). + * @param connection original HTTP connection handle, + * giving the function a last chance + * to inspect the original HTTP request + * @param con_cls last value left in `con_cls` of the `MHD_AccessHandlerCallback` + * @param extra_in if we happened to have read bytes after the + * HTTP header already (because the client sent + * more than the HTTP header of the request before + * we sent the upgrade response), + * these are the extra bytes already read from @a sock + * by MHD. The application should treat these as if + * it had read them from @a sock. + * @param extra_in_size number of bytes in @a extra_in + * @param sock socket to use for bi-directional communication + * with the client. For HTTPS, this may not be a socket + * that is directly connected to the client and thus certain + * operations (TCP-specific setsockopt(), getsockopt(), etc.) + * may not work as expected (as the socket could be from a + * socketpair() or a TCP-loopback). The application is expected + * to perform read()/recv() and write()/send() calls on the socket. + * The application may also call shutdown(), but must not call + * close() directly. + * @param urh argument for #MHD_upgrade_action()s on this @a connection. + * Applications must eventually use this callback to (indirectly) + * perform the close() action on the @a sock. + */ +static void +upgrade_cb (void *cls, + struct MHD_Connection *connection, + void *con_cls, + const char *extra_in, + size_t extra_in_size, + MHD_socket sock, + struct MHD_UpgradeResponseHandle *urh) +{ + (void) cls; + (void) connection; + (void) con_cls; + (void) extra_in; /* Unused. Silent compiler warning. */ + + usock = wr_create_from_plain_sckt (sock); + if (0 != extra_in_size) + abort (); + if (0 != pthread_create (&pt, + NULL, + &run_usock, + urh)) + abort (); +} + + +/** + * A client has requested the given url using the given method + * (#MHD_HTTP_METHOD_GET, #MHD_HTTP_METHOD_PUT, + * #MHD_HTTP_METHOD_DELETE, #MHD_HTTP_METHOD_POST, etc). The callback + * must call MHD callbacks to provide content to give back to the + * client and return an HTTP status code (i.e. #MHD_HTTP_OK, + * #MHD_HTTP_NOT_FOUND, etc.). + * + * @param cls argument given together with the function + * pointer when the handler was registered with MHD + * @param url the requested url + * @param method the HTTP method used (#MHD_HTTP_METHOD_GET, + * #MHD_HTTP_METHOD_PUT, etc.) + * @param version the HTTP version string (i.e. + * #MHD_HTTP_VERSION_1_1) + * @param upload_data the data being uploaded (excluding HEADERS, + * for a POST that fits into memory and that is encoded + * with a supported encoding, the POST data will NOT be + * given in upload_data and is instead available as + * part of #MHD_get_connection_values; very large POST + * data *will* be made available incrementally in + * @a upload_data) + * @param upload_data_size set initially to the size of the + * @a upload_data provided; the method must update this + * value to the number of bytes NOT processed; + * @param con_cls pointer that the callback can set to some + * address and that will be preserved by MHD for future + * calls for this request; since the access handler may + * be called many times (i.e., for a PUT/POST operation + * with plenty of upload data) this allows the application + * to easily associate some request-specific state. + * If necessary, this state can be cleaned up in the + * global #MHD_RequestCompletedCallback (which + * can be set with the #MHD_OPTION_NOTIFY_COMPLETED). + * Initially, `*con_cls` will be NULL. + * @return #MHD_YES if the connection was handled successfully, + * #MHD_NO if the socket must be closed due to a serios + * error while handling the request + */ +static int +ahc_upgrade (void *cls, + struct MHD_Connection *connection, + const char *url, + const char *method, + const char *version, + const char *upload_data, + size_t *upload_data_size, + void **con_cls) +{ + struct MHD_Response *resp; + int ret; + (void) cls; + (void) url; + (void) method; /* Unused. Silent compiler warning. */ + (void) version; + (void) upload_data; + (void) upload_data_size; /* Unused. Silent compiler warning. */ + + if (NULL == *con_cls) + abort (); + if (! pthread_equal (**((pthread_t**)con_cls), pthread_self ())) + abort (); + resp = MHD_create_response_for_upgrade (&upgrade_cb, + NULL); + MHD_add_response_header (resp, + MHD_HTTP_HEADER_UPGRADE, + "Hello World Protocol"); + ret = MHD_queue_response (connection, + MHD_HTTP_SWITCHING_PROTOCOLS, + resp); + MHD_destroy_response (resp); + return ret; +} + + +/** + * Run the MHD external event loop using select. + * + * @param daemon daemon to run it for + */ +static void +run_mhd_select_loop (struct MHD_Daemon *daemon) +{ + fd_set rs; + fd_set ws; + fd_set es; + MHD_socket max_fd; + MHD_UNSIGNED_LONG_LONG to; + struct timeval tv; + + while (! done) + { + FD_ZERO (&rs); + FD_ZERO (&ws); + FD_ZERO (&es); + max_fd = -1; + to = 1000; + + if (MHD_YES != + MHD_get_fdset (daemon, + &rs, + &ws, + &es, + &max_fd)) + abort (); + (void) MHD_get_timeout (daemon, + &to); + if (1000 < to) + to = 1000; + tv.tv_sec = to / 1000; + tv.tv_usec = 1000 * (to % 1000); + if (0 > MHD_SYS_select_ (max_fd + 1, + &rs, + &ws, + &es, + &tv)) + abort (); + MHD_run_from_select (daemon, + &rs, + &ws, + &es); + } +} + +#ifdef HAVE_POLL + +/** + * Run the MHD external event loop using select. + * + * @param daemon daemon to run it for + */ +static void +run_mhd_poll_loop (struct MHD_Daemon *daemon) +{ + (void)daemon; /* Unused. Silent compiler warning. */ + abort (); /* currently not implementable with existing MHD API */ +} +#endif /* HAVE_POLL */ + + +#ifdef EPOLL_SUPPORT +/** + * Run the MHD external event loop using select. + * + * @param daemon daemon to run it for + */ +static void +run_mhd_epoll_loop (struct MHD_Daemon *daemon) +{ + const union MHD_DaemonInfo *di; + MHD_socket ep; + fd_set rs; + MHD_UNSIGNED_LONG_LONG to; + struct timeval tv; + int ret; + + di = MHD_get_daemon_info (daemon, + MHD_DAEMON_INFO_EPOLL_FD); + ep = di->listen_fd; + while (! done) + { + FD_ZERO (&rs); + to = 1000; + + FD_SET (ep, &rs); + (void) MHD_get_timeout (daemon, + &to); + if (1000 < to) + to = 1000; + tv.tv_sec = to / 1000; + tv.tv_usec = 1000 * (to % 1000); + ret = select (ep + 1, + &rs, + NULL, + NULL, + &tv); + if ( (-1 == ret) && + (EAGAIN != errno) && + (EINTR != errno) ) + abort (); + MHD_run (daemon); + } +} +#endif /* EPOLL_SUPPORT */ + +/** + * Run the MHD external event loop using select. + * + * @param daemon daemon to run it for + */ +static void +run_mhd_loop (struct MHD_Daemon *daemon, + int flags) +{ + if (0 == (flags & (MHD_USE_POLL | MHD_USE_EPOLL))) + run_mhd_select_loop (daemon); +#ifdef HAVE_POLL + else if (0 != (flags & MHD_USE_POLL)) + run_mhd_poll_loop (daemon); +#endif /* HAVE_POLL */ +#if EPOLL_SUPPORT + else if (0 != (flags & MHD_USE_EPOLL)) + run_mhd_epoll_loop (daemon); +#endif + else + abort (); +} + + +static bool test_tls; + +/** + * Test upgrading a connection. + * + * @param flags which event loop style should be tested + * @param pool size of the thread pool, 0 to disable + */ +static int +test_upgrade (int flags, + unsigned int pool) +{ + struct MHD_Daemon *d = NULL; + struct wr_socket *sock; + struct sockaddr_in sa; + const union MHD_DaemonInfo *real_flags; + const union MHD_DaemonInfo *dinfo; +#if defined(HTTPS_SUPPORT) && defined(HAVE_FORK) && defined(HAVE_WAITPID) + pid_t pid = -1; +#endif /* HTTPS_SUPPORT && HAVE_FORK && HAVE_WAITPID */ + + done = false; + + if (! test_tls) + d = MHD_start_daemon (flags | MHD_USE_ERROR_LOG | MHD_ALLOW_UPGRADE, + MHD_is_feature_supported(MHD_FEATURE_AUTODETECT_BIND_PORT) ? + 0 : 1090, + NULL, NULL, + &ahc_upgrade, NULL, + MHD_OPTION_CONNECTION_MEMORY_LIMIT, 512, + MHD_OPTION_URI_LOG_CALLBACK, &log_cb, NULL, + MHD_OPTION_NOTIFY_COMPLETED, &notify_completed_cb, NULL, + MHD_OPTION_NOTIFY_CONNECTION, &notify_connection_cb, NULL, + MHD_OPTION_THREAD_POOL_SIZE, pool, + MHD_OPTION_END); +#ifdef HTTPS_SUPPORT + else + d = MHD_start_daemon (flags | MHD_USE_ERROR_LOG | MHD_ALLOW_UPGRADE | MHD_USE_TLS, + MHD_is_feature_supported(MHD_FEATURE_AUTODETECT_BIND_PORT) ? + 0 : 1090, + NULL, NULL, + &ahc_upgrade, NULL, + MHD_OPTION_CONNECTION_MEMORY_LIMIT, 512, + MHD_OPTION_URI_LOG_CALLBACK, &log_cb, NULL, + MHD_OPTION_NOTIFY_COMPLETED, &notify_completed_cb, NULL, + MHD_OPTION_NOTIFY_CONNECTION, &notify_connection_cb, NULL, + MHD_OPTION_HTTPS_MEM_KEY, srv_signed_key_pem, + MHD_OPTION_HTTPS_MEM_CERT, srv_signed_cert_pem, + MHD_OPTION_THREAD_POOL_SIZE, pool, + MHD_OPTION_END); +#endif /* HTTPS_SUPPORT */ + if (NULL == d) + return 2; + real_flags = MHD_get_daemon_info (d, + MHD_DAEMON_INFO_FLAGS); + if (NULL == real_flags) + abort (); + dinfo = MHD_get_daemon_info (d, + MHD_DAEMON_INFO_BIND_PORT); + if ( (NULL == dinfo) || + (0 == dinfo->port) ) + abort (); + if (!test_tls || TLS_LIB_GNUTLS == use_tls_tool) + { + sock = test_tls ? wr_create_tls_sckt () : wr_create_plain_sckt (); + if (NULL == sock) + abort (); + sa.sin_family = AF_INET; + sa.sin_port = htons (dinfo->port); + sa.sin_addr.s_addr = htonl (INADDR_LOOPBACK); + if (0 != wr_connect (sock, + (struct sockaddr *) &sa, + sizeof (sa))) + abort (); + } + else + { +#if defined(HTTPS_SUPPORT) && defined(HAVE_FORK) && defined(HAVE_WAITPID) + MHD_socket tls_fork_sock; + uint16_t port; + + /* make address sanitizer happy */ + memcpy (&port, + dinfo /* ->port */, + sizeof (port)); + if (-1 == (pid = gnutlscli_connect (&tls_fork_sock, + port))) + { + MHD_stop_daemon (d); + return 4; + } + + sock = wr_create_from_plain_sckt (tls_fork_sock); + if (NULL == sock) + abort (); +#else /* !HTTPS_SUPPORT || !HAVE_FORK || !HAVE_WAITPID */ + abort (); +#endif /* !HTTPS_SUPPORT || !HAVE_FORK || !HAVE_WAITPID */ + } + + if (0 != pthread_create (&pt_client, + NULL, + &run_usock_client, + sock)) + abort (); + if (0 == (flags & MHD_USE_INTERNAL_POLLING_THREAD) ) + { + enum MHD_FLAG flags; + + /* make address sanitizer happy */ + memcpy (&flags, + real_flags /* ->flags */, + sizeof (flags)); + run_mhd_loop (d, flags); + } + pthread_join (pt_client, + NULL); + pthread_join (pt, + NULL); +#if defined(HTTPS_SUPPORT) && defined(HAVE_FORK) && defined(HAVE_WAITPID) + if (test_tls && TLS_LIB_GNUTLS != use_tls_tool) + waitpid (pid, NULL, 0); +#endif /* HTTPS_SUPPORT && HAVE_FORK && HAVE_WAITPID */ + MHD_stop_daemon (d); + return 0; +} + + +int +main (int argc, + char *const *argv) +{ + int error_count = 0; + int res; + + use_tls_tool = TLS_CLI_NO_TOOL; + test_tls = has_in_name(argv[0], "_tls"); + + verbose = 1; + if (has_param(argc, argv, "-q") || + has_param(argc, argv, "--quiet")) + verbose = 0; + + if (test_tls) + { +#ifdef HTTPS_SUPPORT + if (has_param(argc, argv, "--use-gnutls-cli")) + use_tls_tool = TLS_CLI_GNUTLS; + else if (has_param(argc, argv, "--use-openssl")) + use_tls_tool = TLS_CLI_OPENSSL; + else if (has_param(argc, argv, "--use-gnutls-lib")) + use_tls_tool = TLS_LIB_GNUTLS; +#if defined(HAVE_FORK) && defined(HAVE_WAITPID) + else if (0 == system ("gnutls-cli --version 1> /dev/null 2> /dev/null")) + use_tls_tool = TLS_CLI_GNUTLS; + else if (0 == system ("openssl version 1> /dev/null 2> /dev/null")) + use_tls_tool = TLS_CLI_OPENSSL; +#endif /* HAVE_FORK && HAVE_WAITPID */ + else + use_tls_tool = TLS_LIB_GNUTLS; /* Should be available as MHD use it. */ + if (verbose) + { + switch (use_tls_tool) + { + case TLS_CLI_GNUTLS: + printf ("GnuTLS-CLI will be used for testing.\n"); + break; + case TLS_CLI_OPENSSL: + printf ("Command line version of OpenSSL will be used for testing.\n"); + break; + case TLS_LIB_GNUTLS: + printf ("GnuTLS library will be used for testing.\n"); + break; + default: + abort (); + } + } + if ( (TLS_LIB_GNUTLS == use_tls_tool) && + (GNUTLS_E_SUCCESS != gnutls_global_init()) ) + abort (); + +#else /* ! HTTPS_SUPPORT */ + fprintf (stderr, "HTTPS support was disabled by configure.\n"); + return 77; +#endif /* ! HTTPS_SUPPORT */ + } + + /* run tests */ + if (verbose) + printf ("Starting HTTP \"Upgrade\" tests with %s connections.\n", + test_tls ? "TLS" : "plain"); + /* try external select */ + res = test_upgrade (0, + 0); + error_count += res; + if (res) + fprintf (stderr, + "FAILED: Upgrade with external select, return code %d.\n", + res); + else if (verbose) + printf ("PASSED: Upgrade with external select.\n"); + + /* Try external auto */ + res = test_upgrade (MHD_USE_AUTO, + 0); + error_count += res; + if (res) + fprintf (stderr, + "FAILED: Upgrade with external 'auto', return code %d.\n", + res); + else if (verbose) + printf ("PASSED: Upgrade with external 'auto'.\n"); + +#ifdef EPOLL_SUPPORT + res = test_upgrade (MHD_USE_EPOLL, + 0); + error_count += res; + if (res) + fprintf (stderr, + "FAILED: Upgrade with external select with EPOLL, return code %d.\n", + res); + else if (verbose) + printf ("PASSED: Upgrade with external select with EPOLL.\n"); +#endif + + /* Test thread-per-connection */ + res = test_upgrade (MHD_USE_INTERNAL_POLLING_THREAD | MHD_USE_THREAD_PER_CONNECTION, + 0); + error_count += res; + if (res) + fprintf (stderr, + "FAILED: Upgrade with thread per connection, return code %d.\n", + res); + else if (verbose) + printf ("PASSED: Upgrade with thread per connection.\n"); + + res = test_upgrade (MHD_USE_AUTO | MHD_USE_INTERNAL_POLLING_THREAD | MHD_USE_THREAD_PER_CONNECTION, + 0); + error_count += res; + if (res) + fprintf (stderr, + "FAILED: Upgrade with thread per connection and 'auto', return code %d.\n", + res); + else if (verbose) + printf ("PASSED: Upgrade with thread per connection and 'auto'.\n"); +#ifdef HAVE_POLL + res = test_upgrade (MHD_USE_INTERNAL_POLLING_THREAD | MHD_USE_THREAD_PER_CONNECTION | MHD_USE_POLL, + 0); + error_count += res; + if (res) + fprintf (stderr, + "FAILED: Upgrade with thread per connection and poll, return code %d.\n", + res); + else if (verbose) + printf ("PASSED: Upgrade with thread per connection and poll.\n"); +#endif /* HAVE_POLL */ + + /* Test different event loops, with and without thread pool */ + res = test_upgrade (MHD_USE_INTERNAL_POLLING_THREAD, + 0); + error_count += res; + if (res) + fprintf (stderr, + "FAILED: Upgrade with internal select, return code %d.\n", + res); + else if (verbose) + printf ("PASSED: Upgrade with internal select.\n"); + res = test_upgrade (MHD_USE_INTERNAL_POLLING_THREAD, + 2); + error_count += res; + if (res) + fprintf (stderr, + "FAILED: Upgrade with internal select with thread pool, return code %d.\n", + res); + else if (verbose) + printf ("PASSED: Upgrade with internal select with thread pool.\n"); + res = test_upgrade (MHD_USE_AUTO | MHD_USE_INTERNAL_POLLING_THREAD, + 0); + error_count += res; + if (res) + fprintf (stderr, + "FAILED: Upgrade with internal 'auto' return code %d.\n", + res); + else if (verbose) + printf ("PASSED: Upgrade with internal 'auto'.\n"); + res = test_upgrade (MHD_USE_AUTO | MHD_USE_INTERNAL_POLLING_THREAD, + 2); + error_count += res; + if (res) + fprintf (stderr, + "FAILED: Upgrade with internal 'auto' with thread pool, return code %d.\n", + res); + else if (verbose) + printf ("PASSED: Upgrade with internal 'auto' with thread pool.\n"); +#ifdef HAVE_POLL + res = test_upgrade (MHD_USE_POLL_INTERNAL_THREAD, + 0); + error_count += res; + if (res) + fprintf (stderr, + "FAILED: Upgrade with internal poll, return code %d.\n", + res); + else if (verbose) + printf ("PASSED: Upgrade with internal poll.\n"); + res = test_upgrade (MHD_USE_POLL_INTERNAL_THREAD, + 2); + if (res) + fprintf (stderr, + "FAILED: Upgrade with internal poll with thread pool, return code %d.\n", + res); + else if (verbose) + printf ("PASSED: Upgrade with internal poll with thread pool.\n"); +#endif +#ifdef EPOLL_SUPPORT + res = test_upgrade (MHD_USE_EPOLL_INTERNAL_THREAD, + 0); + if (res) + fprintf (stderr, + "FAILED: Upgrade with internal epoll, return code %d.\n", + res); + else if (verbose) + printf ("PASSED: Upgrade with internal epoll.\n"); + res = test_upgrade (MHD_USE_EPOLL_INTERNAL_THREAD, + 2); + if (res) + fprintf (stderr, + "FAILED: Upgrade with internal epoll, return code %d.\n", + res); + else if (verbose) + printf ("PASSED: Upgrade with internal epoll.\n"); +#endif + /* report result */ + if (0 != error_count) + fprintf (stderr, + "Error (code: %u)\n", + error_count); +#ifdef HTTPS_SUPPORT + if (test_tls && (TLS_LIB_GNUTLS == use_tls_tool)) + gnutls_global_deinit(); +#endif /* HTTPS_SUPPORT */ + return error_count != 0; /* 0 == pass */ +}