libmicrohttpd

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

test_get.c (30594B)


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