libmicrohttpd

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

test_basicauth.c (23992B)


      1 /*
      2      This file is part of libmicrohttpd
      3      Copyright (C) 2010 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 test_basicauth.c
     24  * @brief  Testcase for libmicrohttpd Basic Authorisation
     25  * @author Amr Ali
     26  * @author Karlson2k (Evgeny Grin)
     27  */
     28 
     29 #include "MHD_config.h"
     30 #include "platform.h"
     31 #include <curl/curl.h>
     32 #include <microhttpd.h>
     33 #include <stdlib.h>
     34 #include <string.h>
     35 #include <time.h>
     36 #include <errno.h>
     37 
     38 #ifndef WINDOWS
     39 #include <sys/socket.h>
     40 #include <unistd.h>
     41 #else
     42 #include <wincrypt.h>
     43 #endif
     44 
     45 #include "mhd_has_param.h"
     46 #include "mhd_has_in_name.h"
     47 
     48 /* Turn any MHD_PANIC() or failing mhd_assert() reached from this
     49    test into a marked, classifiable test error (TESTING.md, P5). */
     50 #include "mhd_panic_tripwire.h"
     51 
     52 #ifndef MHD_STATICSTR_LEN_
     53 /**
     54  * Determine length of static string / macro strings at compile time.
     55  */
     56 #define MHD_STATICSTR_LEN_(macro) (sizeof(macro) / sizeof(char) - 1)
     57 #endif /* ! MHD_STATICSTR_LEN_ */
     58 
     59 #ifndef CURL_VERSION_BITS
     60 #define CURL_VERSION_BITS(x,y,z) ((x)<<16|(y)<<8|(z))
     61 #endif /* ! CURL_VERSION_BITS */
     62 #ifndef CURL_AT_LEAST_VERSION
     63 #define CURL_AT_LEAST_VERSION(x,y,z) \
     64   (LIBCURL_VERSION_NUM >= CURL_VERSION_BITS(x, y, z))
     65 #endif /* ! CURL_AT_LEAST_VERSION */
     66 
     67 #ifndef _MHD_INSTRMACRO
     68 /* Quoted macro parameter */
     69 #define _MHD_INSTRMACRO(a) #a
     70 #endif /* ! _MHD_INSTRMACRO */
     71 #ifndef _MHD_STRMACRO
     72 /* Quoted expanded macro parameter */
     73 #define _MHD_STRMACRO(a) _MHD_INSTRMACRO (a)
     74 #endif /* ! _MHD_STRMACRO */
     75 
     76 #if defined(HAVE___FUNC__)
     77 #define externalErrorExit(ignore) \
     78     _externalErrorExit_func(NULL, __func__, __LINE__)
     79 #define externalErrorExitDesc(errDesc) \
     80     _externalErrorExit_func(errDesc, __func__, __LINE__)
     81 #define libcurlErrorExit(ignore) \
     82     _libcurlErrorExit_func(NULL, __func__, __LINE__)
     83 #define libcurlErrorExitDesc(errDesc) \
     84     _libcurlErrorExit_func(errDesc, __func__, __LINE__)
     85 #define mhdErrorExit(ignore) \
     86     _mhdErrorExit_func(NULL, __func__, __LINE__)
     87 #define mhdErrorExitDesc(errDesc) \
     88     _mhdErrorExit_func(errDesc, __func__, __LINE__)
     89 #define checkCURLE_OK(libcurlcall) \
     90     _checkCURLE_OK_func((libcurlcall), _MHD_STRMACRO(libcurlcall), \
     91                         __func__, __LINE__)
     92 #elif defined(HAVE___FUNCTION__)
     93 #define externalErrorExit(ignore) \
     94     _externalErrorExit_func(NULL, __FUNCTION__, __LINE__)
     95 #define externalErrorExitDesc(errDesc) \
     96     _externalErrorExit_func(errDesc, __FUNCTION__, __LINE__)
     97 #define libcurlErrorExit(ignore) \
     98     _libcurlErrorExit_func(NULL, __FUNCTION__, __LINE__)
     99 #define libcurlErrorExitDesc(errDesc) \
    100     _libcurlErrorExit_func(errDesc, __FUNCTION__, __LINE__)
    101 #define mhdErrorExit(ignore) \
    102     _mhdErrorExit_func(NULL, __FUNCTION__, __LINE__)
    103 #define mhdErrorExitDesc(errDesc) \
    104     _mhdErrorExit_func(errDesc, __FUNCTION__, __LINE__)
    105 #define checkCURLE_OK(libcurlcall) \
    106     _checkCURLE_OK_func((libcurlcall), _MHD_STRMACRO(libcurlcall), \
    107                         __FUNCTION__, __LINE__)
    108 #else
    109 #define externalErrorExit(ignore) _externalErrorExit_func(NULL, NULL, __LINE__)
    110 #define externalErrorExitDesc(errDesc) \
    111   _externalErrorExit_func(errDesc, NULL, __LINE__)
    112 #define libcurlErrorExit(ignore) _libcurlErrorExit_func(NULL, NULL, __LINE__)
    113 #define libcurlErrorExitDesc(errDesc) \
    114   _libcurlErrorExit_func(errDesc, NULL, __LINE__)
    115 #define mhdErrorExit(ignore) _mhdErrorExit_func(NULL, NULL, __LINE__)
    116 #define mhdErrorExitDesc(errDesc) _mhdErrorExit_func(errDesc, NULL, __LINE__)
    117 #define checkCURLE_OK(libcurlcall) \
    118   _checkCURLE_OK_func((libcurlcall), _MHD_STRMACRO(libcurlcall), NULL, __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 /* Not actually used in this test */
    146 static char libcurl_errbuf[CURL_ERROR_SIZE] = "";
    147 
    148 _MHD_NORETURN static void
    149 _libcurlErrorExit_func (const char *errDesc, const char *funcName, int lineNum)
    150 {
    151   fflush (stdout);
    152   if ((NULL != errDesc) && (0 != errDesc[0]))
    153     fprintf (stderr, "%s", errDesc);
    154   else
    155     fprintf (stderr, "CURL library call failed");
    156   if ((NULL != funcName) && (0 != funcName[0]))
    157     fprintf (stderr, " in %s", funcName);
    158   if (0 < lineNum)
    159     fprintf (stderr, " at line %d", lineNum);
    160 
    161   fprintf (stderr, ".\nLast errno value: %d (%s)\n", (int) errno,
    162            strerror (errno));
    163 #ifdef MHD_WINSOCK_SOCKETS
    164   fprintf (stderr, "WSAGetLastError() value: %d\n", (int) WSAGetLastError ());
    165 #endif /* MHD_WINSOCK_SOCKETS */
    166   if (0 != libcurl_errbuf[0])
    167     fprintf (stderr, "Last libcurl error description: %s\n", libcurl_errbuf);
    168 
    169   fflush (stderr);
    170   exit (99);
    171 }
    172 
    173 
    174 _MHD_NORETURN static void
    175 _mhdErrorExit_func (const char *errDesc, const char *funcName, int lineNum)
    176 {
    177   fflush (stdout);
    178   if ((NULL != errDesc) && (0 != errDesc[0]))
    179     fprintf (stderr, "%s", errDesc);
    180   else
    181     fprintf (stderr, "MHD unexpected error");
    182   if ((NULL != funcName) && (0 != funcName[0]))
    183     fprintf (stderr, " in %s", funcName);
    184   if (0 < lineNum)
    185     fprintf (stderr, " at line %d", lineNum);
    186 
    187   fprintf (stderr, ".\nLast errno value: %d (%s)\n", (int) errno,
    188            strerror (errno));
    189 #ifdef MHD_WINSOCK_SOCKETS
    190   fprintf (stderr, "WSAGetLastError() value: %d\n", (int) WSAGetLastError ());
    191 #endif /* MHD_WINSOCK_SOCKETS */
    192 
    193   fflush (stderr);
    194   exit (8);
    195 }
    196 
    197 
    198 #if 0
    199 /* Function unused in this test */
    200 static void
    201 _checkCURLE_OK_func (CURLcode code, const char *curlFunc,
    202                      const char *funcName, int lineNum)
    203 {
    204   if (CURLE_OK == code)
    205     return;
    206 
    207   fflush (stdout);
    208   if ((NULL != curlFunc) && (0 != curlFunc[0]))
    209     fprintf (stderr, "'%s' resulted in '%s'", curlFunc,
    210              curl_easy_strerror (code));
    211   else
    212     fprintf (stderr, "libcurl function call resulted in '%s'",
    213              curl_easy_strerror (code));
    214   if ((NULL != funcName) && (0 != funcName[0]))
    215     fprintf (stderr, " in %s", funcName);
    216   if (0 < lineNum)
    217     fprintf (stderr, " at line %d", lineNum);
    218 
    219   fprintf (stderr, ".\nLast errno value: %d (%s)\n", (int) errno,
    220            strerror (errno));
    221   if (0 != libcurl_errbuf[0])
    222     fprintf (stderr, "Last libcurl error description: %s\n", libcurl_errbuf);
    223 
    224   fflush (stderr);
    225   exit (9);
    226 }
    227 
    228 
    229 #endif
    230 
    231 
    232 /* Could be increased to facilitate debugging */
    233 #define TIMEOUTS_VAL 10
    234 
    235 #define MHD_URI_BASE_PATH "/bar%20foo%3Fkey%3Dvalue"
    236 
    237 #define REALM "TestRealm"
    238 #define USERNAME "Aladdin"
    239 #define PASSWORD "open sesame"
    240 
    241 
    242 #define PAGE \
    243   "<html><head><title>libmicrohttpd demo page</title>" \
    244   "</head><body>Access granted</body></html>"
    245 
    246 #define DENIED \
    247   "<html><head><title>libmicrohttpd - Access denied</title>" \
    248   "</head><body>Access denied</body></html>"
    249 
    250 struct CBC
    251 {
    252   char *buf;
    253   size_t pos;
    254   size_t size;
    255 };
    256 
    257 static int verbose;
    258 static int preauth;
    259 static int oldapi;
    260 
    261 static size_t
    262 copyBuffer (void *ptr,
    263             size_t size,
    264             size_t nmemb,
    265             void *ctx)
    266 {
    267   struct CBC *cbc = ctx;
    268 
    269   if (cbc->pos + size * nmemb > cbc->size)
    270     mhdErrorExitDesc ("Wrong too large data");       /* overflow */
    271   memcpy (&cbc->buf[cbc->pos], ptr, size * nmemb);
    272   cbc->pos += size * nmemb;
    273   return size * nmemb;
    274 }
    275 
    276 
    277 static enum MHD_Result
    278 ahc_echo (void *cls,
    279           struct MHD_Connection *connection,
    280           const char *url,
    281           const char *method,
    282           const char *version,
    283           const char *upload_data,
    284           size_t *upload_data_size,
    285           void **req_cls)
    286 {
    287   struct MHD_Response *response;
    288   enum MHD_Result ret;
    289   static int already_called_marker;
    290   (void) cls; (void) url;         /* Unused. Silent compiler warning. */
    291   (void) method; (void) version; (void) upload_data; /* Unused. Silent compiler warning. */
    292   (void) upload_data_size;        /* Unused. Silent compiler warning. */
    293 
    294   if (&already_called_marker != *req_cls)
    295   { /* Called for the first time, request not fully read yet */
    296     *req_cls = &already_called_marker;
    297     /* Wait for complete request */
    298     return MHD_YES;
    299   }
    300 
    301   if (0 != strcmp (method, MHD_HTTP_METHOD_GET))
    302     mhdErrorExitDesc ("Unexpected HTTP method");
    303 
    304   /* require: USERNAME with password PASSWORD */
    305   if (! oldapi)
    306   {
    307     struct MHD_BasicAuthInfo *creds;
    308 
    309     creds = MHD_basic_auth_get_username_password3 (connection);
    310     if (NULL != creds)
    311     {
    312       if (NULL == creds->username)
    313         mhdErrorExitDesc ("'username' is NULL");
    314       else if (MHD_STATICSTR_LEN_ (USERNAME) != creds->username_len)
    315       {
    316         fprintf (stderr, "'username_len' does not match.\n"
    317                  "Expected: %u\tRecieved: %u. ",
    318                  (unsigned) MHD_STATICSTR_LEN_ (USERNAME),
    319                  (unsigned) creds->username_len);
    320         mhdErrorExitDesc ("Wrong 'username_len'");
    321       }
    322       else if (0 != memcmp (creds->username, USERNAME, creds->username_len))
    323       {
    324         fprintf (stderr, "'username' does not match.\n"
    325                  "Expected: '%s'\tRecieved: '%.*s'. ",
    326                  USERNAME,
    327                  (int) creds->username_len,
    328                  creds->username);
    329         mhdErrorExitDesc ("Wrong 'username'");
    330       }
    331       else if (0 != creds->username[creds->username_len])
    332         mhdErrorExitDesc ("'username' is not zero-terminated");
    333       else if (NULL == creds->password)
    334         mhdErrorExitDesc ("'password' is NULL");
    335       else if (MHD_STATICSTR_LEN_ (PASSWORD) != creds->password_len)
    336       {
    337         fprintf (stderr, "'password_len' does not match.\n"
    338                  "Expected: %u\tRecieved: %u. ",
    339                  (unsigned) MHD_STATICSTR_LEN_ (PASSWORD),
    340                  (unsigned) creds->password_len);
    341         mhdErrorExitDesc ("Wrong 'password_len'");
    342       }
    343       else if (0 != memcmp (creds->password, PASSWORD, creds->password_len))
    344       {
    345         fprintf (stderr, "'password' does not match.\n"
    346                  "Expected: '%s'\tRecieved: '%.*s'. ",
    347                  PASSWORD,
    348                  (int) creds->password_len,
    349                  creds->password);
    350         mhdErrorExitDesc ("Wrong 'username'");
    351       }
    352       else if (0 != creds->password[creds->password_len])
    353         mhdErrorExitDesc ("'password' is not zero-terminated");
    354 
    355       MHD_free (creds);
    356 
    357       response =
    358         MHD_create_response_from_buffer_static (MHD_STATICSTR_LEN_ (PAGE),
    359                                                 (const void *) PAGE);
    360       if (NULL == response)
    361         mhdErrorExitDesc ("Response creation failed");
    362       ret = MHD_queue_response (connection, MHD_HTTP_OK, response);
    363       if (MHD_YES != ret)
    364         mhdErrorExitDesc ("'MHD_queue_response()' failed");
    365     }
    366     else
    367     {
    368       response =
    369         MHD_create_response_from_buffer_static (MHD_STATICSTR_LEN_ (DENIED),
    370                                                 (const void *) DENIED);
    371       if (NULL == response)
    372         mhdErrorExitDesc ("Response creation failed");
    373       ret = MHD_queue_basic_auth_required_response3 (connection, REALM, MHD_YES,
    374                                                      response);
    375       if (MHD_YES != ret)
    376         mhdErrorExitDesc ("'MHD_queue_basic_auth_required_response3()' failed");
    377     }
    378   }
    379   else
    380   {
    381     char *username;
    382     char *password;
    383 
    384     password = NULL;
    385     username = MHD_basic_auth_get_username_password (connection,
    386                                                      &password);
    387     if (NULL != username)
    388     {
    389       if (0 != strcmp (username, USERNAME))
    390       {
    391         fprintf (stderr, "'username' does not match.\n"
    392                  "Expected: '%s'\tRecieved: '%s'. ", USERNAME, username);
    393         mhdErrorExitDesc ("Wrong 'username'");
    394       }
    395       if (NULL == password)
    396         mhdErrorExitDesc ("The password pointer is NULL");
    397       if (0 != strcmp (password, PASSWORD))
    398         fprintf (stderr, "'password' does not match.\n"
    399                  "Expected: '%s'\tRecieved: '%s'. ", PASSWORD, password);
    400       response =
    401         MHD_create_response_from_buffer_static (MHD_STATICSTR_LEN_ (PAGE),
    402                                                 (const void *) PAGE);
    403       if (NULL == response)
    404         mhdErrorExitDesc ("Response creation failed");
    405       ret = MHD_queue_response (connection, MHD_HTTP_OK, response);
    406       if (MHD_YES != ret)
    407         mhdErrorExitDesc ("'MHD_queue_response()' failed");
    408     }
    409     else
    410     {
    411       if (NULL != password)
    412         mhdErrorExitDesc ("The password pointer is NOT NULL");
    413       response =
    414         MHD_create_response_from_buffer_static (MHD_STATICSTR_LEN_ (DENIED),
    415                                                 (const void *) DENIED);
    416       if (NULL == response)
    417         mhdErrorExitDesc ("Response creation failed");
    418       ret = MHD_queue_basic_auth_fail_response (connection, REALM, response);
    419       if (MHD_YES != ret)
    420         mhdErrorExitDesc ("'MHD_queue_basic_auth_fail_response()' failed");
    421     }
    422     if (NULL != username)
    423       MHD_free (username);
    424     if (NULL != password)
    425       MHD_free (password);
    426   }
    427 
    428   MHD_destroy_response (response);
    429   return ret;
    430 }
    431 
    432 
    433 static CURL *
    434 setupCURL (void *cbc, uint16_t port, char *errbuf)
    435 {
    436   CURL *c;
    437   char url[512];
    438 
    439   if (1)
    440   {
    441     int res;
    442     /* A workaround for some old libcurl versions, which ignore the specified
    443      * port by CURLOPT_PORT when authorisation is used. */
    444     res = snprintf (url, (sizeof(url) / sizeof(url[0])),
    445                     "http://127.0.0.1:%u%s", (unsigned int) port,
    446                     MHD_URI_BASE_PATH);
    447     if ((0 >= res) || ((sizeof(url) / sizeof(url[0])) <= (size_t) res))
    448       externalErrorExitDesc ("Cannot form request URL");
    449   }
    450 
    451   c = curl_easy_init ();
    452   if (NULL == c)
    453     libcurlErrorExitDesc ("curl_easy_init() failed");
    454 
    455   if ((CURLE_OK != curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1L)) ||
    456       (CURLE_OK != curl_easy_setopt (c, CURLOPT_ERRORBUFFER,
    457                                      errbuf)) ||
    458       (CURLE_OK != curl_easy_setopt (c, CURLOPT_WRITEFUNCTION,
    459                                      &copyBuffer)) ||
    460       (CURLE_OK != curl_easy_setopt (c, CURLOPT_WRITEDATA, cbc)) ||
    461       (CURLE_OK != curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT,
    462                                      ((long) TIMEOUTS_VAL))) ||
    463       (CURLE_OK != curl_easy_setopt (c, CURLOPT_TIMEOUT,
    464                                      ((long) TIMEOUTS_VAL))) ||
    465       (CURLE_OK != curl_easy_setopt (c, CURLOPT_HTTP_VERSION,
    466                                      CURL_HTTP_VERSION_1_1)) ||
    467       /* (CURLE_OK != curl_easy_setopt (c, CURLOPT_VERBOSE, 1L)) || */
    468       (CURLE_OK != curl_easy_setopt (c, CURLOPT_FAILONERROR, 1L)) ||
    469 #if CURL_AT_LEAST_VERSION (7, 85, 0)
    470       (CURLE_OK != curl_easy_setopt (c, CURLOPT_PROTOCOLS_STR, "http")) ||
    471 #elif CURL_AT_LEAST_VERSION (7, 19, 4)
    472       (CURLE_OK != curl_easy_setopt (c, CURLOPT_PROTOCOLS, CURLPROTO_HTTP)) ||
    473 #endif /* CURL_AT_LEAST_VERSION (7, 19, 4) */
    474 #if CURL_AT_LEAST_VERSION (7, 45, 0)
    475       (CURLE_OK != curl_easy_setopt (c, CURLOPT_DEFAULT_PROTOCOL, "http")) ||
    476 #endif /* CURL_AT_LEAST_VERSION (7, 45, 0) */
    477       (CURLE_OK != curl_easy_setopt (c, CURLOPT_PORT, ((long) port))) ||
    478       (CURLE_OK != curl_easy_setopt (c, CURLOPT_URL, url)))
    479     libcurlErrorExitDesc ("curl_easy_setopt() failed");
    480 #if CURL_AT_LEAST_VERSION (7,21,3)
    481   if ((CURLE_OK != curl_easy_setopt (c, CURLOPT_HTTPAUTH,
    482                                      CURLAUTH_BASIC
    483                                      | (preauth ? 0 : CURLAUTH_ONLY))) ||
    484       (CURLE_OK != curl_easy_setopt (c, CURLOPT_USERPWD,
    485                                      USERNAME ":" PASSWORD)))
    486     libcurlErrorExitDesc ("curl_easy_setopt() authorization options failed");
    487 #else  /* libcurl version before 7.21.3 */
    488   if ((CURLE_OK != curl_easy_setopt (c, CURLOPT_HTTPAUTH, CURLAUTH_BASIC)) ||
    489       (CURLE_OK != curl_easy_setopt (c, CURLOPT_USERPWD,
    490                                      USERNAME ":" PASSWORD)))
    491     libcurlErrorExitDesc ("curl_easy_setopt() authorization options failed");
    492 #endif /* libcurl version before 7.21.3 */
    493   return c;
    494 }
    495 
    496 
    497 static CURLcode
    498 performQueryExternal (struct MHD_Daemon *d, CURL *c)
    499 {
    500   CURLM *multi;
    501   time_t start;
    502   struct timeval tv;
    503   CURLcode ret;
    504 
    505   ret = CURLE_FAILED_INIT; /* will be replaced with real result */
    506   multi = NULL;
    507   multi = curl_multi_init ();
    508   if (multi == NULL)
    509     libcurlErrorExitDesc ("curl_multi_init() failed");
    510   if (CURLM_OK != curl_multi_add_handle (multi, c))
    511     libcurlErrorExitDesc ("curl_multi_add_handle() failed");
    512 
    513   start = time (NULL);
    514   while (time (NULL) - start <= TIMEOUTS_VAL)
    515   {
    516     fd_set rs;
    517     fd_set ws;
    518     fd_set es;
    519     MHD_socket maxMhdSk;
    520     int maxCurlSk;
    521     int running;
    522 
    523     maxMhdSk = MHD_INVALID_SOCKET;
    524     maxCurlSk = -1;
    525     FD_ZERO (&rs);
    526     FD_ZERO (&ws);
    527     FD_ZERO (&es);
    528     if (NULL != multi)
    529     {
    530       curl_multi_perform (multi, &running);
    531       if (0 == running)
    532       {
    533         struct CURLMsg *msg;
    534         int msgLeft;
    535         int totalMsgs = 0;
    536         do
    537         {
    538           msg = curl_multi_info_read (multi, &msgLeft);
    539           if (NULL == msg)
    540             libcurlErrorExitDesc ("curl_multi_info_read() failed");
    541           totalMsgs++;
    542           if (CURLMSG_DONE == msg->msg)
    543             ret = msg->data.result;
    544         } while (msgLeft > 0);
    545         if (1 != totalMsgs)
    546         {
    547           fprintf (stderr,
    548                    "curl_multi_info_read returned wrong "
    549                    "number of results (%d).\n",
    550                    totalMsgs);
    551           externalErrorExit ();
    552         }
    553         curl_multi_remove_handle (multi, c);
    554         curl_multi_cleanup (multi);
    555         multi = NULL;
    556       }
    557       else
    558       {
    559         if (CURLM_OK != curl_multi_fdset (multi, &rs, &ws, &es, &maxCurlSk))
    560           libcurlErrorExitDesc ("curl_multi_fdset() failed");
    561       }
    562     }
    563     if (NULL == multi)
    564     { /* libcurl has finished, check whether MHD still needs to perform cleanup */
    565       if (0 != MHD_get_timeout64s (d))
    566         break; /* MHD finished as well */
    567     }
    568     if (MHD_YES != MHD_get_fdset (d, &rs, &ws, &es, &maxMhdSk))
    569       mhdErrorExitDesc ("MHD_get_fdset() failed");
    570     tv.tv_sec = 0;
    571     tv.tv_usec = 200000;
    572 #ifdef MHD_POSIX_SOCKETS
    573     if (maxMhdSk > maxCurlSk)
    574       maxCurlSk = maxMhdSk;
    575 #endif /* MHD_POSIX_SOCKETS */
    576     if (-1 == select (maxCurlSk + 1, &rs, &ws, &es, &tv))
    577     {
    578 #ifdef MHD_POSIX_SOCKETS
    579       if (EINTR != errno)
    580         externalErrorExitDesc ("Unexpected select() error");
    581 #else
    582       if ((WSAEINVAL != WSAGetLastError ()) ||
    583           (0 != rs.fd_count) || (0 != ws.fd_count) || (0 != es.fd_count) )
    584         externalErrorExitDesc ("Unexpected select() error");
    585       Sleep (200);
    586 #endif
    587     }
    588     if (MHD_YES != MHD_run_from_select (d, &rs, &ws, &es))
    589       mhdErrorExitDesc ("MHD_run_from_select() failed");
    590   }
    591 
    592   return ret;
    593 }
    594 
    595 
    596 /**
    597  * Check request result
    598  * @param curl_code the CURL easy return code
    599  * @param pcbc the pointer struct CBC
    600  * @return non-zero if success, zero if failed
    601  */
    602 static unsigned int
    603 check_result (CURLcode curl_code, struct CBC *pcbc)
    604 {
    605   if (CURLE_OK != curl_code)
    606   {
    607     fflush (stdout);
    608     if (0 != libcurl_errbuf[0])
    609       fprintf (stderr, "First request failed. "
    610                "libcurl error: '%s'.\n"
    611                "libcurl error description: '%s'.\n",
    612                curl_easy_strerror (curl_code),
    613                libcurl_errbuf);
    614     else
    615       fprintf (stderr, "First request failed. "
    616                "libcurl error: '%s'.\n",
    617                curl_easy_strerror (curl_code));
    618     fflush (stderr);
    619     return 0;
    620   }
    621 
    622   if (pcbc->pos != strlen (PAGE))
    623   {
    624     fprintf (stderr, "Got %u bytes ('%.*s'), expected %u bytes. ",
    625              (unsigned) pcbc->pos, (int) pcbc->pos, pcbc->buf,
    626              (unsigned) strlen (PAGE));
    627     mhdErrorExitDesc ("Wrong returned data length");
    628   }
    629   if (0 != memcmp (PAGE, pcbc->buf, pcbc->pos))
    630   {
    631     fprintf (stderr, "Got invalid response '%.*s'. ",
    632              (int) pcbc->pos, pcbc->buf);
    633     mhdErrorExitDesc ("Wrong returned data");
    634   }
    635   return 1;
    636 }
    637 
    638 
    639 static unsigned int
    640 testBasicAuth (void)
    641 {
    642   struct MHD_Daemon *d;
    643   uint16_t port;
    644   struct CBC cbc;
    645   char buf[2048];
    646   CURL *c;
    647   int failed = 0;
    648 
    649   if (MHD_NO != MHD_is_feature_supported (MHD_FEATURE_AUTODETECT_BIND_PORT))
    650     port = 0;
    651   else
    652     port = 4210;
    653 
    654   d = MHD_start_daemon (MHD_USE_ERROR_LOG | MHD_USE_NO_THREAD_SAFETY,
    655                         port, NULL, NULL,
    656                         &ahc_echo, NULL,
    657                         MHD_OPTION_APP_FD_SETSIZE, (int) FD_SETSIZE,
    658                         MHD_OPTION_END);
    659   if (d == NULL)
    660     return 1;
    661   if (0 == port)
    662   {
    663     const union MHD_DaemonInfo *dinfo;
    664 
    665     dinfo = MHD_get_daemon_info (d,
    666                                  MHD_DAEMON_INFO_BIND_PORT);
    667     if ( (NULL == dinfo) ||
    668          (0 == dinfo->port) )
    669       mhdErrorExitDesc ("MHD_get_daemon_info() failed");
    670     port = (uint16_t) dinfo->port;
    671   }
    672 
    673   /* First request */
    674   cbc.buf = buf;
    675   cbc.size = sizeof (buf);
    676   cbc.pos = 0;
    677   memset (cbc.buf, 0, cbc.size);
    678   c = setupCURL (&cbc, port, libcurl_errbuf);
    679   if (check_result (performQueryExternal (d, c), &cbc))
    680   {
    681     if (verbose)
    682       printf ("First request successful.\n");
    683   }
    684   else
    685   {
    686     fprintf (stderr, "First request FAILED.\n");
    687     failed = 1;
    688   }
    689   curl_easy_cleanup (c);
    690 
    691   /* Second request */
    692   cbc.buf = buf;
    693   cbc.size = sizeof (buf);
    694   cbc.pos = 0;
    695   memset (cbc.buf, 0, cbc.size);
    696   c = setupCURL (&cbc, port, libcurl_errbuf);
    697   if (check_result (performQueryExternal (d, c), &cbc))
    698   {
    699     if (verbose)
    700       printf ("Second request successful.\n");
    701   }
    702   else
    703   {
    704     fprintf (stderr, "Second request FAILED.\n");
    705     failed = 1;
    706   }
    707   curl_easy_cleanup (c);
    708   MHD_stop_daemon (d);
    709   return failed ? 1 : 0;
    710 }
    711 
    712 
    713 int
    714 main (int argc, char *const *argv)
    715 {
    716   unsigned int errorCount = 0;
    717   (void) argc; (void) argv; /* Unused. Silent compiler warning. */
    718 
    719   verbose = ! (has_param (argc, argv, "-q") ||
    720                has_param (argc, argv, "--quiet") ||
    721                has_param (argc, argv, "-s") ||
    722                has_param (argc, argv, "--silent"));
    723   preauth = has_in_name (argv[0], "_preauth");
    724 #if ! CURL_AT_LEAST_VERSION (7,21,3)
    725   if (preauth)
    726   {
    727     fprintf (stderr, "libcurl version 7.21.3 or later is "
    728              "required to run this test.\n");
    729     return 77;
    730   }
    731 #endif /* libcurl version before 7.21.3 */
    732 
    733 #ifdef MHD_HTTPS_REQUIRE_GCRYPT
    734 #ifdef HAVE_GCRYPT_H
    735   gcry_control (GCRYCTL_ENABLE_QUICK_RANDOM, 0);
    736 #ifdef GCRYCTL_INITIALIZATION_FINISHED
    737   gcry_control (GCRYCTL_INITIALIZATION_FINISHED, 0);
    738 #endif
    739 #endif
    740 #endif /* MHD_HTTPS_REQUIRE_GCRYPT */
    741   oldapi = has_in_name (argv[0], "_oldapi");
    742   if (0 != curl_global_init (CURL_GLOBAL_WIN32))
    743     return 2;
    744   errorCount += testBasicAuth ();
    745   if (errorCount != 0)
    746     fprintf (stderr, "Error (code: %u)\n", errorCount);
    747   curl_global_cleanup ();
    748   return (0 == errorCount) ? 0 : 1;       /* 0 == pass */
    749 }