libmicrohttpd

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

test_concurrent_stop.c (10063B)


      1 /*
      2      This file is part of libmicrohttpd
      3      Copyright (C) 2007, 2009, 2011, 2015, 2016 Christian Grothoff
      4      Copyright (C) 2014-2022 Evgeny Grin (Karlson2k)
      5 
      6      libmicrohttpd is free software; you can redistribute it and/or modify
      7      it under the terms of the GNU General Public License as published
      8      by the Free Software Foundation; either version 3, or (at your
      9      option) any later version.
     10 
     11      libmicrohttpd is distributed in the hope that it will be useful, but
     12      WITHOUT ANY WARRANTY; without even the implied warranty of
     13      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     14      General Public License for more details.
     15 
     16      You should have received a copy of the GNU General Public License
     17      along with libmicrohttpd; see the file COPYING.  If not, write to the
     18      Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
     19      Boston, MA 02110-1301, USA.
     20 */
     21 
     22 /**
     23  * @file test_concurrent_stop.c
     24  * @brief test stopping server while concurrent GETs are ongoing
     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 <pthread.h>
     36 
     37 /* Turn any MHD_PANIC() or failing mhd_assert() reached from this
     38    test into a marked, classifiable test error (TESTING.md, P5). */
     39 #include "mhd_panic_tripwire.h"
     40 
     41 #if defined(MHD_CPU_COUNT) && (MHD_CPU_COUNT + 0) < 2
     42 #undef MHD_CPU_COUNT
     43 #endif
     44 #if ! defined(MHD_CPU_COUNT)
     45 #define MHD_CPU_COUNT 2
     46 #endif
     47 
     48 /**
     49  * How many requests do we do in parallel?
     50  */
     51 #if SIZEOF_SIZE_T >= 8 || MHD_CPU_COUNT < 8
     52 #  define PAR (MHD_CPU_COUNT * 4)
     53 #elif MHD_CPU_COUNT < 16
     54 /* Limit load */
     55 #  define PAR (MHD_CPU_COUNT * 2)
     56 #else
     57 /* Limit load */
     58 #  define PAR (MHD_CPU_COUNT * 1)
     59 #endif
     60 
     61 /**
     62  * Do we use HTTP 1.1?
     63  */
     64 static int oneone;
     65 
     66 /**
     67  * Response to return (re-used).
     68  */
     69 static struct MHD_Response *response;
     70 
     71 /**
     72  * Continue generating new requests?
     73  */
     74 static volatile int continue_requesting;
     75 
     76 /**
     77  * Continue waiting in watchdog thread?
     78  */
     79 static volatile int watchdog_continue;
     80 
     81 static const char *watchdog_obj;
     82 
     83 /**
     84  * Indicate that client detected error
     85  */
     86 static volatile CURLcode client_error;
     87 
     88 static void *
     89 thread_watchdog (void *param)
     90 {
     91   int seconds_passed;
     92   const int timeout_val = (int) (intptr_t) param;
     93 
     94   seconds_passed = 0;
     95   while (watchdog_continue) /* Poor threads sync, but works for testing. */
     96   {
     97     if (0 == sleep (1))   /* Poor accuracy, but enough for testing. */
     98       seconds_passed++;
     99     if (timeout_val < seconds_passed)
    100     {
    101       fprintf (stderr, "%s timeout expired.\n", watchdog_obj ? watchdog_obj :
    102                "Watchdog");
    103       fflush (stderr);
    104       _exit (16);
    105     }
    106   }
    107   return NULL;
    108 }
    109 
    110 
    111 static pthread_t watchdog_tid;
    112 
    113 static void
    114 start_watchdog (int timeout, const char *obj_name)
    115 {
    116   watchdog_continue = 1;
    117   watchdog_obj = obj_name;
    118   if (0 != pthread_create (&watchdog_tid, NULL, &thread_watchdog,
    119                            (void *) (intptr_t) timeout))
    120   {
    121     fprintf (stderr, "Failed to start watchdog.\n");
    122     _exit (99);
    123   }
    124 }
    125 
    126 
    127 static void
    128 stop_watchdog (void)
    129 {
    130   watchdog_continue = 0;
    131   if (0 != pthread_join (watchdog_tid, NULL))
    132   {
    133     fprintf (stderr, "Failed to stop watchdog.\n");
    134     _exit (99);
    135   }
    136 }
    137 
    138 
    139 static size_t
    140 copyBuffer (void *ptr,
    141             size_t size, size_t nmemb,
    142             void *ctx)
    143 {
    144   (void) ptr; (void) ctx;  /* Unused. Silent compiler warning. */
    145   return size * nmemb;
    146 }
    147 
    148 
    149 static enum MHD_Result
    150 ahc_echo (void *cls,
    151           struct MHD_Connection *connection,
    152           const char *url,
    153           const char *method,
    154           const char *version,
    155           const char *upload_data,
    156           size_t *upload_data_size,
    157           void **req_cls)
    158 {
    159   static int marker;
    160   enum MHD_Result ret;
    161   (void) cls;
    162   (void) url; (void) version;                      /* Unused. Silent compiler warning. */
    163   (void) upload_data; (void) upload_data_size;     /* Unused. Silent compiler warning. */
    164 
    165   if (0 != strcmp (MHD_HTTP_METHOD_GET, method))
    166     return MHD_NO;              /* unexpected method */
    167   if (&marker != *req_cls)
    168   {
    169     *req_cls = &marker;
    170     return MHD_YES;
    171   }
    172   *req_cls = NULL;
    173   ret = MHD_queue_response (connection,
    174                             MHD_HTTP_OK,
    175                             response);
    176   if (ret == MHD_NO)
    177     abort ();
    178   return ret;
    179 }
    180 
    181 
    182 static void *
    183 thread_gets (void *param)
    184 {
    185   CURL *c;
    186   CURLcode errornum;
    187   char *const url = (char *) param;
    188 
    189   c = NULL;
    190   c = curl_easy_init ();
    191   if (NULL == c)
    192   {
    193     fprintf (stderr, "curl_easy_init failed.\n");
    194     _exit (99);
    195   }
    196   curl_easy_setopt (c, CURLOPT_URL, url);
    197   curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
    198   curl_easy_setopt (c, CURLOPT_WRITEDATA, NULL);
    199   curl_easy_setopt (c, CURLOPT_FAILONERROR, 1L);
    200   curl_easy_setopt (c, CURLOPT_TIMEOUT, 2L);
    201   if (oneone)
    202     curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
    203   else
    204     curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
    205   curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 2L);
    206   /* NOTE: use of CONNECTTIMEOUT without also
    207      setting NOSIGNAL results in really weird
    208      crashes on my system! */
    209   curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1L);
    210   while (continue_requesting)
    211   {
    212     errornum = curl_easy_perform (c);
    213     if (CURLE_OK != errornum)
    214     {
    215       curl_easy_cleanup (c);
    216       client_error = errornum;
    217       return NULL;
    218     }
    219   }
    220   curl_easy_cleanup (c);
    221   return NULL;
    222 }
    223 
    224 
    225 static void *
    226 do_gets (void *param)
    227 {
    228   int j;
    229   pthread_t par[PAR];
    230   char url[64];
    231   uint16_t port = (uint16_t) (intptr_t) param;
    232 
    233   snprintf (url,
    234             sizeof (url),
    235             "http://127.0.0.1:%u/hello_world",
    236             (unsigned int) port);
    237 
    238   for (j = 0; j < PAR; j++)
    239   {
    240     if (0 != pthread_create (&par[j], NULL, &thread_gets, (void *) url))
    241     {
    242       fprintf (stderr, "pthread_create failed.\n");
    243       continue_requesting = 0;
    244       for (j--; j >= 0; j--)
    245       {
    246         pthread_join (par[j], NULL);
    247       }
    248       _exit (99);
    249     }
    250   }
    251   (void) sleep (1);
    252   for (j = 0; j < PAR; j++)
    253   {
    254     pthread_join (par[j], NULL);
    255   }
    256   return NULL;
    257 }
    258 
    259 
    260 static pthread_t
    261 start_gets (uint16_t port)
    262 {
    263   pthread_t tid;
    264   continue_requesting = 1;
    265   if (0 != pthread_create (&tid, NULL, &do_gets, (void *) (intptr_t) port))
    266   {
    267     fprintf (stderr, "pthread_create failed.\n");
    268     _exit (99);
    269   }
    270   return tid;
    271 }
    272 
    273 
    274 static unsigned int
    275 testMultithreadedGet (uint16_t port,
    276                       uint32_t poll_flag)
    277 {
    278   struct MHD_Daemon *d;
    279   pthread_t p;
    280   unsigned int result;
    281 
    282   result = 0;
    283   d = MHD_start_daemon (MHD_USE_THREAD_PER_CONNECTION
    284                         | MHD_USE_INTERNAL_POLLING_THREAD | MHD_USE_ERROR_LOG
    285                         | (enum MHD_FLAG) poll_flag,
    286                         port,
    287                         NULL, NULL,
    288                         &ahc_echo, NULL,
    289                         MHD_OPTION_END);
    290   if (d == NULL)
    291     return 16;
    292   if (0 == port)
    293   {
    294     const union MHD_DaemonInfo *dinfo;
    295     dinfo = MHD_get_daemon_info (d, MHD_DAEMON_INFO_BIND_PORT);
    296     if ((NULL == dinfo) || (0 == dinfo->port) )
    297     {
    298       MHD_stop_daemon (d); return 32;
    299     }
    300     port = dinfo->port;
    301   }
    302   client_error = CURLE_OK; /* clear client error state */
    303   p = start_gets (port);
    304   (void) sleep (1);
    305   start_watchdog (10, "daemon_stop() in testMultithreadedGet");
    306   if (CURLE_OK != client_error) /* poor sync, but enough for test */
    307   {
    308     result = 64;
    309     fprintf (stderr, "libcurl reported at least one error: \"%s\"\n",
    310              curl_easy_strerror (client_error));
    311   }
    312   MHD_stop_daemon (d);
    313   stop_watchdog ();
    314   continue_requesting = 0;
    315   pthread_join (p, NULL);
    316   return result;
    317 }
    318 
    319 
    320 static unsigned int
    321 testMultithreadedPoolGet (uint16_t port,
    322                           uint32_t poll_flag)
    323 {
    324   struct MHD_Daemon *d;
    325   pthread_t p;
    326   unsigned int result;
    327 
    328   result = 0;
    329   d = MHD_start_daemon (MHD_USE_INTERNAL_POLLING_THREAD | MHD_USE_ERROR_LOG
    330                         | (enum MHD_FLAG) poll_flag,
    331                         port,
    332                         NULL, NULL,
    333                         &ahc_echo, NULL,
    334                         MHD_OPTION_THREAD_POOL_SIZE, MHD_CPU_COUNT,
    335                         MHD_OPTION_END);
    336   if (d == NULL)
    337     return 16;
    338   if (0 == port)
    339   {
    340     const union MHD_DaemonInfo *dinfo;
    341     dinfo = MHD_get_daemon_info (d, MHD_DAEMON_INFO_BIND_PORT);
    342     if ((NULL == dinfo) || (0 == dinfo->port) )
    343     {
    344       MHD_stop_daemon (d); return 32;
    345     }
    346     port = dinfo->port;
    347   }
    348   client_error = CURLE_OK; /* clear client error state */
    349   p = start_gets (port);
    350   (void) sleep (1);
    351   start_watchdog (10, "daemon_stop() in testMultithreadedPoolGet");
    352   if (CURLE_OK != client_error) /* poor sync, but enough for test */
    353   {
    354     result = 64;
    355     fprintf (stderr, "libcurl reported at least one error: \"%s\"\n",
    356              curl_easy_strerror (client_error));
    357   }
    358   MHD_stop_daemon (d);
    359   stop_watchdog ();
    360   continue_requesting = 0;
    361   pthread_join (p, NULL);
    362   return result;
    363 }
    364 
    365 
    366 int
    367 main (int argc, char *const *argv)
    368 {
    369   unsigned int errorCount = 0;
    370   uint16_t port;
    371   (void) argc;   /* Unused. Silent compiler warning. */
    372   (void) argv;   /* Unused. Silent compiler warning. */
    373 
    374   if (MHD_NO != MHD_is_feature_supported (MHD_FEATURE_AUTODETECT_BIND_PORT))
    375     port = 0;
    376   else
    377     port = 1142;
    378 
    379   /* Do reuse connection, otherwise all available local ports may exhausted. */
    380   oneone = 1;
    381 
    382   if ((0 != port) && oneone)
    383     port += 5;
    384   if (0 != curl_global_init (CURL_GLOBAL_WIN32))
    385     return 2;
    386   response = MHD_create_response_from_buffer_copy (strlen ("/hello_world"),
    387                                                    "/hello_world");
    388   errorCount += testMultithreadedGet (port, 0);
    389   if (0 != port)
    390     port++;
    391   errorCount += testMultithreadedPoolGet (port, 0);
    392   MHD_destroy_response (response);
    393   if (errorCount != 0)
    394     fprintf (stderr, "Error (code: %u)\n", errorCount);
    395   curl_global_cleanup ();
    396   return (0 == errorCount) ? 0 : 1;       /* 0 == pass */
    397 }