libmicrohttpd

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

test_get_iovec.c (21043B)


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