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:
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, ©Buffer);
+ 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, ©Buffer);
+ 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, ©Buffer);
+ 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, ©Buffer);
+ 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, ©Buffer);
+ 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, ©Buffer);
+ 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, ©Buffer);
+ 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 */
+}