libmicrohttpd

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

test_put_header_fold.c (39582B)


      1 /*
      2      This file is part of GNU libmicrohttpd
      3      Copyright (C) 2010 Christian Grothoff
      4      Copyright (C) 2016-2022 Evgeny Grin (Karlson2k)
      5 
      6      GNU 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      GNU 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 testcurl/test_put_header_fold.c
     24  * @brief  Testcase for requests with header fold
     25  * @author Karlson2k (Evgeny Grin)
     26  */
     27 
     28 #include "mhd_options.h"
     29 #include "platform.h"
     30 #include <curl/curl.h>
     31 #include <microhttpd.h>
     32 #include <stdlib.h>
     33 #include <string.h>
     34 #include <time.h>
     35 #include <errno.h>
     36 
     37 #ifndef _WIN32
     38 #include <sys/socket.h>
     39 #include <unistd.h>
     40 #endif
     41 
     42 #include "internal.h"
     43 #include "mhd_has_param.h"
     44 #include "mhd_has_in_name.h"
     45 
     46 /* Turn any MHD_PANIC() or failing mhd_assert() reached from this
     47    test into a marked, classifiable test error (TESTING.md, P5). */
     48 #include "mhd_panic_tripwire.h"
     49 
     50 /* The next macros are borrowed from memorypool.c
     51    Keep them in sync! */
     52 
     53 /**
     54  * Align to 2x word size (as GNU libc does).
     55  */
     56 #define ALIGN_SIZE (2 * sizeof(void*))
     57 /**
     58  * Round up 'n' to a multiple of ALIGN_SIZE.
     59  */
     60 #define ROUND_TO_ALIGN(n) (((n) + (ALIGN_SIZE - 1)) \
     61                            / (ALIGN_SIZE) *(ALIGN_SIZE))
     62 #ifndef MHD_ASAN_POISON_ACTIVE
     63 #define _MHD_RED_ZONE_SIZE (0)
     64 #else  /* MHD_ASAN_POISON_ACTIVE */
     65 #define _MHD_RED_ZONE_SIZE (ALIGN_SIZE)
     66 #endif /* MHD_ASAN_POISON_ACTIVE */
     67 
     68 #define ROUND_TO_ALIGN_PLUS_RED_ZONE(n) (ROUND_TO_ALIGN(n) + _MHD_RED_ZONE_SIZE)
     69 
     70 /* The previous macros are borrowed from memorypool.c
     71    Keep them in sync! */
     72 
     73 #ifndef MHD_STATICSTR_LEN_
     74 /**
     75  * Determine length of static string / macro strings at compile time.
     76  */
     77 #define MHD_STATICSTR_LEN_(macro) (sizeof(macro) / sizeof(char) - 1)
     78 #endif /* ! MHD_STATICSTR_LEN_ */
     79 
     80 #ifndef CURL_VERSION_BITS
     81 #define CURL_VERSION_BITS(x,y,z) ((x) << 16 | (y) << 8 | (z))
     82 #endif /* ! CURL_VERSION_BITS */
     83 #ifndef CURL_AT_LEAST_VERSION
     84 #define CURL_AT_LEAST_VERSION(x,y,z) \
     85   (LIBCURL_VERSION_NUM >= CURL_VERSION_BITS (x, y, z))
     86 #endif /* ! CURL_AT_LEAST_VERSION */
     87 
     88 #ifndef _MHD_INSTRMACRO
     89 /* Quoted macro parameter */
     90 #define _MHD_INSTRMACRO(a) #a
     91 #endif /* ! _MHD_INSTRMACRO */
     92 #ifndef _MHD_STRMACRO
     93 /* Quoted expanded macro parameter */
     94 #define _MHD_STRMACRO(a) _MHD_INSTRMACRO (a)
     95 #endif /* ! _MHD_STRMACRO */
     96 
     97 #if defined(HAVE___FUNC__)
     98 #define externalErrorExit(ignore) \
     99   _externalErrorExit_func (NULL, __func__, __LINE__)
    100 #define externalErrorExitDesc(errDesc) \
    101   _externalErrorExit_func (errDesc, __func__, __LINE__)
    102 #define libcurlErrorExit(ignore) \
    103   _libcurlErrorExit_func (NULL, __func__, __LINE__)
    104 #define libcurlErrorExitDesc(errDesc) \
    105   _libcurlErrorExit_func (errDesc, __func__, __LINE__)
    106 #define mhdErrorExit(ignore) \
    107   _mhdErrorExit_func (NULL, __func__, __LINE__)
    108 #define mhdErrorExitDesc(errDesc) \
    109   _mhdErrorExit_func (errDesc, __func__, __LINE__)
    110 #define checkCURLE_OK(libcurlcall) \
    111   _checkCURLE_OK_func ((libcurlcall), _MHD_STRMACRO (libcurlcall), \
    112                        __func__, __LINE__)
    113 #elif defined(HAVE___FUNCTION__)
    114 #define externalErrorExit(ignore) \
    115   _externalErrorExit_func (NULL, __FUNCTION__, __LINE__)
    116 #define externalErrorExitDesc(errDesc) \
    117   _externalErrorExit_func (errDesc, __FUNCTION__, __LINE__)
    118 #define libcurlErrorExit(ignore) \
    119   _libcurlErrorExit_func (NULL, __FUNCTION__, __LINE__)
    120 #define libcurlErrorExitDesc(errDesc) \
    121   _libcurlErrorExit_func (errDesc, __FUNCTION__, __LINE__)
    122 #define mhdErrorExit(ignore) \
    123   _mhdErrorExit_func (NULL, __FUNCTION__, __LINE__)
    124 #define mhdErrorExitDesc(errDesc) \
    125   _mhdErrorExit_func (errDesc, __FUNCTION__, __LINE__)
    126 #define checkCURLE_OK(libcurlcall) \
    127   _checkCURLE_OK_func ((libcurlcall), _MHD_STRMACRO (libcurlcall), \
    128                        __FUNCTION__, __LINE__)
    129 #else
    130 #define externalErrorExit(ignore) _externalErrorExit_func (NULL, NULL, __LINE__)
    131 #define externalErrorExitDesc(errDesc) \
    132   _externalErrorExit_func (errDesc, NULL, __LINE__)
    133 #define libcurlErrorExit(ignore) _libcurlErrorExit_func (NULL, NULL, __LINE__)
    134 #define libcurlErrorExitDesc(errDesc) \
    135   _libcurlErrorExit_func (errDesc, NULL, __LINE__)
    136 #define mhdErrorExit(ignore) _mhdErrorExit_func (NULL, NULL, __LINE__)
    137 #define mhdErrorExitDesc(errDesc) _mhdErrorExit_func (errDesc, NULL, __LINE__)
    138 #define checkCURLE_OK(libcurlcall) \
    139   _checkCURLE_OK_func ((libcurlcall), _MHD_STRMACRO (libcurlcall), NULL, \
    140                        __LINE__)
    141 #endif
    142 
    143 
    144 _MHD_NORETURN static void
    145 _externalErrorExit_func (const char *errDesc, const char *funcName, int lineNum)
    146 {
    147   fflush (stdout);
    148   if ((NULL != errDesc) && (0 != errDesc[0]))
    149     fprintf (stderr, "%s", errDesc);
    150   else
    151     fprintf (stderr, "System or external library call failed");
    152   if ((NULL != funcName) && (0 != funcName[0]))
    153     fprintf (stderr, " in %s", funcName);
    154   if (0 < lineNum)
    155     fprintf (stderr, " at line %d", lineNum);
    156 
    157   fprintf (stderr, ".\nLast errno value: %d (%s)\n", (int) errno,
    158            strerror (errno));
    159 #ifdef MHD_WINSOCK_SOCKETS
    160   fprintf (stderr, "WSAGetLastError() value: %d\n", (int) WSAGetLastError ());
    161 #endif /* MHD_WINSOCK_SOCKETS */
    162   fflush (stderr);
    163   exit (99);
    164 }
    165 
    166 
    167 static char libcurl_errbuf[CURL_ERROR_SIZE] = "";
    168 
    169 _MHD_NORETURN static void
    170 _libcurlErrorExit_func (const char *errDesc, const char *funcName, int lineNum)
    171 {
    172   fflush (stdout);
    173   if ((NULL != errDesc) && (0 != errDesc[0]))
    174     fprintf (stderr, "%s", errDesc);
    175   else
    176     fprintf (stderr, "CURL library call failed");
    177   if ((NULL != funcName) && (0 != funcName[0]))
    178     fprintf (stderr, " in %s", funcName);
    179   if (0 < lineNum)
    180     fprintf (stderr, " at line %d", lineNum);
    181 
    182   fprintf (stderr, ".\nLast errno value: %d (%s)\n", (int) errno,
    183            strerror (errno));
    184 #ifdef MHD_WINSOCK_SOCKETS
    185   fprintf (stderr, "WSAGetLastError() value: %d\n", (int) WSAGetLastError ());
    186 #endif /* MHD_WINSOCK_SOCKETS */
    187   if (0 != libcurl_errbuf[0])
    188     fprintf (stderr, "Last libcurl error description: %s\n", libcurl_errbuf);
    189 
    190   fflush (stderr);
    191   exit (99);
    192 }
    193 
    194 
    195 _MHD_NORETURN static void
    196 _mhdErrorExit_func (const char *errDesc, const char *funcName, int lineNum)
    197 {
    198   fflush (stdout);
    199   if ((NULL != errDesc) && (0 != errDesc[0]))
    200     fprintf (stderr, "%s", errDesc);
    201   else
    202     fprintf (stderr, "MHD unexpected error");
    203   if ((NULL != funcName) && (0 != funcName[0]))
    204     fprintf (stderr, " in %s", funcName);
    205   if (0 < lineNum)
    206     fprintf (stderr, " at line %d", lineNum);
    207 
    208   fprintf (stderr, ".\nLast errno value: %d (%s)\n", (int) errno,
    209            strerror (errno));
    210 #ifdef MHD_WINSOCK_SOCKETS
    211   fprintf (stderr, "WSAGetLastError() value: %d\n", (int) WSAGetLastError ());
    212 #endif /* MHD_WINSOCK_SOCKETS */
    213 
    214   fflush (stderr);
    215   exit (8);
    216 }
    217 
    218 
    219 /* Could be increased to facilitate debugging */
    220 #define TIMEOUTS_VAL 5
    221 
    222 #define TEST_UPLOAD_DATA_SIZE 2048U
    223 
    224 #define EXPECTED_URI_BASE_PATH  "/"
    225 
    226 #define URL_SCHEME "http:/" "/"
    227 
    228 #define URL_HOST "127.0.0.1"
    229 
    230 #define URL_SCHEME_HOST_PATH URL_SCHEME URL_HOST EXPECTED_URI_BASE_PATH
    231 
    232 #define RP_HEADER1_NAME "First"
    233 #define RP_HEADER1_VALUE "1st"
    234 #define RP_HEADER1 RP_HEADER1_NAME ": " RP_HEADER1_VALUE
    235 #define RP_HEADER1_CRLF RP_HEADER1 "\r\n"
    236 #define RP_HEADER2_NAME "Normal"
    237 #define RP_HEADER2_VALUE "it's fine"
    238 #define RP_HEADER2 RP_HEADER2_NAME ": " RP_HEADER2_VALUE
    239 #define RP_HEADER2_CRLF RP_HEADER2 "\r\n"
    240 
    241 #define HDR_FOLD "\r\n "
    242 #define RQ_HEADER1_NAME RP_HEADER1_NAME
    243 #define RQ_HEADER1_VALUE RP_HEADER1_VALUE
    244 #define RQ_HEADER1 RQ_HEADER1_NAME ": " RQ_HEADER1_VALUE
    245 #define RQ_HEADER2_NAME "Folded"
    246 #define RQ_HEADER2_VALUE_S "start"
    247 #define RQ_HEADER2_VALUE_E "end"
    248 #define RQ_HEADER2_VALUE \
    249   RQ_HEADER2_VALUE_S HDR_FOLD RQ_HEADER2_VALUE_E
    250 #define RQ_HEADER2_VALUE_DF \
    251   RQ_HEADER2_VALUE_S HDR_FOLD HDR_FOLD RQ_HEADER2_VALUE_E
    252 #define RQ_HEADER2 RQ_HEADER2_NAME ": " RQ_HEADER2_VALUE
    253 #define RQ_HEADER2_DF RQ_HEADER2_NAME ": " RQ_HEADER2_VALUE_DF
    254 #define RQ_HEADER3_NAME RP_HEADER2_NAME
    255 #define RQ_HEADER3_VALUE RP_HEADER2_VALUE
    256 #define RQ_HEADER3 RQ_HEADER3_NAME ": " RQ_HEADER3_VALUE
    257 
    258 /**
    259  * The number of request headers: 3 custom headers + 2 automatic headers
    260  */
    261 #define RQ_NUM_HEADERS (3 + 2)
    262 /**
    263  * The extra size in the memory pool for pointers to the headers
    264  */
    265 #define HEADERS_POINTERS_SIZE \
    266   RQ_NUM_HEADERS * \
    267   ROUND_TO_ALIGN_PLUS_RED_ZONE(sizeof(struct MHD_HTTP_Req_Header))
    268 
    269 #define PAGE \
    270   "<html><head><title>libmicrohttpd demo page</title></head>" \
    271   "<body>Success!</body></html>"
    272 
    273 /* Global parameters */
    274 static int verbose;
    275 static int oneone;                  /**< If false use HTTP/1.0 for requests*/
    276 static int use_get;
    277 static int use_put;
    278 static int use_put_large;
    279 static int use_double_fold;
    280 static int use_hdr_last; /**< If non-zero, folded header is placed last */
    281 static int use_hdr_large; /**< If non-zero, folded header is large */
    282 
    283 /* Static data */
    284 static struct curl_slist *libcurl_headers = NULL;
    285 
    286 static char *put_data = NULL;
    287 
    288 /**
    289  * Initialise headers for libcurl
    290  *
    291  * @return non-zero if succeed,
    292  *         zero if failed
    293  */
    294 static void
    295 libcurl_headers_init (void)
    296 {
    297   libcurl_headers = curl_slist_append (NULL, RQ_HEADER1);
    298   if (NULL == libcurl_headers)
    299     libcurlErrorExitDesc ("curl_slist_append() failed");
    300 
    301   if (use_hdr_last)
    302   {
    303     libcurl_headers = curl_slist_append (libcurl_headers, RQ_HEADER3);
    304     if (NULL == libcurl_headers)
    305       libcurlErrorExitDesc ("curl_slist_append() failed");
    306   }
    307 
    308   if (! use_hdr_large)
    309   {
    310     if (! use_double_fold)
    311       libcurl_headers = curl_slist_append (libcurl_headers, RQ_HEADER2);
    312     else
    313       libcurl_headers = curl_slist_append (libcurl_headers, RQ_HEADER2_DF);
    314     if (NULL == libcurl_headers)
    315       libcurlErrorExitDesc ("curl_slist_append() failed");
    316   }
    317   else
    318   {
    319     char *buf;
    320     size_t pos;
    321     buf = malloc (TEST_UPLOAD_DATA_SIZE + 1);
    322     if (NULL == buf)
    323       externalErrorExitDesc ("malloc() failed");
    324     pos = 0;
    325     memcpy (buf, RQ_HEADER2_NAME, MHD_STATICSTR_LEN_ (RQ_HEADER2_NAME));
    326     pos += MHD_STATICSTR_LEN_ (RQ_HEADER2_NAME);
    327     buf[pos++] = ':';
    328     buf[pos++] = ' ';
    329     memcpy (buf + pos,
    330             RQ_HEADER2_VALUE_S, MHD_STATICSTR_LEN_ (RQ_HEADER2_VALUE_S));
    331     pos += MHD_STATICSTR_LEN_ (RQ_HEADER2_VALUE_S);
    332     memcpy (buf + pos, HDR_FOLD, MHD_STATICSTR_LEN_ (HDR_FOLD));
    333     pos += MHD_STATICSTR_LEN_ (HDR_FOLD);
    334     if (use_double_fold)
    335     {
    336       memcpy (buf + pos, HDR_FOLD, MHD_STATICSTR_LEN_ (HDR_FOLD));
    337       pos += MHD_STATICSTR_LEN_ (HDR_FOLD);
    338     }
    339     memset (buf + pos, 'a',
    340             TEST_UPLOAD_DATA_SIZE - pos
    341             - MHD_STATICSTR_LEN_ (RQ_HEADER2_VALUE_E) - 1);
    342     pos += TEST_UPLOAD_DATA_SIZE - pos
    343            - MHD_STATICSTR_LEN_ (RQ_HEADER2_VALUE_E) - 1;
    344     buf[pos++] = ' ';
    345     memcpy (buf + pos,
    346             RQ_HEADER2_VALUE_E, MHD_STATICSTR_LEN_ (RQ_HEADER2_VALUE_E));
    347     pos += MHD_STATICSTR_LEN_ (RQ_HEADER2_VALUE_E);
    348     if (TEST_UPLOAD_DATA_SIZE != pos)
    349       externalErrorExitDesc ("Position miscalculation");
    350     buf[pos] = 0;
    351 
    352     libcurl_headers = curl_slist_append (libcurl_headers, buf);
    353     if (NULL == libcurl_headers)
    354       libcurlErrorExitDesc ("curl_slist_append() failed");
    355 
    356     free (buf);
    357   }
    358 
    359   if (! use_hdr_last)
    360   {
    361     libcurl_headers = curl_slist_append (libcurl_headers, RQ_HEADER3);
    362     if (NULL == libcurl_headers)
    363       libcurlErrorExitDesc ("curl_slist_append() failed");
    364   }
    365 }
    366 
    367 
    368 static void
    369 init_put_data (void)
    370 {
    371   size_t i;
    372   put_data = malloc (TEST_UPLOAD_DATA_SIZE + 1);
    373   if (NULL == put_data)
    374     externalErrorExit ();
    375 
    376   for (i = 0; i < (TEST_UPLOAD_DATA_SIZE - 1); ++i)
    377   {
    378     if (0 == (i % 7))
    379       put_data[i] = ' ';
    380     else if (0 == (i % 47))
    381       put_data[i] = '\n';
    382     else if (0 == (i % 11))
    383       put_data[i] = (char) ('A' + i % ('Z' - 'A' + 1));
    384     else
    385       put_data[i] = (char) ('a' + i % ('z' - 'a' + 1));
    386   }
    387   put_data[TEST_UPLOAD_DATA_SIZE - 1] = '\n';
    388   put_data[TEST_UPLOAD_DATA_SIZE] = 0;
    389 }
    390 
    391 
    392 static void
    393 test_global_init (void)
    394 {
    395   libcurl_errbuf[0] = 0;
    396 
    397   if (0 != curl_global_init (CURL_GLOBAL_WIN32))
    398     externalErrorExit ();
    399 
    400   init_put_data ();
    401   libcurl_headers_init ();
    402 }
    403 
    404 
    405 static void
    406 test_global_cleanup (void)
    407 {
    408   curl_slist_free_all (libcurl_headers);
    409   curl_global_cleanup ();
    410   if (NULL != put_data)
    411     free (put_data);
    412   put_data = NULL;
    413 }
    414 
    415 
    416 struct headers_check_result
    417 {
    418   unsigned int expected_size;
    419   int header1_found;
    420   int header2_found;
    421   unsigned int size_found;
    422   unsigned int size_broken_found;
    423 };
    424 
    425 static size_t
    426 lcurl_hdr_callback (char *buffer, size_t size, size_t nitems,
    427                     void *userdata)
    428 {
    429   const size_t data_size = size * nitems;
    430   struct headers_check_result *check_res =
    431     (struct headers_check_result *) userdata;
    432 
    433   if ((MHD_STATICSTR_LEN_ (RP_HEADER1_CRLF) == data_size) &&
    434       (0 == memcmp (RP_HEADER1_CRLF, buffer, data_size)))
    435     check_res->header1_found++;
    436   else if ((MHD_STATICSTR_LEN_ (RP_HEADER2_CRLF) == data_size) &&
    437            (0 == memcmp (RP_HEADER2_CRLF, buffer, data_size)))
    438     check_res->header2_found++;
    439   else if ((MHD_STATICSTR_LEN_ (MHD_HTTP_HEADER_CONTENT_LENGTH ": ")
    440             < data_size) &&
    441            (0 ==
    442             memcmp (MHD_HTTP_HEADER_CONTENT_LENGTH ": ", buffer,
    443                     MHD_STATICSTR_LEN_ (MHD_HTTP_HEADER_CONTENT_LENGTH ": "))))
    444   {
    445     char cmpbuf[256];
    446     int res;
    447     const unsigned int numbers_pos =
    448       MHD_STATICSTR_LEN_ (MHD_HTTP_HEADER_CONTENT_LENGTH ": ");
    449     res = snprintf (cmpbuf, sizeof(cmpbuf), "%u", check_res->expected_size);
    450     if ((res <= 0) || (res > ((int) (sizeof(cmpbuf) - 1))))
    451       externalErrorExit ();
    452     if (data_size - numbers_pos <= 2)
    453     {
    454       fprintf (stderr, "Broken Content-Length.\n");
    455       check_res->size_broken_found++;
    456     }
    457     else if ((((size_t) res + 2) != data_size - numbers_pos) ||
    458              (0 != memcmp (buffer + numbers_pos, cmpbuf, (size_t) res)))
    459     {
    460       fprintf (stderr, "Wrong Content-Length. "
    461                "Expected: %u. "
    462                "Received: %.*s.\n",
    463                check_res->expected_size,
    464                (int) (data_size - numbers_pos - 2),
    465                buffer + numbers_pos);
    466       check_res->size_broken_found++;
    467     }
    468     else if (0 != memcmp ("\r\n", buffer + data_size - 2, 2))
    469     {
    470       fprintf (stderr, "The Content-Length header is not "
    471                "terminated by CRLF.\n");
    472       check_res->size_broken_found++;
    473     }
    474     else
    475       check_res->size_found++;
    476   }
    477 
    478   return data_size;
    479 }
    480 
    481 
    482 struct CBC
    483 {
    484   /* Upload members */
    485   size_t up_pos;
    486   size_t up_size;
    487   /* Download members */
    488   char *dn_buf;
    489   size_t dn_pos;
    490   size_t dn_buf_size;
    491 };
    492 
    493 
    494 static size_t
    495 copyBuffer (void *ptr,
    496             size_t size,
    497             size_t nmemb,
    498             void *ctx)
    499 {
    500   struct CBC *cbc = ctx;
    501 
    502   if (cbc->dn_pos + size * nmemb > cbc->dn_buf_size)
    503     return 0;                   /* overflow */
    504   memcpy (&cbc->dn_buf[cbc->dn_pos], ptr, size * nmemb);
    505   cbc->dn_pos += size * nmemb;
    506   return size * nmemb;
    507 }
    508 
    509 
    510 static size_t
    511 libcurlUploadDataCB (void *stream, size_t item_size, size_t nitems, void *ctx)
    512 {
    513   size_t to_fill;
    514   struct CBC *cbc = ctx;
    515 
    516   to_fill = cbc->up_size - cbc->up_pos;
    517   if (to_fill > item_size * nitems)
    518     to_fill = item_size * nitems;
    519 
    520   /* Avoid libcurl magic numbers */
    521 #ifdef CURL_READFUNC_PAUSE
    522   if (CURL_READFUNC_PAUSE == to_fill)
    523     to_fill = (CURL_READFUNC_PAUSE - 2);
    524 #endif /* CURL_READFUNC_PAUSE */
    525 #ifdef CURL_READFUNC_ABORT
    526   if (CURL_READFUNC_ABORT == to_fill)
    527     to_fill = (CURL_READFUNC_ABORT - 1);
    528 #endif /* CURL_READFUNC_ABORT */
    529 
    530   memcpy (stream, put_data + cbc->up_pos, to_fill);
    531   cbc->up_pos += to_fill;
    532   return to_fill;
    533 }
    534 
    535 
    536 static int
    537 libcurl_debug_cb (CURL *handle,
    538                   curl_infotype type,
    539                   char *data,
    540                   size_t size,
    541                   void *userptr)
    542 {
    543   static const char excess_mark[] = "Excess found";
    544   static const size_t excess_mark_len = MHD_STATICSTR_LEN_ (excess_mark);
    545 
    546   (void) handle;
    547   (void) userptr;
    548 
    549 #ifdef _DEBUG
    550   switch (type)
    551   {
    552   case CURLINFO_TEXT:
    553     fprintf (stderr, "* %.*s", (int) size, data);
    554     break;
    555   case CURLINFO_HEADER_IN:
    556     fprintf (stderr, "< %.*s", (int) size, data);
    557     break;
    558   case CURLINFO_HEADER_OUT:
    559     fprintf (stderr, "> %.*s", (int) size, data);
    560     break;
    561   case CURLINFO_DATA_IN:
    562 #if 0
    563     fprintf (stderr, "<| %.*s\n", (int) size, data);
    564 #endif
    565     break;
    566   case CURLINFO_DATA_OUT:
    567   case CURLINFO_SSL_DATA_IN:
    568   case CURLINFO_SSL_DATA_OUT:
    569   case CURLINFO_END:
    570   default:
    571     break;
    572   }
    573 #endif /* _DEBUG */
    574   if (CURLINFO_TEXT == type)
    575   {
    576     if ((size >= excess_mark_len) &&
    577         (0 == memcmp (data, excess_mark, excess_mark_len)))
    578       mhdErrorExitDesc ("Extra data has been detected in MHD reply");
    579   }
    580   return 0;
    581 }
    582 
    583 
    584 static CURL *
    585 setupCURL (void *cbc, uint16_t port,
    586            struct headers_check_result *hdr_chk_result)
    587 {
    588   CURL *c;
    589 
    590   c = curl_easy_init ();
    591   if (NULL == c)
    592     libcurlErrorExitDesc ("curl_easy_init() failed");
    593 
    594   if ((CURLE_OK != curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1L)) ||
    595       (CURLE_OK != curl_easy_setopt (c, CURLOPT_WRITEFUNCTION,
    596                                      &copyBuffer)) ||
    597       (CURLE_OK != curl_easy_setopt (c, CURLOPT_WRITEDATA, cbc)) ||
    598       (CURLE_OK != curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT,
    599                                      ((long) TIMEOUTS_VAL))) ||
    600       (CURLE_OK != curl_easy_setopt (c, CURLOPT_HTTP_VERSION,
    601                                      (oneone) ?
    602                                      CURL_HTTP_VERSION_1_1 :
    603                                      CURL_HTTP_VERSION_1_0)) ||
    604       (CURLE_OK != curl_easy_setopt (c, CURLOPT_TIMEOUT,
    605                                      ((long) TIMEOUTS_VAL))) ||
    606       (CURLE_OK != curl_easy_setopt (c, CURLOPT_HEADERFUNCTION,
    607                                      lcurl_hdr_callback)) ||
    608       (CURLE_OK != curl_easy_setopt (c, CURLOPT_HEADERDATA,
    609                                      hdr_chk_result)) ||
    610       (CURLE_OK != curl_easy_setopt (c, CURLOPT_ERRORBUFFER,
    611                                      libcurl_errbuf)) ||
    612       (CURLE_OK != curl_easy_setopt (c, CURLOPT_FAILONERROR, 0L)) ||
    613 #ifdef _DEBUG
    614       (CURLE_OK != curl_easy_setopt (c, CURLOPT_VERBOSE, 1L)) ||
    615 #endif /* _DEBUG */
    616       (CURLE_OK != curl_easy_setopt (c, CURLOPT_DEBUGFUNCTION,
    617                                      &libcurl_debug_cb)) ||
    618 #if CURL_AT_LEAST_VERSION (7, 45, 0)
    619       (CURLE_OK != curl_easy_setopt (c, CURLOPT_DEFAULT_PROTOCOL, "http")) ||
    620 #endif /* CURL_AT_LEAST_VERSION (7, 45, 0) */
    621 #if CURL_AT_LEAST_VERSION (7, 85, 0)
    622       (CURLE_OK != curl_easy_setopt (c, CURLOPT_PROTOCOLS_STR, "http")) ||
    623 #elif CURL_AT_LEAST_VERSION (7, 19, 4)
    624       (CURLE_OK != curl_easy_setopt (c, CURLOPT_PROTOCOLS, CURLPROTO_HTTP)) ||
    625 #endif /* CURL_AT_LEAST_VERSION (7, 19, 4) */
    626       (CURLE_OK != curl_easy_setopt (c, CURLOPT_URL,
    627                                      URL_SCHEME_HOST_PATH)) ||
    628       (CURLE_OK != curl_easy_setopt (c, CURLOPT_PORT, ((long) port))))
    629     libcurlErrorExitDesc ("curl_easy_setopt() failed");
    630 
    631   if (CURLE_OK != curl_easy_setopt (c, CURLOPT_HTTPHEADER, libcurl_headers))
    632     libcurlErrorExitDesc ("Failed to set request headers");
    633 
    634   if (use_put)
    635   {
    636     if ((CURLE_OK != curl_easy_setopt (c, CURLOPT_UPLOAD, 1L)) ||
    637         (CURLE_OK != curl_easy_setopt (c, CURLOPT_READFUNCTION,
    638                                        libcurlUploadDataCB)) ||
    639         (CURLE_OK != curl_easy_setopt (c, CURLOPT_READDATA,
    640                                        cbc)))
    641       libcurlErrorExitDesc ("Failed to configure the PUT upload");
    642   }
    643 
    644   return c;
    645 }
    646 
    647 
    648 struct ahc_cls_type
    649 {
    650   const char *rq_method;
    651   const char *rq_url;
    652 
    653   unsigned int num_req;
    654   /* Position in the upload data, not compatible with parallel requests */
    655   size_t up_pos;
    656   size_t expected_upload_size;
    657   unsigned int req_check_error;
    658 };
    659 
    660 
    661 static enum MHD_Result
    662 ahcCheck (void *cls,
    663           struct MHD_Connection *connection,
    664           const char *url,
    665           const char *method,
    666           const char *version,
    667           const char *upload_data, size_t *upload_data_size,
    668           void **req_cls)
    669 {
    670   static int marker;
    671   struct MHD_Response *response;
    672   enum MHD_Result ret;
    673   struct ahc_cls_type *const param = (struct ahc_cls_type *) cls;
    674 
    675   if (NULL == param)
    676     mhdErrorExitDesc ("cls parameter is NULL");
    677 
    678   if (oneone)
    679   {
    680     if (0 != strcmp (version, MHD_HTTP_VERSION_1_1))
    681       mhdErrorExitDesc ("Unexpected HTTP version");
    682   }
    683   else
    684   {
    685     if (0 != strcmp (version, MHD_HTTP_VERSION_1_0))
    686       mhdErrorExitDesc ("Unexpected HTTP version");
    687   }
    688 
    689   if (0 != strcmp (url, param->rq_url))
    690     mhdErrorExitDesc ("Unexpected URI");
    691 
    692   if (0 != strcmp (param->rq_method, method))
    693     mhdErrorExitDesc ("Unexpected request method");
    694 
    695   if (NULL == upload_data_size)
    696     mhdErrorExitDesc ("'upload_data_size' pointer is NULL");
    697 
    698   if (NULL != upload_data)
    699   {
    700     size_t report_processed_size;
    701     if (0 == *upload_data_size)
    702       mhdErrorExitDesc ("'*upload_data_size' value is zero");
    703     report_processed_size = *upload_data_size;
    704     /* The next checks are not compatible with parallel requests */
    705     if (*upload_data_size > param->expected_upload_size - param->up_pos)
    706     {
    707       fprintf (stderr, "Unexpected *upload_data_size value: %lu. "
    708                "Already processed data size: %lu. "
    709                "Total expected upload size: %lu. "
    710                "Expected unprocessed upload size: %lu. "
    711                "The upload data cannot be checked.\n",
    712                (unsigned long) *upload_data_size,
    713                (unsigned long) param->up_pos,
    714                (unsigned long) param->expected_upload_size,
    715                (unsigned long) (param->expected_upload_size - param->up_pos));
    716       param->req_check_error++;
    717     }
    718     else
    719     {
    720       if (0 != memcmp (upload_data, put_data + param->up_pos,
    721                        *upload_data_size))
    722       {
    723         fprintf (stderr, "Wrong upload data.\n"
    724                  "Expected: '%.*s'\n"
    725                  "Received: '%.*s'.\n",
    726                  (int) *upload_data_size, upload_data,
    727                  (int) *upload_data_size, put_data + param->up_pos);
    728         param->req_check_error++;
    729       }
    730       if (use_put_large &&
    731           (report_processed_size > param->expected_upload_size / 10))
    732         report_processed_size = param->expected_upload_size / 10;
    733 
    734       param->up_pos += report_processed_size;
    735     }
    736     *upload_data_size -= report_processed_size;
    737     return MHD_YES;
    738   }
    739   else
    740   {
    741     if (0 != *upload_data_size)
    742       mhdErrorExitDesc ("'*upload_data_size' value is not zero");
    743   }
    744 
    745   if (1)
    746   {
    747     /* Check headers */
    748     const char *value;
    749     size_t value_len;
    750     unsigned int header_check_error;
    751 
    752     header_check_error = 0;
    753 
    754     value = MHD_lookup_connection_value (connection, MHD_HEADER_KIND,
    755                                          RQ_HEADER1_NAME);
    756     if (NULL == value)
    757     {
    758       fprintf (stderr, "Request header '" RQ_HEADER1_NAME "' not found.\n");
    759       header_check_error++;
    760     }
    761     else
    762     {
    763       if (0 != strcmp (value, RQ_HEADER1_VALUE))
    764       {
    765         fprintf (stderr, "Wrong header '" RQ_HEADER1_NAME "'value. "
    766                  "Expected: '%s'. Received: '%s'.\n",
    767                  RQ_HEADER1_VALUE, value);
    768         header_check_error++;
    769       }
    770     }
    771 
    772     if (MHD_YES !=
    773         MHD_lookup_connection_value_n (connection, MHD_HEADER_KIND,
    774                                        RQ_HEADER2_NAME,
    775                                        MHD_STATICSTR_LEN_ (RQ_HEADER2_NAME),
    776                                        &value, &value_len))
    777     {
    778       fprintf (stderr, "Request header '" RQ_HEADER2_NAME "' not found.\n");
    779       header_check_error++;
    780     }
    781     else
    782     {
    783       if (NULL == value)
    784         mhdErrorExitDesc ("The 'value' pointer is NULL");
    785       if (strlen (value) != value_len)
    786         mhdErrorExitDesc ("The 'value' length does not match strlen(value)");
    787 
    788       if (value_len < MHD_STATICSTR_LEN_ (RQ_HEADER2_VALUE_S)
    789           + MHD_STATICSTR_LEN_ (RQ_HEADER2_VALUE_E))
    790       {
    791         fprintf (stderr, "The value_len is too short. The value: '%s'.\n",
    792                  value);
    793         header_check_error++;
    794       }
    795       if (0 != memcmp (value, RQ_HEADER2_VALUE_S,
    796                        MHD_STATICSTR_LEN_ (RQ_HEADER2_VALUE_S)))
    797       {
    798         fprintf (stderr, "The 'value' does not start with '"
    799                  RQ_HEADER2_VALUE_S "'. The 'value' is '%s'. ",
    800                  value);
    801         header_check_error++;
    802       }
    803       if (0 != memcmp (value
    804                        + value_len - MHD_STATICSTR_LEN_ (RQ_HEADER2_VALUE_E),
    805                        RQ_HEADER2_VALUE_E,
    806                        MHD_STATICSTR_LEN_ (RQ_HEADER2_VALUE_E)))
    807       {
    808         fprintf (stderr, "The 'value' does not end with '"
    809                  RQ_HEADER2_VALUE_E "'. The 'value' is '%s'. ",
    810                  value);
    811         header_check_error++;
    812       }
    813     }
    814 
    815     value = MHD_lookup_connection_value (connection, MHD_HEADER_KIND,
    816                                          RQ_HEADER3_NAME);
    817     if (NULL == value)
    818     {
    819       fprintf (stderr, "Request header '" RQ_HEADER3_NAME "' not found.\n");
    820       header_check_error++;
    821     }
    822     else
    823     {
    824       if (0 != strcmp (value, RQ_HEADER3_VALUE))
    825       {
    826         fprintf (stderr, "Wrong header '" RQ_HEADER3_NAME "'value. "
    827                  "Expected: '%s'. Received: '%s'.\n",
    828                  RQ_HEADER3_VALUE, value);
    829         header_check_error++;
    830       }
    831     }
    832     param->req_check_error += header_check_error;
    833   }
    834 
    835   if (&marker != *req_cls)
    836   {
    837     *req_cls = &marker;
    838     if (param->num_req)
    839       mhdErrorExitDesc ("Got unexpected second request");
    840     param->num_req++;
    841     return MHD_YES;
    842   }
    843   *req_cls = NULL;
    844 
    845   if (0 != strcmp (url, EXPECTED_URI_BASE_PATH))
    846   {
    847     fprintf (stderr, "Unexpected URI: '%s'. ", url);
    848     mhdErrorExitDesc ("Unexpected URI found");
    849   }
    850 
    851   response =
    852     MHD_create_response_from_buffer_static (MHD_STATICSTR_LEN_ (PAGE),
    853                                             PAGE);
    854   if (NULL == response)
    855     mhdErrorExitDesc ("Failed to create response");
    856 
    857   if (MHD_YES != MHD_add_response_header (response,
    858                                           RP_HEADER1_NAME,
    859                                           RP_HEADER1_VALUE))
    860     mhdErrorExitDesc ("Cannot add header1");
    861   if (MHD_YES != MHD_add_response_header (response,
    862                                           RP_HEADER2_NAME,
    863                                           RP_HEADER2_VALUE))
    864     mhdErrorExitDesc ("Cannot add header2");
    865 
    866   ret = MHD_queue_response (connection,
    867                             MHD_HTTP_OK,
    868                             response);
    869   MHD_destroy_response (response);
    870   if (MHD_YES != ret)
    871     mhdErrorExitDesc ("Failed to queue response");
    872 
    873   return ret;
    874 }
    875 
    876 
    877 static CURLcode
    878 performQueryExternal (struct MHD_Daemon *d, CURL *c, CURLM **multi_reuse)
    879 {
    880   CURLM *multi;
    881   time_t start;
    882   struct timeval tv;
    883   CURLcode ret;
    884   int libcurl_finished;
    885 
    886   ret = CURLE_FAILED_INIT; /* will be replaced with real result */
    887   if (NULL != *multi_reuse)
    888     multi = *multi_reuse;
    889   else
    890   {
    891     multi = curl_multi_init ();
    892     if (multi == NULL)
    893       libcurlErrorExitDesc ("curl_multi_init() failed");
    894     *multi_reuse = multi;
    895   }
    896   if (CURLM_OK != curl_multi_add_handle (multi, c))
    897     libcurlErrorExitDesc ("curl_multi_add_handle() failed");
    898   libcurl_finished = 0;
    899 
    900   start = time (NULL);
    901   while (time (NULL) - start <= TIMEOUTS_VAL)
    902   {
    903     fd_set rs;
    904     fd_set ws;
    905     fd_set es;
    906     MHD_socket maxMhdSk;
    907     int maxCurlSk;
    908 
    909     maxMhdSk = MHD_INVALID_SOCKET;
    910     maxCurlSk = -1;
    911     FD_ZERO (&rs);
    912     FD_ZERO (&ws);
    913     FD_ZERO (&es);
    914     if (! libcurl_finished)
    915     {
    916       int running;
    917       curl_multi_perform (multi, &running);
    918       if (0 == running)
    919       {
    920         struct CURLMsg *msg;
    921         int msgLeft;
    922         int totalMsgs = 0;
    923         do
    924         {
    925           msg = curl_multi_info_read (multi, &msgLeft);
    926           if (NULL == msg)
    927             libcurlErrorExitDesc ("curl_multi_info_read() failed");
    928           totalMsgs++;
    929           if (CURLMSG_DONE == msg->msg)
    930             ret = msg->data.result;
    931         } while (msgLeft > 0);
    932         if (1 != totalMsgs)
    933         {
    934           fprintf (stderr,
    935                    "curl_multi_info_read returned wrong "
    936                    "number of results (%d).\n",
    937                    totalMsgs);
    938           externalErrorExit ();
    939         }
    940         curl_multi_remove_handle (multi, c);
    941         libcurl_finished = ! 0;
    942       }
    943       else
    944       {
    945         if (CURLM_OK != curl_multi_fdset (multi, &rs, &ws, &es, &maxCurlSk))
    946           libcurlErrorExitDesc ("curl_multi_fdset() failed");
    947       }
    948     }
    949     if (libcurl_finished)
    950     { /* libcurl has finished, check whether MHD still needs to perform cleanup */
    951       if (0 != MHD_get_timeout64s (d))
    952         break; /* MHD finished as well */
    953     }
    954     if (MHD_YES != MHD_get_fdset (d, &rs, &ws, &es, &maxMhdSk))
    955       mhdErrorExitDesc ("MHD_get_fdset() failed");
    956     tv.tv_sec = 0;
    957     tv.tv_usec = 200000;
    958     if (0 == MHD_get_timeout64s (d))
    959       tv.tv_usec = 0;
    960     else
    961     {
    962       long curl_to = -1;
    963       curl_multi_timeout (multi, &curl_to);
    964       if (0 == curl_to)
    965         tv.tv_usec = 0;
    966     }
    967 #ifdef MHD_POSIX_SOCKETS
    968     if (maxMhdSk > maxCurlSk)
    969       maxCurlSk = maxMhdSk;
    970 #endif /* MHD_POSIX_SOCKETS */
    971     if (-1 == select (maxCurlSk + 1, &rs, &ws, &es, &tv))
    972     {
    973 #ifdef MHD_POSIX_SOCKETS
    974       if (EINTR != errno)
    975         externalErrorExitDesc ("Unexpected select() error");
    976 #else
    977       if ((WSAEINVAL != WSAGetLastError ()) ||
    978           (0 != rs.fd_count) || (0 != ws.fd_count) || (0 != es.fd_count) )
    979         externalErrorExitDesc ("Unexpected select() error");
    980       Sleep ((unsigned long) tv.tv_usec / 1000);
    981 #endif
    982     }
    983     if (MHD_YES != MHD_run_from_select (d, &rs, &ws, &es))
    984       mhdErrorExitDesc ("MHD_run_from_select() failed");
    985   }
    986 
    987   return ret;
    988 }
    989 
    990 
    991 /**
    992  * Check request result
    993  * @param curl_code the CURL easy return code
    994  * @param pcbc the pointer struct CBC
    995  * @return non-zero if success, zero if failed
    996  */
    997 static unsigned int
    998 check_result (CURLcode curl_code, CURL *c, long expected_code,
    999               struct CBC *pcbc, struct headers_check_result *hdr_res,
   1000               struct ahc_cls_type *ahc_cls)
   1001 {
   1002   long code;
   1003   unsigned int ret;
   1004 
   1005   fflush (stderr);
   1006   fflush (stdout);
   1007   if (CURLE_OK != curl_code)
   1008   {
   1009     fflush (stdout);
   1010     if (0 != libcurl_errbuf[0])
   1011       fprintf (stderr, "Request failed. "
   1012                "libcurl error: '%s'.\n"
   1013                "libcurl error description: '%s'.\n",
   1014                curl_easy_strerror (curl_code),
   1015                libcurl_errbuf);
   1016     else
   1017       fprintf (stderr, "Request failed. "
   1018                "libcurl error: '%s'.\n",
   1019                curl_easy_strerror (curl_code));
   1020     return 0;
   1021   }
   1022 
   1023   if (CURLE_OK != curl_easy_getinfo (c, CURLINFO_RESPONSE_CODE, &code))
   1024     libcurlErrorExit ();
   1025 
   1026   if (expected_code != code)
   1027   {
   1028     fprintf (stderr, "The response has wrong HTTP code: %ld\tExpected: %ld.\n",
   1029              code, expected_code);
   1030     return 0;
   1031   }
   1032   else if (verbose)
   1033     printf ("The response has expected HTTP code: %ld\n", expected_code);
   1034 
   1035   ret = 1;
   1036 
   1037   if (ahc_cls->req_check_error)
   1038   {
   1039     fprintf (stderr, "One or more errors have been detected by access "
   1040              "handler callback.\n");
   1041     ret = 0;
   1042   }
   1043   if (ahc_cls->expected_upload_size != ahc_cls->up_pos)
   1044   {
   1045     fprintf (stderr, "Upload size does not match expected. "
   1046              "Expected: %lu. "
   1047              "Received: %lu.\n",
   1048              (unsigned long) ahc_cls->expected_upload_size,
   1049              (unsigned long) ahc_cls->up_pos);
   1050     ret = 0;
   1051   }
   1052 
   1053   if (1 != hdr_res->header1_found)
   1054   {
   1055     if (0 == hdr_res->header1_found)
   1056       fprintf (stderr, "Response header1 was not found.\n");
   1057     else
   1058       fprintf (stderr, "Response header1 was found %d times "
   1059                "instead of one time only.\n", hdr_res->header1_found);
   1060     ret = 0;
   1061   }
   1062   else if (verbose)
   1063     printf ("Header1 is present in the response.\n");
   1064   if (1 != hdr_res->header2_found)
   1065   {
   1066     if (0 == hdr_res->header2_found)
   1067       fprintf (stderr, "Response header2 was not found.\n");
   1068     else
   1069       fprintf (stderr, "Response header2 was found %d times "
   1070                "instead of one time only.\n", hdr_res->header2_found);
   1071     ret = 0;
   1072   }
   1073   else if (verbose)
   1074     printf ("Header2 is present in the response.\n");
   1075   if (1 != hdr_res->size_found)
   1076   {
   1077     if (0 == hdr_res->size_found)
   1078       fprintf (stderr, "Correct response 'Content-Length' header "
   1079                "was not found.\n");
   1080     else
   1081       fprintf (stderr, "Correct response 'Content-Length' header "
   1082                "was found %u times instead of one time only.\n",
   1083                hdr_res->size_found);
   1084     ret = 0;
   1085   }
   1086   else if (verbose)
   1087     printf ("'Content-Length' header with correct value "
   1088             "is present in the response.\n");
   1089   if (0 != hdr_res->size_broken_found)
   1090   {
   1091     fprintf (stderr, "Wrong response 'Content-Length' header was found "
   1092              "%u times.\n", hdr_res->size_broken_found);
   1093     ret = 0;
   1094   }
   1095 
   1096   if (pcbc->dn_pos != MHD_STATICSTR_LEN_ (PAGE))
   1097   {
   1098     fprintf (stderr, "Got %u bytes ('%.*s'), expected %u bytes. ",
   1099              (unsigned) pcbc->dn_pos, (int) pcbc->dn_pos, pcbc->dn_buf,
   1100              (unsigned) MHD_STATICSTR_LEN_ (PAGE));
   1101     mhdErrorExitDesc ("Wrong returned data length");
   1102   }
   1103   if (0 != memcmp (PAGE, pcbc->dn_buf, pcbc->dn_pos))
   1104   {
   1105     fprintf (stderr, "Got invalid response '%.*s'. ",
   1106              (int) pcbc->dn_pos, pcbc->dn_buf);
   1107     mhdErrorExitDesc ("Wrong returned data");
   1108   }
   1109   fflush (stderr);
   1110   fflush (stdout);
   1111   return ret;
   1112 }
   1113 
   1114 
   1115 static unsigned int
   1116 performCheck (void)
   1117 {
   1118   struct MHD_Daemon *d;
   1119   uint16_t port;
   1120   struct CBC cbc;
   1121   struct ahc_cls_type ahc_param;
   1122   struct headers_check_result rp_headers_check;
   1123   char buf[2048];
   1124   CURL *c;
   1125   CURLM *multi_reuse;
   1126   int failed = 0;
   1127 
   1128   if (MHD_NO != MHD_is_feature_supported (MHD_FEATURE_AUTODETECT_BIND_PORT))
   1129     port = 0;
   1130   else
   1131   {
   1132     port =  UINT16_C (4220);
   1133     if (! oneone)
   1134       port += UINT16_C (1);
   1135     if (use_put)
   1136       port += UINT16_C (2);
   1137     if (use_put_large)
   1138       port += UINT16_C (4);
   1139     if (use_hdr_last)
   1140       port += UINT16_C (8);
   1141     if (use_hdr_large)
   1142       port += UINT16_C (16);
   1143   }
   1144 
   1145   if (1)
   1146   {
   1147     size_t mem_limit;
   1148     if (use_put_large)
   1149       mem_limit = (size_t) (TEST_UPLOAD_DATA_SIZE / 2 + HEADERS_POINTERS_SIZE);
   1150     else
   1151       mem_limit = (size_t) ((TEST_UPLOAD_DATA_SIZE * 4) / 3 + 2
   1152                             + HEADERS_POINTERS_SIZE);
   1153 
   1154     d = MHD_start_daemon (MHD_USE_ERROR_LOG | MHD_USE_NO_THREAD_SAFETY,
   1155                           port, NULL, NULL,
   1156                           &ahcCheck, &ahc_param,
   1157                           MHD_OPTION_CONNECTION_MEMORY_LIMIT, mem_limit,
   1158                           MHD_OPTION_APP_FD_SETSIZE, (int) FD_SETSIZE,
   1159                           MHD_OPTION_END);
   1160   }
   1161   if (d == NULL)
   1162     return 1;
   1163   if (0 == port)
   1164   {
   1165     const union MHD_DaemonInfo *dinfo;
   1166 
   1167     dinfo = MHD_get_daemon_info (d,
   1168                                  MHD_DAEMON_INFO_BIND_PORT);
   1169     if ( (NULL == dinfo) ||
   1170          (0 == dinfo->port) )
   1171       mhdErrorExitDesc ("MHD_get_daemon_info() failed");
   1172     port = dinfo->port;
   1173   }
   1174 
   1175   /* First request */
   1176   ahc_param.rq_method = use_put ? MHD_HTTP_METHOD_PUT : MHD_HTTP_METHOD_GET;
   1177   ahc_param.rq_url = EXPECTED_URI_BASE_PATH;
   1178   ahc_param.expected_upload_size = use_put ? TEST_UPLOAD_DATA_SIZE : 0;
   1179   ahc_param.req_check_error = 0;
   1180   ahc_param.num_req = 0;
   1181   ahc_param.up_pos = 0;
   1182   rp_headers_check.expected_size = MHD_STATICSTR_LEN_ (PAGE);
   1183   rp_headers_check.header1_found = 0;
   1184   rp_headers_check.header2_found = 0;
   1185   rp_headers_check.size_found = 0;
   1186   rp_headers_check.size_broken_found = 0;
   1187   cbc.dn_buf = buf;
   1188   cbc.dn_buf_size = sizeof (buf);
   1189   cbc.dn_pos = 0;
   1190   memset (cbc.dn_buf, 0, cbc.dn_buf_size);
   1191   cbc.up_size = TEST_UPLOAD_DATA_SIZE;
   1192   cbc.up_pos = 0;
   1193   c = setupCURL (&cbc, port, &rp_headers_check);
   1194   multi_reuse = NULL;
   1195   /* First request */
   1196   if (check_result (performQueryExternal (d, c, &multi_reuse), c,
   1197                     MHD_HTTP_OK, &cbc, &rp_headers_check, &ahc_param))
   1198   {
   1199     fflush (stderr);
   1200     if (verbose)
   1201       printf ("Got first expected response.\n");
   1202     fflush (stdout);
   1203   }
   1204   else
   1205   {
   1206     fprintf (stderr, "First request FAILED.\n");
   1207     fflush (stderr);
   1208     failed = 1;
   1209   }
   1210   /* Second request */
   1211   ahc_param.req_check_error = 0;
   1212   ahc_param.num_req = 0;
   1213   ahc_param.up_pos = 0;
   1214   rp_headers_check.header1_found = 0;
   1215   rp_headers_check.header2_found = 0;
   1216   rp_headers_check.size_found = 0;
   1217   rp_headers_check.size_broken_found = 0;
   1218   /* Reset buffer position */
   1219   cbc.dn_pos = 0;
   1220   memset (cbc.dn_buf, 0, cbc.dn_buf_size);
   1221   cbc.up_pos = 0;
   1222   if (check_result (performQueryExternal (d, c, &multi_reuse), c,
   1223                     MHD_HTTP_OK, &cbc, &rp_headers_check, &ahc_param))
   1224   {
   1225     fflush (stderr);
   1226     if (verbose)
   1227       printf ("Got second expected response.\n");
   1228     fflush (stdout);
   1229   }
   1230   else
   1231   {
   1232     fprintf (stderr, "Second request FAILED.\n");
   1233     fflush (stderr);
   1234     failed = 1;
   1235   }
   1236   /* Third request */
   1237   ahc_param.req_check_error = 0;
   1238   ahc_param.num_req = 0;
   1239   ahc_param.up_pos = 0;
   1240   rp_headers_check.header1_found = 0;
   1241   rp_headers_check.header2_found = 0;
   1242   rp_headers_check.size_found = 0;
   1243   rp_headers_check.size_broken_found = 0;
   1244   /* Reset buffer position */
   1245   cbc.dn_pos = 0;
   1246   memset (cbc.dn_buf, 0, cbc.dn_buf_size);
   1247   cbc.up_pos = 0;
   1248   if (NULL != multi_reuse)
   1249     curl_multi_cleanup (multi_reuse);
   1250   multi_reuse = NULL; /* Force new connection */
   1251   if (check_result (performQueryExternal (d, c, &multi_reuse), c,
   1252                     MHD_HTTP_OK, &cbc, &rp_headers_check, &ahc_param))
   1253   {
   1254     fflush (stderr);
   1255     if (verbose)
   1256       printf ("Got third expected response.\n");
   1257     fflush (stdout);
   1258   }
   1259   else
   1260   {
   1261     fprintf (stderr, "Third request FAILED.\n");
   1262     fflush (stderr);
   1263     failed = 1;
   1264   }
   1265 
   1266   curl_easy_cleanup (c);
   1267   if (NULL != multi_reuse)
   1268     curl_multi_cleanup (multi_reuse);
   1269 
   1270   MHD_stop_daemon (d);
   1271   return failed ? 1 : 0;
   1272 }
   1273 
   1274 
   1275 int
   1276 main (int argc, char *const *argv)
   1277 {
   1278   unsigned int errorCount = 0;
   1279 
   1280   /* Test type and test parameters */
   1281   verbose = ! (has_param (argc, argv, "-q") ||
   1282                has_param (argc, argv, "--quiet") ||
   1283                has_param (argc, argv, "-s") ||
   1284                has_param (argc, argv, "--silent"));
   1285   oneone = ! has_in_name (argv[0], "10");
   1286 
   1287   use_get = has_in_name (argv[0], "_get");
   1288   use_put = has_in_name (argv[0], "_put");
   1289 
   1290   use_double_fold = has_in_name (argv[0], "_double_fold");
   1291   use_put_large = has_in_name (argv[0], "_put_large");
   1292   use_hdr_last = has_in_name (argv[0], "_last");
   1293   use_hdr_large = has_in_name (argv[0], "_fold_large");
   1294 
   1295   if (1 !=
   1296       ((use_get ? 1 : 0) + (use_put ? 1 : 0)))
   1297   {
   1298     fprintf (stderr, "Wrong test name '%s': no or multiple indications "
   1299              "for the test type.\n", argv[0] ? argv[0] : "(NULL)");
   1300     return 99;
   1301   }
   1302 
   1303   test_global_init ();
   1304 
   1305   errorCount += performCheck ();
   1306   if (errorCount != 0)
   1307     fprintf (stderr, "Error (code: %u)\n", errorCount);
   1308   test_global_cleanup ();
   1309   return (0 == errorCount) ? 0 : 1;       /* 0 == pass */
   1310 }