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 daemontest_put_chunked.c
     23  * @brief Testcase for libmicrohttpd PUT operations with chunked encoding
     24  *        for the upload data
     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 struct CBC
     48 {
     49   char *buf;
     50   size_t pos;
     51   size_t size;
     52 };
     53 
     54 static size_t
     55 putBuffer (void *stream, size_t size, size_t nmemb, void *ptr)
     56 {
     57   unsigned int *pos = ptr;
     58   unsigned int wrt;
     59 
     60   wrt = size * nmemb;
     61   if (wrt > 8 - (*pos))
     62     wrt = 8 - (*pos);
     63   if (wrt > 4)
     64     wrt = 4;                    /* only send half at first => force multiple chunks! */
     65   memcpy (stream, &("Hello123"[*pos]), wrt);
     66   (*pos) += wrt;
     67   return wrt;
     68 }
     69 
     70 static size_t
     71 copyBuffer (void *ptr, size_t size, size_t nmemb, void *ctx)
     72 {
     73   struct CBC *cbc = ctx;
     74 
     75   if (cbc->pos + size * nmemb > cbc->size)
     76     return 0;                   /* overflow */
     77   memcpy (&cbc->buf[cbc->pos], ptr, size * nmemb);
     78   cbc->pos += size * nmemb;
     79   return size * nmemb;
     80 }
     81 
     82 static int
     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 **unused)
     90 {
     91   int *done = cls;
     92   struct MHD_Response *response;
     93   int ret;
     94   int have;
     95 
     96   if (0 != strcmp ("PUT", method))
     97     return MHD_NO;              /* unexpected method */
     98   if ((*done) < 8)
     99     {
    100       have = *upload_data_size;
    101       if (have + *done > 8)
    102         {
    103           printf ("Invalid upload data `%8s'!\n", upload_data);
    104           return MHD_NO;
    105         }
    106       if (0 == memcmp (upload_data, &"Hello123"[*done], have))
    107         {
    108           *done += have;
    109           *upload_data_size = 0;
    110         }
    111       else
    112         {
    113           printf ("Invalid upload data `%8s'!\n", upload_data);
    114           return MHD_NO;
    115         }
    116 #if 0
    117       fprintf (stderr, "Not ready for response: %u/%u\n", *done, 8);
    118 #endif
    119       return MHD_YES;
    120     }
    121   response = MHD_create_response_from_buffer (strlen (url),
    122 					      (void *) url,
    123 					      MHD_RESPMEM_MUST_COPY);
    124   ret = MHD_queue_response (connection, MHD_HTTP_OK, response);
    125   MHD_destroy_response (response);
    126   return ret;
    127 }
    128 
    129 
    130 static int
    131 testInternalPut ()
    132 {
    133   struct MHD_Daemon *d;
    134   CURL *c;
    135   char buf[2048];
    136   struct CBC cbc;
    137   unsigned int pos = 0;
    138   int done_flag = 0;
    139   CURLcode errornum;
    140 
    141   cbc.buf = buf;
    142   cbc.size = 2048;
    143   cbc.pos = 0;
    144   d = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY | MHD_USE_DEBUG,
    145                         11080,
    146                         NULL, NULL, &ahc_echo, &done_flag, MHD_OPTION_END);
    147   if (d == NULL)
    148     return 1;
    149   c = curl_easy_init ();
    150   curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1:11080/hello_world");
    151   curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
    152   curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
    153   curl_easy_setopt (c, CURLOPT_READFUNCTION, &putBuffer);
    154   curl_easy_setopt (c, CURLOPT_READDATA, &pos);
    155   curl_easy_setopt (c, CURLOPT_UPLOAD, 1L);
    156   /*
    157      // by not giving the file size, we force chunking!
    158      curl_easy_setopt (c, CURLOPT_INFILESIZE_LARGE, (curl_off_t) 8L);
    159    */
    160   curl_easy_setopt (c, CURLOPT_FAILONERROR, 1);
    161   curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L);
    162   curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
    163   curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L);
    164   // NOTE: use of CONNECTTIMEOUT without also
    165   //   setting NOSIGNAL results in really weird
    166   //   crashes on my system!
    167   curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1);
    168   if (CURLE_OK != (errornum = curl_easy_perform (c)))
    169     {
    170       fprintf (stderr,
    171                "curl_easy_perform failed: `%s'\n",
    172                curl_easy_strerror (errornum));
    173       curl_easy_cleanup (c);
    174       MHD_stop_daemon (d);
    175       return 2;
    176     }
    177   curl_easy_cleanup (c);
    178   MHD_stop_daemon (d);
    179   if (cbc.pos != strlen ("/hello_world"))
    180     return 4;
    181   if (0 != strncmp ("/hello_world", cbc.buf, strlen ("/hello_world")))
    182     return 8;
    183   return 0;
    184 }
    185 
    186 static int
    187 testMultithreadedPut ()
    188 {
    189   struct MHD_Daemon *d;
    190   CURL *c;
    191   char buf[2048];
    192   struct CBC cbc;
    193   unsigned int pos = 0;
    194   int done_flag = 0;
    195   CURLcode errornum;
    196 
    197   cbc.buf = buf;
    198   cbc.size = 2048;
    199   cbc.pos = 0;
    200   d = MHD_start_daemon (MHD_USE_THREAD_PER_CONNECTION | MHD_USE_DEBUG,
    201                         11081,
    202                         NULL, NULL, &ahc_echo, &done_flag, MHD_OPTION_END);
    203   if (d == NULL)
    204     return 16;
    205   c = curl_easy_init ();
    206   curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1:11081/hello_world");
    207   curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
    208   curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
    209   curl_easy_setopt (c, CURLOPT_READFUNCTION, &putBuffer);
    210   curl_easy_setopt (c, CURLOPT_READDATA, &pos);
    211   curl_easy_setopt (c, CURLOPT_UPLOAD, 1L);
    212   /*
    213      // by not giving the file size, we force chunking!
    214      curl_easy_setopt (c, CURLOPT_INFILESIZE_LARGE, (curl_off_t) 8L);
    215    */
    216   curl_easy_setopt (c, CURLOPT_FAILONERROR, 1);
    217   curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L);
    218   curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
    219   curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L);
    220   // NOTE: use of CONNECTTIMEOUT without also
    221   //   setting NOSIGNAL results in really weird
    222   //   crashes on my system!
    223   curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1);
    224   if (CURLE_OK != (errornum = curl_easy_perform (c)))
    225     {
    226       fprintf (stderr,
    227                "curl_easy_perform failed: `%s'\n",
    228                curl_easy_strerror (errornum));
    229       curl_easy_cleanup (c);
    230       MHD_stop_daemon (d);
    231       return 32;
    232     }
    233   curl_easy_cleanup (c);
    234   MHD_stop_daemon (d);
    235   if (cbc.pos != strlen ("/hello_world"))
    236     return 64;
    237   if (0 != strncmp ("/hello_world", cbc.buf, strlen ("/hello_world")))
    238     return 128;
    239 
    240   return 0;
    241 }
    242 
    243 static int
    244 testMultithreadedPoolPut ()
    245 {
    246   struct MHD_Daemon *d;
    247   CURL *c;
    248   char buf[2048];
    249   struct CBC cbc;
    250   unsigned int pos = 0;
    251   int done_flag = 0;
    252   CURLcode errornum;
    253 
    254   cbc.buf = buf;
    255   cbc.size = 2048;
    256   cbc.pos = 0;
    257   d = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY | MHD_USE_DEBUG,
    258                         11081,
    259                         NULL, NULL, &ahc_echo, &done_flag,
    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:11081/hello_world");
    265   curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
    266   curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
    267   curl_easy_setopt (c, CURLOPT_READFUNCTION, &putBuffer);
    268   curl_easy_setopt (c, CURLOPT_READDATA, &pos);
    269   curl_easy_setopt (c, CURLOPT_UPLOAD, 1L);
    270   /*
    271      // by not giving the file size, we force chunking!
    272      curl_easy_setopt (c, CURLOPT_INFILESIZE_LARGE, (curl_off_t) 8L);
    273    */
    274   curl_easy_setopt (c, CURLOPT_FAILONERROR, 1);
    275   curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L);
    276   curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
    277   curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L);
    278   // NOTE: use of CONNECTTIMEOUT without also
    279   //   setting NOSIGNAL results in really weird
    280   //   crashes on my system!
    281   curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1);
    282   if (CURLE_OK != (errornum = curl_easy_perform (c)))
    283     {
    284       fprintf (stderr,
    285                "curl_easy_perform failed: `%s'\n",
    286                curl_easy_strerror (errornum));
    287       curl_easy_cleanup (c);
    288       MHD_stop_daemon (d);
    289       return 32;
    290     }
    291   curl_easy_cleanup (c);
    292   MHD_stop_daemon (d);
    293   if (cbc.pos != strlen ("/hello_world"))
    294     return 64;
    295   if (0 != strncmp ("/hello_world", cbc.buf, strlen ("/hello_world")))
    296     return 128;
    297 
    298   return 0;
    299 }
    300 
    301 
    302 static int
    303 testExternalPut ()
    304 {
    305   struct MHD_Daemon *d;
    306   CURL *c;
    307   char buf[2048];
    308   struct CBC cbc;
    309   CURLM *multi;
    310   CURLMcode mret;
    311   fd_set rs;
    312   fd_set ws;
    313   fd_set es;
    314   MHD_socket max;
    315   int running;
    316   struct CURLMsg *msg;
    317   time_t start;
    318   struct timeval tv;
    319   unsigned int pos = 0;
    320   int done_flag = 0;
    321 
    322   multi = NULL;
    323   cbc.buf = buf;
    324   cbc.size = 2048;
    325   cbc.pos = 0;
    326   d = MHD_start_daemon (MHD_USE_DEBUG,
    327                         11082,
    328                         NULL, NULL, &ahc_echo, &done_flag, MHD_OPTION_END);
    329   if (d == NULL)
    330     return 256;
    331   c = curl_easy_init ();
    332   curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1:11082/hello_world");
    333   curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
    334   curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
    335   curl_easy_setopt (c, CURLOPT_READFUNCTION, &putBuffer);
    336   curl_easy_setopt (c, CURLOPT_READDATA, &pos);
    337   curl_easy_setopt (c, CURLOPT_UPLOAD, 1L);
    338   /*
    339      // by not giving the file size, we force chunking!
    340      curl_easy_setopt (c, CURLOPT_INFILESIZE_LARGE, (curl_off_t) 8L);
    341    */
    342   curl_easy_setopt (c, CURLOPT_FAILONERROR, 1);
    343   curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L);
    344   curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
    345   curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L);
    346   // NOTE: use of CONNECTTIMEOUT without also
    347   //   setting NOSIGNAL results in really weird
    348   //   crashes on my system!
    349   curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1);
    350 
    351 
    352   multi = curl_multi_init ();
    353   if (multi == NULL)
    354     {
    355       curl_easy_cleanup (c);
    356       MHD_stop_daemon (d);
    357       return 512;
    358     }
    359   mret = curl_multi_add_handle (multi, c);
    360   if (mret != CURLM_OK)
    361     {
    362       curl_multi_cleanup (multi);
    363       curl_easy_cleanup (c);
    364       MHD_stop_daemon (d);
    365       return 1024;
    366     }
    367   start = time (NULL);
    368   while ((time (NULL) - start < 5) && (multi != NULL))
    369     {
    370       max = 0;
    371       FD_ZERO (&rs);
    372       FD_ZERO (&ws);
    373       FD_ZERO (&es);
    374       curl_multi_perform (multi, &running);
    375       mret = curl_multi_fdset (multi, &rs, &ws, &es, &max);
    376       if (mret != CURLM_OK)
    377         {
    378           curl_multi_remove_handle (multi, c);
    379           curl_multi_cleanup (multi);
    380           curl_easy_cleanup (c);
    381           MHD_stop_daemon (d);
    382           return 2048;
    383         }
    384       if (MHD_YES != MHD_get_fdset (d, &rs, &ws, &es, &max))
    385         {
    386           curl_multi_remove_handle (multi, c);
    387           curl_multi_cleanup (multi);
    388           curl_easy_cleanup (c);
    389           MHD_stop_daemon (d);
    390           return 4096;
    391         }
    392       tv.tv_sec = 0;
    393       tv.tv_usec = 1000;
    394       select (max + 1, &rs, &ws, &es, &tv);
    395       curl_multi_perform (multi, &running);
    396       if (running == 0)
    397         {
    398           msg = curl_multi_info_read (multi, &running);
    399           if (msg == NULL)
    400             break;
    401           if (msg->msg == CURLMSG_DONE)
    402             {
    403               if (msg->data.result != CURLE_OK)
    404                 printf ("%s failed at %s:%d: `%s'\n",
    405                         "curl_multi_perform",
    406                         __FILE__,
    407                         __LINE__, curl_easy_strerror (msg->data.result));
    408               curl_multi_remove_handle (multi, c);
    409               curl_multi_cleanup (multi);
    410               curl_easy_cleanup (c);
    411               c = NULL;
    412               multi = NULL;
    413             }
    414         }
    415       MHD_run (d);
    416     }
    417   if (multi != NULL)
    418     {
    419       curl_multi_remove_handle (multi, c);
    420       curl_easy_cleanup (c);
    421       curl_multi_cleanup (multi);
    422     }
    423   MHD_stop_daemon (d);
    424   if (cbc.pos != strlen ("/hello_world"))
    425     return 8192;
    426   if (0 != strncmp ("/hello_world", cbc.buf, strlen ("/hello_world")))
    427     return 16384;
    428   return 0;
    429 }
    430 
    431 
    432 
    433 int
    434 main (int argc, char *const *argv)
    435 {
    436   unsigned int errorCount = 0;
    437 
    438   if (0 != curl_global_init (CURL_GLOBAL_WIN32))
    439     return 2;
    440   errorCount += testInternalPut ();
    441   errorCount += testMultithreadedPut ();
    442   errorCount += testMultithreadedPoolPut ();
    443   errorCount += testExternalPut ();
    444   if (errorCount != 0)
    445     fprintf (stderr, "Error (code: %u)\n", errorCount);
    446   curl_global_cleanup ();
    447   return errorCount != 0;       /* 0 == pass */
    448 }
    449