libmicrohttpd

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

commit 6585ad92514d42c1238a463e4d0c5a73be19674a
parent 4866ea1fef107ddca0f5aa3dcb187c6fd7f4afe1
Author: Christian Grothoff <christian@grothoff.org>
Date:   Mon,  2 Feb 2009 06:29:47 +0000

cleanup

Diffstat:
Msrc/testcurl/https/Makefile.am | 10+++++++++-
Msrc/testcurl/https/bug-test.c | 69++++++++++++++++++++++++++++++++-------------------------------------
Msrc/testcurl/https/tls_authentication_test.c | 6+++---
Msrc/testcurl/https/tls_daemon_options_test.c | 2+-
Asrc/testcurl/https/tls_multi_thread_mode_test.c | 481+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/testcurl/https/tls_thread_mode_test.c | 28+++++++++++-----------------
6 files changed, 537 insertions(+), 59 deletions(-)

diff --git a/src/testcurl/https/Makefile.am b/src/testcurl/https/Makefile.am @@ -18,7 +18,8 @@ check_PROGRAMS = \ tls_authentication_test \ mhds_multi_daemon_test \ mhds_session_info_test \ - tls_thread_mode_test + tls_thread_mode_test \ + tls_multi_thread_mode_test if MHD_DEBUG_TLS check_PROGRAMS += \ @@ -65,6 +66,13 @@ tls_thread_mode_test_LDADD = \ $(top_builddir)/src/daemon/libmicrohttpd.la \ @LIBCURL@ +tls_multi_thread_mode_test_SOURCES = \ + tls_multi_thread_mode_test.c +tls_multi_thread_mode_test_LDADD = \ + $(top_builddir)/src/testcurl/libcurl_version_check.a \ + $(top_builddir)/src/daemon/libmicrohttpd.la \ + @LIBCURL@ + tls_authentication_test_SOURCES = \ tls_authentication_test.c tls_authentication_test_LDADD = \ diff --git a/src/testcurl/https/bug-test.c b/src/testcurl/https/bug-test.c @@ -238,7 +238,7 @@ test_daemon_get (FILE * test_fd, char *cipher_suite, int proto_version) return 0; } -int +static int test_cipher_option (FILE * test_fd, char *cipher_suite, int proto_version) { @@ -264,35 +264,8 @@ test_cipher_option (FILE * test_fd, char *cipher_suite, int proto_version) return ret; } -int -test_mac_option (FILE * test_fd, char *cipher_suite, int proto_version) -{ - - int ret; - int mac[] = { MHD_GNUTLS_MAC_SHA1, 0 }; - struct MHD_Daemon *d; - - d = MHD_start_daemon (MHD_USE_THREAD_PER_CONNECTION | MHD_USE_SSL | - MHD_USE_DEBUG, 42433, - NULL, NULL, &http_ahc, NULL, - MHD_OPTION_HTTPS_MEM_KEY, srv_key_pem, - MHD_OPTION_HTTPS_MEM_CERT, srv_self_signed_cert_pem, - MHD_OPTION_MAC_ALGO, mac, MHD_OPTION_END); - - if (d == NULL) - { - fprintf (stderr, MHD_E_SERVER_INIT); - return -1; - } - - ret = test_daemon_get (test_fd, cipher_suite, proto_version); - - MHD_stop_daemon (d); - return ret; -} - /* setup a temporary transfer test file */ -FILE * +static FILE * setupTestFile () { FILE *test_fd; @@ -320,6 +293,32 @@ setupTestFile () return test_fd; } +/* perform a HTTP GET request via SSL/TLS */ +int +test_secure_get (FILE * test_fd, char *cipher_suite, int proto_version) +{ + int ret; + struct MHD_Daemon *d; + + d = MHD_start_daemon (MHD_USE_THREAD_PER_CONNECTION | MHD_USE_SSL | + MHD_USE_DEBUG, 42433, + NULL, NULL, &http_ahc, NULL, + MHD_OPTION_HTTPS_MEM_KEY, srv_signed_key_pem, + MHD_OPTION_HTTPS_MEM_CERT, srv_signed_cert_pem, + MHD_OPTION_END); + + if (d == NULL) + { + fprintf (stderr, MHD_E_SERVER_INIT); + return -1; + } + + ret = test_daemon_get (test_fd, cipher_suite, proto_version); + + MHD_stop_daemon (d); + return ret; +} + int main (int argc, char *const *argv) { @@ -345,17 +344,13 @@ main (int argc, char *const *argv) return -1; } -// errorCount += -// test_secure_get (test_fd, "AES256-SHA", CURL_SSLVERSION_TLSv1); -// errorCount += -// test_secure_get (test_fd, "AES256-SHA", CURL_SSLVERSION_SSLv3); -// errorCount += -// test_file_certificates (test_fd, "AES256-SHA", CURL_SSLVERSION_SSLv3); + errorCount += + test_secure_get (test_fd, "AES256-SHA", CURL_SSLVERSION_TLSv1); + errorCount += + test_secure_get (test_fd, "AES256-SHA", CURL_SSLVERSION_SSLv3); /* TODO resolve cipher setting issue when compiling against GNU TLS */ errorCount += test_cipher_option (test_fd, "DES-CBC3-SHA", CURL_SSLVERSION_TLSv1); -/* errorCount += - test_kx_option (test_fd, "EDH-RSA-DES-CBC3-SHA", CURL_SSLVERSION_SSLv3); */ curl_global_cleanup (); diff --git a/src/testcurl/https/tls_authentication_test.c b/src/testcurl/https/tls_authentication_test.c @@ -238,7 +238,7 @@ test_daemon_get (FILE * test_fd, char *cipher_suite, int proto_version) } /* perform a HTTP GET request via SSL/TLS */ -int +static int test_secure_get (FILE * test_fd, char *cipher_suite, int proto_version) { int ret; @@ -264,7 +264,7 @@ test_secure_get (FILE * test_fd, char *cipher_suite, int proto_version) } /* setup a temporary transfer test file */ -FILE * +static FILE * setupTestFile () { FILE *test_fd; @@ -292,7 +292,7 @@ setupTestFile () return test_fd; } -FILE * +static FILE * setup_ca_cert () { FILE *fd; diff --git a/src/testcurl/https/tls_daemon_options_test.c b/src/testcurl/https/tls_daemon_options_test.c @@ -32,7 +32,7 @@ #include "gnutls.h" #include <curl/curl.h> -#define DEBUG_CURL_VERBOSE 1 +#define DEBUG_CURL_VERBOSE 0 #define PAGE_NOT_FOUND "<html><head><title>File not found</title></head><body>File not found</body></html>" #define MHD_E_MEM "Error: memory error\n" diff --git a/src/testcurl/https/tls_multi_thread_mode_test.c b/src/testcurl/https/tls_multi_thread_mode_test.c @@ -0,0 +1,481 @@ +/* + This file is part of libmicrohttpd + (C) 2007 Christian Grothoff + + libmicrohttpd is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2, or (at your + option) any later version. + + libmicrohttpd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with libmicrohttpd; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +/** + * @file tls_thread_mode_test.c + * @brief Testcase for libmicrohttpd HTTPS GET operations + * @author Sagie Amir + * @author Christian Grothoff + * + * TODO: add test for external select! + */ + +#include "platform.h" +#include "microhttpd.h" + +#include <sys/stat.h> +#include <limits.h> +#include "gnutls.h" +#include <curl/curl.h> + +#define DEBUG_CURL_VERBOSE 0 +#define PAGE_NOT_FOUND "<html><head><title>File not found</title></head><body>File not found</body></html>" + +#define MHD_E_MEM "Error: memory error\n" +#define MHD_E_SERVER_INIT "Error: failed to start server\n" +#define MHD_E_TEST_FILE_CREAT "Error: failed to setup test file\n" +#define MHD_E_CERT_FILE_CREAT "Error: failed to setup test certificate\n" +#define MHD_E_KEY_FILE_CREAT "Error: failed to setup test certificate\n" + +#include "tls_test_keys.h" + +const char *test_file_name = "https_test_file"; +const char test_file_data[] = "Hello World\n"; + +int curl_check_version (const char *req_version, ...); + +struct CBC +{ + char *buf; + size_t pos; + size_t size; +}; + +struct https_test_data +{ + FILE *test_fd; + char *cipher_suite; + int proto_version; +}; + +struct CipherDef +{ + int options[2]; + char *curlname; +}; + +static size_t +copyBuffer (void *ptr, size_t size, size_t nmemb, void *ctx) +{ + struct CBC *cbc = ctx; + + if (cbc->pos + size * nmemb > cbc->size) + return 0; /* overflow */ + memcpy (&cbc->buf[cbc->pos], ptr, size * nmemb); + cbc->pos += size * nmemb; + return size * nmemb; +} + +static int +file_reader (void *cls, size_t pos, char *buf, int max) +{ + FILE *file = cls; + fseek (file, pos, SEEK_SET); + return fread (buf, 1, max, file); +} + +/* HTTP access handler call back */ +static int +http_ahc (void *cls, struct MHD_Connection *connection, + const char *url, const char *method, const char *upload_data, + const char *version, unsigned int *upload_data_size, void **ptr) +{ + static int aptr; + struct MHD_Response *response; + int ret; + FILE *file; + struct stat buf; + + if (0 != strcmp (method, MHD_HTTP_METHOD_GET)) + return MHD_NO; /* unexpected method */ + if (&aptr != *ptr) + { + /* do never respond on first call */ + *ptr = &aptr; + return MHD_YES; + } + *ptr = NULL; /* reset when done */ + + file = fopen (url, "r"); + if (file == NULL) + { + response = MHD_create_response_from_data (strlen (PAGE_NOT_FOUND), + (void *) PAGE_NOT_FOUND, + MHD_NO, MHD_NO); + ret = MHD_queue_response (connection, MHD_HTTP_NOT_FOUND, response); + MHD_destroy_response (response); + } + else + { + stat (url, &buf); + response = MHD_create_response_from_callback (buf.st_size, 32 * 1024, /* 32k PAGE_NOT_FOUND size */ + &file_reader, file, + (MHD_ContentReaderFreeCallback) + & fclose); + ret = MHD_queue_response (connection, MHD_HTTP_OK, response); + MHD_destroy_response (response); + } + return ret; +} + + + +/** + * test HTTPS transfer + * @param test_fd: file to attempt transfering + */ +static int +test_https_transfer (FILE * test_fd, char *cipher_suite, int proto_version) +{ + CURL *c; + CURLcode errornum; + struct CBC cbc; + char *doc_path; + size_t doc_path_len; + char url[255]; + struct stat statb; + + stat (test_file_name, &statb); + + int len = statb.st_size; + + /* used to memcmp local copy & deamon supplied copy */ + unsigned char *mem_test_file_local; + + /* setup test file path, url */ + doc_path_len = PATH_MAX > 4096 ? 4096 : PATH_MAX; + if (NULL == (doc_path = malloc (doc_path_len))) + { + fclose (test_fd); + fprintf (stderr, MHD_E_MEM); + return -1; + } + if (getcwd (doc_path, doc_path_len) == NULL) + { + fclose (test_fd); + free (doc_path); + fprintf (stderr, "Error: failed to get working directory. %s\n", + strerror (errno)); + return -1; + } + + if (NULL == (mem_test_file_local = malloc (len))) + { + fclose (test_fd); + free (doc_path); + fprintf (stderr, MHD_E_MEM); + return -1; + } + + fseek (test_fd, 0, SEEK_SET); + if (fread (mem_test_file_local, sizeof (char), len, test_fd) != len) + { + fclose (test_fd); + free (doc_path); + fprintf (stderr, "Error: failed to read test file. %s\n", + strerror (errno)); + return -1; + } + + if (NULL == (cbc.buf = malloc (len))) + { + fclose (test_fd); + free (doc_path); + free (mem_test_file_local); + fprintf (stderr, MHD_E_MEM); + return -1; + } + cbc.size = len; + cbc.pos = 0; + + /* construct url - this might use doc_path */ + sprintf (url, "%s%s/%s", "https://localhost:42433", + doc_path, test_file_name); + + c = curl_easy_init (); +#if DEBUG_CURL_VERBOSE + curl_easy_setopt (c, CURLOPT_VERBOSE, 1); +#endif + curl_easy_setopt (c, CURLOPT_URL, url); + curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0); + curl_easy_setopt (c, CURLOPT_TIMEOUT, 15L); + curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 15L); + curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer); + curl_easy_setopt (c, CURLOPT_FILE, &cbc); + + /* TLS options */ + curl_easy_setopt (c, CURLOPT_SSLVERSION, proto_version); + curl_easy_setopt (c, CURLOPT_SSL_CIPHER_LIST, cipher_suite); + + /* currently skip any peer authentication */ + curl_easy_setopt (c, CURLOPT_SSL_VERIFYPEER, 0); + curl_easy_setopt (c, CURLOPT_SSL_VERIFYHOST, 0); + + curl_easy_setopt (c, CURLOPT_FAILONERROR, 1); + + /* NOTE: use of CONNECTTIMEOUT without also + setting NOSIGNAL results in really weird + crashes on my system! */ + curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1); + if (CURLE_OK != (errornum = curl_easy_perform (c))) + { + fprintf (stderr, "curl_easy_perform failed: `%s'\n", + curl_easy_strerror (errornum)); + curl_easy_cleanup (c); + free (cbc.buf); + free (mem_test_file_local); + free (doc_path); + return errornum; + } + + curl_easy_cleanup (c); + + if (memcmp (cbc.buf, mem_test_file_local, len) != 0) + { + fprintf (stderr, "Error: local file & received file differ.\n"); + free (cbc.buf); + free (mem_test_file_local); + free (doc_path); + return -1; + } + + free (mem_test_file_local); + free (cbc.buf); + free (doc_path); + return 0; +} + +/** + * used when spawning multiple threads executing curl server requests + * + */ +static void * +https_transfer_thread_adapter (void *args) +{ + static int nonnull; + struct https_test_data *cargs = args; + int ret; + + /* time spread incomming requests */ + usleep ((useconds_t) 10.0 * ((double) rand ()) / ((double) RAND_MAX)); + ret = test_https_transfer (cargs->test_fd, + cargs->cipher_suite, cargs->proto_version); + if (ret == 0) + return NULL; + return &nonnull; +} + +static FILE * +setupTestFile () +{ + FILE *test_fd; + + if (NULL == (test_fd = fopen (test_file_name, "w+"))) + { + fprintf (stderr, "Error: failed to open `%s': %s\n", + test_file_name, strerror (errno)); + return NULL; + } + if (fwrite (test_file_data, sizeof (char), strlen (test_file_data), test_fd) + != strlen (test_file_data)) + { + fprintf (stderr, "Error: failed to write `%s. %s'\n", + test_file_name, strerror (errno)); + return NULL; + } + if (fflush (test_fd)) + { + fprintf (stderr, "Error: failed to flush test file stream. %s\n", + strerror (errno)); + return NULL; + } + + return test_fd; +} + +static int +setup (struct MHD_Daemon **d, int daemon_flags, va_list arg_list) +{ + *d = MHD_start_daemon_va (daemon_flags, 42433, + NULL, NULL, &http_ahc, NULL, arg_list); + + if (*d == NULL) + { + fprintf (stderr, MHD_E_SERVER_INIT); + return -1; + } + + return 0; +} + +static void +teardown (struct MHD_Daemon *d) +{ + MHD_stop_daemon (d); +} + +/* TODO test_wrap: change sig to (setup_func, test, va_list test_arg) & move to test_util.c */ +static int +test_wrap (char *test_name, int + (*test_function) (FILE * test_fd, char *cipher_suite, + int proto_version), FILE * test_fd, + int daemon_flags, char *cipher_suite, int proto_version, ...) +{ + int ret; + va_list arg_list; + struct MHD_Daemon *d; + + va_start (arg_list, proto_version); + if (setup (&d, daemon_flags, arg_list) != 0) + { + va_end (arg_list); + return -1; + } + + fprintf (stdout, "running test: %s ", test_name); + ret = test_function (test_fd, cipher_suite, proto_version); + + if (ret == 0) + { + fprintf (stdout, "[pass]\n"); + } + else + { + fprintf (stdout, "[fail]\n"); + } + + teardown (d); + va_end (arg_list); + return ret; +} + +/** + * Test non-parallel requests. + * + * @return: 0 upon all client requests returning '0', -1 otherwise. + * + * TODO : make client_count a parameter - numver of curl client threads to spawn + */ +static int +test_single_client (FILE * test_fd, char *cipher_suite, + int curl_proto_version) +{ + void *client_thread_ret; + struct https_test_data client_args = + { test_fd, cipher_suite, curl_proto_version }; + + client_thread_ret = https_transfer_thread_adapter (&client_args); + if (client_thread_ret != NULL) + return -1; + return 0; +} + + +/** + * Test parallel request handling. + * + * @return: 0 upon all client requests returning '0', -1 otherwise. + * + * TODO : make client_count a parameter - numver of curl client threads to spawn + */ +static int +test_parallel_clients (FILE * test_fd, char *cipher_suite, + int curl_proto_version) +{ + int i; + int client_count = 3; + void *client_thread_ret; + pthread_t client_arr[client_count]; + struct https_test_data client_args = + { test_fd, cipher_suite, curl_proto_version }; + + for (i = 0; i < client_count; ++i) + { + if (pthread_create (&client_arr[i], NULL, + &https_transfer_thread_adapter, + &client_args) != 0) + { + fprintf (stderr, "Error: failed to spawn test client threads.\n"); + + return -1; + } + } + + /* check all client requests fulfilled correctly */ + for (i = 0; i < client_count; ++i) + { + if ((pthread_join (client_arr[i], &client_thread_ret) != 0) || + (client_thread_ret != NULL)) + return -1; + } + + return 0; +} + + +int +main (int argc, char *const *argv) +{ + FILE *test_fd; + unsigned int errorCount = 0; + + /* initialize random seed used by curl clients */ + unsigned int iseed = (unsigned int) time (NULL); + srand (iseed); + + if (curl_check_version (MHD_REQ_CURL_VERSION)) + return -1; + + if ((test_fd = setupTestFile ()) == NULL) + { + fprintf (stderr, MHD_E_TEST_FILE_CREAT); + return -1; + } + + if (0 != curl_global_init (CURL_GLOBAL_ALL)) + { + fprintf (stderr, "Error: %s\n", strerror (errno)); + return -1; + } + + errorCount += + test_wrap ("multi threaded daemon, single client", &test_single_client, + test_fd, MHD_USE_SSL | MHD_USE_DEBUG | MHD_USE_THREAD_PER_CONNECTION, "AES256-SHA", + CURL_SSLVERSION_TLSv1, MHD_OPTION_HTTPS_MEM_KEY, srv_key_pem, + MHD_OPTION_HTTPS_MEM_CERT, srv_self_signed_cert_pem, + MHD_OPTION_END); + + errorCount += + test_wrap ("multi threaded daemon, parallel client", + &test_parallel_clients, test_fd, MHD_USE_SSL | MHD_USE_DEBUG | MHD_USE_THREAD_PER_CONNECTION, + "AES256-SHA", CURL_SSLVERSION_TLSv1, MHD_OPTION_HTTPS_MEM_KEY, + srv_key_pem, MHD_OPTION_HTTPS_MEM_CERT, + srv_self_signed_cert_pem, MHD_OPTION_END); + + if (errorCount != 0) + fprintf (stderr, "Failed test: %s.\n", argv[0]); + + curl_global_cleanup (); + fclose (test_fd); + + remove (test_file_name); + + return errorCount != 0; +} diff --git a/src/testcurl/https/tls_thread_mode_test.c b/src/testcurl/https/tls_thread_mode_test.c @@ -179,6 +179,7 @@ test_https_transfer (FILE * test_fd, char *cipher_suite, int proto_version) if (NULL == (mem_test_file_local = malloc (len))) { fclose (test_fd); + free (doc_path); fprintf (stderr, MHD_E_MEM); return -1; } @@ -187,14 +188,17 @@ test_https_transfer (FILE * test_fd, char *cipher_suite, int proto_version) if (fread (mem_test_file_local, sizeof (char), len, test_fd) != len) { fclose (test_fd); + free (doc_path); fprintf (stderr, "Error: failed to read test file. %s\n", strerror (errno)); return -1; } - if (NULL == (cbc.buf = malloc (sizeof (char) * len))) + if (NULL == (cbc.buf = malloc (len))) { fclose (test_fd); + free (doc_path); + free (mem_test_file_local); fprintf (stderr, MHD_E_MEM); return -1; } @@ -235,6 +239,9 @@ test_https_transfer (FILE * test_fd, char *cipher_suite, int proto_version) fprintf (stderr, "curl_easy_perform failed: `%s'\n", curl_easy_strerror (errornum)); curl_easy_cleanup (c); + free (cbc.buf); + free (mem_test_file_local); + free (doc_path); return errornum; } @@ -245,6 +252,7 @@ test_https_transfer (FILE * test_fd, char *cipher_suite, int proto_version) fprintf (stderr, "Error: local file & received file differ.\n"); free (cbc.buf); free (mem_test_file_local); + free (doc_path); return -1; } @@ -401,10 +409,11 @@ test_parallel_clients (FILE * test_fd, char *cipher_suite, for (i = 0; i < client_count; ++i) { if (pthread_create (&client_arr[i], NULL, - (void *) &https_transfer_thread_adapter, + &https_transfer_thread_adapter, &client_args) != 0) { fprintf (stderr, "Error: failed to spawn test client threads.\n"); + return -1; } } @@ -447,27 +456,12 @@ main (int argc, char *const *argv) } errorCount += - test_wrap ("multi threaded daemon, single client", &test_single_client, - test_fd, MHD_USE_SSL | MHD_USE_DEBUG, "AES256-SHA", - CURL_SSLVERSION_TLSv1, MHD_OPTION_HTTPS_MEM_KEY, srv_key_pem, - MHD_OPTION_HTTPS_MEM_CERT, srv_self_signed_cert_pem, - MHD_OPTION_END); - - errorCount += test_wrap ("single threaded daemon, single client", &test_single_client, test_fd, MHD_USE_SELECT_INTERNALLY | MHD_USE_SSL | MHD_USE_DEBUG, "AES256-SHA", CURL_SSLVERSION_TLSv1, MHD_OPTION_HTTPS_MEM_KEY, srv_key_pem, MHD_OPTION_HTTPS_MEM_CERT, srv_self_signed_cert_pem, MHD_OPTION_END); - - errorCount += - test_wrap ("multi threaded daemon, parallel client", - &test_parallel_clients, test_fd, MHD_USE_SSL | MHD_USE_DEBUG, - "AES256-SHA", CURL_SSLVERSION_TLSv1, MHD_OPTION_HTTPS_MEM_KEY, - srv_key_pem, MHD_OPTION_HTTPS_MEM_CERT, - srv_self_signed_cert_pem, MHD_OPTION_END); - errorCount += test_wrap ("single threaded daemon, parallel clients", &test_parallel_clients, test_fd,