libmicrohttpd2

HTTP server C library (MHD 2.x, alpha)
Log | Files | Refs | README | LICENSE

commit 8d35a95be9c4caf1ce304e744c51cd7feda44ad5
parent 7caa8e7968cdef9f80482e16347707de728d9fcb
Author: Christian Grothoff <grothoff@gnunet.org>
Date:   Wed,  4 Dec 2024 20:00:00 +0100

add TLS tests

Diffstat:
Msrc/tests/client_server/Makefile.am | 31+++++++++++++++++++++++++++++--
Msrc/tests/client_server/libtest.c | 120+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------
Msrc/tests/client_server/libtest.h | 217+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------------------
Msrc/tests/client_server/libtest_convenience.c | 141+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
Msrc/tests/client_server/libtest_convenience_client_request.c | 269+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------------------
Msrc/tests/client_server/libtest_convenience_server_reply.c | 2+-
Asrc/tests/client_server/test_cert_tls.c | 170+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/tests/client_server/test_tls.c | 330+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
8 files changed, 1143 insertions(+), 137 deletions(-)

diff --git a/src/tests/client_server/Makefile.am b/src/tests/client_server/Makefile.am @@ -28,6 +28,12 @@ check_PROGRAMS = \ test_client_server \ test_postprocessor +if MHD_ENABLE_HTTPS +check_PROGRAMS += \ + test_tls \ + test_cert_tls +endif + TESTS = $(check_PROGRAMS) @@ -39,6 +45,7 @@ libmhdt_la_SOURCES = \ libtest_convenience.c \ libtest_convenience_client_request.c \ libtest_convenience_server_reply.c +# TODO: fix flags, use configure-detected libmhdt_la_LIBADD = \ -lpthread \ -lcurl \ @@ -47,10 +54,30 @@ libmhdt_la_LIBADD = \ test_client_server_SOURCES = \ test_client_server.c test_client_server_LDADD = \ - libmhdt.la + libmhdt.la + +test_tls_SOURCES = \ + test_tls.c +test_tls_LDADD = \ + libmhdt.la +test_cert_tls_SOURCES = \ + test_cert_tls.c +test_cert_tls_LDADD = \ + libmhdt.la test_postprocessor_SOURCES = \ test_postprocessor.c test_postprocessor_LDADD = \ - libmhdt.la + libmhdt.la + +# TODO: fix out-of-tree 'make check' +EXTRA_DIST = \ + data/root-ca.crt \ + data/inter1-ca.crt \ + data/inter2-ca.crt \ + data/test-server.crt \ + data/test-server-key.pem \ + data/check_certs.sh \ + data/make_chain.sh \ + data/chain.crt diff --git a/src/tests/client_server/libtest.c b/src/tests/client_server/libtest.c @@ -23,12 +23,14 @@ * @brief testing harness with clients against server * @author Christian Grothoff */ +#include "libtest.h" #include <pthread.h> #include <stdbool.h> #include <fcntl.h> #include <unistd.h> #include <errno.h> -#include "libtest.h" +#include <sys/stat.h> +#include <curl/curl.h> /** * A semaphore. @@ -419,6 +421,7 @@ cleanup: &res)); if (! cctxs[i].status) ret = false; + curl_slist_free_all (cctxs[i].pc.hosts); } test_check (0 == close (p[0])); test_check (0 == close (p[1])); @@ -481,7 +484,7 @@ MHDT_test (MHDT_ServerSetup ss_cb, void *ss_cb_cls, MHDT_ServerRunner run_cb, void *run_cb_cls, - const struct MHDT_Phase *phases) + struct MHDT_Phase *phases) { struct ServerContext ctx = { .run_cb = run_cb, @@ -493,8 +496,10 @@ MHDT_test (MHDT_ServerSetup ss_cb, const char *err; pthread_t server_phase_thr; pthread_t server_run_thr; - struct MHDT_PhaseContext pc; - char base_url[128]; + struct MHDT_PhaseContext pc_https; + struct MHDT_PhaseContext pc_http; + char base_http_url[128]; + char base_https_url[128]; unsigned int i; int p[2]; @@ -522,9 +527,16 @@ MHDT_test (MHDT_ServerSetup ss_cb, sc = MHD_daemon_start (d); if (MHD_SC_OK != sc) { +#ifdef FIXME_STATUS_CODE_TO_STRING_NOT_IMPLEMENTED fprintf (stderr, "Failed to start server: %s\n", - err); + MHD_status_code_to_string_lazy (sc)); +#else + fprintf (stderr, + "Failed to start server: %d\n", + sc); +#endif + MHD_daemon_destroy (d); return 1; } } @@ -537,11 +549,16 @@ MHDT_test (MHDT_ServerSetup ss_cb, MHD_DAEMON_INFO_FIXED_BIND_PORT, &info); test_check (MHD_SC_OK == sc); - snprintf (base_url, - sizeof (base_url), + snprintf (base_http_url, + sizeof (base_http_url), "http://localhost:%u/", (unsigned int) info.v_port); - pc.base_url = base_url; + snprintf (base_https_url, + sizeof (base_https_url), + "https://localhost:%u/", + (unsigned int) info.v_port); + pc_http.base_url = base_http_url; + pc_https.base_url = base_https_url; } if (0 != pthread_create (&server_phase_thr, NULL, @@ -551,6 +568,7 @@ MHDT_test (MHDT_ServerSetup ss_cb, fprintf (stderr, "Failed to start server phase thread: %s\n", strerror (errno)); + MHD_daemon_destroy (d); return 77; } ctx.finsig = p[0]; @@ -563,19 +581,28 @@ MHDT_test (MHDT_ServerSetup ss_cb, fprintf (stderr, "Failed to start server run thread: %s\n", strerror (errno)); + MHD_daemon_destroy (d); return 77; } for (i = 0; NULL != phases[i].label; i++) { + struct MHDT_Phase *pi = &phases[i]; + struct MHDT_PhaseContext *pc + = pi->use_tls + ? &pc_https + : &pc_http; + pc->phase = &phases[i]; + pc->hosts = NULL; fprintf (stderr, "Running test phase '%s'\n", - phases[i].label); - if (! run_client_phase (&phases[i], - &pc)) + pi->label); + if (! run_client_phase (pi, + pc)) { res = 1; goto cleanup; } + pc->hosts = NULL; /* client is done with phase */ semaphore_up (&ctx.client_sem); /* wait for server to have moved to new phase */ @@ -613,3 +640,74 @@ cleanup: test_check (0 == close (p[1])); return res; } + + +char * +MHDT_load_pem (const char *name) +{ + char path[256]; + int fd; + struct stat s; + char *buf; + + snprintf (path, + sizeof (path), + "data/%s", + name); + fd = open (path, + O_RDONLY); + if (-1 == fd) + { + fprintf (stderr, + "Failed to open %s: %s\n", + path, + strerror (errno)); + return NULL; + } + if (0 != + fstat (fd, + &s)) + { + fprintf (stderr, + "Failed to fstat %s: %s\n", + path, + strerror (errno)); + (void) close (fd); + return NULL; + } + if (((unsigned long long) s.st_size) >= (unsigned long long) SIZE_MAX) + { + fprintf (stderr, + "File %s too large (%llu >= %llu bytes) to malloc()\n", + path, + (unsigned long long) s.st_size, + (unsigned long long) SIZE_MAX); + (void) close (fd); + return NULL; + } + buf = malloc (((size_t) s.st_size + 1)); + if (NULL == buf) + { + fprintf (stderr, + "Failed to malloc(): %s\n", + strerror (errno)); + (void) close (fd); + return NULL; + } + if (-1 == + read (fd, // FIXME: read() should be called in loop to handle partial reads + buf, + (size_t) s.st_size)) + { + fprintf (stderr, + "Failed to read %s: %s\n", + path, + strerror (errno)); + free (buf); + (void) close (fd); + return NULL; + } + (void) close (fd); + buf[(size_t) s.st_size] = 0; + return buf; +} diff --git a/src/tests/client_server/libtest.h b/src/tests/client_server/libtest.h @@ -35,6 +35,14 @@ #endif #include "microhttpd2.h" + +/** + * A phase defines some server and client-side + * behaviors to execute. + */ +struct MHDT_Phase; + + /** * Information about the current phase. */ @@ -46,9 +54,21 @@ struct MHDT_PhaseContext const char *base_url; /** + * Data structure to keep around during the request because + * Curl. + */ + struct curl_slist *hosts; + + /** * Specific client we are running. */ unsigned int client_id; + + /** + * More details about the phase we are running. + */ + struct MHDT_Phase *phase; + }; @@ -62,7 +82,102 @@ struct MHDT_PhaseContext */ typedef const char * (*MHDT_ClientLogic)(const void *cls, - const struct MHDT_PhaseContext *pc); + struct MHDT_PhaseContext *pc); + + +struct MHDT_Phase +{ + + /** + * Name of the phase, for debugging/logging. + */ + const char *label; + + /** + * Logic for the MHD server for this phase. + */ + MHD_RequestCallback server_cb; + + /** + * Closure for @e server_cb. + */ + void *server_cb_cls; + + /** + * Logic for the CURL client for this phase. + */ + MHDT_ClientLogic client_cb; + + /** + * Closure for @e client_cb. + */ + const void *client_cb_cls; + + /** + * How long is the phase allowed to run at most before + * timing out. 0 for no timeout. + */ + unsigned int timeout_ms; + + /** + * How many clients should be run in parallel. + * 0 to run just one client. + */ + unsigned int num_clients; + + /** + * Set to true if clients should setup the connection to use TLS. + */ + bool use_tls; + + /** + * Set to true if clients should check server cert. + */ + bool check_server_cert; + + /** + * Client certificate to present to the server, NULL for none. + */ + const char *client_cert; + + /** + * Client private key to use, NULL for none. + */ + const char *client_priv; + + /** + * Server certificate to present to the client, NULL for default. + */ + const char *server_cert; + + /** + * Server private key to use, NULL for default. + */ + const char *server_priv; +}; + + +/** + * Load PEM file from data/ folder and return data in it. + * + * @param name name of PEM file to load + * @return NULL on error + */ +char * +MHDT_load_pem (const char *name); + + +/** + * Run request against the root URL of the + * hostname given in @a cls. + * + * @param cls closure with hostname to use + * @param pc context for the client + * @return error message, NULL on success + */ +const char * +MHDT_client_get_host (const void *cls, + struct MHDT_PhaseContext *pc); /** @@ -75,7 +190,7 @@ typedef const char * */ const char * MHDT_client_get_root (const void *cls, - const struct MHDT_PhaseContext *pc); + struct MHDT_PhaseContext *pc); /** @@ -90,7 +205,7 @@ MHDT_client_get_root (const void *cls, */ const char * MHDT_client_get_with_query (const void *cls, - const struct MHDT_PhaseContext *pc); + struct MHDT_PhaseContext *pc); /** @@ -104,7 +219,7 @@ MHDT_client_get_with_query (const void *cls, */ const char * MHDT_client_set_header (const void *cls, - const struct MHDT_PhaseContext *pc); + struct MHDT_PhaseContext *pc); /** @@ -119,7 +234,7 @@ MHDT_client_set_header (const void *cls, */ const char * MHDT_client_expect_header (const void *cls, - const struct MHDT_PhaseContext *pc); + struct MHDT_PhaseContext *pc); /** @@ -132,7 +247,7 @@ MHDT_client_expect_header (const void *cls, */ const char * MHDT_client_put_data (const void *cls, - const struct MHDT_PhaseContext *pc); + struct MHDT_PhaseContext *pc); /** @@ -145,7 +260,7 @@ MHDT_client_put_data (const void *cls, */ const char * MHDT_client_chunk_data (const void *cls, - const struct MHDT_PhaseContext *pc); + struct MHDT_PhaseContext *pc); /** @@ -257,53 +372,7 @@ struct MHDT_PostInstructions const char * MHDT_client_do_post ( const void *cls, - const struct MHDT_PhaseContext *pc); - - -/** - * A phase defines some server and client-side - * behaviors to execute. - */ -struct MHDT_Phase -{ - - /** - * Name of the phase, for debugging/logging. - */ - const char *label; - - /** - * Logic for the MHD server for this phase. - */ - MHD_RequestCallback server_cb; - - /** - * Closure for @e server_cb. - */ - void *server_cb_cls; - - /** - * Logic for the CURL client for this phase. - */ - MHDT_ClientLogic client_cb; - - /** - * Closure for @e client_cb. - */ - const void *client_cb_cls; - - /** - * How long is the phase allowed to run at most before - * timing out. 0 for no timeout. - */ - unsigned int timeout_ms; - - /** - * How many clients should be run in parallel. - * 0 to run just one client. - */ - unsigned int num_clients; -}; + struct MHDT_PhaseContext *pc); /** @@ -559,6 +628,44 @@ MHDT_server_setup_minimal (const void *cls, /** + * Initialize MHD daemon with TLS support, binding to any free port. + * + * @param cls closure + * @param[in,out] d daemon to initialize + * @return error message, NULL on success + */ +const char * +MHDT_server_setup_tls (const void *cls, + struct MHD_Daemon *d); + + +/** + * Initialize MHD daemon with TLS support using GnuTLS, binding to any free + * port. + * + * @param cls closure + * @param[in,out] d daemon to initialize + * @return error message, NULL on success + */ +const char * +MHDT_server_setup_gnutls (const void *cls, + struct MHD_Daemon *d); + + +/** + * Initialize MHD daemon with TLS support using OpenSSL, binding to any free + * port. + * + * @param cls closure + * @param[in,out] d daemon to initialize + * @return error message, NULL on success + */ +const char * +MHDT_server_setup_openssl (const void *cls, + struct MHD_Daemon *d); + + +/** * Function that runs an MHD daemon until * a read() against @a finsig succeeds. * @@ -620,6 +727,6 @@ MHDT_test (MHDT_ServerSetup ss_cb, void *ss_cb_cls, MHDT_ServerRunner run_cb, void *run_cb_cls, - const struct MHDT_Phase *phases); + struct MHDT_Phase *phases); #endif diff --git a/src/tests/client_server/libtest_convenience.c b/src/tests/client_server/libtest_convenience.c @@ -23,12 +23,12 @@ * @brief convenience functions for libtest users * @author Christian Grothoff */ +#include "libtest.h" #include <pthread.h> #include <stdbool.h> #include <fcntl.h> #include <unistd.h> #include <errno.h> -#include "libtest.h" #include <curl/curl.h> @@ -54,6 +54,143 @@ MHDT_server_setup_minimal (const void *cls, } +/** + * Setup TLS at @a d for the given backend @a be. + * + * @return NULL on success, otherwise error message + */ +static const char * +server_setup_tls (struct MHD_Daemon *d, + enum MHD_TlsBackend be) +{ + static const char *mem_cert = + "-----BEGIN CERTIFICATE-----\n\ +MIIDjTCCAnWgAwIBAgIUKkxAx2lVnvYcaNqBpJmTgXh1/VgwDQYJKoZIhvcNAQEL\n\ +BQAwVjELMAkGA1UEBhMCVVMxFjAUBgNVBAgMDU1hc3NhY2h1c2V0dHMxDzANBgNV\n\ +BAcMBkJvc3RvbjENMAsGA1UECgwEUm9vdDEPMA0GA1UEAwwGY2EuZ251MB4XDTI0\n\ +MTEyOTEyNDUyOFoXDTM0MTEyNzEyNDUyOFowVjELMAkGA1UEBhMCVVMxFjAUBgNV\n\ +BAgMDU1hc3NhY2h1c2V0dHMxDzANBgNVBAcMBkJvc3RvbjENMAsGA1UECgwEUm9v\n\ +dDEPMA0GA1UEAwwGY2EuZ251MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC\n\ +AQEA23YSrcGIBgZf9bbzTnmYFy+4tM82kUhsVFKxWCNEMdKmhaeVvXogyd6Evq4P\n\ +NvBGdUABDtHp4pSEijrxWbn8sxddTznoT/8IOuHI0/PtwXYP/sHQ/HzekEUVKN2Z\n\ +NMbMUzQfaJyiIV5TrZlaBwHjQ+sRs8E56C3cQjkwuyjll2zDsEfmEnPimZRAL3kb\n\ +wW8VFfBcR2Id+a9xKjwlnB4eXQFAgYINoRgCtUOUxSeFgNnwkOUSqDknO6Xi47YZ\n\ +EdLlHyUnv5eX547xUkrYhfQuQwaqpGrjHf3GFoysN8P9kd2f1qsJKtQcUbF9DDeZ\n\ +6ya47X/LBO8QflMsVjb1V3oz9QIDAQABo1MwUTAdBgNVHQ4EFgQUsvdZoX3RxdN6\n\ +wrONr31SOA9Qbc4wHwYDVR0jBBgwFoAUsvdZoX3RxdN6wrONr31SOA9Qbc4wDwYD\n\ +VR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAS3PyV7crGk9brqU90aML\n\ +2TWkjgzFb3/nASnpvVYiqyiV4neGiEjtDy7eVlqP6GlD2pYcVodY+ly9wNlo85/h\n\ +YfgCFFl37tMG7PpRac2qBqaSn1DpwsCb08LjRrOvoaRffWUikSoZmsYDlaCgl9nT\n\ +pGtIrz0BSoyu5mHalIZTVQOrbkNBNK6ZgnYy2iWuiLa5Z1xzKpsRBRaKJc1pcQE/\n\ +QVbPdCiyGQMPEVn/KHxitlycFoee/fA+izXVdstVwfig2DoMvrlGZvEkN1ER3Yz4\n\ +QPJ6HzOsBQL1F+YhnMCQfc2rpcwxAWf8JMy6jsCq42KGq53tkWqHyQ6Zu2SiLRYk\n\ +CA==\n\ +-----END CERTIFICATE-----"; + static const char *mem_key = + "-----BEGIN ENCRYPTED PRIVATE KEY-----\n\ +MIIFJDBWBgkqhkiG9w0BBQ0wSTAxBgkqhkiG9w0BBQwwJAQQJ1VSHi+akaaVYO3O\n\ +H7I0EAICCAAwDAYIKoZIhvcNAgkFADAUBggqhkiG9w0DBwQIZlNzQR1bh4IEggTI\n\ +8U86bfGmyAXXSi/R/l3G8ziZFyHrRE5Q/Q3uUW/jyUpe+S0gMRPqwW3V542ForbH\n\ +IH/Aa+KVxlwmsq0jlheCQewj9qZMQGuqa3iTl/OfCcuGMfsuQs2HsutoDMdEYuBI\n\ +6yOqNIrRvSHunZILLDpKz/AmCO6JnRiAwiSqPBixE5M+cm1qc7dy024REiW9l9K6\n\ +Hth9A0iYc94CUyUfHFj4CEkCNqk533Z2Ktkk3RQJnx5ORQG0iBJvoFiVODFKnoAk\n\ +Ge2HNrJH3bVvhQ+p8A/L4VmnWUCfcTyqgzo887WXRxORya6gcWWtrcEJGUbLh8sL\n\ +/mXFYj/0kEllIY+fHOmSx94I3GwBkQKER/CeOCIp+C392Pujgzrz23pdq20uIt3d\n\ +FCgbnIB+5IwOwQcqCkTYa1+Y5qCa6eFLgd8PXGTDyFwP4BHfG6WT/ctHQFi8vnXV\n\ +D1S726do1mA6CFE3DYmi45sf+Te2Xb346xk1GTSWtxGh9y4FblFDAWva4oTuvxPR\n\ +IDseBhXBsIqnOy1gb/5cGj0SIOQzqR1qlg4igv3UZFC8cVl+fNnngDBiX+nTYQVm\n\ +rDyxTzcX9txPSNpLyYRdNHwLGpzZAMoN46bUFnxt0cvRWN6MA7j1r0TYWBZKJ7b7\n\ +Yt/SuYsqSE0UJQEJz4QcQnlxu3qu4HJl7dOlto3fa42MWTkOcNr9XinHmKCZ9oYZ\n\ +PYNTggRGMXlqm66KmHWDqXqw9CeufprHq15SIJJR8v4SlvEZr+YlYQeHRI4E+FDA\n\ +mEFZy/U3ZL7ZHSDsEvpeBzIJkWxHobt57BIxYHE8KN0ZIz/mJZTxljacblFWnJRb\n\ +AUXTfrRZn3lGX+4WA6Biilwyxb71slCKaiz28C55Hnj1UwoUF8vNA3G2FGAX5Wk0\n\ +m3J2SoCHtJQYc/3lEC7zR9i3/F/7vgRxZMUWt/y6KRYq8ZnoQl3Eo2yvJYX/z7I6\n\ +JyqexAx3OvA+frN3rbO/o/k6w9333Smi0QxZzDM9tHn1BAgAtmyC1lizzKn7hDYK\n\ +o/eaPeatILbS0a/bHJBbP/R53keVr0hJ3MWK2nb/DV5Dl9j4Z6sHpo3P9L+Kq06y\n\ +G9q7NhBd7cxGq4AkCp+eSjqTvwgOX1PtAry00TUmzisLz8gIYutwJqbfZGL8WpR/\n\ +/wnLQXuM/tPLdQNy+PZeTQnPFwWQeZz4VgkMRhHV2xDw0mpzE+cdD204+YjHVdMH\n\ +D4MNrDlUmKM0OVoYgXd9YyLKzYVgW95GvY1X0SxTlIUuDiRv/SqRsurPFkSG457d\n\ +zmTUny1NRsnbv9bTXqt1Xewqsylyu02N1dZvjIzBnYMVYXl0r4aej1VNEXozQtWO\n\ +YRfWaZ29dXwZqUzd83ETQvhI4mZbwAlHbqm/CoyY6Vw4Am8hGa7II134lz2b3tkr\n\ +F1zBkvzzl6+HXewGOEjm+YorDMtfADiU/hkkykWq01NG3QSwk7jaKieb5Rlou53d\n\ +IXJQBw0KW5UrgbIFqMjpSZz1jdALBKsV+dw0wvCQ8BVXZm3zZpsV+0E4Z0sdj3TI\n\ +UbkFqQ6GpoxB25UUUlLZhBbtKy7dheuPBk0HowitYlo1kLVA/JiFB4qbdf5X/9Tm\n\ +XRkN+T0orEgy7rBQa7dJN9bdLj+dS5q8\n\ +-----END ENCRYPTED PRIVATE KEY-----"; + + if (MHD_SC_OK != + MHD_DAEMON_SET_OPTIONS ( + d, + MHD_D_OPTION_TLS (be))) + return "Failed to enable TLS!"; + if (MHD_SC_OK != + MHD_DAEMON_SET_OPTIONS ( + d, + MHD_D_OPTION_TLS_CERT_KEY (mem_cert, + mem_key, + "masterword"))) + return "Failed to enable TLS!"; + return NULL; +} + + +const char * +MHDT_server_setup_tls (const void *cls, + struct MHD_Daemon *d) +{ + const struct MHD_DaemonOptionAndValue *options = cls; + const char *err; + + err = MHDT_server_setup_minimal (options, + d); + if (NULL != err) + return err; + err = server_setup_tls (d, + MHD_TLS_BACKEND_ANY); + if (NULL != err) + return err; + return NULL; +} + + +const char * +MHDT_server_setup_gnutls (const void *cls, + struct MHD_Daemon *d) +{ + const struct MHD_DaemonOptionAndValue *options = cls; + const char *err; + + err = MHDT_server_setup_minimal (options, + d); + if (NULL != err) + return err; + err = server_setup_tls (d, + MHD_TLS_BACKEND_GNUTLS); + if (NULL != err) + return err; + return NULL; +} + + +const char * +MHDT_server_setup_openssl (const void *cls, + struct MHD_Daemon *d) +{ + const struct MHD_DaemonOptionAndValue *options = cls; + const char *err; + + err = MHDT_server_setup_minimal (options, + d); + if (NULL != err) + return err; + err = server_setup_tls (d, + MHD_TLS_BACKEND_OPENSSL); + if (NULL != err) + return err; + return NULL; +} + + void MHDT_server_run_minimal (void *cls, int finsig, @@ -130,7 +267,7 @@ MHDT_server_run_blocking (void *cls, strerror (errno)); break; } -#if FIXME +#ifdef FIXME if (MHD_SC_OK != MHD_daemon_process_blocking (d, 1000)) diff --git a/src/tests/client_server/libtest_convenience_client_request.c b/src/tests/client_server/libtest_convenience_client_request.c @@ -23,12 +23,12 @@ * @brief convenience functions implementing clients making requests for libtest users * @author Christian Grothoff */ +#include "libtest.h" #include <pthread.h> #include <stdbool.h> #include <fcntl.h> #include <unistd.h> #include <errno.h> -#include "libtest.h" #include <curl/curl.h> @@ -226,29 +226,179 @@ check_status (CURL *c, } +/** + * Set the @a base_url for the @a c handle. + * + * @param[in,out] c curl handle to manipulate + * @param base_url base URL to set + * @param[in,out] pc phase context with further options + * @return NULL on success, error message on failure (@a c will be cleaned up in this case) + */ +static const char * +set_url (CURL *c, + const char *base_url, + struct MHDT_PhaseContext *pc) +{ + if (CURLE_OK != + curl_easy_setopt (c, + CURLOPT_URL, + base_url)) + { + curl_easy_cleanup (c); + return "Failed to set URL"; + } + if (CURLE_OK != + curl_easy_setopt (c, + CURLOPT_VERBOSE, + 1)) + { + curl_easy_cleanup (c); + return "Failed to set verbosity"; + } + { + /* Force curl to do the request to 127.0.0.1 regardless of + hostname */ + const char *host; + const char *end; + char ri[1024]; + + if (0 == strncasecmp (base_url, + "https://", + strlen ("https://"))) + host = &base_url[strlen ("https://")]; + else + host = &base_url[strlen ("http://")]; + end = strchr (host, '/'); + if (NULL == end) + end = host + strlen (host); + snprintf (ri, + sizeof (ri), + "%.*s:127.0.0.1", + (int) (end - host), + host); + pc->hosts = curl_slist_append (NULL, + ri); + if (CURLE_OK != + curl_easy_setopt (c, + CURLOPT_RESOLVE, + pc->hosts)) + { + curl_easy_cleanup (c); + return "Failed to override DNS"; + } + } + if (0 == strncasecmp (base_url, + "https://", + strlen ("https://"))) + { + struct MHDT_Phase *phase = pc->phase; + + if (phase->check_server_cert) + { + if (CURLE_OK != + curl_easy_setopt (c, + CURLOPT_CAINFO, + "data/root-ca.crt")) + { + curl_easy_cleanup (c); + return "Failed to override root CA"; + } + } + else + { + /* disable certificate checking */ + if ( (CURLE_OK != + curl_easy_setopt (c, + CURLOPT_SSL_VERIFYPEER, + 0L)) || + (CURLE_OK != + curl_easy_setopt (c, + CURLOPT_SSL_VERIFYHOST, + 0L)) ) + { + curl_easy_cleanup (c); + return "Failed to disable X509 server certificate checks"; + } + } + if (NULL != phase->client_cert) + { + if (CURLE_OK != + curl_easy_setopt (c, + CURLOPT_SSLCERT, + phase->client_cert)) + { + curl_easy_cleanup (c); + return "Failed to set client certificate"; + } + } + } + return NULL; +} + + +const char * +MHDT_client_get_host (const void *cls, + struct MHDT_PhaseContext *pc) +{ + const char *host = cls; + const char *err; + size_t alen = strlen (host); + CURL *c; + size_t blen = strlen (pc->base_url); + char u[alen + blen + 1]; + const char *slash = strchr (pc->base_url, + '/'); + const char *colon; + + if (NULL == slash) + return "'/' missing in base URL"; + colon = strchr (slash, + ':'); + if (NULL == colon) + return "':' missing in base URL"; + snprintf (u, + sizeof (u), + "https://%s%s", + host, + colon); + c = curl_easy_init (); + if (NULL == c) + return "Failed to initialize Curl handle"; + err = set_url (c, + u, + pc); + if (NULL != err) + return err; + PERFORM_REQUEST (c); + CHECK_STATUS (c, + MHD_HTTP_STATUS_OK); + curl_easy_cleanup (c); + return NULL; +} + + const char * MHDT_client_get_root ( const void *cls, - const struct MHDT_PhaseContext *pc) + struct MHDT_PhaseContext *pc) { const char *text = cls; CURL *c; + const char *err; DECLARE_WB (text); c = curl_easy_init (); if (NULL == c) return "Failed to initialize Curl handle"; - if (CURLE_OK != - curl_easy_setopt (c, - CURLOPT_URL, - pc->base_url)) - { - curl_easy_cleanup (c); - return "Failed to set URL for curl request"; - } + err = set_url (c, + pc->base_url, + pc); + if (NULL != err) + return err; SETUP_WB (c); PERFORM_REQUEST (c); - CHECK_STATUS (c, MHD_HTTP_STATUS_OK); + CHECK_STATUS (c, + MHD_HTTP_STATUS_OK); curl_easy_cleanup (c); CHECK_WB (text); return NULL; @@ -258,9 +408,10 @@ MHDT_client_get_root ( const char * MHDT_client_get_with_query ( const void *cls, - const struct MHDT_PhaseContext *pc) + struct MHDT_PhaseContext *pc) { const char *args = cls; + const char *err; size_t alen = strlen (args); CURL *c; size_t blen = strlen (pc->base_url); @@ -276,15 +427,11 @@ MHDT_client_get_with_query ( c = curl_easy_init (); if (NULL == c) return "Failed to initialize Curl handle"; - - if (CURLE_OK != - curl_easy_setopt (c, - CURLOPT_URL, - u)) - { - curl_easy_cleanup (c); - return "Failed to set URL for curl request"; - } + err = set_url (c, + u, + pc); + if (NULL != err) + return err; PERFORM_REQUEST (c); CHECK_STATUS (c, MHD_HTTP_STATUS_NO_CONTENT); @@ -296,9 +443,10 @@ MHDT_client_get_with_query ( const char * MHDT_client_set_header ( const void *cls, - const struct MHDT_PhaseContext *pc) + struct MHDT_PhaseContext *pc) { const char *hdr = cls; + const char *err; CURL *c; CURLcode res; struct curl_slist *slist; @@ -306,14 +454,11 @@ MHDT_client_set_header ( c = curl_easy_init (); if (NULL == c) return "Failed to initialize Curl handle"; - if (CURLE_OK != - curl_easy_setopt (c, - CURLOPT_URL, - pc->base_url)) - { - curl_easy_cleanup (c); - return "Failed to set URL for curl request"; - } + err = set_url (c, + pc->base_url, + pc); + if (NULL != err) + return err; slist = curl_slist_append (NULL, hdr); if (CURLE_OK != @@ -341,10 +486,11 @@ MHDT_client_set_header ( const char * MHDT_client_expect_header (const void *cls, - const struct MHDT_PhaseContext *pc) + struct MHDT_PhaseContext *pc) { #ifdef HAVE_LIBCRUL_NEW_HDR_API const char *hdr = cls; + const char *err; size_t hlen = strlen (hdr) + 1; char key[hlen]; const char *colon = strchr (hdr, ':'); @@ -362,14 +508,11 @@ MHDT_client_expect_header (const void *cls, c = curl_easy_init (); if (NULL == c) return "Failed to initialize Curl handle"; - if (CURLE_OK != - curl_easy_setopt (c, - CURLOPT_URL, - pc->base_url)) - { - curl_easy_cleanup (c); - return "Failed to set URL for curl request"; - } + err = set_url (c, + pc->base_url, + pc); + if (NULL != err) + return err; PERFORM_REQUEST (c); CHECK_STATUS (c, MHD_HTTP_STATUS_NO_CONTENT); @@ -471,9 +614,10 @@ read_cb (void *ptr, const char * MHDT_client_put_data ( const void *cls, - const struct MHDT_PhaseContext *pc) + struct MHDT_PhaseContext *pc) { const char *text = cls; + const char *err; struct ReadBuffer rb = { .buf = text, .len = strlen (text) @@ -483,14 +627,11 @@ MHDT_client_put_data ( c = curl_easy_init (); if (NULL == c) return "Failed to initialize Curl handle"; - if (CURLE_OK != - curl_easy_setopt (c, - CURLOPT_URL, - pc->base_url)) - { - curl_easy_cleanup (c); - return "Failed to set URL for curl request"; - } + err = set_url (c, + pc->base_url, + pc); + if (NULL != err) + return err; if (CURLE_OK != curl_easy_setopt (c, CURLOPT_UPLOAD, @@ -534,9 +675,10 @@ MHDT_client_put_data ( const char * MHDT_client_chunk_data ( const void *cls, - const struct MHDT_PhaseContext *pc) + struct MHDT_PhaseContext *pc) { const char *text = cls; + const char *err; struct ReadBuffer rb = { .buf = text, .len = strlen (text), @@ -547,14 +689,11 @@ MHDT_client_chunk_data ( c = curl_easy_init (); if (NULL == c) return "Failed to initialize Curl handle"; - if (CURLE_OK != - curl_easy_setopt (c, - CURLOPT_URL, - pc->base_url)) - { - curl_easy_cleanup (c); - return "Failed to set URL for curl request"; - } + err = set_url (c, + pc->base_url, + pc); + if (NULL != err) + return err; if (CURLE_OK != curl_easy_setopt (c, CURLOPT_UPLOAD, @@ -590,9 +729,10 @@ MHDT_client_chunk_data ( const char * MHDT_client_do_post ( const void *cls, - const struct MHDT_PhaseContext *pc) + struct MHDT_PhaseContext *pc) { const struct MHDT_PostInstructions *pi = cls; + const char *err; CURL *c; struct curl_slist *request_hdr = NULL; @@ -608,14 +748,11 @@ MHDT_client_do_post ( c = curl_easy_init (); if (NULL == c) return "Failed to initialize Curl handle"; - if (CURLE_OK != - curl_easy_setopt (c, - CURLOPT_URL, - pc->base_url)) - { - curl_easy_cleanup (c); - return "Failed to set URL for curl request"; - } + err = set_url (c, + pc->base_url, + pc); + if (NULL != err) + return err; if (CURLE_OK != curl_easy_setopt (c, CURLOPT_POST, diff --git a/src/tests/client_server/libtest_convenience_server_reply.c b/src/tests/client_server/libtest_convenience_server_reply.c @@ -25,13 +25,13 @@ * replies from the server for libtest users * @author Christian Grothoff */ +#include "libtest.h" #include <pthread.h> #include <stdbool.h> #include <fcntl.h> #include <stdio.h> #include <unistd.h> #include <errno.h> -#include "libtest.h" #include <curl/curl.h> diff --git a/src/tests/client_server/test_cert_tls.c b/src/tests/client_server/test_cert_tls.c @@ -0,0 +1,170 @@ +/* + This file is part of GNU libmicrohttpd + Copyright (C) 2016, 2024 Christian Grothoff & 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 test_tls.c + * @brief test with client against TLS server + * @author Christian Grothoff + */ +#include "libtest.h" + + +int +main (int argc, char *argv[]) +{ + char *srv_certs_chain + = MHDT_load_pem ("chain.crt"); + char *srv_cert_key + = MHDT_load_pem ("test-server-key.pem"); + struct MHD_DaemonOptionAndValue rca_options[] = { + MHD_D_OPTION_POLL_SYSCALL (MHD_SPS_AUTO), + MHD_D_OPTION_WM_WORKER_THREADS (1), + MHD_D_OPTION_TLS (MHD_TLS_BACKEND_ANY), + MHD_D_OPTION_TLS_CERT_KEY (srv_certs_chain, + srv_cert_key, + NULL), + MHD_D_OPTION_TERMINATE () + }; +#ifdef MHD_USE_GNUTLS + struct MHD_DaemonOptionAndValue rca_options_gnu[] = { + MHD_D_OPTION_POLL_SYSCALL (MHD_SPS_AUTO), + MHD_D_OPTION_WM_WORKER_THREADS (1), + MHD_D_OPTION_TLS (MHD_TLS_BACKEND_GNUTLS), + MHD_D_OPTION_TLS_CERT_KEY (srv_certs_chain, + srv_cert_key, + NULL), + MHD_D_OPTION_TERMINATE () + }; +#endif +#ifdef MHD_USE_OPENSSL + struct MHD_DaemonOptionAndValue rca_options_open[] = { + MHD_D_OPTION_POLL_SYSCALL (MHD_SPS_AUTO), + MHD_D_OPTION_WM_WORKER_THREADS (1), + MHD_D_OPTION_TLS (MHD_TLS_BACKEND_OPENSSL), + MHD_D_OPTION_TLS_CERT_KEY (srv_certs_chain, + srv_cert_key, + NULL), + MHD_D_OPTION_TERMINATE () + }; +#endif + struct ServerType + { + const char *label; + MHDT_ServerSetup server_setup; + void *server_setup_cls; + MHDT_ServerRunner server_runner; + void *server_runner_cls; + struct MHDT_Phase phase; + } configs[] = { + { + .label = "certs_chain", + .server_setup = &MHDT_server_setup_minimal, + .server_setup_cls = rca_options, + .server_runner = &MHDT_server_run_minimal, + .phase = { + .label = "simple RCA get", + .server_cb = &MHDT_server_reply_text, + .server_cb_cls = (void *) "Hello world", + .client_cb = &MHDT_client_get_host, + .client_cb_cls = "localhost", + .timeout_ms = 2500, + .use_tls = true, + .check_server_cert = true + } + + + }, +#ifdef MHD_USE_GNUTLS + { + .label = "certs_chain", + .server_setup = &MHDT_server_setup_minimal, + .server_setup_cls = rca_options_gnu, + .server_runner = &MHDT_server_run_minimal, + .phase = { + .label = "simple RCA get", + .server_cb = &MHDT_server_reply_text, + .server_cb_cls = (void *) "Hello world", + .client_cb = &MHDT_client_get_host, + .client_cb_cls = "localhost", + .timeout_ms = 2500, + .use_tls = true, + .check_server_cert = true + } + + + }, +#endif +#ifdef MHD_USE_OPENSSL + { + .label = "certs_chain", + .server_setup = &MHDT_server_setup_minimal, + .server_setup_cls = rca_options_open, + .server_runner = &MHDT_server_run_minimal, + .phase = { + .label = "simple RCA get", + .server_cb = &MHDT_server_reply_text, + .server_cb_cls = (void *) "Hello world", + .client_cb = &MHDT_client_get_host, + .client_cb_cls = "localhost", + .timeout_ms = 2500, + .use_tls = true, + .check_server_cert = true + } + + + }, +#endif + { + .label = "END" + } + }; + unsigned int i; + int ret = 0; + + (void) argc; /* Unused. Silence compiler warning. */ + (void) argv; /* Unused. Silence compiler warning. */ + + for (i = 0; NULL != configs[i].server_setup; i++) + { + struct ServerType *st = &configs[i]; + struct MHDT_Phase phases[2] = { + st->phase + }; + fprintf (stderr, + "Running TLS tests with server setup '%s'\n", + st->label); + ret = MHDT_test (st->server_setup, + st->server_setup_cls, + st->server_runner, + st->server_runner_cls, + phases); + if (0 != ret) + { + fprintf (stderr, + "Test failed with server of type '%s' (%u)\n", + st->label, + i); + break; + } + } + free (srv_cert_key); + free (srv_certs_chain); + return ret; +} diff --git a/src/tests/client_server/test_tls.c b/src/tests/client_server/test_tls.c @@ -0,0 +1,330 @@ +/* + This file is part of GNU libmicrohttpd + Copyright (C) 2016, 2024 Christian Grothoff & 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 test_tls.c + * @brief test with client against TLS server + * @author Christian Grothoff + */ +#include "libtest.h" + + +int +main (int argc, char *argv[]) +{ + struct MHD_DaemonOptionAndValue thread1select[] = { + MHD_D_OPTION_POLL_SYSCALL (MHD_SPS_SELECT), + MHD_D_OPTION_WM_WORKER_THREADS (1), + MHD_D_OPTION_TERMINATE () + }; + struct MHD_DaemonOptionAndValue thread2select[] = { + MHD_D_OPTION_POLL_SYSCALL (MHD_SPS_SELECT), + MHD_D_OPTION_WM_WORKER_THREADS (2), + MHD_D_OPTION_TERMINATE () + }; + struct MHD_DaemonOptionAndValue thread1poll[] = { + MHD_D_OPTION_POLL_SYSCALL (MHD_SPS_POLL), + MHD_D_OPTION_WM_WORKER_THREADS (1), + MHD_D_OPTION_TERMINATE () + }; + struct MHD_DaemonOptionAndValue thread2poll[] = { + MHD_D_OPTION_POLL_SYSCALL (MHD_SPS_POLL), + MHD_D_OPTION_WM_WORKER_THREADS (1), + MHD_D_OPTION_TERMINATE () + }; + struct MHD_DaemonOptionAndValue thread1epoll[] = { + MHD_D_OPTION_POLL_SYSCALL (MHD_SPS_EPOLL), + MHD_D_OPTION_WM_WORKER_THREADS (1), + MHD_D_OPTION_TERMINATE () + }; + struct MHD_DaemonOptionAndValue thread2epoll[] = { + MHD_D_OPTION_POLL_SYSCALL (MHD_SPS_EPOLL), + MHD_D_OPTION_WM_WORKER_THREADS (1), + MHD_D_OPTION_TERMINATE () + }; + struct MHD_DaemonOptionAndValue thread1auto[] = { + MHD_D_OPTION_POLL_SYSCALL (MHD_SPS_AUTO), + MHD_D_OPTION_WM_WORKER_THREADS (1), + MHD_D_OPTION_TERMINATE () + }; + struct MHD_DaemonOptionAndValue external0auto[] = { + MHD_D_OPTION_POLL_SYSCALL (MHD_SPS_AUTO), + MHD_D_OPTION_WM_EXTERNAL_PERIODIC (), + MHD_D_OPTION_TERMINATE () + }; + struct ServerType + { + const char *label; + MHDT_ServerSetup server_setup; + void *server_setup_cls; + MHDT_ServerRunner server_runner; + void *server_runner_cls; + } configs[] = { +#ifdef MHD_USE_SELECT + { + .label = "single threaded select", + .server_setup = &MHDT_server_setup_tls, + .server_setup_cls = thread1select, + .server_runner = &MHDT_server_run_minimal, + }, + { + .label = "multi-threaded select", + .server_setup = &MHDT_server_setup_tls, + .server_setup_cls = thread2select, + .server_runner = &MHDT_server_run_minimal, + }, +#if MHD_USE_GNUTLS + { + .label = "multi-threaded select, forcing GnuTLS", + .server_setup = &MHDT_server_setup_gnutls, + .server_setup_cls = thread2select, + .server_runner = &MHDT_server_run_minimal, + }, +#endif +#endif +#ifdef MHD_USE_POLL + { + .label = "single threaded poll", + .server_setup = &MHDT_server_setup_tls, + .server_setup_cls = thread1poll, + .server_runner = &MHDT_server_run_minimal, + }, + { + .label = "multi-threaded poll", + .server_setup = &MHDT_server_setup_tls, + .server_setup_cls = thread2poll, + .server_runner = &MHDT_server_run_minimal, + }, +#if MHD_USE_GNUTLS + { + .label = "multi-threaded poll, forcing GnuTLS", + .server_setup = &MHDT_server_setup_gnutls, + .server_setup_cls = thread2poll, + .server_runner = &MHDT_server_run_minimal, + }, +#endif +#endif +#if MHD_USE_EPOLL + { + .label = "single threaded epoll", + .server_setup = &MHDT_server_setup_tls, + .server_setup_cls = thread1epoll, + .server_runner = &MHDT_server_run_minimal, + }, + { + .label = "multi-threaded epoll", + .server_setup = &MHDT_server_setup_tls, + .server_setup_cls = thread2epoll, + .server_runner = &MHDT_server_run_minimal, + }, +#if MHD_USE_GNUTLS + { + .label = "multi-threaded epoll, forcing GnuTLS", + .server_setup = &MHDT_server_setup_gnutls, + .server_setup_cls = thread2epoll, + .server_runner = &MHDT_server_run_minimal, + }, +#endif +#endif + { + .label = "auto-selected mode, single threaded", + .server_setup = &MHDT_server_setup_tls, + .server_setup_cls = thread1auto, + .server_runner = &MHDT_server_run_minimal, + }, +#if MHD_USE_GNUTLS + { + .label = "auto-selected mode, single threaded, forcing GnuTLS", + .server_setup = &MHDT_server_setup_gnutls, + .server_setup_cls = thread1auto, + .server_runner = &MHDT_server_run_minimal, + }, +#endif +#if MHD_USE_OPENSSL + { + .label = "auto-selected mode, single threaded, forcing OpenSSL", + .server_setup = &MHDT_server_setup_openssl, + .server_setup_cls = thread1auto, + .server_runner = &MHDT_server_run_minimal, + }, +#endif +#if 1 + /* FIXME: remove once MHD_daemon_process_blocking + has been implemented */ + { + .label = "END" + }, +#endif + { + .label = "auto-selected external event loop mode, no threads", + .server_setup = &MHDT_server_setup_tls, + .server_setup_cls = external0auto, + .server_runner = &MHDT_server_run_blocking, + }, + { + .label = "END" + } + }; + struct MHDT_Phase phases[] = { + { + .label = "simple get", + .server_cb = &MHDT_server_reply_text, + .server_cb_cls = (void *) "Hello world", + .client_cb = &MHDT_client_get_root, + .client_cb_cls = "Hello world", + .timeout_ms = 2500, + .use_tls = true + }, + { + .label = "GET with sendfile", + .server_cb = &MHDT_server_reply_file, + .server_cb_cls = (void *) "Hello world", + .client_cb = &MHDT_client_get_root, + .client_cb_cls = "Hello world", + .timeout_ms = 2500, + .use_tls = true + }, + { + .label = "client PUT with content-length", + .server_cb = &MHDT_server_reply_check_upload, + .server_cb_cls = (void *) "simple-upload-value", + .client_cb = &MHDT_client_put_data, + .client_cb_cls = "simple-upload-value", + .timeout_ms = 2500, + .use_tls = true + }, + { + .label = "client PUT with 2 chunks", + .server_cb = &MHDT_server_reply_check_upload, + .server_cb_cls = (void *) "chunky-upload-value", + .client_cb = &MHDT_client_chunk_data, + .client_cb_cls = "chunky-upload-value", + .timeout_ms = 2500, + .use_tls = true + }, + { + .label = "client request with custom header", + .server_cb = &MHDT_server_reply_check_header, + .server_cb_cls = (void *) "C-Header:testvalue", + .client_cb = &MHDT_client_set_header, + .client_cb_cls = "C-Header:testvalue", + .timeout_ms = 2500, + .use_tls = true + }, + { + .label = "server response with custom header", + .server_cb = &MHDT_server_reply_with_header, + .server_cb_cls = (void *) "X-Header:testvalue", + .client_cb = &MHDT_client_expect_header, + .client_cb_cls = "X-Header:testvalue", + .timeout_ms = 2500, + .use_tls = true + }, + { + .label = "URL with query parameters 1", + .server_cb = &MHDT_server_reply_check_query, + .server_cb_cls = (void *) "a=b&c", + .client_cb = &MHDT_client_get_with_query, + .client_cb_cls = "?a=b&c", + .timeout_ms = 5000, + .num_clients = 4, + .use_tls = true + }, + { + .label = "URL with query parameters 2", + .server_cb = &MHDT_server_reply_check_query, + .server_cb_cls = (void *) "a=b&c", /* a => b, c => NULL */ + .client_cb = &MHDT_client_get_with_query, + .client_cb_cls = "?c&a=b", + .timeout_ms = 5000, + .num_clients = 1, + .use_tls = true + }, + { + .label = "URL with query parameters 3", + .server_cb = &MHDT_server_reply_check_query, + .server_cb_cls = (void *) "a=&c", /* a => "", c => NULL */ + .client_cb = &MHDT_client_get_with_query, + .client_cb_cls = "?c&a=", + .timeout_ms = 5000, + .num_clients = 1, + .use_tls = true + }, + { + .label = "URL with query parameters 4", + .server_cb = &MHDT_server_reply_check_query, + .server_cb_cls = (void *) "a=", /* a => "" */ + .client_cb = &MHDT_client_get_with_query, + .client_cb_cls = "?a=", + .timeout_ms = 5000, + .num_clients = 1, + .use_tls = true + }, + { + .label = "URL with query parameters 5", + .server_cb = &MHDT_server_reply_check_query, + .server_cb_cls = (void *) "a=b", /* a => "b" */ + .client_cb = &MHDT_client_get_with_query, + .client_cb_cls = "?a=b", + .timeout_ms = 5000, + .num_clients = 1, + .use_tls = true + }, + { + .label = "chunked response get", + .server_cb = &MHDT_server_reply_chunked_text, + .server_cb_cls = (void *) "Hello world", + .client_cb = &MHDT_client_get_root, + .client_cb_cls = "Hello world", + .timeout_ms = 2500, + .use_tls = true + }, + // TODO: chunked download + { + .label = NULL, + }, + }; + unsigned int i; + int ret = 0; + + (void) argc; /* Unused. Silence compiler warning. */ + (void) argv; /* Unused. Silence compiler warning. */ + + for (i = 0; NULL != configs[i].server_setup; i++) + { + fprintf (stderr, + "Running TLS tests with server setup '%s'\n", + configs[i].label); + ret = MHDT_test (configs[i].server_setup, + configs[i].server_setup_cls, + configs[i].server_runner, + configs[i].server_runner_cls, + phases); + if (0 != ret) + { + fprintf (stderr, + "Test failed with server of type '%s' (%u)\n", + configs[i].label, + i); + break; + } + } + return ret; +}