libmicrohttpd

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

test_get.c (51023B)


      1 /*
      2      This file is part of libmicrohttpd
      3      Copyright (C) 2007, 2008 Christian Grothoff
      4      Copyright (C) 2016-2022 Evgeny Grin (Karlson2k)
      5 
      6      libmicrohttpd is free software; you can redistribute it and/or modify
      7      it under the terms of the GNU General Public License as published
      8      by the Free Software Foundation; either version 2, or (at your
      9      option) any later version.
     10 
     11      libmicrohttpd is distributed in the hope that it will be useful, but
     12      WITHOUT ANY WARRANTY; without even the implied warranty of
     13      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     14      General Public License for more details.
     15 
     16      You should have received a copy of the GNU General Public License
     17      along with libmicrohttpd; see the file COPYING.  If not, write to the
     18      Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
     19      Boston, MA 02110-1301, USA.
     20 */
     21 
     22 /**
     23  * @file testzzuf/test_get.c
     24  * @brief  Several testcases for libmicrohttpd with input fuzzing
     25  * @author Christian Grothoff
     26  * @author Karlson2k (Evgeny Grin)
     27  */
     28 
     29 #include "platform.h"
     30 #include <curl/curl.h>
     31 #include <microhttpd.h>
     32 #include <stdlib.h>
     33 #include <string.h>
     34 #include <time.h>
     35 #include <errno.h>
     36 
     37 #ifndef WINDOWS
     38 #include <unistd.h>
     39 #endif
     40 
     41 #include "mhd_debug_funcs.h"
     42 #include "test_helpers.h"
     43 
     44 #ifndef MHD_STATICSTR_LEN_
     45 /**
     46  * Determine length of static string / macro strings at compile time.
     47  */
     48 #define MHD_STATICSTR_LEN_(macro) (sizeof(macro) / sizeof(char) - 1)
     49 #endif /* ! MHD_STATICSTR_LEN_ */
     50 
     51 #ifndef CURL_VERSION_BITS
     52 #define CURL_VERSION_BITS(x,y,z) ((x) << 16 | (y) << 8 | (z))
     53 #endif /* ! CURL_VERSION_BITS */
     54 #ifndef CURL_AT_LEAST_VERSION
     55 #define CURL_AT_LEAST_VERSION(x,y,z) \
     56   (LIBCURL_VERSION_NUM >= CURL_VERSION_BITS (x, y, z))
     57 #endif /* ! CURL_AT_LEAST_VERSION */
     58 
     59 /**
     60  * A larger loop count will run more random tests --
     61  * which would be good, except that it may take too
     62  * long for most user's patience.  So this small
     63  * value is the default.
     64  * Can be redefined by CPPFLAGS=-DLOOP_COUNT=123
     65  */
     66 #ifndef LOOP_COUNT
     67 #ifndef _MHD_VHEAVY_TESTS
     68 #define LOOP_COUNT 10
     69 #else  /* ! _MHD_HEAVY_TESTS */
     70 #define LOOP_COUNT 200
     71 #endif /* ! _MHD_HEAVY_TESTS */
     72 #endif /* LOOP_COUNT */
     73 
     74 #ifdef _DEBUG
     75 /* Uncomment the next line (or use CPPFLAGS) to see all request and response bodies in log */
     76 /* #define TEST_PRINT_BODY */
     77 /* Uncomment the next line (or use CPPFLAGS) to see all request bodies as they are sent by libcurl */
     78 /* #define TEST_PRINT_BODY_RQ 1 */
     79 /* Uncomment the next line (or use CPPFLAGS) to see all request bodies as they are received by libcurl */
     80 /* #define TEST_PRINT_BODY_RP 1 */
     81 #endif /* _DEBUG */
     82 
     83 #define MHD_TIMEOUT 2
     84 
     85 #define CURL_TIMEOUT 5
     86 
     87 /* Global test parameters */
     88 static int oneone;
     89 static int dry_run;
     90 static int use_get;
     91 static int use_get_chunked;
     92 static int use_put;
     93 static int use_put_large;
     94 static int use_put_chunked;
     95 static int use_post;
     96 static int use_post_form;
     97 static int use_long_header;
     98 static int use_long_uri;
     99 static int use_close;
    100 static int run_with_socat;
    101 
    102 #define TEST_BASE_URI "http:/" "/127.0.0.1/test_uri"
    103 #define TEST_BASE_URI_SOCAT "http:/" "/127.0.0.121/test_uri"
    104 
    105 #define SOCAT_PORT 10121
    106 
    107 #define TEST_BASE_PORT 4010
    108 
    109 #define EMPTY_PAGE "Empty page."
    110 #define EMPTY_PAGE_ALT "Alternative empty page."
    111 #define METHOD_NOT_SUPPORTED "HTTP method is not supported."
    112 #define POST_DATA_BROKEN "The POST request is ill-formed."
    113 
    114 #define POST_KEY1 "test"
    115 #define POST_VALUE1 "test_post"
    116 #define POST_KEY2 "library"
    117 #define POST_VALUE2 "GNU libmicrohttpd"
    118 #define POST_URLENC_DATA \
    119   POST_KEY1 "=" POST_VALUE1 "&" POST_KEY2 "=" "GNU%20libmicrohttpd"
    120 
    121 #define PUT_NORMAL_SIZE 11
    122 /* Does not need to be very large as MHD buffer will be made smaller anyway */
    123 #define PUT_LARGE_SIZE (4 * 1024)
    124 /* The length of "very long" URI and header strings. MHD uses smaller buffer. */
    125 #define TEST_STRING_VLONG_LEN 8 * 1024
    126 
    127 
    128 #if ! CURL_AT_LEAST_VERSION (7,56,0)
    129 #define TEST_USE_STATIC_POST_DATA 1
    130 static struct curl_httppost *post_first;
    131 static struct curl_httppost *post_last;
    132 #endif /* ! CURL_AT_LEAST_VERSION(7,56,0) */
    133 
    134 static struct curl_slist *libcurl_long_header;
    135 
    136 /**
    137  * Initialise long header for libcurl
    138  *
    139  * @return non-zero if succeed,
    140  *         zero if failed
    141  */
    142 static int
    143 long_header_init (void)
    144 {
    145   char *buf;
    146 
    147   buf = malloc (TEST_STRING_VLONG_LEN + 1);
    148   if (NULL == buf)
    149   {
    150     fprintf (stderr, "malloc() failed "
    151              "at line %d.\n", (int) __LINE__);
    152     return 0;
    153   }
    154   buf[TEST_STRING_VLONG_LEN] = 0;
    155   buf[0] = 'A';
    156   memset (buf + 1, 'a', TEST_STRING_VLONG_LEN / 2 - 2);
    157   buf[TEST_STRING_VLONG_LEN / 2 - 1] = ':';
    158   buf[TEST_STRING_VLONG_LEN / 2] = ' ';
    159   memset (buf + TEST_STRING_VLONG_LEN / 2 + 1, 'c',
    160           TEST_STRING_VLONG_LEN / 2 - 1);
    161   libcurl_long_header = curl_slist_append (NULL, buf);
    162   free (buf);
    163   if (NULL != libcurl_long_header)
    164     return ! 0; /* Success exit point */
    165 
    166   fprintf (stderr, "curl_slist_append() failed "
    167            "at line %d.\n", (int) __LINE__);
    168   return 0; /* Failure exit point */
    169 }
    170 
    171 
    172 /**
    173  * Globally initialise test environment
    174  * @return non-zero if succeed,
    175  *         zero if failed
    176  */
    177 static int
    178 test_global_init (void)
    179 {
    180   libcurl_long_header = NULL;
    181   if (CURLE_OK != curl_global_init (CURL_GLOBAL_WIN32))
    182   {
    183     fprintf (stderr, "curl_global_init() failed "
    184              "at line %d.\n", (int) __LINE__);
    185     return 0;
    186   }
    187 
    188   if (long_header_init ())
    189   {
    190 #ifndef TEST_USE_STATIC_POST_DATA
    191     return 1; /* Success exit point */
    192 #else  /* ! TEST_USE_STATIC_POST_DATA */
    193     post_first = NULL;
    194     post_last = NULL;
    195     if ((CURL_FORMADD_OK !=
    196          curl_formadd (&post_first, &post_last,
    197                        CURLFORM_PTRNAME, POST_KEY1,
    198                        CURLFORM_NAMELENGTH,
    199                        (long) MHD_STATICSTR_LEN_ (POST_KEY1),
    200                        CURLFORM_PTRCONTENTS, POST_VALUE1,
    201 #if CURL_AT_LEAST_VERSION (7,46,0)
    202                        CURLFORM_CONTENTLEN,
    203                        (curl_off_t) MHD_STATICSTR_LEN_ (POST_VALUE1),
    204 #else  /* ! CURL_AT_LEAST_VERSION(7,46,0) */
    205                        CURLFORM_CONTENTSLENGTH,
    206                        (long) MHD_STATICSTR_LEN_ (POST_VALUE1),
    207 #endif /* ! CURL_AT_LEAST_VERSION(7,46,0) */
    208                        CURLFORM_END)) ||
    209         (CURL_FORMADD_OK !=
    210          curl_formadd (&post_first, &post_last,
    211                        CURLFORM_PTRNAME, POST_KEY2,
    212                        CURLFORM_NAMELENGTH,
    213                        (long) MHD_STATICSTR_LEN_ (POST_KEY2),
    214                        CURLFORM_PTRCONTENTS, POST_VALUE2,
    215 #if CURL_AT_LEAST_VERSION (7,46,0)
    216                        CURLFORM_CONTENTLEN,
    217                        (curl_off_t) MHD_STATICSTR_LEN_ (POST_VALUE2),
    218 #else  /* ! CURL_AT_LEAST_VERSION(7,46,0) */
    219                        CURLFORM_CONTENTSLENGTH,
    220                        (long) MHD_STATICSTR_LEN_ (POST_VALUE2),
    221 #endif /* ! CURL_AT_LEAST_VERSION(7,46,0) */
    222                        CURLFORM_END)))
    223       fprintf (stderr, "curl_formadd() failed "
    224                "at line %d.\n", (int) __LINE__);
    225     else
    226       return 1; /* Success exit point */
    227 
    228     if (NULL != post_first)
    229       curl_formfree (post_first);
    230     curl_slist_free_all (libcurl_long_header);
    231 #endif /* ! CURL_AT_LEAST_VERSION(7,56,0) */
    232   }
    233   curl_global_cleanup ();
    234   return 0; /* Failure exit point */
    235 }
    236 
    237 
    238 /**
    239  * Globally de-initialise test environment
    240  */
    241 static void
    242 test_global_deinit (void)
    243 {
    244 #ifdef TEST_USE_STATIC_POST_DATA
    245   curl_formfree (post_first);
    246 #endif /* TEST_USE_STATIC_POST_DATA */
    247   curl_global_cleanup ();
    248   if (NULL != libcurl_long_header)
    249     curl_slist_free_all (libcurl_long_header);
    250 }
    251 
    252 
    253 /**
    254  * libcurl callback parameters for uploads, downloads and debug callbacks
    255  */
    256 struct CBC
    257 {
    258   /* Upload members */
    259   size_t up_pos;
    260   size_t up_size;
    261   /* Download members */
    262   char *dn_buf;
    263   size_t dn_pos;
    264   size_t dn_buf_size;
    265   /* Debug callback members */
    266   unsigned int excess_found;
    267 };
    268 
    269 static void
    270 initCBC (struct CBC *libcurlcbc, char *dn_buf, size_t dn_buf_size)
    271 {
    272   libcurlcbc->up_pos = 0;
    273   if (use_put_large)
    274     libcurlcbc->up_size = PUT_LARGE_SIZE;
    275   else if (use_put)
    276     libcurlcbc->up_size = PUT_NORMAL_SIZE;
    277   else
    278     libcurlcbc->up_size = 0;
    279   libcurlcbc->dn_buf = dn_buf;
    280   libcurlcbc->dn_pos = 0;
    281   libcurlcbc->dn_buf_size = dn_buf_size;
    282   libcurlcbc->excess_found = 0;
    283 }
    284 
    285 
    286 static void
    287 resetCBC (struct CBC *libcurlcbc)
    288 {
    289   libcurlcbc->up_pos = 0;
    290   libcurlcbc->dn_pos = 0;
    291 }
    292 
    293 
    294 static size_t
    295 putBuffer (void *stream, size_t item_size, size_t nitems, void *ctx)
    296 {
    297   size_t to_fill;
    298   size_t i;
    299   struct CBC *cbc = ctx;
    300 
    301   to_fill = cbc->up_size - cbc->up_pos;
    302   /* Skip overflow check as the return value is valid anyway */
    303   if (use_put_chunked)
    304   {
    305     /* Send data as several chunks */
    306     if (to_fill > cbc->up_size / 3)
    307       to_fill = cbc->up_size / 3;
    308   }
    309   if (to_fill > item_size * nitems)
    310     to_fill = item_size * nitems;
    311 
    312   /* Avoid libcurl magic numbers */
    313 #ifdef CURL_READFUNC_PAUSE
    314   if (CURL_READFUNC_ABORT == to_fill)
    315     to_fill -= 2;
    316 #endif /* CURL_READFUNC_PAUSE */
    317 #ifdef CURL_READFUNC_ABORT
    318   if (CURL_READFUNC_ABORT == to_fill)
    319     --to_fill;
    320 #endif /* CURL_READFUNC_ABORT */
    321   for (i = 0; i < to_fill; ++i)
    322     ((char *) stream)[i] = 'a' + (char) ((cbc->up_pos + i)
    323                                          % (unsigned char) ('z' - 'a' + 1));
    324 
    325   cbc->up_pos += to_fill;
    326   return to_fill;
    327 }
    328 
    329 
    330 static size_t
    331 copyBuffer (void *ptr, size_t size, size_t nmemb, void *ctx)
    332 {
    333   struct CBC *cbc = ctx;
    334 
    335   if (cbc->dn_pos + size * nmemb > cbc->dn_buf_size)
    336     return 0;                   /* overflow */
    337   memcpy (&cbc->dn_buf[cbc->dn_pos], ptr, size * nmemb);
    338   cbc->dn_pos += size * nmemb;
    339   return size * nmemb;
    340 }
    341 
    342 
    343 #define TEST_MAGIC_MARKER0 0xFEE1C0DE
    344 #define TEST_MAGIC_MARKER1 (TEST_MAGIC_MARKER0 + 1)
    345 #define TEST_MAGIC_MARKER2 (TEST_MAGIC_MARKER0 + 2)
    346 
    347 struct content_cb_param_strct
    348 {
    349   unsigned int magic0;    /**< Must have TEST_MAGIC_MARKER0 value */
    350   struct MHD_Response *response; /**< The pointer to the response structure */
    351 };
    352 
    353 /**
    354  * MHD content reader callback that returns
    355  * data in chunks.
    356  */
    357 static ssize_t
    358 content_cb (void *cls, uint64_t pos, char *buf, size_t max)
    359 {
    360   struct content_cb_param_strct *param = (struct content_cb_param_strct *) cls;
    361   size_t fill_size;
    362 
    363   if ((unsigned int) TEST_MAGIC_MARKER0 != param->magic0)
    364   {
    365     fprintf (stderr, "Wrong cls pointer "
    366              "at line %d.\n", (int) __LINE__);
    367     fflush (stderr);
    368     abort ();
    369   }
    370 
    371   if (pos >= 128 * 10)
    372   {
    373     if (MHD_YES !=
    374         MHD_add_response_footer (param->response, "Footer", "working"))
    375     {
    376       fprintf (stderr, "MHD_add_response_footer() failed "
    377                "at line %d.\n", (int) __LINE__);
    378       fflush (stderr);
    379       abort ();
    380     }
    381     return MHD_CONTENT_READER_END_OF_STREAM;
    382   }
    383 
    384   if (128 > max)
    385     fill_size = 128;
    386   else
    387     fill_size = max;
    388   memset (buf, 'A' + (char) (unsigned int) (pos / 128), fill_size);
    389 
    390   return (ssize_t) fill_size;
    391 }
    392 
    393 
    394 /**
    395  * Deallocate memory for callback cls.
    396  */
    397 static void
    398 crcf (void *ptr)
    399 {
    400   free (ptr);
    401 }
    402 
    403 
    404 struct req_process_strct
    405 {
    406   unsigned int magic2;   /**< Must have TEST_MAGIC_MARKER2 value */
    407   int is_static;         /**< Non-zero if statically allocated, zero if malloc()'ed */
    408   struct MHD_PostProcessor *postprocsr;
    409   unsigned int post_data_sum;
    410 };
    411 
    412 static enum MHD_Result
    413 post_iterator (void *cls,
    414                enum MHD_ValueKind kind,
    415                const char *key,
    416                const char *filename,
    417                const char *content_type,
    418                const char *transfer_encoding,
    419                const char *value, uint64_t off, size_t size)
    420 {
    421   struct req_process_strct *param = (struct req_process_strct *) cls;
    422   size_t i;
    423 
    424   (void) filename; (void) content_type; (void) transfer_encoding;
    425   (void) off; /* Unused. Mute compiler warnings. */
    426 
    427   if (TEST_MAGIC_MARKER2 != param->magic2)
    428   {
    429     fprintf (stderr, "The 'param->magic2' has wrong value "
    430              "at line %d.\n", (int) __LINE__);
    431     abort ();
    432   }
    433 
    434   if (MHD_POSTDATA_KIND != kind)
    435   {
    436     fprintf (stderr, "The 'kind' parameter has wrong value "
    437              "at line %d.\n", (int) __LINE__);
    438     abort ();
    439   }
    440 
    441   if (NULL != key)
    442     param->post_data_sum += (unsigned int) strlen (key);
    443 
    444   for (i = 0; size > i; ++i)
    445     param->post_data_sum += (unsigned int) (unsigned char) value[i];
    446 
    447   return MHD_YES;
    448 }
    449 
    450 
    451 static void
    452 free_req_pr_data (struct req_process_strct *pr_data)
    453 {
    454   if (NULL == pr_data)
    455     return;
    456   if (TEST_MAGIC_MARKER2 != pr_data->magic2)
    457   {
    458     fprintf (stderr, "The 'pr_data->magic2' has wrong value "
    459              "at line %d.\n", (int) __LINE__);
    460     abort ();
    461   }
    462   if (pr_data->is_static)
    463   {
    464     if (NULL != pr_data->postprocsr)
    465     {
    466       fprintf (stderr, "The 'pr_data->postprocsr' has wrong value "
    467                "at line %d.\n", (int) __LINE__);
    468       abort ();
    469     }
    470     return;
    471   }
    472   if (NULL != pr_data->postprocsr)
    473     MHD_destroy_post_processor (pr_data->postprocsr);
    474   pr_data->postprocsr = NULL;
    475   free (pr_data);
    476 }
    477 
    478 
    479 struct ahc_param_strct
    480 {
    481   unsigned int magic1;   /**< Must have TEST_MAGIC_MARKER1 value */
    482   unsigned int err_flag; /**< Non-zero if any error is encountered */
    483   unsigned int num_replies; /**< The number of replies sent for the current request */
    484 };
    485 
    486 static enum MHD_Result
    487 send_error_response (struct MHD_Connection *connection,
    488                      struct ahc_param_strct *param,
    489                      unsigned int status_code,
    490                      const char *static_text,
    491                      const size_t static_text_len)
    492 {
    493   struct MHD_Response *response;
    494   response =
    495     MHD_create_response_from_buffer_static (static_text_len,
    496                                             static_text);
    497   if (NULL != response)
    498   {
    499     if (MHD_YES == MHD_add_response_header (response,
    500                                             MHD_HTTP_HEADER_CONNECTION,
    501                                             "close"))
    502     {
    503       if (MHD_YES == MHD_queue_response (connection, status_code, response))
    504       {
    505         MHD_destroy_response (response);
    506         return MHD_YES; /* Success exit point */
    507       }
    508       else
    509         fprintf (stderr, "MHD_queue_response() failed "
    510                  "at line %d.\n", (int) __LINE__);
    511     }
    512     else
    513       fprintf (stderr, "MHD_add_response_header() failed "
    514                "at line %d.\n", (int) __LINE__);
    515     MHD_destroy_response (response);
    516   }
    517   else
    518     fprintf (stderr, "MHD_create_response_from_callback() failed "
    519              "at line %d.\n", (int) __LINE__);
    520 
    521   param->err_flag = 1;
    522   return MHD_NO; /* Failure exit point */
    523 }
    524 
    525 
    526 static enum MHD_Result
    527 ahc_check (void *cls,
    528            struct MHD_Connection *connection,
    529            const char *url,
    530            const char *method,
    531            const char *version,
    532            const char *upload_data, size_t *upload_data_size,
    533            void **req_cls)
    534 {
    535   static struct req_process_strct static_req_pr_data = {
    536     TEST_MAGIC_MARKER2, ! 0, NULL, 0
    537   };
    538   struct req_process_strct *req_pr_data;
    539   struct ahc_param_strct *param = (struct ahc_param_strct *) cls;
    540   struct MHD_Response *response;
    541   enum MHD_Result ret;
    542   unsigned char data_sum;
    543   int is_post_req;
    544 
    545   if (NULL == cls)
    546   {
    547     fprintf (stderr, "The 'cls' parameter is NULL "
    548              "at line %d.\n", (int) __LINE__);
    549     fflush (stderr);
    550     abort ();
    551   }
    552   if ((unsigned int) TEST_MAGIC_MARKER1 != param->magic1)
    553   {
    554     fprintf (stderr, "The 'param->magic1' has wrong value "
    555              "at line %d.\n", (int) __LINE__);
    556     fflush (stderr);
    557     abort ();
    558   }
    559   if (NULL == connection)
    560   {
    561     fprintf (stderr, "The 'connection' parameter is NULL "
    562              "at line %d.\n", (int) __LINE__);
    563     param->err_flag = 1;
    564     return MHD_NO; /* Should not reply */
    565   }
    566   if (1)
    567   { /* Simple check for 'connection' parameter validity */
    568     const union MHD_ConnectionInfo *conn_info;
    569     conn_info =
    570       MHD_get_connection_info (connection,
    571                                MHD_CONNECTION_INFO_CONNECTION_TIMEOUT);
    572     if (NULL == conn_info)
    573     {
    574       fprintf (stderr, "The 'MHD_get_connection_info' has returned NULL "
    575                "at line %d.\n", (int) __LINE__);
    576       param->err_flag = 1;
    577     }
    578     else if (MHD_TIMEOUT != conn_info->connection_timeout)
    579     {
    580       fprintf (stderr, "The 'MHD_get_connection_info' has returned "
    581                "unexpected timeout value "
    582                "at line %d.\n", (int) __LINE__);
    583       param->err_flag = 1;
    584     }
    585   }
    586   if (NULL == url)
    587   {
    588     fprintf (stderr, "The 'url' parameter is NULL "
    589              "at line %d.\n", (int) __LINE__);
    590     param->err_flag = 1;
    591   }
    592   if (NULL == method)
    593   {
    594     fprintf (stderr, "The 'method' parameter is NULL "
    595              "at line %d.\n", (int) __LINE__);
    596     param->err_flag = 1;
    597     return MHD_NO; /* Should not reply */
    598   }
    599   if (NULL == version)
    600   {
    601     fprintf (stderr, "The 'version' parameter is NULL "
    602              "at line %d.\n", (int) __LINE__);
    603     param->err_flag = 1;
    604     return MHD_NO; /* Should not reply */
    605   }
    606   if (NULL == upload_data_size)
    607   {
    608     fprintf (stderr, "The 'upload_data_size' parameter is NULL "
    609              "at line %d.\n", (int) __LINE__);
    610     param->err_flag = 1;
    611     return MHD_NO; /* Should not reply */
    612   }
    613   if ((0 != *upload_data_size) && (NULL == upload_data))
    614   {
    615     fprintf (stderr, "The 'upload_data' parameter is NULL "
    616              "while '*upload_data_size' is not zero "
    617              "at line %d.\n", (int) __LINE__);
    618     param->err_flag = 1;
    619     return MHD_NO; /* Should not reply */
    620   }
    621   if ((NULL != upload_data) && (0 == *upload_data_size))
    622   {
    623     fprintf (stderr, "The 'upload_data' parameter is NOT NULL "
    624              "while '*upload_data_size' is zero "
    625              "at line %d.\n", (int) __LINE__);
    626     param->err_flag = 1;
    627     return MHD_NO; /* Should not reply */
    628   }
    629 
    630   if (0 != param->num_replies)
    631   {
    632     /* Phantom "second" request due to the fuzzing of the input. Refuse. */
    633     return MHD_NO;
    634   }
    635 
    636   is_post_req = (0 == strcmp (method, MHD_HTTP_METHOD_POST));
    637   if ((0 != strcmp (method, MHD_HTTP_METHOD_GET))
    638       && (0 != strcmp (method, MHD_HTTP_METHOD_HEAD))
    639       && (0 != strcmp (method, MHD_HTTP_METHOD_PUT))
    640       && (! is_post_req))
    641   {
    642     /* Unsupported method for this callback */
    643     return send_error_response (connection, param, MHD_HTTP_NOT_IMPLEMENTED,
    644                                 METHOD_NOT_SUPPORTED,
    645                                 MHD_STATICSTR_LEN_ (METHOD_NOT_SUPPORTED));
    646   }
    647 
    648   if (NULL == *req_cls)
    649   {
    650     if (! is_post_req)
    651     { /* Use static memory */
    652       *req_cls = &static_req_pr_data;
    653     }
    654     else
    655     { /* POST request, use PostProcessor */
    656       req_pr_data =
    657         (struct req_process_strct *) malloc (sizeof (struct req_process_strct));
    658       if (NULL == req_pr_data)
    659       {
    660         fprintf (stderr, "malloc() failed "
    661                  "at line %d.\n", (int) __LINE__);
    662         return MHD_NO;
    663       }
    664       req_pr_data->magic2 = TEST_MAGIC_MARKER2;
    665       req_pr_data->is_static = 0;
    666       req_pr_data->post_data_sum = 0;
    667       req_pr_data->postprocsr = MHD_create_post_processor (connection, 1024,
    668                                                            &post_iterator,
    669                                                            req_pr_data);
    670       if (NULL == req_pr_data->postprocsr)
    671       {
    672         free (req_pr_data);
    673         if (NULL == upload_data)
    674           return send_error_response (connection, param, MHD_HTTP_BAD_REQUEST,
    675                                       POST_DATA_BROKEN,
    676                                       MHD_STATICSTR_LEN_ (POST_DATA_BROKEN));
    677         else
    678           return MHD_NO; /* Cannot handle request, broken POST */
    679       }
    680       *req_cls = req_pr_data;
    681     }
    682     if (NULL == upload_data)
    683       return MHD_YES;
    684   }
    685   req_pr_data = (struct req_process_strct *) *req_cls;
    686 
    687   data_sum = 0;
    688   if (NULL != upload_data)
    689   {
    690     if (is_post_req)
    691     {
    692       if (MHD_YES != MHD_post_process (req_pr_data->postprocsr,
    693                                        upload_data, *upload_data_size))
    694       {
    695         free_req_pr_data (req_pr_data);
    696         *req_cls = NULL;
    697         /* Processing upload body (context), error reply cannot be queued here */
    698         return MHD_NO;
    699       }
    700       *upload_data_size = 0; /* All data have been processed */
    701     }
    702     else
    703     {
    704       /* Check that all 'upload_data' is addressable */
    705       size_t pos;
    706       for (pos = 0; pos < *upload_data_size; ++pos)
    707         data_sum =
    708           (unsigned char) (data_sum + (unsigned char) upload_data[pos]);
    709       if (0 != *upload_data_size)
    710       {
    711         if (3 >= *upload_data_size)
    712           *upload_data_size = 0;                             /* Consume all incoming data */
    713         else
    714           *upload_data_size = data_sum % *upload_data_size;  /* Pseudo-random */
    715       }
    716     }
    717     return MHD_YES;
    718   }
    719   if (is_post_req)
    720   {
    721     if (MHD_YES != MHD_destroy_post_processor (req_pr_data->postprocsr))
    722     {
    723       free (req_pr_data);
    724       *req_cls = NULL;
    725       return send_error_response (connection, param, MHD_HTTP_BAD_REQUEST,
    726                                   POST_DATA_BROKEN,
    727                                   MHD_STATICSTR_LEN_ (POST_DATA_BROKEN));
    728     }
    729     req_pr_data->postprocsr = NULL;
    730   }
    731   data_sum += (unsigned char) req_pr_data->post_data_sum;
    732   free_req_pr_data (req_pr_data);
    733   *req_cls = NULL;
    734 
    735   ret = MHD_YES;
    736   if (use_get_chunked)
    737   {
    738     struct content_cb_param_strct *cnt_cb_param;
    739     cnt_cb_param = malloc (sizeof (struct content_cb_param_strct));
    740     if (NULL == cnt_cb_param)
    741     {
    742       fprintf (stderr, "malloc() failed "
    743                "at line %d.\n", (int) __LINE__);
    744       /* External error, do not rise the error flag */
    745       return MHD_NO;
    746     }
    747     cnt_cb_param->magic0 = (unsigned int) TEST_MAGIC_MARKER0;
    748     response = MHD_create_response_from_callback (MHD_SIZE_UNKNOWN,
    749                                                   1024,
    750                                                   &content_cb, cnt_cb_param,
    751                                                   &crcf);
    752     if (NULL == response)
    753     {
    754       fprintf (stderr, "MHD_create_response_from_callback() failed "
    755                "at line %d.\n", (int) __LINE__);
    756       free (cnt_cb_param);
    757       param->err_flag = 1;
    758       ret = MHD_NO;
    759     }
    760     else
    761       cnt_cb_param->response = response;
    762   }
    763   else if (use_get || use_put || use_post)
    764   {
    765     /* Randomly choose the response page for the POST requests */
    766     if (0 == data_sum % 2)
    767       response =
    768         MHD_create_response_from_buffer_static (MHD_STATICSTR_LEN_ (EMPTY_PAGE),
    769                                                 EMPTY_PAGE);
    770     else
    771       response =
    772         MHD_create_response_from_buffer_static (MHD_STATICSTR_LEN_ ( \
    773                                                   EMPTY_PAGE_ALT),
    774                                                 EMPTY_PAGE_ALT);
    775 
    776     if (NULL == response)
    777     {
    778       fprintf (stderr, "MHD_create_response_from_buffer_static() failed "
    779                "at line %d.\n", (int) __LINE__);
    780       param->err_flag = 1;
    781       ret = MHD_NO;
    782     }
    783   }
    784   else
    785   {
    786     fprintf (stderr, "Response is not implemented for this test. "
    787              "Internal logic is broken. "
    788              "At line %d.\n", (int) __LINE__);
    789     abort ();
    790   }
    791 
    792   if (NULL != response)
    793   {
    794     if ((MHD_YES == ret) &&
    795         (use_close || (! oneone && (0 != strcmp (version,
    796                                                  MHD_HTTP_VERSION_1_0)))))
    797     {
    798       ret = MHD_add_response_header (response,
    799                                      MHD_HTTP_HEADER_CONNECTION,
    800                                      "close");
    801       if (MHD_YES != ret)
    802       {
    803         fprintf (stderr, "MHD_add_response_header() failed "
    804                  "at line %d.\n", (int) __LINE__);
    805         param->err_flag = 1;
    806       }
    807     }
    808     if (MHD_YES == ret)
    809     {
    810       ret = MHD_queue_response (connection, MHD_HTTP_OK, response);
    811       if (MHD_YES != ret)
    812       {
    813         fprintf (stderr, "MHD_queue_response() failed "
    814                  "at line %d.\n", (int) __LINE__);
    815         param->err_flag = 1;
    816       }
    817     }
    818     else
    819       param->num_replies++;
    820 
    821     MHD_destroy_response (response);
    822   }
    823   else
    824   {
    825     fprintf (stderr, "MHD_create_response_from_buffer_static() failed "
    826              "at line %d.\n", (int) __LINE__);
    827     ret = MHD_NO;
    828   }
    829   return ret;
    830 }
    831 
    832 
    833 static void
    834 req_completed_cleanup (void *cls,
    835                        struct MHD_Connection *connection,
    836                        void **req_cls,
    837                        enum MHD_RequestTerminationCode toe)
    838 {
    839   struct ahc_param_strct *param = (struct ahc_param_strct *) cls;
    840   struct req_process_strct *req_pr_data = (struct req_process_strct *) *req_cls;
    841   (void) connection; /* Unused. Mute compiler warning. */
    842 
    843   if (NULL == param)
    844   {
    845     fprintf (stderr, "The 'cls' parameter is NULL at line %d.\n",
    846              (int) __LINE__);
    847     fflush (stderr);
    848     abort ();
    849   }
    850   if ((unsigned int) TEST_MAGIC_MARKER1 != param->magic1)
    851   {
    852     fprintf (stderr, "The 'param->magic1' has wrong value at line %d.\n",
    853              (int) __LINE__);
    854     fflush (stderr);
    855     abort ();
    856   }
    857   if (NULL == req_pr_data)
    858     return; /* The data have been freed */
    859   if ((unsigned int) TEST_MAGIC_MARKER2 != req_pr_data->magic2)
    860   {
    861     fprintf (stderr, "The 'req_pr_data->magic2' has wrong value at line %d.\n",
    862              (int) __LINE__);
    863     fflush (stderr);
    864     abort ();
    865   }
    866   if (MHD_REQUEST_TERMINATED_COMPLETED_OK == toe)
    867   {
    868     fprintf (stderr, "The request completed successful, but request cls has"
    869              "not been cleared. "
    870              "At line %d.\n", (int) __LINE__);
    871     param->err_flag = 1;
    872   }
    873   if (req_pr_data->is_static)
    874     return;
    875   if (NULL != req_pr_data->postprocsr)
    876     MHD_destroy_post_processor (req_pr_data->postprocsr);
    877   req_pr_data->postprocsr = NULL;
    878   free (req_pr_data);
    879   *req_cls = NULL;
    880 }
    881 
    882 
    883 /* Un-comment the next line (or use CPPFLAGS) to avoid
    884    logging of the traffic with debug builds */
    885 /* #define TEST_NO_PRINT_TRAFFIC 1 */
    886 
    887 #ifdef _DEBUG
    888 #ifdef TEST_PRINT_BODY
    889 #ifndef TEST_PRINT_BODY_RQ
    890 #define TEST_PRINT_BODY_RQ 1
    891 #endif /* TEST_PRINT_BODY_RQ */
    892 #ifndef TEST_PRINT_BODY_RP
    893 #define TEST_PRINT_BODY_RP 1
    894 #endif /* TEST_PRINT_BODY_RP */
    895 #endif /* TEST_PRINT_BODY */
    896 #endif /* _DEBUG */
    897 
    898 static int
    899 libcurl_debug_cb (CURL *handle,
    900                   curl_infotype type,
    901                   char *data,
    902                   size_t size,
    903                   void *ctx)
    904 {
    905   static const char excess_mark[] = "Excess found";
    906   static const size_t excess_mark_len = MHD_STATICSTR_LEN_ (excess_mark);
    907   struct CBC *cbc = ctx;
    908   (void) handle;
    909 
    910 #if defined(_DEBUG) && ! defined(TEST_NO_PRINT_TRAFFIC)
    911   switch (type)
    912   {
    913   case CURLINFO_TEXT:
    914     fprintf (stderr, "* %.*s", (int) size, data);
    915     break;
    916   case CURLINFO_HEADER_IN:
    917     fprintf (stderr, "< %.*s", (int) size, data);
    918     break;
    919   case CURLINFO_HEADER_OUT:
    920     fprintf (stderr, "> %.*s", (int) size, data);
    921     break;
    922   case CURLINFO_DATA_IN:
    923 #ifdef TEST_PRINT_BODY_RP
    924     fprintf (stderr, "<| %.*s\n", (int) size, data);
    925 #endif /* TEST_PRINT_BODY_RP */
    926     break;
    927   case CURLINFO_DATA_OUT:
    928 #ifdef TEST_PRINT_BODY_RQ
    929     fprintf (stderr, ">| %.*s\n", (int) size, data);
    930 #endif /* TEST_PRINT_BODY_RQ */
    931     break;
    932   case CURLINFO_SSL_DATA_IN:
    933   case CURLINFO_SSL_DATA_OUT:
    934   case CURLINFO_END:
    935   default:
    936     break;
    937   }
    938 #endif /* _DEBUG  && ! TEST_NO_PRINT_TRAFFIC */
    939   if (use_close || ! oneone)
    940   {
    941     /* Check for extra data only if every connection is terminated by MHD
    942        after one request, otherwise MHD may react on garbage after request
    943        data. */
    944     if (CURLINFO_TEXT == type)
    945     {
    946       if ((size >= excess_mark_len) &&
    947           (0 == memcmp (data, excess_mark, excess_mark_len)))
    948       {
    949         fprintf (stderr, "Extra data has been detected in MHD reply "
    950                  "at line %d.\n", (int) __LINE__);
    951         cbc->excess_found++;
    952       }
    953     }
    954   }
    955   return 0;
    956 }
    957 
    958 
    959 static CURL *
    960 setupCURL (struct CBC *cbc, uint16_t port
    961 #ifndef TEST_USE_STATIC_POST_DATA
    962            , curl_mime **mime
    963 #endif /* ! TEST_USE_STATIC_POST_DATA */
    964            )
    965 {
    966   CURL *c;
    967   CURLcode e;
    968   char *buf;
    969   const char *uri_to_use;
    970   const char *base_uri;
    971 
    972 #ifndef TEST_USE_STATIC_POST_DATA
    973   *mime = NULL;
    974 #endif /* ! TEST_USE_STATIC_POST_DATA */
    975 
    976   base_uri = run_with_socat ? TEST_BASE_URI_SOCAT : TEST_BASE_URI;
    977   if (! use_long_uri)
    978   {
    979     uri_to_use = base_uri;
    980     buf = NULL;
    981   }
    982   else
    983   {
    984     size_t pos;
    985     size_t base_uri_len;
    986 
    987     base_uri_len = strlen (base_uri);
    988     buf = malloc (TEST_STRING_VLONG_LEN + 1);
    989     if (NULL == buf)
    990     {
    991       fprintf (stderr, "malloc() failed "
    992                "at line %d.\n", (int) __LINE__);
    993       return NULL;
    994     }
    995     memcpy (buf, base_uri, base_uri_len);
    996     for (pos = base_uri_len;
    997          pos < TEST_STRING_VLONG_LEN;
    998          ++pos)
    999     {
   1000       if (0 == pos % 9)
   1001         buf[pos] = '/';
   1002       else
   1003         buf[pos] = 'a' + (char) (unsigned char) (pos % ((unsigned char)
   1004                                                         ('z' - 'a' + 1)));
   1005     }
   1006     buf[TEST_STRING_VLONG_LEN] = 0;
   1007     uri_to_use = buf;
   1008   }
   1009   if (run_with_socat)
   1010     port = SOCAT_PORT;
   1011 
   1012   c = curl_easy_init ();
   1013   if (NULL == c)
   1014   {
   1015     fprintf (stderr, "curl_easy_init() failed "
   1016              "at line %d.\n", (int) __LINE__);
   1017     return NULL;
   1018   }
   1019 
   1020   if ((CURLE_OK == (e = curl_easy_setopt (c, CURLOPT_URL,
   1021                                           uri_to_use))) &&
   1022       (CURLE_OK == (e = curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1L))) &&
   1023       (CURLE_OK == (e = curl_easy_setopt (c, CURLOPT_WRITEFUNCTION,
   1024                                           &copyBuffer))) &&
   1025       (CURLE_OK == (e = curl_easy_setopt (c, CURLOPT_WRITEDATA, cbc))) &&
   1026       (CURLE_OK == (e = curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT,
   1027                                           ((long) CURL_TIMEOUT)))) &&
   1028       (CURLE_OK == (e = curl_easy_setopt (c, CURLOPT_TIMEOUT,
   1029                                           ((long) CURL_TIMEOUT)))) &&
   1030       (CURLE_OK == (e = curl_easy_setopt (c, CURLOPT_FAILONERROR, 0L))) &&
   1031 #ifdef _DEBUG
   1032       (CURLE_OK == (e = curl_easy_setopt (c, CURLOPT_VERBOSE, 1L))) &&
   1033 #endif /* _DEBUG */
   1034       (CURLE_OK == (e = curl_easy_setopt (c, CURLOPT_DEBUGFUNCTION,
   1035                                           &libcurl_debug_cb))) &&
   1036       (CURLE_OK == (e = curl_easy_setopt (c, CURLOPT_DEBUGDATA,
   1037                                           cbc))) &&
   1038 #if CURL_AT_LEAST_VERSION (7, 45, 0)
   1039       (CURLE_OK == (e = curl_easy_setopt (c, CURLOPT_DEFAULT_PROTOCOL,
   1040                                           "http"))) &&
   1041 #endif /* CURL_AT_LEAST_VERSION (7, 45, 0) */
   1042 #if CURL_AT_LEAST_VERSION (7, 85, 0)
   1043       (CURLE_OK == (e = curl_easy_setopt (c, CURLOPT_PROTOCOLS_STR,
   1044                                           "http"))) &&
   1045 #elif CURL_AT_LEAST_VERSION (7, 19, 4)
   1046       (CURLE_OK == (e = curl_easy_setopt (c, CURLOPT_PROTOCOLS,
   1047                                           CURLPROTO_HTTP))) &&
   1048 #endif /* CURL_AT_LEAST_VERSION (7, 19, 4) */
   1049       (CURLE_OK == (e = curl_easy_setopt (c, CURLOPT_HTTP_VERSION,
   1050                                           oneone ?
   1051                                           CURL_HTTP_VERSION_1_1 :
   1052                                           CURL_HTTP_VERSION_1_0))) &&
   1053 #if CURL_AT_LEAST_VERSION (7, 24, 0)
   1054       (CURLE_OK == (e = curl_easy_setopt (c, CURLOPT_INTERFACE,
   1055                                           "host!127.0.0.101"))) &&
   1056 #else  /* ! CURL_AT_LEAST_VERSION (7, 24, 0) */
   1057       (CURLE_OK == (e = curl_easy_setopt (c, CURLOPT_INTERFACE,
   1058                                           "127.0.0.101"))) &&
   1059 #endif /* ! CURL_AT_LEAST_VERSION (7, 24, 0) */
   1060       (CURLE_OK == (e = curl_easy_setopt (c, CURLOPT_PORT, ((long) port)))) &&
   1061       (CURLE_OK == (e = curl_easy_setopt (c, CURLOPT_HTTPHEADER,
   1062                                           use_long_header ?
   1063                                           libcurl_long_header : NULL)))
   1064       )
   1065   {
   1066     if (NULL != buf)
   1067     {
   1068       free (buf);
   1069       buf = NULL;
   1070     }
   1071     if (use_put)
   1072     {
   1073       if ((CURLE_OK == (e = curl_easy_setopt (c, CURLOPT_READFUNCTION,
   1074                                               &putBuffer))) &&
   1075           (CURLE_OK == (e = curl_easy_setopt (c, CURLOPT_READDATA, cbc))) &&
   1076           (CURLE_OK == (e = curl_easy_setopt (c, CURLOPT_UPLOAD, (long) 1))) &&
   1077           (CURLE_OK == (e = curl_easy_setopt (c, CURLOPT_INFILESIZE_LARGE,
   1078                                               use_put_chunked ?
   1079                                               ((curl_off_t) -1) :
   1080                                               ((curl_off_t) cbc->up_size)))))
   1081       {
   1082         return c; /* Success exit point for 'use_put' */
   1083       }
   1084       else
   1085         fprintf (stderr, "PUT-related curl_easy_setopt() failed at line %d, "
   1086                  "error: %s\n", (int) __LINE__,
   1087                  curl_easy_strerror (e));
   1088     }
   1089     else if (use_post)
   1090     {
   1091       if (! use_post_form)
   1092       {
   1093         if ((CURLE_OK == (e = curl_easy_setopt (c, CURLOPT_POST, (long) 1))) &&
   1094             (CURLE_OK == (e = curl_easy_setopt (c, CURLOPT_POSTFIELDS,
   1095                                                 POST_URLENC_DATA))) &&
   1096             (CURLE_OK ==
   1097              (e = curl_easy_setopt (c, CURLOPT_POSTFIELDSIZE,
   1098                                     MHD_STATICSTR_LEN_ (POST_URLENC_DATA)))))
   1099         {
   1100           return c; /* Success exit point for 'use_post' */
   1101         }
   1102         else
   1103           fprintf (stderr,
   1104                    "POST-related curl_easy_setopt() failed at line %d, "
   1105                    "error: %s\n", (int) __LINE__,
   1106                    curl_easy_strerror (e));
   1107       }
   1108       else
   1109       {
   1110 #ifndef TEST_USE_STATIC_POST_DATA
   1111         *mime = curl_mime_init (c);
   1112         if (NULL != *mime)
   1113         {
   1114           curl_mimepart *part;
   1115           if ((NULL != (part = curl_mime_addpart (*mime))) &&
   1116               (CURLE_OK == curl_mime_name (part, POST_KEY1)) &&
   1117               (CURLE_OK == curl_mime_data (part, POST_VALUE1,
   1118                                            MHD_STATICSTR_LEN_ (POST_VALUE1))) &&
   1119               (NULL != (part = curl_mime_addpart (*mime))) &&
   1120               (CURLE_OK == curl_mime_name (part, POST_KEY2)) &&
   1121               (CURLE_OK == curl_mime_data (part, POST_VALUE2,
   1122                                            MHD_STATICSTR_LEN_ (POST_VALUE2))))
   1123           {
   1124             if (CURLE_OK ==
   1125                 (e = curl_easy_setopt (c, CURLOPT_MIMEPOST, *mime)))
   1126               return c; /* Success exit point for 'use_post' */
   1127             else
   1128               fprintf (stderr, "curl_easy_setopt(c, CURLOPT_MIMEPOST, mime) "
   1129                        "failed at line %d, error: %s\n",
   1130                        (int) __LINE__, curl_easy_strerror (e));
   1131           }
   1132           else
   1133             fprintf (stderr, "curl_mime_addpart(), curl_mime_name() or "
   1134                      "curl_mime_data() failed.\n");
   1135         }
   1136         else
   1137           fprintf (stderr, "curl_mime_init() failed.\n");
   1138 
   1139 #else  /* TEST_USE_STATIC_POST_DATA */
   1140         if (CURLE_OK == (e = curl_easy_setopt (c,
   1141                                                CURLOPT_HTTPPOST, post_first)))
   1142         {
   1143           return c; /* Success exit point for 'use_post' */
   1144         }
   1145         else
   1146           fprintf (stderr, "POST form-related curl_easy_setopt() failed, "
   1147                    "error: %s\n", curl_easy_strerror (e));
   1148 #endif /* TEST_USE_STATIC_POST_DATA */
   1149       }
   1150     }
   1151     else
   1152       return c; /* Success exit point */
   1153   }
   1154   else
   1155     fprintf (stderr, "curl_easy_setopt() failed at line %d, "
   1156              "error: %s\n", (int) __LINE__,
   1157              curl_easy_strerror (e));
   1158 
   1159   curl_easy_cleanup (c);
   1160 #ifndef TEST_USE_STATIC_POST_DATA
   1161   if (NULL != *mime)
   1162     curl_mime_free (*mime);
   1163 #endif /* ! TEST_USE_STATIC_POST_DATA */
   1164 
   1165   if (NULL != buf)
   1166     free (buf);
   1167 
   1168   return NULL; /* Failure exit point */
   1169 }
   1170 
   1171 
   1172 static struct MHD_Daemon *
   1173 start_daemon_for_test (unsigned int daemon_flags, uint16_t *pport,
   1174                        struct ahc_param_strct *callback_param)
   1175 {
   1176   struct MHD_Daemon *d;
   1177   struct MHD_OptionItem ops[] = {
   1178     { MHD_OPTION_END, 0, NULL },
   1179     { MHD_OPTION_END, 0, NULL },
   1180     { MHD_OPTION_END, 0, NULL }
   1181   };
   1182   size_t num_opt;
   1183 
   1184   num_opt = 0;
   1185 
   1186   callback_param->magic1 = (unsigned int) TEST_MAGIC_MARKER1;
   1187   callback_param->err_flag = 0;
   1188   callback_param->num_replies = 0;
   1189 
   1190   if (use_put_large)
   1191   {
   1192     ops[num_opt].option = MHD_OPTION_CONNECTION_MEMORY_LIMIT;
   1193     ops[num_opt].value = (intptr_t) (PUT_LARGE_SIZE / 4);
   1194     ++num_opt;
   1195   }
   1196   else if (use_long_header || use_long_uri)
   1197   {
   1198     ops[num_opt].option = MHD_OPTION_CONNECTION_MEMORY_LIMIT;
   1199     ops[num_opt].value = (intptr_t) (TEST_STRING_VLONG_LEN / 2);
   1200     ++num_opt;
   1201   }
   1202   if (0 == (MHD_USE_INTERNAL_POLLING_THREAD & daemon_flags))
   1203   {
   1204     ops[num_opt].option = MHD_OPTION_APP_FD_SETSIZE;
   1205     ops[num_opt].value = (intptr_t) (FD_SETSIZE);
   1206     ++num_opt;
   1207   }
   1208   d = MHD_start_daemon (daemon_flags /* | MHD_USE_ERROR_LOG */,
   1209                         *pport, NULL, NULL,
   1210                         &ahc_check, callback_param,
   1211                         MHD_OPTION_CONNECTION_TIMEOUT,
   1212                         (unsigned int) MHD_TIMEOUT,
   1213                         MHD_OPTION_NOTIFY_COMPLETED,
   1214                         &req_completed_cleanup, callback_param,
   1215                         MHD_OPTION_ARRAY, ops,
   1216                         MHD_OPTION_END);
   1217   if (NULL == d)
   1218   {
   1219     fprintf (stderr, "MHD_start_daemon() failed "
   1220              "at line %d.\n", (int) __LINE__);
   1221     return NULL;
   1222   }
   1223 
   1224   /* Do not use accept4() as only accept() is intercepted by zzuf */
   1225   if (! run_with_socat)
   1226     MHD_avoid_accept4_ (d);
   1227 
   1228   if (0 == *pport)
   1229   {
   1230     const union MHD_DaemonInfo *dinfo;
   1231 
   1232     dinfo = MHD_get_daemon_info (d,
   1233                                  MHD_DAEMON_INFO_BIND_PORT);
   1234     if ( (NULL == dinfo) ||
   1235          (0 == dinfo->port) )
   1236     {
   1237       fprintf (stderr, "MHD_get_daemon_info() failed "
   1238                "at line %d.\n", (int) __LINE__);
   1239       MHD_stop_daemon (d);
   1240       return NULL;
   1241     }
   1242     *pport = dinfo->port;
   1243   }
   1244   return d;
   1245 }
   1246 
   1247 
   1248 static void
   1249 print_test_starting (unsigned int daemon_flags)
   1250 {
   1251   fflush (stderr);
   1252   if (0 != (MHD_USE_INTERNAL_POLLING_THREAD & daemon_flags))
   1253   {
   1254     if (0 != (MHD_USE_THREAD_PER_CONNECTION & daemon_flags))
   1255     {
   1256       if (0 != (MHD_USE_POLL & daemon_flags))
   1257         printf ("\nStarting test with internal polling by poll() and "
   1258                 "thread-per-connection.\n");
   1259       else
   1260         printf ("\nStarting test with internal polling by select() and "
   1261                 "thread-per-connection.\n");
   1262     }
   1263     else
   1264     {
   1265       if (0 != (MHD_USE_POLL & daemon_flags))
   1266         printf ("\nStarting test with internal polling by poll().\n");
   1267       else if (0 != (MHD_USE_EPOLL & daemon_flags))
   1268         printf ("\nStarting test with internal polling by 'epoll'.\n");
   1269       else
   1270         printf ("\nStarting test with internal polling by select().\n");
   1271     }
   1272   }
   1273   else
   1274   {
   1275     if (0 != (MHD_USE_EPOLL & daemon_flags))
   1276       printf ("\nStarting test with%s thread safety with external polling "
   1277               "and internal 'epoll'.\n",
   1278               ((0 != (MHD_USE_NO_THREAD_SAFETY & daemon_flags)) ? "out" : ""));
   1279     else
   1280       printf ("\nStarting test with%s thread safety with external polling.\n",
   1281               ((0 != (MHD_USE_NO_THREAD_SAFETY & daemon_flags)) ? "out" : ""));
   1282   }
   1283   fflush (stdout);
   1284 }
   1285 
   1286 
   1287 static unsigned int
   1288 testInternalPolling (uint16_t *pport, unsigned int daemon_flags)
   1289 {
   1290   struct MHD_Daemon *d;
   1291   CURL *c;
   1292   char buf[2048];
   1293   struct CBC cbc;
   1294   struct ahc_param_strct callback_param;
   1295   unsigned int ret;
   1296 #ifndef TEST_USE_STATIC_POST_DATA
   1297   curl_mime *mime;
   1298 #endif /* ! TEST_USE_STATIC_POST_DATA */
   1299 
   1300   if (0 == (MHD_USE_INTERNAL_POLLING_THREAD & daemon_flags))
   1301   {
   1302     fprintf (stderr, "Wrong internal flags, the test is broken. "
   1303              "At line %d.\n", (int) __LINE__);
   1304     abort (); /* Wrong flags, error in code */
   1305   }
   1306 
   1307   print_test_starting (daemon_flags);
   1308   initCBC (&cbc, buf, sizeof(buf));
   1309   d = start_daemon_for_test (daemon_flags, pport, &callback_param);
   1310   if (d == NULL)
   1311     return 1;
   1312 
   1313   ret = 0;
   1314   c = setupCURL (&cbc, *pport
   1315 #ifndef TEST_USE_STATIC_POST_DATA
   1316                  , &mime
   1317 #endif /* ! TEST_USE_STATIC_POST_DATA */
   1318                  );
   1319   if (NULL != c)
   1320   {
   1321     int i;
   1322 
   1323     for (i = dry_run ? LOOP_COUNT : 0; i < LOOP_COUNT; i++)
   1324     {
   1325       fprintf (stderr, ".");
   1326       callback_param.num_replies = 0;
   1327       resetCBC (&cbc);
   1328       /* Run libcurl without checking the result */
   1329       curl_easy_perform (c);
   1330       fflush (stderr);
   1331     }
   1332     curl_easy_cleanup (c);
   1333 #ifndef TEST_USE_STATIC_POST_DATA
   1334     if (NULL != mime)
   1335       curl_mime_free (mime);
   1336 #endif /* ! TEST_USE_STATIC_POST_DATA */
   1337   }
   1338   else
   1339     ret = 99; /* Not an MHD error */
   1340 
   1341   if ((0 == ret) && callback_param.err_flag)
   1342   {
   1343     fprintf (stderr, "One or more errors have been detected by "
   1344              "access handler callback function. "
   1345              "At line %d.\n", (int) __LINE__);
   1346     ret = 1;
   1347   }
   1348   else if ((0 == ret) && cbc.excess_found)
   1349   {
   1350     fprintf (stderr, "The extra reply data have been detected one "
   1351              "or more times. "
   1352              "At line %d.\n", (int) __LINE__);
   1353     ret = 1;
   1354   }
   1355 
   1356   fprintf (stderr, "\n");
   1357   MHD_stop_daemon (d);
   1358   fflush (stderr);
   1359   return ret;
   1360 }
   1361 
   1362 
   1363 static unsigned int
   1364 testExternalPolling (uint16_t *pport, unsigned int daemon_flags)
   1365 {
   1366   struct MHD_Daemon *d;
   1367   CURLM *multi;
   1368   char buf[2048];
   1369   struct CBC cbc;
   1370   struct ahc_param_strct callback_param;
   1371   unsigned int ret;
   1372 #ifndef TEST_USE_STATIC_POST_DATA
   1373   curl_mime *mime;
   1374 #endif /* ! TEST_USE_STATIC_POST_DATA */
   1375 
   1376   if (0 != (MHD_USE_INTERNAL_POLLING_THREAD & daemon_flags))
   1377   {
   1378     fprintf (stderr, "Wrong internal flags, the test is broken. "
   1379              "At line %d.\n", (int) __LINE__);
   1380     abort (); /* Wrong flags, error in code */
   1381   }
   1382 
   1383   print_test_starting (daemon_flags);
   1384   initCBC (&cbc, buf, sizeof(buf));
   1385   d = start_daemon_for_test (daemon_flags, pport, &callback_param);
   1386   if (d == NULL)
   1387     return 1;
   1388 
   1389   ret = 0;
   1390   multi = curl_multi_init ();
   1391   if (multi == NULL)
   1392   {
   1393     fprintf (stderr, "curl_multi_init() failed "
   1394              "at line %d.\n", (int) __LINE__);
   1395     ret = 99; /* Not an MHD error */
   1396   }
   1397   else
   1398   {
   1399     CURL *c;
   1400     c = setupCURL (&cbc, *pport
   1401 #ifndef TEST_USE_STATIC_POST_DATA
   1402                    , &mime
   1403 #endif /* ! TEST_USE_STATIC_POST_DATA */
   1404                    );
   1405 
   1406     if (NULL == c)
   1407       ret = 99; /* Not an MHD error */
   1408     else
   1409     {
   1410       int i;
   1411 
   1412       for (i = dry_run ? LOOP_COUNT : 0;
   1413            (i < LOOP_COUNT) && (0 == ret); i++)
   1414       {
   1415         CURLMcode mret;
   1416 
   1417         /* The same 'multi' handle will be used in transfers so
   1418            connection will be reused.
   1419            The same 'easy' handle is added (and removed later) to (re-)start
   1420            the same transfer. */
   1421         mret = curl_multi_add_handle (multi, c);
   1422         if (CURLM_OK != mret)
   1423         {
   1424           fprintf (stderr, "curl_multi_add_handle() failed at %d, "
   1425                    "error: %s\n", (int) __LINE__,
   1426                    curl_multi_strerror (mret));
   1427           ret = 99; /* Not an MHD error */
   1428         }
   1429         else
   1430         {
   1431           time_t start;
   1432 
   1433           fprintf (stderr, ".");
   1434           callback_param.num_replies = 0;
   1435           resetCBC (&cbc);
   1436           start = time (NULL);
   1437           do
   1438           {
   1439             fd_set rs;
   1440             fd_set ws;
   1441             fd_set es;
   1442             int maxfd_curl;
   1443             MHD_socket maxfd_mhd;
   1444             int maxfd;
   1445             int running;
   1446             struct timeval tv;
   1447 
   1448             maxfd_curl = 0;
   1449             maxfd_mhd = MHD_INVALID_SOCKET;
   1450             FD_ZERO (&rs);
   1451             FD_ZERO (&ws);
   1452             FD_ZERO (&es);
   1453             curl_multi_perform (multi, &running);
   1454             if (0 == running)
   1455             {
   1456               int msgs_left;
   1457               do
   1458               {
   1459                 (void) curl_multi_info_read (multi, &msgs_left);
   1460               } while (0 != msgs_left);
   1461               break; /* The transfer has been finished */
   1462             }
   1463             mret = curl_multi_fdset (multi, &rs, &ws, &es, &maxfd_curl);
   1464             if (CURLM_OK != mret)
   1465             {
   1466               fprintf (stderr, "curl_multi_fdset() failed at line %d, "
   1467                        "error: %s\n", (int) __LINE__,
   1468                        curl_multi_strerror (mret));
   1469               ret = 99; /* Not an MHD error */
   1470               break;
   1471             }
   1472             if (MHD_YES != MHD_get_fdset (d, &rs, &ws, &es, &maxfd_mhd))
   1473             {
   1474               fprintf (stderr, "MHD_get_fdset() failed "
   1475                        "at line %d.\n", (int) __LINE__);
   1476               ret = 1;
   1477               break;
   1478             }
   1479 #ifndef MHD_WINSOCK_SOCKETS
   1480             if ((int) maxfd_mhd > maxfd_curl)
   1481               maxfd = (int) maxfd_mhd;
   1482             else
   1483 #endif /* ! MHD_WINSOCK_SOCKETS */
   1484             maxfd = maxfd_curl;
   1485             tv.tv_sec = 0;
   1486             tv.tv_usec = 100 * 1000;
   1487             if (0 == MHD_get_timeout64s (d))
   1488               tv.tv_usec = 0;
   1489             else
   1490             {
   1491               long curl_to = -1;
   1492               curl_multi_timeout (multi, &curl_to);
   1493               if (0 == curl_to)
   1494                 tv.tv_usec = 0;
   1495             }
   1496             if (-1 == select (maxfd + 1, &rs, &ws, &es, &tv))
   1497             {
   1498 #ifdef MHD_POSIX_SOCKETS
   1499               if (EINTR != errno)
   1500                 fprintf (stderr, "Unexpected select() error "
   1501                          "at line %d.\n", (int) __LINE__);
   1502 #else  /* ! MHD_POSIX_SOCKETS */
   1503               if ((WSAEINVAL != WSAGetLastError ()) ||
   1504                   (0 != rs.fd_count) || (0 != ws.fd_count) ||
   1505                   (0 != es.fd_count))
   1506                 fprintf (stderr, "Unexpected select() error "
   1507                          "at line %d.\n", (int) __LINE__);
   1508               Sleep ((unsigned long) tv.tv_usec / 1000);
   1509 #endif /* ! MHD_POSIX_SOCKETS */
   1510             }
   1511             MHD_run (d);
   1512           } while (time (NULL) - start <= MHD_TIMEOUT);
   1513           /* Remove 'easy' handle from 'multi' handle to
   1514            * restart the transfer or to finish. */
   1515           curl_multi_remove_handle (multi, c);
   1516         }
   1517       }
   1518       curl_easy_cleanup (c);
   1519     }
   1520     curl_multi_cleanup (multi);
   1521 #ifndef TEST_USE_STATIC_POST_DATA
   1522     if (NULL != mime)
   1523       curl_mime_free (mime);
   1524 #endif /* ! TEST_USE_STATIC_POST_DATA */
   1525   }
   1526 
   1527   if ((0 == ret) && callback_param.err_flag)
   1528   {
   1529     fprintf (stderr, "One or more errors have been detected by "
   1530              "access handler callback function. "
   1531              "At line %d.\n", (int) __LINE__);
   1532     ret = 1;
   1533   }
   1534   else if ((0 == ret) && cbc.excess_found)
   1535   {
   1536     fprintf (stderr, "The extra reply data have been detected one "
   1537              "or more times. "
   1538              "At line %d.\n", (int) __LINE__);
   1539     ret = 1;
   1540   }
   1541 
   1542   fprintf (stderr, "\n");
   1543   MHD_stop_daemon (d);
   1544   return 0;
   1545 }
   1546 
   1547 
   1548 static unsigned int
   1549 run_all_checks (void)
   1550 {
   1551   uint16_t port;
   1552   unsigned int testRes;
   1553   unsigned int ret = 0;
   1554 
   1555   if (! run_with_socat)
   1556   {
   1557     if (MHD_are_sanitizers_enabled_ ())
   1558     {
   1559       fprintf (stderr, "Direct run with zzuf does not work with sanitizers. "
   1560                "At line %d.\n", (int) __LINE__);
   1561       return 77;
   1562     }
   1563     if (! MHD_is_avoid_accept4_possible_ ())
   1564     {
   1565       fprintf (stderr,
   1566                "Non-debug build of MHD on this platform use accept4() function. "
   1567                "Direct run with zzuf is not possible. "
   1568                "At line %d.\n", (int) __LINE__);
   1569       return 77;
   1570     }
   1571     if (MHD_NO != MHD_is_feature_supported (MHD_FEATURE_AUTODETECT_BIND_PORT))
   1572       port = 0;     /* Use system automatic assignment */
   1573     else
   1574     {
   1575       port = TEST_BASE_PORT;  /* Use predefined port, may break parallel testing of another MHD build */
   1576       if (oneone)
   1577         port += 100;
   1578       if (use_long_uri)
   1579         port += 30;
   1580       else if (use_long_header)
   1581         port += 35;
   1582       else if (use_get_chunked)
   1583         port += 0;
   1584       else if (use_get)
   1585         port += 5;
   1586       else if (use_post_form)
   1587         port += 10;
   1588       else if (use_post)
   1589         port += 15;
   1590       else if (use_put_large)
   1591         port += 20;
   1592       else if (use_put_chunked)
   1593         port += 25;
   1594     }
   1595   }
   1596   else
   1597     port = TEST_BASE_PORT;  /* Use predefined port, may break parallel testing of another MHD build */
   1598 
   1599   if (! dry_run && (MHD_YES == MHD_is_feature_supported (MHD_FEATURE_THREADS)))
   1600   {
   1601     testRes = testInternalPolling (&port, MHD_USE_SELECT_INTERNALLY);
   1602     if ((77 == testRes) || (99 == testRes))
   1603       return testRes;
   1604     ret += testRes;
   1605     testRes = testInternalPolling (&port, MHD_USE_SELECT_INTERNALLY
   1606                                    | MHD_USE_THREAD_PER_CONNECTION);
   1607     if ((77 == testRes) || (99 == testRes))
   1608       return testRes;
   1609     ret += testRes;
   1610 
   1611     if (MHD_YES == MHD_is_feature_supported (MHD_FEATURE_POLL))
   1612     {
   1613       testRes = testInternalPolling (&port, MHD_USE_POLL_INTERNALLY);
   1614       if ((77 == testRes) || (99 == testRes))
   1615         return testRes;
   1616       ret += testRes;
   1617       testRes = testInternalPolling (&port, MHD_USE_POLL_INTERNALLY
   1618                                      | MHD_USE_THREAD_PER_CONNECTION);
   1619       if ((77 == testRes) || (99 == testRes))
   1620         return testRes;
   1621       ret += testRes;
   1622     }
   1623 
   1624     if (MHD_YES == MHD_is_feature_supported (MHD_FEATURE_EPOLL))
   1625     {
   1626       testRes = testInternalPolling (&port, MHD_USE_EPOLL_INTERNALLY);
   1627       if ((77 == testRes) || (99 == testRes))
   1628         return testRes;
   1629     }
   1630     testRes = testExternalPolling (&port, MHD_NO_FLAG);
   1631   }
   1632   testRes = testExternalPolling (&port, MHD_USE_NO_THREAD_SAFETY);
   1633   if ((77 == testRes) || (99 == testRes))
   1634     return testRes;
   1635   ret += testRes;
   1636 
   1637   return ret;
   1638 }
   1639 
   1640 
   1641 int
   1642 main (int argc, char *const *argv)
   1643 {
   1644   unsigned int res;
   1645   int use_magic_exit_codes;
   1646 
   1647   oneone = ! has_in_name (argv[0], "10");
   1648   use_get = has_in_name (argv[0], "_get");
   1649   use_get_chunked = has_in_name (argv[0], "_get_chunked");
   1650   use_put = has_in_name (argv[0], "_put");
   1651   use_put_large = has_in_name (argv[0], "_put_large");
   1652   use_put_chunked = has_in_name (argv[0], "_put_chunked");
   1653   use_post = has_in_name (argv[0], "_post");
   1654   use_post_form = has_in_name (argv[0], "_post_form");
   1655   use_long_header = has_in_name (argv[0], "_long_header");
   1656   use_long_uri = has_in_name (argv[0], "_long_uri");
   1657   use_close = has_in_name (argv[0], "_close");
   1658 
   1659   run_with_socat = has_param (argc, argv, "--with-socat");
   1660   dry_run = has_param (argc, argv, "--dry-run") ||
   1661             has_param (argc, argv, "-n");
   1662 
   1663   if (1 !=
   1664       ((use_get ? 1 : 0) + (use_put ? 1 : 0) + (use_post ? 1 : 0)))
   1665   {
   1666     fprintf (stderr, "Wrong test name '%s': no or multiple indications "
   1667              "for the test type.\n", argv[0] ? argv[0] : "(NULL)");
   1668     return 99;
   1669   }
   1670   use_magic_exit_codes = run_with_socat || dry_run;
   1671 
   1672   /* zzuf cannot bypass exit values.
   1673      Unless 'dry run' is used, do not return errors for external error
   1674      conditions (like out-of-memory) as they will be reported as test failures. */
   1675   if (! test_global_init ())
   1676     return use_magic_exit_codes ? 99 : 0;
   1677   res = run_all_checks ();
   1678   test_global_deinit ();
   1679   if (99 == res)
   1680     return use_magic_exit_codes ? 99 : 0;
   1681   if (77 == res)
   1682     return use_magic_exit_codes ? 77 : 0;
   1683   return (0 == res) ? 0 : 1;       /* 0 == pass */
   1684 }