libmicrohttpd

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

test_get_chunked.c (22059B)


      1 /*
      2      This file is part of libmicrohttpd
      3      Copyright (C) 2007 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 /**
     23  * @file test_get_chunked.c
     24  * @brief  Testcase for libmicrohttpd GET operations with chunked content encoding
     25  * @author Christian Grothoff
     26  * @author Karlson2k (Evgeny Grin)
     27  */
     28 
     29 #include "MHD_config.h"
     30 #include "platform.h"
     31 #include <curl/curl.h>
     32 #include <microhttpd.h>
     33 #include <stdlib.h>
     34 #include <string.h>
     35 #include <time.h>
     36 #include <errno.h>
     37 
     38 #ifndef WINDOWS
     39 #include <unistd.h>
     40 #endif
     41 
     42 #include "mhd_has_in_name.h"
     43 
     44 #if defined(MHD_CPU_COUNT) && (MHD_CPU_COUNT + 0) < 2
     45 #undef MHD_CPU_COUNT
     46 #endif
     47 #if ! defined(MHD_CPU_COUNT)
     48 #define MHD_CPU_COUNT 2
     49 #endif
     50 
     51 #define HDR_CHUNKED_ENCODING MHD_HTTP_HEADER_TRANSFER_ENCODING ": chunked"
     52 #define RESP_FOOTER_NAME "Footer"
     53 #define RESP_FOOTER_VALUE "working"
     54 #define RESP_FOOTER RESP_FOOTER_NAME ": " RESP_FOOTER_VALUE
     55 
     56 #define RESP_BLOCK_SIZE 128
     57 #define RESP_BLOCK_QUANTIY 10
     58 #define RESP_SIZE (RESP_BLOCK_SIZE * RESP_BLOCK_QUANTIY)
     59 
     60 /**
     61  * Use "Connection: close" header?
     62  */
     63 static int conn_close;
     64 
     65 /**
     66  * Use static string response instead of callback-generated?
     67  */
     68 static int resp_string;
     69 
     70 /**
     71  * Use response with known size?
     72  */
     73 static int resp_sized;
     74 
     75 /**
     76  * Use empty (zero-sized) response?
     77  */
     78 static int resp_empty;
     79 
     80 /**
     81  * Force chunked response by response header?
     82  */
     83 static int chunked_forced;
     84 
     85 /**
     86  * MHD port used for testing
     87  */
     88 static uint16_t port_global;
     89 
     90 
     91 struct headers_check_result
     92 {
     93   int found_chunked;
     94   int found_footer;
     95 };
     96 
     97 static size_t
     98 lcurl_hdr_callback (char *buffer, size_t size, size_t nitems,
     99                     void *userdata)
    100 {
    101   const size_t data_size = size * nitems;
    102   struct headers_check_result *check_res =
    103     (struct headers_check_result *) userdata;
    104 
    105   if ((data_size == strlen (HDR_CHUNKED_ENCODING) + 2) &&
    106       (0 == memcmp (buffer, HDR_CHUNKED_ENCODING "\r\n", data_size)))
    107     check_res->found_chunked = 1;
    108   if ((data_size == strlen (RESP_FOOTER) + 2) &&
    109       (0 == memcmp (buffer, RESP_FOOTER "\r\n", data_size)))
    110     check_res->found_footer = 1;
    111 
    112   return data_size;
    113 }
    114 
    115 
    116 struct CBC
    117 {
    118   char *buf;
    119   size_t pos;
    120   size_t size;
    121 };
    122 
    123 
    124 static size_t
    125 copyBuffer (void *ptr,
    126             size_t size,
    127             size_t nmemb,
    128             void *ctx)
    129 {
    130   struct CBC *cbc = ctx;
    131 
    132   if (cbc->pos + size * nmemb > cbc->size)
    133     return 0;                   /* overflow */
    134   memcpy (&cbc->buf[cbc->pos], ptr, size * nmemb);
    135   cbc->pos += size * nmemb;
    136   return size * nmemb;
    137 }
    138 
    139 
    140 /**
    141  * MHD content reader callback that returns data in chunks.
    142  */
    143 static ssize_t
    144 crc (void *cls,
    145      uint64_t pos,
    146      char *buf,
    147      size_t max)
    148 {
    149   struct MHD_Response **responseptr = cls;
    150 
    151   if (resp_empty || (pos == RESP_SIZE - RESP_BLOCK_SIZE))
    152   { /* Add footer with the last block */
    153     if (MHD_YES != MHD_add_response_footer (*responseptr,
    154                                             RESP_FOOTER_NAME,
    155                                             RESP_FOOTER_VALUE))
    156       abort ();
    157   }
    158   if (resp_empty || (pos == RESP_SIZE))
    159     return MHD_CONTENT_READER_END_OF_STREAM;
    160 
    161   if (max < RESP_BLOCK_SIZE)
    162     abort ();                   /* should not happen in this testcase... */
    163   memset (buf,
    164           'A' + (char) (unsigned char) (pos / RESP_BLOCK_SIZE),
    165           RESP_BLOCK_SIZE);
    166   return RESP_BLOCK_SIZE;
    167 }
    168 
    169 
    170 /**
    171  * Dummy function that frees the "responseptr".
    172  */
    173 static void
    174 crcf (void *ptr)
    175 {
    176   free (ptr);
    177 }
    178 
    179 
    180 static enum MHD_Result
    181 ahc_echo (void *cls,
    182           struct MHD_Connection *connection,
    183           const char *url,
    184           const char *method,
    185           const char *version,
    186           const char *upload_data, size_t *upload_data_size, void **req_cls)
    187 {
    188   static int aptr;
    189   struct MHD_Response *response;
    190   enum MHD_Result ret;
    191 
    192   (void) cls;
    193   (void) url;
    194   (void) version;              /* Unused. Silent compiler warning. */
    195   (void) upload_data;
    196   (void) upload_data_size;     /* Unused. Silent compiler warning. */
    197 
    198   if (0 != strcmp (MHD_HTTP_METHOD_GET, method))
    199     return MHD_NO;              /* unexpected method */
    200   if (&aptr != *req_cls)
    201   {
    202     /* do never respond on first call */
    203     *req_cls = &aptr;
    204     return MHD_YES;
    205   }
    206   if (! resp_string)
    207   {
    208     struct MHD_Response **responseptr;
    209     responseptr = malloc (sizeof (struct MHD_Response *));
    210     if (NULL == responseptr)
    211       _exit (99);
    212 
    213     response = MHD_create_response_from_callback (resp_sized ?
    214                                                   RESP_SIZE : MHD_SIZE_UNKNOWN,
    215                                                   1024,
    216                                                   &crc,
    217                                                   responseptr,
    218                                                   &crcf);
    219     *responseptr = response;
    220   }
    221   else
    222   {
    223     if (! resp_empty)
    224     {
    225       size_t pos;
    226       static const size_t resp_size = RESP_SIZE;
    227       char *buf = malloc (resp_size);
    228       if (NULL == buf)
    229         _exit (99);
    230       for (pos = 0; pos < resp_size; pos += RESP_BLOCK_SIZE)
    231         memset (buf + pos,
    232                 'A' + (char) (unsigned char) (pos / RESP_BLOCK_SIZE),
    233                 RESP_BLOCK_SIZE);
    234 
    235       response = MHD_create_response_from_buffer_copy (resp_size, buf);
    236       free (buf);
    237     }
    238     else
    239       response = MHD_create_response_empty (MHD_RF_NONE);
    240   }
    241   if (NULL == response)
    242     abort ();
    243   if (chunked_forced)
    244   {
    245     if (MHD_NO == MHD_add_response_header (response,
    246                                            MHD_HTTP_HEADER_TRANSFER_ENCODING,
    247                                            "chunked"))
    248       abort ();
    249   }
    250   if (MHD_NO == MHD_add_response_header (response,
    251                                          MHD_HTTP_HEADER_TRAILER,
    252                                          RESP_FOOTER_NAME))
    253     abort ();
    254 
    255   if (resp_string || (resp_sized && resp_empty))
    256   {
    257     /* There is no chance to add footer later */
    258     if (MHD_YES != MHD_add_response_footer (response,
    259                                             RESP_FOOTER_NAME,
    260                                             RESP_FOOTER_VALUE))
    261       abort ();
    262   }
    263 
    264   ret = MHD_queue_response (connection,
    265                             MHD_HTTP_OK,
    266                             response);
    267   MHD_destroy_response (response);
    268   return ret;
    269 }
    270 
    271 
    272 static unsigned int
    273 validate (struct CBC cbc, unsigned int ebase)
    274 {
    275   int i;
    276   char buf[RESP_BLOCK_SIZE];
    277 
    278   if (resp_empty)
    279   {
    280     if (0 != cbc.pos)
    281     {
    282       fprintf (stderr,
    283                "Got %u bytes instead of zero!\n",
    284                (unsigned int) cbc.pos);
    285       return 1;
    286     }
    287     return 0;
    288   }
    289 
    290   if (cbc.pos != RESP_SIZE)
    291   {
    292     fprintf (stderr,
    293              "Got %u bytes instead of 1280!\n",
    294              (unsigned int) cbc.pos);
    295     return ebase;
    296   }
    297 
    298   for (i = 0; i < RESP_BLOCK_QUANTIY; i++)
    299   {
    300     memset (buf, 'A' + i, RESP_BLOCK_SIZE);
    301     if (0 != memcmp (buf, &cbc.buf[i * RESP_BLOCK_SIZE], RESP_BLOCK_SIZE))
    302     {
    303       fprintf (stderr,
    304                "Got  `%.*s'\nWant `%.*s'\n",
    305                RESP_BLOCK_SIZE, &cbc.buf[i * RESP_BLOCK_SIZE],
    306                RESP_BLOCK_SIZE, buf);
    307       return ebase * 2;
    308     }
    309   }
    310   return 0;
    311 }
    312 
    313 
    314 static unsigned int
    315 testInternalGet (void)
    316 {
    317   struct MHD_Daemon *d;
    318   CURL *c;
    319   char buf[2048];
    320   struct CBC cbc;
    321   CURLcode errornum;
    322   uint16_t port;
    323   struct curl_slist *h_list = NULL;
    324   struct headers_check_result hdr_check;
    325 
    326   port = port_global;
    327   cbc.buf = buf;
    328   cbc.size = 2048;
    329   cbc.pos = 0;
    330   d = MHD_start_daemon (MHD_USE_INTERNAL_POLLING_THREAD | MHD_USE_ERROR_LOG,
    331                         port, NULL, NULL, &ahc_echo, NULL, MHD_OPTION_END);
    332   if (d == NULL)
    333     return 1;
    334   if (0 == port)
    335   {
    336     const union MHD_DaemonInfo *dinfo;
    337     dinfo = MHD_get_daemon_info (d, MHD_DAEMON_INFO_BIND_PORT);
    338     if ((NULL == dinfo) || (0 == dinfo->port) )
    339     {
    340       MHD_stop_daemon (d); return 32;
    341     }
    342     port = dinfo->port;
    343     if (0 == port_global)
    344       port_global = port; /* Re-use the same port for all checks */
    345   }
    346   hdr_check.found_chunked = 0;
    347   hdr_check.found_footer = 0;
    348   c = curl_easy_init ();
    349   curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1/hello_world");
    350   curl_easy_setopt (c, CURLOPT_PORT, (long) port);
    351   curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
    352   curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
    353   curl_easy_setopt (c, CURLOPT_FAILONERROR, 1L);
    354   curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L);
    355   curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L);
    356   curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
    357   curl_easy_setopt (c, CURLOPT_HEADERFUNCTION, lcurl_hdr_callback);
    358   curl_easy_setopt (c, CURLOPT_HEADERDATA, &hdr_check);
    359   curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1L);
    360   if (conn_close)
    361   {
    362     h_list = curl_slist_append (h_list, "Connection: close");
    363     if (NULL == h_list)
    364       abort ();
    365     curl_easy_setopt (c, CURLOPT_HTTPHEADER, h_list);
    366   }
    367   if (CURLE_OK != (errornum = curl_easy_perform (c)))
    368   {
    369     fprintf (stderr,
    370              "curl_easy_perform failed: `%s'\n",
    371              curl_easy_strerror (errornum));
    372     curl_easy_cleanup (c);
    373     curl_slist_free_all (h_list);
    374     MHD_stop_daemon (d);
    375     return 2;
    376   }
    377   curl_easy_cleanup (c);
    378   curl_slist_free_all (h_list);
    379   MHD_stop_daemon (d);
    380   if (1 != hdr_check.found_chunked)
    381   {
    382     fprintf (stderr,
    383              "Chunked encoding header was not found in the response\n");
    384     return 8;
    385   }
    386   if (1 != hdr_check.found_footer)
    387   {
    388     fprintf (stderr,
    389              "The specified footer was not found in the response\n");
    390     return 16;
    391   }
    392   return validate (cbc, 4);
    393 }
    394 
    395 
    396 static unsigned int
    397 testMultithreadedGet (void)
    398 {
    399   struct MHD_Daemon *d;
    400   CURL *c;
    401   char buf[2048];
    402   struct CBC cbc;
    403   CURLcode errornum;
    404   uint16_t port;
    405   struct curl_slist *h_list = NULL;
    406   struct headers_check_result hdr_check;
    407 
    408   port = port_global;
    409   cbc.buf = buf;
    410   cbc.size = 2048;
    411   cbc.pos = 0;
    412   d = MHD_start_daemon (MHD_USE_THREAD_PER_CONNECTION
    413                         | MHD_USE_INTERNAL_POLLING_THREAD | MHD_USE_ERROR_LOG,
    414                         port, NULL, NULL, &ahc_echo, NULL, MHD_OPTION_END);
    415   if (d == NULL)
    416     return 16;
    417   if (0 == port)
    418   {
    419     const union MHD_DaemonInfo *dinfo;
    420     dinfo = MHD_get_daemon_info (d, MHD_DAEMON_INFO_BIND_PORT);
    421     if ((NULL == dinfo) || (0 == dinfo->port) )
    422     {
    423       MHD_stop_daemon (d); return 32;
    424     }
    425     port = dinfo->port;
    426     if (0 == port_global)
    427       port_global = port; /* Re-use the same port for all checks */
    428   }
    429   c = curl_easy_init ();
    430   curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1/hello_world");
    431   curl_easy_setopt (c, CURLOPT_PORT, (long) port);
    432   curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
    433   curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
    434   curl_easy_setopt (c, CURLOPT_FAILONERROR, 1L);
    435   curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L);
    436   curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
    437   curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L);
    438   curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1L);
    439   hdr_check.found_chunked = 0;
    440   hdr_check.found_footer = 0;
    441   curl_easy_setopt (c, CURLOPT_HEADERFUNCTION, lcurl_hdr_callback);
    442   curl_easy_setopt (c, CURLOPT_HEADERDATA, &hdr_check);
    443   if (conn_close)
    444   {
    445     h_list = curl_slist_append (h_list, "Connection: close");
    446     if (NULL == h_list)
    447       abort ();
    448     curl_easy_setopt (c, CURLOPT_HTTPHEADER, h_list);
    449   }
    450   if (CURLE_OK != (errornum = curl_easy_perform (c)))
    451   {
    452     fprintf (stderr,
    453              "curl_easy_perform failed: `%s'\n",
    454              curl_easy_strerror (errornum));
    455     curl_easy_cleanup (c);
    456     curl_slist_free_all (h_list);
    457     MHD_stop_daemon (d);
    458     return 32;
    459   }
    460   curl_easy_cleanup (c);
    461   curl_slist_free_all (h_list);
    462   MHD_stop_daemon (d);
    463   if (1 != hdr_check.found_chunked)
    464   {
    465     fprintf (stderr,
    466              "Chunked encoding header was not found in the response\n");
    467     return 8;
    468   }
    469   if (1 != hdr_check.found_footer)
    470   {
    471     fprintf (stderr,
    472              "The specified footer was not found in the response\n");
    473     return 16;
    474   }
    475   return validate (cbc, 64);
    476 }
    477 
    478 
    479 static unsigned int
    480 testMultithreadedPoolGet (void)
    481 {
    482   struct MHD_Daemon *d;
    483   CURL *c;
    484   char buf[2048];
    485   struct CBC cbc;
    486   CURLcode errornum;
    487   uint16_t port;
    488   struct curl_slist *h_list = NULL;
    489   struct headers_check_result hdr_check;
    490 
    491   port = port_global;
    492   cbc.buf = buf;
    493   cbc.size = 2048;
    494   cbc.pos = 0;
    495   d = MHD_start_daemon (MHD_USE_INTERNAL_POLLING_THREAD | MHD_USE_ERROR_LOG,
    496                         port, NULL, NULL, &ahc_echo, NULL,
    497                         MHD_OPTION_THREAD_POOL_SIZE, MHD_CPU_COUNT,
    498                         MHD_OPTION_END);
    499   if (d == NULL)
    500     return 16;
    501   if (0 == port)
    502   {
    503     const union MHD_DaemonInfo *dinfo;
    504     dinfo = MHD_get_daemon_info (d, MHD_DAEMON_INFO_BIND_PORT);
    505     if ((NULL == dinfo) || (0 == dinfo->port) )
    506     {
    507       MHD_stop_daemon (d); return 32;
    508     }
    509     port = dinfo->port;
    510     if (0 == port_global)
    511       port_global = port; /* Re-use the same port for all checks */
    512   }
    513   c = curl_easy_init ();
    514   curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1/hello_world");
    515   curl_easy_setopt (c, CURLOPT_PORT, (long) port);
    516   curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
    517   curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
    518   curl_easy_setopt (c, CURLOPT_FAILONERROR, 1L);
    519   curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L);
    520   curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
    521   curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L);
    522   curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1L);
    523   hdr_check.found_chunked = 0;
    524   hdr_check.found_footer = 0;
    525   curl_easy_setopt (c, CURLOPT_HEADERFUNCTION, lcurl_hdr_callback);
    526   curl_easy_setopt (c, CURLOPT_HEADERDATA, &hdr_check);
    527   if (conn_close)
    528   {
    529     h_list = curl_slist_append (h_list, "Connection: close");
    530     if (NULL == h_list)
    531       abort ();
    532     curl_easy_setopt (c, CURLOPT_HTTPHEADER, h_list);
    533   }
    534   if (CURLE_OK != (errornum = curl_easy_perform (c)))
    535   {
    536     fprintf (stderr,
    537              "curl_easy_perform failed: `%s'\n",
    538              curl_easy_strerror (errornum));
    539     curl_easy_cleanup (c);
    540     curl_slist_free_all (h_list);
    541     MHD_stop_daemon (d);
    542     return 32;
    543   }
    544   curl_easy_cleanup (c);
    545   curl_slist_free_all (h_list);
    546   MHD_stop_daemon (d);
    547   if (1 != hdr_check.found_chunked)
    548   {
    549     fprintf (stderr,
    550              "Chunked encoding header was not found in the response\n");
    551     return 8;
    552   }
    553   if (1 != hdr_check.found_footer)
    554   {
    555     fprintf (stderr,
    556              "The specified footer was not found in the response\n");
    557     return 16;
    558   }
    559   return validate (cbc, 64);
    560 }
    561 
    562 
    563 static unsigned int
    564 testExternalGet (void)
    565 {
    566   struct MHD_Daemon *d;
    567   CURL *c;
    568   char buf[2048];
    569   struct CBC cbc;
    570   CURLM *multi;
    571   CURLMcode mret;
    572   fd_set rs;
    573   fd_set ws;
    574   fd_set es;
    575   MHD_socket maxsock;
    576 #ifdef MHD_WINSOCK_SOCKETS
    577   int maxposixs; /* Max socket number unused on W32 */
    578 #else  /* MHD_POSIX_SOCKETS */
    579 #define maxposixs maxsock
    580 #endif /* MHD_POSIX_SOCKETS */
    581   int running;
    582   struct CURLMsg *msg;
    583   time_t start;
    584   struct timeval tv;
    585   uint16_t port;
    586   struct curl_slist *h_list = NULL;
    587   struct headers_check_result hdr_check;
    588 
    589   port = port_global;
    590   multi = NULL;
    591   cbc.buf = buf;
    592   cbc.size = 2048;
    593   cbc.pos = 0;
    594   d = MHD_start_daemon (MHD_USE_ERROR_LOG | MHD_USE_NO_THREAD_SAFETY,
    595                         port, NULL, NULL, &ahc_echo, NULL,
    596                         MHD_OPTION_APP_FD_SETSIZE, (int) FD_SETSIZE,
    597                         MHD_OPTION_END);
    598   if (d == NULL)
    599     return 256;
    600   if (0 == port)
    601   {
    602     const union MHD_DaemonInfo *dinfo;
    603     dinfo = MHD_get_daemon_info (d, MHD_DAEMON_INFO_BIND_PORT);
    604     if ((NULL == dinfo) || (0 == dinfo->port) )
    605     {
    606       MHD_stop_daemon (d); return 32;
    607     }
    608     port = dinfo->port;
    609     if (0 == port_global)
    610       port_global = port; /* Re-use the same port for all checks */
    611   }
    612   c = curl_easy_init ();
    613   curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1/hello_world");
    614   curl_easy_setopt (c, CURLOPT_PORT, (long) port);
    615   curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
    616   curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
    617   curl_easy_setopt (c, CURLOPT_FAILONERROR, 1L);
    618   curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
    619   curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L);
    620   curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 5L);
    621   curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1L);
    622   hdr_check.found_chunked = 0;
    623   hdr_check.found_footer = 0;
    624   curl_easy_setopt (c, CURLOPT_HEADERFUNCTION, lcurl_hdr_callback);
    625   curl_easy_setopt (c, CURLOPT_HEADERDATA, &hdr_check);
    626   if (conn_close)
    627   {
    628     h_list = curl_slist_append (h_list, "Connection: close");
    629     if (NULL == h_list)
    630       abort ();
    631     curl_easy_setopt (c, CURLOPT_HTTPHEADER, h_list);
    632   }
    633 
    634   multi = curl_multi_init ();
    635   if (multi == NULL)
    636   {
    637     curl_easy_cleanup (c);
    638     curl_slist_free_all (h_list);
    639     MHD_stop_daemon (d);
    640     return 512;
    641   }
    642   mret = curl_multi_add_handle (multi, c);
    643   if (mret != CURLM_OK)
    644   {
    645     curl_multi_cleanup (multi);
    646     curl_easy_cleanup (c);
    647     curl_slist_free_all (h_list);
    648     MHD_stop_daemon (d);
    649     return 1024;
    650   }
    651   start = time (NULL);
    652   while ((time (NULL) - start < 5) && (multi != NULL))
    653   {
    654     maxsock = MHD_INVALID_SOCKET;
    655     maxposixs = -1;
    656     FD_ZERO (&rs);
    657     FD_ZERO (&ws);
    658     FD_ZERO (&es);
    659     curl_multi_perform (multi, &running);
    660     mret = curl_multi_fdset (multi, &rs, &ws, &es, &maxposixs);
    661     if (mret != CURLM_OK)
    662     {
    663       curl_multi_remove_handle (multi, c);
    664       curl_multi_cleanup (multi);
    665       curl_easy_cleanup (c);
    666       curl_slist_free_all (h_list);
    667       MHD_stop_daemon (d);
    668       return 2048;
    669     }
    670     if (MHD_YES != MHD_get_fdset (d, &rs, &ws, &es, &maxsock))
    671     {
    672       curl_multi_remove_handle (multi, c);
    673       curl_multi_cleanup (multi);
    674       curl_easy_cleanup (c);
    675       curl_slist_free_all (h_list);
    676       MHD_stop_daemon (d);
    677       return 4096;
    678     }
    679     tv.tv_sec = 0;
    680     tv.tv_usec = 1000;
    681     if (-1 == select (maxposixs + 1, &rs, &ws, &es, &tv))
    682     {
    683 #ifdef MHD_POSIX_SOCKETS
    684       if (EINTR != errno)
    685       {
    686         fprintf (stderr, "Unexpected select() error: %d. Line: %d\n",
    687                  (int) errno, __LINE__);
    688         fflush (stderr);
    689         exit (99);
    690       }
    691 #else
    692       if ((WSAEINVAL != WSAGetLastError ()) ||
    693           (0 != rs.fd_count) || (0 != ws.fd_count) || (0 != es.fd_count) )
    694       {
    695         fprintf (stderr, "Unexpected select() error: %d. Line: %d\n",
    696                  (int) WSAGetLastError (), __LINE__);
    697         fflush (stderr);
    698         exit (99);
    699       }
    700       Sleep (1);
    701 #endif
    702     }
    703     curl_multi_perform (multi, &running);
    704     if (0 == running)
    705     {
    706       int pending;
    707       int curl_fine = 0;
    708       while (NULL != (msg = curl_multi_info_read (multi, &pending)))
    709       {
    710         if (msg->msg == CURLMSG_DONE)
    711         {
    712           if (msg->data.result == CURLE_OK)
    713             curl_fine = 1;
    714           else
    715           {
    716             fprintf (stderr,
    717                      "%s failed at %s:%d: `%s'\n",
    718                      "curl_multi_perform",
    719                      __FILE__,
    720                      __LINE__, curl_easy_strerror (msg->data.result));
    721             abort ();
    722           }
    723         }
    724       }
    725       if (! curl_fine)
    726       {
    727         fprintf (stderr, "libcurl haven't returned OK code\n");
    728         abort ();
    729       }
    730       curl_multi_remove_handle (multi, c);
    731       curl_multi_cleanup (multi);
    732       curl_easy_cleanup (c);
    733       curl_slist_free_all (h_list);
    734       h_list = NULL;
    735       c = NULL;
    736       multi = NULL;
    737     }
    738     MHD_run (d);
    739   }
    740   MHD_stop_daemon (d);
    741   if (multi != NULL)
    742   {
    743     curl_multi_remove_handle (multi, c);
    744     curl_easy_cleanup (c);
    745     curl_multi_cleanup (multi);
    746   }
    747   curl_slist_free_all (h_list);
    748   if (1 != hdr_check.found_chunked)
    749   {
    750     fprintf (stderr,
    751              "Chunked encoding header was not found in the response\n");
    752     return 8;
    753   }
    754   if (1 != hdr_check.found_footer)
    755   {
    756     fprintf (stderr,
    757              "The specified footer was not found in the response\n");
    758     return 16;
    759   }
    760   return validate (cbc, 8192);
    761 }
    762 
    763 
    764 int
    765 main (int argc, char *const *argv)
    766 {
    767   unsigned int errorCount = 0;
    768   (void) argc; (void) argv; /* Unused. Silent compiler warning. */
    769 
    770   if (0 != curl_global_init (CURL_GLOBAL_WIN32))
    771     return 2;
    772   conn_close = has_in_name (argv[0], "_close");
    773   resp_string = has_in_name (argv[0], "_string");
    774   resp_sized = has_in_name (argv[0], "_sized");
    775   resp_empty = has_in_name (argv[0], "_empty");
    776   chunked_forced = has_in_name (argv[0], "_forced");
    777   if (resp_string)
    778     resp_sized = ! 0;
    779   if (resp_sized)
    780     chunked_forced = ! 0;
    781 
    782   if (MHD_NO != MHD_is_feature_supported (MHD_FEATURE_AUTODETECT_BIND_PORT))
    783     port_global = 0;
    784   else
    785   {
    786     port_global = 4100;
    787     if (conn_close)
    788       port_global += 1 << 0;
    789     if (resp_string)
    790       port_global += 1 << 1;
    791     if (resp_sized)
    792       port_global += 1 << 2;
    793     if (resp_empty)
    794       port_global += 1 << 3;
    795     if (chunked_forced)
    796       port_global += 1 << 4;
    797   }
    798 
    799   if (MHD_YES == MHD_is_feature_supported (MHD_FEATURE_THREADS))
    800   {
    801     errorCount += testInternalGet ();
    802     errorCount += testMultithreadedGet ();
    803     errorCount += testMultithreadedPoolGet ();
    804   }
    805   errorCount += testExternalGet ();
    806   if (errorCount != 0)
    807     fprintf (stderr, "Error (code: %u)\n", errorCount);
    808   curl_global_cleanup ();
    809   return (0 == errorCount) ? 0 : 1;       /* 0 == pass */
    810 }