libmicrohttpd

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

test_head.c (26044B)


      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 HEAD requests
     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 5
    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 HEADER1_NAME "First"
    213 #define HEADER1_VALUE "1st"
    214 #define HEADER1 HEADER1_NAME ": " HEADER1_VALUE
    215 #define HEADER1_CRLF HEADER1 "\r\n"
    216 #define HEADER2_NAME "Normal"
    217 #define HEADER2_VALUE "it's fine"
    218 #define HEADER2 HEADER2_NAME ": " HEADER2_VALUE
    219 #define HEADER2_CRLF HEADER2 "\r\n"
    220 
    221 #define PAGE \
    222   "<html><head><title>libmicrohttpd demo page</title></head>" \
    223   "<body>Success!</body></html>"
    224 
    225 #define PAGE_404 \
    226   "<html><head><title>404 error</title></head>" \
    227   "<body>Error 404: The requested URI does not exist</body></html>"
    228 
    229 /* Global parameters */
    230 static int verbose;
    231 static int oneone;                  /**< If false use HTTP/1.0 for requests*/
    232 
    233 static void
    234 test_global_init (void)
    235 {
    236   libcurl_errbuf[0] = 0;
    237 
    238   if (0 != curl_global_init (CURL_GLOBAL_WIN32))
    239     externalErrorExit ();
    240 }
    241 
    242 
    243 static void
    244 test_global_cleanup (void)
    245 {
    246   curl_global_cleanup ();
    247 }
    248 
    249 
    250 struct headers_check_result
    251 {
    252   unsigned int expected_size;
    253   int header1_found;
    254   int header2_found;
    255   int size_found;
    256 };
    257 
    258 static size_t
    259 lcurl_hdr_callback (char *buffer, size_t size, size_t nitems,
    260                     void *userdata)
    261 {
    262   const size_t data_size = size * nitems;
    263   struct headers_check_result *check_res =
    264     (struct headers_check_result *) userdata;
    265 
    266   if ((MHD_STATICSTR_LEN_ (HEADER1_CRLF) == data_size) &&
    267       (0 == memcmp (HEADER1_CRLF, buffer, data_size)))
    268     check_res->header1_found++;
    269   else if ((MHD_STATICSTR_LEN_ (HEADER2_CRLF) == data_size) &&
    270            (0 == memcmp (HEADER2_CRLF, buffer, data_size)))
    271     check_res->header2_found++;
    272   else if ((MHD_STATICSTR_LEN_ (MHD_HTTP_HEADER_CONTENT_LENGTH ": ")
    273             < data_size) &&
    274            (0 ==
    275             memcmp (MHD_HTTP_HEADER_CONTENT_LENGTH ": ", buffer,
    276                     MHD_STATICSTR_LEN_ (MHD_HTTP_HEADER_CONTENT_LENGTH ": "))))
    277   {
    278     char cmpbuf[256];
    279     int res;
    280     const unsigned int numbers_pos =
    281       MHD_STATICSTR_LEN_ (MHD_HTTP_HEADER_CONTENT_LENGTH ": ");
    282     res = snprintf (cmpbuf, sizeof(cmpbuf), "%u", check_res->expected_size);
    283     if ((res <= 0) || (res > ((int) (sizeof(cmpbuf) - 1))))
    284       externalErrorExit ();
    285     if (data_size - numbers_pos <= 2)
    286       mhdErrorExitDesc ("Broken Content-Length");
    287     else if ((((size_t) res + 2) != data_size - numbers_pos) ||
    288              (0 != memcmp (buffer + numbers_pos, cmpbuf, (size_t) res)))
    289     {
    290       fprintf (stderr, "Wrong Content-Length.\n"
    291                "Expected:\n%u\n"
    292                "Received:\n%s", check_res->expected_size,
    293                buffer + numbers_pos);
    294       mhdErrorExitDesc ("Wrong Content-Length");
    295     }
    296     else if (0 != memcmp ("\r\n", buffer + data_size - 2, 2))
    297     {
    298       mhdErrorExitDesc ("The Content-Length header is not " \
    299                         "terminated by CRLF");
    300     }
    301     check_res->size_found++;
    302   }
    303 
    304   return data_size;
    305 }
    306 
    307 
    308 struct CBC
    309 {
    310   char *buf;
    311   size_t pos;
    312   size_t size;
    313 };
    314 
    315 
    316 static size_t
    317 copyBuffer (void *ptr,
    318             size_t size,
    319             size_t nmemb,
    320             void *ctx)
    321 {
    322   (void) ptr; /* Unused, mute compiler warning */
    323   (void) ctx; /* Unused, mute compiler warning */
    324   if ((0 != size) && (0 != nmemb))
    325     libcurlErrorExitDesc ("Received unexpected body data");
    326   return size * nmemb;
    327 }
    328 
    329 
    330 struct ahc_cls_type
    331 {
    332   const char *rq_method;
    333   const char *rq_url;
    334 };
    335 
    336 
    337 static enum MHD_Result
    338 ahcCheck (void *cls,
    339           struct MHD_Connection *connection,
    340           const char *url,
    341           const char *method,
    342           const char *version,
    343           const char *upload_data, size_t *upload_data_size,
    344           void **req_cls)
    345 {
    346   static int marker;
    347   struct MHD_Response *response;
    348   enum MHD_Result ret;
    349   struct ahc_cls_type *const param = (struct ahc_cls_type *) cls;
    350   unsigned int http_code;
    351 
    352   if (NULL == param)
    353     mhdErrorExitDesc ("cls parameter is NULL");
    354 
    355   if (oneone)
    356   {
    357     if (0 != strcmp (version, MHD_HTTP_VERSION_1_1))
    358       mhdErrorExitDesc ("Unexpected HTTP version");
    359   }
    360   else
    361   {
    362     if (0 != strcmp (version, MHD_HTTP_VERSION_1_0))
    363       mhdErrorExitDesc ("Unexpected HTTP version");
    364   }
    365 
    366   if (0 != strcmp (url, param->rq_url))
    367     mhdErrorExitDesc ("Unexpected URI");
    368 
    369   if (NULL != upload_data)
    370     mhdErrorExitDesc ("'upload_data' is not NULL");
    371 
    372   if (NULL == upload_data_size)
    373     mhdErrorExitDesc ("'upload_data_size' pointer is NULL");
    374 
    375   if (0 != *upload_data_size)
    376     mhdErrorExitDesc ("'*upload_data_size' value is not zero");
    377 
    378   if (0 != strcmp (param->rq_method, method))
    379     mhdErrorExitDesc ("Unexpected request method");
    380 
    381   if (&marker != *req_cls)
    382   {
    383     *req_cls = &marker;
    384     return MHD_YES;
    385   }
    386   *req_cls = NULL;
    387 
    388   if (0 == strcmp (url, EXISTING_URI))
    389   {
    390     response =
    391       MHD_create_response_from_buffer_static (MHD_STATICSTR_LEN_ (PAGE),
    392                                               PAGE);
    393     http_code = MHD_HTTP_OK;
    394   }
    395   else
    396   {
    397     response =
    398       MHD_create_response_from_buffer_static (MHD_STATICSTR_LEN_ (PAGE_404),
    399                                               PAGE_404);
    400     http_code = MHD_HTTP_NOT_FOUND;
    401   }
    402   if (NULL == response)
    403     mhdErrorExitDesc ("Failed to create response");
    404 
    405   if (MHD_YES != MHD_add_response_header (response,
    406                                           HEADER1_NAME,
    407                                           HEADER1_VALUE))
    408     mhdErrorExitDesc ("Cannot add header1");
    409   if (MHD_YES != MHD_add_response_header (response,
    410                                           HEADER2_NAME,
    411                                           HEADER2_VALUE))
    412     mhdErrorExitDesc ("Cannot add header2");
    413 
    414   ret = MHD_queue_response (connection,
    415                             http_code,
    416                             response);
    417   MHD_destroy_response (response);
    418   if (MHD_YES != ret)
    419     mhdErrorExitDesc ("Failed to queue response");
    420 
    421   return ret;
    422 }
    423 
    424 
    425 /**
    426  * Set required URI for the request
    427  * @param c the CURL handle to use
    428  * @param uri_exist if non-zero use request for "existing" URI
    429  */
    430 static void
    431 setCURL_rq_path (CURL *c, int uri_exist)
    432 {
    433   if (uri_exist)
    434   {
    435     if (CURLE_OK !=
    436         curl_easy_setopt (c, CURLOPT_URL,
    437                           URL_SCHEME_HOST EXPECTED_URI_BASE_PATH))
    438       libcurlErrorExitDesc ("Cannot set request URL");
    439   }
    440   else
    441   {
    442     if (CURLE_OK !=
    443         curl_easy_setopt (c, CURLOPT_URL,
    444                           URL_SCHEME_HOST EXPECTED_URI_BASE_PATH_MISSING))
    445       libcurlErrorExitDesc ("Cannot set request URL");
    446   }
    447 }
    448 
    449 
    450 static int
    451 libcurl_debug_cb (CURL *handle,
    452                   curl_infotype type,
    453                   char *data,
    454                   size_t size,
    455                   void *userptr)
    456 {
    457   static const char excess_mark[] = "Excess found";
    458   static const size_t excess_mark_len = MHD_STATICSTR_LEN_ (excess_mark);
    459 
    460   (void) handle;
    461   (void) userptr;
    462 
    463 #ifdef _DEBUG
    464   switch (type)
    465   {
    466   case CURLINFO_TEXT:
    467     fprintf (stderr, "* %.*s", (int) size, data);
    468     break;
    469   case CURLINFO_HEADER_IN:
    470     fprintf (stderr, "< %.*s", (int) size, data);
    471     break;
    472   case CURLINFO_HEADER_OUT:
    473     fprintf (stderr, "> %.*s", (int) size, data);
    474     break;
    475   case CURLINFO_DATA_IN:
    476 #if 0
    477     fprintf (stderr, "<| %.*s\n", (int) size, data);
    478 #endif
    479     break;
    480   case CURLINFO_DATA_OUT:
    481   case CURLINFO_SSL_DATA_IN:
    482   case CURLINFO_SSL_DATA_OUT:
    483   case CURLINFO_END:
    484   default:
    485     break;
    486   }
    487 #endif /* _DEBUG */
    488   if (CURLINFO_TEXT == type)
    489   {
    490     if ((size >= excess_mark_len) &&
    491         (0 == memcmp (data, excess_mark, excess_mark_len)))
    492       mhdErrorExitDesc ("Extra data has been detected in MHD reply");
    493   }
    494   return 0;
    495 }
    496 
    497 
    498 static CURL *
    499 setupCURL (void *cbc, uint16_t port,
    500            struct headers_check_result *hdr_chk_result)
    501 {
    502   CURL *c;
    503 
    504   c = curl_easy_init ();
    505   if (NULL == c)
    506     libcurlErrorExitDesc ("curl_easy_init() failed");
    507 
    508   if ((CURLE_OK != curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1L)) ||
    509       (CURLE_OK != curl_easy_setopt (c, CURLOPT_WRITEFUNCTION,
    510                                      &copyBuffer)) ||
    511       (CURLE_OK != curl_easy_setopt (c, CURLOPT_WRITEDATA, cbc)) ||
    512       (CURLE_OK != curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT,
    513                                      ((long) TIMEOUTS_VAL))) ||
    514       (CURLE_OK != curl_easy_setopt (c, CURLOPT_HTTP_VERSION,
    515                                      (oneone) ?
    516                                      CURL_HTTP_VERSION_1_1 :
    517                                      CURL_HTTP_VERSION_1_0)) ||
    518       (CURLE_OK != curl_easy_setopt (c, CURLOPT_TIMEOUT,
    519                                      ((long) TIMEOUTS_VAL))) ||
    520       (CURLE_OK != curl_easy_setopt (c, CURLOPT_HEADERFUNCTION,
    521                                      lcurl_hdr_callback)) ||
    522       (CURLE_OK != curl_easy_setopt (c, CURLOPT_HEADERDATA,
    523                                      hdr_chk_result)) ||
    524       (CURLE_OK != curl_easy_setopt (c, CURLOPT_ERRORBUFFER,
    525                                      libcurl_errbuf)) ||
    526       (CURLE_OK != curl_easy_setopt (c, CURLOPT_FAILONERROR, 0L)) ||
    527 #ifdef _DEBUG
    528       (CURLE_OK != curl_easy_setopt (c, CURLOPT_VERBOSE, 1L)) ||
    529 #endif /* _DEBUG */
    530       (CURLE_OK != curl_easy_setopt (c, CURLOPT_DEBUGFUNCTION,
    531                                      &libcurl_debug_cb)) ||
    532 #if CURL_AT_LEAST_VERSION (7, 85, 0)
    533       (CURLE_OK != curl_easy_setopt (c, CURLOPT_PROTOCOLS_STR, "http")) ||
    534 #elif CURL_AT_LEAST_VERSION (7, 19, 4)
    535       (CURLE_OK != curl_easy_setopt (c, CURLOPT_PROTOCOLS, CURLPROTO_HTTP)) ||
    536 #endif /* CURL_AT_LEAST_VERSION (7, 19, 4) */
    537 #if CURL_AT_LEAST_VERSION (7, 45, 0)
    538       (CURLE_OK != curl_easy_setopt (c, CURLOPT_DEFAULT_PROTOCOL, "http")) ||
    539 #endif /* CURL_AT_LEAST_VERSION (7, 45, 0) */
    540       (CURLE_OK != curl_easy_setopt (c, CURLOPT_PORT, ((long) port))))
    541     libcurlErrorExitDesc ("curl_easy_setopt() failed");
    542 
    543   /* When 'CURLOPT_NOBODY' is set, libcurl should use HEAD request. */
    544   if (CURLE_OK != curl_easy_setopt (c, CURLOPT_NOBODY, (long) 1))
    545     libcurlErrorExitDesc ("curl_easy_setopt() failed");
    546 
    547   return c;
    548 }
    549 
    550 
    551 static CURLcode
    552 performQueryExternal (struct MHD_Daemon *d, CURL *c, CURLM **multi_reuse)
    553 {
    554   CURLM *multi;
    555   time_t start;
    556   struct timeval tv;
    557   CURLcode ret;
    558 
    559   ret = CURLE_FAILED_INIT; /* will be replaced with real result */
    560   if (NULL != *multi_reuse)
    561     multi = *multi_reuse;
    562   else
    563   {
    564     multi = curl_multi_init ();
    565     if (multi == NULL)
    566       libcurlErrorExitDesc ("curl_multi_init() failed");
    567     *multi_reuse = multi;
    568   }
    569   if (CURLM_OK != curl_multi_add_handle (multi, c))
    570     libcurlErrorExitDesc ("curl_multi_add_handle() failed");
    571 
    572   start = time (NULL);
    573   while (time (NULL) - start <= TIMEOUTS_VAL)
    574   {
    575     fd_set rs;
    576     fd_set ws;
    577     fd_set es;
    578     MHD_socket maxMhdSk;
    579     int maxCurlSk;
    580     int running;
    581 
    582     maxMhdSk = MHD_INVALID_SOCKET;
    583     maxCurlSk = -1;
    584     FD_ZERO (&rs);
    585     FD_ZERO (&ws);
    586     FD_ZERO (&es);
    587     if (NULL != multi)
    588     {
    589       curl_multi_perform (multi, &running);
    590       if (0 == running)
    591       {
    592         struct CURLMsg *msg;
    593         int msgLeft;
    594         int totalMsgs = 0;
    595         do
    596         {
    597           msg = curl_multi_info_read (multi, &msgLeft);
    598           if (NULL == msg)
    599             libcurlErrorExitDesc ("curl_multi_info_read() failed");
    600           totalMsgs++;
    601           if (CURLMSG_DONE == msg->msg)
    602             ret = msg->data.result;
    603         } while (msgLeft > 0);
    604         if (1 != totalMsgs)
    605         {
    606           fprintf (stderr,
    607                    "curl_multi_info_read returned wrong "
    608                    "number of results (%d).\n",
    609                    totalMsgs);
    610           externalErrorExit ();
    611         }
    612         curl_multi_remove_handle (multi, c);
    613         multi = NULL;
    614       }
    615       else
    616       {
    617         if (CURLM_OK != curl_multi_fdset (multi, &rs, &ws, &es, &maxCurlSk))
    618           libcurlErrorExitDesc ("curl_multi_fdset() failed");
    619       }
    620     }
    621     if (NULL == multi)
    622     { /* libcurl has finished, check whether MHD still needs to perform cleanup */
    623       if (0 != MHD_get_timeout64s (d))
    624         break; /* MHD finished as well */
    625     }
    626     if (MHD_YES != MHD_get_fdset (d, &rs, &ws, &es, &maxMhdSk))
    627       mhdErrorExitDesc ("MHD_get_fdset() failed");
    628     tv.tv_sec = 0;
    629     tv.tv_usec = 200000;
    630     if (0 == MHD_get_timeout64s (d))
    631       tv.tv_usec = 0;
    632     else
    633     {
    634       long curl_to = -1;
    635       curl_multi_timeout (multi, &curl_to);
    636       if (0 == curl_to)
    637         tv.tv_usec = 0;
    638     }
    639 #ifdef MHD_POSIX_SOCKETS
    640     if (maxMhdSk > maxCurlSk)
    641       maxCurlSk = maxMhdSk;
    642 #endif /* MHD_POSIX_SOCKETS */
    643     if (-1 == select (maxCurlSk + 1, &rs, &ws, &es, &tv))
    644     {
    645 #ifdef MHD_POSIX_SOCKETS
    646       if (EINTR != errno)
    647         externalErrorExitDesc ("Unexpected select() error");
    648 #else
    649       if ((WSAEINVAL != WSAGetLastError ()) ||
    650           (0 != rs.fd_count) || (0 != ws.fd_count) || (0 != es.fd_count) )
    651         externalErrorExitDesc ("Unexpected select() error");
    652       Sleep ((unsigned long) tv.tv_usec / 1000);
    653 #endif
    654     }
    655     if (MHD_YES != MHD_run_from_select (d, &rs, &ws, &es))
    656       mhdErrorExitDesc ("MHD_run_from_select() failed");
    657   }
    658 
    659   return ret;
    660 }
    661 
    662 
    663 /**
    664  * Check request result
    665  * @param curl_code the CURL easy return code
    666  * @param pcbc the pointer struct CBC
    667  * @return non-zero if success, zero if failed
    668  */
    669 static unsigned int
    670 check_result (CURLcode curl_code, CURL *c, long expected_code,
    671               struct headers_check_result *hdr_res)
    672 {
    673   long code;
    674   unsigned int ret;
    675 
    676   if (CURLE_OK != curl_code)
    677   {
    678     fflush (stdout);
    679     if (0 != libcurl_errbuf[0])
    680       fprintf (stderr, "Request failed. "
    681                "libcurl error: '%s'.\n"
    682                "libcurl error description: '%s'.\n",
    683                curl_easy_strerror (curl_code),
    684                libcurl_errbuf);
    685     else
    686       fprintf (stderr, "Request failed. "
    687                "libcurl error: '%s'.\n",
    688                curl_easy_strerror (curl_code));
    689     fflush (stderr);
    690     return 0;
    691   }
    692 
    693   if (CURLE_OK != curl_easy_getinfo (c, CURLINFO_RESPONSE_CODE, &code))
    694     libcurlErrorExit ();
    695 
    696   if (expected_code != code)
    697   {
    698     fprintf (stderr, "The response has wrong HTTP code: %ld\tExpected: %ld.\n",
    699              code, expected_code);
    700     return 0;
    701   }
    702   else if (verbose)
    703     printf ("The response has expected HTTP code: %ld\n", expected_code);
    704 
    705   ret = 1;
    706   if (1 != hdr_res->header1_found)
    707   {
    708     if (0 == hdr_res->header1_found)
    709       fprintf (stderr, "Response header1 was not found.\n");
    710     else
    711       fprintf (stderr, "Response header1 was found %d times "
    712                "instead of one time only.\n", hdr_res->header1_found);
    713     ret = 0;
    714   }
    715   else if (verbose)
    716     printf ("Header1 is present in the response.\n");
    717   if (1 != hdr_res->header2_found)
    718   {
    719     if (0 == hdr_res->header2_found)
    720       fprintf (stderr, "Response header2 was not found.\n");
    721     else
    722       fprintf (stderr, "Response header2 was found %d times "
    723                "instead of one time only.\n", hdr_res->header2_found);
    724     ret = 0;
    725   }
    726   else if (verbose)
    727     printf ("Header2 is present in the response.\n");
    728   if (1 != hdr_res->size_found)
    729   {
    730     if (0 == hdr_res->size_found)
    731       fprintf (stderr, "Response 'Content-Length' header was not found.\n");
    732     else
    733       fprintf (stderr, "Response 'Content-Length' header was found %d times "
    734                "instead of one time only.\n", hdr_res->size_found);
    735     ret = 0;
    736   }
    737   else if (verbose)
    738     printf ("'Content-Length' header with correct value "
    739             "is present in the response.\n");
    740 
    741   return ret;
    742 }
    743 
    744 
    745 static unsigned int
    746 testHead (void)
    747 {
    748   struct MHD_Daemon *d;
    749   uint16_t port;
    750   struct CBC cbc;
    751   struct ahc_cls_type ahc_param;
    752   struct headers_check_result rp_headers_check;
    753   char buf[2048];
    754   CURL *c;
    755   CURLM *multi_reuse;
    756   int failed = 0;
    757 
    758   if (MHD_NO != MHD_is_feature_supported (MHD_FEATURE_AUTODETECT_BIND_PORT))
    759     port = 0;
    760   else
    761     port = 4220 + oneone ? 0 : 1;
    762 
    763   d = MHD_start_daemon (MHD_USE_ERROR_LOG | MHD_USE_NO_THREAD_SAFETY,
    764                         port, NULL, NULL,
    765                         &ahcCheck, &ahc_param,
    766                         MHD_OPTION_APP_FD_SETSIZE, (int) FD_SETSIZE,
    767                         MHD_OPTION_END);
    768   if (d == NULL)
    769     return 1;
    770   if (0 == port)
    771   {
    772     const union MHD_DaemonInfo *dinfo;
    773 
    774     dinfo = MHD_get_daemon_info (d,
    775                                  MHD_DAEMON_INFO_BIND_PORT);
    776     if ( (NULL == dinfo) ||
    777          (0 == dinfo->port) )
    778       mhdErrorExitDesc ("MHD_get_daemon_info() failed");
    779     port = dinfo->port;
    780   }
    781 
    782   /* First request */
    783   ahc_param.rq_method = MHD_HTTP_METHOD_HEAD;
    784   ahc_param.rq_url = EXPECTED_URI_BASE_PATH;
    785   rp_headers_check.expected_size = MHD_STATICSTR_LEN_ (PAGE);
    786   rp_headers_check.header1_found = 0;
    787   rp_headers_check.header2_found = 0;
    788   rp_headers_check.size_found = 0;
    789   cbc.buf = buf;
    790   cbc.size = sizeof (buf);
    791   cbc.pos = 0;
    792   memset (cbc.buf, 0, cbc.size);
    793   c = setupCURL (&cbc, port, &rp_headers_check);
    794   setCURL_rq_path (c, 1);
    795   multi_reuse = NULL;
    796   /* First request */
    797   if (check_result (performQueryExternal (d, c, &multi_reuse), c,
    798                     MHD_HTTP_OK, &rp_headers_check))
    799   {
    800     fflush (stderr);
    801     if (verbose)
    802       printf ("Got first expected response.\n");
    803     fflush (stdout);
    804   }
    805   else
    806   {
    807     fprintf (stderr, "First request FAILED.\n");
    808     fflush (stderr);
    809     failed = 1;
    810   }
    811   /* Second request */
    812   rp_headers_check.expected_size = MHD_STATICSTR_LEN_ (PAGE_404);
    813   rp_headers_check.header1_found = 0;
    814   rp_headers_check.header2_found = 0;
    815   rp_headers_check.size_found = 0;
    816   cbc.pos = 0; /* Reset buffer position */
    817   ahc_param.rq_url = EXPECTED_URI_BASE_PATH_MISSING;
    818   setCURL_rq_path (c, 0);
    819   if (check_result (performQueryExternal (d, c, &multi_reuse), c,
    820                     MHD_HTTP_NOT_FOUND, &rp_headers_check))
    821   {
    822     fflush (stderr);
    823     if (verbose)
    824       printf ("Got second expected response.\n");
    825     fflush (stdout);
    826   }
    827   else
    828   {
    829     fprintf (stderr, "Second request FAILED.\n");
    830     fflush (stderr);
    831     failed = 1;
    832   }
    833   /* Third request */
    834   rp_headers_check.header1_found = 0;
    835   rp_headers_check.header2_found = 0;
    836   rp_headers_check.size_found = 0;
    837   cbc.pos = 0; /* Reset buffer position */
    838   if (NULL != multi_reuse)
    839     curl_multi_cleanup (multi_reuse);
    840   multi_reuse = NULL; /* Force new connection */
    841   if (check_result (performQueryExternal (d, c, &multi_reuse), c,
    842                     MHD_HTTP_NOT_FOUND, &rp_headers_check))
    843   {
    844     fflush (stderr);
    845     if (verbose)
    846       printf ("Got third expected response.\n");
    847     fflush (stdout);
    848   }
    849   else
    850   {
    851     fprintf (stderr, "Third request FAILED.\n");
    852     fflush (stderr);
    853     failed = 1;
    854   }
    855 
    856   curl_easy_cleanup (c);
    857   if (NULL != multi_reuse)
    858     curl_multi_cleanup (multi_reuse);
    859 
    860   MHD_stop_daemon (d);
    861   return failed ? 1 : 0;
    862 }
    863 
    864 
    865 int
    866 main (int argc, char *const *argv)
    867 {
    868   unsigned int errorCount = 0;
    869 
    870   /* Test type and test parameters */
    871   verbose = ! (has_param (argc, argv, "-q") ||
    872                has_param (argc, argv, "--quiet") ||
    873                has_param (argc, argv, "-s") ||
    874                has_param (argc, argv, "--silent"));
    875   oneone = ! has_in_name (argv[0], "10");
    876 
    877   test_global_init ();
    878 
    879   errorCount += testHead ();
    880   if (errorCount != 0)
    881     fprintf (stderr, "Error (code: %u)\n", errorCount);
    882   test_global_cleanup ();
    883   return (0 == errorCount) ? 0 : 1;       /* 0 == pass */
    884 }