libmicrohttpd

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

test_concurrent_stop.c (9896B)


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