libmicrohttpd

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

test_basicauth.c (23825B)


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