libmicrohttpd

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

test_iplimit.c (9351B)


      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 #ifndef WINDOWS
     39 #include <unistd.h>
     40 #endif
     41 
     42 #ifdef _WIN32
     43 #ifndef WIN32_LEAN_AND_MEAN
     44 #define WIN32_LEAN_AND_MEAN 1
     45 #endif /* !WIN32_LEAN_AND_MEAN */
     46 #include <windows.h>
     47 #endif
     48 
     49 #if defined(MHD_CPU_COUNT) && (MHD_CPU_COUNT + 0) < 2
     50 #undef MHD_CPU_COUNT
     51 #endif
     52 #if ! defined(MHD_CPU_COUNT)
     53 #define MHD_CPU_COUNT 2
     54 #endif
     55 
     56 static int oneone;
     57 
     58 struct CBC
     59 {
     60   char *buf;
     61   size_t pos;
     62   size_t size;
     63 };
     64 
     65 static size_t
     66 copyBuffer (void *ptr, size_t size, size_t nmemb, void *ctx)
     67 {
     68   struct CBC *cbc = ctx;
     69 
     70   if (cbc->pos + size * nmemb > cbc->size)
     71     return 0;                   /* overflow */
     72   memcpy (&cbc->buf[cbc->pos], ptr, size * nmemb);
     73   cbc->pos += size * nmemb;
     74   return size * nmemb;
     75 }
     76 
     77 
     78 static enum MHD_Result
     79 ahc_echo (void *cls,
     80           struct MHD_Connection *connection,
     81           const char *url,
     82           const char *method,
     83           const char *version,
     84           const char *upload_data, size_t *upload_data_size,
     85           void **req_cls)
     86 {
     87   static int ptr;
     88   struct MHD_Response *response;
     89   enum MHD_Result ret;
     90   (void) cls;
     91   (void) version; (void) upload_data; (void) upload_data_size;       /* Unused. Silent compiler warning. */
     92 
     93   if (0 != strcmp (MHD_HTTP_METHOD_GET, method))
     94     return MHD_NO;              /* unexpected method */
     95   if (&ptr != *req_cls)
     96   {
     97     *req_cls = &ptr;
     98     return MHD_YES;
     99   }
    100   *req_cls = NULL;
    101   response = MHD_create_response_from_buffer_copy (strlen (url),
    102                                                    (const void *) url);
    103   ret = MHD_queue_response (connection, MHD_HTTP_OK, response);
    104   MHD_destroy_response (response);
    105   if (ret == MHD_NO)
    106     abort ();
    107   return ret;
    108 }
    109 
    110 
    111 static unsigned int
    112 testMultithreadedGet (void)
    113 {
    114   struct MHD_Daemon *d;
    115   char buf[2048];
    116   int k;
    117   unsigned int success;
    118   unsigned int failure;
    119   uint16_t port;
    120 
    121   if (MHD_NO != MHD_is_feature_supported (MHD_FEATURE_AUTODETECT_BIND_PORT))
    122     port = 0;
    123   else
    124   {
    125     port = 1260;
    126     if (oneone)
    127       port += 5;
    128   }
    129 
    130   /* Test only valid for HTTP/1.1 (uses persistent connections) */
    131   if (! oneone)
    132     return 0;
    133 
    134   d = MHD_start_daemon (MHD_USE_INTERNAL_POLLING_THREAD | MHD_USE_ERROR_LOG,
    135                         port, NULL, NULL,
    136                         &ahc_echo, NULL,
    137                         MHD_OPTION_PER_IP_CONNECTION_LIMIT, (unsigned int) 2,
    138                         MHD_OPTION_END);
    139   if (d == NULL)
    140     return 16;
    141   if (0 == port)
    142   {
    143     const union MHD_DaemonInfo *dinfo;
    144     dinfo = MHD_get_daemon_info (d, MHD_DAEMON_INFO_BIND_PORT);
    145     if ((NULL == dinfo) || (0 == dinfo->port) )
    146     {
    147       MHD_stop_daemon (d); return 32;
    148     }
    149     port = dinfo->port;
    150   }
    151 
    152   for (k = 0; k < 3; ++k)
    153   {
    154     struct CBC cbc[3];
    155     CURL *cenv[3];
    156     int i;
    157 
    158     success = 0;
    159     failure = 0;
    160     for (i = 0; i < 3; ++i)
    161     {
    162       CURL *c;
    163       CURLcode errornum;
    164 
    165       cenv[i] = c = curl_easy_init ();
    166       cbc[i].buf = buf;
    167       cbc[i].size = 2048;
    168       cbc[i].pos = 0;
    169 
    170       curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1/hello_world");
    171       curl_easy_setopt (c, CURLOPT_PORT, (long) port);
    172       curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
    173       curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc[i]);
    174       curl_easy_setopt (c, CURLOPT_FAILONERROR, 1L);
    175       curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L);
    176       curl_easy_setopt (c, CURLOPT_FORBID_REUSE, 0L);
    177       curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
    178       curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L);
    179       /* NOTE: use of CONNECTTIMEOUT without also
    180        *   setting NOSIGNAL results in really weird
    181        *   crashes on my system! */
    182       curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1L);
    183 
    184       errornum = curl_easy_perform (c);
    185       if (CURLE_OK == errornum)
    186         success++;
    187       else
    188         failure++;
    189     }
    190 
    191     /* Cleanup the environments */
    192     for (i = 0; i < 3; ++i)
    193       curl_easy_cleanup (cenv[i]);
    194     if ( (2 != success) ||
    195          (1 != failure) )
    196     {
    197       fprintf (stderr,
    198                "Unexpected number of success (%u) or failure (%u)\n",
    199                success,
    200                failure);
    201       MHD_stop_daemon (d);
    202       return 32;
    203     }
    204 
    205     (void) sleep (2);
    206 
    207     for (i = 0; i < 2; ++i)
    208     {
    209       if (cbc[i].pos != strlen ("/hello_world"))
    210       {
    211         MHD_stop_daemon (d);
    212         return 64;
    213       }
    214       if (0 != strncmp ("/hello_world", cbc[i].buf, strlen ("/hello_world")))
    215       {
    216         MHD_stop_daemon (d);
    217         return 128;
    218       }
    219     }
    220   }
    221   MHD_stop_daemon (d);
    222   return 0;
    223 }
    224 
    225 
    226 static unsigned int
    227 testMultithreadedPoolGet (void)
    228 {
    229   struct MHD_Daemon *d;
    230   char buf[2048];
    231   int k;
    232   uint16_t port;
    233   if (MHD_NO != MHD_is_feature_supported (MHD_FEATURE_AUTODETECT_BIND_PORT))
    234     port = 0;
    235   else
    236   {
    237     port = 1261;
    238     if (oneone)
    239       port += 5;
    240   }
    241 
    242   /* Test only valid for HTTP/1.1 (uses persistent connections) */
    243   if (! oneone)
    244     return 0;
    245 
    246   d = MHD_start_daemon (MHD_USE_INTERNAL_POLLING_THREAD | MHD_USE_ERROR_LOG,
    247                         port, NULL, NULL, &ahc_echo, NULL,
    248                         MHD_OPTION_PER_IP_CONNECTION_LIMIT, (unsigned int) 2,
    249                         MHD_OPTION_THREAD_POOL_SIZE, MHD_CPU_COUNT,
    250                         MHD_OPTION_END);
    251   if (d == NULL)
    252     return 16;
    253   if (0 == port)
    254   {
    255     const union MHD_DaemonInfo *dinfo;
    256     dinfo = MHD_get_daemon_info (d, MHD_DAEMON_INFO_BIND_PORT);
    257     if ((NULL == dinfo) || (0 == dinfo->port) )
    258     {
    259       MHD_stop_daemon (d); return 32;
    260     }
    261     port = dinfo->port;
    262   }
    263 
    264   for (k = 0; k < 3; ++k)
    265   {
    266     struct CBC cbc[3];
    267     CURL *cenv[3];
    268     int i;
    269 
    270     for (i = 0; i < 3; ++i)
    271     {
    272       CURL *c;
    273       CURLcode errornum;
    274 
    275       cenv[i] = c = curl_easy_init ();
    276       cbc[i].buf = buf;
    277       cbc[i].size = 2048;
    278       cbc[i].pos = 0;
    279 
    280       curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1/hello_world");
    281       curl_easy_setopt (c, CURLOPT_PORT, (long) port);
    282       curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
    283       curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc[i]);
    284       curl_easy_setopt (c, CURLOPT_FAILONERROR, 1L);
    285       curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L);
    286       curl_easy_setopt (c, CURLOPT_FORBID_REUSE, 0L);
    287       curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
    288       curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L);
    289       /* NOTE: use of CONNECTTIMEOUT without also
    290        *   setting NOSIGNAL results in really weird
    291        *   crashes on my system! */
    292       curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1L);
    293 
    294       errornum = curl_easy_perform (c);
    295       if ( ( (CURLE_OK != errornum) && (i <  2) ) ||
    296            ( (CURLE_OK == errornum) && (i == 2) ) )
    297       {
    298         int j;
    299 
    300         /* First 2 should succeed */
    301         if (i < 2)
    302           fprintf (stderr,
    303                    "curl_easy_perform failed: `%s'\n",
    304                    curl_easy_strerror (errornum));
    305 
    306         /* Last request should have failed */
    307         else
    308           fprintf (stderr,
    309                    "No error on IP address over limit\n");
    310 
    311         for (j = 0; j < i; ++j)
    312           curl_easy_cleanup (cenv[j]);
    313         MHD_stop_daemon (d);
    314         return 32;
    315       }
    316     }
    317 
    318     /* Cleanup the environments */
    319     for (i = 0; i < 3; ++i)
    320       curl_easy_cleanup (cenv[i]);
    321 
    322     (void) sleep (2);
    323 
    324     for (i = 0; i < 2; ++i)
    325     {
    326       if (cbc[i].pos != strlen ("/hello_world"))
    327       {
    328         MHD_stop_daemon (d);
    329         return 64;
    330       }
    331       if (0 != strncmp ("/hello_world", cbc[i].buf, strlen ("/hello_world")))
    332       {
    333         MHD_stop_daemon (d);
    334         return 128;
    335       }
    336     }
    337 
    338 
    339   }
    340   MHD_stop_daemon (d);
    341   return 0;
    342 }
    343 
    344 
    345 int
    346 main (int argc, char *const *argv)
    347 {
    348   unsigned int errorCount = 0;
    349   (void) argc;   /* Unused. Silent compiler warning. */
    350 
    351   if ((NULL == argv) || (0 == argv[0]))
    352     return 99;
    353   oneone = has_in_name (argv[0], "11");
    354   if (0 != curl_global_init (CURL_GLOBAL_WIN32))
    355     return 2;
    356   errorCount |= testMultithreadedGet ();
    357   errorCount |= testMultithreadedPoolGet ();
    358   if (errorCount != 0)
    359     fprintf (stderr, "Error (code: %u)\n", errorCount);
    360   curl_global_cleanup ();
    361   return (0 == errorCount) ? 0 : 1;       /* 0 == pass */
    362 }