Home | History | Annotate | Download | only in testcurl
      1 /*
      2      This file is part of libmicrohttpd
      3      Copyright (C) 2007 Christian Grothoff
      4 
      5      libmicrohttpd is free software; you can redistribute it and/or modify
      6      it under the terms of the GNU General Public License as published
      7      by the Free Software Foundation; either version 2, or (at your
      8      option) any later version.
      9 
     10      libmicrohttpd is distributed in the hope that it will be useful, but
     11      WITHOUT ANY WARRANTY; without even the implied warranty of
     12      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     13      General Public License for more details.
     14 
     15      You should have received a copy of the GNU General Public License
     16      along with libmicrohttpd; see the file COPYING.  If not, write to the
     17      Free Software Foundation, Inc., 59 Temple Place - Suite 330,
     18      Boston, MA 02111-1307, USA.
     19 */
     20 
     21 /**
     22  * @file test_iplimit.c
     23  * @brief  Testcase for libmicrohttpd GET operations
     24  *         TODO: test parsing of query
     25  * @author Christian Grothoff
     26  */
     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 
     36 #ifndef WINDOWS
     37 #include <unistd.h>
     38 #endif
     39 
     40 #ifdef _WIN32
     41 #ifndef WIN32_LEAN_AND_MEAN
     42 #define WIN32_LEAN_AND_MEAN 1
     43 #endif /* !WIN32_LEAN_AND_MEAN */
     44 #include <windows.h>
     45 #endif
     46 
     47 #if defined(CPU_COUNT) && (CPU_COUNT+0) < 2
     48 #undef CPU_COUNT
     49 #endif
     50 #if !defined(CPU_COUNT)
     51 #define CPU_COUNT 2
     52 #endif
     53 
     54 static int oneone;
     55 
     56 struct CBC
     57 {
     58   char *buf;
     59   size_t pos;
     60   size_t size;
     61 };
     62 
     63 static size_t
     64 copyBuffer (void *ptr, size_t size, size_t nmemb, void *ctx)
     65 {
     66   struct CBC *cbc = ctx;
     67 
     68   if (cbc->pos + size * nmemb > cbc->size)
     69     return 0;                   /* overflow */
     70   memcpy (&cbc->buf[cbc->pos], ptr, size * nmemb);
     71   cbc->pos += size * nmemb;
     72   return size * nmemb;
     73 }
     74 
     75 static int
     76 ahc_echo (void *cls,
     77           struct MHD_Connection *connection,
     78           const char *url,
     79           const char *method,
     80           const char *version,
     81           const char *upload_data, size_t *upload_data_size,
     82           void **unused)
     83 {
     84   static int ptr;
     85   const char *me = cls;
     86   struct MHD_Response *response;
     87   int ret;
     88 
     89   if (0 != strcmp (me, method))
     90     return MHD_NO;              /* unexpected method */
     91   if (&ptr != *unused)
     92     {
     93       *unused = &ptr;
     94       return MHD_YES;
     95     }
     96   *unused = NULL;
     97   response = MHD_create_response_from_buffer (strlen (url),
     98 					      (void *) url,
     99 					      MHD_RESPMEM_MUST_COPY);
    100   ret = MHD_queue_response (connection, MHD_HTTP_OK, response);
    101   MHD_destroy_response (response);
    102   if (ret == MHD_NO)
    103     abort ();
    104   return ret;
    105 }
    106 
    107 static int
    108 testMultithreadedGet ()
    109 {
    110   struct MHD_Daemon *d;
    111   char buf[2048];
    112   int k;
    113   unsigned int success;
    114   unsigned int failure;
    115 
    116   /* Test only valid for HTTP/1.1 (uses persistent connections) */
    117   if (!oneone)
    118     return 0;
    119 
    120   d = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY | MHD_USE_DEBUG,
    121                         1081, NULL, NULL,
    122                         &ahc_echo, "GET",
    123                         MHD_OPTION_PER_IP_CONNECTION_LIMIT, 2,
    124                         MHD_OPTION_END);
    125   if (d == NULL)
    126     return 16;
    127 
    128   for (k = 0; k < 3; ++k)
    129     {
    130       struct CBC cbc[3];
    131       CURL *cenv[3];
    132       int i;
    133 
    134       success = 0;
    135       failure = 0;
    136       for (i = 0; i < 3; ++i)
    137         {
    138           CURL *c;
    139           CURLcode errornum;
    140 
    141           cenv[i] = c = curl_easy_init ();
    142           cbc[i].buf = buf;
    143           cbc[i].size = 2048;
    144           cbc[i].pos = 0;
    145 
    146           curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1:1081/hello_world");
    147           curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
    148           curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc[i]);
    149           curl_easy_setopt (c, CURLOPT_FAILONERROR, 1);
    150           curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L);
    151           curl_easy_setopt (c, CURLOPT_FORBID_REUSE, 0L);
    152           curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
    153           curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L);
    154           // NOTE: use of CONNECTTIMEOUT without also
    155           //   setting NOSIGNAL results in really weird
    156           //   crashes on my system!
    157           curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1);
    158 
    159           errornum = curl_easy_perform (c);
    160           if (CURLE_OK == errornum)
    161             success++;
    162           else
    163             failure++;
    164         }
    165 
    166       /* Cleanup the environments */
    167       for (i = 0; i < 3; ++i)
    168         curl_easy_cleanup (cenv[i]);
    169       if ( (2 != success) ||
    170            (1 != failure) )
    171       {
    172         fprintf (stderr,
    173                  "Unexpected number of success (%u) or failure (%u)\n",
    174                  success,
    175                  failure);
    176         MHD_stop_daemon (d);
    177         return 32;
    178       }
    179 
    180       sleep(2);
    181 
    182       for (i = 0; i < 2; ++i)
    183         {
    184           if (cbc[i].pos != strlen ("/hello_world"))
    185             {
    186               MHD_stop_daemon (d);
    187               return 64;
    188             }
    189           if (0 != strncmp ("/hello_world", cbc[i].buf, strlen ("/hello_world")))
    190             {
    191               MHD_stop_daemon (d);
    192               return 128;
    193             }
    194         }
    195     }
    196   MHD_stop_daemon (d);
    197   return 0;
    198 }
    199 
    200 static int
    201 testMultithreadedPoolGet ()
    202 {
    203   struct MHD_Daemon *d;
    204   char buf[2048];
    205   int k;
    206 
    207   /* Test only valid for HTTP/1.1 (uses persistent connections) */
    208   if (!oneone)
    209     return 0;
    210 
    211   d = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY | MHD_USE_DEBUG,
    212                         1081, NULL, NULL, &ahc_echo, "GET",
    213                         MHD_OPTION_PER_IP_CONNECTION_LIMIT, 2,
    214                         MHD_OPTION_THREAD_POOL_SIZE, CPU_COUNT,
    215                         MHD_OPTION_END);
    216   if (d == NULL)
    217     return 16;
    218 
    219   for (k = 0; k < 3; ++k)
    220     {
    221       struct CBC cbc[3];
    222       CURL *cenv[3];
    223       int i;
    224 
    225       for (i = 0; i < 3; ++i)
    226         {
    227           CURL *c;
    228           CURLcode errornum;
    229 
    230           cenv[i] = c = curl_easy_init ();
    231           cbc[i].buf = buf;
    232           cbc[i].size = 2048;
    233           cbc[i].pos = 0;
    234 
    235           curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1:1081/hello_world");
    236           curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
    237           curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc[i]);
    238           curl_easy_setopt (c, CURLOPT_FAILONERROR, 1);
    239           curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L);
    240           curl_easy_setopt (c, CURLOPT_FORBID_REUSE, 0L);
    241           curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
    242           curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L);
    243           // NOTE: use of CONNECTTIMEOUT without also
    244           //   setting NOSIGNAL results in really weird
    245           //   crashes on my system!
    246           curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1);
    247 
    248           errornum = curl_easy_perform (c);
    249           if ( ( (CURLE_OK != errornum) && (i <  2) ) ||
    250 	       ( (CURLE_OK == errornum) && (i == 2) ) )
    251             {
    252               int j;
    253 
    254               /* First 2 should succeed */
    255               if (i < 2)
    256                 fprintf (stderr,
    257                          "curl_easy_perform failed: `%s'\n",
    258                          curl_easy_strerror (errornum));
    259 
    260               /* Last request should have failed */
    261               else
    262                 fprintf (stderr,
    263                          "No error on IP address over limit\n");
    264 
    265               for (j = 0; j < i; ++j)
    266                 curl_easy_cleanup (cenv[j]);
    267               MHD_stop_daemon (d);
    268               return 32;
    269             }
    270         }
    271 
    272       /* Cleanup the environments */
    273       for (i = 0; i < 3; ++i)
    274         curl_easy_cleanup (cenv[i]);
    275 
    276       sleep(2);
    277 
    278       for (i = 0; i < 2; ++i)
    279         {
    280           if (cbc[i].pos != strlen ("/hello_world"))
    281             {
    282               MHD_stop_daemon (d);
    283               return 64;
    284             }
    285           if (0 != strncmp ("/hello_world", cbc[i].buf, strlen ("/hello_world")))
    286             {
    287               MHD_stop_daemon (d);
    288               return 128;
    289             }
    290         }
    291 
    292 
    293     }
    294   MHD_stop_daemon (d);
    295   return 0;
    296 }
    297 
    298 int
    299 main (int argc, char *const *argv)
    300 {
    301   unsigned int errorCount = 0;
    302 
    303   oneone = (NULL != strrchr (argv[0], (int) '/')) ?
    304     (NULL != strstr (strrchr (argv[0], (int) '/'), "11")) : 0;
    305   if (0 != curl_global_init (CURL_GLOBAL_WIN32))
    306     return 2;
    307   errorCount |= testMultithreadedGet ();
    308   errorCount |= testMultithreadedPoolGet ();
    309   if (errorCount != 0)
    310     fprintf (stderr, "Error (code: %u)\n", errorCount);
    311   curl_global_cleanup ();
    312   return errorCount != 0;       /* 0 == pass */
    313 }
    314