commit 3c7752bab9b90fbf939c95eddc36738d28454b62
parent f9578650704c2260173f5e4aac9c956a9e5a9d6a
Author: Evgeny Grin (Karlson2k) <k2k@drgrin.dev>
Date: Mon, 30 Sep 2024 02:54:31 +0100
test_upgrade: ported test to the new API
Diffstat:
6 files changed, 2328 insertions(+), 1 deletion(-)
diff --git a/configure.ac b/configure.ac
@@ -7177,6 +7177,7 @@ src/include/Makefile
src/mhd2/Makefile
src/tests/Makefile
src/tests/basic/Makefile
+src/tests/upgrade/Makefile
src/tests/client_server/Makefile
src/examples2/Makefile
])
diff --git a/src/tests/Makefile.am b/src/tests/Makefile.am
@@ -1,5 +1,13 @@
# This Makefile.am is in the public domain
-SUBDIRS = basic client_server
+SUBDIRS = basic
+
+if MHD_UPGRADE_SUPPORT
+SUBDIRS += upgrade
+endif
+
+if RUN_LIBCURL_TESTS
+SUBDIRS += client_server
+endif
.NOTPARALLEL:
diff --git a/src/tests/upgrade/Makefile.am b/src/tests/upgrade/Makefile.am
@@ -0,0 +1,40 @@
+# This Makefile.am is in the public domain
+AM_CPPFLAGS = \
+ -I$(top_srcdir)/src/include \
+ -I$(top_srcdir)/src/incl_priv \
+ -I$(top_srcdir)/src/mhd2 \
+ -DMHD_CPU_COUNT=$(CPU_COUNT) \
+ $(CPPFLAGS_ac) $(MHD_TLS_LIB_CPPFLAGS)
+
+AM_CFLAGS = $(CFLAGS_ac)
+
+AM_LDFLAGS = $(LDFLAGS_ac) $(MHD_TLS_LIB_LDFLAGS)
+
+AM_TESTS_ENVIRONMENT = $(TESTS_ENVIRONMENT_ac)
+
+if USE_COVERAGE
+ AM_CFLAGS += -fprofile-arcs -ftest-coverage
+endif
+
+LDADD = $(top_builddir)/src/mhd2/libmicrohttpd2.la
+
+$(top_builddir)/src/mhd2/libmicrohttpd2.la: $(top_builddir)/src/mhd2/Makefile
+ @echo ' cd $(top_builddir)/src/mhd2 && $(MAKE) $(AM_MAKEFLAGS) libmicrohttpd2.la'; \
+ $(am__cd) $(top_builddir)/src/mhd2 && $(MAKE) $(AM_MAKEFLAGS) libmicrohttpd2.la
+
+noinst_HEADERS = \
+ test_helpers.h
+
+check_PROGRAMS = \
+ test_upgrade \
+ test_upgrade_large \
+ test_upgrade_vlarge \
+ $(EMPTY_ITEM)
+
+test_upgrade_SOURCES = test_upgrade.c test_helpers.h tls_test_keys.h
+
+test_upgrade_large_SOURCES = $(test_upgrade_SOURCES)
+
+test_upgrade_vlarge_SOURCES = $(test_upgrade_SOURCES)
+
+TESTS = $(check_PROGRAMS)
diff --git a/src/tests/upgrade/test_helpers.h b/src/tests/upgrade/test_helpers.h
@@ -0,0 +1,95 @@
+/*
+ This file is part of libmicrohttpd
+ Copyright (C) 2016-2024 Karlson2k (Evgeny Grin)
+
+ GNU libmicrohttpd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ GNU 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+*/
+
+/**
+ * @file test_helpers.h
+ * @brief Static functions and macros helpers for testsuite.
+ * @author Karlson2k (Evgeny Grin)
+ */
+
+#include <string.h>
+
+/**
+ * Check whether program name contains specific @a marker string.
+ * Only last component in pathname is checked for marker presence,
+ * all leading directories names (if any) are ignored. Directories
+ * separators are handled correctly on both non-W32 and W32
+ * platforms.
+ * @param prog_name program name, may include path
+ * @param marker marker to look for.
+ * @return zero if any parameter is NULL or empty string or
+ * @a prog_name ends with slash or @a marker is not found in
+ * program name, non-zero if @a maker is found in program
+ * name.
+ */
+static int
+has_in_name (const char *prog_name, const char *marker)
+{
+ size_t name_pos;
+ size_t pos;
+
+ if (! prog_name || ! marker || ! prog_name[0] || ! marker[0])
+ return 0;
+
+ pos = 0;
+ name_pos = 0;
+ while (prog_name[pos])
+ {
+ if ('/' == prog_name[pos])
+ name_pos = pos + 1;
+#if defined(_WIN32) || defined(__CYGWIN__)
+ else if ('\\' == prog_name[pos])
+ name_pos = pos + 1;
+#endif /* _WIN32 || __CYGWIN__ */
+ pos++;
+ }
+ if (name_pos == pos)
+ return 0;
+ return strstr (prog_name + name_pos, marker) != (char *) (void *) 0;
+}
+
+
+/**
+ * Check whether one of strings in array is equal to @a param.
+ * String @a argv[0] is ignored.
+ * @param argc number of strings in @a argv, as passed to main function
+ * @param argv array of strings, as passed to main function
+ * @param param parameter to look for.
+ * @return zero if @a argv is NULL, @a param is NULL or empty string,
+ * @a argc is less then 2 or @a param is not found in @a argv,
+ * non-zero if one of strings in @a argv is equal to @a param.
+ */
+static int
+has_param (int argc, char *const argv[], const char *param)
+{
+ int i;
+ if (! argv || ! param || ! param[0])
+ return 0;
+
+ for (i = 1; i < argc; i++)
+ {
+ if (argv[i] && (strcmp (argv[i], param) == 0) )
+ return ! 0;
+ }
+
+ (void) has_in_name; /* Mute compiler warning */
+ (void) has_param; /* Mute compiler warning */
+ return 0;
+}
diff --git a/src/tests/upgrade/test_upgrade.c b/src/tests/upgrade/test_upgrade.c
@@ -0,0 +1,2000 @@
+/*
+ This file is part of GNU libmicrohttpd
+ Copyright (C) 2016-2020 Christian Grothoff
+ Copyright (C) 2016-2024 Evgeny Grin (Karlson2k)
+
+ GNU libmicrohttpd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ GNU 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+*/
+
+/**
+ * @file src/tests/upgrade/test_upgrade.c
+ * @brief Testcase for libmicrohttpd upgrading a connection
+ * @author Christian Grothoff
+ * @author Karlson2k (Evgeny Grin)
+ */
+
+#include "mhd_sys_options.h"
+#include <stddef.h>
+#include <stdint.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <pthread.h>
+#include <errno.h>
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+#ifdef HAVE_STDBOOL_H
+# include <stdbool.h>
+#endif /* HAVE_STDBOOL_H */
+
+#include "sys_sockets_headers.h"
+#include "sys_sockets_types.h"
+#include "sys_ip_headers.h"
+#include "mhd_sockets_macros.h"
+#include <fcntl.h>
+
+#include "microhttpd2.h"
+
+#include "test_helpers.h"
+
+#ifdef HTTPS_SUPPORT
+# include <gnutls/gnutls.h>
+# include "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 */
+
+#if defined(MHD_POSIX_SOCKETS)
+# ifdef MHD_WINSOCK_SOCKETS
+# error Both MHD_POSIX_SOCKETS and MHD_WINSOCK_SOCKETS are defined
+# endif /* MHD_WINSOCK_SOCKETS */
+#elif ! defined(MHD_WINSOCK_SOCKETS)
+# error Neither MHD_POSIX_SOCKETS nor MHD_WINSOCK_SOCKETS are defined
+#endif /* MHD_WINSOCK_SOCKETS */
+
+
+#ifndef mhd_SSTR_LEN
+/**
+ * Determine length of static string / macro strings at compile time.
+ */
+#define mhd_SSTR_LEN(macro) (sizeof(macro) / sizeof(char) - 1)
+#endif /* ! mhd_SSTR_LEN */
+
+#if ! defined(SHUT_WR) && defined(SD_SEND)
+# define SHUT_WR SD_SEND
+#endif
+
+#if ! defined(SHUT_RD) && defined(SD_RECEIVE)
+# define SHUT_RD SD_RECEIVE
+#endif
+
+#if ! defined(SHUT_RDWR) && defined(SD_BOTH)
+# define SHUT_RDWR SD_BOTH
+#endif
+
+#if defined(MHD_POSIX_SOCKETS)
+# if defined(ENETUNREACH)
+# define mhdt_SCKT_HARD_ERR ENETUNREACH
+# elif defined(ENOTCONN)
+# define mhdt_SCKT_HARD_ERR ENOTCONN
+# elif defined(ECONNRESET)
+# define mhdt_SCKT_HARD_ERR ECONNRESET
+# elif defined(EPIPE)
+# define mhdt_SCKT_HARD_ERR EPIPE
+# elif defined(EBADF)
+# define mhdt_SCKT_HARD_ERR EBADF
+# else
+# define mhdt_SCKT_HARD_ERR 99 /* Fallback, never used in practice */
+# endif
+#else /* MHD_WINSOCK_SOCKETS */
+# define mhdt_SCKT_HARD_ERR WSAENETRESET
+#endif
+
+
+MHD_NORETURN_ static void
+_externalErrorExit_func (const char *errDesc, const char *funcName, int lineNum)
+{
+ fflush (stdout);
+ if ((NULL != errDesc) && (0 != errDesc[0]))
+ fprintf (stderr, "%s", errDesc);
+ else
+ fprintf (stderr, "System or external library call failed");
+ if ((NULL != funcName) && (0 != funcName[0]))
+ fprintf (stderr, " in %s", funcName);
+ if (0 < lineNum)
+ fprintf (stderr, " at line %d", lineNum);
+
+ fprintf (stderr, ".\nLast errno value: %d (%s)\n", (int) errno,
+ strerror (errno));
+#ifdef MHD_WINSOCK_SOCKETS
+ fprintf (stderr, "WSAGetLastError() value: %d\n", (int) WSAGetLastError ());
+#endif /* MHD_WINSOCK_SOCKETS */
+ fflush (stderr);
+ exit (99);
+}
+
+
+MHD_NORETURN_ static void
+_mhdErrorExit_func (const char *errDesc, const char *funcName, int lineNum)
+{
+ fflush (stdout);
+ if ((NULL != errDesc) && (0 != errDesc[0]))
+ fprintf (stderr, "%s", errDesc);
+ else
+ fprintf (stderr, "MHD unexpected error");
+ if ((NULL != funcName) && (0 != funcName[0]))
+ fprintf (stderr, " in %s", funcName);
+ if (0 < lineNum)
+ fprintf (stderr, " at line %d", lineNum);
+
+ fprintf (stderr, ".\nLast errno value: %d (%s)\n", (int) errno,
+ strerror (errno));
+
+ fflush (stderr);
+ exit (8);
+}
+
+
+static void
+_testErrorLog_func (const char *errDesc, const char *funcName, int lineNum)
+{
+ fflush (stdout);
+ if ((NULL != errDesc) && (0 != errDesc[0]))
+ fprintf (stderr, "%s", errDesc);
+ else
+ fprintf (stderr, "System or external library call resulted in error");
+ if ((NULL != funcName) && (0 != funcName[0]))
+ fprintf (stderr, " in %s", funcName);
+ if (0 < lineNum)
+ fprintf (stderr, " at line %d", lineNum);
+
+ fprintf (stderr, ".\nLast errno value: %d (%s)\n", (int) errno,
+ strerror (errno));
+#ifdef MHD_WINSOCK_SOCKETS
+ fprintf (stderr, "WSAGetLastError() value: %d\n", (int) WSAGetLastError ());
+#endif /* MHD_WINSOCK_SOCKETS */
+ fflush (stderr);
+}
+
+
+#ifdef MHD_HAVE_MHD_FUNC_
+#define externalErrorExit(ignore) \
+ _externalErrorExit_func (NULL, MHD_FUNC_, __LINE__)
+#define externalErrorExitDesc(errDesc) \
+ _externalErrorExit_func (errDesc, MHD_FUNC_, __LINE__)
+#define mhdErrorExit(ignore) \
+ _mhdErrorExit_func (NULL, MHD_FUNC_, __LINE__)
+#define mhdErrorExitDesc(errDesc) \
+ _mhdErrorExit_func (errDesc, MHD_FUNC_, __LINE__)
+#define testErrorLog(ignore) \
+ _testErrorLog_func (NULL, MHD_FUNC_, __LINE__)
+#define testErrorLogDesc(errDesc) \
+ _testErrorLog_func (errDesc, MHD_FUNC_, __LINE__)
+#else /* ! MHD_HAVE_MHD_FUNC_ */
+#define externalErrorExit(ignore) _externalErrorExit_func (NULL, NULL, __LINE__)
+#define externalErrorExitDesc(errDesc) \
+ _externalErrorExit_func (errDesc, NULL, __LINE__)
+#define mhdErrorExit(ignore) _mhdErrorExit_func (NULL, NULL, __LINE__)
+#define mhdErrorExitDesc(errDesc) _mhdErrorExit_func (errDesc, NULL, __LINE__)
+#define testErrorLog(ignore) _testErrorLog_func (NULL, NULL, __LINE__)
+#define testErrorLogDesc(errDesc) _testErrorLog_func (errDesc, NULL, __LINE__)
+#endif /* ! MHD_HAVE_MHD_FUNC_ */
+
+/* ** External parameters ** */
+static bool use_large;
+
+static bool use_vlarge;
+
+static bool test_tls;
+
+static int verbose = 0;
+
+enum tls_tool
+{
+ TLS_CLI_NO_TOOL = 0,
+ TLS_CLI_GNUTLS,
+ TLS_CLI_OPENSSL,
+ TLS_LIB_GNUTLS
+};
+
+static enum tls_tool use_tls_tool;
+
+
+/* ** Internal values ** */
+
+/* Could be increased to facilitate debugging */
+static int test_timeout = 5;
+
+static uint_least16_t global_port;
+
+static const void *rclient_msg;
+
+static size_t rclient_msg_size;
+
+static const void *app_msg;
+
+static size_t app_msg_size;
+
+static void *alloc_ptr[2] = {NULL, NULL};
+
+
+static void
+fflush_allstd (void)
+{
+ fflush (stderr);
+ fflush (stdout);
+}
+
+
+#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))
+ {
+ testErrorLogDesc ("socketpair() failed");
+ return (pid_t) -1;
+ }
+ chld = fork ();
+ if (0 != chld)
+ {
+ *sock = sp[1];
+ mhd_socket_close (sp[0]);
+ return chld;
+ }
+ mhd_socket_close (sp[1]);
+ (void) close (0);
+ (void) close (1);
+ if (-1 == dup2 (sp[0], 0))
+ externalErrorExitDesc ("dup2() failed");
+ if (-1 == dup2 (sp[0], 1))
+ externalErrorExitDesc ("dup2() failed");
+ mhd_socket_close (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 */
+
+
+#if 0 /* Unused code */
+/**
+ * Change socket to blocking.
+ *
+ * @param fd the socket to manipulate
+ */
+static void
+make_blocking (MHD_Socket fd)
+{
+#if defined(MHD_POSIX_SOCKETS)
+ int flags;
+
+ flags = fcntl (fd, F_GETFL);
+ if (-1 == flags)
+ externalErrorExitDesc ("fcntl() failed");
+ if ((flags & ~O_NONBLOCK) != flags)
+ if (-1 == fcntl (fd, F_SETFL, flags & ~O_NONBLOCK))
+ externalErrorExitDesc ("fcntl() failed");
+#elif defined(MHD_WINSOCK_SOCKETS)
+ unsigned long flags = 0;
+
+ if (0 != ioctlsocket (fd, (int) FIONBIO, &flags))
+ externalErrorExitDesc ("ioctlsocket() failed");
+#endif /* MHD_WINSOCK_SOCKETS */
+}
+
+
+#endif /* Unused code */
+
+
+/**
+ * Change socket to non-blocking.
+ *
+ * @param fd the socket to manipulate
+ */
+static void
+make_nonblocking (MHD_Socket fd)
+{
+#if defined(MHD_POSIX_SOCKETS)
+ int flags;
+
+ flags = fcntl (fd, F_GETFL);
+ if (-1 == flags)
+ externalErrorExitDesc ("fcntl() failed");
+ if (O_NONBLOCK != (flags & O_NONBLOCK))
+ if (-1 == fcntl (fd, F_SETFL, flags | O_NONBLOCK))
+ externalErrorExitDesc ("fcntl() failed");
+#elif defined(MHD_WINSOCK_SOCKETS)
+ unsigned long flags = 1;
+
+ if (0 != ioctlsocket (fd, (int) FIONBIO, &flags))
+ externalErrorExitDesc ("ioctlsocket() failed");
+#endif /* MHD_WINSOCK_SOCKETS */
+}
+
+
+/**
+ * Enable TCP_NODELAY on TCP/IP socket.
+ *
+ * @param fd the socket to manipulate
+ */
+static void
+make_nodelay (MHD_Socket fd)
+{
+#ifdef TCP_NODELAY
+ const mhd_SCKT_OPT_BOOL on_val = 1;
+
+ if (0 == setsockopt (fd,
+ IPPROTO_TCP,
+ TCP_NODELAY,
+ (const void *) &on_val,
+ sizeof (on_val)))
+ return; /* Success exit point */
+
+#ifndef MHD_WINSOCK_SOCKETS
+ fprintf (stderr, "Failed to enable TCP_NODELAY on socket (ignored). "
+ "errno: %d (%s)\n", (int) errno, strerror (errno));
+#else /* MHD_WINSOCK_SOCKETS */
+ fprintf (stderr, "Failed to enable TCP_NODELAY on socket (ignored). "
+ "WSAGetLastError() value: %d\n", (int) WSAGetLastError ());
+#endif /* MHD_WINSOCK_SOCKETS */
+ fflush (stderr);
+#endif /* TCP_NODELAY */
+}
+
+
+/**
+ * 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;
+
+ bool is_nonblocking;
+
+ bool eof_recieved;
+#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)
+
+
+#if 0 /* Unused code */
+static void
+wr_make_blocking (struct wr_socket *s)
+{
+ if (s->is_nonblocking)
+ make_blocking (s->fd);
+ s->is_nonblocking = false;
+}
+
+
+#endif /* Unused code */
+
+
+static void
+wr_make_nonblocking (struct wr_socket *s)
+{
+ if (! s->is_nonblocking)
+ make_nonblocking (s->fd);
+ s->is_nonblocking = true;
+}
+
+
+/**
+ * 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)
+ {
+ testErrorLogDesc ("malloc() failed");
+ return NULL;
+ }
+ s->t = wr_plain;
+ s->eof_recieved = false;
+ s->fd = socket (AF_INET, SOCK_STREAM, IPPROTO_TCP);
+ s->is_nonblocking = false;
+ if (MHD_INVALID_SOCKET != s->fd)
+ {
+ make_nodelay (s->fd);
+ return s; /* Success */
+ }
+ testErrorLogDesc ("socket() failed");
+ 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)
+ {
+ testErrorLogDesc ("malloc() failed");
+ return NULL;
+ }
+ s->t = wr_tls;
+ s->eof_recieved = false;
+ s->tls_connected = 0;
+ s->fd = socket (AF_INET, SOCK_STREAM, IPPROTO_TCP);
+ s->is_nonblocking = false;
+ if (MHD_INVALID_SOCKET != s->fd)
+ {
+ make_nodelay (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) && ! defined(_WIN64)
+ gnutls_transport_set_int (s->tls_s, (int) (s->fd));
+#else /* GnuTLS before 3.1.9 or Win x64 */
+ gnutls_transport_set_ptr (s->tls_s,
+ (gnutls_transport_ptr_t) \
+ (intptr_t) (s->fd));
+#endif /* GnuTLS before 3.1.9 or Win x64 */
+ return s;
+ }
+ else
+ testErrorLogDesc ("gnutls_credentials_set() failed");
+ gnutls_certificate_free_credentials (s->tls_crd);
+ }
+ else
+ testErrorLogDesc ("gnutls_certificate_allocate_credentials() failed");
+ }
+ else
+ testErrorLogDesc ("gnutls_set_default_priority() failed");
+ gnutls_deinit (s->tls_s);
+ }
+ else
+ testErrorLogDesc ("gnutls_init() failed");
+ (void) mhd_socket_close (s->fd);
+ }
+ else
+ testErrorLogDesc ("socket() failed");
+ free (s);
+#endif /* HTTPS_SUPPORT */
+ return NULL;
+}
+
+
+#if defined(HTTPS_SUPPORT) && defined(HAVE_FORK) && defined(HAVE_WAITPID)
+
+/**
+ * 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)
+ {
+ testErrorLogDesc ("malloc() failed");
+ return NULL;
+ }
+ s->t = wr_plain;
+ s->eof_recieved = false;
+ s->fd = plain_sk;
+ s->is_nonblocking = false; /* The actual mode is unknown */
+ wr_make_nonblocking (s); /* Force set mode to have correct status */
+ make_nodelay (s->fd);
+ return s;
+}
+
+
+#endif /* HTTPS_SUPPORT && HAVE_FORK && HAVE_WAITPID */
+
+#if 0 /* Disabled code */
+/**
+ * Check whether shutdown of connection was received from remote
+ * @param s socket to check
+ * @return zero if shutdown signal has not been received,
+ * 1 if shutdown signal was already received
+ */
+static int
+wr_is_eof_received (struct wr_socket *s)
+{
+ return s->eof_recieved ? 1 : 0;
+}
+
+
+#endif /* Disabled code */
+
+
+enum wr_wait_for_type
+{
+ WR_WAIT_FOR_RECV = 0,
+ WR_WAIT_FOR_SEND = 1
+};
+
+static bool
+wr_wait_socket_ready_noabort_ (struct wr_socket *s,
+ int timeout_ms,
+ enum wr_wait_for_type wait_for)
+{
+ fd_set fds;
+ int sel_res;
+ struct timeval tmo;
+ struct timeval *tmo_ptr;
+
+#ifndef MHD_WINSOCK_SOCKETS
+ if (FD_SETSIZE <= s->fd)
+ externalErrorExitDesc ("Too large FD value");
+#endif /* ! MHD_WINSOCK_SOCKETS */
+ FD_ZERO (&fds);
+ FD_SET (s->fd, &fds);
+ if (0 <= timeout_ms)
+ {
+#if ! defined(_WIN32) || defined(__CYGWIN__)
+ tmo.tv_sec = (time_t) (timeout_ms / 1000);
+#else /* Native W32 */
+ tmo.tv_sec = (long) (timeout_ms / 1000);
+#endif /* Native W32 */
+ tmo.tv_usec = ((long) (timeout_ms % 1000)) * 1000;
+ tmo_ptr = &tmo;
+ }
+ else
+ tmo_ptr = NULL; /* No timeout */
+
+ do
+ {
+ if (WR_WAIT_FOR_RECV == wait_for)
+ sel_res = select (1 + (int) s->fd, &fds, NULL, NULL, tmo_ptr);
+ else
+ sel_res = select (1 + (int) s->fd, NULL, &fds, NULL, tmo_ptr);
+ } while (0 > sel_res && mhd_SCKT_ERR_IS_EINTR (mhd_SCKT_GET_LERR ()));
+
+ if (1 == sel_res)
+ return true;
+
+ if (0 == sel_res)
+ fprintf (stderr, "Timeout");
+ else
+ {
+#ifndef MHD_WINSOCK_SOCKETS
+ fprintf (stderr, "Error %d (%s)", (int) errno, strerror (errno));
+#else /* MHD_WINSOCK_SOCKETS */
+ fprintf (stderr, "Error (WSAGetLastError code: %d)",
+ (int) WSAGetLastError ());
+#endif /* MHD_WINSOCK_SOCKETS */
+ }
+ fprintf (stderr, " waiting for socket to be available for %s.\n",
+ (WR_WAIT_FOR_RECV == wait_for) ? "receiving" : "sending");
+ return false;
+}
+
+
+static void
+wr_wait_socket_ready_ (struct wr_socket *s,
+ int timeout_ms,
+ enum wr_wait_for_type wait_for)
+{
+ if (wr_wait_socket_ready_noabort_ (s, timeout_ms, wait_for))
+ return;
+
+ if (WR_WAIT_FOR_RECV == wait_for)
+ mhdErrorExitDesc ("Client failed to receive the data");
+ else
+ mhdErrorExitDesc ("Client failed to send the data");
+}
+
+
+/**
+ * Connect socket to specified address.
+ * @param s socket to use
+ * @param addr address to connect
+ * @param length of structure pointed by @a addr
+ * @param timeout_ms the maximum wait time in milliseconds to send the data,
+ * no limit if negative value is used
+ * @return zero on success, -1 otherwise.
+ */
+static int
+wr_connect_tmo (struct wr_socket *s,
+ const struct sockaddr *addr,
+ unsigned int length,
+ int timeout_ms)
+{
+ if (0 != connect (s->fd, addr, (socklen_t) length))
+ {
+ int err;
+ bool connect_completed = false;
+
+ err = mhd_SCKT_GET_LERR ();
+#if defined(MHD_POSIX_SOCKETS)
+ while (! connect_completed && (EINTR == err))
+ {
+ connect_completed = (0 == connect (s->fd, addr, (socklen_t) length));
+ if (! connect_completed)
+ {
+ err = errno;
+ if (EALREADY == err)
+ err = EINPROGRESS;
+ else if (EISCONN == err)
+ connect_completed = true;
+ }
+ }
+#endif /* MHD_POSIX_SOCKETS */
+ if (! connect_completed &&
+ (mhd_SCKT_ERR_IS_INPROGRESS (err)
+ || mhd_SCKT_ERR_IS_EAGAIN (err))) /* No modern system uses EAGAIN, except W32 */
+ connect_completed =
+ wr_wait_socket_ready_noabort_ (s, timeout_ms, WR_WAIT_FOR_SEND);
+ if (! connect_completed)
+ {
+ testErrorLogDesc ("connect() failed");
+ return -1;
+ }
+ }
+ if (wr_plain == s->t)
+ return 0;
+#ifdef HTTPS_SUPPORT
+ if (wr_tls == s->t)
+ {
+ /* Do not try handshake here as
+ * it requires 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 */
+ testErrorLogDesc ("HTTPS socket connect called, but code does not support" \
+ " HTTPS sockets");
+ return -1;
+}
+
+
+/**
+ * Connect socket to specified address.
+ * @param s socket to use
+ * @param addr address to connect
+ * @param length of structure pointed by @a addr
+ * @return zero on success, -1 otherwise.
+ */
+static int
+wr_connect (struct wr_socket *s,
+ const struct sockaddr *addr,
+ unsigned int length)
+{
+ return wr_connect_tmo (s, addr, length, test_timeout * 1000);
+}
+
+
+#ifdef HTTPS_SUPPORT
+/* Only to be called from wr_send() and wr_recv() ! */
+static bool
+wr_handshake_tmo_ (struct wr_socket *s,
+ int timeout_ms)
+{
+ int res = gnutls_handshake (s->tls_s);
+
+ while ((GNUTLS_E_AGAIN == res) || (GNUTLS_E_INTERRUPTED == res))
+ {
+ wr_wait_socket_ready_ (s, timeout_ms,
+ gnutls_record_get_direction (s->tls_s) ?
+ WR_WAIT_FOR_SEND : WR_WAIT_FOR_RECV);
+ res = gnutls_handshake (s->tls_s);
+ }
+ if (GNUTLS_E_SUCCESS == res)
+ s->tls_connected = true;
+ else
+ {
+ fprintf (stderr, "The error returned by gnutls_handshake() is "
+ "'%s' ", gnutls_strerror ((int) res));
+#if GNUTLS_VERSION_NUMBER >= 0x020600
+ fprintf (stderr, "(%s)\n", gnutls_strerror_name ((int) res));
+#else /* GNUTLS_VERSION_NUMBER < 0x020600 */
+ fprintf (stderr, "(%d)\n", (int) res);
+#endif /* GNUTLS_VERSION_NUMBER < 0x020600 */
+ testErrorLogDesc ("gnutls_handshake() failed with hard error");
+ mhd_SCKT_SET_LERR (mhdt_SCKT_HARD_ERR); /* hard error */
+ }
+ return s->tls_connected;
+}
+
+
+#if 0 /* Unused function */
+/* Only to be called from wr_send() and wr_recv() ! */
+static bool
+wr_handshake_ (struct wr_socket *s)
+{
+ return wr_handshake_tmo_ (s, test_timeout * 1000);
+}
+
+
+#endif /* Unused function */
+
+#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
+ * @param timeout_ms the maximum wait time in milliseconds to send the data,
+ * no limit if negative value is used
+ * @return number of bytes were sent if succeed,
+ * -1 if failed. Use #mhd_SCKT_GET_LERR()
+ * to get socket error.
+ */
+static ssize_t
+wr_send_tmo (struct wr_socket *s,
+ const void *buf,
+ size_t len,
+ int timeout_ms)
+{
+ if (wr_plain == s->t)
+ {
+ ssize_t res;
+ while (! 0)
+ {
+ int err;
+ res = mhd_sys_send (s->fd, buf, len);
+ if (0 <= res)
+ break; /* Success */
+ err = mhd_SCKT_GET_LERR ();
+ if (! mhd_SCKT_ERR_IS_EAGAIN (err) && ! mhd_SCKT_ERR_IS_EINTR (err))
+ break; /* Failure */
+ wr_wait_socket_ready_ (s, timeout_ms, WR_WAIT_FOR_SEND);
+ }
+ return res;
+ }
+#ifdef HTTPS_SUPPORT
+ else if (wr_tls == s->t)
+ {
+ ssize_t ret;
+ if (! s->tls_connected && ! wr_handshake_tmo_ (s, timeout_ms))
+ return -1;
+
+ while (1)
+ {
+ ret = gnutls_record_send (s->tls_s, buf, len);
+ if (ret >= 0)
+ return ret;
+ if ((GNUTLS_E_AGAIN != ret) && (GNUTLS_E_INTERRUPTED != ret))
+ break;
+ wr_wait_socket_ready_ (s, timeout_ms,
+ gnutls_record_get_direction (s->tls_s) ?
+ WR_WAIT_FOR_SEND : WR_WAIT_FOR_RECV);
+ }
+ fprintf (stderr, "The error returned by gnutls_record_send() is "
+ "'%s' ", gnutls_strerror ((int) ret));
+#if GNUTLS_VERSION_NUMBER >= 0x020600
+ fprintf (stderr, "(%s)\n", gnutls_strerror_name ((int) ret));
+#else /* GNUTLS_VERSION_NUMBER < 0x020600 */
+ fprintf (stderr, "(%d)\n", (int) ret);
+#endif /* GNUTLS_VERSION_NUMBER < 0x020600 */
+ testErrorLogDesc ("gnutls_record_send() failed with hard error");
+ mhd_SCKT_SET_LERR (mhdt_SCKT_HARD_ERR); /* hard error */
+ return -1;
+ }
+#endif /* HTTPS_SUPPORT */
+ testErrorLogDesc ("HTTPS socket send called, but code does not support" \
+ " HTTPS sockets");
+ return -1;
+}
+
+
+/**
+ * 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_SCKT_GET_LERR()
+ * to get socket error.
+ */
+static ssize_t
+wr_send (struct wr_socket *s,
+ const void *buf,
+ size_t len)
+{
+ return wr_send_tmo (s, buf, len, test_timeout * 1000);
+}
+
+
+/**
+ * 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
+ * @param timeout_ms the maximum wait time in milliseconds to receive the data,
+ * no limit if negative value is used
+ * @return number of bytes were received if succeed,
+ * -1 if failed. Use #mhd_SCKT_GET_LERR()
+ * to get socket error.
+ */
+static ssize_t
+wr_recv_tmo (struct wr_socket *s,
+ void *buf,
+ size_t len,
+ int timeout_ms)
+{
+ if (wr_plain == s->t)
+ {
+ ssize_t res;
+ while (! 0)
+ {
+ int err;
+ res = mhd_sys_recv (s->fd, buf, len);
+ if (0 == res)
+ s->eof_recieved = true;
+ if (0 <= res)
+ break; /* Success */
+ err = mhd_SCKT_GET_LERR ();
+ if (! mhd_SCKT_ERR_IS_EAGAIN (err) && ! mhd_SCKT_ERR_IS_EINTR (err))
+ break; /* Failure */
+ wr_wait_socket_ready_ (s, timeout_ms, WR_WAIT_FOR_RECV);
+ }
+ return res;
+ }
+#ifdef HTTPS_SUPPORT
+ if (wr_tls == s->t)
+ {
+ ssize_t ret;
+ if (! s->tls_connected && ! wr_handshake_tmo_ (s, timeout_ms))
+ return -1;
+
+ while (1)
+ {
+ ret = gnutls_record_recv (s->tls_s, buf, len);
+ if (0 == ret)
+ s->eof_recieved = true;
+ if (ret >= 0)
+ return ret;
+ if ((GNUTLS_E_AGAIN != ret) && (GNUTLS_E_INTERRUPTED != ret))
+ break;
+ wr_wait_socket_ready_ (s, timeout_ms,
+ gnutls_record_get_direction (s->tls_s) ?
+ WR_WAIT_FOR_SEND : WR_WAIT_FOR_RECV);
+ }
+
+ fprintf (stderr, "The error returned by gnutls_record_recv() is "
+ "'%s' ", gnutls_strerror ((int) ret));
+#if GNUTLS_VERSION_NUMBER >= 0x020600
+ fprintf (stderr, "(%s)\n", gnutls_strerror_name ((int) ret));
+#else /* GNUTLS_VERSION_NUMBER < 0x020600 */
+ fprintf (stderr, "(%d)\n", (int) ret);
+#endif /* GNUTLS_VERSION_NUMBER < 0x020600 */
+ testErrorLogDesc ("gnutls_record_recv() failed with hard error");
+ mhd_SCKT_SET_LERR (mhdt_SCKT_HARD_ERR); /* hard error */
+ return -1;
+ }
+#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_SCKT_GET_LERR()
+ * to get socket error.
+ */
+static ssize_t
+wr_recv (struct wr_socket *s,
+ void *buf,
+ size_t len)
+{
+ return wr_recv_tmo (s, buf, len, test_timeout * 1000);
+}
+
+
+/**
+ * Shutdown send/write on the socket.
+ * @param s the socket to shutdown
+ * @param how the type of shutdown: SHUT_WR or SHUT_RDWR
+ * @param timeout_ms the maximum wait time in milliseconds to receive the data,
+ * no limit if negative value is used
+ * @return zero on succeed, -1 otherwise
+ */
+static int
+wr_shutdown_tmo (struct wr_socket *s, int how, int timeout_ms)
+{
+ switch (how)
+ {
+ case SHUT_WR: /* Valid value */
+ break;
+ case SHUT_RDWR: /* Valid value */
+ break;
+ case SHUT_RD:
+ externalErrorExitDesc ("Unsupported 'how' value");
+ break;
+ default:
+ externalErrorExitDesc ("Invalid 'how' value");
+ break;
+ }
+ if (wr_plain == s->t)
+ {
+ (void) timeout_ms; /* Unused parameter for plain sockets */
+ return shutdown (s->fd, how);
+ }
+#ifdef HTTPS_SUPPORT
+ if (wr_tls == s->t)
+ {
+ ssize_t ret;
+ if (! s->tls_connected && ! wr_handshake_tmo_ (s, timeout_ms))
+ return -1;
+
+ while (1)
+ {
+ ret =
+ gnutls_bye (s->tls_s,
+ (SHUT_WR == how) ? GNUTLS_SHUT_WR : GNUTLS_SHUT_RDWR);
+ if (GNUTLS_E_SUCCESS == ret)
+ {
+#if 0 /* Disabled to test pure behaviour */
+ if (SHUT_RDWR == how)
+ (void) shutdown (s->fd, how); /* Also shutdown the underlying transport layer */
+#endif
+ return 0;
+ }
+ if ((GNUTLS_E_AGAIN != ret) && (GNUTLS_E_INTERRUPTED != ret))
+ break;
+ wr_wait_socket_ready_ (s, timeout_ms,
+ gnutls_record_get_direction (s->tls_s) ?
+ WR_WAIT_FOR_SEND : WR_WAIT_FOR_RECV);
+ }
+
+ fprintf (stderr, "The error returned by gnutls_bye() is "
+ "'%s' ", gnutls_strerror ((int) ret));
+#if GNUTLS_VERSION_NUMBER >= 0x020600
+ fprintf (stderr, "(%s)\n", gnutls_strerror_name ((int) ret));
+#else /* GNUTLS_VERSION_NUMBER < 0x020600 */
+ fprintf (stderr, "(%d)\n", (int) ret);
+#endif /* GNUTLS_VERSION_NUMBER < 0x020600 */
+ testErrorLogDesc ("gnutls_bye() failed with hard error");
+ mhd_SCKT_SET_LERR (mhdt_SCKT_HARD_ERR); /* hard error */
+ return -1;
+ }
+#endif /* HTTPS_SUPPORT */
+ return -1;
+}
+
+
+/**
+ * Shutdown the socket.
+ * @param s the socket to shutdown
+ * @return zero on succeed, -1 otherwise
+ */
+static int
+wr_shutdown (struct wr_socket *s, int how)
+{
+ return wr_shutdown_tmo (s, how, test_timeout * 1000);
+}
+
+
+/**
+ * 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;
+}
+
+
+/**
+ * String used to identify the test pseudo-protocol
+ */
+#define mhdt_UPGRADE_PROTOCOL_STR "MHDT_upgrade_test/2.0"
+
+/**
+ * Thread we use to run the interaction with the upgraded socket.
+ */
+static pthread_t pt_server;
+
+/**
+ * Thread we use to run the interaction with the upgraded socket.
+ */
+static pthread_t pt_client;
+
+/**
+ * Flag set to true once the client is finished.
+ */
+static volatile bool client_done;
+
+/**
+ * Flag set to true once the app is finished.
+ */
+static volatile bool app_done;
+
+
+static void
+send_all (struct wr_socket *sock,
+ const void *data,
+ size_t data_size)
+{
+ ssize_t ret;
+ size_t sent;
+ const uint8_t *const buf = (const uint8_t *) data;
+
+ wr_make_nonblocking (sock);
+ for (sent = 0; sent < data_size; sent += (size_t) ret)
+ {
+ ret = wr_send (sock,
+ buf + sent,
+ data_size - sent);
+ if (0 > ret)
+ {
+ if (mhd_SCKT_ERR_IS_EAGAIN (mhd_SCKT_GET_LERR ()) ||
+ mhd_SCKT_ERR_IS_EINTR (mhd_SCKT_GET_LERR ()))
+ {
+ ret = 0;
+ continue;
+ }
+ externalErrorExitDesc ("send() failed");
+ }
+ }
+}
+
+
+#define send_all_stext(sk,st) send_all (sk,st,mhd_SSTR_LEN (st))
+
+
+/**
+ * Read character-by-character until we
+ * get 'CRLNCRLN'.
+ */
+static void
+recv_hdr (struct wr_socket *sock)
+{
+ unsigned int i;
+ char next;
+ char c;
+ ssize_t ret;
+
+ wr_make_nonblocking (sock);
+ next = '\r';
+ i = 0;
+ while (i < 4)
+ {
+ ret = wr_recv (sock,
+ &c,
+ 1);
+ if (0 > ret)
+ {
+ if (mhd_SCKT_ERR_IS_EAGAIN (mhd_SCKT_GET_LERR ()))
+ continue;
+ if (mhd_SCKT_ERR_IS_EINTR (mhd_SCKT_GET_LERR ()))
+ continue;
+ externalErrorExitDesc ("recv() failed");
+ }
+ if (0 == ret)
+ mhdErrorExitDesc ("The server unexpectedly closed connection");
+ 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 void *data,
+ size_t data_size)
+{
+ uint8_t *buf;
+ ssize_t ret;
+ size_t rcvd;
+
+ buf = (uint8_t *) malloc (data_size);
+ if (NULL == buf)
+ externalErrorExitDesc ("malloc() failed");
+
+ wr_make_nonblocking (sock);
+ for (rcvd = 0; rcvd < data_size; rcvd += (size_t) ret)
+ {
+ ret = wr_recv (sock,
+ buf + rcvd,
+ data_size - rcvd);
+ if (0 > ret)
+ {
+ if (mhd_SCKT_ERR_IS_EAGAIN (mhd_SCKT_GET_LERR ()) ||
+ mhd_SCKT_ERR_IS_EINTR (mhd_SCKT_GET_LERR ()))
+ {
+ ret = 0;
+ continue;
+ }
+ externalErrorExitDesc ("recv() failed");
+ }
+ else if (0 == ret)
+ {
+ fprintf (stderr, "Partial only received text. Expected: '%.*s' "
+ "(length: %ud). Got: '%.*s' (length: %ud). ",
+ (int) data_size, (const char *) data, (unsigned int) data_size,
+ (int) rcvd, (const char *) buf, (unsigned int) rcvd);
+ mhdErrorExitDesc ("The server unexpectedly closed connection");
+ }
+ if ((data_size - rcvd) < (size_t) ret)
+ externalErrorExitDesc ("recv() returned excessive amount of data");
+ if (0 != memcmp (data, buf, rcvd + (size_t) ret))
+ {
+ fprintf (stderr, "Wrong received text. Expected: '%.*s'. "
+ "Got: '%.*s'. ",
+ (int) (rcvd + (size_t) ret), (const char *) data,
+ (int) (rcvd + (size_t) ret), (const char *) buf);
+ mhdErrorExit ();
+ }
+ }
+ if (0 != memcmp (data, buf, data_size))
+ {
+ fprintf (stderr, "Wrong received text. Expected: '%.*s'. "
+ "Got: '%.*s'. ",
+ (int) data_size, (const char *) data,
+ (int) data_size, (const char *) buf);
+ mhdErrorExit ();
+ }
+ free (buf);
+}
+
+
+#define recv_all_stext(sk,st) recv_all (sk,st,mhd_SSTR_LEN (st))
+
+
+/**
+ * Shutdown write of the connection to signal end of transmission
+ * for the remote side
+ * @param sock the socket to shutdown
+ */
+static void
+send_eof (struct wr_socket *sock)
+{
+ if (0 != wr_shutdown (sock, /*
+ ** On Darwin local shutdown of RD cause error
+ ** if remote side shut down WR before.
+ wr_is_eof_received (sock) ? SHUT_RDWR : */
+ SHUT_WR))
+ externalErrorExitDesc ("Failed to shutdown connection");
+}
+
+
+#if 0 /* Unused code */
+
+/**
+ * Receive end of the transmission indication from the remote side
+ * @param sock the socket to use
+ */
+static void
+receive_eof (struct wr_socket *sock)
+{
+ uint8_t buf[127];
+ ssize_t ret;
+ size_t rcvd;
+ bool got_eof = false;
+
+ wr_make_nonblocking (sock);
+ for (rcvd = 0; rcvd < sizeof(buf); rcvd += (size_t) ret)
+ {
+ ret = wr_recv (sock,
+ buf + rcvd,
+ sizeof(buf) - rcvd);
+ if (0 > ret)
+ {
+ if (mhd_SCKT_ERR_IS_EAGAIN (mhd_SCKT_GET_LERR ()) ||
+ mhd_SCKT_ERR_IS_EINTR (mhd_SCKT_GET_LERR ()))
+ {
+ ret = 0;
+ continue;
+ }
+ externalErrorExitDesc ("recv() failed");
+ }
+ else if (0 == ret)
+ {
+ got_eof = true;
+ break;
+ }
+ }
+ if (got_eof && (0 == rcvd))
+ return; /* Success */
+
+ if (0 != rcvd)
+ {
+ if (sizeof(buf) == rcvd)
+ {
+ fprintf (stderr, "Received at least %lu extra bytes while "
+ "end-of-file is expected.\n", (unsigned long) sizeof(buf));
+ mhdErrorExit ();
+ }
+ fprintf (stderr, "Received at %lu extra bytes and then %s"
+ "end-of-file marker.\n", (unsigned long) rcvd,
+ got_eof ? "" : "NO ");
+ mhdErrorExit ();
+ }
+ if (! got_eof)
+ mhdErrorExitDesc ("Failed to receive end-of-file marker.");
+}
+
+
+#endif /* Unused code */
+
+static void
+recv_upg_all (struct MHD_UpgradedHandle *urh,
+ const void *data,
+ size_t data_size)
+{
+ uint8_t *buf;
+ size_t last_rcvd;
+ size_t rcvd;
+
+ buf = (uint8_t *) malloc (data_size);
+ if (NULL == buf)
+ externalErrorExitDesc ("malloc() failed");
+
+ for (rcvd = 0; rcvd < data_size; rcvd += last_rcvd)
+ {
+ if (MHD_SC_OK !=
+ MHD_upgraded_recv (urh,
+ data_size - rcvd,
+ buf + rcvd,
+ &last_rcvd,
+ 1000 * (unsigned long) test_timeout))
+ mhdErrorExitDesc ("MHD_upgraded_recv() failed");
+
+ if (0 == last_rcvd)
+ {
+ fprintf (stderr, "Partial only received text. Expected: '%.*s' "
+ "(length: %ud). Got: '%.*s' (length: %ud). ",
+ (int) data_size, (const char *) data, (unsigned int) data_size,
+ (int) rcvd, (const char *) buf, (unsigned int) rcvd);
+ mhdErrorExitDesc ("The server unexpectedly closed connection");
+ }
+ if ((data_size - rcvd) < last_rcvd)
+ externalErrorExitDesc ("MHD_upgraded_recv() returned excessive " \
+ "amount of data");
+ if (0 != memcmp (data, buf, rcvd + (size_t) last_rcvd))
+ {
+ fprintf (stderr, "Wrong received text. Expected: '%.*s'. "
+ "Got: '%.*s'. ",
+ (int) (rcvd + last_rcvd), (const char *) data,
+ (int) (rcvd + last_rcvd), (const char *) buf);
+ mhdErrorExit ();
+ }
+ }
+ if (0 != memcmp (data, buf, data_size))
+ {
+ fprintf (stderr, "Wrong received text. Expected: '%.*s'. "
+ "Got: '%.*s'. ",
+ (int) data_size, (const char *) data,
+ (int) data_size, (const char *) buf);
+ mhdErrorExit ();
+ }
+ free (buf);
+}
+
+
+#define recv_upg_all_stext(uh,st) recv_upg_all (uh,st,mhd_SSTR_LEN (st))
+
+
+static void
+send_upg_all (struct MHD_UpgradedHandle *urh,
+ const void *data,
+ size_t data_size)
+{
+ size_t sent_size;
+
+ if (MHD_SC_OK !=
+ MHD_upgraded_send (urh,
+ data_size,
+ data,
+ &sent_size,
+ 1000 * (unsigned long) test_timeout,
+ MHD_NO))
+ mhdErrorExitDesc ("MHD_upgraded_send() failed");
+
+ if (sent_size != data_size)
+ mhdErrorExitDesc ("'sent_size' value is wrong");
+}
+
+
+/**
+ * Receive end of the transmission indication from the remote side
+ * @param urh the "upgraded" handle to use
+ */
+static void
+receive_upg_eof (struct MHD_UpgradedHandle *urh)
+{
+ size_t rcvd_sise;
+ uint8_t buf[1];
+
+ if (MHD_SC_OK !=
+ MHD_upgraded_recv (urh,
+ sizeof(buf),
+ buf,
+ &rcvd_sise,
+ 1000 * (unsigned long) test_timeout))
+ mhdErrorExitDesc ("MHD_upgraded_recv() failed");
+
+ if (0 != rcvd_sise)
+ mhdErrorExitDesc ("EOF marker is not received");
+
+}
+
+
+/**
+ * Main function for the thread that runs the interaction with
+ * the upgraded socket.
+ *
+ * @param cls the handle for the upgrade
+ */
+static void *
+run_usock_server (void *cls)
+{
+ struct MHD_UpgradedHandle *urh = cls;
+
+ recv_upg_all (urh,
+ rclient_msg,
+ rclient_msg_size);
+ send_upg_all (urh,
+ app_msg,
+ app_msg_size);
+ recv_upg_all_stext (urh, \
+ "Finished");
+ if (! test_tls)
+ {
+ receive_upg_eof (urh);
+ }
+ if (MHD_SC_OK !=
+ MHD_upgraded_close (urh))
+ mhdErrorExitDesc ("MHD_upgraded_close() failed");
+
+ app_done = true;
+ 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_stext (sock,
+ "GET / HTTP/1.1\r\n" \
+ "Host: localhost\r\n" \
+ "Connection: Upgrade\r\n" \
+ "Upgrade: " mhdt_UPGRADE_PROTOCOL_STR "\r\n" \
+ "\r\n");
+ recv_hdr (sock);
+ send_all (sock,
+ rclient_msg,
+ rclient_msg_size);
+ recv_all (sock,
+ app_msg,
+ app_msg_size);
+ send_all_stext (sock,
+ "Finished");
+ if (! test_tls)
+ {
+ send_eof (sock);
+ }
+ wr_close (sock);
+ client_done = true;
+ return NULL;
+}
+
+
+/**
+ * Function called after a protocol "upgrade" response was sent successfully
+ * and the connection is being switched to other protocol.
+ *
+ * The newly provided handle @a urh can be used to send and receive the data
+ * by #MHD_upgraded_send() and #MHD_upgraded_recv(). The handle must be closed
+ * by #MHD_upgraded_close() before destroying the daemon.
+ *
+ * "Upgraded" connection will not time out, but still counted for daemon
+ * global connections limit and for per-IP limit (if set).
+ *
+ * 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_action_upgrade().
+ * @param request original HTTP request handle,
+ * giving the function a last chance
+ * to inspect the original HTTP request
+ * @param urh argument for #MHD_upgrade_operation() on this @a response.
+ * Applications must eventually use this callback to (indirectly)
+ * perform the close() action on the @a sock.
+ */
+static void
+upgrade_cb (void *cls,
+ struct MHD_Request *MHD_RESTRICT request,
+ struct MHD_UpgradedHandle *MHD_RESTRICT urh)
+{
+ if (NULL != cls)
+ mhdErrorExitDesc ("'cls' is not NULL");
+ if (NULL == request)
+ mhdErrorExitDesc ("'request' is NULL");
+
+ if (0 != pthread_create (&pt_server,
+ NULL,
+ &run_usock_server,
+ urh))
+ externalErrorExitDesc ("pthread_create() failed");
+}
+
+
+/**
+ * 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).
+ * If @a upload_size is not zero and response action is provided by this
+ * callback, then upload will be discarded and the stream (the connection for
+ * HTTP/1.1) will be closed after sending the response.
+ *
+ * @param cls argument given together with the function
+ * pointer when the handler was registered with MHD
+ * @param request the request object
+ * @param path the requested uri (without arguments after "?")
+ * @param method the HTTP method used (#MHD_HTTP_METHOD_GET,
+ * #MHD_HTTP_METHOD_PUT, etc.)
+ * @param upload_size the size of the message upload content payload,
+ * #MHD_SIZE_UNKNOWN for chunked uploads (if the
+ * final chunk has not been processed yet)
+ * @return action how to proceed, NULL
+ * if the request must be aborted due to a serious
+ * error while handling the request (implies closure
+ * of underling data stream, for HTTP/1.1 it means
+ * socket closure).
+ */
+static const struct MHD_Action *
+req_handle_upgrade (void *cls,
+ struct MHD_Request *MHD_RESTRICT request,
+ const struct MHD_String *MHD_RESTRICT path,
+ enum MHD_HTTP_Method method,
+ uint_fast64_t upload_size)
+{
+ const struct MHD_Action *act;
+ if (NULL != cls)
+ mhdErrorExitDesc ("'cls' is not NULL");
+ if (NULL == request)
+ mhdErrorExitDesc ("'request' is NULL");
+ if (NULL == path)
+ mhdErrorExitDesc ("'path' is NULL");
+ if (1 != path->len)
+ mhdErrorExitDesc ("'path->len' is not 1");
+ if (0 != memcmp ("/", path->cstr, 1))
+ mhdErrorExitDesc ("'path->cstr' is not \"/\"");
+ if (0 != path->cstr[path->len])
+ mhdErrorExitDesc ("'path->cstr' is not zero-terminated");
+ if (MHD_HTTP_METHOD_GET != method)
+ mhdErrorExitDesc ("'method' is not MHD_HTTP_METHOD_GET");
+ if (0 != upload_size)
+ mhdErrorExitDesc ("'upload_size' is not zero");
+
+ act = MHD_action_upgrade (request,
+ mhdt_UPGRADE_PROTOCOL_STR,
+ &upgrade_cb,
+ NULL,
+ 0,
+ NULL);
+ if (NULL == act)
+ mhdErrorExitDesc ("MHD_action_upgrade() failed");
+
+ return act;
+}
+
+
+/**
+ * Test upgrading a connection.
+ * @return zero if succeed
+ */
+static unsigned int
+test_upgrade (void)
+{
+ struct MHD_Daemon *d = NULL;
+ struct wr_socket *sock;
+ struct sockaddr_in sa;
+ union MHD_DaemonInfoFixedData dinfo;
+
+#if defined(HTTPS_SUPPORT) && defined(HAVE_FORK) && defined(HAVE_WAITPID)
+ pid_t pid = -1;
+#endif /* HTTPS_SUPPORT && HAVE_FORK && HAVE_WAITPID */
+
+ client_done = false;
+ app_done = false;
+
+ d = MHD_daemon_create (&req_handle_upgrade,
+ NULL);
+ if (NULL == d)
+ mhdErrorExitDesc ("MHD_daemon_create() failed");
+
+ if (MHD_SC_OK !=
+ MHD_DAEMON_SET_OPTIONS ( \
+ d, \
+ MHD_D_OPTION_BIND_PORT (MHD_AF_DUAL_v6_OPTIONAL, global_port), \
+ MHD_D_OPTION_WORK_MODE (MHD_WM_OPTION_WORKER_THREADS (1))))
+ mhdErrorExitDesc ("MHD_DAEMON_SET_OPTIONS() failed");
+
+ if (MHD_SC_OK !=
+ MHD_daemon_start (d))
+ mhdErrorExitDesc ("MHD_daemon_start() failed");
+
+ if (MHD_SC_OK !=
+ MHD_daemon_get_info_fixed (d, \
+ MHD_DAEMON_INFO_FIXED_BIND_PORT, \
+ &dinfo))
+ mhdErrorExitDesc ("MHD_daemon_get_info_fixed() failed");
+
+ if (0 == dinfo.v_port)
+ mhdErrorExitDesc ("MHD_daemon_get_info_fixed() returned wrong data");
+ global_port = dinfo.v_port; /* Re-use the same port for the next checks */
+ if (! test_tls || (TLS_LIB_GNUTLS == use_tls_tool))
+ {
+ sock = test_tls ? wr_create_tls_sckt () : wr_create_plain_sckt ();
+ if (NULL == sock)
+ externalErrorExitDesc ("Create socket failed");
+ wr_make_nonblocking (sock);
+ sa.sin_family = AF_INET;
+ sa.sin_port = htons (global_port);
+#ifdef INADDR_LOOPBACK
+ sa.sin_addr.s_addr = htonl (INADDR_LOOPBACK);
+#else /* ! INADDR_LOOPBACK */
+ memcpy (&(sa.sin_addr.s_addr), "\x7f\0\0\1", 4);
+#endif /* ! INADDR_LOOPBACK */
+ if (0 != wr_connect (sock,
+ (struct sockaddr *) &sa,
+ sizeof (sa)))
+ externalErrorExitDesc ("Connect socket failed");
+ }
+ else
+ {
+#if defined(HTTPS_SUPPORT) && defined(HAVE_FORK) && defined(HAVE_WAITPID)
+ MHD_Socket tls_fork_sock;
+ uint16_t port;
+
+ port = global_port;
+ if (-1 == (pid = gnutlscli_connect (&tls_fork_sock,
+ port)))
+ externalErrorExitDesc ("gnutlscli_connect() failed");
+
+ sock = wr_create_from_plain_sckt (tls_fork_sock);
+ if (NULL == sock)
+ externalErrorExitDesc ("wr_create_from_plain_sckt() failed");
+
+ wr_make_nonblocking (sock);
+#else /* !HTTPS_SUPPORT || !HAVE_FORK || !HAVE_WAITPID */
+ externalErrorExitDesc ("Unsupported 'use_tls_tool' value");
+#endif /* !HTTPS_SUPPORT || !HAVE_FORK || !HAVE_WAITPID */
+ }
+
+ if (0 != pthread_create (&pt_client,
+ NULL,
+ &run_usock_client,
+ sock))
+ externalErrorExitDesc ("pthread_create() failed");
+ // TODO: support external events
+ if (0 != pthread_join (pt_client,
+ NULL))
+ externalErrorExitDesc ("pthread_join() failed");
+ if (0 != pthread_join (pt_server,
+ NULL))
+ externalErrorExitDesc ("pthread_join() failed");
+#if defined(HTTPS_SUPPORT) && defined(HAVE_FORK) && defined(HAVE_WAITPID)
+ if (test_tls && (TLS_LIB_GNUTLS != use_tls_tool))
+ {
+ if ((pid_t) -1 == waitpid (pid, NULL, 0))
+ externalErrorExitDesc ("waitpid() failed");
+ }
+#endif /* HTTPS_SUPPORT && HAVE_FORK && HAVE_WAITPID */
+ if (! client_done)
+ externalErrorExitDesc ("The client thread has not signalled " \
+ "successful finish");
+ if (! app_done)
+ externalErrorExitDesc ("The application thread has not signalled " \
+ "successful finish");
+ MHD_daemon_destroy (d);
+ return 0;
+}
+
+
+enum test_msg_type
+{
+ test_msg_large_app_data,
+ test_msg_large_rclient_data,
+ test_msg_vlarge_app_data,
+ test_msg_vlarge_rclient_data
+};
+
+/**
+ * Initialise test message data
+ * @param buf the pointer to the buffer to fill with the test data
+ * @param buf_size the size of the @a buf
+ * @param msg_type the type of the data to fill the @a buf
+ * @return the @a buf pointer
+ */
+static void *
+init_test_msg (void *buf, size_t buf_size, enum test_msg_type msg_type)
+{
+ size_t i;
+ char *const text_buf = (char *) buf;
+ uint8_t *const bin_buf = (uint8_t *) buf;
+ if (0 == buf_size)
+ return buf;
+ switch (msg_type)
+ {
+ case test_msg_large_app_data:
+ case test_msg_large_rclient_data:
+ /* Simulate text data */
+ for (i = 0; i < buf_size; ++i)
+ {
+ size_t pos;
+ if (test_msg_large_app_data == msg_type)
+ pos = i + 43;
+ else
+ pos = i + 26;
+ if ((0 == i) || (2 == pos % 100) )
+ text_buf[i] =
+ (char) (unsigned char) ((test_msg_large_app_data == msg_type) ?
+ ('Z' - pos % ('Z' - 'A' + 1)) :
+ ('A' + pos % ('Z' - 'A' + 1)));
+ else if (0 == pos % 100)
+ text_buf[i] = '.';
+ else if (1 == pos % 100)
+ text_buf[i] = ' ';
+ else if ((99 != pos % 100) && (2 != pos % 100) && (0 == pos % 5))
+ text_buf[i] = ' ';
+ else if (test_msg_large_app_data == msg_type)
+ text_buf[i] = (char) (unsigned char) ('z' - pos % ('z' - 'a' + 1));
+ else
+ text_buf[i] = (char) (unsigned char) ('a' + pos % ('z' - 'a' + 1));
+ }
+ break;
+ case test_msg_vlarge_app_data:
+ /* Simulate binary data */
+ for (i = 0; i < buf_size; ++i)
+ {
+ bin_buf[i] = (uint8_t) ((i + 182) & 0xFF);
+ }
+ break;
+ case test_msg_vlarge_rclient_data:
+ /* Simulate binary data */
+ for (i = 0; i < buf_size; ++i)
+ {
+ bin_buf[i] = (uint8_t) ((111 - i) & 0xFF);
+ }
+ break;
+ default:
+ exit (99);
+ break;
+ }
+ return buf;
+}
+
+
+/**
+ * Perform initialisation of variables used in all check in this test
+ * @return true if succeed,
+ * false if failed.
+ */
+static bool
+global_test_init (void)
+{
+ global_port = 0;
+
+ if (use_large || use_vlarge)
+ {
+ unsigned int i;
+ size_t alloc_size;
+ alloc_size = use_vlarge ? (256U * 1024U) : (17U * 1024U);
+ for (i = 0; i < (sizeof(alloc_ptr) / sizeof(alloc_ptr[0])); ++i)
+ {
+ alloc_ptr[i] = malloc (alloc_size);
+ if (NULL == alloc_ptr[i])
+ {
+ for (--i; i < (sizeof(alloc_ptr) / sizeof(alloc_ptr[0])); --i)
+ {
+ free (alloc_ptr[i]);
+ }
+ return false;
+ }
+ }
+
+ rclient_msg_size = alloc_size;
+ rclient_msg = init_test_msg (alloc_ptr[0], rclient_msg_size,
+ use_vlarge ?
+ test_msg_vlarge_rclient_data :
+ test_msg_large_rclient_data);
+ app_msg_size = alloc_size;
+ app_msg = init_test_msg (alloc_ptr[1], app_msg_size,
+ use_vlarge ?
+ test_msg_vlarge_app_data :
+ test_msg_large_app_data);
+ }
+ else
+ {
+ unsigned int i;
+ for (i = 0; i < (sizeof(alloc_ptr) / sizeof(alloc_ptr[0])); ++i)
+ alloc_ptr[i] = NULL;
+
+ rclient_msg_size = mhd_SSTR_LEN ("Hello");
+ rclient_msg = "Hello";
+ app_msg_size = mhd_SSTR_LEN ("World");
+ app_msg = "World";
+ }
+ return true;
+}
+
+
+/**
+ * Perform de-initialisation of variables with memory de-allocation if required.
+ */
+static void
+global_test_deinit (void)
+{
+ unsigned int i;
+ for (i = ((sizeof(alloc_ptr) / sizeof(alloc_ptr[0])) - 1);
+ i < (sizeof(alloc_ptr) / sizeof(alloc_ptr[0]));
+ --i)
+ {
+ if (NULL != alloc_ptr[i])
+ free (alloc_ptr[i]);
+ }
+}
+
+
+int
+main (int argc,
+ char *const *argv)
+{
+ unsigned int error_count = 0;
+ unsigned int res;
+
+ use_vlarge = (0 != has_in_name (argv[0], "_vlarge"));
+ use_large = (! use_vlarge) && (0 != has_in_name (argv[0], "_large"));
+
+ use_tls_tool = TLS_CLI_NO_TOOL;
+ test_tls = has_in_name (argv[0], "_tls");
+
+ verbose = ! (has_param (argc, argv, "-q") ||
+ has_param (argc, argv, "--quiet") ||
+ has_param (argc, argv, "-s") ||
+ has_param (argc, argv, "--silent"));
+
+ if ((((int) ((~((unsigned int) 0U)) >> 1)) / 1000) < test_timeout)
+ {
+ fprintf (stderr, "The test timeout value (%d) is too large.\n"
+ "The test cannot run.\n", test_timeout);
+ fprintf (stderr, "The maximum allowed timeout value is %d.\n",
+ (((int) ((~((unsigned int) 0U)) >> 1)) / 1000));
+ return 3;
+ }
+
+ if (test_tls)
+ {
+ use_tls_tool = TLS_LIB_GNUTLS; /* Should be always available as MHD uses it when TLS is supported. */
+#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 */
+ 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;
+ case TLS_CLI_NO_TOOL:
+ default:
+ externalErrorExitDesc ("Wrong 'use_tls_tool' value");
+ }
+ }
+ if ( (TLS_LIB_GNUTLS == use_tls_tool) &&
+ (GNUTLS_E_SUCCESS != gnutls_global_init ()) )
+ externalErrorExitDesc ("gnutls_global_init() failed");
+
+#else /* ! HTTPS_SUPPORT */
+ fprintf (stderr, "HTTPS support was disabled by configure.\n");
+ return 77;
+#endif /* ! HTTPS_SUPPORT */
+ }
+
+ if (! global_test_init ())
+ {
+#ifdef HTTPS_SUPPORT
+ if (test_tls && (TLS_LIB_GNUTLS == use_tls_tool))
+ gnutls_global_deinit ();
+#endif /* HTTPS_SUPPORT */
+ fprintf (stderr, "Failed to initialise the test.\n");
+ return 99;
+ }
+
+ /* run tests */
+ if (verbose)
+ printf ("Starting HTTP \"Upgrade\" tests with %s connections and "
+ "%s size messages.\n",
+ test_tls ? "TLS" : "plain",
+ use_large ? "large" : (use_vlarge ? "very large" : "basic"));
+ res = test_upgrade ();
+ fflush_allstd ();
+ error_count += res;
+ if (res)
+ fprintf (stderr,
+ "FAILED: HTTP Upgrade, return code %u.\n",
+ res);
+ else if (verbose)
+ printf ("PASSED: HTTP Upgrade.\n");
+
+ // TODO: add thread-per-connection testing
+ // TODO: add external events testing
+
+ /* report result */
+ if (0 != error_count)
+ fprintf (stderr,
+ "Error (code: %u)\n",
+ error_count);
+
+ global_test_deinit ();
+#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/tests/upgrade/tls_test_keys.h b/src/tests/upgrade/tls_test_keys.h
@@ -0,0 +1,183 @@
+/*
+ This file is part of libmicrohttpd
+ Copyright (C) 2006, 2007, 2008 Christian Grothoff (and other contributing authors)
+ Copyright (C) 2021-2022 Evgeny Grin (Karlson2k)
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#ifndef MHD_TLS_TEST_KEYS_H
+#define MHD_TLS_TEST_KEYS_H
+
+/* Test Certificates */
+
+/* Certificate Authority cert */
+static const char ca_cert_pem[] =
+ "-----BEGIN CERTIFICATE-----\n\
+MIIGITCCBAmgAwIBAgIBADANBgkqhkiG9w0BAQsFADCBgTELMAkGA1UEBhMCUlUx\n\
+DzANBgNVBAgMBk1vc2NvdzEPMA0GA1UEBwwGTW9zY293MRswGQYDVQQKDBJ0ZXN0\n\
+LWxpYm1pY3JvaHR0cGQxITAfBgkqhkiG9w0BCQEWEm5vYm9keUBleGFtcGxlLm9y\n\
+ZzEQMA4GA1UEAwwHdGVzdC1DQTAgFw0yMTA0MDcxNzM2MThaGA8yMTIxMDMxNDE3\n\
+MzYxOFowgYExCzAJBgNVBAYTAlJVMQ8wDQYDVQQIDAZNb3Njb3cxDzANBgNVBAcM\n\
+Bk1vc2NvdzEbMBkGA1UECgwSdGVzdC1saWJtaWNyb2h0dHBkMSEwHwYJKoZIhvcN\n\
+AQkBFhJub2JvZHlAZXhhbXBsZS5vcmcxEDAOBgNVBAMMB3Rlc3QtQ0EwggIiMA0G\n\
+CSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDdaWupA4qZjCBNkJoJOm5xnCaizl36\n\
+ZLUwp4xBL/YfXPWE3LkmAREiVI/YnAb8l6G7CJnz8dTsOJWkNXG6T1KVP5/2RvBI\n\
+IaaaufRIAl7hEnj1j9E2hQlV2fxF2ZNhz+nqi0LqKV4LJSpclkXADf2FA9HsVRP/\n\
+B7zYh+DP0fSU8V6bsu8XCeRGshroAPrc8rH8lFEEXpNLNIqQr8yKx6SmdB6hfja6\n\
+6SQ0++qBhl0aJtn4LHWZohgjBmkIaGFPYIJLgxQ/xyp2Grz2q7lGKJ+zBkBF8iOP\n\
+t3x+F1hSCBnr/DGYWmjEm5tYm+7pyuriPddXdCc8+qa2LxMZo3EXxLo5YISpPCyw\n\
+Z7V3YAOZTr3m1C24LiYvPehCq1CTIkhhmqtlVJXU7ISD48cx9y+5Pi34wtbTI/gN\n\
+x4voyTLAfyavKMmIpxxIRsWldiF2n06HdvCRVdihDQUad10ygTmWf1J/s2ZETAtH\n\
+QaSd7MD389t6nQFtTIXigsNKnnDPlrtxt7rOLvLQeR0K04Gzrf/scheOanRAfOXH\n\
+KNBFU7YkDFG8rqizlC65rx9qeXFYXQcHZTuqxK7tgZnSgJat3E70VbTSCsEEG7eR\n\
+bNX/fChUKAIIpWaiW6HDlKLl6m2y+BzM91umBsKOqTvntMVFBSF9pVYlXK854aIR\n\
+q8A2Xujd012seQIDAQABo4GfMIGcMAsGA1UdDwQEAwICpDASBgNVHRMBAf8ECDAG\n\
+AQH/AgEBMB0GA1UdDgQWBBRYdUPApWoxw4U13Rqsjf9AHdbpLDATBgNVHSUEDDAK\n\
+BggrBgEFBQcDATAkBglghkgBhvhCAQ0EFxYVVGVzdCBsaWJtaWNyb2h0dHBkIENB\n\
+MB8GA1UdIwQYMBaAFFh1Q8ClajHDhTXdGqyN/0Ad1uksMA0GCSqGSIb3DQEBCwUA\n\
+A4ICAQBvrrcTKVeI1EYnXo4BQD4oCvf9z1fYQmL21EbHwgjg1nmaPkvStgWAc5p1\n\
+kKwySrpEMKXfu68X76RccXZyWWIamEjz2OCWYZgjX6d6FpjhLphL8WxXDy5C9eay\n\
+ixN7+URz2XQoi22wqR+tCPDhrIzcMPyMkx/6gRgcYeDnaFrkdSeSsKsID4plfcIj\n\
+ISWJDvv+IAgrtsG1NVHnGwpAv0od3A8/4/fR6PPyewaU3aydvjZ7Au8O9DGDjlU9\n\
+9HdlOkkY6GVJ1pfGZib7cV7lhy0D2kj1g9xZh97YjpoUfppPl9r+6A8gDm0hXlAD\n\
+TlzNYlwTb681ZEoSd9PiLEY8HETssHlays2dYXdcNwAEp69iIHz8q1Q98Be9LScl\n\
+WEzgaOT9U7lpIw/MWbELoMsC+Ecs1cVWBIuiIq8aSG2kRr1x3S8yVXbAohAXif2s\n\
+E6puieM/VJ25iaNhkbLmDkk58QVVmn9NZNv6ETxuSQMp9e0EwbVlj68vzClQ91Y/\n\
+nmAiGcLFUEwB9G0szv9+vR+oDW4IkvdFZSUbcICd2cnynnwAD395onqS4hEZO1xM\n\
+Gy5ZldbTMTjgn7fChNopz15ChPBnwFIjhm+S0CyiLRQAowfknRVq2IBkj7/5kOWg\n\
+4mcxcq76HoQWK/8X/8RFL1eFVAvY7TNHYJ0RS51DMuwCNQictA==\n\
+-----END CERTIFICATE-----";
+
+
+/* test server key */
+static const char srv_signed_key_pem[] =
+ "-----BEGIN PRIVATE KEY-----\n\
+MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCff7amw9zNSE+h\n\
+rOMhBrzbbsJluUP3gmd8nOKY5MUimoPkxmAXfp2L0il+MPZT/ZEmo11q0k6J2jfG\n\
+UBQ+oZW9ahNZ9gCDjbYlBblo/mqTai+LdeLO3qk53d0zrZKXvCO6sA3uKpG2WR+g\n\
++sNKxfYpIHCpanqBU6O+degIV/+WKy3nQ2Fwp7K5HUNj1u0pg0QQ18yf68LTnKFU\n\
+HFjZmmaaopWki5wKSBieHivzQy6w+04HSTogHHRK/y/UcoJNSG7xnHmoPPo1vLT8\n\
+CMRIYnSSgU3wJ43XBJ80WxrC2dcoZjV2XZz+XdQwCD4ZrC1ihykcAmiQA+sauNm7\n\
+dztOMkGzAgMBAAECggEAIbKDzlvXDG/YkxnJqrKXt+yAmak4mNQuNP+YSCEdHSBz\n\
++SOILa6MbnvqVETX5grOXdFp7SWdfjZiTj2g6VKOJkSA7iKxHRoVf2DkOTB3J8np\n\
+XZd8YaRdMGKVV1O2guQ20Dxd1RGdU18k9YfFNsj4Jtw5sTFTzHr1P0n9ybV9xCXp\n\
+znSxVfRg8U6TcMHoRDJR9EMKQMO4W3OQEmreEPoGt2/+kMuiHjclxLtbwDxKXTLP\n\
+pD0gdg3ibvlufk/ccKl/yAglDmd0dfW22oS7NgvRKUve7tzDxY1Q6O5v8BCnLFSW\n\
+D+z4hS1PzooYRXRkM0xYudvPkryPyu+1kEpw3fNsoQKBgQDRfXJo82XQvlX8WPdZ\n\
+Ts3PfBKKMVu3Wf8J3SYpuvYT816qR3ot6e4Ivv5ZCQkdDwzzBKe2jAv6JddMJIhx\n\
+pkGHc0KKOodd9HoBewOd8Td++hapJAGaGblhL5beIidLKjXDjLqtgoHRGlv5Cojo\n\
+zHa7Viel1eOPPcBumhp83oJ+mQKBgQDC6PmdETZdrW3QPm7ZXxRzF1vvpC55wmPg\n\
+pRfTRM059jzRzAk0QiBgVp3yk2a6Ob3mB2MLfQVDgzGf37h2oO07s5nspSFZTFnM\n\
+KgSjFy0xVOAVDLe+0VpbmLp1YUTYvdCNowaoTE7++5rpePUDu3BjAifx07/yaSB+\n\
+W+YPOfOuKwKBgQCGK6g5G5qcJSuBIaHZ6yTZvIdLRu2M8vDral5k3793a6m3uWvB\n\
+OFAh/eF9ONJDcD5E7zhTLEMHhXDs7YEN+QODMwjs6yuDu27gv97DK5j1lEsrLUpx\n\
+XgRjAE3KG2m7NF+WzO1K74khWZaKXHrvTvTEaxudlO3X8h7rN3u7ee9uEQKBgQC2\n\
+wI1zeTUZhsiFTlTPWfgppchdHPs6zUqq0wFQ5Zzr8Pa72+zxY+NJkU2NqinTCNsG\n\
+ePykQ/gQgk2gUrt595AYv2De40IuoYk9BlTMuql0LNniwsbykwd/BOgnsSlFdEy8\n\
+0RQn70zOhgmNSg2qDzDklJvxghLi7zE5aV9//V1/ewKBgFRHHZN1a8q/v8AAOeoB\n\
+ROuXfgDDpxNNUKbzLL5MO5odgZGi61PBZlxffrSOqyZoJkzawXycNtoBP47tcVzT\n\
+QPq5ZOB3kjHTcN7dRLmPWjji9h4O3eHCX67XaPVMSWiMuNtOZIg2an06+jxGFhLE\n\
+qdJNJ1DkyUc9dN2cliX4R+rG\n\
+-----END PRIVATE KEY-----";
+
+/* test server CA signed certificates */
+static const char srv_signed_cert_pem[] =
+ "-----BEGIN CERTIFICATE-----\n\
+MIIFSzCCAzOgAwIBAgIBBDANBgkqhkiG9w0BAQsFADCBgTELMAkGA1UEBhMCUlUx\n\
+DzANBgNVBAgMBk1vc2NvdzEPMA0GA1UEBwwGTW9zY293MRswGQYDVQQKDBJ0ZXN0\n\
+LWxpYm1pY3JvaHR0cGQxITAfBgkqhkiG9w0BCQEWEm5vYm9keUBleGFtcGxlLm9y\n\
+ZzEQMA4GA1UEAwwHdGVzdC1DQTAgFw0yMjA0MjAxODQzMDJaGA8yMTIyMDMyNjE4\n\
+NDMwMlowZTELMAkGA1UEBhMCUlUxDzANBgNVBAgMBk1vc2NvdzEPMA0GA1UEBwwG\n\
+TW9zY293MRswGQYDVQQKDBJ0ZXN0LWxpYm1pY3JvaHR0cGQxFzAVBgNVBAMMDnRl\n\
+c3QtbWhkc2VydmVyMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAn3+2\n\
+psPczUhPoazjIQa8227CZblD94JnfJzimOTFIpqD5MZgF36di9IpfjD2U/2RJqNd\n\
+atJOido3xlAUPqGVvWoTWfYAg422JQW5aP5qk2ovi3Xizt6pOd3dM62Sl7wjurAN\n\
+7iqRtlkfoPrDSsX2KSBwqWp6gVOjvnXoCFf/list50NhcKeyuR1DY9btKYNEENfM\n\
+n+vC05yhVBxY2ZpmmqKVpIucCkgYnh4r80MusPtOB0k6IBx0Sv8v1HKCTUhu8Zx5\n\
+qDz6Nby0/AjESGJ0koFN8CeN1wSfNFsawtnXKGY1dl2c/l3UMAg+GawtYocpHAJo\n\
+kAPrGrjZu3c7TjJBswIDAQABo4HmMIHjMAsGA1UdDwQEAwIFoDAMBgNVHRMBAf8E\n\
+AjAAMBYGA1UdJQEB/wQMMAoGCCsGAQUFBwMBMDEGA1UdEQQqMCiCDnRlc3QtbWhk\n\
+c2VydmVyhwR/AAABhxAAAAAAAAAAAAAAAAAAAAABMB0GA1UdDgQWBBQ57Z06WJae\n\
+8fJIHId4QGx/HsRgDDAoBglghkgBhvhCAQ0EGxYZVGVzdCBsaWJtaWNyb2h0dHBk\n\
+IHNlcnZlcjARBglghkgBhvhCAQEEBAMCBkAwHwYDVR0jBBgwFoAUWHVDwKVqMcOF\n\
+Nd0arI3/QB3W6SwwDQYJKoZIhvcNAQELBQADggIBAI7Lggm/XzpugV93H5+KV48x\n\
+X+Ct8unNmPCSzCaI5hAHGeBBJpvD0KME5oiJ5p2wfCtK5Dt9zzf0S0xYdRKqU8+N\n\
+aKIvPoU1hFixXLwTte1qOp6TviGvA9Xn2Fc4n36dLt6e9aiqDnqPbJgBwcVO82ll\n\
+HJxVr3WbrAcQTB3irFUMqgAke/Cva9Bw79VZgX4ghb5EnejDzuyup4pHGzV10Myv\n\
+hdg+VWZbAxpCe0S4eKmstZC7mWsFCLeoRTf/9Pk1kQ6+azbTuV/9QOBNfFi8QNyb\n\
+18jUjmm8sc2HKo8miCGqb2sFqaGD918hfkWmR+fFkzQ3DZQrT+eYbKq2un3k0pMy\n\
+UySy8SRn1eadfab+GwBVb68I9TrPRMrJsIzysNXMX4iKYl2fFE/RSNnaHtPw0C8y\n\
+B7memyxPRl+H2xg6UjpoKYh3+8e44/XKm0rNIzXjrwA8f8gnw2TbqmMDkj1YqGnC\n\
+SCj5A27zUzaf2pT/YsnQXIWOJjVvbEI+YKj34wKWyTrXA093y8YI8T3mal7Kr9YM\n\
+WiIyPts0/aVeziM0Gunglz+8Rj1VesL52FTurobqusPgM/AME82+qb/qnxuPaCKj\n\
+OT1qAbIblaRuWqCsid8BzP7ZQiAnAWgMRSUg1gzDwSwRhrYQRRWAyn/Qipzec+27\n\
+/w0gW9EVWzFhsFeGEssi\n\
+-----END CERTIFICATE-----";
+
+/* test server self signed certificates */
+static const char srv_self_signed_cert_pem[] =
+ "-----BEGIN CERTIFICATE-----\n"
+ "MIIDJzCCAg+gAwIBAgIUOKf6e6Heee2XA+yF5St3t+fVM40wDQYJKoZIhvcNAQEF\n"
+ "BQAwFDESMBAGA1UEAwwJbG9jYWxob3N0MCAXDTIyMTAxMDA4MzQ0N1oYDzIxMjIw\n"
+ "OTE2MDgzNDQ3WjAUMRIwEAYDVQQDDAlsb2NhbGhvc3QwggEiMA0GCSqGSIb3DQEB\n"
+ "AQUAA4IBDwAwggEKAoIBAQClivgF8Xq0ekQli++0l7Q5JFwJCuLf04Cb1UKIS80U\n"
+ "CfphFd1ILJepNw4bWR3OV1sRI1vFiw6LnCz53vOwVNyiZ+sMGi4bDX4AV9Xd+F83\n"
+ "xhG8AjOmKTayW0TxSIvt47Qd5S/4fgraxMtvqrRRBen30iKOwX7uNF/4dYb9vdin\n"
+ "OldV/e8uzbqSurMGkNDznOeSaNBmdO/7x0VMFZM2hwmHyiiw75/j4BhUlLCcMEvK\n"
+ "oN+YHNCNcTt3Qm1vVuiGXmh9QreOV09Gc1SzAltxF2gmI0jzw8r/duz18QXMNsMw\n"
+ "El/Ah4+02gR70L7qlgttN1NPU3RJpK/L34J7yg649wHTAgMBAAGjbzBtMB0GA1Ud\n"
+ "DgQWBBROVferD+YYcV1YEnFgC0jYm5X9BjAfBgNVHSMEGDAWgBROVferD+YYcV1Y\n"
+ "EnFgC0jYm5X9BjAPBgNVHRMBAf8EBTADAQH/MBoGA1UdEQQTMBGCCWxvY2FsaG9z\n"
+ "dIcEfwAAATANBgkqhkiG9w0BAQUFAAOCAQEAoRbozsm5xXdNX3VO++s2LMzw5KM9\n"
+ "RpIInHNkMJbnyLJFKJ8DF7nTxSGCA38YMkX3tphPNKZXbg+V64Dqr/XpzOVyiinU\n"
+ "7hIwyUdSSKKyErZxIWR97lY6Q3SOyPAg8ZElbtvSsSzmd772VE23VTXGDi7AW0PQ\n"
+ "hag9N2EEnHURMvID15O+UXyFpDdyUyQIbx3HuswsGDH9xBTm4irLyrZwO0KwKg5a\n"
+ "JBeUiPs0SYRRfn9/MoE6VwAnmOCg3LLR6ZPU3hQtTPLHj2Op1g5fey3X3X6lC+JC\n"
+ "K6dNZc1zBFPz8KANGUsFYbmoP2bvAAA+6KwCnZZEflUgE7/HFEmQhVOezw==\n"
+ "-----END CERTIFICATE-----\n";
+
+/* test server key */
+static const char srv_self_signed_key_pem[] =
+ "-----BEGIN PRIVATE KEY-----\n"
+ "MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQClivgF8Xq0ekQl\n"
+ "i++0l7Q5JFwJCuLf04Cb1UKIS80UCfphFd1ILJepNw4bWR3OV1sRI1vFiw6LnCz5\n"
+ "3vOwVNyiZ+sMGi4bDX4AV9Xd+F83xhG8AjOmKTayW0TxSIvt47Qd5S/4fgraxMtv\n"
+ "qrRRBen30iKOwX7uNF/4dYb9vdinOldV/e8uzbqSurMGkNDznOeSaNBmdO/7x0VM\n"
+ "FZM2hwmHyiiw75/j4BhUlLCcMEvKoN+YHNCNcTt3Qm1vVuiGXmh9QreOV09Gc1Sz\n"
+ "AltxF2gmI0jzw8r/duz18QXMNsMwEl/Ah4+02gR70L7qlgttN1NPU3RJpK/L34J7\n"
+ "yg649wHTAgMBAAECggEAERbbCtYGakoy7cNX8Ac3Kiz4OVC/4gZWAQBPeX2FwrtS\n"
+ "9yHIMbK0x1mxIZ6eBpabBpZlW2vDCSOKuxLKiloAWt2qdJnhR5apesSWhe8leT7/\n"
+ "xq5dgZpAlMH6SIRKObknd2yY+qicW0A0licDrVeUcypkueL8xP9wJtiPInOuQXkI\n"
+ "QROhB13eStRuRKYwOn5gtwAHJ+J1DFKKiqpBOkrSYf4625StGegJO9+bjK0ei+0W\n"
+ "tp6unpiwA/lXTgz6Xim1Z3fzWs4XjFgVKzK5s/6yBJjr8spHX6lv7QsahP4w6HZ/\n"
+ "VcRxP6cJNd/otiTEtJXpbxiiyccwXm/AOcOn22P1cQKBgQDAnY/0G/ap/G98pneE\n"
+ "suzNXhWOQ8JoL8d66Io8vwTvfiJggfgUcwblI7pPCrSlaZMR7/q6JImE53lZtPk8\n"
+ "eI3c9lN0ocr8E7+huDpYdk7cMYj9SuxySsXoMLiMqzHFi+NcIhKMF56kk6a5CFCt\n"
+ "yP1Ofy76LVweGE3XvTwpwE7wUQKBgQDcBLyH1cC71s0I0Gz28AyELV9hPhasjAKO\n"
+ "12CVbeBVTPd+28uk/3o80wSrTksc6H5ehAA2aTvrb4OhwssWNL+D0fS8YK2cJ3V0\n"
+ "FJxGAM266+vC4d/8jRTHJnc+6PP3ix5t6vAt+K2Y0fePtefLqf4ebgXx/ODAj3J2\n"
+ "aZKBldjK4wKBgGIRFpTLk/eR/dUyEBHw4x3gdAsdtqJDCUYrlQ4+ly20Q55tLbiD\n"
+ "pBQP77CEm9rH+MgeLcKODbIsBB3HRUojet7wTydHpMhY6a1V1ebqPVZgpgWIGwBJ\n"
+ "z59bBusf0lRo15Y2Bslq0SurvSvh7um8NjO8D1fytj7gUumvgC0lq0sxAoGBAI1+\n"
+ "kkx9IBTtIDER8XGhkTsT/uoHxwcyh5abVmbjIclZ1TUFX2L+Vft17ePJVy8BKfvY\n"
+ "wlY7uShBMBNAteDTDXNV/CGFv0DUc4myk4nFjIkwng9XufeuN3WX/Eo+AF/rXSdt\n"
+ "VwcJjYLhTWdjoe1tppqlQTeN3HCaEA+s92ZVGvXnAoGAMCXGS6WZl1e5wsHRq0Yy\n"
+ "8Ef2Wrk620bBjKHolkTfvgfhlvxeZM1sv1ioZGsOeQ0z7O7wdJhvL0M/WAG+3yQj\n"
+ "HSXp81T1vOICPwNYZf8xcvbLKmvj7rHFt6ZAZF2o4EK8ReZTRyA3DUpBCDY+s3FN\n"
+ "GmBv0D7N3QP0CT3SzfQrPkc=\n"
+ "-----END PRIVATE KEY-----\n";
+
+#endif