libmicrohttpd

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

perf_get.c (19329B)


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