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