libmicrohttpd

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

commit 5e5dbf980de763480f7957f64af78bbb2159d9a0
parent b8516d23ba11cf3a94fcbf2ea228ea0456354017
Author: Christian Grothoff <christian@grothoff.org>
Date:   Sat, 26 Oct 2019 18:52:53 +0200

add tests for empty reply in HTTPS

Diffstat:
Msrc/examples/.gitignore | 2++
Msrc/examples/Makefile.am | 9++++++++-
Asrc/examples/minimal_example_empty_tls.c | 150+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/microhttpd/mhd_send.c | 23++++++++++++++++++-----
Msrc/testcurl/.gitignore | 1+
Msrc/testcurl/https/test_https_get.c | 156++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---
Msrc/testcurl/https/tls_test_common.c | 61+++++++++++++++++++++++++++++++++++++++++++++----------------
Msrc/testcurl/test_get.c | 20+++++++++++++++-----
Asrc/testcurl/test_get_empty.c | 941+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
9 files changed, 1331 insertions(+), 32 deletions(-)

diff --git a/src/examples/.gitignore b/src/examples/.gitignore @@ -35,3 +35,5 @@ upgrade_example /timeout http_chunked_compression http_compression +minimal_example_empty +minimal_example_empty_tls diff --git a/src/examples/Makefile.am b/src/examples/Makefile.am @@ -39,7 +39,9 @@ EXTRA_DIST = msgs_i18n.c noinst_EXTRA_DIST = msgs_i18n.c if ENABLE_HTTPS -noinst_PROGRAMS += https_fileserver_example +noinst_PROGRAMS += \ + https_fileserver_example \ + minimal_example_empty_tls endif if HAVE_POSTPROCESSOR noinst_PROGRAMS += \ @@ -89,6 +91,11 @@ minimal_example_empty_SOURCES = \ minimal_example_empty_LDADD = \ $(top_builddir)/src/microhttpd/libmicrohttpd.la +minimal_example_empty_tls_SOURCES = \ + minimal_example_empty_tls.c +minimal_example_empty_tls_LDADD = \ + $(top_builddir)/src/microhttpd/libmicrohttpd.la + upgrade_example_SOURCES = \ upgrade_example.c upgrade_example_CFLAGS = \ diff --git a/src/examples/minimal_example_empty_tls.c b/src/examples/minimal_example_empty_tls.c @@ -0,0 +1,150 @@ +/* + This file is part of libmicrohttpd + Copyright (C) 2007 Christian Grothoff (and other contributing authors) + + 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 +*/ +/** + * @file minimal_example_empty_ssl.c + * @brief minimal example for how to use libmicrohttpd + * @author Christian Grothoff + */ + +#include "platform.h" +#include <microhttpd.h> + + +static int +ahc_echo (void *cls, + struct MHD_Connection *connection, + const char *url, + const char *method, + const char *version, + const char *upload_data, + size_t *upload_data_size, + void **ptr) +{ + static int aptr; + struct MHD_Response *response; + int ret; + + (void) url; /* Unused. Silent compiler warning. */ + (void) version; /* Unused. Silent compiler warning. */ + (void) upload_data; /* Unused. Silent compiler warning. */ + (void) upload_data_size; /* Unused. Silent compiler warning. */ + + if (0 != strcmp (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 */ + response = MHD_create_response_from_buffer (0, + NULL, + MHD_RESPMEM_PERSISTENT); + ret = MHD_queue_response (connection, + MHD_HTTP_NO_CONTENT, + response); + MHD_destroy_response (response); + return ret; +} + + +/* test server key */ +const char srv_signed_key_pem[] = "-----BEGIN RSA PRIVATE KEY-----\n" + "MIIEowIBAAKCAQEAvfTdv+3fgvVTKRnP/HVNG81cr8TrUP/iiyuve/THMzvFXhCW\n" + "+K03KwEku55QvnUndwBfU/ROzLlv+5hotgiDRNFT3HxurmhouySBrJNJv7qWp8IL\n" + "q4sw32vo0fbMu5BZF49bUXK9L3kW2PdhTtSQPWHEzNrCxO+YgCilKHkY3vQNfdJ0\n" + "20Q5EAAEseD1YtWCIpRvJzYlZMpjYB1ubTl24kwrgOKUJYKqM4jmF4DVQp4oOK/6\n" + "QYGGh1QmHRPAy3CBII6sbb+sZT9cAqU6GYQVB35lm4XAgibXV6KgmpVxVQQ69U6x\n" + "yoOl204xuekZOaG9RUPId74Rtmwfi1TLbBzo2wIDAQABAoIBADu09WSICNq5cMe4\n" + "+NKCLlgAT1NiQpLls1gKRbDhKiHU9j8QWNvWWkJWrCya4QdUfLCfeddCMeiQmv3K\n" + "lJMvDs+5OjJSHFoOsGiuW2Ias7IjnIojaJalfBml6frhJ84G27IXmdz6gzOiTIer\n" + "DjeAgcwBaKH5WwIay2TxIaScl7AwHBauQkrLcyb4hTmZuQh6ArVIN6+pzoVuORXM\n" + "bpeNWl2l/HSN3VtUN6aCAKbN/X3o0GavCCMn5Fa85uJFsab4ss/uP+2PusU71+zP\n" + "sBm6p/2IbGvF5k3VPDA7X5YX61sukRjRBihY8xSnNYx1UcoOsX6AiPnbhifD8+xQ\n" + "Tlf8oJUCgYEA0BTfzqNpr9Wxw5/QXaSdw7S/0eP5a0C/nwURvmfSzuTD4equzbEN\n" + "d+dI/s2JMxrdj/I4uoAfUXRGaabevQIjFzC9uyE3LaOyR2zhuvAzX+vVcs6bSXeU\n" + "pKpCAcN+3Z3evMaX2f+z/nfSUAl2i4J2R+/LQAWJW4KwRky/m+cxpfUCgYEA6bN1\n" + "b73bMgM8wpNt6+fcmS+5n0iZihygQ2U2DEud8nZJL4Nrm1dwTnfZfJBnkGj6+0Q0\n" + "cOwj2KS0/wcEdJBP0jucU4v60VMhp75AQeHqidIde0bTViSRo3HWKXHBIFGYoU3T\n" + "LyPyKndbqsOObnsFXHn56Nwhr2HLf6nw4taGQY8CgYBoSW36FLCNbd6QGvLFXBGt\n" + "2lMhEM8az/K58kJ4WXSwOLtr6MD/WjNT2tkcy0puEJLm6BFCd6A6pLn9jaKou/92\n" + "SfltZjJPb3GUlp9zn5tAAeSSi7YMViBrfuFiHObij5LorefBXISLjuYbMwL03MgH\n" + "Ocl2JtA2ywMp2KFXs8GQWQKBgFyIVv5ogQrbZ0pvj31xr9HjqK6d01VxIi+tOmpB\n" + "4ocnOLEcaxX12BzprW55ytfOCVpF1jHD/imAhb3YrHXu0fwe6DXYXfZV4SSG2vB7\n" + "IB9z14KBN5qLHjNGFpMQXHSMek+b/ftTU0ZnPh9uEM5D3YqRLVd7GcdUhHvG8P8Q\n" + "C9aXAoGBAJtID6h8wOGMP0XYX5YYnhlC7dOLfk8UYrzlp3xhqVkzKthTQTj6wx9R\n" + "GtC4k7U1ki8oJsfcIlBNXd768fqDVWjYju5rzShMpo8OCTS6ipAblKjCxPPVhIpv\n" + "tWPlbSn1qj6wylstJ5/3Z+ZW5H4wIKp5jmLiioDhcP0L/Ex3Zx8O\n" + "-----END RSA PRIVATE KEY-----\n"; + +/* test server CA signed certificates */ +const char srv_signed_cert_pem[] = "-----BEGIN CERTIFICATE-----\n" + "MIIDGzCCAgWgAwIBAgIES0KCvTALBgkqhkiG9w0BAQUwFzEVMBMGA1UEAxMMdGVz\n" + "dF9jYV9jZXJ0MB4XDTEwMDEwNTAwMDcyNVoXDTQ1MDMxMjAwMDcyNVowFzEVMBMG\n" + "A1UEAxMMdGVzdF9jYV9jZXJ0MIIBHzALBgkqhkiG9w0BAQEDggEOADCCAQkCggEA\n" + "vfTdv+3fgvVTKRnP/HVNG81cr8TrUP/iiyuve/THMzvFXhCW+K03KwEku55QvnUn\n" + "dwBfU/ROzLlv+5hotgiDRNFT3HxurmhouySBrJNJv7qWp8ILq4sw32vo0fbMu5BZ\n" + "F49bUXK9L3kW2PdhTtSQPWHEzNrCxO+YgCilKHkY3vQNfdJ020Q5EAAEseD1YtWC\n" + "IpRvJzYlZMpjYB1ubTl24kwrgOKUJYKqM4jmF4DVQp4oOK/6QYGGh1QmHRPAy3CB\n" + "II6sbb+sZT9cAqU6GYQVB35lm4XAgibXV6KgmpVxVQQ69U6xyoOl204xuekZOaG9\n" + "RUPId74Rtmwfi1TLbBzo2wIDAQABo3YwdDAMBgNVHRMBAf8EAjAAMBMGA1UdJQQM\n" + "MAoGCCsGAQUFBwMBMA8GA1UdDwEB/wQFAwMHIAAwHQYDVR0OBBYEFOFi4ilKOP1d\n" + "XHlWCMwmVKr7mgy8MB8GA1UdIwQYMBaAFP2olB4s2T/xuoQ5pT2RKojFwZo2MAsG\n" + "CSqGSIb3DQEBBQOCAQEAHVWPxazupbOkG7Did+dY9z2z6RjTzYvurTtEKQgzM2Vz\n" + "GQBA+3pZ3c5mS97fPIs9hZXfnQeelMeZ2XP1a+9vp35bJjZBBhVH+pqxjCgiUflg\n" + "A3Zqy0XwwVCgQLE2HyaU3DLUD/aeIFK5gJaOSdNTXZLv43K8kl4cqDbMeRpVTbkt\n" + "YmG4AyEOYRNKGTqMEJXJoxD5E3rBUNrVI/XyTjYrulxbNPcMWEHKNeeqWpKDYTFo\n" + "Bb01PCthGXiq/4A2RLAFosadzRa8SBpoSjPPfZ0b2w4MJpReHqKbR5+T2t6hzml6\n" + "4ToyOKPDmamiTuN5KzLN3cw7DQlvWMvqSOChPLnA3Q==\n" + "-----END CERTIFICATE-----\n"; + + +int +main (int argc, + char *const *argv) +{ + struct MHD_Daemon *d; + + if (argc != 2) + { + printf ("%s PORT\n", argv[0]); + return 1; + } + d = MHD_start_daemon (/* MHD_USE_INTERNAL_POLLING_THREAD | MHD_USE_ERROR_LOG, */ + MHD_USE_AUTO | MHD_USE_INTERNAL_POLLING_THREAD | MHD_USE_ERROR_LOG + | MHD_USE_TLS, + /* MHD_USE_INTERNAL_POLLING_THREAD | MHD_USE_ERROR_LOG | MHD_USE_POLL, */ + /* MHD_USE_THREAD_PER_CONNECTION | MHD_USE_INTERNAL_POLLING_THREAD | MHD_USE_ERROR_LOG | MHD_USE_POLL, */ + /* MHD_USE_THREAD_PER_CONNECTION | MHD_USE_INTERNAL_POLLING_THREAD | MHD_USE_ERROR_LOG, */ + atoi (argv[1]), + NULL, NULL, &ahc_echo, NULL, + MHD_OPTION_CONNECTION_TIMEOUT, (unsigned int) 120, + MHD_OPTION_STRICT_FOR_CLIENT, (int) 1, + /* Optionally, the gnutls_load_file() can be used to + load the key and the certificate from file. */ + MHD_OPTION_HTTPS_MEM_KEY, srv_signed_key_pem, + MHD_OPTION_HTTPS_MEM_CERT, srv_signed_cert_pem, + MHD_OPTION_END); + if (d == NULL) + return 1; + (void) getc (stdin); + MHD_stop_daemon (d); + return 0; +} diff --git a/src/microhttpd/mhd_send.c b/src/microhttpd/mhd_send.c @@ -240,7 +240,8 @@ MHD_send_on_connection_ (struct MHD_Connection *connection, ssize_t ret; /* error handling from send_param_adapter() */ - if ((MHD_INVALID_SOCKET == s) || (MHD_CONNECTION_CLOSED == connection->state)) + if ( (MHD_INVALID_SOCKET == s) || + (MHD_CONNECTION_CLOSED == connection->state) ) { return MHD_ERR_NOTCONN_; } @@ -382,10 +383,22 @@ MHD_send_on_connection2_ (struct MHD_Connection *connection, { #ifdef HTTPS_SUPPORT if (0 != (connection->daemon->options & MHD_USE_TLS)) - return MHD_send_on_connection_ (connection, - header, - header_size, - MHD_SSO_HDR_CORK); + { + ssize_t ret; + + ret = MHD_send_on_connection_ (connection, + header, + header_size, + MHD_SSO_HDR_CORK); + if ( (ret == header_size) && + (0 == buffer_size) && + connection->sk_cork_on) + { + (void) gnutls_record_uncork (connection->tls_session, 0); + connection->sk_cork_on = false; + } + return ret; + } #endif #if defined(HAVE_SENDMSG) || defined(HAVE_WRITEV) MHD_socket s = connection->socket_fd; diff --git a/src/testcurl/.gitignore b/src/testcurl/.gitignore @@ -108,3 +108,4 @@ test_large_put_inc11 test_delete test_digestauth_sha256 perf_get_concurrent11 +test_get_empty diff --git a/src/testcurl/https/test_https_get.c b/src/testcurl/https/test_https_get.c @@ -37,6 +37,10 @@ extern const char srv_signed_cert_pem[]; extern const char srv_signed_key_pem[]; + +static int global_port; + + /* perform a HTTP GET request via SSL/TLS */ static int test_secure_get (FILE *test_fd, @@ -55,7 +59,8 @@ test_secure_get (FILE *test_fd, d = MHD_start_daemon (MHD_USE_THREAD_PER_CONNECTION | MHD_USE_INTERNAL_POLLING_THREAD | MHD_USE_TLS | MHD_USE_ERROR_LOG, port, - NULL, NULL, &http_ahc, NULL, + 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); @@ -76,13 +81,156 @@ test_secure_get (FILE *test_fd, port = (int) dinfo->port; } - ret = test_https_transfer (test_fd, port, cipher_suite, proto_version); + ret = test_https_transfer (test_fd, + port, + cipher_suite, + proto_version); MHD_stop_daemon (d); return ret; } +static int +ahc_empty (void *cls, + struct MHD_Connection *connection, + const char *url, + const char *method, + const char *version, + const char *upload_data, + size_t *upload_data_size, + void **unused) +{ + static int ptr; + struct MHD_Response *response; + int ret; + (void) cls; + (void) url; + (void) url; + (void) version; /* Unused. Silent compiler warning. */ + (void) upload_data; + (void) upload_data_size; /* Unused. Silent compiler warning. */ + + if (0 != strcasecmp ("GET", + method)) + return MHD_NO; /* unexpected method */ + if (&ptr != *unused) + { + *unused = &ptr; + return MHD_YES; + } + *unused = NULL; + response = MHD_create_response_from_buffer (0, + NULL, + MHD_RESPMEM_PERSISTENT); + ret = MHD_queue_response (connection, + MHD_HTTP_OK, + response); + MHD_destroy_response (response); + if (ret == MHD_NO) + { + fprintf (stderr, "Failed to queue response.\n"); + _exit (20); + } + return ret; +} + + +static int +curlExcessFound (CURL *c, + curl_infotype type, + char *data, + size_t size, + void *cls) +{ + static const char *excess_found = "Excess found"; + const size_t str_size = strlen (excess_found); + (void) c; /* Unused. Silence compiler warning. */ + + if ((CURLINFO_TEXT == type) + &&(size >= str_size) + &&(0 == strncmp (excess_found, data, str_size))) + *(int *) cls = 1; + return 0; +} + + +static int +testEmptyGet (int poll_flag) +{ + struct MHD_Daemon *d; + CURL *c; + char buf[2048]; + struct CBC cbc; + CURLcode errornum; + int excess_found = 0; + + + if ( (0 == global_port) && + (MHD_NO == MHD_is_feature_supported (MHD_FEATURE_AUTODETECT_BIND_PORT)) ) + { + global_port = 1225; + + } + + cbc.buf = buf; + cbc.size = 2048; + cbc.pos = 0; + d = MHD_start_daemon (MHD_USE_INTERNAL_POLLING_THREAD | MHD_USE_ERROR_LOG + | poll_flag | MHD_USE_TLS, + global_port, NULL, NULL, + &ahc_empty, 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) + return 4194304; + if (0 == global_port) + { + const union MHD_DaemonInfo *dinfo; + dinfo = MHD_get_daemon_info (d, MHD_DAEMON_INFO_BIND_PORT); + if ((NULL == dinfo) ||(0 == dinfo->port) ) + { + MHD_stop_daemon (d); return 32; + } + global_port = (int) dinfo->port; + } + c = curl_easy_init (); + curl_easy_setopt (c, CURLOPT_URL, "https://127.0.0.1/"); + curl_easy_setopt (c, CURLOPT_PORT, (long) global_port); + curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer); + curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc); + curl_easy_setopt (c, CURLOPT_DEBUGFUNCTION, &curlExcessFound); + curl_easy_setopt (c, CURLOPT_DEBUGDATA, &excess_found); + curl_easy_setopt (c, CURLOPT_VERBOSE, 1L); + curl_easy_setopt (c, CURLOPT_FAILONERROR, 1L); + curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L); + curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L); + curl_easy_setopt (c, CURLOPT_SSL_VERIFYPEER, 0L); + curl_easy_setopt (c, CURLOPT_SSL_VERIFYHOST, 0L); + /* NOTE: use of CONNECTTIMEOUT without also + setting NOSIGNAL results in really weird + crashes on my system!*/ + curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1L); + if (CURLE_OK != (errornum = curl_easy_perform (c))) + { + fprintf (stderr, + "curl_easy_perform failed: `%s'\n", + curl_easy_strerror (errornum)); + curl_easy_cleanup (c); + MHD_stop_daemon (d); + return 8388608; + } + curl_easy_cleanup (c); + MHD_stop_daemon (d); + if (cbc.pos != 0) + return 16777216; + if (excess_found) + return 33554432; + return 0; +} + + int main (int argc, char *const *argv) { @@ -109,11 +257,9 @@ main (int argc, char *const *argv) { aes256_sha_tlsv1 = "rsa_aes_256_sha"; } - errorCount += test_secure_get (NULL, aes256_sha_tlsv1, CURL_SSLVERSION_TLSv1); - print_test_result (errorCount, argv[0]); - + errorCount += testEmptyGet (0); curl_global_cleanup (); return errorCount != 0 ? 1 : 0; diff --git a/src/testcurl/https/tls_test_common.c b/src/testcurl/https/tls_test_common.c @@ -62,7 +62,8 @@ setup_ca_cert () */ int test_daemon_get (void *cls, - const char *cipher_suite, int proto_version, + const char *cipher_suite, + int proto_version, int port, int ver_peer) { @@ -71,7 +72,7 @@ test_daemon_get (void *cls, CURLcode errornum; char url[255]; size_t len; - (void) cls; /* Unused. Silent compiler warning. */ + (void) cls; /* Unused. Silence compiler warning. */ len = strlen (test_data); if (NULL == (cbc.buf = malloc (sizeof (char) * len))) @@ -138,20 +139,29 @@ test_daemon_get (void *cls, void -print_test_result (int test_outcome, char *test_name) +print_test_result (int test_outcome, + char *test_name) { if (test_outcome != 0) - fprintf (stderr, "running test: %s [fail: %u]\n", test_name, (unsigned - int) + fprintf (stderr, + "running test: %s [fail: %u]\n", + test_name, (unsigned + int) test_outcome); #if 0 else - fprintf (stdout, "running test: %s [pass]\n", test_name); + fprintf (stdout, + "running test: %s [pass]\n", + test_name); #endif } + size_t -copyBuffer (void *ptr, size_t size, size_t nmemb, void *ctx) +copyBuffer (void *ptr, + size_t size, + size_t nmemb, + void *ctx) { struct CBC *cbc = ctx; @@ -162,13 +172,19 @@ copyBuffer (void *ptr, size_t size, size_t nmemb, void *ctx) return size * nmemb; } + /** * HTTP access handler call back */ int -http_ahc (void *cls, struct MHD_Connection *connection, - const char *url, const char *method, const char *version, - const char *upload_data, size_t *upload_data_size, void **ptr) +http_ahc (void *cls, + struct MHD_Connection *connection, + const char *url, + const char *method, + const char *version, + const char *upload_data, + size_t *upload_data_size, + void **ptr) { static int aptr; struct MHD_Response *response; @@ -193,15 +209,26 @@ http_ahc (void *cls, struct MHD_Connection *connection, return ret; } + /* HTTP access handler call back */ int -http_dummy_ahc (void *cls, struct MHD_Connection *connection, - const char *url, const char *method, const char *version, - const char *upload_data, size_t *upload_data_size, +http_dummy_ahc (void *cls, + struct MHD_Connection *connection, + const char *url, + const char *method, + const char *version, + const char *upload_data, + size_t *upload_data_size, void **ptr) { - (void) cls; (void) connection; (void) url; (void) method; (void) version; /* Unused. Silent compiler warning. */ - (void) upload_data; (void) upload_data_size; (void) ptr; /* Unused. Silent compiler warning. */ + (void) cls; + (void) connection; + (void) url; + (void) method; + (void) version; /* Unused. Silent compiler warning. */ + (void) upload_data; + (void) upload_data_size; + (void) ptr; /* Unused. Silent compiler warning. */ return 0; } @@ -215,7 +242,9 @@ http_dummy_ahc (void *cls, struct MHD_Connection *connection, */ /* TODO have test wrap consider a NULL cbc */ int -send_curl_req (char *url, struct CBC *cbc, const char *cipher_suite, +send_curl_req (char *url, + struct CBC *cbc, + const char *cipher_suite, int proto_version) { CURL *c; diff --git a/src/testcurl/test_get.c b/src/testcurl/test_get.c @@ -719,14 +719,19 @@ ahc_empty (void *cls, const char *url, const char *method, const char *version, - const char *upload_data, size_t *upload_data_size, + const char *upload_data, + size_t *upload_data_size, void **unused) { static int ptr; struct MHD_Response *response; int ret; - (void) cls; (void) url; (void) url; (void) version; /* Unused. Silent compiler warning. */ - (void) upload_data; (void) upload_data_size; /* Unused. Silent compiler warning. */ + (void) cls; + (void) url; + (void) url; + (void) version; /* Unused. Silent compiler warning. */ + (void) upload_data; + (void) upload_data_size; /* Unused. Silent compiler warning. */ if (0 != strcasecmp ("GET", method)) return MHD_NO; /* unexpected method */ @@ -739,7 +744,9 @@ ahc_empty (void *cls, response = MHD_create_response_from_buffer (0, NULL, MHD_RESPMEM_PERSISTENT); - ret = MHD_queue_response (connection, MHD_HTTP_OK, response); + ret = MHD_queue_response (connection, + MHD_HTTP_OK, + response); MHD_destroy_response (response); if (ret == MHD_NO) { @@ -751,7 +758,10 @@ ahc_empty (void *cls, static int -curlExcessFound (CURL *c, curl_infotype type, char *data, size_t size, +curlExcessFound (CURL *c, + curl_infotype type, + char *data, + size_t size, void *cls) { static const char *excess_found = "Excess found"; diff --git a/src/testcurl/test_get_empty.c b/src/testcurl/test_get_empty.c @@ -0,0 +1,941 @@ +/* + This file is part of libmicrohttpd + Copyright (C) 2007, 2009, 2011, 2019 Christian Grothoff + + libmicrohttpd is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2, or (at your + option) any later version. + + libmicrohttpd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with libmicrohttpd; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ +/** + * @file test_get_empty.c + * @brief Testcase for libmicrohttpd GET operations returning an empty body + * @author Christian Grothoff + */ +#include "MHD_config.h" +#include "platform.h" +#include <curl/curl.h> +#include <microhttpd.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include "test_helpers.h" +#include "mhd_sockets.h" /* only macros used */ + + +#define EXPECTED_URI_PATH "/hello_world?a=%26&b=c" + +#ifdef _WIN32 +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN 1 +#endif /* !WIN32_LEAN_AND_MEAN */ +#include <windows.h> +#endif + +#ifndef WINDOWS +#include <unistd.h> +#include <sys/socket.h> +#endif + +#if defined(CPU_COUNT) && (CPU_COUNT + 0) < 2 +#undef CPU_COUNT +#endif +#if ! defined(CPU_COUNT) +#define CPU_COUNT 2 +#endif + +static int oneone; +static int global_port; + +struct CBC +{ + char *buf; + size_t pos; + size_t size; +}; + + +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 void * +log_cb (void *cls, + const char *uri, + struct MHD_Connection *con) +{ + (void) cls; + (void) con; + if (0 != strcmp (uri, + EXPECTED_URI_PATH)) + { + fprintf (stderr, + "Wrong URI: `%s'\n", + uri); + _exit (22); + } + return NULL; +} + + +static int +ahc_echo (void *cls, + struct MHD_Connection *connection, + const char *url, + const char *method, + const char *version, + const char *upload_data, size_t *upload_data_size, + void **unused) +{ + static int ptr; + const char *me = cls; + struct MHD_Response *response; + int ret; + (void) version; + (void) upload_data; + (void) upload_data_size; /* Unused. Silence compiler warning. */ + + if (0 != strcasecmp (me, method)) + return MHD_NO; /* unexpected method */ + if (&ptr != *unused) + { + *unused = &ptr; + return MHD_YES; + } + *unused = NULL; + response = MHD_create_response_from_buffer (0, + NULL, + MHD_RESPMEM_PERSISTENT); + ret = MHD_queue_response (connection, + MHD_HTTP_NO_CONTENT, + response); + MHD_destroy_response (response); + if (ret == MHD_NO) + { + fprintf (stderr, "Failed to queue response.\n"); + _exit (19); + } + return ret; +} + + +static int +testInternalGet (int poll_flag) +{ + struct MHD_Daemon *d; + CURL *c; + char buf[2048]; + struct CBC cbc; + CURLcode errornum; + + if ( (0 == global_port) && + (MHD_NO == MHD_is_feature_supported (MHD_FEATURE_AUTODETECT_BIND_PORT)) ) + { + global_port = 1220; + if (oneone) + global_port += 20; + } + + cbc.buf = buf; + cbc.size = 2048; + cbc.pos = 0; + d = MHD_start_daemon (MHD_USE_INTERNAL_POLLING_THREAD | MHD_USE_ERROR_LOG + | poll_flag, + global_port, NULL, NULL, + &ahc_echo, "GET", + MHD_OPTION_URI_LOG_CALLBACK, &log_cb, NULL, + MHD_OPTION_END); + if (d == NULL) + return 1; + if (0 == global_port) + { + const union MHD_DaemonInfo *dinfo; + dinfo = MHD_get_daemon_info (d, MHD_DAEMON_INFO_BIND_PORT); + if ((NULL == dinfo) ||(0 == dinfo->port) ) + { + MHD_stop_daemon (d); return 32; + } + global_port = (int) dinfo->port; + } + c = curl_easy_init (); + curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1" EXPECTED_URI_PATH); + curl_easy_setopt (c, CURLOPT_PORT, (long) global_port); + curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer); + curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc); + curl_easy_setopt (c, CURLOPT_FAILONERROR, 1L); + curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L); + curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L); + if (oneone) + curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1); + else + curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0); + /* NOTE: use of CONNECTTIMEOUT without also + setting NOSIGNAL results in really weird + crashes on my system!*/ + curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1L); + if (CURLE_OK != (errornum = curl_easy_perform (c))) + { + fprintf (stderr, + "curl_easy_perform failed: `%s'\n", + curl_easy_strerror (errornum)); + curl_easy_cleanup (c); + MHD_stop_daemon (d); + return 2; + } + curl_easy_cleanup (c); + MHD_stop_daemon (d); + if (cbc.pos != 0) + return 4; + return 0; +} + + +static int +testMultithreadedGet (int poll_flag) +{ + struct MHD_Daemon *d; + CURL *c; + char buf[2048]; + struct CBC cbc; + CURLcode errornum; + + if ( (0 == global_port) && + (MHD_NO == MHD_is_feature_supported (MHD_FEATURE_AUTODETECT_BIND_PORT)) ) + { + global_port = 1221; + if (oneone) + global_port += 20; + } + + cbc.buf = buf; + cbc.size = 2048; + cbc.pos = 0; + d = MHD_start_daemon (MHD_USE_THREAD_PER_CONNECTION + | MHD_USE_INTERNAL_POLLING_THREAD | MHD_USE_ERROR_LOG + | poll_flag, + global_port, NULL, NULL, + &ahc_echo, "GET", + MHD_OPTION_URI_LOG_CALLBACK, &log_cb, NULL, + MHD_OPTION_END); + if (d == NULL) + return 16; + if (0 == global_port) + { + const union MHD_DaemonInfo *dinfo; + dinfo = MHD_get_daemon_info (d, MHD_DAEMON_INFO_BIND_PORT); + if ((NULL == dinfo) ||(0 == dinfo->port) ) + { + MHD_stop_daemon (d); return 32; + } + global_port = (int) dinfo->port; + } + c = curl_easy_init (); + curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1" EXPECTED_URI_PATH); + curl_easy_setopt (c, CURLOPT_PORT, (long) global_port); + curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer); + curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc); + curl_easy_setopt (c, CURLOPT_FAILONERROR, 1L); + curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L); + if (oneone) + curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1); + else + curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0); + curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L); + /* NOTE: use of CONNECTTIMEOUT without also + setting NOSIGNAL results in really weird + crashes on my system! */ + curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1L); + if (CURLE_OK != (errornum = curl_easy_perform (c))) + { + fprintf (stderr, + "curl_easy_perform failed: `%s'\n", + curl_easy_strerror (errornum)); + curl_easy_cleanup (c); + MHD_stop_daemon (d); + return 32; + } + curl_easy_cleanup (c); + MHD_stop_daemon (d); + if (cbc.pos != 0) + return 64; + return 0; +} + + +static int +testMultithreadedPoolGet (int poll_flag) +{ + struct MHD_Daemon *d; + CURL *c; + char buf[2048]; + struct CBC cbc; + CURLcode errornum; + + if ( (0 == global_port) && + (MHD_NO == MHD_is_feature_supported (MHD_FEATURE_AUTODETECT_BIND_PORT)) ) + { + global_port = 1222; + if (oneone) + global_port += 20; + } + + cbc.buf = buf; + cbc.size = 2048; + cbc.pos = 0; + d = MHD_start_daemon (MHD_USE_INTERNAL_POLLING_THREAD | MHD_USE_ERROR_LOG + | poll_flag, + global_port, NULL, NULL, + &ahc_echo, "GET", + MHD_OPTION_THREAD_POOL_SIZE, CPU_COUNT, + MHD_OPTION_URI_LOG_CALLBACK, &log_cb, NULL, + MHD_OPTION_END); + if (d == NULL) + return 16; + if (0 == global_port) + { + const union MHD_DaemonInfo *dinfo; + dinfo = MHD_get_daemon_info (d, MHD_DAEMON_INFO_BIND_PORT); + if ((NULL == dinfo) ||(0 == dinfo->port) ) + { + MHD_stop_daemon (d); return 32; + } + global_port = (int) dinfo->port; + } + c = curl_easy_init (); + curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1" EXPECTED_URI_PATH); + curl_easy_setopt (c, CURLOPT_PORT, (long) global_port); + curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer); + curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc); + curl_easy_setopt (c, CURLOPT_FAILONERROR, 1L); + curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L); + if (oneone) + curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1); + else + curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0); + curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L); + /* NOTE: use of CONNECTTIMEOUT without also + setting NOSIGNAL results in really weird + crashes on my system!*/ + curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1L); + if (CURLE_OK != (errornum = curl_easy_perform (c))) + { + fprintf (stderr, + "curl_easy_perform failed: `%s'\n", + curl_easy_strerror (errornum)); + curl_easy_cleanup (c); + MHD_stop_daemon (d); + return 32; + } + curl_easy_cleanup (c); + MHD_stop_daemon (d); + if (cbc.pos != 0) + return 64; + return 0; +} + + +static int +testExternalGet () +{ + struct MHD_Daemon *d; + CURL *c; + char buf[2048]; + struct CBC cbc; + CURLM *multi; + CURLMcode mret; + fd_set rs; + fd_set ws; + fd_set es; + MHD_socket maxsock; + int maxposixs; + int running; + struct CURLMsg *msg; + time_t start; + struct timeval tv; + + if ( (0 == global_port) && + (MHD_NO == MHD_is_feature_supported (MHD_FEATURE_AUTODETECT_BIND_PORT)) ) + { + global_port = 1223; + if (oneone) + global_port += 20; + } + + multi = NULL; + cbc.buf = buf; + cbc.size = 2048; + cbc.pos = 0; + d = MHD_start_daemon (MHD_USE_ERROR_LOG, + global_port, NULL, NULL, + &ahc_echo, "GET", + MHD_OPTION_URI_LOG_CALLBACK, &log_cb, NULL, + MHD_OPTION_END); + if (d == NULL) + return 256; + if (0 == global_port) + { + const union MHD_DaemonInfo *dinfo; + dinfo = MHD_get_daemon_info (d, MHD_DAEMON_INFO_BIND_PORT); + if ((NULL == dinfo) ||(0 == dinfo->port) ) + { + MHD_stop_daemon (d); return 32; + } + global_port = (int) dinfo->port; + } + c = curl_easy_init (); + curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1" EXPECTED_URI_PATH); + curl_easy_setopt (c, CURLOPT_PORT, (long) global_port); + curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer); + curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc); + curl_easy_setopt (c, CURLOPT_FAILONERROR, 1L); + if (oneone) + curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1); + else + curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0); + curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L); + curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L); + /* NOTE: use of CONNECTTIMEOUT without also + setting NOSIGNAL results in really weird + crashes on my system! */ + curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1L); + + + multi = curl_multi_init (); + if (multi == NULL) + { + curl_easy_cleanup (c); + MHD_stop_daemon (d); + return 512; + } + mret = curl_multi_add_handle (multi, c); + if (mret != CURLM_OK) + { + curl_multi_cleanup (multi); + curl_easy_cleanup (c); + MHD_stop_daemon (d); + return 1024; + } + start = time (NULL); + while ((time (NULL) - start < 5) && (multi != NULL)) + { + maxsock = MHD_INVALID_SOCKET; + maxposixs = -1; + FD_ZERO (&rs); + FD_ZERO (&ws); + FD_ZERO (&es); + curl_multi_perform (multi, &running); + mret = curl_multi_fdset (multi, &rs, &ws, &es, &maxposixs); + if (mret != CURLM_OK) + { + curl_multi_remove_handle (multi, c); + curl_multi_cleanup (multi); + curl_easy_cleanup (c); + MHD_stop_daemon (d); + return 2048; + } + if (MHD_YES != MHD_get_fdset (d, &rs, &ws, &es, &maxsock)) + { + curl_multi_remove_handle (multi, c); + curl_multi_cleanup (multi); + curl_easy_cleanup (c); + MHD_stop_daemon (d); + return 4096; + } + tv.tv_sec = 0; + tv.tv_usec = 1000; +#ifdef MHD_POSIX_SOCKETS + if (maxsock > maxposixs) + maxposixs = maxsock; +#endif /* MHD_POSIX_SOCKETS */ + if (-1 == select (maxposixs + 1, &rs, &ws, &es, &tv)) + { +#ifdef MHD_POSIX_SOCKETS + if (EINTR != errno) + abort (); +#else + if ((WSAEINVAL != WSAGetLastError ()) ||(0 != rs.fd_count) ||(0 != + ws.fd_count) + ||(0 != es.fd_count) ) + _exit (99); + Sleep (1000); +#endif + } + curl_multi_perform (multi, &running); + if (running == 0) + { + msg = curl_multi_info_read (multi, &running); + if (msg == NULL) + break; + if (msg->msg == CURLMSG_DONE) + { + if (msg->data.result != CURLE_OK) + printf ("%s failed at %s:%d: `%s'\n", + "curl_multi_perform", + __FILE__, + __LINE__, curl_easy_strerror (msg->data.result)); + curl_multi_remove_handle (multi, c); + curl_multi_cleanup (multi); + curl_easy_cleanup (c); + c = NULL; + multi = NULL; + } + } + MHD_run (d); + } + if (multi != NULL) + { + curl_multi_remove_handle (multi, c); + curl_easy_cleanup (c); + curl_multi_cleanup (multi); + } + MHD_stop_daemon (d); + if (cbc.pos != 0) + return 8192; + return 0; +} + + +static int +testUnknownPortGet (int poll_flag) +{ + struct MHD_Daemon *d; + const union MHD_DaemonInfo *di; + CURL *c; + char buf[2048]; + struct CBC cbc; + CURLcode errornum; + int port; + + struct sockaddr_in addr; + socklen_t addr_len = sizeof(addr); + memset (&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_port = 0; + addr.sin_addr.s_addr = INADDR_ANY; + + cbc.buf = buf; + cbc.size = 2048; + cbc.pos = 0; + d = MHD_start_daemon (MHD_USE_INTERNAL_POLLING_THREAD | MHD_USE_ERROR_LOG + | poll_flag, + 0, NULL, NULL, &ahc_echo, "GET", + MHD_OPTION_SOCK_ADDR, &addr, + MHD_OPTION_URI_LOG_CALLBACK, &log_cb, NULL, + MHD_OPTION_END); + if (MHD_NO == MHD_is_feature_supported (MHD_FEATURE_AUTODETECT_BIND_PORT)) + { + di = MHD_get_daemon_info (d, MHD_DAEMON_INFO_LISTEN_FD); + if (di == NULL) + return 65536; + + if (0 != getsockname (di->listen_fd, (struct sockaddr *) &addr, &addr_len)) + return 131072; + + if (addr.sin_family != AF_INET) + return 26214; + port = (int) ntohs (addr.sin_port); + } + else + { + const union MHD_DaemonInfo *dinfo; + dinfo = MHD_get_daemon_info (d, MHD_DAEMON_INFO_BIND_PORT); + if ((NULL == dinfo) ||(0 == dinfo->port) ) + { + MHD_stop_daemon (d); return 32; + } + port = (int) dinfo->port; + } + + snprintf (buf, + sizeof(buf), + "http://127.0.0.1:%d%s", + port, + EXPECTED_URI_PATH); + + c = curl_easy_init (); + curl_easy_setopt (c, CURLOPT_URL, buf); + curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer); + curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc); + curl_easy_setopt (c, CURLOPT_FAILONERROR, 1L); + curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L); + curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L); + if (oneone) + curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1); + else + curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0); + /* NOTE: use of CONNECTTIMEOUT without also + setting NOSIGNAL results in really weird + crashes on my system! */ + curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1L); + if (CURLE_OK != (errornum = curl_easy_perform (c))) + { + fprintf (stderr, + "curl_easy_perform failed: `%s'\n", + curl_easy_strerror (errornum)); + curl_easy_cleanup (c); + MHD_stop_daemon (d); + return 524288; + } + curl_easy_cleanup (c); + MHD_stop_daemon (d); + if (cbc.pos != 0) + return 1048576; + return 0; +} + + +static int +testStopRace (int poll_flag) +{ + struct sockaddr_in sin; + MHD_socket fd; + struct MHD_Daemon *d; + + if ( (0 == global_port) && + (MHD_NO == MHD_is_feature_supported (MHD_FEATURE_AUTODETECT_BIND_PORT)) ) + { + global_port = 1224; + if (oneone) + global_port += 20; + } + + d = MHD_start_daemon (MHD_USE_THREAD_PER_CONNECTION + | MHD_USE_INTERNAL_POLLING_THREAD | MHD_USE_ERROR_LOG + | poll_flag, + global_port, NULL, NULL, + &ahc_echo, "GET", + MHD_OPTION_URI_LOG_CALLBACK, &log_cb, NULL, + MHD_OPTION_END); + if (d == NULL) + return 16; + if (0 == global_port) + { + const union MHD_DaemonInfo *dinfo; + dinfo = MHD_get_daemon_info (d, MHD_DAEMON_INFO_BIND_PORT); + if ((NULL == dinfo) ||(0 == dinfo->port) ) + { + MHD_stop_daemon (d); return 32; + } + global_port = (int) dinfo->port; + } + + fd = socket (PF_INET, SOCK_STREAM, 0); + if (fd == MHD_INVALID_SOCKET) + { + fprintf (stderr, "socket error\n"); + return 256; + } + + memset (&sin, 0, sizeof(sin)); + sin.sin_family = AF_INET; + sin.sin_port = htons (global_port); + sin.sin_addr.s_addr = htonl (0x7f000001); + + if (connect (fd, (struct sockaddr *) (&sin), sizeof(sin)) < 0) + { + fprintf (stderr, "connect error\n"); + MHD_socket_close_chk_ (fd); + return 512; + } + + /* printf("Waiting\n"); */ + /* Let the thread get going. */ + usleep (500000); + + /* printf("Stopping daemon\n"); */ + MHD_stop_daemon (d); + + MHD_socket_close_chk_ (fd); + + /* printf("good\n"); */ + return 0; +} + + +static int +ahc_empty (void *cls, + struct MHD_Connection *connection, + const char *url, + const char *method, + const char *version, + const char *upload_data, size_t *upload_data_size, + void **unused) +{ + static int ptr; + struct MHD_Response *response; + int ret; + (void) cls; + (void) url; + (void) url; + (void) version; /* Unused. Silence compiler warning. */ + (void) upload_data; + (void) upload_data_size; /* Unused. Silent compiler warning. */ + + if (0 != strcasecmp ("GET", method)) + return MHD_NO; /* unexpected method */ + if (&ptr != *unused) + { + *unused = &ptr; + return MHD_YES; + } + *unused = NULL; + response = MHD_create_response_from_buffer (0, + NULL, + MHD_RESPMEM_PERSISTENT); + ret = MHD_queue_response (connection, MHD_HTTP_OK, response); + MHD_destroy_response (response); + if (ret == MHD_NO) + { + fprintf (stderr, "Failed to queue response.\n"); + _exit (20); + } + return ret; +} + + +static int +curlExcessFound (CURL *c, curl_infotype type, char *data, size_t size, + void *cls) +{ + static const char *excess_found = "Excess found"; + const size_t str_size = strlen (excess_found); + (void) c; /* Unused. Silent compiler warning. */ + + if ((CURLINFO_TEXT == type) + &&(size >= str_size) + &&(0 == strncmp (excess_found, data, str_size))) + *(int *) cls = 1; + return 0; +} + + +static int +testEmptyGet (int poll_flag) +{ + struct MHD_Daemon *d; + CURL *c; + char buf[2048]; + struct CBC cbc; + CURLcode errornum; + int excess_found = 0; + + if ( (0 == global_port) && + (MHD_NO == MHD_is_feature_supported (MHD_FEATURE_AUTODETECT_BIND_PORT)) ) + { + global_port = 1225; + if (oneone) + global_port += 20; + } + + cbc.buf = buf; + cbc.size = 2048; + cbc.pos = 0; + d = MHD_start_daemon (MHD_USE_INTERNAL_POLLING_THREAD | MHD_USE_ERROR_LOG + | poll_flag, + global_port, NULL, NULL, + &ahc_empty, NULL, + MHD_OPTION_URI_LOG_CALLBACK, &log_cb, NULL, + MHD_OPTION_END); + if (d == NULL) + return 4194304; + if (0 == global_port) + { + const union MHD_DaemonInfo *dinfo; + dinfo = MHD_get_daemon_info (d, MHD_DAEMON_INFO_BIND_PORT); + if ((NULL == dinfo) ||(0 == dinfo->port) ) + { + MHD_stop_daemon (d); return 32; + } + global_port = (int) dinfo->port; + } + c = curl_easy_init (); + curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1" EXPECTED_URI_PATH); + curl_easy_setopt (c, CURLOPT_PORT, (long) global_port); + curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer); + curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc); + curl_easy_setopt (c, CURLOPT_DEBUGFUNCTION, &curlExcessFound); + curl_easy_setopt (c, CURLOPT_DEBUGDATA, &excess_found); + curl_easy_setopt (c, CURLOPT_VERBOSE, 1L); + curl_easy_setopt (c, CURLOPT_FAILONERROR, 1L); + curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L); + curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L); + if (oneone) + curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1); + else + curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0); + /* NOTE: use of CONNECTTIMEOUT without also + setting NOSIGNAL results in really weird + crashes on my system!*/ + curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1L); + if (CURLE_OK != (errornum = curl_easy_perform (c))) + { + fprintf (stderr, + "curl_easy_perform failed: `%s'\n", + curl_easy_strerror (errornum)); + curl_easy_cleanup (c); + MHD_stop_daemon (d); + return 8388608; + } + curl_easy_cleanup (c); + MHD_stop_daemon (d); + if (cbc.pos != 0) + return 16777216; + if (excess_found) + return 33554432; + return 0; +} + + +int +main (int argc, char *const *argv) +{ + unsigned int errorCount = 0; + unsigned int test_result = 0; + int verbose = 0; + + if ((NULL == argv)||(0 == argv[0])) + return 99; + oneone = has_in_name (argv[0], "11"); + verbose = has_param (argc, argv, "-v") || has_param (argc, argv, "--verbose"); + if (0 != curl_global_init (CURL_GLOBAL_WIN32)) + return 2; + global_port = 0; + test_result = testExternalGet (); + if (test_result) + fprintf (stderr, "FAILED: testExternalGet () - %u.\n", test_result); + else if (verbose) + printf ("PASSED: testExternalGet ().\n"); + errorCount += test_result; + if (MHD_YES == MHD_is_feature_supported (MHD_FEATURE_THREADS)) + { + test_result += testInternalGet (0); + if (test_result) + fprintf (stderr, "FAILED: testInternalGet (0) - %u.\n", test_result); + else if (verbose) + printf ("PASSED: testInternalGet (0).\n"); + errorCount += test_result; + test_result += testMultithreadedGet (0); + if (test_result) + fprintf (stderr, "FAILED: testMultithreadedGet (0) - %u.\n", test_result); + else if (verbose) + printf ("PASSED: testMultithreadedGet (0).\n"); + errorCount += test_result; + test_result += testMultithreadedPoolGet (0); + if (test_result) + fprintf (stderr, "FAILED: testMultithreadedPoolGet (0) - %u.\n", + test_result); + else if (verbose) + printf ("PASSED: testMultithreadedPoolGet (0).\n"); + errorCount += test_result; + test_result += testUnknownPortGet (0); + if (test_result) + fprintf (stderr, "FAILED: testUnknownPortGet (0) - %u.\n", test_result); + else if (verbose) + printf ("PASSED: testUnknownPortGet (0).\n"); + errorCount += test_result; + test_result += testEmptyGet (0); + if (test_result) + fprintf (stderr, "FAILED: testEmptyGet (0) - %u.\n", test_result); + else if (verbose) + printf ("PASSED: testEmptyGet (0).\n"); + errorCount += test_result; + if (MHD_YES == MHD_is_feature_supported (MHD_FEATURE_POLL)) + { + test_result += testInternalGet (MHD_USE_POLL); + if (test_result) + fprintf (stderr, "FAILED: testInternalGet (MHD_USE_POLL) - %u.\n", + test_result); + else if (verbose) + printf ("PASSED: testInternalGet (MHD_USE_POLL).\n"); + errorCount += test_result; + test_result += testMultithreadedGet (MHD_USE_POLL); + if (test_result) + fprintf (stderr, "FAILED: testMultithreadedGet (MHD_USE_POLL) - %u.\n", + test_result); + else if (verbose) + printf ("PASSED: testMultithreadedGet (MHD_USE_POLL).\n"); + errorCount += test_result; + test_result += testMultithreadedPoolGet (MHD_USE_POLL); + if (test_result) + fprintf (stderr, + "FAILED: testMultithreadedPoolGet (MHD_USE_POLL) - %u.\n", + test_result); + else if (verbose) + printf ("PASSED: testMultithreadedPoolGet (MHD_USE_POLL).\n"); + errorCount += test_result; + test_result += testUnknownPortGet (MHD_USE_POLL); + if (test_result) + fprintf (stderr, "FAILED: testUnknownPortGet (MHD_USE_POLL) - %u.\n", + test_result); + else if (verbose) + printf ("PASSED: testUnknownPortGet (MHD_USE_POLL).\n"); + errorCount += test_result; + test_result += testEmptyGet (MHD_USE_POLL); + if (test_result) + fprintf (stderr, "FAILED: testEmptyGet (MHD_USE_POLL) - %u.\n", + test_result); + else if (verbose) + printf ("PASSED: testEmptyGet (MHD_USE_POLL).\n"); + errorCount += test_result; + } + if (MHD_YES == MHD_is_feature_supported (MHD_FEATURE_EPOLL)) + { + test_result += testInternalGet (MHD_USE_EPOLL); + if (test_result) + fprintf (stderr, "FAILED: testInternalGet (MHD_USE_EPOLL) - %u.\n", + test_result); + else if (verbose) + printf ("PASSED: testInternalGet (MHD_USE_EPOLL).\n"); + errorCount += test_result; + test_result += testMultithreadedPoolGet (MHD_USE_EPOLL); + if (test_result) + fprintf (stderr, + "FAILED: testMultithreadedPoolGet (MHD_USE_EPOLL) - %u.\n", + test_result); + else if (verbose) + printf ("PASSED: testMultithreadedPoolGet (MHD_USE_EPOLL).\n"); + errorCount += test_result; + test_result += testUnknownPortGet (MHD_USE_EPOLL); + if (test_result) + fprintf (stderr, "FAILED: testUnknownPortGet (MHD_USE_EPOLL) - %u.\n", + test_result); + else if (verbose) + printf ("PASSED: testUnknownPortGet (MHD_USE_EPOLL).\n"); + errorCount += test_result; + test_result += testEmptyGet (MHD_USE_EPOLL); + if (test_result) + fprintf (stderr, "FAILED: testEmptyGet (MHD_USE_EPOLL) - %u.\n", + test_result); + else if (verbose) + printf ("PASSED: testEmptyGet (MHD_USE_EPOLL).\n"); + errorCount += test_result; + } + } + if (0 != errorCount) + fprintf (stderr, + "Error (code: %u)\n", + errorCount); + else if (verbose) + printf ("All tests passed.\n"); + curl_global_cleanup (); + return errorCount != 0; /* 0 == pass */ +}