libmicrohttpd

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

test_get.c (30427B)


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