libmicrohttpd

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

test_put_broken_len.c (21164B)


      1 /*
      2      This file is part of GNU libmicrohttpd
      3      Copyright (C) 2010 Christian Grothoff
      4      Copyright (C) 2016-2022 Evgeny Grin (Karlson2k)
      5 
      6      GNU libmicrohttpd is free software; you can redistribute it and/or modify
      7      it under the terms of the GNU General Public License as published
      8      by the Free Software Foundation; either version 2, or (at your
      9      option) any later version.
     10 
     11      GNU libmicrohttpd is distributed in the hope that it will be useful, but
     12      WITHOUT ANY WARRANTY; without even the implied warranty of
     13      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     14      General Public License for more details.
     15 
     16      You should have received a copy of the GNU General Public License
     17      along with libmicrohttpd; see the file COPYING.  If not, write to the
     18      Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
     19      Boston, MA 02110-1301, USA.
     20 */
     21 
     22 /**
     23  * @file testcurl/test_head.c
     24  * @brief  Testcase for PUT requests with broken Content-Length header
     25  * @author Karlson2k (Evgeny Grin)
     26  */
     27 
     28 #include "mhd_options.h"
     29 #include "platform.h"
     30 #include <curl/curl.h>
     31 #include <microhttpd.h>
     32 #include <stdlib.h>
     33 #include <string.h>
     34 #include <time.h>
     35 #include <errno.h>
     36 
     37 #ifndef _WIN32
     38 #include <sys/socket.h>
     39 #include <unistd.h>
     40 #else
     41 #include <wincrypt.h>
     42 #endif
     43 
     44 #include "mhd_has_param.h"
     45 #include "mhd_has_in_name.h"
     46 
     47 /* Turn any MHD_PANIC() or failing mhd_assert() reached from this
     48    test into a marked, classifiable test error (TESTING.md, P5). */
     49 #include "mhd_panic_tripwire.h"
     50 
     51 #ifndef MHD_STATICSTR_LEN_
     52 /**
     53  * Determine length of static string / macro strings at compile time.
     54  */
     55 #define MHD_STATICSTR_LEN_(macro) (sizeof(macro) / sizeof(char) - 1)
     56 #endif /* ! MHD_STATICSTR_LEN_ */
     57 
     58 #ifndef CURL_VERSION_BITS
     59 #define CURL_VERSION_BITS(x,y,z) ((x) << 16 | (y) << 8 | (z))
     60 #endif /* ! CURL_VERSION_BITS */
     61 #ifndef CURL_AT_LEAST_VERSION
     62 #define CURL_AT_LEAST_VERSION(x,y,z) \
     63   (LIBCURL_VERSION_NUM >= CURL_VERSION_BITS (x, y, z))
     64 #endif /* ! CURL_AT_LEAST_VERSION */
     65 
     66 #ifndef _MHD_INSTRMACRO
     67 /* Quoted macro parameter */
     68 #define _MHD_INSTRMACRO(a) #a
     69 #endif /* ! _MHD_INSTRMACRO */
     70 #ifndef _MHD_STRMACRO
     71 /* Quoted expanded macro parameter */
     72 #define _MHD_STRMACRO(a) _MHD_INSTRMACRO (a)
     73 #endif /* ! _MHD_STRMACRO */
     74 
     75 #if defined(HAVE___FUNC__)
     76 #define externalErrorExit(ignore) \
     77   _externalErrorExit_func (NULL, __func__, __LINE__)
     78 #define externalErrorExitDesc(errDesc) \
     79   _externalErrorExit_func (errDesc, __func__, __LINE__)
     80 #define libcurlErrorExit(ignore) \
     81   _libcurlErrorExit_func (NULL, __func__, __LINE__)
     82 #define libcurlErrorExitDesc(errDesc) \
     83   _libcurlErrorExit_func (errDesc, __func__, __LINE__)
     84 #define mhdErrorExit(ignore) \
     85   _mhdErrorExit_func (NULL, __func__, __LINE__)
     86 #define mhdErrorExitDesc(errDesc) \
     87   _mhdErrorExit_func (errDesc, __func__, __LINE__)
     88 #define checkCURLE_OK(libcurlcall) \
     89   _checkCURLE_OK_func ((libcurlcall), _MHD_STRMACRO (libcurlcall), \
     90                        __func__, __LINE__)
     91 #elif defined(HAVE___FUNCTION__)
     92 #define externalErrorExit(ignore) \
     93   _externalErrorExit_func (NULL, __FUNCTION__, __LINE__)
     94 #define externalErrorExitDesc(errDesc) \
     95   _externalErrorExit_func (errDesc, __FUNCTION__, __LINE__)
     96 #define libcurlErrorExit(ignore) \
     97   _libcurlErrorExit_func (NULL, __FUNCTION__, __LINE__)
     98 #define libcurlErrorExitDesc(errDesc) \
     99   _libcurlErrorExit_func (errDesc, __FUNCTION__, __LINE__)
    100 #define mhdErrorExit(ignore) \
    101   _mhdErrorExit_func (NULL, __FUNCTION__, __LINE__)
    102 #define mhdErrorExitDesc(errDesc) \
    103   _mhdErrorExit_func (errDesc, __FUNCTION__, __LINE__)
    104 #define checkCURLE_OK(libcurlcall) \
    105   _checkCURLE_OK_func ((libcurlcall), _MHD_STRMACRO (libcurlcall), \
    106                        __FUNCTION__, __LINE__)
    107 #else
    108 #define externalErrorExit(ignore) _externalErrorExit_func (NULL, NULL, __LINE__)
    109 #define externalErrorExitDesc(errDesc) \
    110   _externalErrorExit_func (errDesc, NULL, __LINE__)
    111 #define libcurlErrorExit(ignore) _libcurlErrorExit_func (NULL, NULL, __LINE__)
    112 #define libcurlErrorExitDesc(errDesc) \
    113   _libcurlErrorExit_func (errDesc, NULL, __LINE__)
    114 #define mhdErrorExit(ignore) _mhdErrorExit_func (NULL, NULL, __LINE__)
    115 #define mhdErrorExitDesc(errDesc) _mhdErrorExit_func (errDesc, NULL, __LINE__)
    116 #define checkCURLE_OK(libcurlcall) \
    117   _checkCURLE_OK_func ((libcurlcall), _MHD_STRMACRO (libcurlcall), NULL, \
    118                        __LINE__)
    119 #endif
    120 
    121 
    122 _MHD_NORETURN static void
    123 _externalErrorExit_func (const char *errDesc, const char *funcName, int lineNum)
    124 {
    125   fflush (stdout);
    126   if ((NULL != errDesc) && (0 != errDesc[0]))
    127     fprintf (stderr, "%s", errDesc);
    128   else
    129     fprintf (stderr, "System or external library call failed");
    130   if ((NULL != funcName) && (0 != funcName[0]))
    131     fprintf (stderr, " in %s", funcName);
    132   if (0 < lineNum)
    133     fprintf (stderr, " at line %d", lineNum);
    134 
    135   fprintf (stderr, ".\nLast errno value: %d (%s)\n", (int) errno,
    136            strerror (errno));
    137 #ifdef MHD_WINSOCK_SOCKETS
    138   fprintf (stderr, "WSAGetLastError() value: %d\n", (int) WSAGetLastError ());
    139 #endif /* MHD_WINSOCK_SOCKETS */
    140   fflush (stderr);
    141   exit (99);
    142 }
    143 
    144 
    145 static char libcurl_errbuf[CURL_ERROR_SIZE] = "";
    146 
    147 _MHD_NORETURN static void
    148 _libcurlErrorExit_func (const char *errDesc, const char *funcName, int lineNum)
    149 {
    150   fflush (stdout);
    151   if ((NULL != errDesc) && (0 != errDesc[0]))
    152     fprintf (stderr, "%s", errDesc);
    153   else
    154     fprintf (stderr, "CURL library call failed");
    155   if ((NULL != funcName) && (0 != funcName[0]))
    156     fprintf (stderr, " in %s", funcName);
    157   if (0 < lineNum)
    158     fprintf (stderr, " at line %d", lineNum);
    159 
    160   fprintf (stderr, ".\nLast errno value: %d (%s)\n", (int) errno,
    161            strerror (errno));
    162 #ifdef MHD_WINSOCK_SOCKETS
    163   fprintf (stderr, "WSAGetLastError() value: %d\n", (int) WSAGetLastError ());
    164 #endif /* MHD_WINSOCK_SOCKETS */
    165   if (0 != libcurl_errbuf[0])
    166     fprintf (stderr, "Last libcurl error description: %s\n", libcurl_errbuf);
    167 
    168   fflush (stderr);
    169   exit (99);
    170 }
    171 
    172 
    173 _MHD_NORETURN static void
    174 _mhdErrorExit_func (const char *errDesc, const char *funcName, int lineNum)
    175 {
    176   fflush (stdout);
    177   if ((NULL != errDesc) && (0 != errDesc[0]))
    178     fprintf (stderr, "%s", errDesc);
    179   else
    180     fprintf (stderr, "MHD unexpected error");
    181   if ((NULL != funcName) && (0 != funcName[0]))
    182     fprintf (stderr, " in %s", funcName);
    183   if (0 < lineNum)
    184     fprintf (stderr, " at line %d", lineNum);
    185 
    186   fprintf (stderr, ".\nLast errno value: %d (%s)\n", (int) errno,
    187            strerror (errno));
    188 #ifdef MHD_WINSOCK_SOCKETS
    189   fprintf (stderr, "WSAGetLastError() value: %d\n", (int) WSAGetLastError ());
    190 #endif /* MHD_WINSOCK_SOCKETS */
    191 
    192   fflush (stderr);
    193   exit (8);
    194 }
    195 
    196 
    197 /* Could be increased to facilitate debugging */
    198 #define TIMEOUTS_VAL 500000
    199 
    200 #define EXPECTED_URI_BASE_PATH  "/"
    201 
    202 #define EXISTING_URI  EXPECTED_URI_BASE_PATH
    203 
    204 #define EXPECTED_URI_BASE_PATH_MISSING  "/wrong_uri"
    205 
    206 #define URL_SCHEME "http:/" "/"
    207 
    208 #define URL_HOST "127.0.0.1"
    209 
    210 #define URL_SCHEME_HOST URL_SCHEME URL_HOST
    211 
    212 #define PAGE \
    213   "<html><head><title>libmicrohttpd demo page</title></head>" \
    214   "<body>Success!</body></html>"
    215 
    216 #define PAGE_404 \
    217   "<html><head><title>404 error</title></head>" \
    218   "<body>Error 404: The requested URI does not exist</body></html>"
    219 
    220 /* Global parameters */
    221 static int verbose;
    222 static int oneone;                  /**< If false use HTTP/1.0 for requests*/
    223 
    224 static struct curl_slist *hdr_broken_cnt_len = NULL;
    225 
    226 static void
    227 test_global_init (void)
    228 {
    229   libcurl_errbuf[0] = 0;
    230 
    231   if (0 != curl_global_init (CURL_GLOBAL_WIN32))
    232     externalErrorExit ();
    233 
    234   hdr_broken_cnt_len =
    235     curl_slist_append (hdr_broken_cnt_len,
    236                        MHD_HTTP_HEADER_CONTENT_LENGTH ": 123bad");
    237 }
    238 
    239 
    240 static void
    241 test_global_cleanup (void)
    242 {
    243   curl_slist_free_all (hdr_broken_cnt_len);
    244 
    245   curl_global_cleanup ();
    246 }
    247 
    248 
    249 struct CBC
    250 {
    251   char *buf;
    252   size_t pos;
    253   size_t size;
    254 };
    255 
    256 
    257 static size_t
    258 copyBuffer (void *ptr,
    259             size_t size,
    260             size_t nmemb,
    261             void *ctx)
    262 {
    263   (void) ptr; /* Unused, mute compiler warning */
    264   (void) ctx; /* Unused, mute compiler warning */
    265   /* Discard data */
    266   return size * nmemb;
    267 }
    268 
    269 
    270 struct ahc_cls_type
    271 {
    272   const char *rq_method;
    273   const char *rq_url;
    274 };
    275 
    276 
    277 static enum MHD_Result
    278 ahcCheck (void *cls,
    279           struct MHD_Connection *connection,
    280           const char *url,
    281           const char *method,
    282           const char *version,
    283           const char *upload_data, size_t *upload_data_size,
    284           void **req_cls)
    285 {
    286   static int marker;
    287   struct MHD_Response *response;
    288   enum MHD_Result ret;
    289   struct ahc_cls_type *const param = (struct ahc_cls_type *) cls;
    290   unsigned int http_code;
    291 
    292   if (NULL == param)
    293     mhdErrorExitDesc ("cls parameter is NULL");
    294 
    295   if (oneone)
    296   {
    297     if (0 != strcmp (version, MHD_HTTP_VERSION_1_1))
    298       mhdErrorExitDesc ("Unexpected HTTP version");
    299   }
    300   else
    301   {
    302     if (0 != strcmp (version, MHD_HTTP_VERSION_1_0))
    303       mhdErrorExitDesc ("Unexpected HTTP version");
    304   }
    305 
    306   if (0 != strcmp (url, param->rq_url))
    307     mhdErrorExitDesc ("Unexpected URI");
    308 
    309   if (NULL != upload_data)
    310     mhdErrorExitDesc ("'upload_data' is not NULL");
    311 
    312   if (NULL == upload_data_size)
    313     mhdErrorExitDesc ("'upload_data_size' pointer is NULL");
    314 
    315   if (0 != *upload_data_size)
    316     mhdErrorExitDesc ("'*upload_data_size' value is not zero");
    317 
    318   if (0 != strcmp (param->rq_method, method))
    319     mhdErrorExitDesc ("Unexpected request method");
    320 
    321   if (&marker != *req_cls)
    322   {
    323     *req_cls = &marker;
    324     return MHD_YES;
    325   }
    326   *req_cls = NULL;
    327 
    328   if (0 == strcmp (url, EXISTING_URI))
    329   {
    330     response =
    331       MHD_create_response_from_buffer_static (MHD_STATICSTR_LEN_ (PAGE),
    332                                               PAGE);
    333     http_code = MHD_HTTP_OK;
    334   }
    335   else
    336   {
    337     response =
    338       MHD_create_response_from_buffer_static (MHD_STATICSTR_LEN_ (PAGE_404),
    339                                               PAGE_404);
    340     http_code = MHD_HTTP_NOT_FOUND;
    341   }
    342   if (NULL == response)
    343     mhdErrorExitDesc ("Failed to create response");
    344 
    345   ret = MHD_queue_response (connection,
    346                             http_code,
    347                             response);
    348   MHD_destroy_response (response);
    349   if (MHD_YES != ret)
    350     mhdErrorExitDesc ("Failed to queue response");
    351 
    352   return ret;
    353 }
    354 
    355 
    356 static int
    357 libcurl_debug_cb (CURL *handle,
    358                   curl_infotype type,
    359                   char *data,
    360                   size_t size,
    361                   void *userptr)
    362 {
    363   static const char excess_mark[] = "Excess found";
    364   static const size_t excess_mark_len = MHD_STATICSTR_LEN_ (excess_mark);
    365   (void) handle;
    366   (void) userptr;
    367 
    368 #ifdef _DEBUG
    369   switch (type)
    370   {
    371   case CURLINFO_TEXT:
    372     fprintf (stderr, "* %.*s", (int) size, data);
    373     break;
    374   case CURLINFO_HEADER_IN:
    375     fprintf (stderr, "< %.*s", (int) size, data);
    376     break;
    377   case CURLINFO_HEADER_OUT:
    378     fprintf (stderr, "> %.*s", (int) size, data);
    379     break;
    380   case CURLINFO_DATA_IN:
    381 #if 0
    382     fprintf (stderr, "<| %.*s\n", (int) size, data);
    383 #endif
    384     break;
    385   case CURLINFO_DATA_OUT:
    386   case CURLINFO_SSL_DATA_IN:
    387   case CURLINFO_SSL_DATA_OUT:
    388   case CURLINFO_END:
    389   default:
    390     break;
    391   }
    392 #endif /* _DEBUG */
    393   if (CURLINFO_TEXT == type)
    394   {
    395     if ((size >= excess_mark_len) &&
    396         (0 == memcmp (data, excess_mark, excess_mark_len)))
    397       mhdErrorExitDesc ("Extra data has been detected in MHD reply");
    398   }
    399   return 0;
    400 }
    401 
    402 
    403 static CURL *
    404 setupCURL (void *cbc, uint16_t port)
    405 {
    406   CURL *c;
    407 
    408   c = curl_easy_init ();
    409   if (NULL == c)
    410     libcurlErrorExitDesc ("curl_easy_init() failed");
    411 
    412   if ((CURLE_OK != curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1L)) ||
    413       (CURLE_OK != curl_easy_setopt (c, CURLOPT_WRITEFUNCTION,
    414                                      &copyBuffer)) ||
    415       (CURLE_OK != curl_easy_setopt (c, CURLOPT_WRITEDATA, cbc)) ||
    416       (CURLE_OK != curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT,
    417                                      ((long) TIMEOUTS_VAL))) ||
    418       (CURLE_OK != curl_easy_setopt (c, CURLOPT_HTTP_VERSION,
    419                                      (oneone) ?
    420                                      CURL_HTTP_VERSION_1_1 :
    421                                      CURL_HTTP_VERSION_1_0)) ||
    422       (CURLE_OK != curl_easy_setopt (c, CURLOPT_TIMEOUT,
    423                                      ((long) TIMEOUTS_VAL))) ||
    424       (CURLE_OK != curl_easy_setopt (c, CURLOPT_ERRORBUFFER,
    425                                      libcurl_errbuf)) ||
    426       (CURLE_OK != curl_easy_setopt (c, CURLOPT_FAILONERROR, 0L)) ||
    427 #ifdef _DEBUG
    428       (CURLE_OK != curl_easy_setopt (c, CURLOPT_VERBOSE, 1L)) ||
    429 #endif /* _DEBUG */
    430       (CURLE_OK != curl_easy_setopt (c, CURLOPT_DEBUGFUNCTION,
    431                                      &libcurl_debug_cb)) ||
    432 #if CURL_AT_LEAST_VERSION (7, 85, 0)
    433       (CURLE_OK != curl_easy_setopt (c, CURLOPT_PROTOCOLS_STR, "http")) ||
    434 #elif CURL_AT_LEAST_VERSION (7, 19, 4)
    435       (CURLE_OK != curl_easy_setopt (c, CURLOPT_PROTOCOLS, CURLPROTO_HTTP)) ||
    436 #endif /* CURL_AT_LEAST_VERSION (7, 19, 4) */
    437 #if CURL_AT_LEAST_VERSION (7, 45, 0)
    438       (CURLE_OK != curl_easy_setopt (c, CURLOPT_DEFAULT_PROTOCOL, "http")) ||
    439 #endif /* CURL_AT_LEAST_VERSION (7, 45, 0) */
    440       (CURLE_OK != curl_easy_setopt (c, CURLOPT_PORT, ((long) port))))
    441     libcurlErrorExitDesc ("curl_easy_setopt() failed");
    442 
    443   if (CURLE_OK !=
    444       curl_easy_setopt (c, CURLOPT_URL,
    445                         URL_SCHEME_HOST EXPECTED_URI_BASE_PATH))
    446     libcurlErrorExitDesc ("Cannot set request URI");
    447 
    448   /* Set as a "custom" request, because no actual upload data is provided. */
    449   if (CURLE_OK != curl_easy_setopt (c, CURLOPT_CUSTOMREQUEST,
    450                                     MHD_HTTP_METHOD_PUT))
    451     libcurlErrorExitDesc ("curl_easy_setopt() failed");
    452 
    453   if (CURLE_OK !=
    454       curl_easy_setopt (c, CURLOPT_HTTPHEADER,
    455                         hdr_broken_cnt_len))
    456     libcurlErrorExitDesc ("Cannot set '" MHD_HTTP_HEADER_CONTENT_LENGTH "'.\n");
    457 
    458   return c;
    459 }
    460 
    461 
    462 static CURLcode
    463 performQueryExternal (struct MHD_Daemon *d, CURL *c, CURLM **multi_reuse)
    464 {
    465   CURLM *multi;
    466   time_t start;
    467   struct timeval tv;
    468   CURLcode ret;
    469 
    470   ret = CURLE_FAILED_INIT; /* will be replaced with real result */
    471   if (NULL != *multi_reuse)
    472     multi = *multi_reuse;
    473   else
    474   {
    475     multi = curl_multi_init ();
    476     if (multi == NULL)
    477       libcurlErrorExitDesc ("curl_multi_init() failed");
    478     *multi_reuse = multi;
    479   }
    480   if (CURLM_OK != curl_multi_add_handle (multi, c))
    481     libcurlErrorExitDesc ("curl_multi_add_handle() failed");
    482 
    483   start = time (NULL);
    484   while (time (NULL) - start <= TIMEOUTS_VAL)
    485   {
    486     fd_set rs;
    487     fd_set ws;
    488     fd_set es;
    489     MHD_socket maxMhdSk;
    490     int maxCurlSk;
    491     int running;
    492 
    493     maxMhdSk = MHD_INVALID_SOCKET;
    494     maxCurlSk = -1;
    495     FD_ZERO (&rs);
    496     FD_ZERO (&ws);
    497     FD_ZERO (&es);
    498     if (NULL != multi)
    499     {
    500       curl_multi_perform (multi, &running);
    501       if (0 == running)
    502       {
    503         struct CURLMsg *msg;
    504         int msgLeft;
    505         int totalMsgs = 0;
    506         do
    507         {
    508           msg = curl_multi_info_read (multi, &msgLeft);
    509           if (NULL == msg)
    510             libcurlErrorExitDesc ("curl_multi_info_read() failed");
    511           totalMsgs++;
    512           if (CURLMSG_DONE == msg->msg)
    513             ret = msg->data.result;
    514         } while (msgLeft > 0);
    515         if (1 != totalMsgs)
    516         {
    517           fprintf (stderr,
    518                    "curl_multi_info_read returned wrong "
    519                    "number of results (%d).\n",
    520                    totalMsgs);
    521           externalErrorExit ();
    522         }
    523         curl_multi_remove_handle (multi, c);
    524         multi = NULL;
    525       }
    526       else
    527       {
    528         if (CURLM_OK != curl_multi_fdset (multi, &rs, &ws, &es, &maxCurlSk))
    529           libcurlErrorExitDesc ("curl_multi_fdset() failed");
    530       }
    531     }
    532     if (NULL == multi)
    533     { /* libcurl has finished, check whether MHD still needs to perform cleanup */
    534       if (0 != MHD_get_timeout64s (d))
    535         break; /* MHD finished as well */
    536     }
    537     if (MHD_YES != MHD_get_fdset (d, &rs, &ws, &es, &maxMhdSk))
    538       mhdErrorExitDesc ("MHD_get_fdset() failed");
    539     tv.tv_sec = 0;
    540     tv.tv_usec = 200000;
    541     if (0 == MHD_get_timeout64s (d))
    542       tv.tv_usec = 0;
    543     else
    544     {
    545       long curl_to = -1;
    546       curl_multi_timeout (multi, &curl_to);
    547       if (0 == curl_to)
    548         tv.tv_usec = 0;
    549     }
    550 #ifdef MHD_POSIX_SOCKETS
    551     if (maxMhdSk > maxCurlSk)
    552       maxCurlSk = maxMhdSk;
    553 #endif /* MHD_POSIX_SOCKETS */
    554     if (-1 == select (maxCurlSk + 1, &rs, &ws, &es, &tv))
    555     {
    556 #ifdef MHD_POSIX_SOCKETS
    557       if (EINTR != errno)
    558         externalErrorExitDesc ("Unexpected select() error");
    559 #else
    560       if ((WSAEINVAL != WSAGetLastError ()) ||
    561           (0 != rs.fd_count) || (0 != ws.fd_count) || (0 != es.fd_count) )
    562         externalErrorExitDesc ("Unexpected select() error");
    563       Sleep ((unsigned long) tv.tv_usec / 1000);
    564 #endif
    565     }
    566     if (MHD_YES != MHD_run_from_select (d, &rs, &ws, &es))
    567       mhdErrorExitDesc ("MHD_run_from_select() failed");
    568   }
    569 
    570   return ret;
    571 }
    572 
    573 
    574 /**
    575  * Check request result
    576  * @param curl_code the CURL easy return code
    577  * @param pcbc the pointer struct CBC
    578  * @return non-zero if success, zero if failed
    579  */
    580 static unsigned int
    581 check_result (CURLcode curl_code, CURL *c, long expected_code)
    582 {
    583   long code;
    584 
    585   if (CURLE_OK != curl_code)
    586   {
    587     fflush (stdout);
    588     if (0 != libcurl_errbuf[0])
    589       fprintf (stderr, "Request failed. "
    590                "libcurl error: '%s'.\n"
    591                "libcurl error description: '%s'.\n",
    592                curl_easy_strerror (curl_code),
    593                libcurl_errbuf);
    594     else
    595       fprintf (stderr, "Request failed. "
    596                "libcurl error: '%s'.\n",
    597                curl_easy_strerror (curl_code));
    598     fflush (stderr);
    599     return 0;
    600   }
    601 
    602   if (CURLE_OK != curl_easy_getinfo (c, CURLINFO_RESPONSE_CODE, &code))
    603     libcurlErrorExit ();
    604 
    605   if (expected_code != code)
    606   {
    607     fprintf (stderr, "The response has wrong HTTP code: %ld\tExpected: %ld.\n",
    608              code, expected_code);
    609     return 0;
    610   }
    611   else if (verbose)
    612     printf ("The response has expected HTTP code: %ld\n", expected_code);
    613 
    614   return ! 0;
    615 }
    616 
    617 
    618 static unsigned int
    619 performTest (void)
    620 {
    621   struct MHD_Daemon *d;
    622   uint16_t port;
    623   struct CBC cbc;
    624   struct ahc_cls_type ahc_param;
    625   char buf[2048];
    626   CURL *c;
    627   CURLM *multi_reuse;
    628   int failed = 0;
    629 
    630   if (MHD_NO != MHD_is_feature_supported (MHD_FEATURE_AUTODETECT_BIND_PORT))
    631     port = 0;
    632   else
    633     port = 4220 + oneone ? 0 : 1;
    634 
    635   d = MHD_start_daemon (MHD_USE_ERROR_LOG | MHD_USE_NO_THREAD_SAFETY,
    636                         port, NULL, NULL,
    637                         &ahcCheck, &ahc_param,
    638                         MHD_OPTION_APP_FD_SETSIZE, (int) FD_SETSIZE,
    639                         MHD_OPTION_END);
    640   if (d == NULL)
    641     return 1;
    642   if (0 == port)
    643   {
    644     const union MHD_DaemonInfo *dinfo;
    645 
    646     dinfo = MHD_get_daemon_info (d,
    647                                  MHD_DAEMON_INFO_BIND_PORT);
    648     if ( (NULL == dinfo) ||
    649          (0 == dinfo->port) )
    650       mhdErrorExitDesc ("MHD_get_daemon_info() failed");
    651     port = dinfo->port;
    652   }
    653 
    654   /* First request */
    655   ahc_param.rq_method = MHD_HTTP_METHOD_PUT;
    656   ahc_param.rq_url = EXPECTED_URI_BASE_PATH;
    657   cbc.buf = buf;
    658   cbc.size = sizeof (buf);
    659   cbc.pos = 0;
    660   memset (cbc.buf, 0, cbc.size);
    661   c = setupCURL (&cbc, port);
    662   multi_reuse = NULL;
    663   /* First request */
    664   if (check_result (performQueryExternal (d, c, &multi_reuse), c,
    665                     MHD_HTTP_BAD_REQUEST))
    666   {
    667     fflush (stderr);
    668     if (verbose)
    669       printf ("Got first expected response.\n");
    670     fflush (stdout);
    671   }
    672   else
    673   {
    674     fprintf (stderr, "First request FAILED.\n");
    675     fflush (stderr);
    676     failed = 1;
    677   }
    678   /* Second request */
    679   cbc.pos = 0; /* Reset buffer position */
    680   if (check_result (performQueryExternal (d, c, &multi_reuse), c,
    681                     MHD_HTTP_BAD_REQUEST))
    682   {
    683     fflush (stderr);
    684     if (verbose)
    685       printf ("Got second expected response.\n");
    686     fflush (stdout);
    687   }
    688   else
    689   {
    690     fprintf (stderr, "Second request FAILED.\n");
    691     fflush (stderr);
    692     failed = 1;
    693   }
    694   /* Third request */
    695   cbc.pos = 0; /* Reset buffer position */
    696   if (NULL != multi_reuse)
    697     curl_multi_cleanup (multi_reuse);
    698   multi_reuse = NULL; /* Force new connection */
    699   if (check_result (performQueryExternal (d, c, &multi_reuse), c,
    700                     MHD_HTTP_BAD_REQUEST))
    701   {
    702     fflush (stderr);
    703     if (verbose)
    704       printf ("Got third expected response.\n");
    705     fflush (stdout);
    706   }
    707   else
    708   {
    709     fprintf (stderr, "Third request FAILED.\n");
    710     fflush (stderr);
    711     failed = 1;
    712   }
    713 
    714   curl_easy_cleanup (c);
    715   if (NULL != multi_reuse)
    716     curl_multi_cleanup (multi_reuse);
    717 
    718   MHD_stop_daemon (d);
    719   return failed ? 1 : 0;
    720 }
    721 
    722 
    723 int
    724 main (int argc, char *const *argv)
    725 {
    726   unsigned int errorCount = 0;
    727 
    728   /* Test type and test parameters */
    729   verbose = ! (has_param (argc, argv, "-q") ||
    730                has_param (argc, argv, "--quiet") ||
    731                has_param (argc, argv, "-s") ||
    732                has_param (argc, argv, "--silent"));
    733   oneone = ! has_in_name (argv[0], "10");
    734 
    735   test_global_init ();
    736 
    737   errorCount += performTest ();
    738   if (errorCount != 0)
    739     fprintf (stderr, "Error (code: %u)\n", errorCount);
    740   test_global_cleanup ();
    741   return (0 == errorCount) ? 0 : 1;       /* 0 == pass */
    742 }