libmicrohttpd

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

test_put_broken_len.c (20997B)


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