Home | History | Annotate | Download | only in testcurl
      1 /*
      2      This file is part of libmicrohttpd
      3      Copyright (C) 2007, 2008 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_large_put.c
     23  * @brief  Testcase for libmicrohttpd PUT operations
     24  * @author Christian Grothoff
     25  */
     26 
     27 #include "MHD_config.h"
     28 #include "platform.h"
     29 #include <curl/curl.h>
     30 #include <microhttpd.h>
     31 #include <stdlib.h>
     32 #include <string.h>
     33 #include <time.h>
     34 
     35 #ifndef WINDOWS
     36 #include <unistd.h>
     37 #endif
     38 
     39 #if defined(CPU_COUNT) && (CPU_COUNT+0) < 2
     40 #undef CPU_COUNT
     41 #endif
     42 #if !defined(CPU_COUNT)
     43 #define CPU_COUNT 2
     44 #endif
     45 
     46 static int oneone;
     47 
     48 /**
     49  * Do not make this much larger since we will hit the
     50  * MHD default buffer limit and the test code is not
     51  * written for incremental upload processing...
     52  * (larger values will likely cause MHD to generate
     53  * an internal server error -- which would be avoided
     54  * by writing the putBuffer method in a more general
     55  * fashion).
     56  */
     57 #define PUT_SIZE (256 * 1024)
     58 
     59 static char *put_buffer;
     60 
     61 struct CBC
     62 {
     63   char *buf;
     64   size_t pos;
     65   size_t size;
     66 };
     67 
     68 static size_t
     69 putBuffer (void *stream, size_t size, size_t nmemb, void *ptr)
     70 {
     71   unsigned int *pos = ptr;
     72   unsigned int wrt;
     73 
     74   wrt = size * nmemb;
     75   if (wrt > PUT_SIZE - (*pos))
     76     wrt = PUT_SIZE - (*pos);
     77   memcpy (stream, &put_buffer[*pos], wrt);
     78   (*pos) += wrt;
     79   return wrt;
     80 }
     81 
     82 static size_t
     83 copyBuffer (void *ptr, size_t size, size_t nmemb, void *ctx)
     84 {
     85   struct CBC *cbc = ctx;
     86 
     87   if (cbc->pos + size * nmemb > cbc->size)
     88     return 0;                   /* overflow */
     89   memcpy (&cbc->buf[cbc->pos], ptr, size * nmemb);
     90   cbc->pos += size * nmemb;
     91   return size * nmemb;
     92 }
     93 
     94 static int
     95 ahc_echo (void *cls,
     96           struct MHD_Connection *connection,
     97           const char *url,
     98           const char *method,
     99           const char *version,
    100           const char *upload_data, size_t *upload_data_size,
    101           void **unused)
    102 {
    103   int *done = cls;
    104   struct MHD_Response *response;
    105   int ret;
    106 
    107   if (0 != strcmp ("PUT", method))
    108     return MHD_NO;              /* unexpected method */
    109   if ((*done) == 0)
    110     {
    111       if (*upload_data_size != PUT_SIZE)
    112         {
    113 #if 0
    114           fprintf (stderr,
    115                    "Waiting for more data (%u/%u)...\n",
    116                    *upload_data_size, PUT_SIZE);
    117 #endif
    118           return MHD_YES;       /* not yet ready */
    119         }
    120       if (0 == memcmp (upload_data, put_buffer, PUT_SIZE))
    121         {
    122           *upload_data_size = 0;
    123         }
    124       else
    125         {
    126           printf ("Invalid upload data!\n");
    127           return MHD_NO;
    128         }
    129       *done = 1;
    130       return MHD_YES;
    131     }
    132   response = MHD_create_response_from_buffer (strlen (url),
    133 					      (void *) url,
    134 					      MHD_RESPMEM_MUST_COPY);
    135   ret = MHD_queue_response (connection, MHD_HTTP_OK, response);
    136   MHD_destroy_response (response);
    137   return ret;
    138 }
    139 
    140 
    141 static int
    142 testInternalPut ()
    143 {
    144   struct MHD_Daemon *d;
    145   CURL *c;
    146   struct CBC cbc;
    147   unsigned int pos = 0;
    148   int done_flag = 0;
    149   CURLcode errornum;
    150   char buf[2048];
    151 
    152   cbc.buf = buf;
    153   cbc.size = 2048;
    154   cbc.pos = 0;
    155   d = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY | MHD_USE_DEBUG,
    156                         1080,
    157                         NULL, NULL, &ahc_echo, &done_flag,
    158 			MHD_OPTION_CONNECTION_MEMORY_LIMIT, (size_t) (1024*1024),
    159 			MHD_OPTION_END);
    160   if (d == NULL)
    161     return 1;
    162   c = curl_easy_init ();
    163   curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1:1080/hello_world");
    164   curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
    165   curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
    166   curl_easy_setopt (c, CURLOPT_READFUNCTION, &putBuffer);
    167   curl_easy_setopt (c, CURLOPT_READDATA, &pos);
    168   curl_easy_setopt (c, CURLOPT_UPLOAD, 1L);
    169   curl_easy_setopt (c, CURLOPT_INFILESIZE, (long) PUT_SIZE);
    170   curl_easy_setopt (c, CURLOPT_FAILONERROR, 1);
    171   curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L);
    172   if (oneone)
    173     curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
    174   else
    175     curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
    176   curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L);
    177   // NOTE: use of CONNECTTIMEOUT without also
    178   //   setting NOSIGNAL results in really weird
    179   //   crashes on my system!
    180   curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1);
    181   if (CURLE_OK != (errornum = curl_easy_perform (c)))
    182     {
    183       fprintf (stderr,
    184                "curl_easy_perform failed: `%s'\n",
    185                curl_easy_strerror (errornum));
    186       curl_easy_cleanup (c);
    187       MHD_stop_daemon (d);
    188       return 2;
    189     }
    190   curl_easy_cleanup (c);
    191   MHD_stop_daemon (d);
    192   if (cbc.pos != strlen ("/hello_world"))
    193     return 4;
    194   if (0 != strncmp ("/hello_world", cbc.buf, strlen ("/hello_world")))
    195     return 8;
    196   return 0;
    197 }
    198 
    199 static int
    200 testMultithreadedPut ()
    201 {
    202   struct MHD_Daemon *d;
    203   CURL *c;
    204   struct CBC cbc;
    205   unsigned int pos = 0;
    206   int done_flag = 0;
    207   CURLcode errornum;
    208   char buf[2048];
    209 
    210   cbc.buf = buf;
    211   cbc.size = 2048;
    212   cbc.pos = 0;
    213   d = MHD_start_daemon (MHD_USE_THREAD_PER_CONNECTION | MHD_USE_DEBUG,
    214                         1081,
    215                         NULL, NULL, &ahc_echo, &done_flag,
    216 			MHD_OPTION_CONNECTION_MEMORY_LIMIT, (size_t) (1024*1024),
    217 			MHD_OPTION_END);
    218   if (d == NULL)
    219     return 16;
    220   c = curl_easy_init ();
    221   curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1:1081/hello_world");
    222   curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
    223   curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
    224   curl_easy_setopt (c, CURLOPT_READFUNCTION, &putBuffer);
    225   curl_easy_setopt (c, CURLOPT_READDATA, &pos);
    226   curl_easy_setopt (c, CURLOPT_UPLOAD, 1L);
    227   curl_easy_setopt (c, CURLOPT_INFILESIZE, (long) PUT_SIZE);
    228   curl_easy_setopt (c, CURLOPT_FAILONERROR, 1);
    229   curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L);
    230   if (oneone)
    231     curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
    232   else
    233     curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
    234   curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L);
    235   // NOTE: use of CONNECTTIMEOUT without also
    236   //   setting NOSIGNAL results in really weird
    237   //   crashes on my system!
    238   curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1);
    239   if (CURLE_OK != (errornum = curl_easy_perform (c)))
    240     {
    241       fprintf (stderr,
    242                "curl_easy_perform failed: `%s'\n",
    243                curl_easy_strerror (errornum));
    244       curl_easy_cleanup (c);
    245       MHD_stop_daemon (d);
    246       return 32;
    247     }
    248   curl_easy_cleanup (c);
    249   MHD_stop_daemon (d);
    250   if (cbc.pos != strlen ("/hello_world"))
    251     {
    252       fprintf (stderr, "Got invalid response `%.*s'\n", (int)cbc.pos, cbc.buf);
    253       return 64;
    254     }
    255   if (0 != strncmp ("/hello_world", cbc.buf, strlen ("/hello_world")))
    256     return 128;
    257   return 0;
    258 }
    259 
    260 static int
    261 testMultithreadedPoolPut ()
    262 {
    263   struct MHD_Daemon *d;
    264   CURL *c;
    265   struct CBC cbc;
    266   unsigned int pos = 0;
    267   int done_flag = 0;
    268   CURLcode errornum;
    269   char buf[2048];
    270 
    271   cbc.buf = buf;
    272   cbc.size = 2048;
    273   cbc.pos = 0;
    274   d = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY | MHD_USE_DEBUG,
    275                         1081,
    276                         NULL, NULL, &ahc_echo, &done_flag,
    277                         MHD_OPTION_THREAD_POOL_SIZE, CPU_COUNT,
    278 			MHD_OPTION_CONNECTION_MEMORY_LIMIT, (size_t) (1024*1024),
    279 			MHD_OPTION_END);
    280   if (d == NULL)
    281     return 16;
    282   c = curl_easy_init ();
    283   curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1:1081/hello_world");
    284   curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
    285   curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
    286   curl_easy_setopt (c, CURLOPT_READFUNCTION, &putBuffer);
    287   curl_easy_setopt (c, CURLOPT_READDATA, &pos);
    288   curl_easy_setopt (c, CURLOPT_UPLOAD, 1L);
    289   curl_easy_setopt (c, CURLOPT_INFILESIZE, (long) PUT_SIZE);
    290   curl_easy_setopt (c, CURLOPT_FAILONERROR, 1);
    291   curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L);
    292   if (oneone)
    293     curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
    294   else
    295     curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
    296   curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L);
    297   // NOTE: use of CONNECTTIMEOUT without also
    298   //   setting NOSIGNAL results in really weird
    299   //   crashes on my system!
    300   curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1);
    301   if (CURLE_OK != (errornum = curl_easy_perform (c)))
    302     {
    303       fprintf (stderr,
    304                "curl_easy_perform failed: `%s'\n",
    305                curl_easy_strerror (errornum));
    306       curl_easy_cleanup (c);
    307       MHD_stop_daemon (d);
    308       return 32;
    309     }
    310   curl_easy_cleanup (c);
    311   MHD_stop_daemon (d);
    312   if (cbc.pos != strlen ("/hello_world"))
    313     {
    314       fprintf (stderr, "Got invalid response `%.*s'\n", (int)cbc.pos, cbc.buf);
    315       return 64;
    316     }
    317   if (0 != strncmp ("/hello_world", cbc.buf, strlen ("/hello_world")))
    318     return 128;
    319   return 0;
    320 }
    321 
    322 static int
    323 testExternalPut ()
    324 {
    325   struct MHD_Daemon *d;
    326   CURL *c;
    327   struct CBC cbc;
    328   CURLM *multi;
    329   CURLMcode mret;
    330   fd_set rs;
    331   fd_set ws;
    332   fd_set es;
    333   MHD_socket max;
    334   int running;
    335   struct CURLMsg *msg;
    336   time_t start;
    337   struct timeval tv;
    338   unsigned int pos = 0;
    339   int done_flag = 0;
    340   char buf[2048];
    341 
    342   cbc.buf = buf;
    343   cbc.size = 2048;
    344   cbc.pos = 0;
    345   multi = NULL;
    346   d = MHD_start_daemon (MHD_USE_DEBUG,
    347                         1082,
    348                         NULL, NULL, &ahc_echo, &done_flag,
    349                         MHD_OPTION_CONNECTION_MEMORY_LIMIT,
    350                         (size_t) (PUT_SIZE * 4), MHD_OPTION_END);
    351   if (d == NULL)
    352     return 256;
    353   c = curl_easy_init ();
    354   curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1:1082/hello_world");
    355   curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
    356   curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
    357   curl_easy_setopt (c, CURLOPT_READFUNCTION, &putBuffer);
    358   curl_easy_setopt (c, CURLOPT_READDATA, &pos);
    359   curl_easy_setopt (c, CURLOPT_UPLOAD, 1L);
    360   curl_easy_setopt (c, CURLOPT_INFILESIZE, (long) PUT_SIZE);
    361   curl_easy_setopt (c, CURLOPT_FAILONERROR, 1);
    362   curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L);
    363   if (oneone)
    364     curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
    365   else
    366     curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
    367   curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L);
    368   // NOTE: use of CONNECTTIMEOUT without also
    369   //   setting NOSIGNAL results in really weird
    370   //   crashes on my system!
    371   curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1);
    372 
    373 
    374   multi = curl_multi_init ();
    375   if (multi == NULL)
    376     {
    377       curl_easy_cleanup (c);
    378       MHD_stop_daemon (d);
    379       return 512;
    380     }
    381   mret = curl_multi_add_handle (multi, c);
    382   if (mret != CURLM_OK)
    383     {
    384       curl_multi_cleanup (multi);
    385       curl_easy_cleanup (c);
    386       MHD_stop_daemon (d);
    387       return 1024;
    388     }
    389   start = time (NULL);
    390   while ((time (NULL) - start < 5) && (multi != NULL))
    391     {
    392       max = 0;
    393       FD_ZERO (&rs);
    394       FD_ZERO (&ws);
    395       FD_ZERO (&es);
    396       curl_multi_perform (multi, &running);
    397       mret = curl_multi_fdset (multi, &rs, &ws, &es, &max);
    398       if (mret != CURLM_OK)
    399         {
    400           curl_multi_remove_handle (multi, c);
    401           curl_multi_cleanup (multi);
    402           curl_easy_cleanup (c);
    403           MHD_stop_daemon (d);
    404           return 2048;
    405         }
    406       if (MHD_YES != MHD_get_fdset (d, &rs, &ws, &es, &max))
    407         {
    408           curl_multi_remove_handle (multi, c);
    409           curl_multi_cleanup (multi);
    410           curl_easy_cleanup (c);
    411           MHD_stop_daemon (d);
    412           return 4096;
    413         }
    414       tv.tv_sec = 0;
    415       tv.tv_usec = 1000;
    416       select (max + 1, &rs, &ws, &es, &tv);
    417       curl_multi_perform (multi, &running);
    418       if (running == 0)
    419         {
    420           msg = curl_multi_info_read (multi, &running);
    421           if (msg == NULL)
    422             break;
    423           if (msg->msg == CURLMSG_DONE)
    424             {
    425               if (msg->data.result != CURLE_OK)
    426                 printf ("%s failed at %s:%d: `%s'\n",
    427                         "curl_multi_perform",
    428                         __FILE__,
    429                         __LINE__, curl_easy_strerror (msg->data.result));
    430               curl_multi_remove_handle (multi, c);
    431               curl_multi_cleanup (multi);
    432               curl_easy_cleanup (c);
    433               c = NULL;
    434               multi = NULL;
    435             }
    436         }
    437       MHD_run (d);
    438     }
    439   if (multi != NULL)
    440     {
    441       curl_multi_remove_handle (multi, c);
    442       curl_easy_cleanup (c);
    443       curl_multi_cleanup (multi);
    444     }
    445   MHD_stop_daemon (d);
    446   if (cbc.pos != strlen ("/hello_world"))
    447     {
    448       fprintf (stderr, "Got invalid response `%.*s'\n", (int)cbc.pos, cbc.buf);
    449       return 8192;
    450     }
    451   if (0 != strncmp ("/hello_world", cbc.buf, strlen ("/hello_world")))
    452     return 16384;
    453   return 0;
    454 }
    455 
    456 
    457 
    458 int
    459 main (int argc, char *const *argv)
    460 {
    461   unsigned int errorCount = 0;
    462 
    463   oneone = (NULL != strrchr (argv[0], (int) '/')) ?
    464     (NULL != strstr (strrchr (argv[0], (int) '/'), "11")) : 0;
    465   if (0 != curl_global_init (CURL_GLOBAL_WIN32))
    466     return 2;
    467   put_buffer = malloc (PUT_SIZE);
    468   if (NULL == put_buffer) return 1;
    469   memset (put_buffer, 1, PUT_SIZE);
    470   errorCount += testInternalPut ();
    471   errorCount += testMultithreadedPut ();
    472   errorCount += testMultithreadedPoolPut ();
    473   errorCount += testExternalPut ();
    474   free (put_buffer);
    475   if (errorCount != 0)
    476     fprintf (stderr, "Error (code: %u)\n", errorCount);
    477   curl_global_cleanup ();
    478   return errorCount != 0;       /* 0 == pass */
    479 }
    480