libmicrohttpd

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

test_quiesce.c (23832B)


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