libmicrohttpd

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

test_add_conn.c (37494B)


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