libmicrohttpd

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

test_get_empty.c (27987B)


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