libmicrohttpd

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

test_iplimit.c (9518B)


      1 /*
      2      This file is part of libmicrohttpd
      3      Copyright (C) 2007 Christian Grothoff
      4      Copyright (C) 2014-2022 Evgeny Grin (Karlson2k)
      5 
      6      libmicrohttpd is free software; you can redistribute it and/or modify
      7      it under the terms of the GNU General Public License as published
      8      by the Free Software Foundation; either version 2, or (at your
      9      option) any later version.
     10 
     11      libmicrohttpd is distributed in the hope that it will be useful, but
     12      WITHOUT ANY WARRANTY; without even the implied warranty of
     13      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     14      General Public License for more details.
     15 
     16      You should have received a copy of the GNU General Public License
     17      along with libmicrohttpd; see the file COPYING.  If not, write to the
     18      Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
     19      Boston, MA 02110-1301, USA.
     20 */
     21 
     22 /**
     23  * @file test_iplimit.c
     24  * @brief  Testcase for libmicrohttpd limits per IP
     25  * @author Christian Grothoff
     26  * @author Karlson2k (Evgeny Grin)
     27  */
     28 
     29 #include "MHD_config.h"
     30 #include "platform.h"
     31 #include <curl/curl.h>
     32 #include <microhttpd.h>
     33 #include <stdlib.h>
     34 #include <string.h>
     35 #include <time.h>
     36 #include "mhd_has_in_name.h"
     37 
     38 /* Turn any MHD_PANIC() or failing mhd_assert() reached from this
     39    test into a marked, classifiable test error (TESTING.md, P5). */
     40 #include "mhd_panic_tripwire.h"
     41 
     42 #ifndef WINDOWS
     43 #include <unistd.h>
     44 #endif
     45 
     46 #ifdef _WIN32
     47 #ifndef WIN32_LEAN_AND_MEAN
     48 #define WIN32_LEAN_AND_MEAN 1
     49 #endif /* !WIN32_LEAN_AND_MEAN */
     50 #include <windows.h>
     51 #endif
     52 
     53 #if defined(MHD_CPU_COUNT) && (MHD_CPU_COUNT + 0) < 2
     54 #undef MHD_CPU_COUNT
     55 #endif
     56 #if ! defined(MHD_CPU_COUNT)
     57 #define MHD_CPU_COUNT 2
     58 #endif
     59 
     60 static int oneone;
     61 
     62 struct CBC
     63 {
     64   char *buf;
     65   size_t pos;
     66   size_t size;
     67 };
     68 
     69 static size_t
     70 copyBuffer (void *ptr, size_t size, size_t nmemb, void *ctx)
     71 {
     72   struct CBC *cbc = ctx;
     73 
     74   if (cbc->pos + size * nmemb > cbc->size)
     75     return 0;                   /* overflow */
     76   memcpy (&cbc->buf[cbc->pos], ptr, size * nmemb);
     77   cbc->pos += size * nmemb;
     78   return size * nmemb;
     79 }
     80 
     81 
     82 static enum MHD_Result
     83 ahc_echo (void *cls,
     84           struct MHD_Connection *connection,
     85           const char *url,
     86           const char *method,
     87           const char *version,
     88           const char *upload_data, size_t *upload_data_size,
     89           void **req_cls)
     90 {
     91   static int ptr;
     92   struct MHD_Response *response;
     93   enum MHD_Result ret;
     94   (void) cls;
     95   (void) version; (void) upload_data; (void) upload_data_size;       /* Unused. Silent compiler warning. */
     96 
     97   if (0 != strcmp (MHD_HTTP_METHOD_GET, method))
     98     return MHD_NO;              /* unexpected method */
     99   if (&ptr != *req_cls)
    100   {
    101     *req_cls = &ptr;
    102     return MHD_YES;
    103   }
    104   *req_cls = NULL;
    105   response = MHD_create_response_from_buffer_copy (strlen (url),
    106                                                    (const void *) url);
    107   ret = MHD_queue_response (connection, MHD_HTTP_OK, response);
    108   MHD_destroy_response (response);
    109   if (ret == MHD_NO)
    110     abort ();
    111   return ret;
    112 }
    113 
    114 
    115 static unsigned int
    116 testMultithreadedGet (void)
    117 {
    118   struct MHD_Daemon *d;
    119   char buf[2048];
    120   int k;
    121   unsigned int success;
    122   unsigned int failure;
    123   uint16_t port;
    124 
    125   if (MHD_NO != MHD_is_feature_supported (MHD_FEATURE_AUTODETECT_BIND_PORT))
    126     port = 0;
    127   else
    128   {
    129     port = 1260;
    130     if (oneone)
    131       port += 5;
    132   }
    133 
    134   /* Test only valid for HTTP/1.1 (uses persistent connections) */
    135   if (! oneone)
    136     return 0;
    137 
    138   d = MHD_start_daemon (MHD_USE_INTERNAL_POLLING_THREAD | MHD_USE_ERROR_LOG,
    139                         port, NULL, NULL,
    140                         &ahc_echo, NULL,
    141                         MHD_OPTION_PER_IP_CONNECTION_LIMIT, (unsigned int) 2,
    142                         MHD_OPTION_END);
    143   if (d == NULL)
    144     return 16;
    145   if (0 == port)
    146   {
    147     const union MHD_DaemonInfo *dinfo;
    148     dinfo = MHD_get_daemon_info (d, MHD_DAEMON_INFO_BIND_PORT);
    149     if ((NULL == dinfo) || (0 == dinfo->port) )
    150     {
    151       MHD_stop_daemon (d); return 32;
    152     }
    153     port = dinfo->port;
    154   }
    155 
    156   for (k = 0; k < 3; ++k)
    157   {
    158     struct CBC cbc[3];
    159     CURL *cenv[3];
    160     int i;
    161 
    162     success = 0;
    163     failure = 0;
    164     for (i = 0; i < 3; ++i)
    165     {
    166       CURL *c;
    167       CURLcode errornum;
    168 
    169       cenv[i] = c = curl_easy_init ();
    170       cbc[i].buf = buf;
    171       cbc[i].size = 2048;
    172       cbc[i].pos = 0;
    173 
    174       curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1/hello_world");
    175       curl_easy_setopt (c, CURLOPT_PORT, (long) port);
    176       curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
    177       curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc[i]);
    178       curl_easy_setopt (c, CURLOPT_FAILONERROR, 1L);
    179       curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L);
    180       curl_easy_setopt (c, CURLOPT_FORBID_REUSE, 0L);
    181       curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
    182       curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L);
    183       /* NOTE: use of CONNECTTIMEOUT without also
    184        *   setting NOSIGNAL results in really weird
    185        *   crashes on my system! */
    186       curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1L);
    187 
    188       errornum = curl_easy_perform (c);
    189       if (CURLE_OK == errornum)
    190         success++;
    191       else
    192         failure++;
    193     }
    194 
    195     /* Cleanup the environments */
    196     for (i = 0; i < 3; ++i)
    197       curl_easy_cleanup (cenv[i]);
    198     if ( (2 != success) ||
    199          (1 != failure) )
    200     {
    201       fprintf (stderr,
    202                "Unexpected number of success (%u) or failure (%u)\n",
    203                success,
    204                failure);
    205       MHD_stop_daemon (d);
    206       return 32;
    207     }
    208 
    209     (void) sleep (2);
    210 
    211     for (i = 0; i < 2; ++i)
    212     {
    213       if (cbc[i].pos != strlen ("/hello_world"))
    214       {
    215         MHD_stop_daemon (d);
    216         return 64;
    217       }
    218       if (0 != strncmp ("/hello_world", cbc[i].buf, strlen ("/hello_world")))
    219       {
    220         MHD_stop_daemon (d);
    221         return 128;
    222       }
    223     }
    224   }
    225   MHD_stop_daemon (d);
    226   return 0;
    227 }
    228 
    229 
    230 static unsigned int
    231 testMultithreadedPoolGet (void)
    232 {
    233   struct MHD_Daemon *d;
    234   char buf[2048];
    235   int k;
    236   uint16_t port;
    237   if (MHD_NO != MHD_is_feature_supported (MHD_FEATURE_AUTODETECT_BIND_PORT))
    238     port = 0;
    239   else
    240   {
    241     port = 1261;
    242     if (oneone)
    243       port += 5;
    244   }
    245 
    246   /* Test only valid for HTTP/1.1 (uses persistent connections) */
    247   if (! oneone)
    248     return 0;
    249 
    250   d = MHD_start_daemon (MHD_USE_INTERNAL_POLLING_THREAD | MHD_USE_ERROR_LOG,
    251                         port, NULL, NULL, &ahc_echo, NULL,
    252                         MHD_OPTION_PER_IP_CONNECTION_LIMIT, (unsigned int) 2,
    253                         MHD_OPTION_THREAD_POOL_SIZE, MHD_CPU_COUNT,
    254                         MHD_OPTION_END);
    255   if (d == NULL)
    256     return 16;
    257   if (0 == port)
    258   {
    259     const union MHD_DaemonInfo *dinfo;
    260     dinfo = MHD_get_daemon_info (d, MHD_DAEMON_INFO_BIND_PORT);
    261     if ((NULL == dinfo) || (0 == dinfo->port) )
    262     {
    263       MHD_stop_daemon (d); return 32;
    264     }
    265     port = dinfo->port;
    266   }
    267 
    268   for (k = 0; k < 3; ++k)
    269   {
    270     struct CBC cbc[3];
    271     CURL *cenv[3];
    272     int i;
    273 
    274     for (i = 0; i < 3; ++i)
    275     {
    276       CURL *c;
    277       CURLcode errornum;
    278 
    279       cenv[i] = c = curl_easy_init ();
    280       cbc[i].buf = buf;
    281       cbc[i].size = 2048;
    282       cbc[i].pos = 0;
    283 
    284       curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1/hello_world");
    285       curl_easy_setopt (c, CURLOPT_PORT, (long) port);
    286       curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
    287       curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc[i]);
    288       curl_easy_setopt (c, CURLOPT_FAILONERROR, 1L);
    289       curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L);
    290       curl_easy_setopt (c, CURLOPT_FORBID_REUSE, 0L);
    291       curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
    292       curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L);
    293       /* NOTE: use of CONNECTTIMEOUT without also
    294        *   setting NOSIGNAL results in really weird
    295        *   crashes on my system! */
    296       curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1L);
    297 
    298       errornum = curl_easy_perform (c);
    299       if ( ( (CURLE_OK != errornum) && (i <  2) ) ||
    300            ( (CURLE_OK == errornum) && (i == 2) ) )
    301       {
    302         int j;
    303 
    304         /* First 2 should succeed */
    305         if (i < 2)
    306           fprintf (stderr,
    307                    "curl_easy_perform failed: `%s'\n",
    308                    curl_easy_strerror (errornum));
    309 
    310         /* Last request should have failed */
    311         else
    312           fprintf (stderr,
    313                    "No error on IP address over limit\n");
    314 
    315         for (j = 0; j < i; ++j)
    316           curl_easy_cleanup (cenv[j]);
    317         MHD_stop_daemon (d);
    318         return 32;
    319       }
    320     }
    321 
    322     /* Cleanup the environments */
    323     for (i = 0; i < 3; ++i)
    324       curl_easy_cleanup (cenv[i]);
    325 
    326     (void) sleep (2);
    327 
    328     for (i = 0; i < 2; ++i)
    329     {
    330       if (cbc[i].pos != strlen ("/hello_world"))
    331       {
    332         MHD_stop_daemon (d);
    333         return 64;
    334       }
    335       if (0 != strncmp ("/hello_world", cbc[i].buf, strlen ("/hello_world")))
    336       {
    337         MHD_stop_daemon (d);
    338         return 128;
    339       }
    340     }
    341 
    342 
    343   }
    344   MHD_stop_daemon (d);
    345   return 0;
    346 }
    347 
    348 
    349 int
    350 main (int argc, char *const *argv)
    351 {
    352   unsigned int errorCount = 0;
    353   (void) argc;   /* Unused. Silent compiler warning. */
    354 
    355   if ((NULL == argv) || (0 == argv[0]))
    356     return 99;
    357   oneone = has_in_name (argv[0], "11");
    358   if (0 != curl_global_init (CURL_GLOBAL_WIN32))
    359     return 2;
    360   errorCount |= testMultithreadedGet ();
    361   errorCount |= testMultithreadedPoolGet ();
    362   if (errorCount != 0)
    363     fprintf (stderr, "Error (code: %u)\n", errorCount);
    364   curl_global_cleanup ();
    365   return (0 == errorCount) ? 0 : 1;       /* 0 == pass */
    366 }