libmicrohttpd

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

perf_get.c (19496B)


      1 /*
      2      This file is part of libmicrohttpd
      3      Copyright (C) 2007, 2009, 2011 Christian Grothoff
      4      Copyright (C) 2014-2022 Evgeny Grin (Karlson2k)
      5 
      6      libmicrohttpd is free software; you can redistribute it and/or modify
      7      it under the terms of the GNU General Public License as published
      8      by the Free Software Foundation; either version 2, or (at your
      9      option) any later version.
     10 
     11      libmicrohttpd is distributed in the hope that it will be useful, but
     12      WITHOUT ANY WARRANTY; without even the implied warranty of
     13      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     14      General Public License for more details.
     15 
     16      You should have received a copy of the GNU General Public License
     17      along with libmicrohttpd; see the file COPYING.  If not, write to the
     18      Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
     19      Boston, MA 02110-1301, USA.
     20 */
     21 
     22 /**
     23  * @file perf_get.c
     24  * @brief benchmark simple GET operations (sequential access).
     25  *        Note that we run libcurl in the same process at the
     26  *        same time, so the execution time given is the combined
     27  *        time for both MHD and libcurl; it is quite possible
     28  *        that more time is spend with libcurl than with MHD,
     29  *        so the performance scores calculated with this code
     30  *        should NOT be used to compare with other HTTP servers
     31  *        (since MHD is actually better); only the relative
     32  *        scores between MHD versions are meaningful.
     33  *        Furthermore, this code ONLY tests MHD processing
     34  *        a single request at a time.  This is again
     35  *        not universally meaningful (i.e. when comparing
     36  *        multithreaded vs. single-threaded or select/poll).
     37  * @author Christian Grothoff
     38  * @author Karlson2k (Evgeny Grin)
     39  */
     40 
     41 #include "MHD_config.h"
     42 #include "platform.h"
     43 #include <curl/curl.h>
     44 #include <microhttpd.h>
     45 #include <stdlib.h>
     46 #include <string.h>
     47 #include <time.h>
     48 #include <errno.h>
     49 #include "mhd_has_in_name.h"
     50 
     51 /* Turn any MHD_PANIC() or failing mhd_assert() reached from this
     52    test into a marked, classifiable test error (TESTING.md, P5). */
     53 #include "mhd_panic_tripwire.h"
     54 
     55 #ifndef WINDOWS
     56 #include <unistd.h>
     57 #include <sys/socket.h>
     58 #endif
     59 
     60 #if defined(MHD_CPU_COUNT) && (MHD_CPU_COUNT + 0) < 2
     61 #undef MHD_CPU_COUNT
     62 #endif
     63 #if ! defined(MHD_CPU_COUNT)
     64 #define MHD_CPU_COUNT 2
     65 #endif
     66 
     67 /**
     68  * How many rounds of operations do we do for each
     69  * test?
     70  */
     71 #if MHD_CPU_COUNT > 8
     72 #ifndef _WIN32
     73 #define ROUNDS (1 + (30000 / 12) / MHD_CPU_COUNT)
     74 #else /* _WIN32 */
     75 #define ROUNDS (1 + (3000 / 12) / MHD_CPU_COUNT)
     76 #endif /* _WIN32 */
     77 #else
     78 #define ROUNDS 500
     79 #endif
     80 
     81 /**
     82  * Do we use HTTP 1.1?
     83  */
     84 static int oneone;
     85 
     86 /**
     87  * Response to return (re-used).
     88  */
     89 static struct MHD_Response *response;
     90 
     91 /**
     92  * Time this round was started.
     93  */
     94 static unsigned long long start_time;
     95 
     96 
     97 /**
     98  * Get the current timestamp
     99  *
    100  * @return current time in ms
    101  */
    102 static unsigned long long
    103 now (void)
    104 {
    105   struct timeval tv;
    106 
    107   gettimeofday (&tv, NULL);
    108   return (((unsigned long long) tv.tv_sec * 1000LL)
    109           + ((unsigned long long) tv.tv_usec / 1000LL));
    110 }
    111 
    112 
    113 /**
    114  * Start the timer.
    115  */
    116 static void
    117 start_timer (void)
    118 {
    119   start_time = now ();
    120 }
    121 
    122 
    123 /**
    124  * Stop the timer and report performance
    125  *
    126  * @param desc description of the threading mode we used
    127  */
    128 static void
    129 stop (const char *desc)
    130 {
    131   double rps = ((double) (ROUNDS * 1000)) / ((double) (now () - start_time));
    132 
    133   fprintf (stderr,
    134            "Sequential GETs using %s: %f %s\n",
    135            desc,
    136            rps,
    137            "requests/s");
    138 }
    139 
    140 
    141 struct CBC
    142 {
    143   char *buf;
    144   size_t pos;
    145   size_t size;
    146 };
    147 
    148 
    149 static size_t
    150 copyBuffer (void *ptr,
    151             size_t size, size_t nmemb,
    152             void *ctx)
    153 {
    154   struct CBC *cbc = ctx;
    155 
    156   if (cbc->pos + size * nmemb > cbc->size)
    157     return 0;                   /* overflow */
    158   memcpy (&cbc->buf[cbc->pos], ptr, size * nmemb);
    159   cbc->pos += size * nmemb;
    160   return size * nmemb;
    161 }
    162 
    163 
    164 static enum MHD_Result
    165 ahc_echo (void *cls,
    166           struct MHD_Connection *connection,
    167           const char *url,
    168           const char *method,
    169           const char *version,
    170           const char *upload_data, size_t *upload_data_size,
    171           void **req_cls)
    172 {
    173   static int ptr;
    174   enum MHD_Result ret;
    175   (void) cls;
    176   (void) url; (void) version;                      /* Unused. Silent compiler warning. */
    177   (void) upload_data; (void) upload_data_size;     /* Unused. Silent compiler warning. */
    178 
    179   if (0 != strcmp (MHD_HTTP_METHOD_GET, method))
    180     return MHD_NO;              /* unexpected method */
    181   if (&ptr != *req_cls)
    182   {
    183     *req_cls = &ptr;
    184     return MHD_YES;
    185   }
    186   *req_cls = NULL;
    187   ret = MHD_queue_response (connection, MHD_HTTP_OK, response);
    188   if (ret == MHD_NO)
    189     abort ();
    190   return ret;
    191 }
    192 
    193 
    194 static unsigned int
    195 testInternalGet (uint16_t port, uint32_t poll_flag)
    196 {
    197   struct MHD_Daemon *d;
    198   CURL *c;
    199   char buf[2048];
    200   struct CBC cbc;
    201   CURLcode errornum;
    202   unsigned int i;
    203   char url[64];
    204 
    205   if (MHD_NO != MHD_is_feature_supported (MHD_FEATURE_AUTODETECT_BIND_PORT))
    206     port = 0;
    207 
    208   cbc.buf = buf;
    209   cbc.size = 2048;
    210   d = MHD_start_daemon (MHD_USE_INTERNAL_POLLING_THREAD | MHD_USE_ERROR_LOG
    211                         | (enum MHD_FLAG) poll_flag,
    212                         port, NULL, NULL, &ahc_echo, NULL, MHD_OPTION_END);
    213   if (d == NULL)
    214     return 1;
    215   if (0 == port)
    216   {
    217     const union MHD_DaemonInfo *dinfo;
    218     dinfo = MHD_get_daemon_info (d, MHD_DAEMON_INFO_BIND_PORT);
    219     if ((NULL == dinfo) || (0 == dinfo->port) )
    220     {
    221       MHD_stop_daemon (d); return 32;
    222     }
    223     port = dinfo->port;
    224   }
    225   snprintf (url,
    226             sizeof (url),
    227             "http://127.0.0.1:%u/hello_world",
    228             (unsigned int) port);
    229   start_timer ();
    230   for (i = 0; i < ROUNDS; i++)
    231   {
    232     cbc.pos = 0;
    233     c = curl_easy_init ();
    234     curl_easy_setopt (c, CURLOPT_URL, url);
    235     curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
    236     curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
    237     curl_easy_setopt (c, CURLOPT_FAILONERROR, 1L);
    238     curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L);
    239     curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L);
    240     if (oneone)
    241       curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
    242     else
    243       curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
    244     /* NOTE: use of CONNECTTIMEOUT without also
    245  setting NOSIGNAL results in really weird
    246  crashes on my system!*/
    247     curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1L);
    248     if (CURLE_OK != (errornum = curl_easy_perform (c)))
    249     {
    250       fprintf (stderr,
    251                "curl_easy_perform failed: `%s'\n",
    252                curl_easy_strerror (errornum));
    253       curl_easy_cleanup (c);
    254       MHD_stop_daemon (d);
    255       return 2;
    256     }
    257     curl_easy_cleanup (c);
    258   }
    259   stop (poll_flag == MHD_USE_AUTO ? "internal thread with 'auto'" :
    260         poll_flag == MHD_USE_POLL ? "internal thread with poll()" :
    261         poll_flag == MHD_USE_EPOLL ? "internal thread with epoll" :
    262         "internal thread with select()");
    263   MHD_stop_daemon (d);
    264   if (cbc.pos != strlen ("/hello_world"))
    265     return 4;
    266   if (0 != strncmp ("/hello_world", cbc.buf, strlen ("/hello_world")))
    267     return 8;
    268   return 0;
    269 }
    270 
    271 
    272 static unsigned int
    273 testMultithreadedGet (uint16_t port, uint32_t poll_flag)
    274 {
    275   struct MHD_Daemon *d;
    276   CURL *c;
    277   char buf[2048];
    278   struct CBC cbc;
    279   CURLcode errornum;
    280   unsigned int i;
    281   char url[64];
    282 
    283   if (MHD_NO != MHD_is_feature_supported (MHD_FEATURE_AUTODETECT_BIND_PORT))
    284     port = 0;
    285 
    286   cbc.buf = buf;
    287   cbc.size = 2048;
    288   d = MHD_start_daemon (MHD_USE_THREAD_PER_CONNECTION
    289                         | MHD_USE_INTERNAL_POLLING_THREAD | MHD_USE_ERROR_LOG
    290                         | (enum MHD_FLAG) poll_flag,
    291                         port, NULL, NULL, &ahc_echo, NULL, MHD_OPTION_END);
    292   if (d == NULL)
    293     return 16;
    294   if (0 == port)
    295   {
    296     const union MHD_DaemonInfo *dinfo;
    297     dinfo = MHD_get_daemon_info (d, MHD_DAEMON_INFO_BIND_PORT);
    298     if ((NULL == dinfo) || (0 == dinfo->port) )
    299     {
    300       MHD_stop_daemon (d); return 32;
    301     }
    302     port = dinfo->port;
    303   }
    304   snprintf (url,
    305             sizeof (url),
    306             "http://127.0.0.1:%u/hello_world",
    307             (unsigned int) port);
    308   start_timer ();
    309   for (i = 0; i < ROUNDS; i++)
    310   {
    311     cbc.pos = 0;
    312     c = curl_easy_init ();
    313     curl_easy_setopt (c, CURLOPT_URL, url);
    314     curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
    315     curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
    316     curl_easy_setopt (c, CURLOPT_FAILONERROR, 1L);
    317     curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L);
    318     if (oneone)
    319       curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
    320     else
    321       curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
    322     curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L);
    323     /* NOTE: use of CONNECTTIMEOUT without also
    324  setting NOSIGNAL results in really weird
    325  crashes on my system! */
    326     curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1L);
    327     if (CURLE_OK != (errornum = curl_easy_perform (c)))
    328     {
    329       fprintf (stderr,
    330                "curl_easy_perform failed: `%s'\n",
    331                curl_easy_strerror (errornum));
    332       curl_easy_cleanup (c);
    333       MHD_stop_daemon (d);
    334       return 32;
    335     }
    336     curl_easy_cleanup (c);
    337   }
    338   stop ((poll_flag & MHD_USE_AUTO) ?
    339         "internal thread with 'auto' and thread per connection" :
    340         (poll_flag & MHD_USE_POLL) ?
    341         "internal thread with poll() and thread per connection" :
    342         (poll_flag & MHD_USE_EPOLL) ?
    343         "internal thread with epoll and thread per connection" :
    344         "internal thread with select() and thread per connection");
    345   MHD_stop_daemon (d);
    346   if (cbc.pos != strlen ("/hello_world"))
    347     return 64;
    348   if (0 != strncmp ("/hello_world", cbc.buf, strlen ("/hello_world")))
    349     return 128;
    350   return 0;
    351 }
    352 
    353 
    354 static unsigned int
    355 testMultithreadedPoolGet (uint16_t port, uint32_t poll_flag)
    356 {
    357   struct MHD_Daemon *d;
    358   CURL *c;
    359   char buf[2048];
    360   struct CBC cbc;
    361   CURLcode errornum;
    362   unsigned int i;
    363   char url[64];
    364 
    365   if (MHD_NO != MHD_is_feature_supported (MHD_FEATURE_AUTODETECT_BIND_PORT))
    366     port = 0;
    367 
    368   cbc.buf = buf;
    369   cbc.size = 2048;
    370   d = MHD_start_daemon (MHD_USE_INTERNAL_POLLING_THREAD | MHD_USE_ERROR_LOG
    371                         | (enum MHD_FLAG) poll_flag,
    372                         port, NULL, NULL, &ahc_echo, NULL,
    373                         MHD_OPTION_THREAD_POOL_SIZE, MHD_CPU_COUNT,
    374                         MHD_OPTION_END);
    375   if (d == NULL)
    376     return 16;
    377   if (0 == port)
    378   {
    379     const union MHD_DaemonInfo *dinfo;
    380     dinfo = MHD_get_daemon_info (d, MHD_DAEMON_INFO_BIND_PORT);
    381     if ((NULL == dinfo) || (0 == dinfo->port) )
    382     {
    383       MHD_stop_daemon (d); return 32;
    384     }
    385     port = dinfo->port;
    386   }
    387   snprintf (url,
    388             sizeof (url),
    389             "http://127.0.0.1:%u/hello_world",
    390             (unsigned int) port);
    391   start_timer ();
    392   for (i = 0; i < ROUNDS; i++)
    393   {
    394     cbc.pos = 0;
    395     c = curl_easy_init ();
    396     curl_easy_setopt (c, CURLOPT_URL, url);
    397     curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
    398     curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
    399     curl_easy_setopt (c, CURLOPT_FAILONERROR, 1L);
    400     curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L);
    401     if (oneone)
    402       curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
    403     else
    404       curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
    405     curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L);
    406     /* NOTE: use of CONNECTTIMEOUT without also
    407  setting NOSIGNAL results in really weird
    408  crashes on my system!*/
    409     curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1L);
    410     if (CURLE_OK != (errornum = curl_easy_perform (c)))
    411     {
    412       fprintf (stderr,
    413                "curl_easy_perform failed: `%s'\n",
    414                curl_easy_strerror (errornum));
    415       curl_easy_cleanup (c);
    416       MHD_stop_daemon (d);
    417       return 32;
    418     }
    419     curl_easy_cleanup (c);
    420   }
    421   stop (0 != (poll_flag & MHD_USE_AUTO) ? "internal thread pool with 'auto'" :
    422         0 != (poll_flag & MHD_USE_POLL) ? "internal thread pool with poll()" :
    423         0 != (poll_flag & MHD_USE_EPOLL) ? "internal thread pool with epoll" :
    424         "internal thread pool with select()");
    425   MHD_stop_daemon (d);
    426   if (cbc.pos != strlen ("/hello_world"))
    427     return 64;
    428   if (0 != strncmp ("/hello_world", cbc.buf, strlen ("/hello_world")))
    429     return 128;
    430   return 0;
    431 }
    432 
    433 
    434 static unsigned int
    435 testExternalGet (uint16_t port)
    436 {
    437   struct MHD_Daemon *d;
    438   CURL *c;
    439   char buf[2048];
    440   struct CBC cbc;
    441   CURLM *multi;
    442   CURLMcode mret;
    443   fd_set rs;
    444   fd_set ws;
    445   fd_set es;
    446   MHD_socket maxsock;
    447 #ifdef MHD_WINSOCK_SOCKETS
    448   int maxposixs; /* Max socket number unused on W32 */
    449 #else  /* MHD_POSIX_SOCKETS */
    450 #define maxposixs maxsock
    451 #endif /* MHD_POSIX_SOCKETS */
    452   int running;
    453   struct CURLMsg *msg;
    454   time_t start;
    455   struct timeval tv;
    456   unsigned int i;
    457   char url[64];
    458 
    459   if (MHD_NO != MHD_is_feature_supported (MHD_FEATURE_AUTODETECT_BIND_PORT))
    460     port = 0;
    461 
    462   multi = NULL;
    463   cbc.buf = buf;
    464   cbc.size = 2048;
    465   d = MHD_start_daemon (MHD_USE_ERROR_LOG | MHD_USE_NO_THREAD_SAFETY,
    466                         port, NULL, NULL,
    467                         &ahc_echo, NULL,
    468                         MHD_OPTION_APP_FD_SETSIZE, (int) FD_SETSIZE,
    469                         MHD_OPTION_END);
    470   if (NULL == d)
    471     return 256;
    472   if (0 == port)
    473   {
    474     const union MHD_DaemonInfo *dinfo;
    475     dinfo = MHD_get_daemon_info (d, MHD_DAEMON_INFO_BIND_PORT);
    476     if ((NULL == dinfo) || (0 == dinfo->port) )
    477     {
    478       MHD_stop_daemon (d); return 32;
    479     }
    480     port = dinfo->port;
    481   }
    482   snprintf (url,
    483             sizeof (url),
    484             "http://127.0.0.1:%u/hello_world",
    485             (unsigned int) port);
    486   start_timer ();
    487   multi = curl_multi_init ();
    488   if (multi == NULL)
    489   {
    490     MHD_stop_daemon (d);
    491     return 512;
    492   }
    493   for (i = 0; i < ROUNDS; i++)
    494   {
    495     cbc.pos = 0;
    496     c = curl_easy_init ();
    497     curl_easy_setopt (c, CURLOPT_URL, url);
    498     curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
    499     curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
    500     curl_easy_setopt (c, CURLOPT_FAILONERROR, 1L);
    501     if (oneone)
    502       curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
    503     else
    504       curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
    505     curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L);
    506     curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L);
    507     /* NOTE: use of CONNECTTIMEOUT without also
    508  setting NOSIGNAL results in really weird
    509  crashes on my system! */
    510     curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1L);
    511     mret = curl_multi_add_handle (multi, c);
    512     if (mret != CURLM_OK)
    513     {
    514       curl_multi_cleanup (multi);
    515       curl_easy_cleanup (c);
    516       MHD_stop_daemon (d);
    517       return 1024;
    518     }
    519     start = time (NULL);
    520     while ((time (NULL) - start < 5) && (c != NULL))
    521     {
    522       maxsock = MHD_INVALID_SOCKET;
    523       maxposixs = -1;
    524       FD_ZERO (&rs);
    525       FD_ZERO (&ws);
    526       FD_ZERO (&es);
    527       curl_multi_perform (multi, &running);
    528       mret = curl_multi_fdset (multi, &rs, &ws, &es, &maxposixs);
    529       if (mret != CURLM_OK)
    530       {
    531         curl_multi_remove_handle (multi, c);
    532         curl_multi_cleanup (multi);
    533         curl_easy_cleanup (c);
    534         MHD_stop_daemon (d);
    535         return 2048;
    536       }
    537       if (MHD_YES != MHD_get_fdset (d, &rs, &ws, &es, &maxsock))
    538       {
    539         curl_multi_remove_handle (multi, c);
    540         curl_multi_cleanup (multi);
    541         curl_easy_cleanup (c);
    542         MHD_stop_daemon (d);
    543         return 4096;
    544       }
    545       tv.tv_sec = 0;
    546       tv.tv_usec = 1000;
    547       if (-1 == select (maxposixs + 1, &rs, &ws, &es, &tv))
    548       {
    549   #ifdef MHD_POSIX_SOCKETS
    550         if (EINTR != errno)
    551         {
    552           fprintf (stderr, "Unexpected select() error: %d. Line: %d\n",
    553                    (int) errno, __LINE__);
    554           fflush (stderr);
    555           exit (99);
    556         }
    557   #else
    558         if ((WSAEINVAL != WSAGetLastError ()) ||
    559             (0 != rs.fd_count) || (0 != ws.fd_count) || (0 != es.fd_count) )
    560         {
    561           fprintf (stderr, "Unexpected select() error: %d. Line: %d\n",
    562                    (int) WSAGetLastError (), __LINE__);
    563           fflush (stderr);
    564           exit (99);
    565         }
    566         Sleep (1);
    567   #endif
    568       }
    569       curl_multi_perform (multi, &running);
    570       if (0 == running)
    571       {
    572         int pending;
    573         int curl_fine = 0;
    574         while (NULL != (msg = curl_multi_info_read (multi, &pending)))
    575         {
    576           if (msg->msg == CURLMSG_DONE)
    577           {
    578             if (msg->data.result == CURLE_OK)
    579               curl_fine = 1;
    580             else
    581             {
    582               fprintf (stderr,
    583                        "%s failed at %s:%d: `%s'\n",
    584                        "curl_multi_perform",
    585                        __FILE__,
    586                        __LINE__, curl_easy_strerror (msg->data.result));
    587               abort ();
    588             }
    589           }
    590         }
    591         if (! curl_fine)
    592         {
    593           fprintf (stderr, "libcurl haven't returned OK code\n");
    594           abort ();
    595         }
    596         curl_multi_remove_handle (multi, c);
    597         curl_easy_cleanup (c);
    598         c = NULL;
    599         break;
    600       }
    601       /* two possibilities here; as select sets are
    602          tiny, this makes virtually no difference
    603          in actual runtime right now, even though the
    604          number of select calls is virtually cut in half
    605          (and 'select' is the most expensive of our system
    606          calls according to 'strace') */
    607       if (0)
    608         MHD_run (d);
    609       else
    610         MHD_run_from_select (d, &rs, &ws, &es);
    611     }
    612     if (NULL != c)
    613     {
    614       curl_multi_remove_handle (multi, c);
    615       curl_easy_cleanup (c);
    616       fprintf (stderr, "Timeout!?\n");
    617     }
    618   }
    619   stop ("external select");
    620   if (multi != NULL)
    621   {
    622     curl_multi_cleanup (multi);
    623   }
    624   MHD_stop_daemon (d);
    625   if (cbc.pos != strlen ("/hello_world"))
    626     return 8192;
    627   if (0 != strncmp ("/hello_world", cbc.buf, strlen ("/hello_world")))
    628     return 16384;
    629   return 0;
    630 }
    631 
    632 
    633 int
    634 main (int argc, char *const *argv)
    635 {
    636   unsigned int errorCount = 0;
    637   uint16_t port = 1130;
    638   (void) argc;   /* Unused. Silent compiler warning. */
    639 
    640   if ((NULL == argv) || (0 == argv[0]))
    641     return 99;
    642   oneone = has_in_name (argv[0], "11");
    643   if (oneone)
    644     port += 15;
    645   if (0 != curl_global_init (CURL_GLOBAL_WIN32))
    646     return 2;
    647   response = MHD_create_response_from_buffer_copy (strlen ("/hello_world"),
    648                                                    "/hello_world");
    649   errorCount += testExternalGet (port++);
    650   if (MHD_YES == MHD_is_feature_supported (MHD_FEATURE_THREADS))
    651   {
    652     errorCount += testInternalGet (port++, MHD_USE_AUTO);
    653     errorCount += testMultithreadedGet (port++, MHD_USE_AUTO);
    654     errorCount += testMultithreadedPoolGet (port++, MHD_USE_AUTO);
    655     errorCount += testInternalGet (port++, 0);
    656     errorCount += testMultithreadedGet (port++, 0);
    657     errorCount += testMultithreadedPoolGet (port++, 0);
    658     if (MHD_YES == MHD_is_feature_supported (MHD_FEATURE_POLL))
    659     {
    660       errorCount += testInternalGet (port++, MHD_USE_POLL);
    661       errorCount += testMultithreadedGet (port++, MHD_USE_POLL);
    662       errorCount += testMultithreadedPoolGet (port++, MHD_USE_POLL);
    663     }
    664     if (MHD_YES == MHD_is_feature_supported (MHD_FEATURE_EPOLL))
    665     {
    666       errorCount += testInternalGet (port++, MHD_USE_EPOLL);
    667       errorCount += testMultithreadedPoolGet (port++, MHD_USE_EPOLL);
    668     }
    669   }
    670   MHD_destroy_response (response);
    671   if (errorCount != 0)
    672     fprintf (stderr, "Error (code: %u)\n", errorCount);
    673   curl_global_cleanup ();
    674   return errorCount != 0;       /* 0 == pass */
    675 }