libmicrohttpd

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

test_get_empty.c (27820B)


      1 /*
      2      This file is part of libmicrohttpd
      3      Copyright (C) 2007, 2009, 2011, 2019 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  * @file test_get_empty.c
     23  * @brief  Testcase for libmicrohttpd GET operations returning an empty body
     24  * @author Christian Grothoff
     25  * @author Karlson2k (Evgeny Grin)
     26  */
     27 #include "MHD_config.h"
     28 #include "platform.h"
     29 #include <curl/curl.h>
     30 #include <microhttpd.h>
     31 #include <stdlib.h>
     32 #include <string.h>
     33 #include <time.h>
     34 #include <errno.h>
     35 #include "mhd_has_in_name.h"
     36 #include "mhd_has_param.h"
     37 #include "mhd_sockets.h" /* only macros used */
     38 
     39 
     40 #define EXPECTED_URI_PATH "/hello_world?a=%26&b=c"
     41 
     42 #ifdef _WIN32
     43 #ifndef WIN32_LEAN_AND_MEAN
     44 #define WIN32_LEAN_AND_MEAN 1
     45 #endif /* !WIN32_LEAN_AND_MEAN */
     46 #include <windows.h>
     47 #endif
     48 
     49 #ifndef WINDOWS
     50 #include <unistd.h>
     51 #include <sys/socket.h>
     52 #endif
     53 
     54 #if defined(MHD_CPU_COUNT) && (MHD_CPU_COUNT + 0) < 2
     55 #undef MHD_CPU_COUNT
     56 #endif
     57 #if ! defined(MHD_CPU_COUNT)
     58 #define MHD_CPU_COUNT 2
     59 #endif
     60 
     61 static int oneone;
     62 static uint16_t global_port;
     63 
     64 struct CBC
     65 {
     66   char *buf;
     67   size_t pos;
     68   size_t size;
     69 };
     70 
     71 
     72 static size_t
     73 copyBuffer (void *ptr, size_t size, size_t nmemb, void *ctx)
     74 {
     75   struct CBC *cbc = ctx;
     76 
     77   if (cbc->pos + size * nmemb > cbc->size)
     78     return 0;                   /* overflow */
     79   memcpy (&cbc->buf[cbc->pos], ptr, size * nmemb);
     80   cbc->pos += size * nmemb;
     81   return size * nmemb;
     82 }
     83 
     84 
     85 static void *
     86 log_cb (void *cls,
     87         const char *uri,
     88         struct MHD_Connection *con)
     89 {
     90   (void) cls;
     91   (void) con;
     92   if (0 != strcmp (uri,
     93                    EXPECTED_URI_PATH))
     94   {
     95     fprintf (stderr,
     96              "Wrong URI: `%s'\n",
     97              uri);
     98     _exit (22);
     99   }
    100   return NULL;
    101 }
    102 
    103 
    104 static int
    105 ahc_echo (void *cls,
    106           struct MHD_Connection *connection,
    107           const char *url,
    108           const char *method,
    109           const char *version,
    110           const char *upload_data, size_t *upload_data_size,
    111           void **req_cls)
    112 {
    113   static int ptr;
    114   const char *me = cls;
    115   struct MHD_Response *response;
    116   int ret;
    117   (void) version;
    118   (void) upload_data;
    119   (void) upload_data_size;       /* Unused. Silence compiler warning. */
    120 
    121   if (0 != strcmp (me, method))
    122     return MHD_NO;              /* unexpected method */
    123   if (&ptr != *req_cls)
    124   {
    125     *req_cls = &ptr;
    126     return MHD_YES;
    127   }
    128   *req_cls = NULL;
    129   response = MHD_create_response_empty (MHD_RF_NONE);
    130   ret = MHD_queue_response (connection,
    131                             MHD_HTTP_NO_CONTENT,
    132                             response);
    133   MHD_destroy_response (response);
    134   if (ret == MHD_NO)
    135   {
    136     fprintf (stderr, "Failed to queue response.\n");
    137     _exit (19);
    138   }
    139   return ret;
    140 }
    141 
    142 
    143 static unsigned int
    144 testInternalGet (uint32_t poll_flag)
    145 {
    146   struct MHD_Daemon *d;
    147   CURL *c;
    148   char buf[2048];
    149   struct CBC cbc;
    150   CURLcode errornum;
    151 
    152   if ( (0 == global_port) &&
    153        (MHD_NO == MHD_is_feature_supported (MHD_FEATURE_AUTODETECT_BIND_PORT)) )
    154   {
    155     global_port = 1220;
    156     if (oneone)
    157       global_port += 20;
    158   }
    159 
    160   cbc.buf = buf;
    161   cbc.size = 2048;
    162   cbc.pos = 0;
    163   d = MHD_start_daemon (MHD_USE_INTERNAL_POLLING_THREAD | MHD_USE_ERROR_LOG
    164                         | (enum MHD_FLAG) poll_flag,
    165                         global_port, NULL, NULL,
    166                         &ahc_echo, "GET",
    167                         MHD_OPTION_URI_LOG_CALLBACK, &log_cb, NULL,
    168                         MHD_OPTION_END);
    169   if (d == NULL)
    170     return 1;
    171   if (0 == global_port)
    172   {
    173     const union MHD_DaemonInfo *dinfo;
    174     dinfo = MHD_get_daemon_info (d, MHD_DAEMON_INFO_BIND_PORT);
    175     if ((NULL == dinfo) || (0 == dinfo->port) )
    176     {
    177       MHD_stop_daemon (d); return 32;
    178     }
    179     global_port = dinfo->port;
    180   }
    181   c = curl_easy_init ();
    182   curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1" EXPECTED_URI_PATH);
    183   curl_easy_setopt (c, CURLOPT_PORT, (long) global_port);
    184   curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
    185   curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
    186   curl_easy_setopt (c, CURLOPT_FAILONERROR, 1L);
    187   curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L);
    188   curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L);
    189   if (oneone)
    190     curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
    191   else
    192     curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
    193   /* NOTE: use of CONNECTTIMEOUT without also
    194      setting NOSIGNAL results in really weird
    195      crashes on my system!*/
    196   curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1L);
    197   if (CURLE_OK != (errornum = curl_easy_perform (c)))
    198   {
    199     fprintf (stderr,
    200              "curl_easy_perform failed: `%s'\n",
    201              curl_easy_strerror (errornum));
    202     curl_easy_cleanup (c);
    203     MHD_stop_daemon (d);
    204     return 2;
    205   }
    206   curl_easy_cleanup (c);
    207   MHD_stop_daemon (d);
    208   if (cbc.pos != 0)
    209     return 4;
    210   return 0;
    211 }
    212 
    213 
    214 static unsigned int
    215 testMultithreadedGet (uint32_t poll_flag)
    216 {
    217   struct MHD_Daemon *d;
    218   CURL *c;
    219   char buf[2048];
    220   struct CBC cbc;
    221   CURLcode errornum;
    222 
    223   if ( (0 == global_port) &&
    224        (MHD_NO == MHD_is_feature_supported (MHD_FEATURE_AUTODETECT_BIND_PORT)) )
    225   {
    226     global_port = 1221;
    227     if (oneone)
    228       global_port += 20;
    229   }
    230 
    231   cbc.buf = buf;
    232   cbc.size = 2048;
    233   cbc.pos = 0;
    234   d = MHD_start_daemon (MHD_USE_THREAD_PER_CONNECTION
    235                         | MHD_USE_INTERNAL_POLLING_THREAD | MHD_USE_ERROR_LOG
    236                         | (enum MHD_FLAG) poll_flag,
    237                         global_port, NULL, NULL,
    238                         &ahc_echo, "GET",
    239                         MHD_OPTION_URI_LOG_CALLBACK, &log_cb, NULL,
    240                         MHD_OPTION_END);
    241   if (d == NULL)
    242     return 16;
    243   if (0 == global_port)
    244   {
    245     const union MHD_DaemonInfo *dinfo;
    246     dinfo = MHD_get_daemon_info (d, MHD_DAEMON_INFO_BIND_PORT);
    247     if ((NULL == dinfo) || (0 == dinfo->port) )
    248     {
    249       MHD_stop_daemon (d); return 32;
    250     }
    251     global_port = dinfo->port;
    252   }
    253   c = curl_easy_init ();
    254   curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1" EXPECTED_URI_PATH);
    255   curl_easy_setopt (c, CURLOPT_PORT, (long) global_port);
    256   curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
    257   curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
    258   curl_easy_setopt (c, CURLOPT_FAILONERROR, 1L);
    259   curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L);
    260   if (oneone)
    261     curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
    262   else
    263     curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
    264   curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L);
    265   /* NOTE: use of CONNECTTIMEOUT without also
    266      setting NOSIGNAL results in really weird
    267      crashes on my system! */
    268   curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1L);
    269   if (CURLE_OK != (errornum = curl_easy_perform (c)))
    270   {
    271     fprintf (stderr,
    272              "curl_easy_perform failed: `%s'\n",
    273              curl_easy_strerror (errornum));
    274     curl_easy_cleanup (c);
    275     MHD_stop_daemon (d);
    276     return 32;
    277   }
    278   curl_easy_cleanup (c);
    279   MHD_stop_daemon (d);
    280   if (cbc.pos != 0)
    281     return 64;
    282   return 0;
    283 }
    284 
    285 
    286 static unsigned int
    287 testMultithreadedPoolGet (uint32_t poll_flag)
    288 {
    289   struct MHD_Daemon *d;
    290   CURL *c;
    291   char buf[2048];
    292   struct CBC cbc;
    293   CURLcode errornum;
    294 
    295   if ( (0 == global_port) &&
    296        (MHD_NO == MHD_is_feature_supported (MHD_FEATURE_AUTODETECT_BIND_PORT)) )
    297   {
    298     global_port = 1222;
    299     if (oneone)
    300       global_port += 20;
    301   }
    302 
    303   cbc.buf = buf;
    304   cbc.size = 2048;
    305   cbc.pos = 0;
    306   d = MHD_start_daemon (MHD_USE_INTERNAL_POLLING_THREAD | MHD_USE_ERROR_LOG
    307                         | (enum MHD_FLAG) poll_flag,
    308                         global_port, NULL, NULL,
    309                         &ahc_echo, "GET",
    310                         MHD_OPTION_THREAD_POOL_SIZE, MHD_CPU_COUNT,
    311                         MHD_OPTION_URI_LOG_CALLBACK, &log_cb, NULL,
    312                         MHD_OPTION_END);
    313   if (d == NULL)
    314     return 16;
    315   if (0 == global_port)
    316   {
    317     const union MHD_DaemonInfo *dinfo;
    318     dinfo = MHD_get_daemon_info (d, MHD_DAEMON_INFO_BIND_PORT);
    319     if ((NULL == dinfo) || (0 == dinfo->port) )
    320     {
    321       MHD_stop_daemon (d); return 32;
    322     }
    323     global_port = dinfo->port;
    324   }
    325   c = curl_easy_init ();
    326   curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1" EXPECTED_URI_PATH);
    327   curl_easy_setopt (c, CURLOPT_PORT, (long) global_port);
    328   curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
    329   curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
    330   curl_easy_setopt (c, CURLOPT_FAILONERROR, 1L);
    331   curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L);
    332   if (oneone)
    333     curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
    334   else
    335     curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
    336   curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L);
    337   /* NOTE: use of CONNECTTIMEOUT without also
    338      setting NOSIGNAL results in really weird
    339      crashes on my system!*/
    340   curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1L);
    341   if (CURLE_OK != (errornum = curl_easy_perform (c)))
    342   {
    343     fprintf (stderr,
    344              "curl_easy_perform failed: `%s'\n",
    345              curl_easy_strerror (errornum));
    346     curl_easy_cleanup (c);
    347     MHD_stop_daemon (d);
    348     return 32;
    349   }
    350   curl_easy_cleanup (c);
    351   MHD_stop_daemon (d);
    352   if (cbc.pos != 0)
    353     return 64;
    354   return 0;
    355 }
    356 
    357 
    358 static unsigned int
    359 testExternalGet ()
    360 {
    361   struct MHD_Daemon *d;
    362   CURL *c;
    363   char buf[2048];
    364   struct CBC cbc;
    365   CURLM *multi;
    366   CURLMcode mret;
    367   fd_set rs;
    368   fd_set ws;
    369   fd_set es;
    370   MHD_socket maxsock;
    371   int maxposixs;
    372   int running;
    373   struct CURLMsg *msg;
    374   time_t start;
    375   struct timeval tv;
    376 
    377   if ( (0 == global_port) &&
    378        (MHD_NO == MHD_is_feature_supported (MHD_FEATURE_AUTODETECT_BIND_PORT)) )
    379   {
    380     global_port = 1223;
    381     if (oneone)
    382       global_port += 20;
    383   }
    384 
    385   multi = NULL;
    386   cbc.buf = buf;
    387   cbc.size = 2048;
    388   cbc.pos = 0;
    389   d = MHD_start_daemon (MHD_USE_ERROR_LOG,
    390                         global_port, NULL, NULL,
    391                         &ahc_echo, "GET",
    392                         MHD_OPTION_URI_LOG_CALLBACK, &log_cb, NULL,
    393                         MHD_OPTION_APP_FD_SETSIZE, (int) FD_SETSIZE,
    394                         MHD_OPTION_END);
    395   if (d == NULL)
    396     return 256;
    397   if (0 == global_port)
    398   {
    399     const union MHD_DaemonInfo *dinfo;
    400     dinfo = MHD_get_daemon_info (d, MHD_DAEMON_INFO_BIND_PORT);
    401     if ((NULL == dinfo) || (0 == dinfo->port) )
    402     {
    403       MHD_stop_daemon (d); return 32;
    404     }
    405     global_port = dinfo->port;
    406   }
    407   c = curl_easy_init ();
    408   curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1" EXPECTED_URI_PATH);
    409   curl_easy_setopt (c, CURLOPT_PORT, (long) global_port);
    410   curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
    411   curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
    412   curl_easy_setopt (c, CURLOPT_FAILONERROR, 1L);
    413   if (oneone)
    414     curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
    415   else
    416     curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
    417   curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L);
    418   curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L);
    419   /* NOTE: use of CONNECTTIMEOUT without also
    420      setting NOSIGNAL results in really weird
    421      crashes on my system! */
    422   curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1L);
    423 
    424 
    425   multi = curl_multi_init ();
    426   if (multi == NULL)
    427   {
    428     curl_easy_cleanup (c);
    429     MHD_stop_daemon (d);
    430     return 512;
    431   }
    432   mret = curl_multi_add_handle (multi, c);
    433   if (mret != CURLM_OK)
    434   {
    435     curl_multi_cleanup (multi);
    436     curl_easy_cleanup (c);
    437     MHD_stop_daemon (d);
    438     return 1024;
    439   }
    440   start = time (NULL);
    441   while ((time (NULL) - start < 5) && (multi != NULL))
    442   {
    443     maxsock = MHD_INVALID_SOCKET;
    444     maxposixs = -1;
    445     FD_ZERO (&rs);
    446     FD_ZERO (&ws);
    447     FD_ZERO (&es);
    448     curl_multi_perform (multi, &running);
    449     mret = curl_multi_fdset (multi, &rs, &ws, &es, &maxposixs);
    450     if (mret != CURLM_OK)
    451     {
    452       curl_multi_remove_handle (multi, c);
    453       curl_multi_cleanup (multi);
    454       curl_easy_cleanup (c);
    455       MHD_stop_daemon (d);
    456       return 2048;
    457     }
    458     if (MHD_YES != MHD_get_fdset (d, &rs, &ws, &es, &maxsock))
    459     {
    460       curl_multi_remove_handle (multi, c);
    461       curl_multi_cleanup (multi);
    462       curl_easy_cleanup (c);
    463       MHD_stop_daemon (d);
    464       return 4096;
    465     }
    466     tv.tv_sec = 0;
    467     tv.tv_usec = 1000;
    468 #ifdef MHD_POSIX_SOCKETS
    469     if (maxsock > maxposixs)
    470       maxposixs = maxsock;
    471 #endif /* MHD_POSIX_SOCKETS */
    472     if (-1 == select (maxposixs + 1, &rs, &ws, &es, &tv))
    473     {
    474 #ifdef MHD_POSIX_SOCKETS
    475       if (EINTR != errno)
    476       {
    477         fprintf (stderr, "Unexpected select() error: %d. Line: %d\n",
    478                  (int) errno, __LINE__);
    479         fflush (stderr);
    480         exit (99);
    481       }
    482 #else
    483       if ((WSAEINVAL != WSAGetLastError ()) ||
    484           (0 != rs.fd_count) || (0 != ws.fd_count) || (0 != es.fd_count) )
    485       {
    486         fprintf (stderr, "Unexpected select() error: %d. Line: %d\n",
    487                  (int) WSAGetLastError (), __LINE__);
    488         fflush (stderr);
    489         exit (99);
    490       }
    491       Sleep (1);
    492 #endif
    493     }
    494     curl_multi_perform (multi, &running);
    495     if (0 == running)
    496     {
    497       int pending;
    498       int curl_fine = 0;
    499       while (NULL != (msg = curl_multi_info_read (multi, &pending)))
    500       {
    501         if (msg->msg == CURLMSG_DONE)
    502         {
    503           if (msg->data.result == CURLE_OK)
    504             curl_fine = 1;
    505           else
    506           {
    507             fprintf (stderr,
    508                      "%s failed at %s:%d: `%s'\n",
    509                      "curl_multi_perform",
    510                      __FILE__,
    511                      __LINE__, curl_easy_strerror (msg->data.result));
    512             abort ();
    513           }
    514         }
    515       }
    516       if (! curl_fine)
    517       {
    518         fprintf (stderr, "libcurl haven't returned OK code\n");
    519         abort ();
    520       }
    521       curl_multi_remove_handle (multi, c);
    522       curl_multi_cleanup (multi);
    523       curl_easy_cleanup (c);
    524       c = NULL;
    525       multi = NULL;
    526     }
    527     MHD_run (d);
    528   }
    529   if (multi != NULL)
    530   {
    531     curl_multi_remove_handle (multi, c);
    532     curl_easy_cleanup (c);
    533     curl_multi_cleanup (multi);
    534   }
    535   MHD_stop_daemon (d);
    536   if (cbc.pos != 0)
    537     return 8192;
    538   return 0;
    539 }
    540 
    541 
    542 static unsigned int
    543 testUnknownPortGet (uint32_t poll_flag)
    544 {
    545   struct MHD_Daemon *d;
    546   const union MHD_DaemonInfo *di;
    547   CURL *c;
    548   char buf[2048];
    549   struct CBC cbc;
    550   CURLcode errornum;
    551   uint16_t port;
    552 
    553   struct sockaddr_in addr;
    554   socklen_t addr_len = sizeof(addr);
    555   memset (&addr, 0, sizeof(addr));
    556   addr.sin_family = AF_INET;
    557   addr.sin_port = 0;
    558   addr.sin_addr.s_addr = INADDR_ANY;
    559 
    560   cbc.buf = buf;
    561   cbc.size = 2048;
    562   cbc.pos = 0;
    563   d = MHD_start_daemon (MHD_USE_INTERNAL_POLLING_THREAD | MHD_USE_ERROR_LOG
    564                         | (enum MHD_FLAG) poll_flag,
    565                         0, NULL, NULL, &ahc_echo, "GET",
    566                         MHD_OPTION_SOCK_ADDR, &addr,
    567                         MHD_OPTION_URI_LOG_CALLBACK, &log_cb, NULL,
    568                         MHD_OPTION_END);
    569   if (MHD_NO == MHD_is_feature_supported (MHD_FEATURE_AUTODETECT_BIND_PORT))
    570   {
    571     di = MHD_get_daemon_info (d, MHD_DAEMON_INFO_LISTEN_FD);
    572     if (di == NULL)
    573       return 65536;
    574 
    575     if (0 != getsockname (di->listen_fd, (struct sockaddr *) &addr, &addr_len))
    576       return 131072;
    577 
    578     if (addr.sin_family != AF_INET)
    579       return 26214;
    580     port = ntohs (addr.sin_port);
    581   }
    582   else
    583   {
    584     const union MHD_DaemonInfo *dinfo;
    585     dinfo = MHD_get_daemon_info (d, MHD_DAEMON_INFO_BIND_PORT);
    586     if ((NULL == dinfo) || (0 == dinfo->port) )
    587     {
    588       MHD_stop_daemon (d); return 32;
    589     }
    590     port = dinfo->port;
    591   }
    592 
    593   snprintf (buf,
    594             sizeof(buf),
    595             "http://127.0.0.1:%u%s",
    596             (unsigned int) port,
    597             EXPECTED_URI_PATH);
    598 
    599   c = curl_easy_init ();
    600   curl_easy_setopt (c, CURLOPT_URL, buf);
    601   curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
    602   curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
    603   curl_easy_setopt (c, CURLOPT_FAILONERROR, 1L);
    604   curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L);
    605   curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L);
    606   if (oneone)
    607     curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
    608   else
    609     curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
    610   /* NOTE: use of CONNECTTIMEOUT without also
    611      setting NOSIGNAL results in really weird
    612      crashes on my system! */
    613   curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1L);
    614   if (CURLE_OK != (errornum = curl_easy_perform (c)))
    615   {
    616     fprintf (stderr,
    617              "curl_easy_perform failed: `%s'\n",
    618              curl_easy_strerror (errornum));
    619     curl_easy_cleanup (c);
    620     MHD_stop_daemon (d);
    621     return 524288;
    622   }
    623   curl_easy_cleanup (c);
    624   MHD_stop_daemon (d);
    625   if (cbc.pos != 0)
    626     return 1048576;
    627   return 0;
    628 }
    629 
    630 
    631 static unsigned int
    632 testStopRace (uint32_t poll_flag)
    633 {
    634   struct sockaddr_in sin;
    635   MHD_socket fd;
    636   struct MHD_Daemon *d;
    637 
    638   if ( (0 == global_port) &&
    639        (MHD_NO == MHD_is_feature_supported (MHD_FEATURE_AUTODETECT_BIND_PORT)) )
    640   {
    641     global_port = 1224;
    642     if (oneone)
    643       global_port += 20;
    644   }
    645 
    646   d = MHD_start_daemon (MHD_USE_THREAD_PER_CONNECTION
    647                         | MHD_USE_INTERNAL_POLLING_THREAD | MHD_USE_ERROR_LOG
    648                         | (enum MHD_FLAG) poll_flag,
    649                         global_port, NULL, NULL,
    650                         &ahc_echo, "GET",
    651                         MHD_OPTION_URI_LOG_CALLBACK, &log_cb, NULL,
    652                         MHD_OPTION_END);
    653   if (d == NULL)
    654     return 16;
    655   if (0 == global_port)
    656   {
    657     const union MHD_DaemonInfo *dinfo;
    658     dinfo = MHD_get_daemon_info (d, MHD_DAEMON_INFO_BIND_PORT);
    659     if ((NULL == dinfo) || (0 == dinfo->port) )
    660     {
    661       MHD_stop_daemon (d); return 32;
    662     }
    663     global_port = dinfo->port;
    664   }
    665 
    666   fd = socket (PF_INET, SOCK_STREAM, 0);
    667   if (fd == MHD_INVALID_SOCKET)
    668   {
    669     fprintf (stderr, "socket error\n");
    670     return 256;
    671   }
    672 
    673   memset (&sin, 0, sizeof(sin));
    674   sin.sin_family = AF_INET;
    675   sin.sin_port = htons (global_port);
    676   sin.sin_addr.s_addr = htonl (0x7f000001);
    677 
    678   if (connect (fd, (struct sockaddr *) (&sin), sizeof(sin)) < 0)
    679   {
    680     fprintf (stderr, "connect error\n");
    681     MHD_socket_close_chk_ (fd);
    682     return 512;
    683   }
    684 
    685   /*  printf("Waiting\n"); */
    686   /* Let the thread get going. */
    687   usleep (500000);
    688 
    689   /* printf("Stopping daemon\n"); */
    690   MHD_stop_daemon (d);
    691 
    692   MHD_socket_close_chk_ (fd);
    693 
    694   /* printf("good\n"); */
    695   return 0;
    696 }
    697 
    698 
    699 static int
    700 ahc_empty (void *cls,
    701            struct MHD_Connection *connection,
    702            const char *url,
    703            const char *method,
    704            const char *version,
    705            const char *upload_data, size_t *upload_data_size,
    706            void **req_cls)
    707 {
    708   static int ptr;
    709   struct MHD_Response *response;
    710   int ret;
    711   (void) cls;
    712   (void) url;
    713   (void) url;
    714   (void) version;          /* Unused. Silence compiler warning. */
    715   (void) upload_data;
    716   (void) upload_data_size; /* Unused. Silent compiler warning. */
    717 
    718   if (0 != strcmp ("GET", method))
    719     return MHD_NO;              /* unexpected method */
    720   if (&ptr != *req_cls)
    721   {
    722     *req_cls = &ptr;
    723     return MHD_YES;
    724   }
    725   *req_cls = NULL;
    726   response = MHD_create_response_empty (MHD_RF_NONE);
    727   ret = MHD_queue_response (connection, MHD_HTTP_OK, response);
    728   MHD_destroy_response (response);
    729   if (ret == MHD_NO)
    730   {
    731     fprintf (stderr, "Failed to queue response.\n");
    732     _exit (20);
    733   }
    734   return ret;
    735 }
    736 
    737 
    738 static int
    739 curlExcessFound (CURL *c, curl_infotype type, char *data, size_t size,
    740                  void *cls)
    741 {
    742   static const char *excess_found = "Excess found";
    743   const size_t str_size = strlen (excess_found);
    744   (void) c;      /* Unused. Silent compiler warning. */
    745 
    746   if ((CURLINFO_TEXT == type)
    747       && (size >= str_size)
    748       && (0 == strncmp (excess_found, data, str_size)))
    749     *(int *) cls = 1;
    750   return 0;
    751 }
    752 
    753 
    754 static unsigned int
    755 testEmptyGet (uint32_t poll_flag)
    756 {
    757   struct MHD_Daemon *d;
    758   CURL *c;
    759   char buf[2048];
    760   struct CBC cbc;
    761   CURLcode errornum;
    762   int excess_found = 0;
    763 
    764   if ( (0 == global_port) &&
    765        (MHD_NO == MHD_is_feature_supported (MHD_FEATURE_AUTODETECT_BIND_PORT)) )
    766   {
    767     global_port = 1225;
    768     if (oneone)
    769       global_port += 20;
    770   }
    771 
    772   cbc.buf = buf;
    773   cbc.size = 2048;
    774   cbc.pos = 0;
    775   d = MHD_start_daemon (MHD_USE_INTERNAL_POLLING_THREAD | MHD_USE_ERROR_LOG
    776                         | (enum MHD_FLAG) poll_flag,
    777                         global_port, NULL, NULL,
    778                         &ahc_empty, NULL,
    779                         MHD_OPTION_URI_LOG_CALLBACK, &log_cb, NULL,
    780                         MHD_OPTION_END);
    781   if (d == NULL)
    782     return 4194304;
    783   if (0 == global_port)
    784   {
    785     const union MHD_DaemonInfo *dinfo;
    786     dinfo = MHD_get_daemon_info (d, MHD_DAEMON_INFO_BIND_PORT);
    787     if ((NULL == dinfo) || (0 == dinfo->port) )
    788     {
    789       MHD_stop_daemon (d); return 32;
    790     }
    791     global_port = dinfo->port;
    792   }
    793   c = curl_easy_init ();
    794   curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1" EXPECTED_URI_PATH);
    795   curl_easy_setopt (c, CURLOPT_PORT, (long) global_port);
    796   curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
    797   curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
    798   curl_easy_setopt (c, CURLOPT_DEBUGFUNCTION, &curlExcessFound);
    799   curl_easy_setopt (c, CURLOPT_DEBUGDATA, &excess_found);
    800   curl_easy_setopt (c, CURLOPT_VERBOSE, 1L);
    801   curl_easy_setopt (c, CURLOPT_FAILONERROR, 1L);
    802   curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L);
    803   curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L);
    804   if (oneone)
    805     curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
    806   else
    807     curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
    808   /* NOTE: use of CONNECTTIMEOUT without also
    809      setting NOSIGNAL results in really weird
    810      crashes on my system!*/
    811   curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1L);
    812   if (CURLE_OK != (errornum = curl_easy_perform (c)))
    813   {
    814     fprintf (stderr,
    815              "curl_easy_perform failed: `%s'\n",
    816              curl_easy_strerror (errornum));
    817     curl_easy_cleanup (c);
    818     MHD_stop_daemon (d);
    819     return 8388608;
    820   }
    821   curl_easy_cleanup (c);
    822   MHD_stop_daemon (d);
    823   if (cbc.pos != 0)
    824     return 16777216;
    825   if (excess_found)
    826     return 33554432;
    827   return 0;
    828 }
    829 
    830 
    831 int
    832 main (int argc, char *const *argv)
    833 {
    834   unsigned int errorCount = 0;
    835   unsigned int test_result = 0;
    836   int verbose = 0;
    837 
    838   if ((NULL == argv) || (0 == argv[0]))
    839     return 99;
    840   oneone = has_in_name (argv[0], "11");
    841   verbose = has_param (argc, argv, "-v") || has_param (argc, argv, "--verbose");
    842   if (0 != curl_global_init (CURL_GLOBAL_WIN32))
    843     return 2;
    844   global_port = 0;
    845   test_result = testExternalGet ();
    846   if (test_result)
    847     fprintf (stderr, "FAILED: testExternalGet () - %u.\n", test_result);
    848   else if (verbose)
    849     printf ("PASSED: testExternalGet ().\n");
    850   errorCount += test_result;
    851   if (MHD_YES == MHD_is_feature_supported (MHD_FEATURE_THREADS))
    852   {
    853     test_result += testInternalGet (0);
    854     if (test_result)
    855       fprintf (stderr, "FAILED: testInternalGet (0) - %u.\n", test_result);
    856     else if (verbose)
    857       printf ("PASSED: testInternalGet (0).\n");
    858     errorCount += test_result;
    859     test_result += testMultithreadedGet (0);
    860     if (test_result)
    861       fprintf (stderr, "FAILED: testMultithreadedGet (0) - %u.\n", test_result);
    862     else if (verbose)
    863       printf ("PASSED: testMultithreadedGet (0).\n");
    864     errorCount += test_result;
    865     test_result += testMultithreadedPoolGet (0);
    866     if (test_result)
    867       fprintf (stderr, "FAILED: testMultithreadedPoolGet (0) - %u.\n",
    868                test_result);
    869     else if (verbose)
    870       printf ("PASSED: testMultithreadedPoolGet (0).\n");
    871     errorCount += test_result;
    872     test_result += testUnknownPortGet (0);
    873     if (test_result)
    874       fprintf (stderr, "FAILED: testUnknownPortGet (0) - %u.\n", test_result);
    875     else if (verbose)
    876       printf ("PASSED: testUnknownPortGet (0).\n");
    877     errorCount += test_result;
    878     test_result += testEmptyGet (0);
    879     if (test_result)
    880       fprintf (stderr, "FAILED: testEmptyGet (0) - %u.\n", test_result);
    881     else if (verbose)
    882       printf ("PASSED: testEmptyGet (0).\n");
    883     errorCount += test_result;
    884     if (MHD_YES == MHD_is_feature_supported (MHD_FEATURE_POLL))
    885     {
    886       test_result += testInternalGet (MHD_USE_POLL);
    887       if (test_result)
    888         fprintf (stderr, "FAILED: testInternalGet (MHD_USE_POLL) - %u.\n",
    889                  test_result);
    890       else if (verbose)
    891         printf ("PASSED: testInternalGet (MHD_USE_POLL).\n");
    892       errorCount += test_result;
    893       test_result += testMultithreadedGet (MHD_USE_POLL);
    894       if (test_result)
    895         fprintf (stderr, "FAILED: testMultithreadedGet (MHD_USE_POLL) - %u.\n",
    896                  test_result);
    897       else if (verbose)
    898         printf ("PASSED: testMultithreadedGet (MHD_USE_POLL).\n");
    899       errorCount += test_result;
    900       test_result += testMultithreadedPoolGet (MHD_USE_POLL);
    901       if (test_result)
    902         fprintf (stderr,
    903                  "FAILED: testMultithreadedPoolGet (MHD_USE_POLL) - %u.\n",
    904                  test_result);
    905       else if (verbose)
    906         printf ("PASSED: testMultithreadedPoolGet (MHD_USE_POLL).\n");
    907       errorCount += test_result;
    908       test_result += testUnknownPortGet (MHD_USE_POLL);
    909       if (test_result)
    910         fprintf (stderr, "FAILED: testUnknownPortGet (MHD_USE_POLL) - %u.\n",
    911                  test_result);
    912       else if (verbose)
    913         printf ("PASSED: testUnknownPortGet (MHD_USE_POLL).\n");
    914       errorCount += test_result;
    915       test_result += testEmptyGet (MHD_USE_POLL);
    916       if (test_result)
    917         fprintf (stderr, "FAILED: testEmptyGet (MHD_USE_POLL) - %u.\n",
    918                  test_result);
    919       else if (verbose)
    920         printf ("PASSED: testEmptyGet (MHD_USE_POLL).\n");
    921       errorCount += test_result;
    922     }
    923     if (MHD_YES == MHD_is_feature_supported (MHD_FEATURE_EPOLL))
    924     {
    925       test_result += testInternalGet (MHD_USE_EPOLL);
    926       if (test_result)
    927         fprintf (stderr, "FAILED: testInternalGet (MHD_USE_EPOLL) - %u.\n",
    928                  test_result);
    929       else if (verbose)
    930         printf ("PASSED: testInternalGet (MHD_USE_EPOLL).\n");
    931       errorCount += test_result;
    932       test_result += testMultithreadedPoolGet (MHD_USE_EPOLL);
    933       if (test_result)
    934         fprintf (stderr,
    935                  "FAILED: testMultithreadedPoolGet (MHD_USE_EPOLL) - %u.\n",
    936                  test_result);
    937       else if (verbose)
    938         printf ("PASSED: testMultithreadedPoolGet (MHD_USE_EPOLL).\n");
    939       errorCount += test_result;
    940       test_result += testUnknownPortGet (MHD_USE_EPOLL);
    941       if (test_result)
    942         fprintf (stderr, "FAILED: testUnknownPortGet (MHD_USE_EPOLL) - %u.\n",
    943                  test_result);
    944       else if (verbose)
    945         printf ("PASSED: testUnknownPortGet (MHD_USE_EPOLL).\n");
    946       errorCount += test_result;
    947       test_result += testEmptyGet (MHD_USE_EPOLL);
    948       if (test_result)
    949         fprintf (stderr, "FAILED: testEmptyGet (MHD_USE_EPOLL) - %u.\n",
    950                  test_result);
    951       else if (verbose)
    952         printf ("PASSED: testEmptyGet (MHD_USE_EPOLL).\n");
    953       errorCount += test_result;
    954     }
    955   }
    956   if (0 != errorCount)
    957     fprintf (stderr,
    958              "Error (code: %u)\n",
    959              errorCount);
    960   else if (verbose)
    961     printf ("All tests passed.\n");
    962   curl_global_cleanup ();
    963   return (0 == errorCount) ? 0 : 1;       /* 0 == pass */
    964 }