libmicrohttpd

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

commit d2630b7e6970c4a67c4e914e4f426f7da0f51c29
parent ebd810e770e0147dde2251b02e92d2f16f538942
Author: Christian Grothoff <christian@grothoff.org>
Date:   Tue, 17 Mar 2009 06:59:31 +0000

adding thread pool functionality -- code by Richard Alimi


Diffstat:
Msrc/daemon/daemon.c | 131+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----
Msrc/daemon/internal.h | 10++++++++++
Msrc/include/microhttpd.h | 11++++++++++-
Msrc/testcurl/daemontest_get.c | 50++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/testcurl/daemontest_get_chunked.c | 43+++++++++++++++++++++++++++++++++++++++++++
Msrc/testcurl/daemontest_iplimit.c | 99+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/testcurl/daemontest_large_put.c | 60++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/testcurl/daemontest_post.c | 53+++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/testcurl/daemontest_post_loop.c | 65+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/testcurl/daemontest_postform.c | 55+++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/testcurl/daemontest_process_headers.c | 50++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/testcurl/daemontest_put.c | 59+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/testcurl/daemontest_put_chunked.c | 59+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
13 files changed, 738 insertions(+), 7 deletions(-)

diff --git a/src/daemon/daemon.c b/src/daemon/daemon.c @@ -1,6 +1,6 @@ /* This file is part of libmicrohttpd - (C) 2007, 2008 Daniel Pittman and Christian Grothoff + (C) 2007, 2008, 2009 Daniel Pittman and Christian Grothoff This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public @@ -187,8 +187,8 @@ MHD_ip_limit_add(struct MHD_Daemon *daemon, if (daemon->per_ip_connection_limit == 0) return MHD_YES; - key = (struct MHD_IPCount*) malloc (sizeof(*key)); - if (!key) + key = malloc (sizeof(*key)); + if (NULL == key) return MHD_NO; /* Initialize key */ @@ -625,7 +625,7 @@ MHD_accept_connection (struct MHD_Daemon *daemon) #endif #endif connection = malloc (sizeof (struct MHD_Connection)); - if (connection == NULL) + if (NULL == connection) { #if HAVE_MESSAGES MHD_DLOG (daemon, "Error allocating memory: %s\n", STRERROR (errno)); @@ -1017,6 +1017,7 @@ MHD_start_daemon_va (unsigned int options, const struct sockaddr *servaddr = NULL; socklen_t addrlen; enum MHD_OPTION opt; + unsigned int i; if ((port == 0) || (dh == NULL)) return NULL; @@ -1080,6 +1081,9 @@ MHD_start_daemon_va (unsigned int options, va_arg (ap, LogCallback); retVal->uri_log_callback_cls = va_arg (ap, void *); break; + case MHD_OPTION_THREAD_POOL_SIZE: + retVal->worker_pool_size = va_arg (ap, unsigned int); + break; #if HTTPS_SUPPORT case MHD_OPTION_PROTOCOL_VERSION: _set_priority (&retVal->priority_cache->protocol, @@ -1126,6 +1130,17 @@ MHD_start_daemon_va (unsigned int options, } } + /* Thread pooling currently works only with internal select thread model */ + if ((0 == (options & MHD_USE_SELECT_INTERNALLY)) + && (retVal->worker_pool_size > 0)) + { +#if HAVE_MESSAGES + FPRINTF (stderr, + "MHD thread pooling only works with MHD_USE_SELECT_INTERNALLY\n"); +#endif + return NULL; + } + if ((options & MHD_USE_IPv6) != 0) #if HAVE_INET6 socket_fd = SOCKET (PF_INET6, SOCK_STREAM, 0); @@ -1235,7 +1250,8 @@ MHD_start_daemon_va (unsigned int options, } #endif if (((0 != (options & MHD_USE_THREAD_PER_CONNECTION)) || - (0 != (options & MHD_USE_SELECT_INTERNALLY))) + ((0 != (options & MHD_USE_SELECT_INTERNALLY)) + && (0 == retVal->worker_pool_size))) && (0 != pthread_create (&retVal->pid, NULL, &MHD_select_thread, retVal))) { @@ -1248,6 +1264,87 @@ MHD_start_daemon_va (unsigned int options, CLOSE (socket_fd); return NULL; } + else if (retVal->worker_pool_size > 0) + { + /* Coarse-grained count of connections per thread (note error + * due to integer division). Also keep track of how many + * connections are leftover after an equal split. */ + unsigned int conns_per_thread = retVal->max_connections + / retVal->worker_pool_size; + unsigned int leftover_conns = retVal->max_connections + % retVal->worker_pool_size; + + /* Allocate memory for pooled objects */ + retVal->worker_pool = malloc (sizeof (*retVal->worker_pool) + * retVal->worker_pool_size); + + /* Start the workers in the pool */ + for (i = 0; i < retVal->worker_pool_size; ++i) + { + /* Create copy of the Daemon object for each worker */ + struct MHD_Daemon *d = (struct MHD_Daemon*) malloc (sizeof (*d)); + if (!d) + { +#if HAVE_MESSAGES + MHD_DLOG (retVal, + "Failed to copy daemon object: %d\n", STRERROR (errno)); +#endif + goto thread_failed; + } + memcpy (d, retVal, sizeof (*d)); + + /* Adjust pooling params for worker daemons; note that memcpy() + * has already copied MHD_USE_SELECT_INTERNALLY thread model into + * the worker threads. */ + d->master = retVal; + d->worker_pool_size = 0; + d->worker_pool = NULL; + + /* Divide available connections evenly amongst the threads. + * Thread indexes in [0, leftover_conns) each get one of the + * leftover connections. */ + d->max_connections = conns_per_thread; + if (i < leftover_conns) + ++d->max_connections; + + /* Spawn the worker thread */ + if (0 != pthread_create (&d->pid, NULL, &MHD_select_thread, d)) + { +#if HAVE_MESSAGES + MHD_DLOG (retVal, + "Failed to create pool thread: %d\n", STRERROR (errno)); +#endif + /* Free memory for this worker; cleanup below handles + * all previously-created workers. */ + free (d); + goto thread_failed; + } + + retVal->worker_pool[i] = d; + continue; + +thread_failed: + /* If no worker threads created, then shut down normally. Calling + * MHD_stop_daemon (as we do below) doesn't work here since it + * assumes a 0-sized thread pool means we had been in the default + * MHD_USE_SELECT_INTERNALLY mode. */ + if (i == 0) + { + CLOSE (socket_fd); + pthread_mutex_destroy (&retVal->per_ip_connection_mutex); + free (retVal); + return NULL; + } + + /* Shutdown worker threads we've already created. Pretend + * as though we had fully initialized our daemon, but + * with a smaller number of threads than had been + * requested. */ + retVal->worker_pool_size = i - 1; + MHD_stop_daemon (retVal); + return NULL; + } + } return retVal; } @@ -1281,20 +1378,42 @@ MHD_stop_daemon (struct MHD_Daemon *daemon) { void *unused; int fd; + unsigned int i; if (daemon == NULL) return; daemon->shutdown = MHD_YES; fd = daemon->socket_fd; daemon->socket_fd = -1; + + /* Prepare workers for shutdown */ + for (i = 0; i < daemon->worker_pool_size; ++i) + { + daemon->worker_pool[i]->shutdown = MHD_YES; + daemon->worker_pool[i]->socket_fd = -1; + } + #if DEBUG_CLOSE #if HAVE_MESSAGES MHD_DLOG (daemon, "MHD shutdown, closing listen socket\n"); #endif #endif CLOSE (fd); + + /* Signal workers to stop and clean them up */ + for (i = 0; i < daemon->worker_pool_size; ++i) + pthread_kill (daemon->worker_pool[i]->pid, SIGALRM); + for (i = 0; i < daemon->worker_pool_size; ++i) + { + pthread_join (daemon->worker_pool[i]->pid, &unused); + MHD_close_connections (daemon->worker_pool[i]); + free (daemon->worker_pool[i]); + } + free (daemon->worker_pool); + if ((0 != (daemon->options & MHD_USE_THREAD_PER_CONNECTION)) || - (0 != (daemon->options & MHD_USE_SELECT_INTERNALLY))) + ((0 != (daemon->options & MHD_USE_SELECT_INTERNALLY)) + && (0 == daemon->worker_pool_size))) { pthread_kill (daemon->pid, SIGALRM); pthread_join (daemon->pid, &unused); diff --git a/src/daemon/internal.h b/src/daemon/internal.h @@ -775,6 +775,16 @@ struct MHD_Daemon * Pointer to master daemon (NULL if this is the master) */ struct MHD_Daemon *master; + + /** + * Worker daemons (one per thread) + */ + struct MHD_Daemon **worker_pool; + + /** + * Number of worker daemons + */ + unsigned int worker_pool_size; }; diff --git a/src/include/microhttpd.h b/src/include/microhttpd.h @@ -429,8 +429,17 @@ enum MHD_OPTION * if it was compiled without the "--enable-messages" * flag being set. */ - MHD_OPTION_EXTERNAL_LOGGER = 13 + MHD_OPTION_EXTERNAL_LOGGER = 13, + /** + * Number (unsigned int) of threads in thread pool. Enable + * thread pooling by setting this value to to something + * greater than 1. Currently, thread model must be + * MHD_USE_SELECT_INTERNALLY if thread pooling is enabled + * (MHD_start_daemon returns NULL for an unsupported thread + * model). + */ + MHD_OPTION_THREAD_POOL_SIZE = 14, }; /** diff --git a/src/testcurl/daemontest_get.c b/src/testcurl/daemontest_get.c @@ -188,6 +188,55 @@ testMultithreadedGet () return 0; } +static int +testMultithreadedPoolGet () +{ + struct MHD_Daemon *d; + CURL *c; + char buf[2048]; + struct CBC cbc; + CURLcode errornum; + + cbc.buf = buf; + cbc.size = 2048; + cbc.pos = 0; + d = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY | MHD_USE_DEBUG, + 1081, NULL, NULL, &ahc_echo, "GET", + MHD_OPTION_THREAD_POOL_SIZE, 4, MHD_OPTION_END); + if (d == NULL) + return 16; + c = curl_easy_init (); + curl_easy_setopt (c, CURLOPT_URL, "http://localhost:1081/hello_world"); + curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer); + curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc); + curl_easy_setopt (c, CURLOPT_FAILONERROR, 1); + 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, 15L); + // 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); + MHD_stop_daemon (d); + return 32; + } + curl_easy_cleanup (c); + MHD_stop_daemon (d); + if (cbc.pos != strlen ("/hello_world")) + return 64; + if (0 != strncmp ("/hello_world", cbc.buf, strlen ("/hello_world"))) + return 128; + return 0; +} static int testExternalGet () @@ -323,6 +372,7 @@ main (int argc, char *const *argv) return 2; errorCount += testInternalGet (); errorCount += testMultithreadedGet (); + errorCount += testMultithreadedPoolGet (); errorCount += testExternalGet (); if (errorCount != 0) fprintf (stderr, "Error (code: %u)\n", errorCount); diff --git a/src/testcurl/daemontest_get_chunked.c b/src/testcurl/daemontest_get_chunked.c @@ -227,6 +227,48 @@ testMultithreadedGet () return validate (cbc, 64); } +static int +testMultithreadedPoolGet () +{ + struct MHD_Daemon *d; + CURL *c; + char buf[2048]; + struct CBC cbc; + CURLcode errornum; + + cbc.buf = buf; + cbc.size = 2048; + cbc.pos = 0; + d = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY | MHD_USE_DEBUG, + 1081, NULL, NULL, &ahc_echo, "GET", + MHD_OPTION_THREAD_POOL_SIZE, 4, MHD_OPTION_END); + if (d == NULL) + return 16; + c = curl_easy_init (); + curl_easy_setopt (c, CURLOPT_URL, "http://localhost:1081/hello_world"); + curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer); + curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc); + curl_easy_setopt (c, CURLOPT_FAILONERROR, 1); + curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L); + curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1); + curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 15L); + // 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); + MHD_stop_daemon (d); + return 32; + } + curl_easy_cleanup (c); + MHD_stop_daemon (d); + return validate (cbc, 64); +} static int testExternalGet () @@ -354,6 +396,7 @@ main (int argc, char *const *argv) return 2; errorCount += testInternalGet (); errorCount += testMultithreadedGet (); + errorCount += testMultithreadedPoolGet (); errorCount += testExternalGet (); if (errorCount != 0) fprintf (stderr, "Error (code: %u)\n", errorCount); diff --git a/src/testcurl/daemontest_iplimit.c b/src/testcurl/daemontest_iplimit.c @@ -90,6 +90,103 @@ ahc_echo (void *cls, } static int +testMultithreadedGet () +{ + struct MHD_Daemon *d; + char buf[2048]; + int k; + + /* Test only valid for HTTP/1.1 (uses persistent connections) */ + if (!oneone) + return 0; + + d = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY | MHD_USE_DEBUG, + 1081, NULL, NULL, &ahc_echo, "GET", + MHD_OPTION_PER_IP_CONNECTION_LIMIT, 2, + MHD_OPTION_END); + if (d == NULL) + return 16; + + for (k = 0; k < 3; ++k) + { + struct CBC cbc[3]; + CURL *cenv[3]; + int i; + + for (i = 0; i < 3; ++i) + { + CURL *c; + CURLcode errornum; + + cenv[i] = c = curl_easy_init (); + cbc[i].buf = buf; + cbc[i].size = 2048; + cbc[i].pos = 0; + + curl_easy_setopt (c, CURLOPT_URL, "http://localhost:1081/hello_world"); + curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer); + curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc[i]); + curl_easy_setopt (c, CURLOPT_FAILONERROR, 1); + curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L); + curl_easy_setopt (c, CURLOPT_FORBID_REUSE, 0L); + curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1); + curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 15L); + // NOTE: use of CONNECTTIMEOUT without also + // setting NOSIGNAL results in really weird + // crashes on my system! + curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1); + + errornum = curl_easy_perform (c); + if (CURLE_OK != errornum && i < 2 + || CURLE_OK == errornum && i == 2) + { + int j; + + /* First 2 should succeed */ + if (i < 2) + fprintf (stderr, + "curl_easy_perform failed: `%s'\n", + curl_easy_strerror (errornum)); + + /* Last request should have failed */ + else + fprintf (stderr, + "No error on IP address over limit\n"); + + for (j = 0; j < i; ++j) + curl_easy_cleanup (cenv[j]); + MHD_stop_daemon (d); + return 32; + } + } + + /* Cleanup the environments */ + for (i = 0; i < 3; ++i) + curl_easy_cleanup (cenv[i]); + + sleep(2); + + for (i = 0; i < 2; ++i) + { + if (cbc[i].pos != strlen ("/hello_world")) + { + MHD_stop_daemon (d); + return 64; + } + if (0 != strncmp ("/hello_world", cbc[i].buf, strlen ("/hello_world"))) + { + MHD_stop_daemon (d); + return 128; + } + } + + + } + MHD_stop_daemon (d); + return 0; +} + +static int testMultithreadedPoolGet () { struct MHD_Daemon *d; @@ -103,6 +200,7 @@ testMultithreadedPoolGet () d = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY | MHD_USE_DEBUG, 1081, NULL, NULL, &ahc_echo, "GET", MHD_OPTION_PER_IP_CONNECTION_LIMIT, 2, + MHD_OPTION_THREAD_POOL_SIZE, 4, MHD_OPTION_END); if (d == NULL) return 16; @@ -194,6 +292,7 @@ main (int argc, char *const *argv) oneone = NULL != strstr (argv[0], "11"); if (0 != curl_global_init (CURL_GLOBAL_WIN32)) return 2; + errorCount += testMultithreadedGet (); errorCount += testMultithreadedPoolGet (); if (errorCount != 0) fprintf (stderr, "Error (code: %u)\n", errorCount); diff --git a/src/testcurl/daemontest_large_put.c b/src/testcurl/daemontest_large_put.c @@ -245,6 +245,65 @@ testMultithreadedPut () return 0; } +static int +testMultithreadedPoolPut () +{ + struct MHD_Daemon *d; + CURL *c; + struct CBC cbc; + unsigned int pos = 0; + int done_flag = 0; + CURLcode errornum; + char buf[2048]; + + cbc.buf = buf; + cbc.size = 2048; + cbc.pos = 0; + d = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY | MHD_USE_DEBUG, + 1081, + NULL, NULL, &ahc_echo, &done_flag, + MHD_OPTION_THREAD_POOL_SIZE, 4, MHD_OPTION_END); + if (d == NULL) + return 16; + c = curl_easy_init (); + curl_easy_setopt (c, CURLOPT_URL, "http://localhost:1081/hello_world"); + curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer); + curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc); + curl_easy_setopt (c, CURLOPT_READFUNCTION, &putBuffer); + curl_easy_setopt (c, CURLOPT_READDATA, &pos); + curl_easy_setopt (c, CURLOPT_UPLOAD, 1L); + curl_easy_setopt (c, CURLOPT_INFILESIZE, (long) PUT_SIZE); + curl_easy_setopt (c, CURLOPT_FAILONERROR, 1); + 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, 15L); + // 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); + MHD_stop_daemon (d); + return 32; + } + curl_easy_cleanup (c); + MHD_stop_daemon (d); + if (cbc.pos != strlen ("/hello_world")) + { + fprintf (stderr, "Got invalid response `%.*s'\n", cbc.pos, cbc.buf); + return 64; + } + if (0 != strncmp ("/hello_world", cbc.buf, strlen ("/hello_world"))) + return 128; + return 0; +} static int testExternalPut () @@ -394,6 +453,7 @@ main (int argc, char *const *argv) memset (put_buffer, 1, PUT_SIZE); errorCount += testInternalPut (); errorCount += testMultithreadedPut (); + errorCount += testMultithreadedPoolPut (); errorCount += testExternalPut (); free (put_buffer); if (errorCount != 0) diff --git a/src/testcurl/daemontest_post.c b/src/testcurl/daemontest_post.c @@ -231,6 +231,58 @@ testMultithreadedPost () return 0; } +static int +testMultithreadedPoolPost () +{ + struct MHD_Daemon *d; + CURL *c; + char buf[2048]; + struct CBC cbc; + CURLcode errornum; + + cbc.buf = buf; + cbc.size = 2048; + cbc.pos = 0; + d = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY | MHD_USE_DEBUG, + 1081, NULL, NULL, &ahc_echo, NULL, + MHD_OPTION_THREAD_POOL_SIZE, 4, MHD_OPTION_END); + if (d == NULL) + return 16; + c = curl_easy_init (); + curl_easy_setopt (c, CURLOPT_URL, "http://localhost:1081/hello_world"); + curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer); + curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc); + curl_easy_setopt (c, CURLOPT_POSTFIELDS, POST_DATA); + curl_easy_setopt (c, CURLOPT_POSTFIELDSIZE, strlen (POST_DATA)); + curl_easy_setopt (c, CURLOPT_POST, 1L); + curl_easy_setopt (c, CURLOPT_FAILONERROR, 1); + 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, 15L); + // 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); + MHD_stop_daemon (d); + return 32; + } + curl_easy_cleanup (c); + MHD_stop_daemon (d); + if (cbc.pos != strlen ("/hello_world")) + return 64; + if (0 != strncmp ("/hello_world", cbc.buf, strlen ("/hello_world"))) + return 128; + return 0; +} static int testExternalPost () @@ -369,6 +421,7 @@ main (int argc, char *const *argv) return 2; errorCount += testInternalPost (); errorCount += testMultithreadedPost (); + errorCount += testMultithreadedPoolPost (); errorCount += testExternalPost (); if (errorCount != 0) fprintf (stderr, "Error (code: %u)\n", errorCount); diff --git a/src/testcurl/daemontest_post_loop.c b/src/testcurl/daemontest_post_loop.c @@ -225,6 +225,70 @@ testMultithreadedPost () return 0; } +static int +testMultithreadedPoolPost () +{ + struct MHD_Daemon *d; + CURL *c; + char buf[2048]; + struct CBC cbc; + CURLcode errornum; + int i; + char url[1024]; + + cbc.buf = buf; + cbc.size = 2048; + d = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY | MHD_USE_DEBUG, + 1081, NULL, NULL, &ahc_echo, NULL, + MHD_OPTION_THREAD_POOL_SIZE, 4, MHD_OPTION_END); + if (d == NULL) + return 16; + for (i = 0; i < LOOPCOUNT; i++) + { + if (99 == i % 100) + fprintf (stderr, "."); + c = curl_easy_init (); + cbc.pos = 0; + buf[0] = '\0'; + sprintf (url, "http://localhost:1081/hw%d", i); + curl_easy_setopt (c, CURLOPT_URL, url); + curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer); + curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc); + curl_easy_setopt (c, CURLOPT_POSTFIELDS, POST_DATA); + curl_easy_setopt (c, CURLOPT_POSTFIELDSIZE, strlen (POST_DATA)); + curl_easy_setopt (c, CURLOPT_POST, 1L); + curl_easy_setopt (c, CURLOPT_FAILONERROR, 1); + 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, 15L); + // 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); + MHD_stop_daemon (d); + return 32; + } + curl_easy_cleanup (c); + if ((buf[0] != 'O') || (buf[1] != 'K')) + { + MHD_stop_daemon (d); + return 64; + } + } + MHD_stop_daemon (d); + if (LOOPCOUNT >= 99) + fprintf (stderr, "\n"); + return 0; +} static int testExternalPost () @@ -379,6 +443,7 @@ main (int argc, char *const *argv) return 2; errorCount += testInternalPost (); errorCount += testMultithreadedPost (); + errorCount += testMultithreadedPoolPost (); errorCount += testExternalPost (); if (errorCount != 0) fprintf (stderr, "Error (code: %u)\n", errorCount); diff --git a/src/testcurl/daemontest_postform.c b/src/testcurl/daemontest_postform.c @@ -251,6 +251,60 @@ testMultithreadedPost () return 0; } +static int +testMultithreadedPoolPost () +{ + struct MHD_Daemon *d; + CURL *c; + char buf[2048]; + struct CBC cbc; + CURLcode errornum; + struct curl_httppost *pd; + + cbc.buf = buf; + cbc.size = 2048; + cbc.pos = 0; + d = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY | MHD_USE_DEBUG, + 1081, NULL, NULL, &ahc_echo, NULL, + MHD_OPTION_THREAD_POOL_SIZE, 4, MHD_OPTION_END); + if (d == NULL) + return 16; + c = curl_easy_init (); + curl_easy_setopt (c, CURLOPT_URL, "http://localhost:1081/hello_world"); + curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer); + curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc); + pd = make_form (); + curl_easy_setopt (c, CURLOPT_HTTPPOST, pd); + curl_easy_setopt (c, CURLOPT_FAILONERROR, 1); + 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, 5L); + // 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); + curl_formfree (pd); + MHD_stop_daemon (d); + return 32; + } + curl_easy_cleanup (c); + curl_formfree (pd); + MHD_stop_daemon (d); + if (cbc.pos != strlen ("/hello_world")) + return 64; + if (0 != strncmp ("/hello_world", cbc.buf, strlen ("/hello_world"))) + return 128; + return 0; +} static int testExternalPost () @@ -394,6 +448,7 @@ main (int argc, char *const *argv) return 2; errorCount += testInternalPost (); errorCount += testMultithreadedPost (); + errorCount += testMultithreadedPoolPost (); errorCount += testExternalPost (); if (errorCount != 0) fprintf (stderr, "Error (code: %u)\n", errorCount); diff --git a/src/testcurl/daemontest_process_headers.c b/src/testcurl/daemontest_process_headers.c @@ -235,6 +235,55 @@ testMultithreadedGet () return 0; } +static int +testMultithreadedPoolGet () +{ + struct MHD_Daemon *d; + CURL *c; + char buf[2048]; + struct CBC cbc; + CURLcode errornum; + + cbc.buf = buf; + cbc.size = 2048; + cbc.pos = 0; + d = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY | MHD_USE_DEBUG, + 21080, NULL, NULL, &ahc_echo, "GET", + MHD_OPTION_THREAD_POOL_SIZE, 4, MHD_OPTION_END); + if (d == NULL) + return 16; + c = curl_easy_init (); + curl_easy_setopt (c, CURLOPT_URL, "http://localhost:21080/hello_world"); + curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer); + curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc); + curl_easy_setopt (c, CURLOPT_FAILONERROR, 1); + 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, 15L); + /* 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); + MHD_stop_daemon (d); + return 32; + } + curl_easy_cleanup (c); + MHD_stop_daemon (d); + if (cbc.pos != strlen ("/hello_world")) + return 64; + if (0 != strncmp ("/hello_world", cbc.buf, strlen ("/hello_world"))) + return 128; + return 0; +} static int testExternalGet () @@ -370,6 +419,7 @@ main (int argc, char *const *argv) return 2; errorCount += testInternalGet (); errorCount += testMultithreadedGet (); + errorCount += testMultithreadedPoolGet (); errorCount += testExternalGet (); if (errorCount != 0) fprintf (stderr, "Error (code: %u)\n", errorCount); diff --git a/src/testcurl/daemontest_put.c b/src/testcurl/daemontest_put.c @@ -223,6 +223,64 @@ testMultithreadedPut () return 0; } +static int +testMultithreadedPoolPut () +{ + struct MHD_Daemon *d; + CURL *c; + char buf[2048]; + struct CBC cbc; + unsigned int pos = 0; + int done_flag = 0; + CURLcode errornum; + + cbc.buf = buf; + cbc.size = 2048; + cbc.pos = 0; + d = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY | MHD_USE_DEBUG, + 1081, + NULL, NULL, &ahc_echo, &done_flag, + MHD_OPTION_THREAD_POOL_SIZE, 4, MHD_OPTION_END); + if (d == NULL) + return 16; + c = curl_easy_init (); + curl_easy_setopt (c, CURLOPT_URL, "http://localhost:1081/hello_world"); + curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer); + curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc); + curl_easy_setopt (c, CURLOPT_READFUNCTION, &putBuffer); + curl_easy_setopt (c, CURLOPT_READDATA, &pos); + curl_easy_setopt (c, CURLOPT_UPLOAD, 1L); + curl_easy_setopt (c, CURLOPT_INFILESIZE_LARGE, (curl_off_t) 8L); + curl_easy_setopt (c, CURLOPT_FAILONERROR, 1); + 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, 15L); + // 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); + MHD_stop_daemon (d); + return 32; + } + curl_easy_cleanup (c); + MHD_stop_daemon (d); + if (cbc.pos != strlen ("/hello_world")) + return 64; + if (0 != strncmp ("/hello_world", cbc.buf, strlen ("/hello_world"))) + return 128; + + return 0; +} + static int testExternalPut () @@ -365,6 +423,7 @@ main (int argc, char *const *argv) return 2; errorCount += testInternalPut (); errorCount += testMultithreadedPut (); + errorCount += testMultithreadedPoolPut (); errorCount += testExternalPut (); if (errorCount != 0) fprintf (stderr, "Error (code: %u)\n", errorCount); diff --git a/src/testcurl/daemontest_put_chunked.c b/src/testcurl/daemontest_put_chunked.c @@ -232,6 +232,64 @@ testMultithreadedPut () return 0; } +static int +testMultithreadedPoolPut () +{ + struct MHD_Daemon *d; + CURL *c; + char buf[2048]; + struct CBC cbc; + unsigned int pos = 0; + int done_flag = 0; + CURLcode errornum; + + cbc.buf = buf; + cbc.size = 2048; + cbc.pos = 0; + d = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY | MHD_USE_DEBUG, + 11081, + NULL, NULL, &ahc_echo, &done_flag, + MHD_OPTION_THREAD_POOL_SIZE, 4, MHD_OPTION_END); + if (d == NULL) + return 16; + c = curl_easy_init (); + curl_easy_setopt (c, CURLOPT_URL, "http://localhost:11081/hello_world"); + curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer); + curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc); + curl_easy_setopt (c, CURLOPT_READFUNCTION, &putBuffer); + curl_easy_setopt (c, CURLOPT_READDATA, &pos); + curl_easy_setopt (c, CURLOPT_UPLOAD, 1L); + /* + // by not giving the file size, we force chunking! + curl_easy_setopt (c, CURLOPT_INFILESIZE_LARGE, (curl_off_t) 8L); + */ + curl_easy_setopt (c, CURLOPT_FAILONERROR, 1); + curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L); + curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1); + curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 15L); + // 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); + MHD_stop_daemon (d); + return 32; + } + curl_easy_cleanup (c); + MHD_stop_daemon (d); + if (cbc.pos != strlen ("/hello_world")) + return 64; + if (0 != strncmp ("/hello_world", cbc.buf, strlen ("/hello_world"))) + return 128; + + return 0; +} + static int testExternalPut () @@ -373,6 +431,7 @@ main (int argc, char *const *argv) return 2; errorCount += testInternalPut (); errorCount += testMultithreadedPut (); + errorCount += testMultithreadedPoolPut (); errorCount += testExternalPut (); if (errorCount != 0) fprintf (stderr, "Error (code: %u)\n", errorCount);