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 test_postform.c
     23  * @brief  Testcase for libmicrohttpd POST operations using multipart/postform data
     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 #ifdef HAVE_GCRYPT_H
     35 #include <gcrypt.h>
     36 #endif
     37 
     38 #ifndef WINDOWS
     39 #include <unistd.h>
     40 #endif
     41 
     42 #if defined(CPU_COUNT) && (CPU_COUNT+0) < 2
     43 #undef CPU_COUNT
     44 #endif
     45 #if !defined(CPU_COUNT)
     46 #define CPU_COUNT 2
     47 #endif
     48 
     49 static int oneone;
     50 
     51 struct CBC
     52 {
     53   char *buf;
     54   size_t pos;
     55   size_t size;
     56 };
     57 
     58 
     59 static void
     60 completed_cb (void *cls,
     61 	      struct MHD_Connection *connection,
     62 	      void **con_cls,
     63 	      enum MHD_RequestTerminationCode toe)
     64 {
     65   struct MHD_PostProcessor *pp = *con_cls;
     66 
     67   if (NULL != pp)
     68     MHD_destroy_post_processor (pp);
     69   *con_cls = NULL;
     70 }
     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 
     86 /**
     87  * Note that this post_iterator is not perfect
     88  * in that it fails to support incremental processing.
     89  * (to be fixed in the future)
     90  */
     91 static int
     92 post_iterator (void *cls,
     93                enum MHD_ValueKind kind,
     94                const char *key,
     95                const char *filename,
     96                const char *content_type,
     97                const char *transfer_encoding,
     98                const char *value, uint64_t off, size_t size)
     99 {
    100   int *eok = cls;
    101 
    102 #if 0
    103   fprintf (stderr, "PI sees %s-%.*s\n", key, size, value);
    104 #endif
    105   if ((0 == strcmp (key, "name")) &&
    106       (size == strlen ("daniel")) && (0 == strncmp (value, "daniel", size)))
    107     (*eok) |= 1;
    108   if ((0 == strcmp (key, "project")) &&
    109       (size == strlen ("curl")) && (0 == strncmp (value, "curl", size)))
    110     (*eok) |= 2;
    111   return MHD_YES;
    112 }
    113 
    114 
    115 static int
    116 ahc_echo (void *cls,
    117           struct MHD_Connection *connection,
    118           const char *url,
    119           const char *method,
    120           const char *version,
    121           const char *upload_data, size_t *upload_data_size,
    122           void **unused)
    123 {
    124   static int eok;
    125   struct MHD_Response *response;
    126   struct MHD_PostProcessor *pp;
    127   int ret;
    128 
    129   if (0 != strcmp ("POST", method))
    130     {
    131       printf ("METHOD: %s\n", method);
    132       return MHD_NO;            /* unexpected method */
    133     }
    134   pp = *unused;
    135   if (pp == NULL)
    136     {
    137       eok = 0;
    138       pp = MHD_create_post_processor (connection, 1024, &post_iterator, &eok);
    139       if (pp == NULL)
    140         abort ();
    141       *unused = pp;
    142     }
    143   MHD_post_process (pp, upload_data, *upload_data_size);
    144   if ((eok == 3) && (0 == *upload_data_size))
    145     {
    146       response = MHD_create_response_from_buffer (strlen (url),
    147 						  (void *) url,
    148 						  MHD_RESPMEM_MUST_COPY);
    149       ret = MHD_queue_response (connection, MHD_HTTP_OK, response);
    150       MHD_destroy_response (response);
    151       MHD_destroy_post_processor (pp);
    152       *unused = NULL;
    153       return ret;
    154     }
    155   *upload_data_size = 0;
    156   return MHD_YES;
    157 }
    158 
    159 static struct curl_httppost *
    160 make_form ()
    161 {
    162   struct curl_httppost *post = NULL;
    163   struct curl_httppost *last = NULL;
    164 
    165   curl_formadd (&post, &last, CURLFORM_COPYNAME, "name",
    166                 CURLFORM_COPYCONTENTS, "daniel", CURLFORM_END);
    167   curl_formadd (&post, &last, CURLFORM_COPYNAME, "project",
    168                 CURLFORM_COPYCONTENTS, "curl", CURLFORM_END);
    169   return post;
    170 }
    171 
    172 
    173 static int
    174 testInternalPost ()
    175 {
    176   struct MHD_Daemon *d;
    177   CURL *c;
    178   char buf[2048];
    179   struct CBC cbc;
    180   CURLcode errornum;
    181   struct curl_httppost *pd;
    182 
    183   cbc.buf = buf;
    184   cbc.size = 2048;
    185   cbc.pos = 0;
    186   d = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY | MHD_USE_DEBUG,
    187                         1080, NULL, NULL, &ahc_echo, NULL,
    188 			MHD_OPTION_NOTIFY_COMPLETED, &completed_cb, NULL,
    189 			MHD_OPTION_END);
    190   if (d == NULL)
    191     return 1;
    192   c = curl_easy_init ();
    193   curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1:1080/hello_world");
    194   curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
    195   curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
    196   pd = make_form ();
    197   curl_easy_setopt (c, CURLOPT_HTTPPOST, pd);
    198   curl_easy_setopt (c, CURLOPT_FAILONERROR, 1);
    199   curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L);
    200   if (oneone)
    201     curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
    202   else
    203     curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
    204   curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L);
    205   // NOTE: use of CONNECTTIMEOUT without also
    206   //   setting NOSIGNAL results in really weird
    207   //   crashes on my system!
    208   curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1);
    209   if (CURLE_OK != (errornum = curl_easy_perform (c)))
    210     {
    211       fprintf (stderr,
    212                "curl_easy_perform failed: `%s'\n",
    213                curl_easy_strerror (errornum));
    214       curl_easy_cleanup (c);
    215       curl_formfree (pd);
    216       MHD_stop_daemon (d);
    217       return 2;
    218     }
    219   curl_easy_cleanup (c);
    220   curl_formfree (pd);
    221   MHD_stop_daemon (d);
    222   if (cbc.pos != strlen ("/hello_world"))
    223     return 4;
    224   if (0 != strncmp ("/hello_world", cbc.buf, strlen ("/hello_world")))
    225     return 8;
    226   return 0;
    227 }
    228 
    229 static int
    230 testMultithreadedPost ()
    231 {
    232   struct MHD_Daemon *d;
    233   CURL *c;
    234   char buf[2048];
    235   struct CBC cbc;
    236   CURLcode errornum;
    237   struct curl_httppost *pd;
    238 
    239   cbc.buf = buf;
    240   cbc.size = 2048;
    241   cbc.pos = 0;
    242   d = MHD_start_daemon (MHD_USE_THREAD_PER_CONNECTION | MHD_USE_DEBUG,
    243                         1081, NULL, NULL, &ahc_echo, NULL,
    244 			MHD_OPTION_NOTIFY_COMPLETED, &completed_cb, NULL,
    245 			MHD_OPTION_END);
    246   if (d == NULL)
    247     return 16;
    248   c = curl_easy_init ();
    249   curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1:1081/hello_world");
    250   curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
    251   curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
    252   pd = make_form ();
    253   curl_easy_setopt (c, CURLOPT_HTTPPOST, pd);
    254   curl_easy_setopt (c, CURLOPT_FAILONERROR, 1);
    255   curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L);
    256   if (oneone)
    257     curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
    258   else
    259     curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
    260   curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 5L);
    261   // NOTE: use of CONNECTTIMEOUT without also
    262   //   setting NOSIGNAL results in really weird
    263   //   crashes on my system!
    264   curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1);
    265   if (CURLE_OK != (errornum = curl_easy_perform (c)))
    266     {
    267       fprintf (stderr,
    268                "curl_easy_perform failed: `%s'\n",
    269                curl_easy_strerror (errornum));
    270       curl_easy_cleanup (c);
    271       curl_formfree (pd);
    272       MHD_stop_daemon (d);
    273       return 32;
    274     }
    275   curl_easy_cleanup (c);
    276   curl_formfree (pd);
    277   MHD_stop_daemon (d);
    278   if (cbc.pos != strlen ("/hello_world"))
    279     return 64;
    280   if (0 != strncmp ("/hello_world", cbc.buf, strlen ("/hello_world")))
    281     return 128;
    282   return 0;
    283 }
    284 
    285 static int
    286 testMultithreadedPoolPost ()
    287 {
    288   struct MHD_Daemon *d;
    289   CURL *c;
    290   char buf[2048];
    291   struct CBC cbc;
    292   CURLcode errornum;
    293   struct curl_httppost *pd;
    294 
    295   cbc.buf = buf;
    296   cbc.size = 2048;
    297   cbc.pos = 0;
    298   d = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY | MHD_USE_DEBUG,
    299                         1081, NULL, NULL, &ahc_echo, NULL,
    300                         MHD_OPTION_THREAD_POOL_SIZE, CPU_COUNT,
    301 			MHD_OPTION_NOTIFY_COMPLETED, &completed_cb, NULL,
    302 			MHD_OPTION_END);
    303   if (d == NULL)
    304     return 16;
    305   c = curl_easy_init ();
    306   curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1:1081/hello_world");
    307   curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
    308   curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
    309   pd = make_form ();
    310   curl_easy_setopt (c, CURLOPT_HTTPPOST, pd);
    311   curl_easy_setopt (c, CURLOPT_FAILONERROR, 1);
    312   curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L);
    313   if (oneone)
    314     curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
    315   else
    316     curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
    317   curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 5L);
    318   // NOTE: use of CONNECTTIMEOUT without also
    319   //   setting NOSIGNAL results in really weird
    320   //   crashes on my system!
    321   curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1);
    322   if (CURLE_OK != (errornum = curl_easy_perform (c)))
    323     {
    324       fprintf (stderr,
    325                "curl_easy_perform failed: `%s'\n",
    326                curl_easy_strerror (errornum));
    327       curl_easy_cleanup (c);
    328       curl_formfree (pd);
    329       MHD_stop_daemon (d);
    330       return 32;
    331     }
    332   curl_easy_cleanup (c);
    333   curl_formfree (pd);
    334   MHD_stop_daemon (d);
    335   if (cbc.pos != strlen ("/hello_world"))
    336     return 64;
    337   if (0 != strncmp ("/hello_world", cbc.buf, strlen ("/hello_world")))
    338     return 128;
    339   return 0;
    340 }
    341 
    342 static int
    343 testExternalPost ()
    344 {
    345   struct MHD_Daemon *d;
    346   CURL *c;
    347   char buf[2048];
    348   struct CBC cbc;
    349   CURLM *multi;
    350   CURLMcode mret;
    351   fd_set rs;
    352   fd_set ws;
    353   fd_set es;
    354   MHD_socket max;
    355   int running;
    356   struct CURLMsg *msg;
    357   time_t start;
    358   struct timeval tv;
    359   struct curl_httppost *pd;
    360 
    361   multi = NULL;
    362   cbc.buf = buf;
    363   cbc.size = 2048;
    364   cbc.pos = 0;
    365   d = MHD_start_daemon (MHD_USE_DEBUG,
    366                         1082, NULL, NULL, &ahc_echo, NULL,
    367 			MHD_OPTION_NOTIFY_COMPLETED, &completed_cb, NULL,
    368 			MHD_OPTION_END);
    369   if (d == NULL)
    370     return 256;
    371   c = curl_easy_init ();
    372   curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1:1082/hello_world");
    373   curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
    374   curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
    375   pd = make_form ();
    376   curl_easy_setopt (c, CURLOPT_HTTPPOST, pd);
    377   curl_easy_setopt (c, CURLOPT_FAILONERROR, 1);
    378   curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L);
    379   if (oneone)
    380     curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
    381   else
    382     curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
    383   curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L);
    384   // NOTE: use of CONNECTTIMEOUT without also
    385   //   setting NOSIGNAL results in really weird
    386   //   crashes on my system!
    387   curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1);
    388 
    389 
    390   multi = curl_multi_init ();
    391   if (multi == NULL)
    392     {
    393       curl_easy_cleanup (c);
    394       curl_formfree (pd);
    395       MHD_stop_daemon (d);
    396       return 512;
    397     }
    398   mret = curl_multi_add_handle (multi, c);
    399   if (mret != CURLM_OK)
    400     {
    401       curl_multi_cleanup (multi);
    402       curl_formfree (pd);
    403       curl_easy_cleanup (c);
    404       MHD_stop_daemon (d);
    405       return 1024;
    406     }
    407   start = time (NULL);
    408   while ((time (NULL) - start < 5) && (multi != NULL))
    409     {
    410       max = 0;
    411       FD_ZERO (&rs);
    412       FD_ZERO (&ws);
    413       FD_ZERO (&es);
    414       curl_multi_perform (multi, &running);
    415       mret = curl_multi_fdset (multi, &rs, &ws, &es, &max);
    416       if (mret != CURLM_OK)
    417         {
    418           curl_multi_remove_handle (multi, c);
    419           curl_multi_cleanup (multi);
    420           curl_easy_cleanup (c);
    421           MHD_stop_daemon (d);
    422           curl_formfree (pd);
    423           return 2048;
    424         }
    425       if (MHD_YES != MHD_get_fdset (d, &rs, &ws, &es, &max))
    426         {
    427           curl_multi_remove_handle (multi, c);
    428           curl_multi_cleanup (multi);
    429           curl_easy_cleanup (c);
    430           curl_formfree (pd);
    431           MHD_stop_daemon (d);
    432           return 4096;
    433         }
    434       tv.tv_sec = 0;
    435       tv.tv_usec = 1000;
    436       select (max + 1, &rs, &ws, &es, &tv);
    437       curl_multi_perform (multi, &running);
    438       if (running == 0)
    439         {
    440           msg = curl_multi_info_read (multi, &running);
    441           if (msg == NULL)
    442             break;
    443           if (msg->msg == CURLMSG_DONE)
    444             {
    445               if (msg->data.result != CURLE_OK)
    446                 printf ("%s failed at %s:%d: `%s'\n",
    447                         "curl_multi_perform",
    448                         __FILE__,
    449                         __LINE__, curl_easy_strerror (msg->data.result));
    450               curl_multi_remove_handle (multi, c);
    451               curl_multi_cleanup (multi);
    452               curl_easy_cleanup (c);
    453               c = NULL;
    454               multi = NULL;
    455             }
    456         }
    457       MHD_run (d);
    458     }
    459   if (multi != NULL)
    460     {
    461       curl_multi_remove_handle (multi, c);
    462       curl_easy_cleanup (c);
    463       curl_multi_cleanup (multi);
    464     }
    465   curl_formfree (pd);
    466   MHD_stop_daemon (d);
    467   if (cbc.pos != strlen ("/hello_world"))
    468     return 8192;
    469   if (0 != strncmp ("/hello_world", cbc.buf, strlen ("/hello_world")))
    470     return 16384;
    471   return 0;
    472 }
    473 
    474 
    475 int
    476 main (int argc, char *const *argv)
    477 {
    478   unsigned int errorCount = 0;
    479 
    480 #ifdef HAVE_GCRYPT_H
    481   gcry_control (GCRYCTL_ENABLE_QUICK_RANDOM, 0);
    482 #ifdef GCRYCTL_INITIALIZATION_FINISHED
    483   gcry_control (GCRYCTL_INITIALIZATION_FINISHED, 0);
    484 #endif
    485 #endif
    486   oneone = (NULL != strrchr (argv[0], (int) '/')) ?
    487     (NULL != strstr (strrchr (argv[0], (int) '/'), "11")) : 0;
    488   if (0 != curl_global_init (CURL_GLOBAL_WIN32))
    489     return 2;
    490   errorCount += testInternalPost ();
    491   errorCount += testMultithreadedPost ();
    492   errorCount += testMultithreadedPoolPost ();
    493   errorCount += testExternalPost ();
    494   if (errorCount != 0)
    495     fprintf (stderr, "Error (code: %u)\n", errorCount);
    496   curl_global_cleanup ();
    497   return errorCount != 0;       /* 0 == pass */
    498 }
    499