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_post_form.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 
     35 #ifndef WINDOWS
     36 #include <unistd.h>
     37 #endif
     38 
     39 
     40 #include "socat.c"
     41 
     42 static int oneone;
     43 
     44 struct CBC
     45 {
     46   char *buf;
     47   size_t pos;
     48   size_t size;
     49 };
     50 
     51 
     52 static void
     53 completed_cb (void *cls,
     54 	      struct MHD_Connection *connection,
     55 	      void **con_cls,
     56 	      enum MHD_RequestTerminationCode toe)
     57 {
     58   struct MHD_PostProcessor *pp = *con_cls;
     59 
     60   if (NULL != pp)
     61     MHD_destroy_post_processor (pp);
     62   *con_cls = NULL;
     63 }
     64 
     65 
     66 static size_t
     67 copyBuffer (void *ptr, size_t size, size_t nmemb, void *ctx)
     68 {
     69   struct CBC *cbc = ctx;
     70 
     71   if (cbc->pos + size * nmemb > cbc->size)
     72     return 0;                   /* overflow */
     73   memcpy (&cbc->buf[cbc->pos], ptr, size * nmemb);
     74   cbc->pos += size * nmemb;
     75   return size * nmemb;
     76 }
     77 
     78 /**
     79  * Note that this post_iterator is not perfect
     80  * in that it fails to support incremental processing.
     81  * (to be fixed in the future)
     82  */
     83 static int
     84 post_iterator (void *cls,
     85                enum MHD_ValueKind kind,
     86                const char *key,
     87                const char *filename,
     88                const char *content_type,
     89                const char *transfer_encoding,
     90                const char *value, uint64_t off, size_t size)
     91 {
     92   int *eok = cls;
     93 
     94   if (key == NULL)
     95     return MHD_YES;
     96 #if 0
     97   fprintf (stderr, "PI sees %s-%.*s\n", key, size, value);
     98 #endif
     99   if ((0 == strcmp (key, "name")) &&
    100       (size == strlen ("daniel")) && (0 == strncmp (value, "daniel", size)))
    101     (*eok) |= 1;
    102   if ((0 == strcmp (key, "project")) &&
    103       (size == strlen ("curl")) && (0 == strncmp (value, "curl", size)))
    104     (*eok) |= 2;
    105   return MHD_YES;
    106 }
    107 
    108 
    109 static int
    110 ahc_echo (void *cls,
    111           struct MHD_Connection *connection,
    112           const char *url,
    113           const char *method,
    114           const char *version,
    115           const char *upload_data, size_t *upload_data_size,
    116           void **unused)
    117 {
    118   static int eok;
    119   struct MHD_Response *response;
    120   struct MHD_PostProcessor *pp;
    121   int ret;
    122 
    123   if (0 != strcmp ("POST", method))
    124     {
    125       return MHD_NO;            /* unexpected method */
    126     }
    127   pp = *unused;
    128   if (pp == NULL)
    129     {
    130       eok = 0;
    131       pp = MHD_create_post_processor (connection, 1024, &post_iterator, &eok);
    132       if (pp == NULL)
    133         return MHD_NO;
    134       *unused = pp;
    135     }
    136   MHD_post_process (pp, upload_data, *upload_data_size);
    137   if ((eok == 3) && (0 == *upload_data_size))
    138     {
    139       response = MHD_create_response_from_buffer (strlen (url),
    140 						  (void *) url,
    141 						  MHD_RESPMEM_MUST_COPY);
    142       ret = MHD_queue_response (connection, MHD_HTTP_OK, response);
    143       MHD_destroy_response (response);
    144       MHD_destroy_post_processor (pp);
    145       *unused = NULL;
    146       return ret;
    147     }
    148   *upload_data_size = 0;
    149   return MHD_YES;
    150 }
    151 
    152 static struct curl_httppost *
    153 make_form ()
    154 {
    155   struct curl_httppost *post = NULL;
    156   struct curl_httppost *last = NULL;
    157 
    158   curl_formadd (&post, &last, CURLFORM_COPYNAME, "name",
    159                 CURLFORM_COPYCONTENTS, "daniel", CURLFORM_END);
    160   curl_formadd (&post, &last, CURLFORM_COPYNAME, "project",
    161                 CURLFORM_COPYCONTENTS, "curl", CURLFORM_END);
    162   return post;
    163 }
    164 
    165 
    166 static int
    167 testInternalPost ()
    168 {
    169   struct MHD_Daemon *d;
    170   CURL *c;
    171   char buf[2048];
    172   struct CBC cbc;
    173   int i;
    174   struct curl_httppost *pd;
    175 
    176   cbc.buf = buf;
    177   cbc.size = 2048;
    178   cbc.pos = 0;
    179   d = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY /* | MHD_USE_DEBUG */ ,
    180                         11080, NULL, NULL, &ahc_echo, NULL,
    181 			MHD_OPTION_NOTIFY_COMPLETED, &completed_cb, NULL,
    182 			MHD_OPTION_END);
    183   if (d == NULL)
    184     return 1;
    185   zzuf_socat_start ();
    186   for (i = 0; i < LOOP_COUNT; i++)
    187     {
    188       fprintf (stderr, ".");
    189       c = curl_easy_init ();
    190       curl_easy_setopt (c, CURLOPT_URL, "http://localhost:11081/hello_world");
    191       curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
    192       curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
    193       pd = make_form ();
    194       curl_easy_setopt (c, CURLOPT_HTTPPOST, pd);
    195       curl_easy_setopt (c, CURLOPT_FAILONERROR, 1);
    196       curl_easy_setopt (c, CURLOPT_TIMEOUT_MS, CURL_TIMEOUT);
    197       if (oneone)
    198         curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
    199       else
    200         curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
    201       curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT_MS, CURL_TIMEOUT);
    202       // NOTE: use of CONNECTTIMEOUT without also
    203       //   setting NOSIGNAL results in really weird
    204       //   crashes on my system!
    205       curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1);
    206       curl_easy_perform (c);
    207       curl_easy_cleanup (c);
    208       curl_formfree (pd);
    209     }
    210   fprintf (stderr, "\n");
    211   zzuf_socat_stop ();
    212   MHD_stop_daemon (d);
    213   return 0;
    214 }
    215 
    216 
    217 static int
    218 testMultithreadedPost ()
    219 {
    220   struct MHD_Daemon *d;
    221   CURL *c;
    222   char buf[2048];
    223   struct CBC cbc;
    224   int i;
    225   struct curl_httppost *pd;
    226 
    227   cbc.buf = buf;
    228   cbc.size = 2048;
    229   cbc.pos = 0;
    230   d = MHD_start_daemon (MHD_USE_THREAD_PER_CONNECTION /* | MHD_USE_DEBUG */ ,
    231                         11080, NULL, NULL, &ahc_echo, NULL,
    232 			MHD_OPTION_NOTIFY_COMPLETED, &completed_cb, NULL,
    233 			MHD_OPTION_END);
    234   if (d == NULL)
    235     return 16;
    236   zzuf_socat_start ();
    237   for (i = 0; i < LOOP_COUNT; i++)
    238     {
    239       fprintf (stderr, ".");
    240       c = curl_easy_init ();
    241       curl_easy_setopt (c, CURLOPT_URL, "http://localhost:11081/hello_world");
    242       curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
    243       curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
    244       pd = make_form ();
    245       curl_easy_setopt (c, CURLOPT_HTTPPOST, pd);
    246       curl_easy_setopt (c, CURLOPT_FAILONERROR, 1);
    247       curl_easy_setopt (c, CURLOPT_TIMEOUT_MS, CURL_TIMEOUT);
    248       if (oneone)
    249         curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
    250       else
    251         curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
    252       curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT_MS, CURL_TIMEOUT);
    253       // NOTE: use of CONNECTTIMEOUT without also
    254       //   setting NOSIGNAL results in really weird
    255       //   crashes on my system!
    256       curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1);
    257       curl_easy_perform (c);
    258       curl_easy_cleanup (c);
    259       curl_formfree (pd);
    260     }
    261   fprintf (stderr, "\n");
    262   zzuf_socat_stop ();
    263   MHD_stop_daemon (d);
    264   return 0;
    265 }
    266 
    267 
    268 static int
    269 testExternalPost ()
    270 {
    271   struct MHD_Daemon *d;
    272   CURL *c;
    273   char buf[2048];
    274   struct CBC cbc;
    275   CURLM *multi;
    276   CURLMcode mret;
    277   fd_set rs;
    278   fd_set ws;
    279   fd_set es;
    280   int max;
    281   int running;
    282   time_t start;
    283   struct timeval tv;
    284   struct curl_httppost *pd;
    285   int i;
    286 
    287   multi = NULL;
    288   cbc.buf = buf;
    289   cbc.size = 2048;
    290   cbc.pos = 0;
    291   d = MHD_start_daemon (MHD_NO_FLAG /* | MHD_USE_DEBUG */ ,
    292                         1082, NULL, NULL, &ahc_echo, NULL,
    293 			MHD_OPTION_NOTIFY_COMPLETED, &completed_cb, NULL,
    294 			MHD_OPTION_END);
    295   if (d == NULL)
    296     return 256;
    297   multi = curl_multi_init ();
    298   if (multi == NULL)
    299     {
    300       MHD_stop_daemon (d);
    301       return 512;
    302     }
    303   zzuf_socat_start ();
    304   for (i = 0; i < LOOP_COUNT; i++)
    305     {
    306       fprintf (stderr, ".");
    307 
    308       c = curl_easy_init ();
    309       curl_easy_setopt (c, CURLOPT_URL, "http://localhost:1082/hello_world");
    310       curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
    311       curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
    312       pd = make_form ();
    313       curl_easy_setopt (c, CURLOPT_HTTPPOST, pd);
    314       curl_easy_setopt (c, CURLOPT_FAILONERROR, 1);
    315       curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L);
    316       if (oneone)
    317         curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
    318       else
    319         curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
    320       curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 15L);
    321       // NOTE: use of CONNECTTIMEOUT without also
    322       //   setting NOSIGNAL results in really weird
    323       //   crashes on my system!
    324       curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1);
    325 
    326 
    327       mret = curl_multi_add_handle (multi, c);
    328       if (mret != CURLM_OK)
    329         {
    330           curl_multi_cleanup (multi);
    331           curl_formfree (pd);
    332           curl_easy_cleanup (c);
    333           zzuf_socat_stop ();
    334           MHD_stop_daemon (d);
    335           return 1024;
    336         }
    337       start = time (NULL);
    338       while ((time (NULL) - start < 5) && (c != NULL))
    339         {
    340           max = 0;
    341           FD_ZERO (&rs);
    342           FD_ZERO (&ws);
    343           FD_ZERO (&es);
    344           curl_multi_perform (multi, &running);
    345           mret = curl_multi_fdset (multi, &rs, &ws, &es, &max);
    346           if (mret != CURLM_OK)
    347             {
    348               curl_multi_remove_handle (multi, c);
    349               curl_multi_cleanup (multi);
    350               curl_easy_cleanup (c);
    351               zzuf_socat_stop ();
    352               MHD_stop_daemon (d);
    353               curl_formfree (pd);
    354               return 2048;
    355             }
    356           if (MHD_YES != MHD_get_fdset (d, &rs, &ws, &es, &max))
    357             {
    358               curl_multi_remove_handle (multi, c);
    359               curl_multi_cleanup (multi);
    360               curl_easy_cleanup (c);
    361               curl_formfree (pd);
    362               zzuf_socat_stop ();
    363               MHD_stop_daemon (d);
    364               return 4096;
    365             }
    366           tv.tv_sec = 0;
    367           tv.tv_usec = 1000;
    368           select (max + 1, &rs, &ws, &es, &tv);
    369           curl_multi_perform (multi, &running);
    370           if (running == 0)
    371             {
    372               curl_multi_info_read (multi, &running);
    373               curl_multi_remove_handle (multi, c);
    374               curl_easy_cleanup (c);
    375               c = NULL;
    376             }
    377           MHD_run (d);
    378         }
    379       if (c != NULL)
    380         {
    381           curl_multi_remove_handle (multi, c);
    382           curl_easy_cleanup (c);
    383         }
    384       curl_formfree (pd);
    385     }
    386   fprintf (stderr, "\n");
    387   zzuf_socat_stop ();
    388 
    389   MHD_stop_daemon (d);
    390   return 0;
    391 }
    392 
    393 
    394 int
    395 main (int argc, char *const *argv)
    396 {
    397   unsigned int errorCount = 0;
    398 
    399   oneone = (NULL != strrchr (argv[0], (int) '/')) ?
    400     (NULL != strstr (strrchr (argv[0], (int) '/'), "11")) : 0;
    401   if (0 != curl_global_init (CURL_GLOBAL_WIN32))
    402     return 2;
    403   errorCount += testInternalPost ();
    404   errorCount += testMultithreadedPost ();
    405   errorCount += testExternalPost ();
    406   if (errorCount != 0)
    407     fprintf (stderr, "Error (code: %u)\n", errorCount);
    408   curl_global_cleanup ();
    409   return errorCount != 0;       /* 0 == pass */
    410 }
    411