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