Home | History | Annotate | Download | only in testcurl
      1 
      2 /*
      3      This file is part of libmicrohttpd
      4      Copyright (C) 2007 Christian Grothoff
      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., 59 Temple Place - Suite 330,
     19      Boston, MA 02111-1307, USA.
     20 */
     21 
     22 /**
     23  * @file test_process_headers.c
     24  * @brief  Testcase for HTTP header access
     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 #if defined(CPU_COUNT) && (CPU_COUNT+0) < 2
     41 #undef CPU_COUNT
     42 #endif
     43 #if !defined(CPU_COUNT)
     44 #define CPU_COUNT 2
     45 #endif
     46 
     47 static int oneone;
     48 
     49 struct CBC
     50 {
     51   char *buf;
     52   size_t pos;
     53   size_t size;
     54 };
     55 
     56 static size_t
     57 copyBuffer (void *ptr, size_t size, size_t nmemb, void *ctx)
     58 {
     59   struct CBC *cbc = ctx;
     60 
     61   if (cbc->pos + size * nmemb > cbc->size)
     62     return 0;                   /* overflow */
     63   memcpy (&cbc->buf[cbc->pos], ptr, size * nmemb);
     64   cbc->pos += size * nmemb;
     65   return size * nmemb;
     66 }
     67 
     68 static int
     69 kv_cb (void *cls, enum MHD_ValueKind kind, const char *key, const char *value)
     70 {
     71   if ((0 == strcmp (key, MHD_HTTP_HEADER_HOST)) &&
     72       (0 == strcmp (value, "127.0.0.1:21080")) && (kind == MHD_HEADER_KIND))
     73     {
     74       *((int *) cls) = 1;
     75       return MHD_NO;
     76     }
     77   return MHD_YES;
     78 }
     79 
     80 static int
     81 ahc_echo (void *cls,
     82           struct MHD_Connection *connection,
     83           const char *url,
     84           const char *method,
     85           const char *version,
     86           const char *upload_data, size_t *upload_data_size,
     87           void **unused)
     88 {
     89   static int ptr;
     90   const char *me = cls;
     91   struct MHD_Response *response;
     92   int ret;
     93   const char *hdr;
     94 
     95   if (0 != strcmp (me, method))
     96     return MHD_NO;              /* unexpected method */
     97   if (&ptr != *unused)
     98     {
     99       *unused = &ptr;
    100       return MHD_YES;
    101     }
    102   *unused = NULL;
    103   ret = 0;
    104   MHD_get_connection_values (connection, MHD_HEADER_KIND, &kv_cb, &ret);
    105   if (ret != 1)
    106     abort ();
    107   hdr = MHD_lookup_connection_value (connection, MHD_HEADER_KIND, "NotFound");
    108   if (hdr != NULL)
    109     abort ();
    110   hdr = MHD_lookup_connection_value (connection,
    111                                      MHD_HEADER_KIND, MHD_HTTP_HEADER_ACCEPT);
    112   if ((hdr == NULL) || (0 != strcmp (hdr, "*/*")))
    113     abort ();
    114   hdr = MHD_lookup_connection_value (connection,
    115                                      MHD_HEADER_KIND, MHD_HTTP_HEADER_HOST);
    116   if ((hdr == NULL) || (0 != strcmp (hdr, "127.0.0.1:21080")))
    117     abort ();
    118   MHD_set_connection_value (connection,
    119                             MHD_HEADER_KIND, "FakeHeader", "NowPresent");
    120   hdr = MHD_lookup_connection_value (connection,
    121                                      MHD_HEADER_KIND, "FakeHeader");
    122   if ((hdr == NULL) || (0 != strcmp (hdr, "NowPresent")))
    123     abort ();
    124 
    125   response = MHD_create_response_from_buffer (strlen (url),
    126 					      (void *) url,
    127 					      MHD_RESPMEM_MUST_COPY);
    128   MHD_add_response_header (response, "MyHeader", "MyValue");
    129   hdr = MHD_get_response_header (response, "MyHeader");
    130   if (0 != strcmp ("MyValue", hdr))
    131     abort ();
    132   MHD_add_response_header (response, "MyHeader", "MyValueToo");
    133   if (MHD_YES != MHD_del_response_header (response, "MyHeader", "MyValue"))
    134     abort ();
    135   hdr = MHD_get_response_header (response, "MyHeader");
    136   if (0 != strcmp ("MyValueToo", hdr))
    137     abort ();
    138   if (1 != MHD_get_response_headers (response, NULL, NULL))
    139     abort ();
    140   ret = MHD_queue_response (connection, MHD_HTTP_OK, response);
    141   MHD_destroy_response (response);
    142   if (ret == MHD_NO)
    143     abort ();
    144   return ret;
    145 }
    146 
    147 
    148 static int
    149 testInternalGet ()
    150 {
    151   struct MHD_Daemon *d;
    152   CURL *c;
    153   char buf[2048];
    154   struct CBC cbc;
    155   CURLcode errornum;
    156 
    157   cbc.buf = buf;
    158   cbc.size = 2048;
    159   cbc.pos = 0;
    160   d = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY | MHD_USE_DEBUG,
    161                         21080, NULL, NULL, &ahc_echo, "GET", MHD_OPTION_END);
    162   if (d == NULL)
    163     return 1;
    164   c = curl_easy_init ();
    165   curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1:21080/hello_world");
    166   curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
    167   curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
    168   curl_easy_setopt (c, CURLOPT_FAILONERROR, 1);
    169   curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L);
    170   curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L);
    171   if (oneone)
    172     curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
    173   else
    174     curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
    175   /* NOTE: use of CONNECTTIMEOUT without also
    176      setting NOSIGNAL results in really weird
    177      crashes on my system! */
    178   curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1);
    179   if (CURLE_OK != (errornum = curl_easy_perform (c)))
    180     {
    181       fprintf (stderr,
    182                "curl_easy_perform failed: `%s'\n",
    183                curl_easy_strerror (errornum));
    184       curl_easy_cleanup (c);
    185       MHD_stop_daemon (d);
    186       return 2;
    187     }
    188   curl_easy_cleanup (c);
    189   MHD_stop_daemon (d);
    190   if (cbc.pos != strlen ("/hello_world"))
    191     return 4;
    192   if (0 != strncmp ("/hello_world", cbc.buf, strlen ("/hello_world")))
    193     return 8;
    194   return 0;
    195 }
    196 
    197 static int
    198 testMultithreadedGet ()
    199 {
    200   struct MHD_Daemon *d;
    201   CURL *c;
    202   char buf[2048];
    203   struct CBC cbc;
    204   CURLcode errornum;
    205 
    206   cbc.buf = buf;
    207   cbc.size = 2048;
    208   cbc.pos = 0;
    209   d = MHD_start_daemon (MHD_USE_THREAD_PER_CONNECTION | MHD_USE_DEBUG,
    210                         21080, NULL, NULL, &ahc_echo, "GET", MHD_OPTION_END);
    211   if (d == NULL)
    212     return 16;
    213   c = curl_easy_init ();
    214   curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1:21080/hello_world");
    215   curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
    216   curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
    217   curl_easy_setopt (c, CURLOPT_FAILONERROR, 1);
    218   curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L);
    219   if (oneone)
    220     curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
    221   else
    222     curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
    223   curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L);
    224   /* NOTE: use of CONNECTTIMEOUT without also
    225      setting NOSIGNAL results in really weird
    226      crashes on my system! */
    227   curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1);
    228   if (CURLE_OK != (errornum = curl_easy_perform (c)))
    229     {
    230       fprintf (stderr,
    231                "curl_easy_perform failed: `%s'\n",
    232                curl_easy_strerror (errornum));
    233       curl_easy_cleanup (c);
    234       MHD_stop_daemon (d);
    235       return 32;
    236     }
    237   curl_easy_cleanup (c);
    238   MHD_stop_daemon (d);
    239   if (cbc.pos != strlen ("/hello_world"))
    240     return 64;
    241   if (0 != strncmp ("/hello_world", cbc.buf, strlen ("/hello_world")))
    242     return 128;
    243   return 0;
    244 }
    245 
    246 static int
    247 testMultithreadedPoolGet ()
    248 {
    249   struct MHD_Daemon *d;
    250   CURL *c;
    251   char buf[2048];
    252   struct CBC cbc;
    253   CURLcode errornum;
    254 
    255   cbc.buf = buf;
    256   cbc.size = 2048;
    257   cbc.pos = 0;
    258   d = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY | MHD_USE_DEBUG,
    259                         21080, NULL, NULL, &ahc_echo, "GET",
    260                         MHD_OPTION_THREAD_POOL_SIZE, CPU_COUNT, MHD_OPTION_END);
    261   if (d == NULL)
    262     return 16;
    263   c = curl_easy_init ();
    264   curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1:21080/hello_world");
    265   curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
    266   curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
    267   curl_easy_setopt (c, CURLOPT_FAILONERROR, 1);
    268   curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L);
    269   if (oneone)
    270     curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
    271   else
    272     curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
    273   curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L);
    274   /* NOTE: use of CONNECTTIMEOUT without also
    275      setting NOSIGNAL results in really weird
    276      crashes on my system! */
    277   curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1);
    278   if (CURLE_OK != (errornum = curl_easy_perform (c)))
    279     {
    280       fprintf (stderr,
    281                "curl_easy_perform failed: `%s'\n",
    282                curl_easy_strerror (errornum));
    283       curl_easy_cleanup (c);
    284       MHD_stop_daemon (d);
    285       return 32;
    286     }
    287   curl_easy_cleanup (c);
    288   MHD_stop_daemon (d);
    289   if (cbc.pos != strlen ("/hello_world"))
    290     return 64;
    291   if (0 != strncmp ("/hello_world", cbc.buf, strlen ("/hello_world")))
    292     return 128;
    293   return 0;
    294 }
    295 
    296 static int
    297 testExternalGet ()
    298 {
    299   struct MHD_Daemon *d;
    300   CURL *c;
    301   char buf[2048];
    302   struct CBC cbc;
    303   CURLM *multi;
    304   CURLMcode mret;
    305   fd_set rs;
    306   fd_set ws;
    307   fd_set es;
    308   MHD_socket max;
    309   int running;
    310   struct CURLMsg *msg;
    311   time_t start;
    312   struct timeval tv;
    313 
    314   multi = NULL;
    315   cbc.buf = buf;
    316   cbc.size = 2048;
    317   cbc.pos = 0;
    318   d = MHD_start_daemon (MHD_USE_DEBUG,
    319                         21080, NULL, NULL, &ahc_echo, "GET", MHD_OPTION_END);
    320   if (d == NULL)
    321     return 256;
    322   c = curl_easy_init ();
    323   curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1:21080/hello_world");
    324   curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
    325   curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
    326   curl_easy_setopt (c, CURLOPT_FAILONERROR, 1);
    327   if (oneone)
    328     curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
    329   else
    330     curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
    331   curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L);
    332   curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L);
    333   /* NOTE: use of CONNECTTIMEOUT without also
    334      setting NOSIGNAL results in really weird
    335      crashes on my system! */
    336   curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1);
    337 
    338 
    339   multi = curl_multi_init ();
    340   if (multi == NULL)
    341     {
    342       curl_easy_cleanup (c);
    343       MHD_stop_daemon (d);
    344       return 512;
    345     }
    346   mret = curl_multi_add_handle (multi, c);
    347   if (mret != CURLM_OK)
    348     {
    349       curl_multi_cleanup (multi);
    350       curl_easy_cleanup (c);
    351       MHD_stop_daemon (d);
    352       return 1024;
    353     }
    354   start = time (NULL);
    355   while ((time (NULL) - start < 5) && (multi != NULL))
    356     {
    357       max = 0;
    358       FD_ZERO (&rs);
    359       FD_ZERO (&ws);
    360       FD_ZERO (&es);
    361       curl_multi_perform (multi, &running);
    362       mret = curl_multi_fdset (multi, &rs, &ws, &es, &max);
    363       if (mret != CURLM_OK)
    364         {
    365           curl_multi_remove_handle (multi, c);
    366           curl_multi_cleanup (multi);
    367           curl_easy_cleanup (c);
    368           MHD_stop_daemon (d);
    369           return 2048;
    370         }
    371       if (MHD_YES != MHD_get_fdset (d, &rs, &ws, &es, &max))
    372         {
    373           curl_multi_remove_handle (multi, c);
    374           curl_multi_cleanup (multi);
    375           curl_easy_cleanup (c);
    376           MHD_stop_daemon (d);
    377           return 4096;
    378         }
    379       tv.tv_sec = 0;
    380       tv.tv_usec = 1000;
    381       select (max + 1, &rs, &ws, &es, &tv);
    382       curl_multi_perform (multi, &running);
    383       if (running == 0)
    384         {
    385           msg = curl_multi_info_read (multi, &running);
    386           if (msg == NULL)
    387             break;
    388           if (msg->msg == CURLMSG_DONE)
    389             {
    390               if (msg->data.result != CURLE_OK)
    391                 printf ("%s failed at %s:%d: `%s'\n",
    392                         "curl_multi_perform",
    393                         __FILE__,
    394                         __LINE__, curl_easy_strerror (msg->data.result));
    395               curl_multi_remove_handle (multi, c);
    396               curl_multi_cleanup (multi);
    397               curl_easy_cleanup (c);
    398               c = NULL;
    399               multi = NULL;
    400             }
    401         }
    402       MHD_run (d);
    403     }
    404   if (multi != NULL)
    405     {
    406       curl_multi_remove_handle (multi, c);
    407       curl_easy_cleanup (c);
    408       curl_multi_cleanup (multi);
    409     }
    410   MHD_stop_daemon (d);
    411   if (cbc.pos != strlen ("/hello_world"))
    412     return 8192;
    413   if (0 != strncmp ("/hello_world", cbc.buf, strlen ("/hello_world")))
    414     return 16384;
    415   return 0;
    416 }
    417 
    418 
    419 
    420 int
    421 main (int argc, char *const *argv)
    422 {
    423   unsigned int errorCount = 0;
    424 
    425   oneone = (NULL != strrchr (argv[0], (int) '/')) ?
    426     (NULL != strstr (strrchr (argv[0], (int) '/'), "11")) : 0;
    427   errorCount += testMultithreadedGet ();
    428   errorCount += testMultithreadedPoolGet ();
    429   errorCount += testExternalGet ();
    430   if (errorCount != 0)
    431     fprintf (stderr, "Error (code: %u)\n", errorCount);
    432   curl_global_cleanup ();
    433   return errorCount != 0;       /* 0 == pass */
    434 }
    435