libmicrohttpd

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

test_get_chunked.c (22226B)


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