From 9ea89671719e23e5bcff40b17a3c0fe66c047cc3 Mon Sep 17 00:00:00 2001 From: "Evgeny Grin (Karlson2k)" Date: Sat, 5 Nov 2016 13:43:44 +0300 Subject: Combine upgrade tests into single source file, port to platforms without fork() --- configure.ac | 42 ++ src/microhttpd/Makefile.am | 30 +- src/microhttpd/test_upgrade.c | 1062 +++++++++++++++++++++++++++++++++- src/microhttpd/test_upgrade_common.c | 562 ------------------ src/microhttpd/test_upgrade_ssl.c | 236 -------- 5 files changed, 1109 insertions(+), 823 deletions(-) delete mode 100644 src/microhttpd/test_upgrade_common.c delete mode 100644 src/microhttpd/test_upgrade_ssl.c diff --git a/configure.ac b/configure.ac index c8a7c3f8..7a74e240 100644 --- a/configure.ac +++ b/configure.ac @@ -1173,6 +1173,48 @@ AS_VAR_IF([[enable_httpupgrade]],[["yes"]], AM_CONDITIONAL([ENABLE_UPGRADE], [[test "x$enable_httpupgrade" = "xyes"]]) AC_MSG_RESULT([[$enable_httpupgrade]]) +# Check for fork() and waitpid(). They are used for tests. +AC_MSG_CHECKING([[for fork()]]) +AC_LINK_IFELSE( + [ + AC_LANG_PROGRAM( + [[ +#ifdef HAVE_SYS_TYPES_H +#include +#endif +#ifdef HAVE_UNISTD_H +#include +#endif + ]], [[ + pid_t p = fork (); + if (0 == p) + return 1; + ]]) + ], [ + AC_DEFINE([[HAVE_FORK]], [[1]], [Define to 1 if you have the usable `fork' function.]) + AC_MSG_RESULT([[yes]]) + + AC_MSG_CHECKING([[for waitpid()]]) + AC_LINK_IFELSE( + [ + AC_LANG_PROGRAM( + [[ +#include + ]], [[ + pid_t p = fork (); + if (0 == p) + return 1; + waitpid (p, (void*)0, 0) + ]]) + ], [ + AC_DEFINE([[HAVE_WAITPID]], [[1]], [Define to 1 if you have the usable `waitpid' function.]) + AC_MSG_RESULT([[yes]]) + ],[ + AC_MSG_RESULT([[no]]) + ]) +],[ + AC_MSG_RESULT([[no]]) +]) MHD_LIB_LDFLAGS="$MHD_LIB_LDFLAGS -export-dynamic -no-undefined" diff --git a/src/microhttpd/Makefile.am b/src/microhttpd/Makefile.am index 81dc95c2..cc48d11d 100644 --- a/src/microhttpd/Makefile.am +++ b/src/microhttpd/Makefile.am @@ -152,10 +152,12 @@ check_PROGRAMS = \ test_shutdown_poll \ test_daemon +if HAVE_POSIX_THREADS if ENABLE_UPGRADE check_PROGRAMS += test_upgrade if ENABLE_HTTPS - check_PROGRAMS += test_upgrade_ssl + check_PROGRAMS += test_upgrade_tls +endif endif endif @@ -180,14 +182,28 @@ test_daemon_LDADD = \ $(top_builddir)/src/microhttpd/libmicrohttpd.la test_upgrade_SOURCES = \ - test_upgrade.c + test_upgrade.c test_helpers.h mhd_sockets.h +test_upgrade_CPPFLAGS = \ + $(AM_CPPFLAGS) $(GNUTLS_CPPFLAGS) +test_upgrade_CFLAGS = \ + $(AM_CFLAGS) $(PTHREAD_CFLAGS) +test_upgrade_LDFLAGS = \ + $(GNUTLS_LDFLAGS) test_upgrade_LDADD = \ - $(top_builddir)/src/microhttpd/libmicrohttpd.la + $(top_builddir)/src/microhttpd/libmicrohttpd.la \ + $(PTHREAD_LIBS) -test_upgrade_ssl_SOURCES = \ - test_upgrade_ssl.c -test_upgrade_ssl_LDADD = \ - $(top_builddir)/src/microhttpd/libmicrohttpd.la +test_upgrade_tls_SOURCES = \ + test_upgrade.c test_helpers.h mhd_sockets.h +test_upgrade_tls_CPPFLAGS = \ + $(AM_CPPFLAGS) $(GNUTLS_CPPFLAGS) +test_upgrade_tls_CFLAGS = \ + $(AM_CFLAGS) $(PTHREAD_CFLAGS) +test_upgrade_tls_LDFLAGS = \ + $(GNUTLS_LDFLAGS) +test_upgrade_tls_LDADD = \ + $(top_builddir)/src/microhttpd/libmicrohttpd.la \ + $(PTHREAD_LIBS) test_postprocessor_SOURCES = \ test_postprocessor.c diff --git a/src/microhttpd/test_upgrade.c b/src/microhttpd/test_upgrade.c index 72583451..e9836461 100644 --- a/src/microhttpd/test_upgrade.c +++ b/src/microhttpd/test_upgrade.c @@ -22,29 +22,960 @@ * @file test_upgrade.c * @brief Testcase for libmicrohttpd upgrading a connection * @author Christian Grothoff + * @author Karlson2k (Evgeny Grin) */ -#include "platform.h" -#include "microhttpd.h" #include #include #include #include - +#include +#include #ifndef WINDOWS #include #endif +#include "mhd_options.h" +#ifdef HAVE_STDBOOL_H +#include +#endif /* HAVE_STDBOOL_H */ #include "mhd_sockets.h" #ifdef HAVE_NETINET_IP_H #include #endif /* HAVE_NETINET_IP_H */ -static int verbose = 0; +#include "platform.h" +#include "microhttpd.h" + #include "test_helpers.h" -#include "test_upgrade_common.c" + +#ifdef HTTPS_SUPPORT +#include +#include "../testcurl/https/tls_test_keys.h" + +#if defined(HAVE_FORK) && defined(HAVE_WAITPID) +#include +#include +#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); + dup2 (sp[0], 0); + dup2 (sp[0], 1); + 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, + "localhost", + (char *) NULL); + } + else if (TLS_CLI_OPENSSL == use_tls_tool) + { + snprintf (destination, + sizeof(destination), + "localhost:%u", + (unsigned int) port); + execlp ("openssl", + "openssl", + "s_client", + "-connect", + destination, + "-verify", + "0", + (char *) NULL); + } + _exit (1); +} +#endif /* HTTPS_SUPPORT && HAVE_FORK && HAVE_WAITPID */ + + +/** + * Wrapper structure for plain&TLS sockets + */ +struct wr_socket_strc +{ + /** + * 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 +}; + + +/** + * Pseudo type for plain&TLS sockets + */ +typedef struct wr_socket_strc* wr_socket; + + +/** + * Invalid value of wr_socket + */ +#define WR_BAD (NULL) + + +/** + * 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, WR_BAD otherwise + */ +static wr_socket wr_create_plain_sckt(void) +{ + wr_socket s = (wr_socket)malloc(sizeof(struct wr_socket_strc)); + if (WR_BAD == s) + return WR_BAD; + s->t = wr_plain; + s->fd = socket (AF_INET, SOCK_STREAM, IPPROTO_TCP); + if (MHD_INVALID_SOCKET != s->fd) + return s; + free(s); + return WR_BAD; +} +/** + * Create wr_socket with TLS TCP underlying socket + * @return created socket on success, WR_BAD otherwise + */ +static wr_socket wr_create_tls_sckt(void) +{ +#ifdef HTTPS_SUPPORT + wr_socket s = (wr_socket)malloc(sizeof(struct wr_socket_strc)); + if (WR_BAD == s) + return WR_BAD; + 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)) + { + gnutls_transport_set_int (s->tls_s, (int )(s->fd)); + 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 WR_BAD; +} + + +/** + * Create wr_socket with plain TCP underlying socket + * from already created TCP socket. + * @param plain_sk real TCP socket + * @return created socket on success, WR_BAD otherwise + */ +static wr_socket wr_create_from_plain_sckt(MHD_socket plain_sk) +{ + wr_socket s = (wr_socket)malloc(sizeof(struct wr_socket_strc)); + if (WR_BAD == s) + return WR_BAD; + 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(wr_socket s, 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) + { + s->tls_connected = 0; + return 0; + /* 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(). */ + /* + int res = gnutls_handshake (s->tls_s); + if (GNUTLS_E_SUCCESS == res) + { + s->tls_connected = !0; + return 0; + } + if (GNUTLS_E_AGAIN == res) + return 0; + */ + } +#endif /* HTTPS_SUPPORT */ + return -1; +} + +#ifdef HTTPS_SUPPORT +/* Only to be called from wr_send() and wr_recv() ! */ +static bool wr_handshake(wr_socket s) +{ + int res = gnutls_handshake (s->tls_s); + if (GNUTLS_E_SUCCESS == res) + s->tls_connected = !0; + 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(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(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; +} + + +/** + * Perform shutdown of TCP socket for plain sockets or + * shutdown of TLS layer for TLS sockets. + * @param s the socket to shutdown + * @param how SHUT_WR or SHUT_RDWR + * @return zero on succeed, -1 otherwise + */ +static int wr_shutdown(wr_socket s, int how) +{ + if (wr_plain == s->t) + return shutdown (s->fd, how); +#ifdef HTTPS_SUPPORT + if (wr_tls == s->t) + { + ssize_t ret; + if (SHUT_WR == how) + ret = gnutls_bye (s->tls_s, GNUTLS_SHUT_WR); + else + ret = gnutls_bye (s->tls_s, GNUTLS_SHUT_RDWR); + + if (GNUTLS_E_SUCCESS == ret) + return 0; + } +#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(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 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 int done; + + +static void +notify_completed_cb (void *cls, + struct MHD_Connection *connection, + void **con_cls, + enum MHD_RequestTerminationCode toe) +{ + if ( (toe != MHD_REQUEST_TERMINATED_COMPLETED_OK) && + (toe != MHD_REQUEST_TERMINATED_CLIENT_ABORT) && + (toe != MHD_REQUEST_TERMINATED_DAEMON_SHUTDOWN) ) + abort (); + if (((long) *con_cls) != (long) pthread_self ()) + abort (); + *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) +{ + if (0 != strcmp (uri, + "/")) + abort (); + return (void *) (long) pthread_self (); +} + + +/** + * 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; + + 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 (wr_socket sock, + const char *text) +{ + size_t len = strlen (text); + ssize_t ret; + + make_blocking (wr_fd (sock)); + for (size_t 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 (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 (wr_socket sock, + const char *text) +{ + size_t len = strlen (text); + char buf[len]; + ssize_t ret; + + make_blocking (wr_fd (sock)); + for (size_t 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, + "Hello"); + recv_all (usock, + "World"); + send_all (usock, + "Finished"); + MHD_upgrade_action (urh, + MHD_UPGRADE_ACTION_CLOSE); + 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) +{ + wr_socket *sock = cls; + + send_all (*sock, + "GET / HTTP/1.1\r\nConnection: Upgrade\r\n\r\n"); + recv_hdr (*sock); + recv_all (*sock, + "Hello"); + send_all (*sock, + "World"); + recv_all (*sock, + "Finished"); + wr_close (*sock); + done = 1; + 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) +{ + 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; + + if (((long) *con_cls) != (long) 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); + } +} + + +/** + * 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) +{ + abort (); /* currently not implementable with existing MHD API */ +} + + +/** + * 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; + + 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); + select (ep + 1, + &rs, + NULL, + NULL, + &tv); + MHD_run (daemon); + } +} + + +/** + * 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)) + run_mhd_poll_loop (daemon); +#if EPOLL_SUPPORT + else if (0 != (flags & MHD_USE_EPOLL)) + run_mhd_epoll_loop (daemon); +#endif + else + run_mhd_select_loop (daemon); +} + +static bool test_tls; + /** * Test upgrading a connection. * @@ -55,13 +986,17 @@ static int test_upgrade (int flags, unsigned int pool) { - struct MHD_Daemon *d; - MHD_socket sock; + struct MHD_Daemon *d = NULL; + wr_socket sock; struct sockaddr_in sa; +#if defined(HTTPS_SUPPORT) && defined(HAVE_FORK) && defined(HAVE_WAITPID) + pid_t pid = -1; +#endif /* HTTPS_SUPPORT && HAVE_FORK && HAVE_WAITPID */ done = 0; - d = MHD_start_daemon (flags | MHD_USE_DEBUG | MHD_USE_UPGRADE, + if (!test_tls) + d = MHD_start_daemon (flags | MHD_USE_DEBUG | MHD_USE_UPGRADE, 1080, NULL, NULL, &ahc_upgrade, NULL, @@ -70,18 +1005,52 @@ test_upgrade (int flags, MHD_OPTION_NOTIFY_CONNECTION, ¬ify_connection_cb, NULL, MHD_OPTION_THREAD_POOL_SIZE, pool, MHD_OPTION_END); +#ifdef HTTPS_SUPPORT + else + d = MHD_start_daemon (flags | MHD_USE_DEBUG | MHD_USE_UPGRADE | MHD_USE_TLS, + 1080, + NULL, NULL, + &ahc_upgrade, NULL, + MHD_OPTION_URI_LOG_CALLBACK, &log_cb, NULL, + MHD_OPTION_NOTIFY_COMPLETED, ¬ify_completed_cb, NULL, + MHD_OPTION_NOTIFY_CONNECTION, ¬ify_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; - sock = socket (AF_INET, SOCK_STREAM, IPPROTO_TCP); - if (MHD_INVALID_SOCKET == sock) - abort (); - sa.sin_family = AF_INET; - sa.sin_port = htons (1080); - sa.sin_addr.s_addr = htonl (INADDR_LOOPBACK); - if (0 != connect (sock, - (struct sockaddr *) &sa, - sizeof (sa))) - abort (); + if (!test_tls || TLS_LIB_GNUTLS == use_tls_tool) + { + sock = test_tls ? wr_create_tls_sckt () : wr_create_plain_sckt (); + if (WR_BAD == sock) + abort (); + sa.sin_family = AF_INET; + sa.sin_port = htons (1080); + 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; + if (-1 == (pid = gnutlscli_connect (&tls_fork_sock, 1080))) + { + MHD_stop_daemon (d); + return 4; + } + sock = wr_create_from_plain_sckt (tls_fork_sock); + if (WR_BAD == 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, @@ -94,6 +1063,10 @@ test_upgrade (int flags, 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; } @@ -106,9 +1079,58 @@ main (int argc, int error_count = 0; int res; + use_tls_tool = TLS_CLI_NO_TOOL; + test_tls = has_in_name(argv[0], "_tls"); + if (has_param(argc, argv, "-v") || has_param(argc, argv, "--verbose")) verbose = 1; + 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")) + use_tls_tool = TLS_CLI_GNUTLS; + else if (0 == system ("openssl version 1> /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 99; +#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); @@ -194,5 +1216,9 @@ main (int argc, 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 */ } diff --git a/src/microhttpd/test_upgrade_common.c b/src/microhttpd/test_upgrade_common.c deleted file mode 100644 index a96ffcdb..00000000 --- a/src/microhttpd/test_upgrade_common.c +++ /dev/null @@ -1,562 +0,0 @@ -/* - This file is part of libmicrohttpd - Copyright (C) 2016 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 2, 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_common.c - * @brief Shared logic for testcases for libmicrohttpd upgrading a connection - * @author Christian Grothoff - */ - -#include "mhd_sockets.h" - -/** - * Thread we use to run the interaction with the upgraded socket. - */ -static pthread_t pt; - -/** - * Will be set to the upgraded socket. - */ -static MHD_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 int done; - - -static void -notify_completed_cb (void *cls, - struct MHD_Connection *connection, - void **con_cls, - enum MHD_RequestTerminationCode toe) -{ - if ( (toe != MHD_REQUEST_TERMINATED_COMPLETED_OK) && - (toe != MHD_REQUEST_TERMINATED_CLIENT_ABORT) && - (toe != MHD_REQUEST_TERMINATED_DAEMON_SHUTDOWN) ) - abort (); - if (((long) *con_cls) != (long) pthread_self ()) - abort (); - *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) -{ - if (0 != strcmp (uri, - "/")) - abort (); - return (void *) (long) pthread_self (); -} - - -/** - * 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; - - 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 (MHD_socket sock, - const char *text) -{ - size_t len = strlen (text); - ssize_t ret; - - make_blocking (sock); - for (size_t off = 0; off < len; off += ret) - { - ret = MHD_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 (MHD_socket sock) -{ - unsigned int i; - char next; - char c; - ssize_t ret; - - make_blocking (sock); - next = '\r'; - i = 0; - while (i < 4) - { - ret = MHD_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 (MHD_socket sock, - const char *text) -{ - size_t len = strlen (text); - char buf[len]; - ssize_t ret; - - make_blocking (sock); - for (size_t off = 0; off < len; off += ret) - { - ret = MHD_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, - "Hello"); - recv_all (usock, - "World"); - send_all (usock, - "Finished"); - MHD_upgrade_action (urh, - MHD_UPGRADE_ACTION_CLOSE); - 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) -{ - MHD_socket *sock = cls; - - send_all (*sock, - "GET / HTTP/1.1\r\nConnection: Upgrade\r\n\r\n"); - recv_hdr (*sock); - recv_all (*sock, - "Hello"); - send_all (*sock, - "World"); - recv_all (*sock, - "Finished"); - MHD_socket_close_chk_ (*sock); - done = 1; - 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) -{ - usock = 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; - - if (((long) *con_cls) != (long) 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); - } -} - - -/** - * 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) -{ - abort (); /* currently not implementable with existing MHD API */ -} - - -/** - * 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; - - 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); - select (ep + 1, - &rs, - NULL, - NULL, - &tv); - MHD_run (daemon); - } -} - - -/** - * 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)) - run_mhd_poll_loop (daemon); -#if EPOLL_SUPPORT - else if (0 != (flags & MHD_USE_EPOLL)) - run_mhd_epoll_loop (daemon); -#endif - else - run_mhd_select_loop (daemon); -} diff --git a/src/microhttpd/test_upgrade_ssl.c b/src/microhttpd/test_upgrade_ssl.c deleted file mode 100644 index bb3d2c3b..00000000 --- a/src/microhttpd/test_upgrade_ssl.c +++ /dev/null @@ -1,236 +0,0 @@ -/* - This file is part of libmicrohttpd - Copyright (C) 2016 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_ssl.c - * @brief Testcase for libmicrohttpd upgrading a connection - * @author Christian Grothoff - */ - -#include "platform.h" -#include "microhttpd.h" -#include -#include -#include -#include -#include - -#ifndef WINDOWS -#include -#endif - -#include -#include "mhd_sockets.h" -#ifdef HAVE_NETINET_IP_H -#include -#endif /* HAVE_NETINET_IP_H */ -#include "mhd_sockets.h" -#include "test_upgrade_common.c" - -#include "../testcurl/https/tls_test_keys.h" - - -enum tls_cli_tool -{ - TLS_CLI_NO_TOOL = 0, - TLS_CLI_GNUTLS, - TLS_CLI_OPENSSL -}; - -enum tls_cli_tool use_tool; - -/** - * Fork child that connects via OpenSSL 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 SSL child process - */ -static pid_t -openssl_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); - dup2 (sp[0], 0); - dup2 (sp[0], 1); - MHD_socket_close_chk_ (sp[0]); - if (TLS_CLI_GNUTLS == use_tool) - { - snprintf (destination, - sizeof(destination), - "%u", - (unsigned int) port); - execlp ("gnutls-cli", - "gnutls-cli", - "--insecure", - "-p", - destination, - "localhost", - (char *) NULL); - } - else if (TLS_CLI_OPENSSL == use_tool) - { - snprintf (destination, - sizeof(destination), - "localhost:%u", - (unsigned int) port); - execlp ("openssl", - "openssl", - "s_client", - "-connect", - destination, - "-verify", - "0", - (char *) NULL); - } - _exit (1); -} - - -/** - * 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; - MHD_socket sock; - pid_t pid; - - done = 0; - - d = MHD_start_daemon (flags | MHD_USE_DEBUG | MHD_USE_UPGRADE | MHD_USE_TLS, - 1080, - NULL, NULL, - &ahc_upgrade, NULL, - MHD_OPTION_URI_LOG_CALLBACK, &log_cb, NULL, - MHD_OPTION_NOTIFY_COMPLETED, ¬ify_completed_cb, NULL, - MHD_OPTION_NOTIFY_CONNECTION, ¬ify_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); - if (NULL == d) - return 2; - if (-1 == (pid = openssl_connect (&sock, 1080))) - { - MHD_stop_daemon (d); - return 4; - } - - pthread_create (&pt_client, - NULL, - &run_usock_client, - &sock); - if (0 == (flags & (MHD_USE_SELECT_INTERNALLY | - MHD_USE_THREAD_PER_CONNECTION)) ) - run_mhd_loop (d, flags); - pthread_join (pt_client, - NULL); - if (0 == (flags & (MHD_USE_SELECT_INTERNALLY | - MHD_USE_THREAD_PER_CONNECTION)) ) - run_mhd_loop (d, flags); - pthread_join (pt, - NULL); - waitpid (pid, - NULL, - 0); - MHD_stop_daemon (d); - return 0; -} - - -int -main (int argc, - char *const *argv) -{ - int error_count = 0; - - use_tool = TLS_CLI_NO_TOOL; - if (0 == system ("gnutls-cli --version 1> /dev/null")) - use_tool = TLS_CLI_GNUTLS; - else if (0 == system ("openssl version 1> /dev/null")) - use_tool = TLS_CLI_OPENSSL; - else - return 77; /* not possible to test */ - - /* try external select */ - error_count += test_upgrade (0, - 0); -#ifdef EPOLL_SUPPORT - error_count += test_upgrade (MHD_USE_EPOLL | MHD_USE_TLS, - 0); -#endif - - /* Test thread-per-connection */ - error_count += test_upgrade (MHD_USE_THREAD_PER_CONNECTION, - 0); - error_count += test_upgrade (MHD_USE_THREAD_PER_CONNECTION | MHD_USE_POLL, - 0); - - /* Test different event loops, with and without thread pool */ - error_count += test_upgrade (MHD_USE_SELECT_INTERNALLY, - 0); - error_count += test_upgrade (MHD_USE_SELECT_INTERNALLY, - 2); -#ifdef HAVE_POLL - error_count += test_upgrade (MHD_USE_POLL_INTERNALLY, - 0); - error_count += test_upgrade (MHD_USE_POLL_INTERNALLY, - 2); -#endif -#ifdef EPOLL_SUPPORT - error_count += test_upgrade (MHD_USE_EPOLL_INTERNALLY | - MHD_USE_TLS, - 0); - error_count += test_upgrade (MHD_USE_EPOLL_INTERNALLY | - MHD_USE_TLS, - 2); -#endif - /* report result */ - if (0 != error_count) - fprintf (stderr, - "Error (code: %u)\n", - error_count); - return error_count != 0; /* 0 == pass */ -} -- cgit v1.2.3