libmicrohttpd

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

test_add_conn.c (37661B)


      1 /*
      2      This file is part of libmicrohttpd
      3      Copyright (C) 2007, 2009, 2011 Christian Grothoff
      4      Copyright (C) 2014-2022 Evgeny Grin (Karlson2k) - large rework,
      5                              multithreading.
      6 
      7      libmicrohttpd is free software; you can redistribute it and/or modify
      8      it under the terms of the GNU General Public License as published
      9      by the Free Software Foundation; either version 2, or (at your
     10      option) any later version.
     11 
     12      libmicrohttpd is distributed in the hope that it will be useful, but
     13      WITHOUT ANY WARRANTY; without even the implied warranty of
     14      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     15      General Public License for more details.
     16 
     17      You should have received a copy of the GNU General Public License
     18      along with libmicrohttpd; see the file COPYING.  If not, write to the
     19      Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
     20      Boston, MA 02110-1301, USA.
     21 */
     22 /**
     23  * @file test_add_conn.c
     24  * @brief  Testcase for libmicrohttpd GET operations
     25  * @author Christian Grothoff
     26  * @author Karlson2k (Evgeny Grin)
     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 <errno.h>
     36 #include "mhd_has_in_name.h"
     37 #include "mhd_has_param.h"
     38 #include "mhd_sockets.h" /* only macros used */
     39 
     40 /* Turn any MHD_PANIC() or failing mhd_assert() reached from this
     41    test into a marked, classifiable test error (TESTING.md, P5). */
     42 #include "mhd_panic_tripwire.h"
     43 
     44 
     45 #ifdef _WIN32
     46 #ifndef WIN32_LEAN_AND_MEAN
     47 #define WIN32_LEAN_AND_MEAN 1
     48 #endif /* !WIN32_LEAN_AND_MEAN */
     49 #include <windows.h>
     50 #endif
     51 
     52 #ifndef WINDOWS
     53 #include <unistd.h>
     54 #include <sys/socket.h>
     55 #endif
     56 
     57 #ifdef HAVE_LIMITS_H
     58 #include <limits.h>
     59 #endif /* HAVE_LIMITS_H */
     60 
     61 #ifdef HAVE_PTHREAD_H
     62 #include <pthread.h>
     63 #endif /* HAVE_PTHREAD_H */
     64 
     65 #if defined(MHD_CPU_COUNT) && (MHD_CPU_COUNT + 0) < 2
     66 #undef MHD_CPU_COUNT
     67 #endif
     68 #if ! defined(MHD_CPU_COUNT)
     69 #define MHD_CPU_COUNT 2
     70 #endif
     71 #if MHD_CPU_COUNT > 32
     72 #undef MHD_CPU_COUNT
     73 /* Limit to reasonable value */
     74 #define MHD_CPU_COUNT 32
     75 #endif /* MHD_CPU_COUNT > 32 */
     76 
     77 /* Could be increased to facilitate debugging */
     78 #define TIMEOUTS_VAL 5
     79 
     80 /* Number of requests per daemon in cleanup test,
     81  * the number must be more than one as the first connection
     82  * will be processed and the rest will stay in the list of unprocessed */
     83 #define CLEANUP_NUM_REQS_PER_DAEMON 6
     84 
     85 /* Cleanup test: max number of concurrent daemons depending on maximum number
     86  * of open FDs. */
     87 #define CLEANUP_MAX_DAEMONS(max_fds) (unsigned int) \
     88   ( ((max_fds) < 10) ? \
     89     0 : ( (((max_fds) - 10) / (CLEANUP_NUM_REQS_PER_DAEMON * 5 + 3)) ) )
     90 
     91 #define EXPECTED_URI_BASE_PATH  "/hello_world"
     92 #define EXPECTED_URI_QUERY      "a=%26&b=c"
     93 #define EXPECTED_URI_FULL_PATH  EXPECTED_URI_BASE_PATH "?" EXPECTED_URI_QUERY
     94 
     95 /* Global parameters */
     96 static int oneone;           /**< Use HTTP/1.1 instead of HTTP/1.0 */
     97 static int no_listen;        /**< Start MHD daemons without listen socket */
     98 static uint16_t global_port; /**< MHD daemons listen port number */
     99 static int cleanup_test;     /**< Test for final cleanup */
    100 static int slow_reply = 0; /**< Slowdown MHD replies */
    101 static int ignore_response_errors = 0; /**< Do not fail test if CURL
    102                                             returns error */
    103 static int response_timeout_val = TIMEOUTS_VAL;
    104 static int sys_max_fds;    /**< Current system limit for number of open
    105                                 files. */
    106 
    107 
    108 struct CBC
    109 {
    110   char *buf;
    111   size_t pos;
    112   size_t size;
    113 };
    114 
    115 
    116 static size_t
    117 copyBuffer (void *ptr, size_t size, size_t nmemb, void *ctx)
    118 {
    119   struct CBC *cbc = ctx;
    120 
    121   if (cbc->pos + size * nmemb > cbc->size)
    122     return 0;                   /* overflow */
    123   memcpy (&cbc->buf[cbc->pos], ptr, size * nmemb);
    124   cbc->pos += size * nmemb;
    125   return size * nmemb;
    126 }
    127 
    128 
    129 static void *
    130 log_cb (void *cls,
    131         const char *uri,
    132         struct MHD_Connection *con)
    133 {
    134   (void) cls;
    135   (void) con;
    136   if (0 != strcmp (uri,
    137                    EXPECTED_URI_FULL_PATH))
    138   {
    139     fprintf (stderr,
    140              "Wrong URI: `%s'\n",
    141              uri);
    142     _exit (22);
    143   }
    144   return NULL;
    145 }
    146 
    147 
    148 static enum MHD_Result
    149 ahc_echo (void *cls,
    150           struct MHD_Connection *connection,
    151           const char *url,
    152           const char *method,
    153           const char *version,
    154           const char *upload_data, size_t *upload_data_size,
    155           void **req_cls)
    156 {
    157   static int ptr;
    158   struct MHD_Response *response;
    159   enum MHD_Result ret;
    160   const char *v;
    161   (void) cls;
    162   (void) version;
    163   (void) upload_data;
    164   (void) upload_data_size;       /* Unused. Silence compiler warning. */
    165 
    166   if (0 != strcmp (MHD_HTTP_METHOD_GET, method))
    167     return MHD_NO;              /* unexpected method */
    168   if (&ptr != *req_cls)
    169   {
    170     *req_cls = &ptr;
    171     return MHD_YES;
    172   }
    173   *req_cls = NULL;
    174   v = MHD_lookup_connection_value (connection,
    175                                    MHD_GET_ARGUMENT_KIND,
    176                                    "a");
    177   if ( (NULL == v) ||
    178        (0 != strcmp ("&",
    179                      v)) )
    180   {
    181     fprintf (stderr, "Found while looking for 'a=&': 'a=%s'\n",
    182              NULL == v ? "NULL" : v);
    183     _exit (17);
    184   }
    185   v = NULL;
    186   if (MHD_YES != MHD_lookup_connection_value_n (connection,
    187                                                 MHD_GET_ARGUMENT_KIND,
    188                                                 "b",
    189                                                 1,
    190                                                 &v,
    191                                                 NULL))
    192   {
    193     fprintf (stderr, "Not found 'b' GET argument.\n");
    194     _exit (18);
    195   }
    196   if ( (NULL == v) ||
    197        (0 != strcmp ("c",
    198                      v)) )
    199   {
    200     fprintf (stderr, "Found while looking for 'b=c': 'b=%s'\n",
    201              NULL == v ? "NULL" : v);
    202     _exit (19);
    203   }
    204   if (slow_reply)
    205     usleep (200000);
    206 
    207   response = MHD_create_response_from_buffer_copy (strlen (url),
    208                                                    (const void *) url);
    209   ret = MHD_queue_response (connection,
    210                             MHD_HTTP_OK,
    211                             response);
    212   MHD_destroy_response (response);
    213   if (ret == MHD_NO)
    214   {
    215     fprintf (stderr, "Failed to queue response.\n");
    216     _exit (19);
    217   }
    218   return ret;
    219 }
    220 
    221 
    222 _MHD_NORETURN static void
    223 _externalErrorExit_func (const char *errDesc, const char *funcName, int lineNum)
    224 {
    225   if ((NULL != errDesc) && (0 != errDesc[0]))
    226     fprintf (stderr, "%s", errDesc);
    227   else
    228     fprintf (stderr, "System or external library call failed");
    229   if ((NULL != funcName) && (0 != funcName[0]))
    230     fprintf (stderr, " in %s", funcName);
    231   if (0 < lineNum)
    232     fprintf (stderr, " at line %d", lineNum);
    233 
    234   fprintf (stderr, ".\nLast errno value: %d (%s)\n", (int) errno,
    235            strerror (errno));
    236 #ifdef MHD_WINSOCK_SOCKETS
    237   fprintf (stderr, "WSAGetLastError() value: %d\n", (int) WSAGetLastError ());
    238 #endif /* MHD_WINSOCK_SOCKETS */
    239   fflush (stderr);
    240   _exit (99);
    241 }
    242 
    243 
    244 #if defined(HAVE___FUNC__)
    245 #define externalErrorExit(ignore) \
    246   _externalErrorExit_func (NULL, __func__, __LINE__)
    247 #define externalErrorExitDesc(errDesc) \
    248   _externalErrorExit_func (errDesc, __func__, __LINE__)
    249 #elif defined(HAVE___FUNCTION__)
    250 #define externalErrorExit(ignore) \
    251   _externalErrorExit_func (NULL, __FUNCTION__, __LINE__)
    252 #define externalErrorExitDesc(errDesc) \
    253   _externalErrorExit_func (errDesc, __FUNCTION__, __LINE__)
    254 #else
    255 #define externalErrorExit(ignore) _externalErrorExit_func (NULL, NULL, __LINE__)
    256 #define externalErrorExitDesc(errDesc) \
    257   _externalErrorExit_func (errDesc, NULL, __LINE__)
    258 #endif
    259 
    260 
    261 /* Static const value, indicates that result value was not set yet */
    262 static const unsigned int eMarker = 0xCE;
    263 
    264 
    265 static MHD_socket
    266 createListeningSocket (uint16_t *pport)
    267 {
    268   MHD_socket skt;
    269   struct sockaddr_in sin;
    270   socklen_t sin_len;
    271 #ifdef MHD_POSIX_SOCKETS
    272   static int on = 1;
    273 #endif /* MHD_POSIX_SOCKETS */
    274 
    275   skt = socket (PF_INET, SOCK_STREAM, IPPROTO_TCP);
    276   if (MHD_INVALID_SOCKET == skt)
    277     externalErrorExitDesc ("socket() failed");
    278 
    279 #ifdef MHD_POSIX_SOCKETS
    280   setsockopt (skt, SOL_SOCKET, SO_REUSEADDR, (void *) &on, sizeof (on));
    281   /* Ignore possible error */
    282 #endif /* MHD_POSIX_SOCKETS */
    283 
    284   memset (&sin, 0, sizeof(sin));
    285   sin.sin_family = AF_INET;
    286   sin.sin_port = htons (*pport);
    287   sin.sin_addr.s_addr = htonl (INADDR_LOOPBACK);
    288   if (0 != bind (skt, (struct sockaddr *) &sin, sizeof(sin)))
    289     externalErrorExitDesc ("bind() failed");
    290 
    291   if (0 != listen (skt, SOMAXCONN))
    292     externalErrorExitDesc ("listen() failed");
    293 
    294   if (0 == *pport)
    295   {
    296     memset (&sin, 0, sizeof(sin));
    297     sin_len = (socklen_t) sizeof(sin);
    298     if (0 != getsockname (skt, (struct sockaddr *) &sin, &sin_len))
    299       externalErrorExitDesc ("getsockname() failed");
    300 
    301     if (sizeof(sin) < (size_t) sin_len)
    302       externalErrorExitDesc ("getsockname() failed");
    303 
    304     if (AF_INET != sin.sin_family)
    305       externalErrorExitDesc ("getsockname() returned wrong socket family");
    306 
    307     *pport = ntohs (sin.sin_port);
    308   }
    309 
    310   return skt;
    311 }
    312 
    313 
    314 static MHD_socket
    315 acceptTimeLimited (MHD_socket lstn_sk, struct sockaddr *paddr,
    316                    socklen_t *paddr_len)
    317 {
    318   fd_set rs;
    319   struct timeval timeoutval;
    320   MHD_socket accepted;
    321 
    322   FD_ZERO (&rs);
    323   FD_SET (lstn_sk, &rs);
    324   timeoutval.tv_sec = TIMEOUTS_VAL;
    325   timeoutval.tv_usec = 0;
    326   if (1 != select (((int) lstn_sk) + 1, &rs, NULL, NULL, &timeoutval))
    327     externalErrorExitDesc ("select() failed");
    328 
    329   accepted = accept (lstn_sk, paddr, paddr_len);
    330   if (MHD_INVALID_SOCKET == accepted)
    331     externalErrorExitDesc ("accept() failed");
    332 
    333   return accepted;
    334 }
    335 
    336 
    337 struct addConnParam
    338 {
    339   struct MHD_Daemon *d;
    340 
    341   MHD_socket lstn_sk;
    342 
    343   MHD_socket clent_sk;
    344   /* Non-zero indicate error */
    345   volatile unsigned int result;
    346 
    347 #ifdef HAVE_PTHREAD_H
    348   pthread_t addConnThread;
    349 #endif /* HAVE_PTHREAD_H */
    350 };
    351 
    352 static unsigned int
    353 doAcceptAndAddConnInThread (struct addConnParam *p)
    354 {
    355   struct sockaddr addr;
    356   socklen_t addr_len = sizeof(addr);
    357 
    358   p->clent_sk = acceptTimeLimited (p->lstn_sk, &addr, &addr_len);
    359 
    360   p->result = (MHD_YES == MHD_add_connection (p->d, p->clent_sk,
    361                                               &addr, addr_len)) ?
    362               0 : 1;
    363   if (p->result)
    364     fprintf (stderr, "MHD_add_connection() failed, errno=%d.\n", errno);
    365   return p->result;
    366 }
    367 
    368 
    369 #ifdef HAVE_PTHREAD_H
    370 static void *
    371 doAcceptAndAddConn (void *param)
    372 {
    373   struct addConnParam *p = param;
    374 
    375   (void) doAcceptAndAddConnInThread (p);
    376 
    377   return (void *) p;
    378 }
    379 
    380 
    381 static void
    382 startThreadAddConn (struct addConnParam *param)
    383 {
    384   /* thread must reset this value to zero if succeed */
    385   param->result = eMarker;
    386 
    387   if (0 != pthread_create (&param->addConnThread, NULL, &doAcceptAndAddConn,
    388                            (void *) param))
    389     externalErrorExitDesc ("pthread_create() failed");
    390 }
    391 
    392 
    393 static unsigned int
    394 finishThreadAddConn (struct addConnParam *param)
    395 {
    396   struct addConnParam *result;
    397 
    398   if (0 != pthread_join (param->addConnThread, (void **) &result))
    399     externalErrorExitDesc ("pthread_join() failed");
    400 
    401   if (param != result)
    402     abort (); /* Test used in a wrong way */
    403 
    404   if (eMarker == param->result)
    405     abort (); /* Test used in a wrong way */
    406 
    407   return result->result;
    408 }
    409 
    410 
    411 #endif /* HAVE_PTHREAD_H */
    412 
    413 
    414 struct curlQueryParams
    415 {
    416   /* Destination path for CURL query */
    417   const char *queryPath;
    418 
    419   /* Destination port for CURL query */
    420   uint16_t queryPort;
    421 
    422   /* CURL query result error flag */
    423   volatile unsigned int queryError;
    424 
    425 #ifdef HAVE_PTHREAD_H
    426   pthread_t queryThread;
    427 #endif /* HAVE_PTHREAD_H */
    428 };
    429 
    430 static CURL *
    431 curlEasyInitForTest (const char *queryPath, uint16_t port, struct CBC *pcbc)
    432 {
    433   CURL *c;
    434 
    435   c = curl_easy_init ();
    436   if (NULL == c)
    437   {
    438     fprintf (stderr, "curl_easy_init() failed.\n");
    439     _exit (99);
    440   }
    441   if ((CURLE_OK != curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1L)) ||
    442       (CURLE_OK != curl_easy_setopt (c, CURLOPT_URL, queryPath)) ||
    443       (CURLE_OK != curl_easy_setopt (c, CURLOPT_PORT, (long) port)) ||
    444       (CURLE_OK != curl_easy_setopt (c, CURLOPT_WRITEFUNCTION,
    445                                      &copyBuffer)) ||
    446       (CURLE_OK != curl_easy_setopt (c, CURLOPT_WRITEDATA, pcbc)) ||
    447       (CURLE_OK != curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT,
    448                                      (long) response_timeout_val)) ||
    449       (CURLE_OK != curl_easy_setopt (c, CURLOPT_TIMEOUT,
    450                                      (long) response_timeout_val)) ||
    451       (CURLE_OK != curl_easy_setopt (c, CURLOPT_FAILONERROR, 1L)) ||
    452       (CURLE_OK != curl_easy_setopt (c, CURLOPT_HTTP_VERSION,
    453                                      (oneone) ?
    454                                      CURL_HTTP_VERSION_1_1 :
    455                                      CURL_HTTP_VERSION_1_0)))
    456   {
    457     fprintf (stderr, "curl_easy_setopt() failed.\n");
    458     _exit (99);
    459   }
    460 
    461   return c;
    462 }
    463 
    464 
    465 static unsigned int
    466 doCurlQueryInThread (struct curlQueryParams *p)
    467 {
    468   CURL *c;
    469   char buf[2048];
    470   struct CBC cbc;
    471   CURLcode errornum;
    472 
    473   if (NULL == p->queryPath)
    474     abort ();
    475 
    476   if (0 == p->queryPort)
    477     abort ();
    478 
    479   cbc.buf = buf;
    480   cbc.size = sizeof(buf);
    481   cbc.pos = 0;
    482 
    483   c = curlEasyInitForTest (p->queryPath, p->queryPort, &cbc);
    484 
    485   errornum = curl_easy_perform (c);
    486   if (ignore_response_errors)
    487   {
    488     p->queryError = 0;
    489     curl_easy_cleanup (c);
    490 
    491     return p->queryError;
    492   }
    493   if (CURLE_OK != errornum)
    494   {
    495     fprintf (stderr,
    496              "curl_easy_perform failed: `%s'\n",
    497              curl_easy_strerror (errornum));
    498     p->queryError = 2;
    499   }
    500   else
    501   {
    502     if (cbc.pos != strlen (EXPECTED_URI_BASE_PATH))
    503     {
    504       fprintf (stderr, "curl reports wrong size of MHD reply body data.\n");
    505       p->queryError = 4;
    506     }
    507     else if (0 != strncmp (EXPECTED_URI_BASE_PATH, cbc.buf,
    508                            strlen (EXPECTED_URI_BASE_PATH)))
    509     {
    510       fprintf (stderr, "curl reports wrong MHD reply body data.\n");
    511       p->queryError = 4;
    512     }
    513     else
    514       p->queryError = 0;
    515   }
    516   curl_easy_cleanup (c);
    517 
    518   return p->queryError;
    519 }
    520 
    521 
    522 #ifdef HAVE_PTHREAD_H
    523 static void *
    524 doCurlQuery (void *param)
    525 {
    526   struct curlQueryParams *p = (struct curlQueryParams *) param;
    527 
    528   (void) doCurlQueryInThread (p);
    529 
    530   return param;
    531 }
    532 
    533 
    534 static void
    535 startThreadCurlQuery (struct curlQueryParams *param)
    536 {
    537   /* thread must reset this value to zero if succeed */
    538   param->queryError = eMarker;
    539 
    540   if (0 != pthread_create (&param->queryThread, NULL, &doCurlQuery,
    541                            (void *) param))
    542     externalErrorExitDesc ("pthread_create() failed");
    543 }
    544 
    545 
    546 static unsigned int
    547 finishThreadCurlQuery (struct curlQueryParams *param)
    548 {
    549   struct curlQueryParams *result;
    550 
    551   if (0 != pthread_join (param->queryThread, (void **) &result))
    552     externalErrorExitDesc ("pthread_join() failed");
    553 
    554   if (param != result)
    555     abort (); /* Test used in wrong way */
    556 
    557   if (eMarker == param->queryError)
    558     abort (); /* Test used in wrong way */
    559 
    560   return result->queryError;
    561 }
    562 
    563 
    564 /* Perform test queries and shut down MHD daemon */
    565 static unsigned int
    566 performTestQueries (struct MHD_Daemon *d, uint16_t d_port)
    567 {
    568   struct curlQueryParams qParam;
    569   struct addConnParam aParam;
    570   uint16_t a_port;      /* Additional listening socket port */
    571   unsigned int ret = 0; /* Return value */
    572 
    573   qParam.queryPath = "http://127.0.0.1" EXPECTED_URI_FULL_PATH;
    574   a_port = 0; /* auto-assign */
    575 
    576   aParam.d = d;
    577   aParam.lstn_sk = createListeningSocket (&a_port); /* Sets a_port */
    578 
    579   /* Test of adding connection in the same thread */
    580   qParam.queryError = eMarker; /* to be zeroed in new thread */
    581   qParam.queryPort = a_port;   /* Connect to additional socket */
    582   startThreadCurlQuery (&qParam);
    583   ret |= doAcceptAndAddConnInThread (&aParam);
    584   ret |= finishThreadCurlQuery (&qParam);
    585 
    586   if (! no_listen)
    587   {
    588     /* Test of the daemon itself can accept and process new connection. */
    589     ret <<= 3;                   /* Remember errors for each step */
    590     qParam.queryPort = d_port;   /* Connect to the daemon */
    591     ret |= doCurlQueryInThread (&qParam);
    592   }
    593 
    594   /* Test of adding connection in an external thread */
    595   ret <<= 3;                   /* Remember errors for each step */
    596   aParam.result = eMarker;     /* to be zeroed in new thread */
    597   qParam.queryPort = a_port;   /* Connect to the daemon */
    598   startThreadAddConn (&aParam);
    599   ret |= doCurlQueryInThread (&qParam);
    600   ret |= finishThreadAddConn (&aParam);
    601 
    602   (void) MHD_socket_close_ (aParam.lstn_sk);
    603   MHD_stop_daemon (d);
    604 
    605   return ret;
    606 }
    607 
    608 
    609 /* Perform test for cleanup and shutdown MHD daemon */
    610 static unsigned int
    611 performTestCleanup (struct MHD_Daemon *d, unsigned int num_queries)
    612 {
    613   struct curlQueryParams *qParamList;
    614   struct addConnParam aParam;
    615   MHD_socket lstn_sk;   /* Additional listening socket */
    616   MHD_socket *clntSkList;
    617   uint16_t a_port;      /* Additional listening socket port */
    618   unsigned int i;
    619   unsigned int ret = 0; /* Return value */
    620 
    621   a_port = 0; /* auto-assign */
    622 
    623   if (0 >= num_queries)
    624     abort (); /* Test's API violation */
    625 
    626   lstn_sk = createListeningSocket (&a_port); /* Sets a_port */
    627 
    628   qParamList = malloc (sizeof(struct curlQueryParams) * num_queries);
    629   clntSkList = malloc (sizeof(MHD_socket) * num_queries);
    630   if ((NULL == qParamList) || (NULL == clntSkList))
    631     externalErrorExitDesc ("malloc failed");
    632 
    633   /* Start CURL queries */
    634   for (i = 0; i < num_queries; i++)
    635   {
    636     qParamList[i].queryPath = "http://127.0.0.1" EXPECTED_URI_FULL_PATH;
    637     qParamList[i].queryError = 0;
    638     qParamList[i].queryPort = a_port;
    639 
    640     startThreadCurlQuery (qParamList + i);
    641   }
    642 
    643   /* Accept and add required number of client sockets */
    644   aParam.d = d;
    645   aParam.lstn_sk = lstn_sk;
    646   for (i = 0; i < num_queries; i++)
    647   {
    648     aParam.clent_sk = MHD_INVALID_SOCKET;
    649     ret |= doAcceptAndAddConnInThread (&aParam);
    650     clntSkList[i] = aParam.clent_sk;
    651   }
    652 
    653   /* Stop daemon while some of new connection are not yet
    654    * processed because of slow response to the first queries. */
    655   MHD_stop_daemon (d);
    656   (void) MHD_socket_close_ (aParam.lstn_sk);
    657 
    658   /* Check whether all client sockets were closed by MHD.
    659    * Closure of socket by MHD indicate valid cleanup performed. */
    660   for (i = 0; i < num_queries; i++)
    661   {
    662     if (MHD_INVALID_SOCKET != clntSkList[i])
    663     { /* Check whether socket could be closed one more time. */
    664       if (MHD_socket_close_ (clntSkList[i]))
    665       {
    666         ret |= 2;
    667         fprintf (stderr, "Client socket was not closed by MHD during" \
    668                  "cleanup process.\n");
    669       }
    670     }
    671   }
    672 
    673   /* Wait for CURL threads to complete. */
    674   /* Ignore soft CURL errors as many connection shouldn't get any response.
    675    * Hard failures are detected in processing function. */
    676   for (i = 0; i < num_queries; i++)
    677     (void) finishThreadCurlQuery (qParamList + i);
    678 
    679   free (clntSkList);
    680   free (qParamList);
    681 
    682   return ret;
    683 }
    684 
    685 
    686 #endif /* HAVE_PTHREAD_H */
    687 
    688 enum testMhdThreadsType
    689 {
    690   testMhdThreadExternal              = 0,
    691   testMhdThreadInternal              = MHD_USE_INTERNAL_POLLING_THREAD,
    692   testMhdThreadInternalPerConnection = MHD_USE_THREAD_PER_CONNECTION
    693                                        | MHD_USE_INTERNAL_POLLING_THREAD,
    694   testMhdThreadInternalPool
    695 };
    696 
    697 enum testMhdPollType
    698 {
    699   testMhdPollBySelect = 0,
    700   testMhdPollByPoll   = MHD_USE_POLL,
    701   testMhdPollByEpoll  = MHD_USE_EPOLL,
    702   testMhdPollAuto     = MHD_USE_AUTO
    703 };
    704 
    705 /* Get number of threads for thread pool depending
    706  * on used poll function and test type. */
    707 static unsigned int
    708 testNumThreadsForPool (enum testMhdPollType pollType)
    709 {
    710   unsigned int numThreads = MHD_CPU_COUNT;
    711   if (! cleanup_test)
    712     return numThreads; /* No practical limit for non-cleanup test */
    713   if (CLEANUP_MAX_DAEMONS (sys_max_fds) < numThreads)
    714     numThreads = CLEANUP_MAX_DAEMONS (sys_max_fds);
    715   if ((testMhdPollBySelect == pollType) &&
    716       (CLEANUP_MAX_DAEMONS (FD_SETSIZE) < numThreads))
    717     numThreads = CLEANUP_MAX_DAEMONS (FD_SETSIZE);
    718 
    719   if (2 > numThreads)
    720     abort ();
    721   return (unsigned int) numThreads;
    722 }
    723 
    724 
    725 static struct MHD_Daemon *
    726 startTestMhdDaemon (enum testMhdThreadsType thrType,
    727                     enum testMhdPollType pollType, uint16_t *pport)
    728 {
    729   struct MHD_Daemon *d;
    730   const union MHD_DaemonInfo *dinfo;
    731 
    732   if ( (0 == *pport) &&
    733        (MHD_NO == MHD_is_feature_supported (MHD_FEATURE_AUTODETECT_BIND_PORT)) )
    734   {
    735     *pport = 1550;
    736     if (oneone)
    737       *pport += 1;
    738     if (no_listen)
    739       *pport += 2;
    740     if (cleanup_test)
    741       *pport += 4;
    742   }
    743 
    744   switch (thrType)
    745   {
    746   case testMhdThreadExternal:
    747     d = MHD_start_daemon (((unsigned int) thrType) | ((unsigned int) pollType)
    748                           | MHD_USE_NO_THREAD_SAFETY
    749                           | (no_listen ? MHD_USE_NO_LISTEN_SOCKET : 0)
    750                           | MHD_USE_ERROR_LOG,
    751                           *pport, NULL, NULL,
    752                           &ahc_echo, NULL,
    753                           MHD_OPTION_URI_LOG_CALLBACK, &log_cb, NULL,
    754                           MHD_OPTION_APP_FD_SETSIZE, (int) FD_SETSIZE,
    755                           MHD_OPTION_END);
    756     break;
    757   case testMhdThreadInternalPool:
    758     d = MHD_start_daemon (MHD_USE_INTERNAL_POLLING_THREAD
    759                           | ((unsigned int) pollType)
    760                           | MHD_USE_ITC
    761                           | (no_listen ? MHD_USE_NO_LISTEN_SOCKET : 0)
    762                           | MHD_USE_ERROR_LOG,
    763                           *pport, NULL, NULL,
    764                           &ahc_echo, NULL,
    765                           MHD_OPTION_THREAD_POOL_SIZE,
    766                           testNumThreadsForPool (pollType),
    767                           MHD_OPTION_URI_LOG_CALLBACK, &log_cb, NULL,
    768                           MHD_OPTION_END);
    769     break;
    770   case testMhdThreadInternal:
    771   case testMhdThreadInternalPerConnection:
    772     d = MHD_start_daemon (((unsigned int) thrType) | ((unsigned int) pollType)
    773                           | MHD_USE_ITC
    774                           | (no_listen ? MHD_USE_NO_LISTEN_SOCKET : 0)
    775                           | MHD_USE_ERROR_LOG,
    776                           *pport, NULL, NULL,
    777                           &ahc_echo, NULL,
    778                           MHD_OPTION_URI_LOG_CALLBACK, &log_cb, NULL,
    779                           MHD_OPTION_END);
    780     break;
    781   default:
    782     abort ();
    783     break;
    784   }
    785 
    786   if (NULL == d)
    787   {
    788     fprintf (stderr, "Failed to start MHD daemon, errno=%d.\n", errno);
    789     abort ();
    790   }
    791 
    792   if ((! no_listen) && (0 == *pport))
    793   {
    794     dinfo = MHD_get_daemon_info (d, MHD_DAEMON_INFO_BIND_PORT);
    795     if ((NULL == dinfo) || (0 == dinfo->port) )
    796     {
    797       fprintf (stderr, "MHD_get_daemon_info() failed.\n");
    798       abort ();
    799     }
    800     *pport = dinfo->port;
    801   }
    802 
    803   return d;
    804 }
    805 
    806 
    807 /* Test runners */
    808 
    809 
    810 static unsigned int
    811 testExternalGet (void)
    812 {
    813   struct MHD_Daemon *d;
    814   CURL *c_d;
    815   char buf_d[2048];
    816   struct CBC cbc_d;
    817   CURL *c_a;
    818   char buf_a[2048];
    819   struct CBC cbc_a;
    820   CURLM *multi;
    821   time_t start;
    822   struct timeval tv;
    823   uint16_t d_port = global_port; /* Daemon's port */
    824   uint16_t a_port = 0;      /* Additional listening socket port */
    825   struct addConnParam aParam;
    826   unsigned int ret = 0;     /* Return value of the test */
    827   const int c_no_listen = no_listen; /* Local const value to mute analyzer */
    828 
    829   d = startTestMhdDaemon (testMhdThreadExternal, testMhdPollBySelect, &d_port);
    830 
    831   aParam.d = d;
    832   aParam.lstn_sk = createListeningSocket (&a_port);
    833 
    834   multi = NULL;
    835   cbc_d.buf = buf_d;
    836   cbc_d.size = sizeof(buf_d);
    837   cbc_d.pos = 0;
    838   cbc_a.buf = buf_a;
    839   cbc_a.size = sizeof(buf_a);
    840   cbc_a.pos = 0;
    841 
    842   if (cleanup_test)
    843     abort (); /* Not possible with "external poll" as connections are directly
    844                  added to the daemon processing in the mode. */
    845 
    846   if (! c_no_listen)
    847     c_d = curlEasyInitForTest ("http://127.0.0.1" EXPECTED_URI_FULL_PATH,
    848                                d_port, &cbc_d);
    849   else
    850     c_d = NULL; /* To mute compiler warning only */
    851 
    852   c_a = curlEasyInitForTest ("http://127.0.0.1" EXPECTED_URI_FULL_PATH,
    853                              a_port, &cbc_a);
    854 
    855   multi = curl_multi_init ();
    856   if (multi == NULL)
    857   {
    858     fprintf (stderr, "curl_multi_init() failed.\n");
    859     _exit (99);
    860   }
    861   if (! c_no_listen)
    862   {
    863     if (CURLM_OK != curl_multi_add_handle (multi, c_d))
    864     {
    865       fprintf (stderr, "curl_multi_add_handle() failed.\n");
    866       _exit (99);
    867     }
    868   }
    869 
    870   if (CURLM_OK != curl_multi_add_handle (multi, c_a))
    871   {
    872     fprintf (stderr, "curl_multi_add_handle() failed.\n");
    873     _exit (99);
    874   }
    875 
    876   start = time (NULL);
    877   while (time (NULL) - start <= TIMEOUTS_VAL)
    878   {
    879     fd_set rs;
    880     fd_set ws;
    881     fd_set es;
    882     MHD_socket maxMhdSk;
    883     int maxCurlSk;
    884     int running;
    885 
    886     maxMhdSk = MHD_INVALID_SOCKET;
    887     maxCurlSk = -1;
    888     FD_ZERO (&rs);
    889     FD_ZERO (&ws);
    890     FD_ZERO (&es);
    891     curl_multi_perform (multi, &running);
    892     if (0 == running)
    893     {
    894       struct CURLMsg *msg;
    895       int msgLeft;
    896       int totalMsgs = 0;
    897       do
    898       {
    899         msg = curl_multi_info_read (multi, &msgLeft);
    900         if (NULL == msg)
    901         {
    902           fprintf (stderr, "curl_multi_info_read failed, NULL returned.\n");
    903           _exit (99);
    904         }
    905         totalMsgs++;
    906         if (CURLMSG_DONE == msg->msg)
    907         {
    908           if (CURLE_OK != msg->data.result)
    909           {
    910             fprintf (stderr, "curl_multi_info_read failed, error: '%s'\n",
    911                      curl_easy_strerror (msg->data.result));
    912             ret |= 2;
    913           }
    914         }
    915       } while (msgLeft > 0);
    916       if ((no_listen ? 1 : 2) != totalMsgs)
    917       {
    918         fprintf (stderr,
    919                  "curl_multi_info_read returned wrong "
    920                  "number of results (%d).\n",
    921                  totalMsgs);
    922         _exit (99);
    923       }
    924       break; /* All transfers have finished. */
    925     }
    926     if (CURLM_OK != curl_multi_fdset (multi, &rs, &ws, &es, &maxCurlSk))
    927     {
    928       fprintf (stderr, "curl_multi_fdset() failed.\n");
    929       _exit (99);
    930     }
    931     if (MHD_YES != MHD_get_fdset (d, &rs, &ws, &es, &maxMhdSk))
    932     {
    933       ret |= 8;
    934       break;
    935     }
    936     FD_SET (aParam.lstn_sk, &rs);
    937     if (maxMhdSk < aParam.lstn_sk)
    938       maxMhdSk = aParam.lstn_sk;
    939     tv.tv_sec = 0;
    940     tv.tv_usec = 1000;
    941 #ifdef MHD_POSIX_SOCKETS
    942     if (maxMhdSk > maxCurlSk)
    943       maxCurlSk = maxMhdSk;
    944 #endif /* MHD_POSIX_SOCKETS */
    945     if (-1 == select (maxCurlSk + 1, &rs, &ws, &es, &tv))
    946     {
    947 #ifdef MHD_POSIX_SOCKETS
    948       if (EINTR != errno)
    949       {
    950         fprintf (stderr, "Unexpected select() error: %d. Line: %d\n",
    951                  (int) errno, __LINE__);
    952         fflush (stderr);
    953         exit (99);
    954       }
    955 #else
    956       if ((WSAEINVAL != WSAGetLastError ()) ||
    957           (0 != rs.fd_count) || (0 != ws.fd_count) || (0 != es.fd_count) )
    958       {
    959         fprintf (stderr, "Unexpected select() error: %d. Line: %d\n",
    960                  (int) WSAGetLastError (), __LINE__);
    961         fflush (stderr);
    962         exit (99);
    963       }
    964       Sleep (1);
    965 #endif
    966     }
    967     if (FD_ISSET (aParam.lstn_sk, &rs))
    968       ret |= doAcceptAndAddConnInThread (&aParam);
    969 
    970     if (MHD_YES != MHD_run_from_select (d, &rs, &ws, &es))
    971     {
    972       fprintf (stderr, "MHD_run_from_select() failed.\n");
    973       ret |= 1;
    974       break;
    975     }
    976   }
    977 
    978   MHD_stop_daemon (d);
    979   (void) MHD_socket_close_ (aParam.lstn_sk);
    980 
    981   if (! c_no_listen)
    982   {
    983     curl_multi_remove_handle (multi, c_d);
    984     curl_easy_cleanup (c_d);
    985     if (cbc_d.pos != strlen ("/hello_world"))
    986     {
    987       fprintf (stderr,
    988                "curl reports wrong size of MHD reply body data at line %d.\n",
    989                __LINE__);
    990       ret |= 4;
    991     }
    992     if (0 != strncmp ("/hello_world", cbc_d.buf, strlen ("/hello_world")))
    993     {
    994       fprintf (stderr, "curl reports wrong MHD reply body data at line %d.\n",
    995                __LINE__);
    996       ret |= 4;
    997     }
    998   }
    999   curl_multi_remove_handle (multi, c_a);
   1000   curl_easy_cleanup (c_a);
   1001   curl_multi_cleanup (multi);
   1002   if (cbc_a.pos != strlen ("/hello_world"))
   1003   {
   1004     fprintf (stderr,
   1005              "curl reports wrong size of MHD reply body data at line %d.\n",
   1006              __LINE__);
   1007     ret |= 4;
   1008   }
   1009   if (0 != strncmp ("/hello_world", cbc_a.buf, strlen ("/hello_world")))
   1010   {
   1011     fprintf (stderr, "curl reports wrong MHD reply body data at line %d.\n",
   1012              __LINE__);
   1013     ret |= 4;
   1014   }
   1015   return ret;
   1016 }
   1017 
   1018 
   1019 #ifdef HAVE_PTHREAD_H
   1020 static unsigned int
   1021 testInternalGet (enum testMhdPollType pollType)
   1022 {
   1023   struct MHD_Daemon *d;
   1024   uint16_t d_port = global_port; /* Daemon's port */
   1025 
   1026   d = startTestMhdDaemon (testMhdThreadInternal, pollType,
   1027                           &d_port);
   1028   if (cleanup_test)
   1029     return performTestCleanup (d, CLEANUP_NUM_REQS_PER_DAEMON);
   1030 
   1031   return performTestQueries (d, d_port);
   1032 }
   1033 
   1034 
   1035 static unsigned int
   1036 testMultithreadedGet (enum testMhdPollType pollType)
   1037 {
   1038   struct MHD_Daemon *d;
   1039   uint16_t d_port = global_port; /* Daemon's port */
   1040 
   1041   d = startTestMhdDaemon (testMhdThreadInternalPerConnection, pollType,
   1042                           &d_port);
   1043   if (cleanup_test)
   1044     abort (); /* Cannot be tested as main daemon thread cannot be slowed down
   1045                  by slow responses, so it processes all new connections before
   1046                  daemon could be stopped. */
   1047 
   1048   return performTestQueries (d, d_port);
   1049 }
   1050 
   1051 
   1052 static unsigned int
   1053 testMultithreadedPoolGet (enum testMhdPollType pollType)
   1054 {
   1055   struct MHD_Daemon *d;
   1056   uint16_t d_port = global_port; /* Daemon's port */
   1057 
   1058   d = startTestMhdDaemon (testMhdThreadInternalPool, pollType,
   1059                           &d_port);
   1060 
   1061   if (cleanup_test)
   1062     return performTestCleanup (d, CLEANUP_NUM_REQS_PER_DAEMON
   1063                                * testNumThreadsForPool (pollType));
   1064   return performTestQueries (d, d_port);
   1065 }
   1066 
   1067 
   1068 static unsigned int
   1069 testStopRace (enum testMhdPollType pollType)
   1070 {
   1071   struct MHD_Daemon *d;
   1072   uint16_t d_port = global_port; /* Daemon's port */
   1073   uint16_t a_port = 0;           /* Additional listening socket port */
   1074   struct sockaddr_in sin;
   1075   MHD_socket fd1;
   1076   MHD_socket fd2;
   1077   struct addConnParam aParam;
   1078   unsigned int ret = 0;              /* Return value of the test */
   1079 
   1080   d = startTestMhdDaemon (testMhdThreadInternal, pollType,
   1081                           &d_port);
   1082 
   1083   if (! no_listen)
   1084   {
   1085     fd1 = socket (PF_INET, SOCK_STREAM, IPPROTO_TCP);
   1086     if (MHD_INVALID_SOCKET == fd1)
   1087       externalErrorExitDesc ("socket() failed");
   1088 
   1089     memset (&sin, 0, sizeof(sin));
   1090     sin.sin_family = AF_INET;
   1091     sin.sin_port = htons (d_port);
   1092     sin.sin_addr.s_addr = htonl (INADDR_LOOPBACK);
   1093     if (connect (fd1, (struct sockaddr *) (&sin), sizeof(sin)) < 0)
   1094       externalErrorExitDesc ("socket() failed");
   1095   }
   1096   else
   1097     fd1 = MHD_INVALID_SOCKET;
   1098 
   1099   aParam.d = d;
   1100   aParam.lstn_sk = createListeningSocket (&a_port); /* Sets a_port */
   1101   startThreadAddConn (&aParam);
   1102 
   1103   fd2 = socket (PF_INET, SOCK_STREAM, IPPROTO_TCP);
   1104   if (MHD_INVALID_SOCKET == fd2)
   1105     externalErrorExitDesc ("socket() failed");
   1106   memset (&sin, 0, sizeof(sin));
   1107   sin.sin_family = AF_INET;
   1108   sin.sin_port = htons (a_port);
   1109   sin.sin_addr.s_addr = htonl (INADDR_LOOPBACK);
   1110   if (connect (fd2, (struct sockaddr *) (&sin), sizeof(sin)) < 0)
   1111     externalErrorExitDesc ("socket() failed");
   1112   ret |= finishThreadAddConn (&aParam);
   1113 
   1114   /* Let the thread get going. */
   1115   usleep (500000);
   1116 
   1117   MHD_stop_daemon (d);
   1118 
   1119   if (MHD_INVALID_SOCKET != fd1)
   1120     (void) MHD_socket_close_ (fd1);
   1121   (void) MHD_socket_close_ (aParam.lstn_sk);
   1122   (void) MHD_socket_close_ (fd2);
   1123 
   1124   return ret;
   1125 }
   1126 
   1127 
   1128 #endif /* HAVE_PTHREAD_H */
   1129 
   1130 
   1131 int
   1132 main (int argc, char *const *argv)
   1133 {
   1134   unsigned int errorCount = 0;
   1135   unsigned int test_result = 0;
   1136   int verbose = 0;
   1137 
   1138   if ((NULL == argv) || (0 == argv[0]))
   1139     return 99;
   1140   oneone = has_in_name (argv[0], "11");
   1141   /* Whether to test MHD daemons without listening socket. */
   1142   no_listen = has_in_name (argv[0], "_nolisten");
   1143   /* Whether to test for correct final cleanup instead of
   1144    * of test of normal processing. */
   1145   cleanup_test = has_in_name (argv[0], "_cleanup");
   1146   /* There are almost nothing that could be tested externally
   1147    * for final cleanup. Cleanup test actually just tests that
   1148    * all added client connections were closed by MHD and
   1149    * nothing fails or crashes when final cleanup is performed.
   1150    * Mostly useful when configured with '--enable-asserts. */
   1151   slow_reply = cleanup_test;
   1152   ignore_response_errors = cleanup_test;
   1153 #ifndef HAVE_PTHREAD_H
   1154   if (cleanup_test)
   1155     return 77; /* Cannot run without threads */
   1156 #endif /* HAVE_PTHREAD_H */
   1157   verbose = ! (has_param (argc, argv, "-q") ||
   1158                has_param (argc, argv, "--quiet") ||
   1159                has_param (argc, argv, "-s") ||
   1160                has_param (argc, argv, "--silent"));
   1161   if (cleanup_test)
   1162   {
   1163 #ifndef _WIN32
   1164     /* Find system limit for number of open FDs. */
   1165 #if defined(HAVE_SYSCONF) && defined(_SC_OPEN_MAX)
   1166     sys_max_fds = sysconf (_SC_OPEN_MAX) > 500000 ?
   1167                   500000 : (int) sysconf (_SC_OPEN_MAX);
   1168 #else  /* ! HAVE_SYSCONF || ! _SC_OPEN_MAX */
   1169     sys_max_fds = -1;
   1170 #endif /* ! HAVE_SYSCONF || ! _SC_OPEN_MAX */
   1171     if (0 > sys_max_fds)
   1172     {
   1173 #if defined(OPEN_MAX) && (0 < ((OPEN_MAX) +1))
   1174       sys_max_fds = OPEN_MAX > 500000 ? 500000 : (int) OPEN_MAX;
   1175 #else  /* ! OPEN_MAX */
   1176       sys_max_fds = 256; /* Use reasonable value */
   1177 #endif /* ! OPEN_MAX */
   1178       if (2 > CLEANUP_MAX_DAEMONS (sys_max_fds))
   1179         return 77; /* Multithreaded test cannot be run */
   1180     }
   1181 #else  /* _WIN32 */
   1182     sys_max_fds = 120; /* W32 has problems with ports exhaust */
   1183 #endif /* _WIN32 */
   1184   }
   1185   if (0 != curl_global_init (CURL_GLOBAL_WIN32))
   1186     return 99;
   1187   /* Could be set to non-zero value to enforce using specific port
   1188    * in the test */
   1189   global_port = 0;
   1190   if (! cleanup_test)
   1191   {
   1192     test_result = testExternalGet ();
   1193     if (test_result)
   1194       fprintf (stderr, "FAILED: testExternalGet () - %u.\n", test_result);
   1195     else if (verbose)
   1196       printf ("PASSED: testExternalGet ().\n");
   1197     errorCount += test_result;
   1198   }
   1199 #ifdef HAVE_PTHREAD_H
   1200   if (MHD_YES == MHD_is_feature_supported (MHD_FEATURE_THREADS))
   1201   {
   1202     test_result = testInternalGet (testMhdPollBySelect);
   1203     if (test_result)
   1204       fprintf (stderr, "FAILED: testInternalGet (testMhdPollBySelect) - %u.\n",
   1205                test_result);
   1206     else if (verbose)
   1207       printf ("PASSED: testInternalGet (testMhdPollBySelect).\n");
   1208     errorCount += test_result;
   1209     test_result = testMultithreadedPoolGet (testMhdPollBySelect);
   1210     if (test_result)
   1211       fprintf (stderr,
   1212                "FAILED: testMultithreadedPoolGet (testMhdPollBySelect) - %u.\n",
   1213                test_result);
   1214     else if (verbose)
   1215       printf ("PASSED: testMultithreadedPoolGet (testMhdPollBySelect).\n");
   1216     errorCount += test_result;
   1217     if (! cleanup_test)
   1218     {
   1219       test_result = testMultithreadedGet (testMhdPollBySelect);
   1220       if (test_result)
   1221         fprintf (stderr,
   1222                  "FAILED: testMultithreadedGet (testMhdPollBySelect) - %u.\n",
   1223                  test_result);
   1224       else if (verbose)
   1225         printf ("PASSED: testMultithreadedGet (testMhdPollBySelect).\n");
   1226       errorCount += test_result;
   1227       test_result = testStopRace (testMhdPollBySelect);
   1228       if (test_result)
   1229         fprintf (stderr, "FAILED: testStopRace (testMhdPollBySelect) - %u.\n",
   1230                  test_result);
   1231       else if (verbose)
   1232         printf ("PASSED: testStopRace (testMhdPollBySelect).\n");
   1233       errorCount += test_result;
   1234     }
   1235     if (MHD_YES == MHD_is_feature_supported (MHD_FEATURE_POLL))
   1236     {
   1237       test_result = testInternalGet (testMhdPollByPoll);
   1238       if (test_result)
   1239         fprintf (stderr, "FAILED: testInternalGet (testMhdPollByPoll) - %u.\n",
   1240                  test_result);
   1241       else if (verbose)
   1242         printf ("PASSED: testInternalGet (testMhdPollByPoll).\n");
   1243       errorCount += test_result;
   1244       test_result = testMultithreadedPoolGet (testMhdPollByPoll);
   1245       if (test_result)
   1246         fprintf (stderr,
   1247                  "FAILED: testMultithreadedPoolGet (testMhdPollByPoll) - %u.\n",
   1248                  test_result);
   1249       else if (verbose)
   1250         printf ("PASSED: testMultithreadedPoolGet (testMhdPollByPoll).\n");
   1251       errorCount += test_result;
   1252       if (! cleanup_test)
   1253       {
   1254         test_result = testMultithreadedGet (testMhdPollByPoll);
   1255         if (test_result)
   1256           fprintf (stderr,
   1257                    "FAILED: testMultithreadedGet (testMhdPollByPoll) - %u.\n",
   1258                    test_result);
   1259         else if (verbose)
   1260           printf ("PASSED: testMultithreadedGet (testMhdPollByPoll).\n");
   1261         errorCount += test_result;
   1262         test_result = testStopRace (testMhdPollByPoll);
   1263         if (test_result)
   1264           fprintf (stderr, "FAILED: testStopRace (testMhdPollByPoll) - %u.\n",
   1265                    test_result);
   1266         else if (verbose)
   1267           printf ("PASSED: testStopRace (testMhdPollByPoll).\n");
   1268         errorCount += test_result;
   1269       }
   1270     }
   1271     if (MHD_YES == MHD_is_feature_supported (MHD_FEATURE_EPOLL))
   1272     {
   1273       test_result = testInternalGet (testMhdPollByEpoll);
   1274       if (test_result)
   1275         fprintf (stderr, "FAILED: testInternalGet (testMhdPollByEpoll) - %u.\n",
   1276                  test_result);
   1277       else if (verbose)
   1278         printf ("PASSED: testInternalGet (testMhdPollByEpoll).\n");
   1279       errorCount += test_result;
   1280       test_result = testMultithreadedPoolGet (testMhdPollByEpoll);
   1281       if (test_result)
   1282         fprintf (stderr,
   1283                  "FAILED: testMultithreadedPoolGet (testMhdPollByEpoll) - %u.\n",
   1284                  test_result);
   1285       else if (verbose)
   1286         printf ("PASSED: testMultithreadedPoolGet (testMhdPollByEpoll).\n");
   1287       errorCount += test_result;
   1288     }
   1289   }
   1290 #endif /* HAVE_PTHREAD_H */
   1291   if (0 != errorCount)
   1292     fprintf (stderr,
   1293              "Error (code: %u)\n",
   1294              errorCount);
   1295   else if (verbose)
   1296     printf ("All tests passed.\n");
   1297   curl_global_cleanup ();
   1298   return (errorCount == 0) ? 0 : 1;       /* 0 == pass */
   1299 }