libmicrohttpd

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

test_get_iovec.c (20876B)


      1 /*
      2      This file is part of libmicrohttpd
      3      Copyright (C) 2007-2021 Christian Grothoff
      4      Copyright (C) 2014-2022 Evgeny Grin
      5 
      6      libmicrohttpd is free software; you can redistribute it and/or modify
      7      it under the terms of the GNU General Public License as published
      8      by the Free Software Foundation; either version 2, or (at your
      9      option) any later version.
     10 
     11      libmicrohttpd is distributed in the hope that it will be useful, but
     12      WITHOUT ANY WARRANTY; without even the implied warranty of
     13      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     14      General Public License for more details.
     15 
     16      You should have received a copy of the GNU General Public License
     17      along with libmicrohttpd; see the file COPYING.  If not, write to the
     18      Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
     19      Boston, MA 02110-1301, USA.
     20 */
     21 
     22 /**
     23  * @file test_get_iovec.c
     24  * @brief  Testcase for libmicrohttpd response from scatter/gather array
     25  * @author Christian Grothoff
     26  * @author Karlson2k (Evgeny Grin)
     27  * @author Lawrence Sebald
     28  */
     29 
     30 /*
     31  * This test is largely derived from the test_get_sendfile.c file, with the
     32  * daemon using MHD_create_response_from_iovec instead of working from an fd.
     33  */
     34 
     35 #include "mhd_options.h"
     36 #include "platform.h"
     37 #include <curl/curl.h>
     38 #include <microhttpd.h>
     39 #include <stdlib.h>
     40 #include <string.h>
     41 #include <time.h>
     42 #include <sys/types.h>
     43 #include <fcntl.h>
     44 #ifdef HAVE_STDBOOL_H
     45 #include <stdbool.h>
     46 #endif
     47 #include <errno.h>
     48 #include "mhd_sockets.h"
     49 #include "mhd_has_in_name.h"
     50 
     51 #ifndef WINDOWS
     52 #include <sys/socket.h>
     53 #include <unistd.h>
     54 #endif
     55 
     56 #if defined(MHD_CPU_COUNT) && (MHD_CPU_COUNT + 0) < 2
     57 #undef MHD_CPU_COUNT
     58 #endif
     59 #if ! defined(MHD_CPU_COUNT)
     60 #define MHD_CPU_COUNT 2
     61 #endif
     62 
     63 #define TESTSTR_IOVLEN 20480
     64 #define TESTSTR_IOVCNT 20
     65 #define TESTSTR_SIZE   (TESTSTR_IOVCNT * TESTSTR_IOVLEN)
     66 
     67 static int oneone;
     68 
     69 static int readbuf[TESTSTR_SIZE * 2 / sizeof(int)];
     70 
     71 struct CBC
     72 {
     73   char *buf;
     74   size_t pos;
     75   size_t size;
     76 };
     77 
     78 
     79 static size_t
     80 copyBuffer (void *ptr, size_t size, size_t nmemb, void *ctx)
     81 {
     82   struct CBC *cbc = ctx;
     83 
     84   if (cbc->pos + size * nmemb > cbc->size)
     85     _exit (7);                   /* overflow */
     86   memcpy (&cbc->buf[cbc->pos], ptr, size * nmemb);
     87   cbc->pos += size * nmemb;
     88   return size * nmemb;
     89 }
     90 
     91 
     92 static void
     93 iov_free_callback (void *cls)
     94 {
     95   free (cls);
     96 }
     97 
     98 
     99 struct iovncont_data
    100 {
    101   void *ptrs[TESTSTR_IOVCNT];
    102 };
    103 
    104 static void
    105 iovncont_free_callback (void *cls)
    106 {
    107   struct iovncont_data *data = (struct iovncont_data *) cls;
    108   unsigned int i;
    109 
    110   for (i = 0; i < TESTSTR_IOVCNT; ++i)
    111     free (data->ptrs[i]);
    112   free (data);
    113 }
    114 
    115 
    116 static int
    117 check_read_data (const void *ptr, size_t len)
    118 {
    119   const int *buf;
    120   size_t i;
    121 
    122   if (len % sizeof(int))
    123     return -1;
    124 
    125   buf = (const int *) ptr;
    126 
    127   for (i = 0; i < len / sizeof(int); ++i)
    128   {
    129     if (buf[i] != (int) i)
    130       return -1;
    131   }
    132 
    133   return 0;
    134 }
    135 
    136 
    137 static enum MHD_Result
    138 ahc_cont (void *cls,
    139           struct MHD_Connection *connection,
    140           const char *url,
    141           const char *method,
    142           const char *version,
    143           const char *upload_data, size_t *upload_data_size,
    144           void **req_cls)
    145 {
    146   static int ptr;
    147   struct MHD_Response *response;
    148   enum MHD_Result ret;
    149   int *data;
    150   struct MHD_IoVec iov[TESTSTR_IOVCNT];
    151   int i;
    152   (void) cls;
    153   (void) url; (void) version;                      /* Unused. Silent compiler warning. */
    154   (void) upload_data; (void) upload_data_size;     /* Unused. Silent compiler warning. */
    155 
    156   if (0 != strcmp (MHD_HTTP_METHOD_GET, method))
    157     return MHD_NO;              /* unexpected method */
    158   if (&ptr != *req_cls)
    159   {
    160     *req_cls = &ptr;
    161     return MHD_YES;
    162   }
    163   *req_cls = NULL;
    164 
    165   /* Create some test data. */
    166   if (NULL == (data = malloc (TESTSTR_SIZE)))
    167     return MHD_NO;
    168 
    169   for (i = 0; i < (int) (TESTSTR_SIZE / sizeof(int)); ++i)
    170   {
    171     data[i] = i;
    172   }
    173 
    174   for (i = 0; i < TESTSTR_IOVCNT; ++i)
    175   {
    176     iov[i].iov_base = data + (((size_t) i)
    177                               * (TESTSTR_SIZE / TESTSTR_IOVCNT / sizeof(int)));
    178     iov[i].iov_len = TESTSTR_SIZE / TESTSTR_IOVCNT;
    179   }
    180 
    181   response = MHD_create_response_from_iovec (iov,
    182                                              TESTSTR_IOVCNT,
    183                                              &iov_free_callback,
    184                                              data);
    185   ret = MHD_queue_response (connection, MHD_HTTP_OK, response);
    186   MHD_destroy_response (response);
    187   if (ret == MHD_NO)
    188     abort ();
    189   return ret;
    190 }
    191 
    192 
    193 static enum MHD_Result
    194 ahc_ncont (void *cls,
    195            struct MHD_Connection *connection,
    196            const char *url,
    197            const char *method,
    198            const char *version,
    199            const char *upload_data, size_t *upload_data_size,
    200            void **req_cls)
    201 {
    202   static int ptr;
    203   struct MHD_Response *response;
    204   enum MHD_Result ret;
    205   struct MHD_IoVec iov[TESTSTR_IOVCNT];
    206   struct iovncont_data *clear_cls;
    207   int i, j;
    208   (void) cls;
    209   (void) url; (void) version;                      /* Unused. Silent compiler warning. */
    210   (void) upload_data; (void) upload_data_size;     /* Unused. Silent compiler warning. */
    211 
    212   if (0 != strcmp (MHD_HTTP_METHOD_GET, method))
    213     return MHD_NO;              /* unexpected method */
    214   if (&ptr != *req_cls)
    215   {
    216     *req_cls = &ptr;
    217     return MHD_YES;
    218   }
    219   *req_cls = NULL;
    220 
    221   clear_cls = malloc (sizeof(struct iovncont_data));
    222   if (NULL == clear_cls)
    223     abort ();
    224   memset (iov, 0, sizeof(struct MHD_IoVec) * TESTSTR_IOVCNT);
    225 
    226   /* Create some test data. */
    227   for (j = TESTSTR_IOVCNT - 1; j >= 0; --j)
    228   {
    229     int *data;
    230     data = malloc (TESTSTR_IOVLEN);
    231     if (NULL == data)
    232       abort ();
    233     clear_cls->ptrs[j] = (void *) data;
    234 
    235     for (i = 0; i < (int) (TESTSTR_IOVLEN / sizeof(int)); ++i)
    236     {
    237       data[i] = i + (j * (int) (TESTSTR_IOVLEN / sizeof(int)));
    238     }
    239     iov[j].iov_base = (const void *) data;
    240     iov[j].iov_len = TESTSTR_IOVLEN;
    241 
    242   }
    243 
    244   response = MHD_create_response_from_iovec (iov,
    245                                              TESTSTR_IOVCNT,
    246                                              &iovncont_free_callback,
    247                                              clear_cls);
    248   ret = MHD_queue_response (connection,
    249                             MHD_HTTP_OK,
    250                             response);
    251   MHD_destroy_response (response);
    252   if (ret == MHD_NO)
    253     abort ();
    254   return ret;
    255 }
    256 
    257 
    258 static unsigned int
    259 testInternalGet (bool contiguous)
    260 {
    261   struct MHD_Daemon *d;
    262   CURL *c;
    263   struct CBC cbc;
    264   CURLcode errornum;
    265   uint16_t port;
    266 
    267   if (MHD_NO != MHD_is_feature_supported (MHD_FEATURE_AUTODETECT_BIND_PORT))
    268     port = 0;
    269   else
    270   {
    271     port = 1200;
    272     if (oneone)
    273       port += 10;
    274   }
    275 
    276   cbc.buf = (char *) readbuf;
    277   cbc.size = sizeof(readbuf);
    278   cbc.pos = 0;
    279 
    280   if (contiguous)
    281   {
    282     d = MHD_start_daemon (MHD_USE_INTERNAL_POLLING_THREAD | MHD_USE_ERROR_LOG,
    283                           port, NULL, NULL, &ahc_cont, NULL, MHD_OPTION_END);
    284   }
    285   else
    286   {
    287     d = MHD_start_daemon (MHD_USE_INTERNAL_POLLING_THREAD | MHD_USE_ERROR_LOG,
    288                           port, NULL, NULL, &ahc_ncont, NULL, MHD_OPTION_END);
    289   }
    290 
    291   if (d == NULL)
    292     return 1;
    293   if (0 == port)
    294   {
    295     const union MHD_DaemonInfo *dinfo;
    296     dinfo = MHD_get_daemon_info (d, MHD_DAEMON_INFO_BIND_PORT);
    297     if ((NULL == dinfo) || (0 == dinfo->port) )
    298     {
    299       MHD_stop_daemon (d); return 32;
    300     }
    301     port = dinfo->port;
    302   }
    303   c = curl_easy_init ();
    304   curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1/");
    305   curl_easy_setopt (c, CURLOPT_PORT, (long) port);
    306   curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
    307   curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
    308   curl_easy_setopt (c, CURLOPT_FAILONERROR, 1L);
    309   curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L);
    310   curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L);
    311   if (oneone)
    312     curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
    313   else
    314     curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
    315   /* NOTE: use of CONNECTTIMEOUT without also
    316      setting NOSIGNAL results in really weird
    317      crashes on my system!*/
    318   curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1L);
    319   if (CURLE_OK != (errornum = curl_easy_perform (c)))
    320   {
    321     fprintf (stderr,
    322              "curl_easy_perform failed: `%s'\n",
    323              curl_easy_strerror (errornum));
    324     curl_easy_cleanup (c);
    325     MHD_stop_daemon (d);
    326     return 2;
    327   }
    328   curl_easy_cleanup (c);
    329   MHD_stop_daemon (d);
    330   if (cbc.pos != TESTSTR_SIZE)
    331     return 4;
    332   if (0 != check_read_data (cbc.buf, cbc.pos))
    333     return 8;
    334   return 0;
    335 }
    336 
    337 
    338 static unsigned int
    339 testMultithreadedGet (void)
    340 {
    341   struct MHD_Daemon *d;
    342   CURL *c;
    343   struct CBC cbc;
    344   CURLcode errornum;
    345   uint16_t port;
    346 
    347   if (MHD_NO != MHD_is_feature_supported (MHD_FEATURE_AUTODETECT_BIND_PORT))
    348     port = 0;
    349   else
    350   {
    351     port = 1201;
    352     if (oneone)
    353       port += 10;
    354   }
    355 
    356   cbc.buf = (char *) readbuf;
    357   cbc.size = sizeof(readbuf);
    358   cbc.pos = 0;
    359   d = MHD_start_daemon (MHD_USE_THREAD_PER_CONNECTION
    360                         | MHD_USE_INTERNAL_POLLING_THREAD | MHD_USE_ERROR_LOG
    361                         | MHD_USE_AUTO,
    362                         port, NULL, NULL, &ahc_cont, NULL, MHD_OPTION_END);
    363   if (d == NULL)
    364     return 16;
    365   if (0 == port)
    366   {
    367     const union MHD_DaemonInfo *dinfo;
    368     dinfo = MHD_get_daemon_info (d, MHD_DAEMON_INFO_BIND_PORT);
    369     if ((NULL == dinfo) || (0 == dinfo->port) )
    370     {
    371       MHD_stop_daemon (d); return 32;
    372     }
    373     port = dinfo->port;
    374   }
    375   c = curl_easy_init ();
    376   curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1/");
    377   curl_easy_setopt (c, CURLOPT_PORT, (long) port);
    378   curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
    379   curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
    380   curl_easy_setopt (c, CURLOPT_FAILONERROR, 1L);
    381   curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L);
    382   if (oneone)
    383     curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
    384   else
    385     curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
    386   curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L);
    387   /* NOTE: use of CONNECTTIMEOUT without also
    388      setting NOSIGNAL results in really weird
    389      crashes on my system! */
    390   curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1L);
    391   if (CURLE_OK != (errornum = curl_easy_perform (c)))
    392   {
    393     fprintf (stderr,
    394              "curl_easy_perform failed: `%s'\n",
    395              curl_easy_strerror (errornum));
    396     curl_easy_cleanup (c);
    397     MHD_stop_daemon (d);
    398     return 32;
    399   }
    400   curl_easy_cleanup (c);
    401   MHD_stop_daemon (d);
    402   if (cbc.pos != TESTSTR_SIZE)
    403     return 64;
    404   if (0 != check_read_data (cbc.buf, cbc.pos))
    405     return 128;
    406   return 0;
    407 }
    408 
    409 
    410 static unsigned int
    411 testMultithreadedPoolGet (void)
    412 {
    413   struct MHD_Daemon *d;
    414   CURL *c;
    415   struct CBC cbc;
    416   CURLcode errornum;
    417   uint16_t port;
    418 
    419   if (MHD_NO != MHD_is_feature_supported (MHD_FEATURE_AUTODETECT_BIND_PORT))
    420     port = 0;
    421   else
    422   {
    423     port = 1202;
    424     if (oneone)
    425       port += 10;
    426   }
    427 
    428   cbc.buf = (char *) readbuf;
    429   cbc.size = sizeof(readbuf);
    430   cbc.pos = 0;
    431   d = MHD_start_daemon (MHD_USE_INTERNAL_POLLING_THREAD | MHD_USE_ERROR_LOG
    432                         | MHD_USE_AUTO,
    433                         port, NULL, NULL, &ahc_cont, NULL,
    434                         MHD_OPTION_THREAD_POOL_SIZE, MHD_CPU_COUNT,
    435                         MHD_OPTION_END);
    436   if (d == NULL)
    437     return 16;
    438   if (0 == port)
    439   {
    440     const union MHD_DaemonInfo *dinfo;
    441     dinfo = MHD_get_daemon_info (d, MHD_DAEMON_INFO_BIND_PORT);
    442     if ((NULL == dinfo) || (0 == dinfo->port) )
    443     {
    444       MHD_stop_daemon (d); return 32;
    445     }
    446     port = dinfo->port;
    447   }
    448   c = curl_easy_init ();
    449   curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1/");
    450   curl_easy_setopt (c, CURLOPT_PORT, (long) port);
    451   curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
    452   curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
    453   curl_easy_setopt (c, CURLOPT_FAILONERROR, 1L);
    454   curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L);
    455   if (oneone)
    456     curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
    457   else
    458     curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
    459   curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L);
    460   /* NOTE: use of CONNECTTIMEOUT without also
    461      setting NOSIGNAL results in really weird
    462      crashes on my system!*/
    463   curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1L);
    464   if (CURLE_OK != (errornum = curl_easy_perform (c)))
    465   {
    466     fprintf (stderr,
    467              "curl_easy_perform failed: `%s'\n",
    468              curl_easy_strerror (errornum));
    469     curl_easy_cleanup (c);
    470     MHD_stop_daemon (d);
    471     return 32;
    472   }
    473   curl_easy_cleanup (c);
    474   MHD_stop_daemon (d);
    475   if (cbc.pos != TESTSTR_SIZE)
    476     return 64;
    477   if (0 != check_read_data (cbc.buf, cbc.pos))
    478     return 128;
    479   return 0;
    480 }
    481 
    482 
    483 static unsigned int
    484 testExternalGet (int thread_unsafe)
    485 {
    486   struct MHD_Daemon *d;
    487   CURL *c;
    488   struct CBC cbc;
    489   CURLM *multi;
    490   CURLMcode mret;
    491   fd_set rs;
    492   fd_set ws;
    493   fd_set es;
    494   MHD_socket maxsock;
    495 #ifdef MHD_WINSOCK_SOCKETS
    496   int maxposixs; /* Max socket number unused on W32 */
    497 #else  /* MHD_POSIX_SOCKETS */
    498 #define maxposixs maxsock
    499 #endif /* MHD_POSIX_SOCKETS */
    500   int running;
    501   struct CURLMsg *msg;
    502   time_t start;
    503   struct timeval tv;
    504   uint16_t port;
    505 
    506   if (MHD_NO != MHD_is_feature_supported (MHD_FEATURE_AUTODETECT_BIND_PORT))
    507     port = 0;
    508   else
    509   {
    510     port = 1203;
    511     if (oneone)
    512       port += 10;
    513   }
    514 
    515   multi = NULL;
    516   cbc.buf = (char *) readbuf;
    517   cbc.size = sizeof(readbuf);
    518   cbc.pos = 0;
    519   d = MHD_start_daemon (MHD_USE_ERROR_LOG
    520                         | (thread_unsafe ? MHD_USE_NO_THREAD_SAFETY : 0),
    521                         port, NULL, NULL, &ahc_cont, NULL,
    522                         MHD_OPTION_APP_FD_SETSIZE, (int) FD_SETSIZE,
    523                         MHD_OPTION_END);
    524   if (d == NULL)
    525     return 256;
    526   if (0 == port)
    527   {
    528     const union MHD_DaemonInfo *dinfo;
    529     dinfo = MHD_get_daemon_info (d, MHD_DAEMON_INFO_BIND_PORT);
    530     if ((NULL == dinfo) || (0 == dinfo->port) )
    531     {
    532       MHD_stop_daemon (d); return 32;
    533     }
    534     port = dinfo->port;
    535   }
    536   c = curl_easy_init ();
    537   curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1/");
    538   curl_easy_setopt (c, CURLOPT_PORT, (long) port);
    539   curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
    540   curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
    541   curl_easy_setopt (c, CURLOPT_FAILONERROR, 1L);
    542   if (oneone)
    543     curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
    544   else
    545     curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
    546   curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L);
    547   curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L);
    548   /* NOTE: use of CONNECTTIMEOUT without also
    549      setting NOSIGNAL results in really weird
    550      crashes on my system! */
    551   curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1L);
    552 
    553 
    554   multi = curl_multi_init ();
    555   if (multi == NULL)
    556   {
    557     curl_easy_cleanup (c);
    558     MHD_stop_daemon (d);
    559     return 512;
    560   }
    561   mret = curl_multi_add_handle (multi, c);
    562   if (mret != CURLM_OK)
    563   {
    564     curl_multi_cleanup (multi);
    565     curl_easy_cleanup (c);
    566     MHD_stop_daemon (d);
    567     return 1024;
    568   }
    569   start = time (NULL);
    570   while ((time (NULL) - start < 5) && (multi != NULL))
    571   {
    572     maxsock = MHD_INVALID_SOCKET;
    573     maxposixs = -1;
    574     FD_ZERO (&rs);
    575     FD_ZERO (&ws);
    576     FD_ZERO (&es);
    577     curl_multi_perform (multi, &running);
    578     mret = curl_multi_fdset (multi, &rs, &ws, &es, &maxposixs);
    579     if (mret != CURLM_OK)
    580     {
    581       curl_multi_remove_handle (multi, c);
    582       curl_multi_cleanup (multi);
    583       curl_easy_cleanup (c);
    584       MHD_stop_daemon (d);
    585       return 2048;
    586     }
    587     if (MHD_YES != MHD_get_fdset (d, &rs, &ws, &es, &maxsock))
    588     {
    589       curl_multi_remove_handle (multi, c);
    590       curl_multi_cleanup (multi);
    591       curl_easy_cleanup (c);
    592       MHD_stop_daemon (d);
    593       return 4096;
    594     }
    595     tv.tv_sec = 0;
    596     tv.tv_usec = 1000;
    597     if (-1 == select (maxposixs + 1, &rs, &ws, &es, &tv))
    598     {
    599 #ifdef MHD_POSIX_SOCKETS
    600       if (EINTR != errno)
    601       {
    602         fprintf (stderr, "Unexpected select() error: %d. Line: %d\n",
    603                  (int) errno, __LINE__);
    604         fflush (stderr);
    605         exit (99);
    606       }
    607 #else
    608       if ((WSAEINVAL != WSAGetLastError ()) ||
    609           (0 != rs.fd_count) || (0 != ws.fd_count) || (0 != es.fd_count) )
    610       {
    611         fprintf (stderr, "Unexpected select() error: %d. Line: %d\n",
    612                  (int) WSAGetLastError (), __LINE__);
    613         fflush (stderr);
    614         exit (99);
    615       }
    616       Sleep (1);
    617 #endif
    618     }
    619     curl_multi_perform (multi, &running);
    620     if (0 == running)
    621     {
    622       int pending;
    623       int curl_fine = 0;
    624       while (NULL != (msg = curl_multi_info_read (multi, &pending)))
    625       {
    626         if (msg->msg == CURLMSG_DONE)
    627         {
    628           if (msg->data.result == CURLE_OK)
    629             curl_fine = 1;
    630           else
    631           {
    632             fprintf (stderr,
    633                      "%s failed at %s:%d: `%s'\n",
    634                      "curl_multi_perform",
    635                      __FILE__,
    636                      __LINE__, curl_easy_strerror (msg->data.result));
    637             abort ();
    638           }
    639         }
    640       }
    641       if (! curl_fine)
    642       {
    643         fprintf (stderr, "libcurl haven't returned OK code\n");
    644         abort ();
    645       }
    646       curl_multi_remove_handle (multi, c);
    647       curl_multi_cleanup (multi);
    648       curl_easy_cleanup (c);
    649       c = NULL;
    650       multi = NULL;
    651     }
    652     MHD_run (d);
    653   }
    654   if (multi != NULL)
    655   {
    656     curl_multi_remove_handle (multi, c);
    657     curl_easy_cleanup (c);
    658     curl_multi_cleanup (multi);
    659   }
    660   MHD_stop_daemon (d);
    661   if (cbc.pos != TESTSTR_SIZE)
    662     return 8192;
    663   if (0 != check_read_data (cbc.buf, cbc.pos))
    664     return 16384;
    665   return 0;
    666 }
    667 
    668 
    669 static unsigned int
    670 testUnknownPortGet (void)
    671 {
    672   struct MHD_Daemon *d;
    673   const union MHD_DaemonInfo *di;
    674   CURL *c;
    675   struct CBC cbc;
    676   CURLcode errornum;
    677   uint16_t port;
    678   char buf[2048];
    679 
    680   struct sockaddr_in addr;
    681   socklen_t addr_len = sizeof(addr);
    682   memset (&addr, 0, sizeof(addr));
    683   addr.sin_family = AF_INET;
    684   addr.sin_port = 0;
    685   addr.sin_addr.s_addr = INADDR_ANY;
    686 
    687   cbc.buf = (char *) readbuf;
    688   cbc.size = sizeof(readbuf);
    689   cbc.pos = 0;
    690   d = MHD_start_daemon (MHD_USE_INTERNAL_POLLING_THREAD | MHD_USE_ERROR_LOG,
    691                         0, NULL, NULL, &ahc_cont, NULL,
    692                         MHD_OPTION_SOCK_ADDR, &addr,
    693                         MHD_OPTION_END);
    694   if (d == NULL)
    695     return 32768;
    696 
    697   if (MHD_NO == MHD_is_feature_supported (MHD_FEATURE_AUTODETECT_BIND_PORT))
    698   {
    699     di = MHD_get_daemon_info (d, MHD_DAEMON_INFO_LISTEN_FD);
    700     if (di == NULL)
    701       return 65536;
    702 
    703     if (0 != getsockname (di->listen_fd, (struct sockaddr *) &addr, &addr_len))
    704       return 131072;
    705 
    706     if (addr.sin_family != AF_INET)
    707       return 26214;
    708     port = ntohs (addr.sin_port);
    709   }
    710   else
    711   {
    712     const union MHD_DaemonInfo *dinfo;
    713     dinfo = MHD_get_daemon_info (d, MHD_DAEMON_INFO_BIND_PORT);
    714     if ((NULL == dinfo) || (0 == dinfo->port) )
    715     {
    716       MHD_stop_daemon (d); return 32;
    717     }
    718     port = dinfo->port;
    719   }
    720 
    721   snprintf (buf, sizeof(buf), "http://127.0.0.1:%u/",
    722             (unsigned int) port);
    723 
    724   c = curl_easy_init ();
    725   curl_easy_setopt (c, CURLOPT_URL, buf);
    726   curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
    727   curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
    728   curl_easy_setopt (c, CURLOPT_FAILONERROR, 1L);
    729   curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L);
    730   curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L);
    731   if (oneone)
    732     curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
    733   else
    734     curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
    735   /* NOTE: use of CONNECTTIMEOUT without also
    736      setting NOSIGNAL results in really weird
    737      crashes on my system! */
    738   curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1L);
    739   if (CURLE_OK != (errornum = curl_easy_perform (c)))
    740   {
    741     fprintf (stderr,
    742              "curl_easy_perform failed: `%s'\n",
    743              curl_easy_strerror (errornum));
    744     curl_easy_cleanup (c);
    745     MHD_stop_daemon (d);
    746     return 524288;
    747   }
    748   curl_easy_cleanup (c);
    749   MHD_stop_daemon (d);
    750   if (cbc.pos != TESTSTR_SIZE)
    751     return 1048576;
    752   if (0 != check_read_data (cbc.buf, cbc.pos))
    753     return 2097152;
    754   return 0;
    755 }
    756 
    757 
    758 int
    759 main (int argc, char *const *argv)
    760 {
    761   unsigned int errorCount = 0;
    762   (void) argc;   /* Unused. Silent compiler warning. */
    763 
    764   if ((NULL == argv) || (0 == argv[0]))
    765     return 99;
    766   oneone = has_in_name (argv[0], "11");
    767 
    768   if (0 != curl_global_init (CURL_GLOBAL_WIN32))
    769     return 2;
    770   if (MHD_YES == MHD_is_feature_supported (MHD_FEATURE_THREADS))
    771   {
    772     errorCount += testInternalGet (true);
    773     errorCount += testInternalGet (false);
    774     errorCount += testMultithreadedGet ();
    775     errorCount += testMultithreadedPoolGet ();
    776     errorCount += testUnknownPortGet ();
    777     errorCount += testExternalGet (0);
    778   }
    779   errorCount += testExternalGet (! 0);
    780   if (errorCount != 0)
    781     fprintf (stderr, "Error (code: %u)\n", errorCount);
    782   curl_global_cleanup ();
    783   return (0 == errorCount) ? 0 : 1;       /* 0 == pass */
    784 }