libmicrohttpd

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

test_toolarge.c (57417B)


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