libmicrohttpd

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

test_quiesce.c (23999B)


      1 /*
      2      This file is part of libmicrohttpd
      3      Copyright (C) 2013, 2015 Christian Grothoff
      4      Copyright (C) 2014-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  * @file test_quiesce.c
     23  * @brief  Testcase for libmicrohttpd quiescing
     24  * @author Christian Grothoff
     25  * @author Karlson2k (Evgeny Grin)
     26  */
     27 
     28 #include "MHD_config.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 <sys/types.h>
     36 #include <pthread.h>
     37 #include <errno.h>
     38 #include "mhd_sockets.h" /* only macros used */
     39 #include "mhd_has_in_name.h"
     40 #include "mhd_has_param.h"
     41 
     42 /* Turn any MHD_PANIC() or failing mhd_assert() reached from this
     43    test into a marked, classifiable test error (TESTING.md, P5). */
     44 #include "mhd_panic_tripwire.h"
     45 
     46 
     47 #ifndef WINDOWS
     48 #include <unistd.h>
     49 #include <sys/socket.h>
     50 #endif
     51 
     52 #if defined(MHD_CPU_COUNT) && (MHD_CPU_COUNT + 0) < 2
     53 #undef MHD_CPU_COUNT
     54 #endif
     55 #if ! defined(MHD_CPU_COUNT)
     56 #define MHD_CPU_COUNT 2
     57 #endif
     58 
     59 
     60 #ifndef _MHD_INSTRMACRO
     61 /* Quoted macro parameter */
     62 #define _MHD_INSTRMACRO(a) #a
     63 #endif /* ! _MHD_INSTRMACRO */
     64 #ifndef _MHD_STRMACRO
     65 /* Quoted expanded macro parameter */
     66 #define _MHD_STRMACRO(a) _MHD_INSTRMACRO (a)
     67 #endif /* ! _MHD_STRMACRO */
     68 
     69 #if defined(HAVE___FUNC__)
     70 #define externalErrorExit(ignore) \
     71     _externalErrorExit_func(NULL, __func__, __LINE__)
     72 #define externalErrorExitDesc(errDesc) \
     73     _externalErrorExit_func(errDesc, __func__, __LINE__)
     74 #define libcurlErrorExit(ignore) \
     75     _libcurlErrorExit_func(NULL, __func__, __LINE__)
     76 #define libcurlErrorExitDesc(errDesc) \
     77     _libcurlErrorExit_func(errDesc, __func__, __LINE__)
     78 #define mhdErrorExit(ignore) \
     79     _mhdErrorExit_func(NULL, __func__, __LINE__)
     80 #define mhdErrorExitDesc(errDesc) \
     81     _mhdErrorExit_func(errDesc, __func__, __LINE__)
     82 #define checkCURLE_OK(libcurlcall) \
     83     _checkCURLE_OK_func((libcurlcall), _MHD_STRMACRO(libcurlcall), \
     84                         __func__, __LINE__)
     85 #elif defined(HAVE___FUNCTION__)
     86 #define externalErrorExit(ignore) \
     87     _externalErrorExit_func(NULL, __FUNCTION__, __LINE__)
     88 #define externalErrorExitDesc(errDesc) \
     89     _externalErrorExit_func(errDesc, __FUNCTION__, __LINE__)
     90 #define libcurlErrorExit(ignore) \
     91     _libcurlErrorExit_func(NULL, __FUNCTION__, __LINE__)
     92 #define libcurlErrorExitDesc(errDesc) \
     93     _libcurlErrorExit_func(errDesc, __FUNCTION__, __LINE__)
     94 #define mhdErrorExit(ignore) \
     95     _mhdErrorExit_func(NULL, __FUNCTION__, __LINE__)
     96 #define mhdErrorExitDesc(errDesc) \
     97     _mhdErrorExit_func(errDesc, __FUNCTION__, __LINE__)
     98 #define checkCURLE_OK(libcurlcall) \
     99     _checkCURLE_OK_func((libcurlcall), _MHD_STRMACRO(libcurlcall), \
    100                         __FUNCTION__, __LINE__)
    101 #else
    102 #define externalErrorExit(ignore) _externalErrorExit_func(NULL, NULL, __LINE__)
    103 #define externalErrorExitDesc(errDesc) \
    104   _externalErrorExit_func(errDesc, NULL, __LINE__)
    105 #define libcurlErrorExit(ignore) _libcurlErrorExit_func(NULL, NULL, __LINE__)
    106 #define libcurlErrorExitDesc(errDesc) \
    107   _libcurlErrorExit_func(errDesc, NULL, __LINE__)
    108 #define mhdErrorExit(ignore) _mhdErrorExit_func(NULL, NULL, __LINE__)
    109 #define mhdErrorExitDesc(errDesc) _mhdErrorExit_func(errDesc, NULL, __LINE__)
    110 #define checkCURLE_OK(libcurlcall) \
    111   _checkCURLE_OK_func((libcurlcall), _MHD_STRMACRO(libcurlcall), NULL, __LINE__)
    112 #endif
    113 
    114 
    115 _MHD_NORETURN static void
    116 _externalErrorExit_func (const char *errDesc, const char *funcName, int lineNum)
    117 {
    118   fflush (stdout);
    119   if ((NULL != errDesc) && (0 != errDesc[0]))
    120     fprintf (stderr, "%s", errDesc);
    121   else
    122     fprintf (stderr, "System or external library call failed");
    123   if ((NULL != funcName) && (0 != funcName[0]))
    124     fprintf (stderr, " in %s", funcName);
    125   if (0 < lineNum)
    126     fprintf (stderr, " at line %d", lineNum);
    127 
    128   fprintf (stderr, ".\nLast errno value: %d (%s)\n", (int) errno,
    129            strerror (errno));
    130 #ifdef MHD_WINSOCK_SOCKETS
    131   fprintf (stderr, "WSAGetLastError() value: %d\n", (int) WSAGetLastError ());
    132 #endif /* MHD_WINSOCK_SOCKETS */
    133   fflush (stderr);
    134   exit (99);
    135 }
    136 
    137 
    138 static char libcurl_errbuf[CURL_ERROR_SIZE] = "";
    139 
    140 _MHD_NORETURN static void
    141 _libcurlErrorExit_func (const char *errDesc, const char *funcName, int lineNum)
    142 {
    143   fflush (stdout);
    144   if ((NULL != errDesc) && (0 != errDesc[0]))
    145     fprintf (stderr, "%s", errDesc);
    146   else
    147     fprintf (stderr, "CURL library call failed");
    148   if ((NULL != funcName) && (0 != funcName[0]))
    149     fprintf (stderr, " in %s", funcName);
    150   if (0 < lineNum)
    151     fprintf (stderr, " at line %d", lineNum);
    152 
    153   fprintf (stderr, ".\nLast errno value: %d (%s)\n", (int) errno,
    154            strerror (errno));
    155 #ifdef MHD_WINSOCK_SOCKETS
    156   fprintf (stderr, "WSAGetLastError() value: %d\n", (int) WSAGetLastError ());
    157 #endif /* MHD_WINSOCK_SOCKETS */
    158   if (0 != libcurl_errbuf[0])
    159     fprintf (stderr, "Last libcurl error description: %s\n", libcurl_errbuf);
    160 
    161   fflush (stderr);
    162   exit (99);
    163 }
    164 
    165 
    166 _MHD_NORETURN static void
    167 _mhdErrorExit_func (const char *errDesc, const char *funcName, int lineNum)
    168 {
    169   fflush (stdout);
    170   if ((NULL != errDesc) && (0 != errDesc[0]))
    171     fprintf (stderr, "%s", errDesc);
    172   else
    173     fprintf (stderr, "MHD unexpected error");
    174   if ((NULL != funcName) && (0 != funcName[0]))
    175     fprintf (stderr, " in %s", funcName);
    176   if (0 < lineNum)
    177     fprintf (stderr, " at line %d", lineNum);
    178 
    179   fprintf (stderr, ".\nLast errno value: %d (%s)\n", (int) errno,
    180            strerror (errno));
    181 #ifdef MHD_WINSOCK_SOCKETS
    182   fprintf (stderr, "WSAGetLastError() value: %d\n", (int) WSAGetLastError ());
    183 #endif /* MHD_WINSOCK_SOCKETS */
    184 
    185   fflush (stderr);
    186   exit (8);
    187 }
    188 
    189 
    190 static void
    191 _checkCURLE_OK_func (CURLcode code, const char *curlFunc,
    192                      const char *funcName, int lineNum)
    193 {
    194   if (CURLE_OK == code)
    195     return;
    196 
    197   fflush (stdout);
    198   if ((NULL != curlFunc) && (0 != curlFunc[0]))
    199     fprintf (stderr, "'%s' resulted in '%s'", curlFunc,
    200              curl_easy_strerror (code));
    201   else
    202     fprintf (stderr, "libcurl function call resulted in '%s'",
    203              curl_easy_strerror (code));
    204   if ((NULL != funcName) && (0 != funcName[0]))
    205     fprintf (stderr, " in %s", funcName);
    206   if (0 < lineNum)
    207     fprintf (stderr, " at line %d", lineNum);
    208 
    209   fprintf (stderr, ".\nLast errno value: %d (%s)\n", (int) errno,
    210            strerror (errno));
    211   if (0 != libcurl_errbuf[0])
    212     fprintf (stderr, "Last libcurl error description: %s\n", libcurl_errbuf);
    213 
    214   fflush (stderr);
    215   exit (9);
    216 }
    217 
    218 
    219 /* Could be increased to facilitate debugging */
    220 #define TIMEOUTS_VAL 4
    221 
    222 #define MHD_URI_BASE_PATH "/hello_world"
    223 
    224 /* Global parameters */
    225 static int verbose;                 /**< Be verbose */
    226 static int oneone;                  /**< If false use HTTP/1.0 for requests*/
    227 static uint16_t global_port;        /**< MHD daemons listen port number */
    228 
    229 struct CBC
    230 {
    231   char *buf;
    232   size_t pos;
    233   size_t size;
    234 };
    235 
    236 static size_t
    237 copyBuffer (void *ptr, size_t size, size_t nmemb, void *ctx)
    238 {
    239   struct CBC *cbc = ctx;
    240 
    241   if (cbc->pos + size * nmemb > cbc->size)
    242     return 0;                   /* overflow */
    243   memcpy (&cbc->buf[cbc->pos], ptr, size * nmemb);
    244   cbc->pos += size * nmemb;
    245   return size * nmemb;
    246 }
    247 
    248 
    249 static enum MHD_Result
    250 ahc_echo (void *cls,
    251           struct MHD_Connection *connection,
    252           const char *url,
    253           const char *method,
    254           const char *version,
    255           const char *upload_data, size_t *upload_data_size,
    256           void **req_cls)
    257 {
    258   static int ptr;
    259   struct MHD_Response *response;
    260   (void) cls;
    261   (void) version; (void) upload_data; (void) upload_data_size;       /* Unused. Silent compiler warning. */
    262 
    263   if (0 != strcmp (MHD_HTTP_METHOD_GET, method))
    264   {
    265     fprintf (stderr, "Unexpected HTTP method '%s'. ", method);
    266     externalErrorExit ();
    267   }
    268   if (&ptr != *req_cls)
    269   {
    270     *req_cls = &ptr;
    271     return MHD_YES;
    272   }
    273   *req_cls = NULL;
    274   response = MHD_create_response_from_buffer_copy (strlen (url),
    275                                                    (const void *) url);
    276   if (NULL == response)
    277     mhdErrorExitDesc ("MHD_create_response failed");
    278   /* Make sure that connection will not be reused */
    279   if (MHD_NO == MHD_add_response_header (response, MHD_HTTP_HEADER_CONNECTION,
    280                                          "close"))
    281     mhdErrorExitDesc ("MHD_add_response_header() failed");
    282   if (MHD_NO == MHD_queue_response (connection, MHD_HTTP_OK, response))
    283     mhdErrorExitDesc ("MHD_queue_response() failed");
    284   MHD_destroy_response (response);
    285   return MHD_YES;
    286 }
    287 
    288 
    289 static void
    290 request_completed (void *cls, struct MHD_Connection *connection,
    291                    void **req_cls, enum MHD_RequestTerminationCode code)
    292 {
    293   int *done = (int *) cls;
    294   (void) connection; (void) req_cls; (void) code;    /* Unused. Silent compiler warning. */
    295   if (MHD_REQUEST_TERMINATED_COMPLETED_OK != code)
    296   {
    297     fprintf (stderr, "Unexpected termination code: %d. ", (int) code);
    298     mhdErrorExit ();
    299   }
    300   *done = 1;
    301   if (verbose)
    302     printf ("Notify callback has been called with OK code.\n");
    303 }
    304 
    305 
    306 static void *
    307 ServeOneRequest (void *param)
    308 {
    309   struct MHD_Daemon *d;
    310   fd_set rs;
    311   fd_set ws;
    312   fd_set es;
    313   MHD_socket fd, max;
    314   time_t start;
    315   struct timeval tv;
    316   volatile int done = 0;
    317 
    318   if (NULL == param)
    319     externalErrorExit ();
    320 
    321   fd = *((MHD_socket *) param);
    322 
    323   d = MHD_start_daemon (MHD_USE_ERROR_LOG,
    324                         0, NULL, NULL, &ahc_echo, NULL,
    325                         MHD_OPTION_LISTEN_SOCKET, fd,
    326                         MHD_OPTION_NOTIFY_COMPLETED, &request_completed, &done,
    327                         MHD_OPTION_APP_FD_SETSIZE, (int) FD_SETSIZE,
    328                         MHD_OPTION_END);
    329   if (d == NULL)
    330     mhdErrorExit ();
    331 
    332   if (verbose)
    333     printf ("Started MHD daemon in ServeOneRequest().\n");
    334 
    335   start = time (NULL);
    336   while ((time (NULL) - start < TIMEOUTS_VAL * 2) && done == 0)
    337   {
    338     max = 0;
    339     FD_ZERO (&rs);
    340     FD_ZERO (&ws);
    341     FD_ZERO (&es);
    342     if (MHD_YES != MHD_get_fdset (d, &rs, &ws, &es, &max))
    343       mhdErrorExit ("MHD_get_fdset() failed");
    344     tv.tv_sec = 0;
    345     tv.tv_usec = 100000;
    346     if (-1 == MHD_SYS_select_ (max + 1, &rs, &ws, &es, &tv))
    347     {
    348 #ifdef MHD_POSIX_SOCKETS
    349       if (EINTR != errno)
    350         externalErrorExitDesc ("Unexpected select() error");
    351 #else
    352       if ((WSAEINVAL != WSAGetLastError ()) ||
    353           (0 != rs.fd_count) || (0 != ws.fd_count) || (0 != es.fd_count) )
    354         externalErrorExitDesc ("Unexpected select() error");
    355       Sleep ((DWORD) (tv.tv_sec * 1000 + tv.tv_usec / 1000));
    356 #endif
    357     }
    358     MHD_run (d);
    359   }
    360   if (! done)
    361     mhdErrorExit ("ServeOneRequest() failed and finished by timeout");
    362   fd = MHD_quiesce_daemon (d);
    363   if (MHD_INVALID_SOCKET == fd)
    364     mhdErrorExit ("MHD_quiesce_daemon() failed in ServeOneRequest()");
    365 
    366   MHD_stop_daemon (d);
    367   return NULL;
    368 }
    369 
    370 
    371 static CURL *
    372 setupCURL (void *cbc)
    373 {
    374   CURL *c;
    375 
    376   c = curl_easy_init ();
    377   if (NULL == c)
    378     libcurlErrorExitDesc ("curl_easy_init() failed");
    379 
    380   if ((CURLE_OK != curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1L)) ||
    381       (CURLE_OK != curl_easy_setopt (c, CURLOPT_URL,
    382                                      "http://127.0.0.1" MHD_URI_BASE_PATH)) ||
    383       (CURLE_OK != curl_easy_setopt (c, CURLOPT_PORT, (long) global_port)) ||
    384       (CURLE_OK != curl_easy_setopt (c, CURLOPT_WRITEFUNCTION,
    385                                      &copyBuffer)) ||
    386       (CURLE_OK != curl_easy_setopt (c, CURLOPT_WRITEDATA, cbc)) ||
    387       (CURLE_OK != curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT,
    388                                      (long) (TIMEOUTS_VAL / 2))) ||
    389       (CURLE_OK != curl_easy_setopt (c, CURLOPT_TIMEOUT,
    390                                      (long) TIMEOUTS_VAL)) ||
    391       (CURLE_OK != curl_easy_setopt (c, CURLOPT_ERRORBUFFER,
    392                                      libcurl_errbuf)) ||
    393       (CURLE_OK != curl_easy_setopt (c, CURLOPT_FAILONERROR, 1L)) ||
    394       (CURLE_OK != curl_easy_setopt (c, CURLOPT_HTTP_VERSION,
    395                                      (oneone) ?
    396                                      CURL_HTTP_VERSION_1_1 :
    397                                      CURL_HTTP_VERSION_1_0)))
    398     libcurlErrorExitDesc ("curl_easy_setopt() failed");
    399   return c;
    400 }
    401 
    402 
    403 static unsigned int
    404 testGet (unsigned int type, int pool_count, uint32_t poll_flag)
    405 {
    406   struct MHD_Daemon *d;
    407   CURL *c;
    408   char buf[2048];
    409   struct CBC cbc;
    410   MHD_socket fd;
    411   pthread_t thrd;
    412   char *thrdRet;
    413 
    414   if (verbose)
    415     printf ("testGet(%u, %d, %u) test started.\n",
    416             type, pool_count, (unsigned int) poll_flag);
    417 
    418   cbc.buf = buf;
    419   cbc.size = sizeof(buf);
    420   cbc.pos = 0;
    421   if (pool_count > 0)
    422   {
    423     d = MHD_start_daemon (type | MHD_USE_ERROR_LOG | MHD_USE_ITC
    424                           | (enum MHD_FLAG) poll_flag,
    425                           global_port, NULL, NULL, &ahc_echo, NULL,
    426                           MHD_OPTION_THREAD_POOL_SIZE,
    427                           (unsigned int) pool_count,
    428                           MHD_OPTION_END);
    429 
    430   }
    431   else
    432   {
    433     d = MHD_start_daemon (type | MHD_USE_ERROR_LOG | MHD_USE_ITC
    434                           | (enum MHD_FLAG) poll_flag,
    435                           global_port, NULL, NULL, &ahc_echo, NULL,
    436                           MHD_OPTION_END);
    437   }
    438   if (d == NULL)
    439     mhdErrorExitDesc ("MHD_start_daemon() failed");
    440   if (0 == global_port)
    441   {
    442     const union MHD_DaemonInfo *dinfo;
    443     dinfo = MHD_get_daemon_info (d, MHD_DAEMON_INFO_BIND_PORT);
    444     if ((NULL == dinfo) || (0 == dinfo->port) )
    445       mhdErrorExit ();
    446     global_port = dinfo->port;
    447   }
    448 
    449   c = setupCURL (&cbc);
    450 
    451   checkCURLE_OK (curl_easy_perform (c));
    452 
    453   if (cbc.pos != strlen (MHD_URI_BASE_PATH))
    454   {
    455     fprintf (stderr, "Got %u bytes ('%.*s'), expected %u bytes. ",
    456              (unsigned) cbc.pos, (int) cbc.pos, cbc.buf,
    457              (unsigned) strlen (MHD_URI_BASE_PATH));
    458     mhdErrorExitDesc ("Wrong returned data length");
    459   }
    460   if (0 != strncmp (MHD_URI_BASE_PATH, cbc.buf, strlen (MHD_URI_BASE_PATH)))
    461   {
    462     fprintf (stderr, "Got invalid response '%.*s'. ", (int) cbc.pos, cbc.buf);
    463     mhdErrorExitDesc ("Wrong returned data");
    464   }
    465   if (verbose)
    466     printf ("Received valid response data.\n");
    467 
    468   fd = MHD_quiesce_daemon (d);
    469   if (MHD_INVALID_SOCKET == fd)
    470     mhdErrorExitDesc ("MHD_quiesce_daemon failed");
    471 
    472   if (0 != pthread_create (&thrd, NULL, &ServeOneRequest,
    473                            (void *) &fd))
    474     externalErrorExitDesc ("pthread_create() failed");
    475 
    476   /* No need for the thread sync as socket is already listening,
    477    * so libcurl may start connecting before MHD is started in another thread */
    478   cbc.pos = 0;
    479   checkCURLE_OK (curl_easy_perform (c));
    480 
    481   if (cbc.pos != strlen (MHD_URI_BASE_PATH))
    482   {
    483     fprintf (stderr, "Got %u bytes ('%.*s'), expected %u bytes. ",
    484              (unsigned) cbc.pos, (int) cbc.pos, cbc.buf,
    485              (unsigned) strlen (MHD_URI_BASE_PATH));
    486     mhdErrorExitDesc ("Wrong returned data length");
    487   }
    488   if (0 != strncmp (MHD_URI_BASE_PATH, cbc.buf, strlen (MHD_URI_BASE_PATH)))
    489   {
    490     fprintf (stderr, "Got invalid response '%.*s'. ", (int) cbc.pos, cbc.buf);
    491     mhdErrorExitDesc ("Wrong returned data");
    492   }
    493 
    494   if (0 != pthread_join (thrd, (void **) &thrdRet))
    495     externalErrorExitDesc ("pthread_join() failed");
    496   if (NULL != thrdRet)
    497     externalErrorExitDesc ("ServeOneRequest() returned non-NULL result");
    498 
    499   if (verbose)
    500   {
    501     printf ("ServeOneRequest() thread was joined.\n");
    502     fflush (stdout);
    503   }
    504 
    505   /* at this point, the forked server quiesced and quit,
    506    * so new requests should fail
    507    */
    508   cbc.pos = 0;
    509   if (CURLE_OK == curl_easy_perform (c))
    510   {
    511     fprintf (stderr, "curl_easy_perform() succeed while it should fail. ");
    512     fprintf (stderr, "Got %u bytes ('%.*s'), "
    513              "valid data would be %u bytes (%s). ",
    514              (unsigned) cbc.pos, (int) cbc.pos, cbc.buf,
    515              (unsigned) strlen (MHD_URI_BASE_PATH), MHD_URI_BASE_PATH);
    516     mhdErrorExitDesc ("Unexpected succeed request");
    517   }
    518   if (verbose)
    519     printf ("curl_easy_perform() failed as expected.\n");
    520   curl_easy_cleanup (c);
    521   MHD_stop_daemon (d);
    522   MHD_socket_close_chk_ (fd);
    523 
    524   if (verbose)
    525   {
    526     printf ("testGet(%u, %d, %u) test succeed.\n",
    527             type, pool_count, (unsigned int) poll_flag);
    528     fflush (stdout);
    529   }
    530 
    531   return 0;
    532 }
    533 
    534 
    535 static unsigned int
    536 testExternalGet (void)
    537 {
    538   struct MHD_Daemon *d;
    539   CURL *c;
    540   char buf[2048];
    541   struct CBC cbc;
    542   CURLM *multi;
    543   CURLMcode mret;
    544   fd_set rs;
    545   fd_set ws;
    546   fd_set es;
    547   int running;
    548   struct CURLMsg *msg;
    549   time_t start;
    550   struct timeval tv;
    551   int i;
    552   MHD_socket fd = MHD_INVALID_SOCKET;
    553 
    554   if (verbose)
    555     printf ("testExternalGet test started.\n");
    556 
    557   fd = MHD_INVALID_SOCKET;
    558   multi = NULL;
    559   cbc.buf = buf;
    560   cbc.size = sizeof(buf);
    561   cbc.pos = 0;
    562   d = MHD_start_daemon (MHD_USE_ERROR_LOG,
    563                         global_port,
    564                         NULL, NULL,
    565                         &ahc_echo, NULL,
    566                         MHD_OPTION_APP_FD_SETSIZE, (int) FD_SETSIZE,
    567                         MHD_OPTION_END);
    568   if (d == NULL)
    569     mhdErrorExitDesc ("Failed to start MHD daemon");
    570   if (0 == global_port)
    571   {
    572     const union MHD_DaemonInfo *dinfo;
    573     dinfo = MHD_get_daemon_info (d, MHD_DAEMON_INFO_BIND_PORT);
    574     if ((NULL == dinfo) || (0 == dinfo->port) )
    575       mhdErrorExit ();
    576     global_port = dinfo->port;
    577   }
    578 
    579   for (i = 0; i < 2; i++)
    580   {
    581     c = setupCURL (&cbc);
    582 
    583     multi = curl_multi_init ();
    584     if (multi == NULL)
    585       libcurlErrorExit ();
    586 
    587     mret = curl_multi_add_handle (multi, c);
    588     if (mret != CURLM_OK)
    589       libcurlErrorExit ();
    590 
    591     start = time (NULL);
    592     while ( (time (NULL) - start < TIMEOUTS_VAL * 2) &&
    593             (NULL != multi) )
    594     {
    595       MHD_socket maxsock;
    596       int maxposixs;
    597       maxsock = MHD_INVALID_SOCKET;
    598       maxposixs = -1;
    599       FD_ZERO (&rs);
    600       FD_ZERO (&ws);
    601       FD_ZERO (&es);
    602       curl_multi_perform (multi, &running);
    603       mret = curl_multi_fdset (multi, &rs, &ws, &es, &maxposixs);
    604       if (mret != CURLM_OK)
    605         libcurlErrorExit ();
    606       if (MHD_YES != MHD_get_fdset (d, &rs, &ws, &es, &maxsock))
    607         mhdErrorExit ();
    608 #ifndef MHD_WINSOCK_SOCKETS
    609       if (maxsock > maxposixs)
    610         maxposixs = maxsock;
    611 #endif /* MHD_POSIX_SOCKETS */
    612       tv.tv_sec = 0;
    613       tv.tv_usec = 100000;
    614       if (-1 == select (maxposixs + 1, &rs, &ws, &es, &tv))
    615       {
    616 #ifdef MHD_POSIX_SOCKETS
    617         if (EINTR != errno)
    618           externalErrorExitDesc ("Unexpected select() error");
    619 #else
    620         if ((WSAEINVAL != WSAGetLastError ()) ||
    621             (0 != rs.fd_count) || (0 != ws.fd_count) || (0 != es.fd_count) )
    622           externalErrorExitDesc ("Unexpected select() error");
    623         Sleep ((DWORD) (tv.tv_sec * 1000 + tv.tv_usec / 1000));
    624 #endif
    625       }
    626       curl_multi_perform (multi, &running);
    627       if (0 == running)
    628       {
    629         int pending;
    630         int curl_fine = 0;
    631         while (NULL != (msg = curl_multi_info_read (multi, &pending)))
    632         {
    633           if (msg->msg == CURLMSG_DONE)
    634           {
    635             if (msg->data.result == CURLE_OK)
    636             {
    637               curl_fine = 1;
    638               if (verbose)
    639                 printf ("libcurl reported success.\n");
    640             }
    641             else if (i == 0)
    642             {
    643               fprintf (stderr,
    644                        "curl_multi_perform() failed with '%s'. ",
    645                        curl_easy_strerror (msg->data.result));
    646               mhdErrorExit ();
    647             }
    648           }
    649         }
    650         if (i == 0)
    651         {
    652           if (! curl_fine)
    653           {
    654             fprintf (stderr, "libcurl haven't returned OK code\n");
    655             mhdErrorExit ();
    656           }
    657           /* MHD is running, result should be correct */
    658           if (cbc.pos != strlen (MHD_URI_BASE_PATH))
    659           {
    660             fprintf (stderr, "Got %u bytes ('%.*s'), expected %u bytes. ",
    661                      (unsigned) cbc.pos, (int) cbc.pos, cbc.buf,
    662                      (unsigned) strlen (MHD_URI_BASE_PATH));
    663             mhdErrorExitDesc ("Wrong returned data length");
    664           }
    665           if (0 != strncmp (MHD_URI_BASE_PATH, cbc.buf,
    666                             strlen (MHD_URI_BASE_PATH)))
    667           {
    668             fprintf (stderr, "Got invalid response '%.*s'. ", (int) cbc.pos,
    669                      cbc.buf);
    670             mhdErrorExitDesc ("Wrong returned data");
    671           }
    672           if (verbose)
    673           {
    674             printf ("First request was successful.\n");
    675             fflush (stdout);
    676           }
    677         }
    678         else if (i == 1)
    679         {
    680           if  (curl_fine)
    681           {
    682             fprintf (stderr, "libcurl returned OK code, while it shouldn't\n");
    683             mhdErrorExit ();
    684           }
    685           if (verbose)
    686             printf ("Second request failed as expected.\n");
    687         }
    688         curl_multi_remove_handle (multi, c);
    689         curl_multi_cleanup (multi);
    690         curl_easy_cleanup (c);
    691         c = NULL;
    692         multi = NULL;
    693         break;
    694       }
    695       MHD_run (d);
    696     }
    697 
    698     if (NULL != multi)
    699       mhdErrorExitDesc ("Test failed and finished by timeout");
    700 
    701     if (0 == i)
    702     {
    703       /* quiesce the daemon on the 1st iteration, so the 2nd should fail */
    704       fd = MHD_quiesce_daemon (d);
    705       if (MHD_INVALID_SOCKET == fd)
    706         mhdErrorExitDesc ("MHD_quiesce_daemon() failed");
    707     }
    708   }
    709   MHD_stop_daemon (d);
    710   if (MHD_INVALID_SOCKET == fd)
    711   {
    712     fprintf (stderr, "Failed to MHD_quiesce_daemon() at some point. ");
    713     externalErrorExit ();
    714   }
    715   MHD_socket_close_chk_ (fd);
    716 
    717   if (verbose)
    718   {
    719     printf ("testExternalGet succeed.\n");
    720     fflush (stdout);
    721   }
    722 
    723   return 0;
    724 }
    725 
    726 
    727 int
    728 main (int argc, char *const *argv)
    729 {
    730   unsigned int errorCount = 0;
    731   oneone = ! has_in_name (argv[0], "10");
    732   verbose = ! (has_param (argc, argv, "-q") ||
    733                has_param (argc, argv, "--quiet") ||
    734                has_param (argc, argv, "-s") ||
    735                has_param (argc, argv, "--silent"));
    736 
    737   if (0 != curl_global_init (CURL_GLOBAL_WIN32))
    738     return 2;
    739 
    740   if (MHD_NO != MHD_is_feature_supported (MHD_FEATURE_AUTODETECT_BIND_PORT))
    741     global_port = 0;
    742   else
    743     global_port = 1480 + (oneone ? 1 : 0);
    744 
    745   errorCount += testExternalGet ();
    746   if (MHD_YES == MHD_is_feature_supported (MHD_FEATURE_THREADS))
    747   {
    748     errorCount += testGet (MHD_USE_INTERNAL_POLLING_THREAD, 0, 0);
    749     errorCount += testGet (MHD_USE_THREAD_PER_CONNECTION
    750                            | MHD_USE_INTERNAL_POLLING_THREAD, 0, 0);
    751     errorCount += testGet (MHD_USE_INTERNAL_POLLING_THREAD, MHD_CPU_COUNT, 0);
    752     if (MHD_YES == MHD_is_feature_supported (MHD_FEATURE_POLL))
    753     {
    754       errorCount += testGet (MHD_USE_INTERNAL_POLLING_THREAD, 0, MHD_USE_POLL);
    755       errorCount += testGet (MHD_USE_THREAD_PER_CONNECTION
    756                              | MHD_USE_INTERNAL_POLLING_THREAD, 0,
    757                              MHD_USE_POLL);
    758       errorCount += testGet (MHD_USE_INTERNAL_POLLING_THREAD, MHD_CPU_COUNT,
    759                              MHD_USE_POLL);
    760     }
    761     if (MHD_YES == MHD_is_feature_supported (MHD_FEATURE_EPOLL))
    762     {
    763       errorCount += testGet (MHD_USE_INTERNAL_POLLING_THREAD, 0, MHD_USE_EPOLL);
    764       errorCount += testGet (MHD_USE_INTERNAL_POLLING_THREAD, MHD_CPU_COUNT,
    765                              MHD_USE_EPOLL);
    766     }
    767   }
    768   if (0 != errorCount)
    769     fprintf (stderr,
    770              "Error (code: %u)\n",
    771              errorCount);
    772   curl_global_cleanup ();
    773   return (0 == errorCount) ? 0 : 1;       /* 0 == pass */
    774 }