Home | History | Annotate | Download | only in testzzuf
      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 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 #include "socat.c"
     41 
     42 struct CBC
     43 {
     44   char *buf;
     45   size_t pos;
     46   size_t size;
     47 };
     48 
     49 static size_t
     50 putBuffer (void *stream, size_t size, size_t nmemb, void *ptr)
     51 {
     52   unsigned int *pos = ptr;
     53   unsigned int wrt;
     54 
     55   wrt = size * nmemb;
     56   if (wrt > 8 - (*pos))
     57     wrt = 8 - (*pos);
     58   if (wrt > 4)
     59     wrt = 4;                    /* only send half at first => force multiple chunks! */
     60   memcpy (stream, &("Hello123"[*pos]), wrt);
     61   (*pos) += wrt;
     62   return wrt;
     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 static int
     78 ahc_echo (void *cls,
     79           struct MHD_Connection *connection,
     80           const char *url,
     81           const char *method,
     82           const char *version,
     83           const char *upload_data, size_t *upload_data_size,
     84           void **unused)
     85 {
     86   int *done = cls;
     87   struct MHD_Response *response;
     88   int ret;
     89   int have;
     90 
     91   if (0 != strcmp ("PUT", method))
     92     return MHD_NO;              /* unexpected method */
     93   if ((*done) < 8)
     94     {
     95       have = *upload_data_size;
     96       if (have + *done > 8)
     97         {
     98           return MHD_NO;
     99         }
    100       if (0 == memcmp (upload_data, &"Hello123"[*done], have))
    101         {
    102           *done += have;
    103           *upload_data_size = 0;
    104         }
    105       else
    106         {
    107           return MHD_NO;
    108         }
    109 #if 0
    110       fprintf (stderr, "Not ready for response: %u/%u\n", *done, 8);
    111 #endif
    112       return MHD_YES;
    113     }
    114   response = MHD_create_response_from_buffer (strlen (url),
    115 					      (void *) url,
    116 					      MHD_RESPMEM_MUST_COPY);
    117   ret = MHD_queue_response (connection, MHD_HTTP_OK, response);
    118   MHD_destroy_response (response);
    119   return ret;
    120 }
    121 
    122 
    123 static int
    124 testInternalPut ()
    125 {
    126   struct MHD_Daemon *d;
    127   CURL *c;
    128   char buf[2048];
    129   struct CBC cbc;
    130   unsigned int pos = 0;
    131   int done_flag = 0;
    132   int i;
    133 
    134   cbc.buf = buf;
    135   cbc.size = 2048;
    136   cbc.pos = 0;
    137   d = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY | MHD_USE_DEBUG,
    138                         11080,
    139                         NULL, NULL, &ahc_echo, &done_flag, MHD_OPTION_END);
    140   if (d == NULL)
    141     return 1;
    142   zzuf_socat_start ();
    143   for (i = 0; i < LOOP_COUNT; i++)
    144     {
    145       fprintf (stderr, ".");
    146       c = curl_easy_init ();
    147       curl_easy_setopt (c, CURLOPT_URL, "http://localhost:11080/hello_world");
    148       curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
    149       curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
    150       curl_easy_setopt (c, CURLOPT_READFUNCTION, &putBuffer);
    151       curl_easy_setopt (c, CURLOPT_READDATA, &pos);
    152       curl_easy_setopt (c, CURLOPT_UPLOAD, 1L);
    153       /*
    154          // by not giving the file size, we force chunking!
    155          curl_easy_setopt (c, CURLOPT_INFILESIZE_LARGE, (curl_off_t) 8L);
    156        */
    157       curl_easy_setopt (c, CURLOPT_FAILONERROR, 1);
    158       curl_easy_setopt (c, CURLOPT_TIMEOUT_MS, CURL_TIMEOUT);
    159       curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
    160       curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT_MS, CURL_TIMEOUT);
    161       // NOTE: use of CONNECTTIMEOUT without also
    162       //   setting NOSIGNAL results in really weird
    163       //   crashes on my system!
    164       curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1);
    165       curl_easy_perform (c);
    166       curl_easy_cleanup (c);
    167     }
    168   fprintf (stderr, "\n");
    169   zzuf_socat_stop ();
    170   MHD_stop_daemon (d);
    171   return 0;
    172 }
    173 
    174 static int
    175 testMultithreadedPut ()
    176 {
    177   struct MHD_Daemon *d;
    178   CURL *c;
    179   char buf[2048];
    180   struct CBC cbc;
    181   unsigned int pos = 0;
    182   int done_flag = 0;
    183   CURLcode errornum;
    184 
    185   cbc.buf = buf;
    186   cbc.size = 2048;
    187   cbc.pos = 0;
    188   d = MHD_start_daemon (MHD_USE_THREAD_PER_CONNECTION | MHD_USE_DEBUG,
    189                         11081,
    190                         NULL, NULL, &ahc_echo, &done_flag, MHD_OPTION_END);
    191   if (d == NULL)
    192     return 16;
    193   c = curl_easy_init ();
    194   curl_easy_setopt (c, CURLOPT_URL, "http://localhost:11081/hello_world");
    195   curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
    196   curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
    197   curl_easy_setopt (c, CURLOPT_READFUNCTION, &putBuffer);
    198   curl_easy_setopt (c, CURLOPT_READDATA, &pos);
    199   curl_easy_setopt (c, CURLOPT_UPLOAD, 1L);
    200   /*
    201      // by not giving the file size, we force chunking!
    202      curl_easy_setopt (c, CURLOPT_INFILESIZE_LARGE, (curl_off_t) 8L);
    203    */
    204   curl_easy_setopt (c, CURLOPT_FAILONERROR, 1);
    205   curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L);
    206   curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
    207   curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 15L);
    208   // NOTE: use of CONNECTTIMEOUT without also
    209   //   setting NOSIGNAL results in really weird
    210   //   crashes on my system!
    211   curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1);
    212   if (CURLE_OK != (errornum = curl_easy_perform (c)))
    213     {
    214       fprintf (stderr,
    215                "curl_easy_perform failed: `%s'\n",
    216                curl_easy_strerror (errornum));
    217       curl_easy_cleanup (c);
    218       MHD_stop_daemon (d);
    219       return 32;
    220     }
    221   curl_easy_cleanup (c);
    222   MHD_stop_daemon (d);
    223   if (cbc.pos != strlen ("/hello_world"))
    224     return 64;
    225   if (0 != strncmp ("/hello_world", cbc.buf, strlen ("/hello_world")))
    226     return 128;
    227 
    228   return 0;
    229 }
    230 
    231 
    232 static int
    233 testExternalPut ()
    234 {
    235   struct MHD_Daemon *d;
    236   CURL *c;
    237   char buf[2048];
    238   struct CBC cbc;
    239   CURLM *multi;
    240   CURLMcode mret;
    241   fd_set rs;
    242   fd_set ws;
    243   fd_set es;
    244   int max;
    245   int running;
    246   time_t start;
    247   struct timeval tv;
    248   unsigned int pos = 0;
    249   int done_flag = 0;
    250   int i;
    251 
    252   multi = NULL;
    253   cbc.buf = buf;
    254   cbc.size = 2048;
    255   cbc.pos = 0;
    256   d = MHD_start_daemon (MHD_USE_DEBUG,
    257                         11082,
    258                         NULL, NULL, &ahc_echo, &done_flag, MHD_OPTION_END);
    259   if (d == NULL)
    260     return 256;
    261 
    262   multi = curl_multi_init ();
    263   if (multi == NULL)
    264     {
    265       MHD_stop_daemon (d);
    266       return 512;
    267     }
    268   zzuf_socat_start ();
    269   for (i = 0; i < LOOP_COUNT; i++)
    270     {
    271       fprintf (stderr, ".");
    272       c = curl_easy_init ();
    273       curl_easy_setopt (c, CURLOPT_URL, "http://localhost:11082/hello_world");
    274       curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
    275       curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
    276       curl_easy_setopt (c, CURLOPT_READFUNCTION, &putBuffer);
    277       curl_easy_setopt (c, CURLOPT_READDATA, &pos);
    278       curl_easy_setopt (c, CURLOPT_UPLOAD, 1L);
    279       /*
    280          // by not giving the file size, we force chunking!
    281          curl_easy_setopt (c, CURLOPT_INFILESIZE_LARGE, (curl_off_t) 8L);
    282        */
    283       curl_easy_setopt (c, CURLOPT_FAILONERROR, 1);
    284       curl_easy_setopt (c, CURLOPT_TIMEOUT_MS, CURL_TIMEOUT);
    285       curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
    286       curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT_MS, CURL_TIMEOUT);
    287       // NOTE: use of CONNECTTIMEOUT without also
    288       //   setting NOSIGNAL results in really weird
    289       //   crashes on my system!
    290       curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1);
    291 
    292 
    293       mret = curl_multi_add_handle (multi, c);
    294       if (mret != CURLM_OK)
    295         {
    296           curl_multi_cleanup (multi);
    297           curl_easy_cleanup (c);
    298           zzuf_socat_stop ();
    299           MHD_stop_daemon (d);
    300           return 1024;
    301         }
    302       start = time (NULL);
    303       while ((time (NULL) - start < 5) && (c != NULL))
    304         {
    305           max = 0;
    306           FD_ZERO (&rs);
    307           FD_ZERO (&ws);
    308           FD_ZERO (&es);
    309           curl_multi_perform (multi, &running);
    310           mret = curl_multi_fdset (multi, &rs, &ws, &es, &max);
    311           if (mret != CURLM_OK)
    312             {
    313               curl_multi_remove_handle (multi, c);
    314               curl_multi_cleanup (multi);
    315               curl_easy_cleanup (c);
    316               zzuf_socat_stop ();
    317               MHD_stop_daemon (d);
    318               return 2048;
    319             }
    320           if (MHD_YES != MHD_get_fdset (d, &rs, &ws, &es, &max))
    321             {
    322               curl_multi_remove_handle (multi, c);
    323               curl_multi_cleanup (multi);
    324               curl_easy_cleanup (c);
    325               zzuf_socat_stop ();
    326               MHD_stop_daemon (d);
    327               return 4096;
    328             }
    329           tv.tv_sec = 0;
    330           tv.tv_usec = 1000;
    331           select (max + 1, &rs, &ws, &es, &tv);
    332           curl_multi_perform (multi, &running);
    333           if (running == 0)
    334             {
    335               curl_multi_info_read (multi, &running);
    336               curl_multi_remove_handle (multi, c);
    337               curl_easy_cleanup (c);
    338               c = NULL;
    339             }
    340           MHD_run (d);
    341         }
    342       if (c != NULL)
    343         {
    344           curl_multi_remove_handle (multi, c);
    345           curl_easy_cleanup (c);
    346         }
    347     }
    348   fprintf (stderr, "\n");
    349   curl_multi_cleanup (multi);
    350   zzuf_socat_stop ();
    351   MHD_stop_daemon (d);
    352   return 0;
    353 }
    354 
    355 
    356 
    357 int
    358 main (int argc, char *const *argv)
    359 {
    360   unsigned int errorCount = 0;
    361 
    362   if (0 != curl_global_init (CURL_GLOBAL_WIN32))
    363     return 2;
    364   errorCount += testInternalPut ();
    365   errorCount += testMultithreadedPut ();
    366   errorCount += testExternalPut ();
    367   if (errorCount != 0)
    368     fprintf (stderr, "Error (code: %u)\n", errorCount);
    369   curl_global_cleanup ();
    370   return errorCount != 0;       /* 0 == pass */
    371 }
    372