libmicrohttpd

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

test_toolarge.c (57584B)


      1 /*
      2      This file is part of libmicrohttpd
      3      Copyright (C) 2014-2022 Evgeny Grin (Karlson2k)
      4      Copyright (C) 2007, 2009, 2011 Christian Grothoff
      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  * @file test_toolarge.c
     23  * @brief  Testcase for handling of data larger then buffers.
     24  * @details Testcases for handling of various situations when data cannot fit
     25  *          the buffers. Address sanitizers and debug asserts should increase
     26  *          number of problems detected by this test (detect actual overrun).
     27  *          Tests start with valid sizes to ensure that normal data is processed
     28  *          correctly and then sizes are monotonically increased to ensure that
     29  *          overflow is handled correctly at all stages in all codepaths.
     30  *          Tests with valid sizes are repeated several times to ensure that
     31  *          tests are failed because of overflow, but because of second run.
     32  * @author Karlson2k (Evgeny Grin)
     33  * @author Christian Grothoff
     34  */
     35 #include "mhd_options.h"
     36 #include "platform.h"
     37 #include <curl/curl.h>
     38 #include <microhttpd.h>
     39 #include <stdlib.h>
     40 #include <string.h>
     41 #include <time.h>
     42 #include <errno.h>
     43 #include "mhd_has_in_name.h"
     44 #include "mhd_has_param.h"
     45 #include "mhd_sockets.h" /* only macros used */
     46 
     47 /* Turn any MHD_PANIC() or failing mhd_assert() reached from this
     48    test into a marked, classifiable test error (TESTING.md, P5). */
     49 #include "mhd_panic_tripwire.h"
     50 
     51 #ifdef HAVE_STRINGS_H
     52 #include <strings.h>
     53 #endif /* HAVE_STRINGS_H */
     54 
     55 #ifdef _WIN32
     56 #ifndef WIN32_LEAN_AND_MEAN
     57 #define WIN32_LEAN_AND_MEAN 1
     58 #endif /* !WIN32_LEAN_AND_MEAN */
     59 #include <windows.h>
     60 #endif
     61 
     62 #ifndef WINDOWS
     63 #include <unistd.h>
     64 #include <sys/socket.h>
     65 #endif
     66 
     67 #ifdef HAVE_LIMITS_H
     68 #include <limits.h>
     69 #endif /* HAVE_LIMITS_H */
     70 
     71 #if defined(MHD_CPU_COUNT) && (MHD_CPU_COUNT + 0) < 2
     72 #undef MHD_CPU_COUNT
     73 #endif
     74 #if ! defined(MHD_CPU_COUNT)
     75 #define MHD_CPU_COUNT 2
     76 #endif
     77 #if MHD_CPU_COUNT > 32
     78 #undef MHD_CPU_COUNT
     79 /* Limit to reasonable value */
     80 #define MHD_CPU_COUNT 32
     81 #endif /* MHD_CPU_COUNT > 32 */
     82 
     83 
     84 #if defined(HAVE___FUNC__)
     85 #define externalErrorExit(ignore) \
     86     _externalErrorExit_func(NULL, __func__, __LINE__)
     87 #define externalErrorExitDesc(errDesc) \
     88     _externalErrorExit_func(errDesc, __func__, __LINE__)
     89 #define libcurlErrorExit(ignore) \
     90     _libcurlErrorExit_func(NULL, __func__, __LINE__)
     91 #define libcurlErrorExitDesc(errDesc) \
     92     _libcurlErrorExit_func(errDesc, __func__, __LINE__)
     93 #define mhdErrorExit(ignore) \
     94     _mhdErrorExit_func(NULL, __func__, __LINE__)
     95 #define mhdErrorExitDesc(errDesc) \
     96     _mhdErrorExit_func(errDesc, __func__, __LINE__)
     97 #elif defined(HAVE___FUNCTION__)
     98 #define externalErrorExit(ignore) \
     99     _externalErrorExit_func(NULL, __FUNCTION__, __LINE__)
    100 #define externalErrorExitDesc(errDesc) \
    101     _externalErrorExit_func(errDesc, __FUNCTION__, __LINE__)
    102 #define libcurlErrorExit(ignore) \
    103     _libcurlErrorExit_func(NULL, __FUNCTION__, __LINE__)
    104 #define libcurlErrorExitDesc(errDesc) \
    105     _libcurlErrorExit_func(errDesc, __FUNCTION__, __LINE__)
    106 #define mhdErrorExit(ignore) \
    107     _mhdErrorExit_func(NULL, __FUNCTION__, __LINE__)
    108 #define mhdErrorExitDesc(errDesc) \
    109     _mhdErrorExit_func(errDesc, __FUNCTION__, __LINE__)
    110 #else
    111 #define externalErrorExit(ignore) _externalErrorExit_func(NULL, NULL, __LINE__)
    112 #define externalErrorExitDesc(errDesc) \
    113   _externalErrorExit_func(errDesc, NULL, __LINE__)
    114 #define libcurlErrorExit(ignore) _libcurlErrorExit_func(NULL, NULL, __LINE__)
    115 #define libcurlErrorExitDesc(errDesc) \
    116   _libcurlErrorExit_func(errDesc, NULL, __LINE__)
    117 #define mhdErrorExit(ignore) _mhdErrorExit_func(NULL, NULL, __LINE__)
    118 #define mhdErrorExitDesc(errDesc) _mhdErrorExit_func(errDesc, NULL, __LINE__)
    119 #endif
    120 
    121 
    122 _MHD_NORETURN static void
    123 _externalErrorExit_func (const char *errDesc, const char *funcName, int lineNum)
    124 {
    125   if ((NULL != errDesc) && (0 != errDesc[0]))
    126     fprintf (stderr, "%s", errDesc);
    127   else
    128     fprintf (stderr, "System or external library call failed");
    129   if ((NULL != funcName) && (0 != funcName[0]))
    130     fprintf (stderr, " in %s", funcName);
    131   if (0 < lineNum)
    132     fprintf (stderr, " at line %d", lineNum);
    133 
    134   fprintf (stderr, ".\nLast errno value: %d (%s)\n", (int) errno,
    135            strerror (errno));
    136 #ifdef MHD_WINSOCK_SOCKETS
    137   fprintf (stderr, "WSAGetLastError() value: %d\n", (int) WSAGetLastError ());
    138 #endif /* MHD_WINSOCK_SOCKETS */
    139   fflush (stderr);
    140   exit (99);
    141 }
    142 
    143 
    144 static char libcurl_errbuf[CURL_ERROR_SIZE] = "";
    145 
    146 _MHD_NORETURN static void
    147 _libcurlErrorExit_func (const char *errDesc, const char *funcName, int lineNum)
    148 {
    149   if ((NULL != errDesc) && (0 != errDesc[0]))
    150     fprintf (stderr, "%s", errDesc);
    151   else
    152     fprintf (stderr, "CURL library call failed");
    153   if ((NULL != funcName) && (0 != funcName[0]))
    154     fprintf (stderr, " in %s", funcName);
    155   if (0 < lineNum)
    156     fprintf (stderr, " at line %d", lineNum);
    157 
    158   fprintf (stderr, ".\nLast errno value: %d (%s)\n", (int) errno,
    159            strerror (errno));
    160   if (0 != libcurl_errbuf[0])
    161     fprintf (stderr, "Last libcurl error details: %s\n", libcurl_errbuf);
    162 
    163   fflush (stderr);
    164   exit (99);
    165 }
    166 
    167 
    168 _MHD_NORETURN static void
    169 _mhdErrorExit_func (const char *errDesc, const char *funcName, int lineNum)
    170 {
    171   if ((NULL != errDesc) && (0 != errDesc[0]))
    172     fprintf (stderr, "%s", errDesc);
    173   else
    174     fprintf (stderr, "MHD unexpected error");
    175   if ((NULL != funcName) && (0 != funcName[0]))
    176     fprintf (stderr, " in %s", funcName);
    177   if (0 < lineNum)
    178     fprintf (stderr, " at line %d", lineNum);
    179 
    180   fprintf (stderr, ".\nLast errno value: %d (%s)\n", (int) errno,
    181            strerror (errno));
    182 
    183   fflush (stderr);
    184   exit (8);
    185 }
    186 
    187 
    188 /* Could be increased to facilitate debugging */
    189 #define TIMEOUTS_VAL 5
    190 
    191 #define EXPECTED_URI_BASE_PATH  "/a"
    192 
    193 #define URL_SCHEME_HOST "http:/" "/127.0.0.1"
    194 
    195 #define N1_HEADER_NAME "n"
    196 #define N1_HEADER_VALUE "1"
    197 #define N1_HEADER N1_HEADER_NAME ": " N1_HEADER_VALUE
    198 #define N1_HEADER_CRLF N1_HEADER "\r\n"
    199 
    200 #define BUFFER_SIZE 1024
    201 
    202 /* The size of the test element that must pass the test */
    203 #ifndef MHD_ASAN_POISON_ACTIVE
    204 #define TEST_OK_SIZE (BUFFER_SIZE - 384)
    205 #else  /* MHD_ASAN_POISON_ACTIVE */
    206 #define TEST_OK_SIZE (BUFFER_SIZE - 384 - 80)
    207 #endif /* MHD_ASAN_POISON_ACTIVE */
    208 
    209 /* The size of the test element where tests are started */
    210 #define TEST_START_SIZE (TEST_OK_SIZE - 16)
    211 
    212 /* The size of the test element that must definitely fail */
    213 #define TEST_FAIL_SIZE (BUFFER_SIZE + 32)
    214 
    215 /* Special value for request many headers test.
    216  * MHD uses the same buffer to store headers strings and pointers to the strings
    217  * so allocation is multiplied for small request header. */
    218 /* The size of the test element that must pass the test */
    219 #define TEST_RQ_N1_OK_SIZE 50
    220 
    221 /* The size of the test element where tests are started */
    222 #define TEST_RQ_N1_START_SIZE (TEST_RQ_N1_OK_SIZE - 32)
    223 
    224 /* Global parameters */
    225 static int verbose;                 /**< Be verbose */
    226 static int oneone;                  /**< If false use HTTP/1.0 for requests*/
    227 static uint16_t global_port;        /**< MHD daemons listen port number */
    228 static int large_req_method;        /**< Large request method */
    229 static int large_req_url;           /**< Large request URL */
    230 static int large_req_header_name; /**< Large request single header name */
    231 static int large_req_header_value; /**< Large request single header value */
    232 static int large_req_headers; /**< Large request headers */
    233 static int large_rsp_header_name; /**< Large response single header name */
    234 static int large_rsp_header_value; /**< Large response single header value */
    235 static int large_rsp_headers; /**< Large response headers */
    236 static int response_timeout_val = TIMEOUTS_VAL;
    237 
    238 /* Current test parameters */
    239 /* * Moved to local variables * */
    240 
    241 /* Static helper variables */
    242 /* * None for this test * */
    243 
    244 static void
    245 test_global_init (void)
    246 {
    247   libcurl_errbuf[0] = 0;
    248 
    249   if (0 != curl_global_init (CURL_GLOBAL_WIN32))
    250     externalErrorExit ();
    251 }
    252 
    253 
    254 static void
    255 test_global_cleanup (void)
    256 {
    257   curl_global_cleanup ();
    258 }
    259 
    260 
    261 struct headers_check_result
    262 {
    263   unsigned int num_n1_headers;
    264   size_t large_header_name_size;
    265   size_t large_header_value_size;
    266   int large_header_valid;
    267 };
    268 
    269 static size_t
    270 lcurl_hdr_callback (char *buffer, size_t size, size_t nitems,
    271                     void *userdata)
    272 {
    273   const size_t data_size = size * nitems;
    274   struct headers_check_result *check_res =
    275     (struct headers_check_result *) userdata;
    276 
    277   if ((6 == data_size) && (0 == memcmp (N1_HEADER_CRLF, buffer, 6)))
    278     check_res->num_n1_headers++;
    279   else if ((5 <= data_size) && ('0' == buffer[0]))
    280   {
    281     const char *const col_ptr = memchr (buffer, ':', data_size);
    282     if (0 != check_res->large_header_value_size)
    283       mhdErrorExitDesc ("Expected only one large header, " \
    284                         "but found two large headers in the reply");
    285     if (NULL == col_ptr)
    286       check_res->large_header_valid = 0;
    287     else if ((size_t) (col_ptr - buffer) >= data_size - 2)
    288       check_res->large_header_valid = 0;
    289     else if (*(col_ptr + 1) != ' ')
    290       check_res->large_header_valid = 0;
    291     else
    292     {
    293       const char *const name = buffer;
    294       const size_t name_len = (size_t) (col_ptr - buffer);
    295       const size_t val_pos = name_len + 2;
    296       const size_t val_len = data_size - val_pos - 2; /* 2 = strlen("\r\n") */
    297       const char *const value = buffer + val_pos;
    298       size_t i;
    299       check_res->large_header_name_size = name_len;
    300       check_res->large_header_value_size = val_len;
    301       check_res->large_header_valid = 1; /* To be reset if any problem found */
    302       for (i = 1; i < name_len; i++)
    303         if ('a' + (char) (i % ('z' - 'a' + 1)) != name[i])
    304         {
    305           fprintf (stderr, "Wrong sequence in reply header name " \
    306                    "at position %u. Expected '%c', got '%c'\n",
    307                    (unsigned int) i,
    308                    'a' + (char) (i % ('z' - 'a' + 1)),
    309                    name[i]);
    310           check_res->large_header_valid = 0;
    311           break;
    312         }
    313       for (i = 0; i < val_len; i++)
    314         if ('Z' - (char) (i % ('Z' - 'A' + 1)) != value[i])
    315         {
    316           fprintf (stderr, "Wrong sequence in reply header value " \
    317                    "at position %u. Expected '%c', got '%c'\n",
    318                    (unsigned int) i,
    319                    'Z' - (char) (i % ('Z' - 'A' + 1)),
    320                    value[i]);
    321           check_res->large_header_valid = 0;
    322           break;
    323         }
    324     }
    325   }
    326 
    327   return data_size;
    328 }
    329 
    330 
    331 struct lcurl_data_cb_param
    332 {
    333   char *buf;
    334   size_t pos;
    335   size_t size;
    336 };
    337 
    338 
    339 static size_t
    340 copyBuffer (void *ptr, size_t size, size_t nmemb, void *ctx)
    341 {
    342   struct lcurl_data_cb_param *cbc = ctx;
    343 
    344   if (cbc->pos + size * nmemb > cbc->size)
    345     externalErrorExit ();  /* overflow */
    346   memcpy (&cbc->buf[cbc->pos], ptr, size * nmemb);
    347   cbc->pos += size * nmemb;
    348   return size * nmemb;
    349 }
    350 
    351 
    352 struct check_uri_cls
    353 {
    354   const char *volatile uri;
    355 };
    356 
    357 static void *
    358 check_uri_cb (void *cls,
    359               const char *uri,
    360               struct MHD_Connection *con)
    361 {
    362   struct check_uri_cls *param = (struct check_uri_cls *) cls;
    363   (void) con;
    364 
    365   if (0 != strcmp (param->uri,
    366                    uri))
    367   {
    368     fprintf (stderr,
    369              "Wrong URI: `%s', line: %d\n",
    370              uri, __LINE__);
    371     exit (22);
    372   }
    373   return NULL;
    374 }
    375 
    376 
    377 struct mhd_header_checker_param
    378 {
    379   unsigned int num_n1_headers;
    380   size_t large_header_name_size;
    381   size_t large_header_value_size;
    382   int large_header_valid;
    383 };
    384 
    385 static enum MHD_Result
    386 headerCheckerInterator (void *cls,
    387                         enum MHD_ValueKind kind,
    388                         const char *key,
    389                         size_t key_size,
    390                         const char *value,
    391                         size_t value_size)
    392 {
    393   struct mhd_header_checker_param *const param =
    394     (struct mhd_header_checker_param *) cls;
    395 
    396   if (NULL == param)
    397     mhdErrorExitDesc ("cls parameter is NULL");
    398 
    399   if (MHD_HEADER_KIND != kind)
    400     return MHD_YES; /* Continue iteration */
    401 
    402   if (0 == key_size)
    403     mhdErrorExitDesc ("Zero key length");
    404 
    405   if ((1 == key_size) && (1 == value_size) &&
    406       ('n' == key[0]) && ('1' == value[0]))
    407     param->num_n1_headers++;
    408   else if ('0' == key[0])
    409   { /* Found 'large' header */
    410     size_t i;
    411     param->large_header_name_size = key_size;
    412     param->large_header_value_size = value_size;
    413     param->large_header_valid = 1;
    414     for (i = 1; i < key_size; i++)
    415       if ('a' + (char) (i % ('z' - 'a' + 1)) != key[i])
    416       {
    417         fprintf (stderr, "Wrong sequence in request header name " \
    418                  "at position %u. Expected '%c', got '%c'\n",
    419                  (unsigned int) i,
    420                  'a' + (char) (i % ('z' - 'a' + 1)),
    421                  key[i]);
    422         param->large_header_valid = 0;
    423         break;
    424       }
    425     for (i = 0; i < value_size; i++)
    426       if ('Z' - (char) (i % ('Z' - 'A' + 1)) != value[i])
    427       {
    428         fprintf (stderr, "Wrong sequence in request header value " \
    429                  "at position %u. Expected '%c', got '%c'\n",
    430                  (unsigned int) i,
    431                  'Z' - (char) (i % ('Z' - 'A' + 1)),
    432                  value[i]);
    433         param->large_header_valid = 0;
    434         break;
    435       }
    436   }
    437   return MHD_YES;
    438 }
    439 
    440 
    441 struct ahc_cls_type
    442 {
    443   const char *volatile rp_data;
    444   volatile size_t rp_data_size;
    445   volatile size_t rp_num_n1_hdrs;
    446   volatile size_t rp_large_hdr_name_size;
    447   volatile size_t rp_large_hdr_value_size;
    448   struct mhd_header_checker_param header_check_param;
    449   const char *volatile rq_method;
    450   const char *volatile rq_url;
    451 };
    452 
    453 
    454 static enum MHD_Result
    455 ahcCheck (void *cls,
    456           struct MHD_Connection *connection,
    457           const char *url,
    458           const char *method,
    459           const char *version,
    460           const char *upload_data, size_t *upload_data_size,
    461           void **req_cls)
    462 {
    463   static int ptr;
    464   struct MHD_Response *response;
    465   enum MHD_Result ret;
    466   struct ahc_cls_type *const param = (struct ahc_cls_type *) cls;
    467   size_t i;
    468 
    469   if (NULL == param)
    470     mhdErrorExitDesc ("cls parameter is NULL");
    471 
    472   if (0 != strcmp (version, MHD_HTTP_VERSION_1_1))
    473     mhdErrorExitDesc ("Unexpected HTTP version");
    474 
    475   if (0 != strcmp (url, param->rq_url))
    476     mhdErrorExitDesc ("Unexpected URI");
    477 
    478   if (NULL != upload_data)
    479     mhdErrorExitDesc ("'upload_data' is not NULL");
    480 
    481   if (NULL == upload_data_size)
    482     mhdErrorExitDesc ("'upload_data_size' pointer is NULL");
    483 
    484   if (0 != *upload_data_size)
    485     mhdErrorExitDesc ("'*upload_data_size' value is not zero");
    486 
    487   if (0 != strcmp (param->rq_method, method))
    488     mhdErrorExitDesc ("Unexpected request method");
    489 
    490   if (&ptr != *req_cls)
    491   {
    492     *req_cls = &ptr;
    493     return MHD_YES;
    494   }
    495   *req_cls = NULL;
    496 
    497   if (1 > MHD_get_connection_values_n (connection, MHD_HEADER_KIND,
    498                                        &headerCheckerInterator,
    499                                        &param->header_check_param))
    500     mhdErrorExitDesc ("Wrong number of headers in the request");
    501 
    502   response =
    503     MHD_create_response_from_buffer_copy (param->rp_data_size,
    504                                           (const void *) param->rp_data);
    505   if (NULL == response)
    506     mhdErrorExitDesc ("Failed to create response");
    507 
    508   for (i = 0; i < param->rp_num_n1_hdrs; i++)
    509     if (MHD_YES != MHD_add_response_header (response,
    510                                             N1_HEADER_NAME,
    511                                             N1_HEADER_VALUE))
    512       mhdErrorExitDesc ("Cannot add header");
    513 
    514   if (0 != param->rp_large_hdr_name_size)
    515   {
    516     const size_t large_hdr_name_size = param->rp_large_hdr_name_size;
    517     char *large_hrd_name;
    518     const size_t large_hdr_value_size = param->rp_large_hdr_value_size;
    519     char *large_hrd_value;
    520 
    521     large_hrd_name = malloc (large_hdr_name_size + 1);
    522     if (NULL == large_hrd_name)
    523       externalErrorExit ();
    524     if (0 != large_hdr_value_size)
    525       large_hrd_value = malloc (large_hdr_value_size + 1);
    526     else
    527       large_hrd_value = NULL;
    528 
    529     if ((0 != large_hdr_value_size) && (NULL == large_hrd_value))
    530       externalErrorExit ();
    531     large_hrd_name[0] = '0'; /* Name starts with zero for unique identification */
    532     for (i = 1; i < large_hdr_name_size; i++)
    533       large_hrd_name[i] = 'a' + (char) (unsigned char) (i % ('z' - 'a' + 1));
    534     large_hrd_name[large_hdr_name_size] = 0;
    535     for (i = 0; i < large_hdr_value_size; i++)
    536       large_hrd_value[i] = 'Z' - (char) (unsigned char) (i % ('Z' - 'A' + 1));
    537     if (NULL != large_hrd_value)
    538       large_hrd_value[large_hdr_value_size] = 0;
    539     if (MHD_YES != MHD_add_response_header (response,
    540                                             large_hrd_name,
    541                                             large_hrd_value))
    542       mhdErrorExitDesc ("Cannot add large header");
    543 
    544     if (NULL != large_hrd_value)
    545       free (large_hrd_value);
    546     free (large_hrd_name);
    547   }
    548 
    549   ret = MHD_queue_response (connection,
    550                             MHD_HTTP_OK,
    551                             response);
    552   MHD_destroy_response (response);
    553   if (MHD_YES != ret)
    554     mhdErrorExitDesc ("Failed to queue response");
    555 
    556   return ret;
    557 }
    558 
    559 
    560 static CURL *
    561 curlEasyInitForTest (const char *queryPath, const char *method,
    562                      uint16_t port,
    563                      struct lcurl_data_cb_param *dcbp,
    564                      struct headers_check_result *hdr_chk_result,
    565                      struct curl_slist *headers)
    566 {
    567   CURL *c;
    568 
    569   c = curl_easy_init ();
    570   if (NULL == c)
    571     libcurlErrorExitDesc ("curl_easy_init() failed");
    572 
    573   if ((CURLE_OK != curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1L)) ||
    574       (CURLE_OK != curl_easy_setopt (c, CURLOPT_URL, queryPath)) ||
    575       (CURLE_OK != curl_easy_setopt (c, CURLOPT_PORT, (long) port)) ||
    576       (CURLE_OK != curl_easy_setopt (c, CURLOPT_WRITEFUNCTION,
    577                                      &copyBuffer)) ||
    578       (CURLE_OK != curl_easy_setopt (c, CURLOPT_WRITEDATA, dcbp)) ||
    579       (CURLE_OK != curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT,
    580                                      (long) response_timeout_val)) ||
    581       (CURLE_OK != curl_easy_setopt (c, CURLOPT_TIMEOUT,
    582                                      (long) response_timeout_val)) ||
    583       (CURLE_OK != curl_easy_setopt (c, CURLOPT_ERRORBUFFER,
    584                                      libcurl_errbuf)) ||
    585       (CURLE_OK != curl_easy_setopt (c, CURLOPT_HEADERFUNCTION,
    586                                      lcurl_hdr_callback)) ||
    587       (CURLE_OK != curl_easy_setopt (c, CURLOPT_HEADERDATA,
    588                                      hdr_chk_result)) ||
    589       (CURLE_OK != curl_easy_setopt (c, CURLOPT_FAILONERROR, 1L)) ||
    590       (CURLE_OK != curl_easy_setopt (c, CURLOPT_HTTP_VERSION,
    591                                      (oneone) ?
    592                                      CURL_HTTP_VERSION_1_1 :
    593                                      CURL_HTTP_VERSION_1_0)))
    594     libcurlErrorExitDesc ("curl_easy_setopt() failed");
    595 
    596   if (CURLE_OK != curl_easy_setopt (c, CURLOPT_CUSTOMREQUEST, method))
    597     libcurlErrorExitDesc ("curl_easy_setopt() failed");
    598 
    599   if (CURLE_OK != curl_easy_setopt (c, CURLOPT_HTTPHEADER, headers))
    600     libcurlErrorExitDesc ("curl_easy_setopt() failed");
    601 
    602   return c;
    603 }
    604 
    605 
    606 static CURLcode
    607 performQueryExternal (struct MHD_Daemon *d, CURL *c)
    608 {
    609   CURLM *multi;
    610   time_t start;
    611   struct timeval tv;
    612   CURLcode ret;
    613 
    614   ret = CURLE_FAILED_INIT; /* will be replaced with real result */
    615   multi = NULL;
    616   multi = curl_multi_init ();
    617   if (multi == NULL)
    618     libcurlErrorExitDesc ("curl_multi_init() failed");
    619   if (CURLM_OK != curl_multi_add_handle (multi, c))
    620     libcurlErrorExitDesc ("curl_multi_add_handle() failed");
    621 
    622   start = time (NULL);
    623   while (time (NULL) - start <= TIMEOUTS_VAL)
    624   {
    625     fd_set rs;
    626     fd_set ws;
    627     fd_set es;
    628     MHD_socket maxMhdSk;
    629     int maxCurlSk;
    630     int running;
    631 
    632     maxMhdSk = MHD_INVALID_SOCKET;
    633     maxCurlSk = -1;
    634     FD_ZERO (&rs);
    635     FD_ZERO (&ws);
    636     FD_ZERO (&es);
    637     if (NULL != multi)
    638     {
    639       curl_multi_perform (multi, &running);
    640       if (0 == running)
    641       {
    642         struct CURLMsg *msg;
    643         int msgLeft;
    644         int totalMsgs = 0;
    645         do
    646         {
    647           msg = curl_multi_info_read (multi, &msgLeft);
    648           if (NULL == msg)
    649             libcurlErrorExitDesc ("curl_multi_info_read() failed");
    650           totalMsgs++;
    651           if (CURLMSG_DONE == msg->msg)
    652             ret = msg->data.result;
    653         } while (msgLeft > 0);
    654         if (1 != totalMsgs)
    655         {
    656           fprintf (stderr,
    657                    "curl_multi_info_read returned wrong "
    658                    "number of results (%d).\n",
    659                    totalMsgs);
    660           externalErrorExit ();
    661         }
    662         curl_multi_remove_handle (multi, c);
    663         curl_multi_cleanup (multi);
    664         multi = NULL;
    665       }
    666       else
    667       {
    668         if (CURLM_OK != curl_multi_fdset (multi, &rs, &ws, &es, &maxCurlSk))
    669           libcurlErrorExitDesc ("curl_multi_fdset() failed");
    670       }
    671     }
    672     if (NULL == multi)
    673     { /* libcurl has finished, check whether MHD still needs to perform cleanup */
    674       if (0 != MHD_get_timeout64s (d))
    675         break; /* MHD finished as well */
    676     }
    677     if (MHD_YES != MHD_get_fdset (d, &rs, &ws, &es, &maxMhdSk))
    678       mhdErrorExitDesc ("MHD_get_fdset() failed");
    679     tv.tv_sec = 0;
    680     tv.tv_usec = 1000;
    681 #ifdef MHD_POSIX_SOCKETS
    682     if (maxMhdSk > maxCurlSk)
    683       maxCurlSk = maxMhdSk;
    684 #endif /* MHD_POSIX_SOCKETS */
    685     if (-1 == select (maxCurlSk + 1, &rs, &ws, &es, &tv))
    686     {
    687 #ifdef MHD_POSIX_SOCKETS
    688       if (EINTR != errno)
    689         externalErrorExitDesc ("Unexpected select() error");
    690 #else
    691       if ((WSAEINVAL != WSAGetLastError ()) ||
    692           (0 != rs.fd_count) || (0 != ws.fd_count) || (0 != es.fd_count) )
    693         externalErrorExitDesc ("Unexpected select() error");
    694       Sleep (1);
    695 #endif
    696     }
    697     if (MHD_YES != MHD_run_from_select (d, &rs, &ws, &es))
    698       mhdErrorExitDesc ("MHD_run_from_select() failed");
    699   }
    700 
    701   return ret;
    702 }
    703 
    704 
    705 struct curlQueryParams
    706 {
    707   /* Destination path for CURL query */
    708   const char *queryPath;
    709 
    710   /* Custom query method, NULL for default */
    711   const char *method;
    712 
    713   /* Destination port for CURL query */
    714   uint16_t queryPort;
    715 
    716   /* List of additional request headers */
    717   struct curl_slist *headers;
    718 
    719   /* CURL query result error flag */
    720   volatile unsigned int queryError;
    721 
    722   /* Response HTTP code, zero if no response */
    723   volatile int responseCode;
    724 };
    725 
    726 
    727 /* Returns zero for successful response and non-zero for failed response */
    728 static unsigned int
    729 doCurlQueryInThread (struct MHD_Daemon *d,
    730                      struct curlQueryParams *p,
    731                      struct headers_check_result *hdr_res,
    732                      const char *expected_data,
    733                      size_t expected_data_size)
    734 {
    735   const union MHD_DaemonInfo *dinfo;
    736   CURL *c;
    737   struct lcurl_data_cb_param dcbp;
    738   CURLcode errornum;
    739   int use_external_poll;
    740   long resp_code;
    741 
    742   dinfo = MHD_get_daemon_info (d, MHD_DAEMON_INFO_FLAGS);
    743   if (NULL == dinfo)
    744     mhdErrorExitDesc ("MHD_get_daemon_info() failed");
    745   use_external_poll = (0 == (dinfo->flags
    746                              & MHD_USE_INTERNAL_POLLING_THREAD));
    747 
    748   if (NULL == p->queryPath)
    749     abort ();
    750 
    751   if (0 == p->queryPort)
    752     abort ();
    753 
    754   /* Test must not fail due to test's internal buffer shortage */
    755   dcbp.size = TEST_FAIL_SIZE * 2 + 1;
    756   dcbp.buf = malloc (dcbp.size);
    757   if (NULL == dcbp.buf)
    758     externalErrorExit ();
    759   dcbp.pos = 0;
    760 
    761   memset (hdr_res, 0, sizeof(*hdr_res));
    762 
    763   c = curlEasyInitForTest (p->queryPath, p->method, p->queryPort,
    764                            &dcbp, hdr_res, p->headers);
    765 
    766   if (! use_external_poll)
    767     errornum = curl_easy_perform (c);
    768   else
    769     errornum = performQueryExternal (d, c);
    770 
    771   if (CURLE_OK != curl_easy_getinfo (c, CURLINFO_RESPONSE_CODE, &resp_code))
    772     libcurlErrorExitDesc ("curl_easy_getinfo() failed");
    773 
    774   p->responseCode = (int) resp_code;
    775   if ((CURLE_OK == errornum) && (200 != resp_code))
    776   {
    777     fprintf (stderr,
    778              "Got reply with unexpected status code: %d\n",
    779              p->responseCode);
    780     mhdErrorExit ();
    781   }
    782 
    783   if (CURLE_OK != errornum)
    784   {
    785     if ((CURLE_GOT_NOTHING != errornum) && (CURLE_RECV_ERROR != errornum)
    786         && (CURLE_HTTP_RETURNED_ERROR != errornum))
    787     {
    788       if (CURLE_OPERATION_TIMEDOUT == errornum)
    789         mhdErrorExitDesc ("Request was aborted due to timeout");
    790       fprintf (stderr, "libcurl returned unexpected error: %s\n",
    791                curl_easy_strerror (errornum));
    792       mhdErrorExitDesc ("Request failed due to unexpected error");
    793     }
    794     p->queryError = 1;
    795     switch (resp_code)
    796     {
    797     case 0: /* No parsed response */
    798     case 413: /* "Content Too Large" */
    799     case 414: /* "URI Too Long" */
    800     case 431: /* "Request Header Fields Too Large" */
    801     case 501: /* "Not Implemented" */
    802       /* Expected error codes */
    803       break;
    804     default:
    805       fprintf (stderr,
    806                "Got reply with unexpected status code: %ld\n",
    807                resp_code);
    808       mhdErrorExit ();
    809     }
    810   }
    811   else
    812   {
    813     if (dcbp.pos != expected_data_size)
    814       mhdErrorExit ("libcurl reports wrong size of MHD reply body data");
    815     else if (0 != memcmp (expected_data, dcbp.buf,
    816                           expected_data_size))
    817       mhdErrorExit ("libcurl reports wrong MHD reply body data");
    818     else
    819       p->queryError = 0;
    820   }
    821 
    822   curl_easy_cleanup (c);
    823   free (dcbp.buf);
    824 
    825   return p->queryError;
    826 }
    827 
    828 
    829 /* Perform test queries, shut down MHD daemon, and free parameters */
    830 static unsigned int
    831 performTestQueries (struct MHD_Daemon *d, uint16_t d_port,
    832                     struct ahc_cls_type *ahc_param,
    833                     struct check_uri_cls *uri_cb_param)
    834 {
    835   struct curlQueryParams qParam;
    836   unsigned int ret = 0;          /* Return value */
    837   struct headers_check_result rp_headers_check;
    838   char *buf;
    839   size_t i;
    840   size_t first_failed_at = 0;
    841 
    842 
    843   buf = malloc (TEST_FAIL_SIZE + 1 + strlen (URL_SCHEME_HOST));
    844   if (NULL == buf)
    845     externalErrorExit ();
    846 
    847   /* Common parameters, to be individually overridden by specific test cases */
    848   qParam.queryPort = d_port;
    849   qParam.method = NULL;  /* Use libcurl default: GET */
    850   qParam.queryPath = URL_SCHEME_HOST EXPECTED_URI_BASE_PATH;
    851   qParam.headers = NULL; /* No additional headers */
    852   uri_cb_param->uri = EXPECTED_URI_BASE_PATH;
    853   ahc_param->rq_url = EXPECTED_URI_BASE_PATH;
    854   ahc_param->rq_method = "GET"; /* Default expected method */
    855 
    856   ahc_param->rp_data = "~";
    857   ahc_param->rp_data_size = 1;
    858   ahc_param->rp_large_hdr_name_size = 0;
    859   ahc_param->rp_large_hdr_value_size = 0;
    860   ahc_param->rp_num_n1_hdrs = 0;
    861 
    862   if (large_req_method)
    863   {
    864     for (i = 0; i < TEST_START_SIZE; i++)
    865       buf[i] = 'A' + (char) (unsigned char) (i % ('Z' - 'A' + 1));
    866     for (; i <= TEST_FAIL_SIZE; i++)
    867     {
    868       buf[i] = 0;
    869 
    870       qParam.method = buf;
    871       ahc_param->rq_method = buf;
    872 
    873       memset (&ahc_param->header_check_param, 0,
    874               sizeof (ahc_param->header_check_param));
    875       if (0 != doCurlQueryInThread (d, &qParam, &rp_headers_check,
    876                                     ahc_param->rp_data,
    877                                     ahc_param->rp_data_size))
    878       {
    879         if ((0 != qParam.responseCode) && (501 != qParam.responseCode))
    880         {
    881           fprintf (stderr,
    882                    "Got reply with status code %d, "
    883                    "while code 501 (\"Not Implemented\") is expected.\n",
    884                    qParam.responseCode);
    885           mhdErrorExit ();
    886         }
    887         if (TEST_OK_SIZE >= i)
    888         {
    889           fprintf (stderr,
    890                    "Request failed when running with the valid value size.\n");
    891           ret = 1; /* Failed too early */
    892         }
    893         if (0 == first_failed_at)
    894         {
    895           if (verbose)
    896             fprintf (stderr, "First failed size is %u.\n", (unsigned int) i);
    897           first_failed_at = i;
    898         }
    899       }
    900       else
    901       {
    902         if (TEST_FAIL_SIZE == i)
    903         {
    904           fprintf (stderr, "Request succeed with the largest size.\n");
    905           ret = 1; /* Succeed with largest value */
    906         }
    907       }
    908       if (0 != ahc_param->header_check_param.num_n1_headers)
    909         mhdErrorExitDesc ("Detected unexpected request headers");
    910       if (0 != ahc_param->header_check_param.large_header_name_size)
    911         mhdErrorExitDesc ("Detected unexpected large request header");
    912       if (0 != rp_headers_check.num_n1_headers)
    913         mhdErrorExitDesc ("Detected unexpected reply headers");
    914       if (0 != rp_headers_check.large_header_name_size)
    915         mhdErrorExitDesc ("Detected unexpected large reply header");
    916 
    917       buf[i] = 'A' + (char) (unsigned char) (i % ('Z' - 'A' + 1));
    918     }
    919   }
    920   else if (large_req_url)
    921   {
    922     const size_t base_size = strlen (URL_SCHEME_HOST);
    923     char *const url = buf + base_size;
    924 
    925     memcpy (buf, URL_SCHEME_HOST, base_size);
    926     url[0] = '/';
    927     for (i = 1; i < TEST_START_SIZE; i++)
    928       url[i] = 'a' + (char) (unsigned char) (i % ('z' - 'a' + 1));
    929     for (; i <= TEST_FAIL_SIZE; i++)
    930     {
    931       url[i] = 0;
    932 
    933       qParam.queryPath = buf;
    934       uri_cb_param->uri = url;
    935       ahc_param->rq_url = url;
    936 
    937       memset (&ahc_param->header_check_param, 0,
    938               sizeof (ahc_param->header_check_param));
    939       if (0 != doCurlQueryInThread (d, &qParam, &rp_headers_check,
    940                                     ahc_param->rp_data,
    941                                     ahc_param->rp_data_size))
    942       {
    943         if ((0 != qParam.responseCode) && (414 != qParam.responseCode))
    944         {
    945           fprintf (stderr,
    946                    "Got reply with status code %d, "
    947                    "while code 414 (\"URI Too Long\") is expected.\n",
    948                    qParam.responseCode);
    949           mhdErrorExit ();
    950         }
    951         if (TEST_OK_SIZE >= i)
    952         {
    953           fprintf (stderr,
    954                    "Request failed when running with the valid value size.\n");
    955           ret = 1; /* Failed too early */
    956         }
    957         if (0 == first_failed_at)
    958         {
    959           if (verbose)
    960             fprintf (stderr, "First failed size is %u.\n", (unsigned int) i);
    961           first_failed_at = i;
    962         }
    963       }
    964       else
    965       {
    966         if (TEST_FAIL_SIZE == i)
    967         {
    968           fprintf (stderr, "Request succeed with the largest size.\n");
    969           ret = 1; /* Succeed with largest value */
    970         }
    971       }
    972       if (0 != ahc_param->header_check_param.num_n1_headers)
    973         mhdErrorExitDesc ("Detected unexpected request headers");
    974       if (0 != ahc_param->header_check_param.large_header_name_size)
    975         mhdErrorExitDesc ("Detected unexpected large request header");
    976       if (0 != rp_headers_check.num_n1_headers)
    977         mhdErrorExitDesc ("Detected unexpected reply headers");
    978       if (0 != rp_headers_check.large_header_name_size)
    979         mhdErrorExitDesc ("Detected unexpected large reply header");
    980 
    981       url[i] = 'a' + (char) (unsigned char) (i % ('z' - 'a' + 1));
    982     }
    983   }
    984   else if (large_req_header_name)
    985   {
    986     buf[0] = '0'; /* Name starts with zero for unique identification */
    987     for (i = 1; i < TEST_START_SIZE; i++)
    988       buf[i] = 'a' + (char) (unsigned char) (i % ('z' - 'a' + 1));
    989     for (; i <= TEST_FAIL_SIZE; i++)
    990     {
    991       struct curl_slist *curl_headers;
    992       curl_headers = NULL;
    993 
    994       memcpy (buf + i, ": Z", 3); /* Note: strlen(": Z") is less than strlen(URL_SCHEME_HOST) */
    995       buf[i + 3] = 0;
    996 
    997       curl_headers = curl_slist_append (curl_headers, buf);
    998       if (NULL == curl_headers)
    999         externalErrorExit ();
   1000 
   1001       qParam.headers = curl_headers;
   1002 
   1003       memset (&ahc_param->header_check_param, 0,
   1004               sizeof (ahc_param->header_check_param));
   1005       if (0 != doCurlQueryInThread (d, &qParam, &rp_headers_check,
   1006                                     ahc_param->rp_data,
   1007                                     ahc_param->rp_data_size))
   1008       {
   1009         if ((0 != qParam.responseCode) && (431 != qParam.responseCode))
   1010         {
   1011           fprintf (stderr,
   1012                    "Got reply with status code %d, while code 431 "
   1013                    "(\"Request Header Fields Too Large\") is expected.\n",
   1014                    qParam.responseCode);
   1015           mhdErrorExit ();
   1016         }
   1017         if (0 != ahc_param->header_check_param.large_header_name_size)
   1018         { /* If large header was processed, it must be valid */
   1019           if (i != ahc_param->header_check_param.large_header_name_size)
   1020             mhdErrorExitDesc ("Detected wrong large request header name size");
   1021           if (1 != ahc_param->header_check_param.large_header_value_size)
   1022             mhdErrorExitDesc ("Detected wrong large request header value size");
   1023           if (0 == ahc_param->header_check_param.large_header_valid)
   1024             mhdErrorExitDesc ("Detected wrong large request header");
   1025         }
   1026         if (TEST_OK_SIZE >= i)
   1027         {
   1028           fprintf (stderr,
   1029                    "Request failed when running with the valid value size.\n");
   1030           ret = 1; /* Failed too early */
   1031         }
   1032         if (0 == first_failed_at)
   1033         {
   1034           if (verbose)
   1035             fprintf (stderr, "First failed size is %u.\n", (unsigned int) i);
   1036           first_failed_at = i;
   1037         }
   1038       }
   1039       else
   1040       {
   1041         if (i != ahc_param->header_check_param.large_header_name_size)
   1042           mhdErrorExitDesc ("Detected wrong large request header name size");
   1043         if (1 != ahc_param->header_check_param.large_header_value_size)
   1044           mhdErrorExitDesc ("Detected wrong large request header value size");
   1045         if (0 == ahc_param->header_check_param.large_header_valid)
   1046           mhdErrorExitDesc ("Detected wrong large request header");
   1047         if (TEST_FAIL_SIZE == i)
   1048         {
   1049           fprintf (stderr, "Request succeed with the largest size.\n");
   1050           ret = 1; /* Succeed with largest value */
   1051         }
   1052       }
   1053       if (0 != ahc_param->header_check_param.num_n1_headers)
   1054         mhdErrorExitDesc ("Detected unexpected request headers");
   1055       if (0 != rp_headers_check.num_n1_headers)
   1056         mhdErrorExitDesc ("Detected unexpected reply headers");
   1057       if (0 != rp_headers_check.large_header_name_size)
   1058         mhdErrorExitDesc ("Detected unexpected large reply header");
   1059 
   1060       curl_slist_free_all (curl_headers);
   1061       buf[i] = 'a' + (char) (unsigned char) (i % ('z' - 'a' + 1));
   1062     }
   1063   }
   1064   else if (large_req_header_value)
   1065   {
   1066     char *const hdr_value = buf + 3;
   1067     /* Name starts with zero for unique identification */
   1068     memcpy (buf, "0: ", 3); /* Note: strlen(": Z") is less than strlen(URL_SCHEME_HOST) */
   1069     for (i = 0; i < TEST_START_SIZE; i++)
   1070       hdr_value[i] = 'Z' - (char) (unsigned char) (i % ('Z' - 'A' + 1));
   1071     for (; i <= TEST_FAIL_SIZE; i++)
   1072     {
   1073       struct curl_slist *curl_headers;
   1074       curl_headers = NULL;
   1075 
   1076       hdr_value[i] = 0;
   1077 
   1078       curl_headers = curl_slist_append (curl_headers, buf);
   1079       if (NULL == curl_headers)
   1080         externalErrorExit ();
   1081 
   1082       qParam.headers = curl_headers;
   1083 
   1084       memset (&ahc_param->header_check_param, 0,
   1085               sizeof (ahc_param->header_check_param));
   1086       if (0 != doCurlQueryInThread (d, &qParam, &rp_headers_check,
   1087                                     ahc_param->rp_data,
   1088                                     ahc_param->rp_data_size))
   1089       {
   1090         if ((0 != qParam.responseCode) && (431 != qParam.responseCode))
   1091         {
   1092           fprintf (stderr,
   1093                    "Got reply with status code %d, while code 431 "
   1094                    "(\"Request Header Fields Too Large\") is expected.\n",
   1095                    qParam.responseCode);
   1096           mhdErrorExit ();
   1097         }
   1098         if (0 != ahc_param->header_check_param.large_header_name_size)
   1099         { /* If large header was processed, it must be valid */
   1100           if (1 != ahc_param->header_check_param.large_header_name_size)
   1101             mhdErrorExitDesc ("Detected wrong large request header name size");
   1102           if (i != ahc_param->header_check_param.large_header_value_size)
   1103             mhdErrorExitDesc ("Detected wrong large request header value size");
   1104           if (0 == ahc_param->header_check_param.large_header_valid)
   1105             mhdErrorExitDesc ("Detected wrong large request header");
   1106         }
   1107         if (TEST_OK_SIZE >= i)
   1108         {
   1109           fprintf (stderr,
   1110                    "Request failed when running with the valid value size.\n");
   1111           ret = 1; /* Failed too early */
   1112         }
   1113         if (0 == first_failed_at)
   1114         {
   1115           if (verbose)
   1116             fprintf (stderr, "First failed size is %u.\n", (unsigned int) i);
   1117           first_failed_at = i;
   1118         }
   1119       }
   1120       else
   1121       {
   1122         if (1 != ahc_param->header_check_param.large_header_name_size)
   1123           mhdErrorExitDesc ("Detected wrong large request header name size");
   1124         if (i != ahc_param->header_check_param.large_header_value_size)
   1125           mhdErrorExitDesc ("Detected wrong large request header value size");
   1126         if (0 == ahc_param->header_check_param.large_header_valid)
   1127           mhdErrorExitDesc ("Detected wrong large request header");
   1128         if (TEST_FAIL_SIZE == i)
   1129         {
   1130           fprintf (stderr, "Request succeed with the largest size.\n");
   1131           ret = 1; /* Succeed with largest value */
   1132         }
   1133       }
   1134       if (0 != ahc_param->header_check_param.num_n1_headers)
   1135         mhdErrorExitDesc ("Detected unexpected request headers");
   1136       if (0 != rp_headers_check.num_n1_headers)
   1137         mhdErrorExitDesc ("Detected unexpected reply headers");
   1138       if (0 != rp_headers_check.large_header_name_size)
   1139         mhdErrorExitDesc ("Detected unexpected large reply header");
   1140 
   1141       curl_slist_free_all (curl_headers);
   1142       hdr_value[i] = 'Z' - (char) (unsigned char) (i % ('Z' - 'A' + 1));
   1143     }
   1144   }
   1145   else if (large_req_headers)
   1146   {
   1147     unsigned int num_hdrs = 0;
   1148     struct curl_slist *curl_headers;
   1149     const size_t hdr_size = strlen (N1_HEADER_CRLF);
   1150 
   1151     curl_headers = NULL;
   1152 
   1153     for (i = 0; i < TEST_RQ_N1_START_SIZE; i += hdr_size)
   1154     {
   1155       curl_headers = curl_slist_append (curl_headers, N1_HEADER);
   1156       if (NULL == curl_headers)
   1157         externalErrorExit ();
   1158       num_hdrs++;
   1159     }
   1160     for (; i <= TEST_FAIL_SIZE; i += hdr_size)
   1161     {
   1162       qParam.headers = curl_headers;
   1163       ahc_param->header_check_param.num_n1_headers = num_hdrs;
   1164 
   1165       memset (&ahc_param->header_check_param, 0,
   1166               sizeof (ahc_param->header_check_param));
   1167       if (0 != doCurlQueryInThread (d, &qParam, &rp_headers_check,
   1168                                     ahc_param->rp_data,
   1169                                     ahc_param->rp_data_size))
   1170       {
   1171         if ((0 != qParam.responseCode) && (431 != qParam.responseCode))
   1172         {
   1173           fprintf (stderr,
   1174                    "Got reply with status code %d, while code 431 "
   1175                    "(\"Request Header Fields Too Large\") is expected.\n",
   1176                    qParam.responseCode);
   1177           mhdErrorExit ();
   1178         }
   1179         if (0 != ahc_param->header_check_param.num_n1_headers)
   1180         { /* If headers were processed, they must be valid */
   1181           if (num_hdrs != ahc_param->header_check_param.num_n1_headers)
   1182             mhdErrorExitDesc ("Detected wrong number of request headers");
   1183         }
   1184         if (TEST_RQ_N1_OK_SIZE >= i)
   1185         {
   1186           fprintf (stderr,
   1187                    "Request failed when running with the valid value size.\n");
   1188           ret = 1; /* Failed too early */
   1189         }
   1190         if (0 == first_failed_at)
   1191         {
   1192           if (verbose)
   1193             fprintf (stderr, "First failed size is %u.\n", (unsigned int) i);
   1194           first_failed_at = i;
   1195         }
   1196       }
   1197       else
   1198       {
   1199         if (num_hdrs != ahc_param->header_check_param.num_n1_headers)
   1200           mhdErrorExitDesc ("Detected wrong number of request headers");
   1201         if (TEST_FAIL_SIZE == i)
   1202         {
   1203           fprintf (stderr, "Request succeed with the largest size.\n");
   1204           ret = 1; /* Succeed with largest value */
   1205         }
   1206       }
   1207       if (0 != ahc_param->header_check_param.large_header_name_size)
   1208         mhdErrorExitDesc ("Detected unexpected large request header");
   1209       if (0 != rp_headers_check.num_n1_headers)
   1210         mhdErrorExitDesc ("Detected unexpected reply headers");
   1211       if (0 != rp_headers_check.large_header_name_size)
   1212         mhdErrorExitDesc ("Detected unexpected large reply header");
   1213 
   1214       curl_headers = curl_slist_append (curl_headers, N1_HEADER);
   1215       if (NULL == curl_headers)
   1216         externalErrorExit ();
   1217       num_hdrs++;
   1218     }
   1219     curl_slist_free_all (curl_headers);
   1220   }
   1221   else if (large_rsp_header_name)
   1222   {
   1223     for (i = TEST_START_SIZE; i <= TEST_FAIL_SIZE; i++)
   1224     {
   1225       ahc_param->rp_large_hdr_name_size = i;
   1226       ahc_param->rp_large_hdr_value_size = 1;
   1227 
   1228       memset (&ahc_param->header_check_param, 0,
   1229               sizeof (ahc_param->header_check_param));
   1230       if (0 != doCurlQueryInThread (d, &qParam, &rp_headers_check,
   1231                                     ahc_param->rp_data,
   1232                                     ahc_param->rp_data_size))
   1233       {
   1234         (void) qParam.responseCode; /* TODO: check for the right response code */
   1235         if (0 != rp_headers_check.large_header_name_size)
   1236           mhdErrorExitDesc ("Detected unexpected large reply header");
   1237         if (TEST_OK_SIZE >= i)
   1238         {
   1239           fprintf (stderr,
   1240                    "Request failed when running with the valid value size.\n");
   1241           ret = 1; /* Failed too early */
   1242         }
   1243         if (0 == first_failed_at)
   1244         {
   1245           if (verbose)
   1246             fprintf (stderr, "First failed size is %u.\n", (unsigned int) i);
   1247           first_failed_at = i;
   1248         }
   1249       }
   1250       else
   1251       {
   1252         if (i != rp_headers_check.large_header_name_size)
   1253           mhdErrorExitDesc ("Detected wrong large reply header name size");
   1254         if (1 != rp_headers_check.large_header_value_size)
   1255           mhdErrorExitDesc ("Detected wrong large reply header value size");
   1256         if (0 == rp_headers_check.large_header_valid)
   1257           mhdErrorExitDesc ("Detected wrong large reply header");
   1258         if (TEST_FAIL_SIZE == i)
   1259         {
   1260           fprintf (stderr, "Request succeed with the largest size.\n");
   1261           ret = 1; /* Succeed with largest value */
   1262         }
   1263       }
   1264       if (0 != ahc_param->header_check_param.num_n1_headers)
   1265         mhdErrorExitDesc ("Detected unexpected request headers");
   1266       if (0 != ahc_param->header_check_param.large_header_name_size)
   1267         mhdErrorExitDesc ("Detected unexpected large request header");
   1268       if (0 != rp_headers_check.num_n1_headers)
   1269         mhdErrorExitDesc ("Detected unexpected reply headers");
   1270     }
   1271   }
   1272   else if (large_rsp_header_value)
   1273   {
   1274     for (i = TEST_START_SIZE; i <= TEST_FAIL_SIZE; i++)
   1275     {
   1276       ahc_param->rp_large_hdr_name_size = 1;
   1277       ahc_param->rp_large_hdr_value_size = i;
   1278 
   1279       memset (&ahc_param->header_check_param, 0,
   1280               sizeof (ahc_param->header_check_param));
   1281       if (0 != doCurlQueryInThread (d, &qParam, &rp_headers_check,
   1282                                     ahc_param->rp_data,
   1283                                     ahc_param->rp_data_size))
   1284       {
   1285         (void) qParam.responseCode; /* TODO: check for the right response code */
   1286         if (0 != rp_headers_check.large_header_name_size)
   1287           mhdErrorExitDesc ("Detected unexpected large reply header");
   1288         if (TEST_OK_SIZE >= i)
   1289         {
   1290           fprintf (stderr,
   1291                    "Request failed when running with the valid value size.\n");
   1292           ret = 1; /* Failed too early */
   1293         }
   1294         if (0 == first_failed_at)
   1295         {
   1296           if (verbose)
   1297             fprintf (stderr, "First failed size is %u.\n", (unsigned int) i);
   1298           first_failed_at = i;
   1299         }
   1300       }
   1301       else
   1302       {
   1303         if (1 != rp_headers_check.large_header_name_size)
   1304           mhdErrorExitDesc ("Detected wrong large reply header name size");
   1305         if (i != rp_headers_check.large_header_value_size)
   1306           mhdErrorExitDesc ("Detected wrong large reply header value size");
   1307         if (0 == rp_headers_check.large_header_valid)
   1308           mhdErrorExitDesc ("Detected wrong large reply header");
   1309         if (TEST_FAIL_SIZE == i)
   1310         {
   1311           fprintf (stderr, "Request succeed with the largest size.\n");
   1312           ret = 1; /* Succeed with largest value */
   1313         }
   1314       }
   1315       if (0 != ahc_param->header_check_param.num_n1_headers)
   1316         mhdErrorExitDesc ("Detected unexpected request headers");
   1317       if (0 != ahc_param->header_check_param.large_header_name_size)
   1318         mhdErrorExitDesc ("Detected unexpected large request header");
   1319       if (0 != rp_headers_check.num_n1_headers)
   1320         mhdErrorExitDesc ("Detected unexpected reply headers");
   1321     }
   1322   }
   1323   else if (large_rsp_headers)
   1324   {
   1325     size_t num_hrds;
   1326     const size_t hdr_size = strlen (N1_HEADER_CRLF);
   1327 
   1328     for (num_hrds = TEST_START_SIZE / hdr_size;
   1329          num_hrds * hdr_size <= TEST_FAIL_SIZE; num_hrds++)
   1330     {
   1331       i = num_hrds * hdr_size;
   1332       ahc_param->rp_num_n1_hdrs = num_hrds;
   1333 
   1334       memset (&ahc_param->header_check_param, 0,
   1335               sizeof (ahc_param->header_check_param));
   1336       if (0 != doCurlQueryInThread (d, &qParam, &rp_headers_check,
   1337                                     ahc_param->rp_data,
   1338                                     ahc_param->rp_data_size))
   1339       {
   1340         (void) qParam.responseCode; /* TODO: check for the right response code */
   1341         if (0 != rp_headers_check.num_n1_headers)
   1342           mhdErrorExitDesc ("Detected unexpected reply headers");
   1343         if (TEST_OK_SIZE >= i)
   1344         {
   1345           fprintf (stderr,
   1346                    "Request failed when running with the valid value size.\n");
   1347           ret = 1; /* Failed too early */
   1348         }
   1349         if (0 == first_failed_at)
   1350         {
   1351           if (verbose)
   1352             fprintf (stderr, "First failed size is %u.\n", (unsigned int) i);
   1353           first_failed_at = i;
   1354         }
   1355       }
   1356       else
   1357       {
   1358         if (num_hrds != rp_headers_check.num_n1_headers)
   1359           mhdErrorExitDesc ("Detected wrong number of reply headers");
   1360         if (TEST_FAIL_SIZE == i)
   1361         {
   1362           fprintf (stderr, "Request succeed with the largest size.\n");
   1363           ret = 1; /* Succeed with largest value */
   1364         }
   1365       }
   1366       if (0 != ahc_param->header_check_param.num_n1_headers)
   1367         mhdErrorExitDesc ("Detected unexpected request headers");
   1368       if (0 != ahc_param->header_check_param.large_header_name_size)
   1369         mhdErrorExitDesc ("Detected unexpected large request header");
   1370       if (0 != rp_headers_check.large_header_name_size)
   1371         mhdErrorExitDesc ("Detected unexpected large reply header");
   1372     }
   1373   }
   1374   else
   1375     externalErrorExitDesc ("No valid test test was selected");
   1376 
   1377   MHD_stop_daemon (d);
   1378   free (buf);
   1379   free (uri_cb_param);
   1380   free (ahc_param);
   1381 
   1382   return ret;
   1383 }
   1384 
   1385 
   1386 enum testMhdThreadsType
   1387 {
   1388   testMhdThreadExternal              = 0,
   1389   testMhdThreadInternal              = MHD_USE_INTERNAL_POLLING_THREAD,
   1390   testMhdThreadInternalPerConnection = MHD_USE_THREAD_PER_CONNECTION
   1391                                        | MHD_USE_INTERNAL_POLLING_THREAD,
   1392   testMhdThreadInternalPool
   1393 };
   1394 
   1395 enum testMhdPollType
   1396 {
   1397   testMhdPollBySelect = 0,
   1398   testMhdPollByPoll   = MHD_USE_POLL,
   1399   testMhdPollByEpoll  = MHD_USE_EPOLL,
   1400   testMhdPollAuto     = MHD_USE_AUTO
   1401 };
   1402 
   1403 /* Get number of threads for thread pool depending
   1404  * on used poll function and test type. */
   1405 static unsigned int
   1406 testNumThreadsForPool (enum testMhdPollType pollType)
   1407 {
   1408   unsigned int numThreads = MHD_CPU_COUNT;
   1409   (void) pollType; /* Don't care about pollType for this test */
   1410   return numThreads; /* No practical limit for non-cleanup test */
   1411 }
   1412 
   1413 
   1414 static struct MHD_Daemon *
   1415 startTestMhdDaemon (enum testMhdThreadsType thrType,
   1416                     enum testMhdPollType pollType, uint16_t *pport,
   1417                     struct ahc_cls_type **ahc_param,
   1418                     struct check_uri_cls **uri_cb_param)
   1419 {
   1420   struct MHD_Daemon *d;
   1421   const union MHD_DaemonInfo *dinfo;
   1422 
   1423   if ((NULL == ahc_param) || (NULL == uri_cb_param))
   1424     abort ();
   1425 
   1426   *ahc_param = (struct ahc_cls_type *) malloc (sizeof(struct ahc_cls_type));
   1427   if (NULL == *ahc_param)
   1428     externalErrorExit ();
   1429   *uri_cb_param =
   1430     (struct check_uri_cls *) malloc (sizeof(struct check_uri_cls));
   1431   if (NULL == *uri_cb_param)
   1432     externalErrorExit ();
   1433 
   1434   if ( (0 == *pport) &&
   1435        (MHD_NO == MHD_is_feature_supported (MHD_FEATURE_AUTODETECT_BIND_PORT)) )
   1436   {
   1437     *pport = 4100;
   1438     if (large_req_method)
   1439       *pport += 1;
   1440     if (large_req_url)
   1441       *pport += 2;
   1442     if (large_req_header_name)
   1443       *pport += 3;
   1444     if (large_req_header_value)
   1445       *pport += 4;
   1446     if (large_req_headers)
   1447       *pport += 5;
   1448     if (large_rsp_header_name)
   1449       *pport += 6;
   1450     if (large_rsp_header_value)
   1451       *pport += 7;
   1452     if (large_rsp_headers)
   1453       *pport += 8;
   1454     if (! oneone)
   1455       *pport += 16;
   1456   }
   1457 
   1458   if (testMhdThreadExternal == thrType)
   1459     d = MHD_start_daemon (((unsigned int) thrType) | ((unsigned int) pollType)
   1460                           | (verbose ? MHD_USE_ERROR_LOG : 0)
   1461                           | MHD_USE_NO_THREAD_SAFETY,
   1462                           *pport, NULL, NULL,
   1463                           &ahcCheck, *ahc_param,
   1464                           MHD_OPTION_URI_LOG_CALLBACK, &check_uri_cb,
   1465                           *uri_cb_param,
   1466                           MHD_OPTION_CONNECTION_MEMORY_LIMIT,
   1467                           (size_t) BUFFER_SIZE,
   1468                           MHD_OPTION_APP_FD_SETSIZE, (int) FD_SETSIZE,
   1469                           MHD_OPTION_END);
   1470   else if (testMhdThreadInternalPool != thrType)
   1471     d = MHD_start_daemon (((unsigned int) thrType) | ((unsigned int) pollType)
   1472                           | (verbose ? MHD_USE_ERROR_LOG : 0),
   1473                           *pport, NULL, NULL,
   1474                           &ahcCheck, *ahc_param,
   1475                           MHD_OPTION_URI_LOG_CALLBACK, &check_uri_cb,
   1476                           *uri_cb_param,
   1477                           MHD_OPTION_CONNECTION_MEMORY_LIMIT,
   1478                           (size_t) BUFFER_SIZE,
   1479                           MHD_OPTION_END);
   1480   else
   1481     d = MHD_start_daemon (MHD_USE_INTERNAL_POLLING_THREAD
   1482                           | ((unsigned int) pollType)
   1483                           | (verbose ? MHD_USE_ERROR_LOG : 0),
   1484                           *pport, NULL, NULL,
   1485                           &ahcCheck, *ahc_param,
   1486                           MHD_OPTION_THREAD_POOL_SIZE,
   1487                           testNumThreadsForPool (pollType),
   1488                           MHD_OPTION_URI_LOG_CALLBACK, &check_uri_cb,
   1489                           *uri_cb_param,
   1490                           MHD_OPTION_CONNECTION_MEMORY_LIMIT,
   1491                           (size_t) BUFFER_SIZE,
   1492                           MHD_OPTION_END);
   1493 
   1494   if (NULL == d)
   1495   {
   1496     fprintf (stderr, "Failed to start MHD daemon, errno=%d.\n", errno);
   1497     abort ();
   1498   }
   1499 
   1500   if (0 == *pport)
   1501   {
   1502     dinfo = MHD_get_daemon_info (d, MHD_DAEMON_INFO_BIND_PORT);
   1503     if ((NULL == dinfo) || (0 == dinfo->port) )
   1504     {
   1505       fprintf (stderr, "MHD_get_daemon_info() failed.\n");
   1506       abort ();
   1507     }
   1508     *pport = dinfo->port;
   1509     if (0 == global_port)
   1510       global_port = *pport; /* Reuse the same port for all tests */
   1511   }
   1512 
   1513   return d;
   1514 }
   1515 
   1516 
   1517 /* Test runners */
   1518 
   1519 
   1520 static unsigned int
   1521 testExternalGet (void)
   1522 {
   1523   struct MHD_Daemon *d;
   1524   uint16_t d_port = global_port; /* Daemon's port */
   1525   struct ahc_cls_type *ahc_param;
   1526   struct check_uri_cls *uri_cb_param;
   1527 
   1528   d = startTestMhdDaemon (testMhdThreadExternal, testMhdPollBySelect, &d_port,
   1529                           &ahc_param, &uri_cb_param);
   1530 
   1531   return performTestQueries (d, d_port, ahc_param, uri_cb_param);
   1532 }
   1533 
   1534 
   1535 static unsigned int
   1536 testInternalGet (enum testMhdPollType pollType)
   1537 {
   1538   struct MHD_Daemon *d;
   1539   uint16_t d_port = global_port; /* Daemon's port */
   1540   struct ahc_cls_type *ahc_param;
   1541   struct check_uri_cls *uri_cb_param;
   1542 
   1543   d = startTestMhdDaemon (testMhdThreadInternal, pollType, &d_port,
   1544                           &ahc_param, &uri_cb_param);
   1545 
   1546   return performTestQueries (d, d_port, ahc_param, uri_cb_param);
   1547 }
   1548 
   1549 
   1550 static unsigned int
   1551 testMultithreadedGet (enum testMhdPollType pollType)
   1552 {
   1553   struct MHD_Daemon *d;
   1554   uint16_t d_port = global_port; /* Daemon's port */
   1555   struct ahc_cls_type *ahc_param;
   1556   struct check_uri_cls *uri_cb_param;
   1557 
   1558   d = startTestMhdDaemon (testMhdThreadInternalPerConnection, pollType, &d_port,
   1559                           &ahc_param, &uri_cb_param);
   1560   return performTestQueries (d, d_port, ahc_param, uri_cb_param);
   1561 }
   1562 
   1563 
   1564 static unsigned int
   1565 testMultithreadedPoolGet (enum testMhdPollType pollType)
   1566 {
   1567   struct MHD_Daemon *d;
   1568   uint16_t d_port = global_port; /* Daemon's port */
   1569   struct ahc_cls_type *ahc_param;
   1570   struct check_uri_cls *uri_cb_param;
   1571 
   1572   d = startTestMhdDaemon (testMhdThreadInternalPool, pollType, &d_port,
   1573                           &ahc_param, &uri_cb_param);
   1574   return performTestQueries (d, d_port, ahc_param, uri_cb_param);
   1575 }
   1576 
   1577 
   1578 int
   1579 main (int argc, char *const *argv)
   1580 {
   1581   unsigned int errorCount = 0;
   1582   unsigned int test_result = 0;
   1583   verbose = 0;
   1584 
   1585   if ((NULL == argv) || (0 == argv[0]))
   1586     return 99;
   1587   oneone = ! has_in_name (argv[0], "10");
   1588   large_req_method = has_in_name (argv[0], "_method") ? 1 : 0;
   1589   large_req_url = has_in_name (argv[0], "_url") ? 1 : 0;
   1590   large_req_header_name = has_in_name (argv[0], "_request_header_name") ?
   1591                           1 : 0;
   1592   large_req_header_value = has_in_name (argv[0], "_request_header_value") ?
   1593                            1 : 0;
   1594   large_req_headers = has_in_name (argv[0], "_request_headers") ? 1 : 0;
   1595   large_rsp_header_name = has_in_name (argv[0], "_reply_header_name") ?
   1596                           1 : 0;
   1597   large_rsp_header_value = has_in_name (argv[0], "_reply_header_value") ?
   1598                            1 : 0;
   1599   large_rsp_headers = has_in_name (argv[0], "_reply_headers") ? 1 : 0;
   1600   if (large_req_method + large_req_url + large_req_header_name
   1601       + large_req_header_value + large_req_headers + large_rsp_header_name
   1602       + large_rsp_header_value + large_rsp_headers != 1)
   1603     return 99;
   1604   verbose = ! (has_param (argc, argv, "-q") ||
   1605                has_param (argc, argv, "--quiet") ||
   1606                has_param (argc, argv, "-s") ||
   1607                has_param (argc, argv, "--silent"));
   1608 
   1609   test_global_init ();
   1610 
   1611   /* Could be set to non-zero value to enforce using specific port
   1612    * in the test */
   1613   global_port = 0;
   1614   test_result = testExternalGet ();
   1615   if (test_result)
   1616     fprintf (stderr, "FAILED: testExternalGet () - %u.\n", test_result);
   1617   else if (verbose)
   1618     printf ("PASSED: testExternalGet ().\n");
   1619   errorCount += test_result;
   1620   if (MHD_YES == MHD_is_feature_supported (MHD_FEATURE_THREADS))
   1621   {
   1622     test_result = testInternalGet (testMhdPollAuto);
   1623     if (test_result)
   1624       fprintf (stderr, "FAILED: testInternalGet (testMhdPollAuto) - %u.\n",
   1625                test_result);
   1626     else if (verbose)
   1627       printf ("PASSED: testInternalGet (testMhdPollBySelect).\n");
   1628     errorCount += test_result;
   1629 #ifdef _MHD_HEAVY_TESTS
   1630     /* Actually tests are not heavy, but took too long to complete while
   1631      * not really provide any additional results. */
   1632     test_result = testInternalGet (testMhdPollBySelect);
   1633     if (test_result)
   1634       fprintf (stderr, "FAILED: testInternalGet (testMhdPollBySelect) - %u.\n",
   1635                test_result);
   1636     else if (verbose)
   1637       printf ("PASSED: testInternalGet (testMhdPollBySelect).\n");
   1638     errorCount += test_result;
   1639     test_result = testMultithreadedPoolGet (testMhdPollBySelect);
   1640     if (test_result)
   1641       fprintf (stderr,
   1642                "FAILED: testMultithreadedPoolGet (testMhdPollBySelect) - %u.\n",
   1643                test_result);
   1644     else if (verbose)
   1645       printf ("PASSED: testMultithreadedPoolGet (testMhdPollBySelect).\n");
   1646     errorCount += test_result;
   1647     test_result = testMultithreadedGet (testMhdPollBySelect);
   1648     if (test_result)
   1649       fprintf (stderr,
   1650                "FAILED: testMultithreadedGet (testMhdPollBySelect) - %u.\n",
   1651                test_result);
   1652     else if (verbose)
   1653       printf ("PASSED: testMultithreadedGet (testMhdPollBySelect).\n");
   1654     errorCount += test_result;
   1655     if (MHD_YES == MHD_is_feature_supported (MHD_FEATURE_POLL))
   1656     {
   1657       test_result = testInternalGet (testMhdPollByPoll);
   1658       if (test_result)
   1659         fprintf (stderr, "FAILED: testInternalGet (testMhdPollByPoll) - %u.\n",
   1660                  test_result);
   1661       else if (verbose)
   1662         printf ("PASSED: testInternalGet (testMhdPollByPoll).\n");
   1663       errorCount += test_result;
   1664     }
   1665     if (MHD_YES == MHD_is_feature_supported (MHD_FEATURE_EPOLL))
   1666     {
   1667       test_result = testInternalGet (testMhdPollByEpoll);
   1668       if (test_result)
   1669         fprintf (stderr, "FAILED: testInternalGet (testMhdPollByEpoll) - %u.\n",
   1670                  test_result);
   1671       else if (verbose)
   1672         printf ("PASSED: testInternalGet (testMhdPollByEpoll).\n");
   1673       errorCount += test_result;
   1674     }
   1675 #else
   1676     /* Mute compiler warnings */
   1677     (void) testMultithreadedGet;
   1678     (void) testMultithreadedPoolGet;
   1679 #endif /* _MHD_HEAVY_TESTS */
   1680   }
   1681   if (0 != errorCount)
   1682     fprintf (stderr,
   1683              "Error (code: %u)\n",
   1684              errorCount);
   1685   else if (verbose)
   1686     printf ("All tests passed.\n");
   1687 
   1688   test_global_cleanup ();
   1689 
   1690   return (errorCount == 0) ? 0 : 1;       /* 0 == pass */
   1691 }