libmicrohttpd

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

test_digestauth_concurrent.c (22386B)


      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_digestauth_concurrent.c
     24  * @brief  Testcase for libmicrohttpd concurrent Digest Authorisation
     25  * @author Amr Ali
     26  * @author Karlson2k (Evgeny Grin)
     27  */
     28 
     29 #include "mhd_options.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 #if defined(MHD_HTTPS_REQUIRE_GCRYPT) && \
     37   (defined(MHD_SHA256_TLSLIB) || defined(MHD_MD5_TLSLIB))
     38 #define NEED_GCRYP_INIT 1
     39 #include <gcrypt.h>
     40 #endif /* MHD_HTTPS_REQUIRE_GCRYPT && (MHD_SHA256_TLSLIB || MHD_MD5_TLSLIB) */
     41 #include <errno.h>
     42 
     43 #ifndef WINDOWS
     44 #include <sys/socket.h>
     45 #include <unistd.h>
     46 #else
     47 #include <wincrypt.h>
     48 #endif
     49 
     50 #include <pthread.h>
     51 
     52 #include "mhd_has_param.h"
     53 
     54 #ifndef CURL_VERSION_BITS
     55 #define CURL_VERSION_BITS(x,y,z) ((x) << 16 | (y) << 8 | (z))
     56 #endif /* ! CURL_VERSION_BITS */
     57 #ifndef CURL_AT_LEAST_VERSION
     58 #define CURL_AT_LEAST_VERSION(x,y,z) \
     59   (LIBCURL_VERSION_NUM >= CURL_VERSION_BITS (x, y, z))
     60 #endif /* ! CURL_AT_LEAST_VERSION */
     61 
     62 #ifndef _MHD_INSTRMACRO
     63 /* Quoted macro parameter */
     64 #define _MHD_INSTRMACRO(a) #a
     65 #endif /* ! _MHD_INSTRMACRO */
     66 #ifndef _MHD_STRMACRO
     67 /* Quoted expanded macro parameter */
     68 #define _MHD_STRMACRO(a) _MHD_INSTRMACRO (a)
     69 #endif /* ! _MHD_STRMACRO */
     70 
     71 #if defined(HAVE___FUNC__)
     72 #define externalErrorExit(ignore) \
     73   _externalErrorExit_func (NULL, __func__, __LINE__)
     74 #define externalErrorExitDesc(errDesc) \
     75   _externalErrorExit_func (errDesc, __func__, __LINE__)
     76 #define libcurlErrorExit(ignore) \
     77   _libcurlErrorExit_func (NULL, __func__, __LINE__)
     78 #define libcurlErrorExitDesc(errDesc) \
     79   _libcurlErrorExit_func (errDesc, __func__, __LINE__)
     80 #define mhdErrorExit(ignore) \
     81   _mhdErrorExit_func (NULL, __func__, __LINE__)
     82 #define mhdErrorExitDesc(errDesc) \
     83   _mhdErrorExit_func (errDesc, __func__, __LINE__)
     84 #define checkCURLE_OK(libcurlcall) \
     85   _checkCURLE_OK_func ((libcurlcall), _MHD_STRMACRO (libcurlcall), \
     86                        __func__, __LINE__)
     87 #elif defined(HAVE___FUNCTION__)
     88 #define externalErrorExit(ignore) \
     89   _externalErrorExit_func (NULL, __FUNCTION__, __LINE__)
     90 #define externalErrorExitDesc(errDesc) \
     91   _externalErrorExit_func (errDesc, __FUNCTION__, __LINE__)
     92 #define libcurlErrorExit(ignore) \
     93   _libcurlErrorExit_func (NULL, __FUNCTION__, __LINE__)
     94 #define libcurlErrorExitDesc(errDesc) \
     95   _libcurlErrorExit_func (errDesc, __FUNCTION__, __LINE__)
     96 #define mhdErrorExit(ignore) \
     97   _mhdErrorExit_func (NULL, __FUNCTION__, __LINE__)
     98 #define mhdErrorExitDesc(errDesc) \
     99   _mhdErrorExit_func (errDesc, __FUNCTION__, __LINE__)
    100 #define checkCURLE_OK(libcurlcall) \
    101   _checkCURLE_OK_func ((libcurlcall), _MHD_STRMACRO (libcurlcall), \
    102                        __FUNCTION__, __LINE__)
    103 #else
    104 #define externalErrorExit(ignore) _externalErrorExit_func (NULL, NULL, __LINE__)
    105 #define externalErrorExitDesc(errDesc) \
    106   _externalErrorExit_func (errDesc, NULL, __LINE__)
    107 #define libcurlErrorExit(ignore) _libcurlErrorExit_func (NULL, NULL, __LINE__)
    108 #define libcurlErrorExitDesc(errDesc) \
    109   _libcurlErrorExit_func (errDesc, NULL, __LINE__)
    110 #define mhdErrorExit(ignore) _mhdErrorExit_func (NULL, NULL, __LINE__)
    111 #define mhdErrorExitDesc(errDesc) _mhdErrorExit_func (errDesc, NULL, __LINE__)
    112 #define checkCURLE_OK(libcurlcall) \
    113   _checkCURLE_OK_func ((libcurlcall), _MHD_STRMACRO (libcurlcall), NULL, \
    114                        __LINE__)
    115 #endif
    116 
    117 
    118 _MHD_NORETURN static void
    119 _externalErrorExit_func (const char *errDesc, const char *funcName, int lineNum)
    120 {
    121   fflush (stdout);
    122   if ((NULL != errDesc) && (0 != errDesc[0]))
    123     fprintf (stderr, "%s", errDesc);
    124   else
    125     fprintf (stderr, "System or external library call failed");
    126   if ((NULL != funcName) && (0 != funcName[0]))
    127     fprintf (stderr, " in %s", funcName);
    128   if (0 < lineNum)
    129     fprintf (stderr, " at line %d", lineNum);
    130 
    131   fprintf (stderr, ".\nLast errno value: %d (%s)\n", (int) errno,
    132            strerror (errno));
    133 #ifdef MHD_WINSOCK_SOCKETS
    134   fprintf (stderr, "WSAGetLastError() value: %d\n", (int) WSAGetLastError ());
    135 #endif /* MHD_WINSOCK_SOCKETS */
    136   fflush (stderr);
    137   exit (99);
    138 }
    139 
    140 
    141 /* 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 5
    230 
    231 #define MHD_URI_BASE_PATH "/bar%20foo?key=value"
    232 
    233 #define PAGE \
    234   "<html><head><title>libmicrohttpd demo</title></head><body>Access granted</body></html>"
    235 
    236 #define DENIED \
    237   "<html><head><title>libmicrohttpd demo</title></head><body>Access denied</body></html>"
    238 
    239 #define MY_OPAQUE "11733b200778ce33060f31c9af70a870ba96ddd4"
    240 
    241 struct CBC
    242 {
    243   char *buf;
    244   size_t pos;
    245   size_t size;
    246 };
    247 
    248 static int verbose;
    249 
    250 static size_t
    251 copyBuffer (void *ptr,
    252             size_t size,
    253             size_t nmemb,
    254             void *ctx)
    255 {
    256   struct CBC *cbc = ctx;
    257 
    258   if (cbc->pos + size * nmemb > cbc->size)
    259     mhdErrorExitDesc ("Wrong too large data");       /* overflow */
    260   memcpy (&cbc->buf[cbc->pos], ptr, size * nmemb);
    261   cbc->pos += size * nmemb;
    262   return size * nmemb;
    263 }
    264 
    265 
    266 static enum MHD_Result
    267 ahc_echo (void *cls,
    268           struct MHD_Connection *connection,
    269           const char *url,
    270           const char *method,
    271           const char *version,
    272           const char *upload_data,
    273           size_t *upload_data_size,
    274           void **req_cls)
    275 {
    276   struct MHD_Response *response;
    277   char *username;
    278   const char *password = "testpass";
    279   const char *realm = "test@example.com";
    280   enum MHD_Result ret;
    281   enum MHD_DigestAuthResult ret_e;
    282   static int already_called_marker;
    283   (void) cls; (void) url;                         /* Unused. Silent compiler warning. */
    284   (void) method; (void) version; (void) upload_data; /* Unused. Silent compiler warning. */
    285   (void) upload_data_size; (void) req_cls;        /* Unused. Silent compiler warning. */
    286 
    287   if (&already_called_marker != *req_cls)
    288   { /* Called for the first time, request not fully read yet */
    289     *req_cls = &already_called_marker;
    290     /* Wait for complete request */
    291     return MHD_YES;
    292   }
    293 
    294   username = MHD_digest_auth_get_username (connection);
    295   if ( (username == NULL) ||
    296        (0 != strcmp (username, "testuser")) )
    297   {
    298     response = MHD_create_response_from_buffer_static (strlen (DENIED),
    299                                                        DENIED);
    300     if (NULL == response)
    301       mhdErrorExitDesc ("MHD_create_response_from_buffer failed");
    302     ret = MHD_queue_auth_fail_response2 (connection,
    303                                          realm,
    304                                          MY_OPAQUE,
    305                                          response,
    306                                          MHD_NO,
    307                                          MHD_DIGEST_ALG_MD5);
    308     if (MHD_YES != ret)
    309       mhdErrorExitDesc ("MHD_queue_auth_fail_response2 failed");
    310     MHD_destroy_response (response);
    311     return ret;
    312   }
    313   ret_e = MHD_digest_auth_check3 (connection,
    314                                   realm,
    315                                   username,
    316                                   password,
    317                                   50 * TIMEOUTS_VAL,
    318                                   0, MHD_DIGEST_AUTH_MULT_QOP_AUTH,
    319                                   MHD_DIGEST_AUTH_MULT_ALGO3_MD5);
    320   MHD_free (username);
    321   if (ret_e != MHD_DAUTH_OK)
    322   {
    323     response = MHD_create_response_from_buffer_static (strlen (DENIED),
    324                                                        DENIED);
    325     if (NULL == response)
    326       mhdErrorExitDesc ("MHD_create_response_from_buffer() failed");
    327     ret = MHD_queue_auth_fail_response2 (connection,
    328                                          realm,
    329                                          MY_OPAQUE,
    330                                          response,
    331                                          (MHD_DAUTH_NONCE_STALE == ret_e) ?
    332                                          MHD_YES : MHD_NO,
    333                                          MHD_DIGEST_ALG_MD5);
    334     if (MHD_YES != ret)
    335       mhdErrorExitDesc ("MHD_queue_auth_fail_response2() failed");
    336     MHD_destroy_response (response);
    337     return ret;
    338   }
    339   response = MHD_create_response_from_buffer_static (strlen (PAGE),
    340                                                      PAGE);
    341   if (NULL == response)
    342     mhdErrorExitDesc ("MHD_create_response_from_buffer() failed");
    343   ret = MHD_queue_response (connection,
    344                             MHD_HTTP_OK,
    345                             response);
    346   if (MHD_YES != ret)
    347     mhdErrorExitDesc ("MHD_queue_auth_fail_response2() failed");
    348   MHD_destroy_response (response);
    349   return ret;
    350 }
    351 
    352 
    353 static CURL *
    354 setupCURL (void *cbc, uint16_t port, char *errbuf)
    355 {
    356   CURL *c;
    357   char url[512];
    358 
    359   if (1)
    360   {
    361     int res;
    362     /* A workaround for some old libcurl versions, which ignore the specified
    363      * port by CURLOPT_PORT when digest authorisation is used. */
    364     res = snprintf (url, (sizeof(url) / sizeof(url[0])),
    365                     "http://127.0.0.1:%u%s", (unsigned int) port,
    366                     MHD_URI_BASE_PATH);
    367     if ((0 >= res) || ((sizeof(url) / sizeof(url[0])) <= (size_t) res))
    368       externalErrorExitDesc ("Cannot form request URL");
    369   }
    370 
    371   c = curl_easy_init ();
    372   if (NULL == c)
    373     libcurlErrorExitDesc ("curl_easy_init() failed");
    374 
    375   if ((CURLE_OK != curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1L)) ||
    376       (CURLE_OK != curl_easy_setopt (c, CURLOPT_ERRORBUFFER,
    377                                      errbuf)) ||
    378       (CURLE_OK != curl_easy_setopt (c, CURLOPT_WRITEFUNCTION,
    379                                      &copyBuffer)) ||
    380       (CURLE_OK != curl_easy_setopt (c, CURLOPT_WRITEDATA, cbc)) ||
    381       (CURLE_OK != curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT,
    382                                      ((long) TIMEOUTS_VAL))) ||
    383       (CURLE_OK != curl_easy_setopt (c, CURLOPT_TIMEOUT,
    384                                      ((long) TIMEOUTS_VAL))) ||
    385       (CURLE_OK != curl_easy_setopt (c, CURLOPT_HTTP_VERSION,
    386                                      CURL_HTTP_VERSION_1_1)) ||
    387       (CURLE_OK != curl_easy_setopt (c, CURLOPT_FAILONERROR, 1L)) ||
    388 #ifdef _DEBUG
    389       (CURLE_OK != curl_easy_setopt (c, CURLOPT_VERBOSE, 1L)) ||
    390 #endif /* _DEBUG */
    391 #if CURL_AT_LEAST_VERSION (7, 85, 0)
    392       (CURLE_OK != curl_easy_setopt (c, CURLOPT_PROTOCOLS_STR, "http")) ||
    393 #elif CURL_AT_LEAST_VERSION (7, 19, 4)
    394       (CURLE_OK != curl_easy_setopt (c, CURLOPT_PROTOCOLS, CURLPROTO_HTTP)) ||
    395 #endif /* CURL_AT_LEAST_VERSION (7, 19, 4) */
    396 #if CURL_AT_LEAST_VERSION (7, 45, 0)
    397       (CURLE_OK != curl_easy_setopt (c, CURLOPT_DEFAULT_PROTOCOL, "http")) ||
    398 #endif /* CURL_AT_LEAST_VERSION (7, 45, 0) */
    399       (CURLE_OK != curl_easy_setopt (c, CURLOPT_PORT, ((long) port))) ||
    400       (CURLE_OK != curl_easy_setopt (c, CURLOPT_URL, url)))
    401     libcurlErrorExitDesc ("curl_easy_setopt() failed");
    402   if ((CURLE_OK != curl_easy_setopt (c, CURLOPT_HTTPAUTH, CURLAUTH_DIGEST)) ||
    403       (CURLE_OK != curl_easy_setopt (c, CURLOPT_USERPWD,
    404                                      "testuser:testpass")))
    405     libcurlErrorExitDesc ("curl_easy_setopt() authorization options failed");
    406   return c;
    407 }
    408 
    409 
    410 static void
    411 getRnd (void *buf, size_t size)
    412 {
    413 #ifndef WINDOWS
    414   int fd;
    415   size_t off = 0;
    416 
    417   fd = open ("/dev/urandom",
    418              O_RDONLY);
    419   if (-1 == fd)
    420     externalErrorExitDesc ("Failed to open '/dev/urandom'");
    421 
    422   do
    423   {
    424     ssize_t res;
    425     res = read (fd, ((uint8_t *) buf) + off, size - off);
    426     if (0 > res)
    427       externalErrorExitDesc ("Failed to read '/dev/urandom'");
    428     off += (size_t) res;
    429   } while (off < size);
    430   (void) close (fd);
    431 #else
    432   HCRYPTPROV cc;
    433   BOOL b;
    434 
    435   b = CryptAcquireContext (&cc,
    436                            NULL,
    437                            NULL,
    438                            PROV_RSA_FULL,
    439                            CRYPT_VERIFYCONTEXT | CRYPT_SILENT);
    440   if (b == 0)
    441     externalErrorExitDesc ("CryptAcquireContext() failed");
    442   b = CryptGenRandom (cc, (DWORD) size, (BYTE *) buf);
    443   if (b == 0)
    444     externalErrorExitDesc ("CryptGenRandom() failed");
    445   CryptReleaseContext (cc, 0);
    446 #endif /* ! WINDOWS */
    447 }
    448 
    449 
    450 struct curlWokerInfo
    451 {
    452   int workerNumber;
    453   struct CBC cbc;
    454   pthread_t tid;
    455   /**
    456    * The libcurl handle to run in thread
    457    */
    458   CURL *c;
    459   char *libcurl_errbuf;
    460   /**
    461    * Non-zero if worker is finished
    462    */
    463   volatile int finished;
    464   /**
    465    * The number of successful worker results
    466    */
    467   volatile unsigned int success;
    468 };
    469 
    470 
    471 static void *
    472 worker_func (void *param)
    473 {
    474   struct curlWokerInfo *const w = (struct curlWokerInfo *) param;
    475   CURLcode req_result;
    476   if (NULL == w)
    477     externalErrorExit ();
    478 
    479   req_result = curl_easy_perform (w->c);
    480   if (CURLE_OK != req_result)
    481   {
    482     if (0 != w->libcurl_errbuf[0])
    483       fprintf (stderr, "Worker %d: first request failed. "
    484                "libcurl error: '%s'.\n"
    485                "libcurl error description: '%s'.\n",
    486                w->workerNumber, curl_easy_strerror (req_result),
    487                w->libcurl_errbuf);
    488     else
    489       fprintf (stderr, "Worker %d: first request failed. "
    490                "libcurl error: '%s'.\n",
    491                w->workerNumber, curl_easy_strerror (req_result));
    492   }
    493   else
    494   {
    495     if (w->cbc.pos != strlen (PAGE))
    496     {
    497       fprintf (stderr, "Worker %d: Got %u bytes ('%.*s'), expected %u bytes. ",
    498                w->workerNumber,
    499                (unsigned) w->cbc.pos, (int) w->cbc.pos, w->cbc.buf,
    500                (unsigned) strlen (MHD_URI_BASE_PATH));
    501       mhdErrorExitDesc ("Wrong returned data length");
    502     }
    503     if (0 != strncmp (PAGE, w->cbc.buf, strlen (PAGE)))
    504     {
    505       fprintf (stderr, "Worker %d: Got invalid response '%.*s'. ",
    506                w->workerNumber,
    507                (int) w->cbc.pos, w->cbc.buf);
    508       mhdErrorExitDesc ("Wrong returned data");
    509     }
    510     if (verbose)
    511       printf ("Worker %d: first request successful.\n", w->workerNumber);
    512     w->success++;
    513   }
    514 #ifdef _DEBUG
    515   fflush (stderr);
    516   fflush (stdout);
    517 #endif /* _DEBUG */
    518 
    519   /* Second request */
    520   w->cbc.pos = 0;
    521   req_result = curl_easy_perform (w->c);
    522   if (CURLE_OK != req_result)
    523   {
    524     if (0 != w->libcurl_errbuf[0])
    525       fprintf (stderr, "Worker %d: second request failed. "
    526                "libcurl error: '%s'.\n"
    527                "libcurl error description: '%s'.\n",
    528                w->workerNumber, curl_easy_strerror (req_result),
    529                w->libcurl_errbuf);
    530     else
    531       fprintf (stderr, "Worker %d: second request failed. "
    532                "libcurl error: '%s'.\n",
    533                w->workerNumber, curl_easy_strerror (req_result));
    534   }
    535   else
    536   {
    537     if (w->cbc.pos != strlen (PAGE))
    538     {
    539       fprintf (stderr, "Worker %d: Got %u bytes ('%.*s'), expected %u bytes. ",
    540                w->workerNumber,
    541                (unsigned) w->cbc.pos, (int) w->cbc.pos, w->cbc.buf,
    542                (unsigned) strlen (MHD_URI_BASE_PATH));
    543       mhdErrorExitDesc ("Wrong returned data length");
    544     }
    545     if (0 != strncmp (PAGE, w->cbc.buf, strlen (PAGE)))
    546     {
    547       fprintf (stderr, "Worker %d: Got invalid response '%.*s'. ",
    548                w->workerNumber,
    549                (int) w->cbc.pos, w->cbc.buf);
    550       mhdErrorExitDesc ("Wrong returned data");
    551     }
    552     if (verbose)
    553       printf ("Worker %d: second request successful.\n", w->workerNumber);
    554     w->success++;
    555   }
    556 #ifdef _DEBUG
    557   fflush (stderr);
    558   fflush (stdout);
    559 #endif /* _DEBUG */
    560 
    561   w->finished = ! 0;
    562   return NULL;
    563 }
    564 
    565 
    566 #define CLIENT_BUF_SIZE 2048
    567 
    568 static unsigned int
    569 testDigestAuth (void)
    570 {
    571   struct MHD_Daemon *d;
    572   char rnd[8];
    573   uint16_t port;
    574   size_t i;
    575   /* Run three workers in parallel so at least two workers would start within
    576    * the same monotonic clock second.*/
    577   struct curlWokerInfo workers[3];
    578   unsigned int ret;
    579 
    580   if (MHD_NO != MHD_is_feature_supported (MHD_FEATURE_AUTODETECT_BIND_PORT))
    581     port = 0;
    582   else
    583     port = 4200;
    584 
    585   getRnd (rnd, sizeof(rnd));
    586 
    587   d = MHD_start_daemon (MHD_USE_INTERNAL_POLLING_THREAD | MHD_USE_ERROR_LOG,
    588                         port, NULL, NULL,
    589                         &ahc_echo, NULL,
    590                         MHD_OPTION_DIGEST_AUTH_RANDOM, sizeof (rnd), rnd,
    591                         MHD_OPTION_NONCE_NC_SIZE, 300,
    592                         MHD_OPTION_THREAD_POOL_SIZE,
    593                         (unsigned int) (sizeof(workers) / sizeof(workers[0])),
    594                         MHD_OPTION_DIGEST_AUTH_DEFAULT_MAX_NC, (uint32_t) 999,
    595                         MHD_OPTION_END);
    596   if (d == NULL)
    597     return 1;
    598   if (0 == port)
    599   {
    600     const union MHD_DaemonInfo *dinfo;
    601 
    602     dinfo = MHD_get_daemon_info (d,
    603                                  MHD_DAEMON_INFO_BIND_PORT);
    604     if ( (NULL == dinfo) ||
    605          (0 == dinfo->port) )
    606       mhdErrorExitDesc ("MHD_get_daemon_info() failed");
    607     port = dinfo->port;
    608   }
    609 
    610   /* Initialise all workers */
    611   for (i = 0; i < sizeof(workers) / sizeof(workers[0]); i++)
    612   {
    613     struct curlWokerInfo *const w = workers + i;
    614     w->workerNumber = (int) i + 1; /* Use 1-based numbering */
    615     w->cbc.buf = malloc (CLIENT_BUF_SIZE);
    616     if (NULL == w->cbc.buf)
    617       externalErrorExitDesc ("malloc() failed");
    618     w->cbc.size = CLIENT_BUF_SIZE;
    619     w->cbc.pos = 0;
    620     w->libcurl_errbuf = malloc (CURL_ERROR_SIZE);
    621     if (NULL == w->libcurl_errbuf)
    622       externalErrorExitDesc ("malloc() failed");
    623     w->libcurl_errbuf[0] = 0;
    624     w->c = setupCURL (&w->cbc, port, w->libcurl_errbuf);
    625     w->finished = 0;
    626     w->success = 0;
    627   }
    628 
    629   /* Fire already initialised workers */
    630   for (i = 0; i < sizeof(workers) / sizeof(workers[0]); i++)
    631   {
    632     struct curlWokerInfo *const w = workers + i;
    633     if (0 != pthread_create (&w->tid, NULL, &worker_func, w))
    634       externalErrorExitDesc ("pthread_create() failed");
    635   }
    636 
    637   /* Collect results, cleanup workers */
    638   ret = 0;
    639   for (i = 0; i < sizeof(workers) / sizeof(workers[0]); i++)
    640   {
    641     struct curlWokerInfo *const w = workers + i;
    642     if (0 != pthread_join (w->tid, NULL))
    643       externalErrorExitDesc ("pthread_join() failed");
    644     curl_easy_cleanup (w->c);
    645     free (w->libcurl_errbuf);
    646     free (w->cbc.buf);
    647     if (! w->finished)
    648       externalErrorExitDesc ("The worker thread did't signal 'finished' state");
    649     ret += 2 - w->success;
    650   }
    651 
    652   MHD_stop_daemon (d);
    653   return ret;
    654 }
    655 
    656 
    657 int
    658 main (int argc, char *const *argv)
    659 {
    660   unsigned int errorCount = 0;
    661   (void) argc; (void) argv; /* Unused. Silent compiler warning. */
    662 #if (LIBCURL_VERSION_MAJOR == 7) && (LIBCURL_VERSION_MINOR == 62)
    663   if (1)
    664   {
    665     fprintf (stderr, "libcurl version 7.62.x has bug in processing "
    666              "URI with GET arguments for Digest Auth.\n");
    667     fprintf (stderr, "This test cannot be performed.\n");
    668     exit (77);
    669   }
    670 #endif /* libcurl version 7.62.x */
    671 
    672   verbose = ! (has_param (argc, argv, "-q") ||
    673                has_param (argc, argv, "--quiet") ||
    674                has_param (argc, argv, "-s") ||
    675                has_param (argc, argv, "--silent"));
    676 
    677 #ifdef NEED_GCRYP_INIT
    678   gcry_control (GCRYCTL_ENABLE_QUICK_RANDOM, 0);
    679 #ifdef GCRYCTL_INITIALIZATION_FINISHED
    680   gcry_control (GCRYCTL_INITIALIZATION_FINISHED, 0);
    681 #endif /* GCRYCTL_INITIALIZATION_FINISHED */
    682 #endif /* NEED_GCRYP_INIT */
    683   if (0 != curl_global_init (CURL_GLOBAL_WIN32))
    684     return 2;
    685   errorCount += testDigestAuth ();
    686   if (errorCount != 0)
    687     fprintf (stderr, "Error (code: %u)\n", errorCount);
    688   curl_global_cleanup ();
    689   return (0 == errorCount) ? 0 : 1;       /* 0 == pass */
    690 }