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_post_loop.c
     23  * @brief  Testcase for libmicrohttpd POST operations using URL-encoding
     24  * @author Christian Grothoff (inspired by bug report #1296)
     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 #include <gauger.h>
     35 
     36 #ifndef WINDOWS
     37 #include <unistd.h>
     38 #endif
     39 
     40 #if defined(CPU_COUNT) && (CPU_COUNT+0) < 2
     41 #undef CPU_COUNT
     42 #endif
     43 #if !defined(CPU_COUNT)
     44 #define CPU_COUNT 2
     45 #endif
     46 
     47 #define POST_DATA "<?xml version='1.0' ?>\n<xml>\n<data-id>1</data-id>\n</xml>\n"
     48 
     49 #define LOOPCOUNT 1000
     50 
     51 static int oneone;
     52 
     53 struct CBC
     54 {
     55   char *buf;
     56   size_t pos;
     57   size_t size;
     58 };
     59 
     60 static size_t
     61 copyBuffer (void *ptr, size_t size, size_t nmemb, void *ctx)
     62 {
     63   struct CBC *cbc = ctx;
     64 
     65   if (cbc->pos + size * nmemb > cbc->size)
     66     return 0;                   /* overflow */
     67   memcpy (&cbc->buf[cbc->pos], ptr, size * nmemb);
     68   cbc->pos += size * nmemb;
     69   return size * nmemb;
     70 }
     71 
     72 static int
     73 ahc_echo (void *cls,
     74           struct MHD_Connection *connection,
     75           const char *url,
     76           const char *method,
     77           const char *version,
     78           const char *upload_data, size_t *upload_data_size,
     79           void **mptr)
     80 {
     81   static int marker;
     82   struct MHD_Response *response;
     83   int ret;
     84 
     85   if (0 != strcmp ("POST", method))
     86     {
     87       printf ("METHOD: %s\n", method);
     88       return MHD_NO;            /* unexpected method */
     89     }
     90   if ((*mptr != NULL) && (0 == *upload_data_size))
     91     {
     92       if (*mptr != &marker)
     93         abort ();
     94       response = MHD_create_response_from_buffer (2, "OK",
     95 						  MHD_RESPMEM_PERSISTENT);
     96       ret = MHD_queue_response (connection, MHD_HTTP_OK, response);
     97       MHD_destroy_response (response);
     98       *mptr = NULL;
     99       return ret;
    100     }
    101   if (strlen (POST_DATA) != *upload_data_size)
    102     return MHD_YES;
    103   *upload_data_size = 0;
    104   *mptr = &marker;
    105   return MHD_YES;
    106 }
    107 
    108 
    109 static int
    110 testInternalPost ()
    111 {
    112   struct MHD_Daemon *d;
    113   CURL *c;
    114   char buf[2048];
    115   struct CBC cbc;
    116   CURLcode errornum;
    117   int i;
    118   char url[1024];
    119 
    120   cbc.buf = buf;
    121   cbc.size = 2048;
    122   d = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY | MHD_USE_DEBUG,
    123                         1080, NULL, NULL, &ahc_echo, NULL, MHD_OPTION_END);
    124   if (d == NULL)
    125     return 1;
    126   for (i = 0; i < LOOPCOUNT; i++)
    127     {
    128       if (99 == i % 100)
    129         fprintf (stderr, ".");
    130       c = curl_easy_init ();
    131       cbc.pos = 0;
    132       buf[0] = '\0';
    133       sprintf (url, "http://127.0.0.1:1080/hw%d", i);
    134       curl_easy_setopt (c, CURLOPT_URL, url);
    135       curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
    136       curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
    137       curl_easy_setopt (c, CURLOPT_POSTFIELDS, POST_DATA);
    138       curl_easy_setopt (c, CURLOPT_POSTFIELDSIZE, strlen (POST_DATA));
    139       curl_easy_setopt (c, CURLOPT_POST, 1L);
    140       curl_easy_setopt (c, CURLOPT_FAILONERROR, 1);
    141       curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L);
    142       if (oneone)
    143         curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
    144       else
    145         curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
    146       curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L);
    147       // NOTE: use of CONNECTTIMEOUT without also
    148       //   setting NOSIGNAL results in really weird
    149       //   crashes on my system!
    150       curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1);
    151       if (CURLE_OK != (errornum = curl_easy_perform (c)))
    152         {
    153           fprintf (stderr,
    154                    "curl_easy_perform failed: `%s'\n",
    155                    curl_easy_strerror (errornum));
    156           curl_easy_cleanup (c);
    157           MHD_stop_daemon (d);
    158           return 2;
    159         }
    160       curl_easy_cleanup (c);
    161       if ((buf[0] != 'O') || (buf[1] != 'K'))
    162         {
    163           MHD_stop_daemon (d);
    164           return 4;
    165         }
    166     }
    167   MHD_stop_daemon (d);
    168   if (LOOPCOUNT >= 99)
    169     fprintf (stderr, "\n");
    170   return 0;
    171 }
    172 
    173 static int
    174 testMultithreadedPost ()
    175 {
    176   struct MHD_Daemon *d;
    177   CURL *c;
    178   char buf[2048];
    179   struct CBC cbc;
    180   CURLcode errornum;
    181   int i;
    182   char url[1024];
    183 
    184   cbc.buf = buf;
    185   cbc.size = 2048;
    186   d = MHD_start_daemon (MHD_USE_THREAD_PER_CONNECTION | MHD_USE_DEBUG,
    187                         1081, NULL, NULL, &ahc_echo, NULL, MHD_OPTION_END);
    188   if (d == NULL)
    189     return 16;
    190   for (i = 0; i < LOOPCOUNT; i++)
    191     {
    192       if (99 == i % 100)
    193         fprintf (stderr, ".");
    194       c = curl_easy_init ();
    195       cbc.pos = 0;
    196       buf[0] = '\0';
    197       sprintf (url, "http://127.0.0.1:1081/hw%d", i);
    198       curl_easy_setopt (c, CURLOPT_URL, url);
    199       curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
    200       curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
    201       curl_easy_setopt (c, CURLOPT_POSTFIELDS, POST_DATA);
    202       curl_easy_setopt (c, CURLOPT_POSTFIELDSIZE, strlen (POST_DATA));
    203       curl_easy_setopt (c, CURLOPT_POST, 1L);
    204       curl_easy_setopt (c, CURLOPT_FAILONERROR, 1);
    205       curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L);
    206       if (oneone)
    207         curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
    208       else
    209         curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
    210       curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L);
    211       // NOTE: use of CONNECTTIMEOUT without also
    212       //   setting NOSIGNAL results in really weird
    213       //   crashes on my system!
    214       curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1);
    215       if (CURLE_OK != (errornum = curl_easy_perform (c)))
    216         {
    217           fprintf (stderr,
    218                    "curl_easy_perform failed: `%s'\n",
    219                    curl_easy_strerror (errornum));
    220           curl_easy_cleanup (c);
    221           MHD_stop_daemon (d);
    222           return 32;
    223         }
    224       curl_easy_cleanup (c);
    225       if ((buf[0] != 'O') || (buf[1] != 'K'))
    226         {
    227           MHD_stop_daemon (d);
    228           return 64;
    229         }
    230     }
    231   MHD_stop_daemon (d);
    232   if (LOOPCOUNT >= 99)
    233     fprintf (stderr, "\n");
    234   return 0;
    235 }
    236 
    237 static int
    238 testMultithreadedPoolPost ()
    239 {
    240   struct MHD_Daemon *d;
    241   CURL *c;
    242   char buf[2048];
    243   struct CBC cbc;
    244   CURLcode errornum;
    245   int i;
    246   char url[1024];
    247 
    248   cbc.buf = buf;
    249   cbc.size = 2048;
    250   d = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY | MHD_USE_DEBUG,
    251                         1081, NULL, NULL, &ahc_echo, NULL,
    252                         MHD_OPTION_THREAD_POOL_SIZE, CPU_COUNT, MHD_OPTION_END);
    253   if (d == NULL)
    254     return 16;
    255   for (i = 0; i < LOOPCOUNT; i++)
    256     {
    257       if (99 == i % 100)
    258         fprintf (stderr, ".");
    259       c = curl_easy_init ();
    260       cbc.pos = 0;
    261       buf[0] = '\0';
    262       sprintf (url, "http://127.0.0.1:1081/hw%d", i);
    263       curl_easy_setopt (c, CURLOPT_URL, url);
    264       curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
    265       curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
    266       curl_easy_setopt (c, CURLOPT_POSTFIELDS, POST_DATA);
    267       curl_easy_setopt (c, CURLOPT_POSTFIELDSIZE, strlen (POST_DATA));
    268       curl_easy_setopt (c, CURLOPT_POST, 1L);
    269       curl_easy_setopt (c, CURLOPT_FAILONERROR, 1);
    270       curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L);
    271       if (oneone)
    272         curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
    273       else
    274         curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
    275       curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L);
    276       // NOTE: use of CONNECTTIMEOUT without also
    277       //   setting NOSIGNAL results in really weird
    278       //   crashes on my system!
    279       curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1);
    280       if (CURLE_OK != (errornum = curl_easy_perform (c)))
    281         {
    282           fprintf (stderr,
    283                    "curl_easy_perform failed: `%s'\n",
    284                    curl_easy_strerror (errornum));
    285           curl_easy_cleanup (c);
    286           MHD_stop_daemon (d);
    287           return 32;
    288         }
    289       curl_easy_cleanup (c);
    290       if ((buf[0] != 'O') || (buf[1] != 'K'))
    291         {
    292           MHD_stop_daemon (d);
    293           return 64;
    294         }
    295     }
    296   MHD_stop_daemon (d);
    297   if (LOOPCOUNT >= 99)
    298     fprintf (stderr, "\n");
    299   return 0;
    300 }
    301 
    302 static int
    303 testExternalPost ()
    304 {
    305   struct MHD_Daemon *d;
    306   CURL *c;
    307   char buf[2048];
    308   struct CBC cbc;
    309   CURLM *multi;
    310   CURLMcode mret;
    311   fd_set rs;
    312   fd_set ws;
    313   fd_set es;
    314   MHD_socket max;
    315   int running;
    316   struct CURLMsg *msg;
    317   time_t start;
    318   struct timeval tv;
    319   int i;
    320   unsigned long long timeout;
    321   long ctimeout;
    322   char url[1024];
    323 
    324   multi = NULL;
    325   cbc.buf = buf;
    326   cbc.size = 2048;
    327   cbc.pos = 0;
    328   d = MHD_start_daemon (MHD_USE_DEBUG,
    329                         1082, NULL, NULL, &ahc_echo, NULL, MHD_OPTION_END);
    330   if (d == NULL)
    331     return 256;
    332   multi = curl_multi_init ();
    333   if (multi == NULL)
    334     {
    335       MHD_stop_daemon (d);
    336       return 512;
    337     }
    338   for (i = 0; i < LOOPCOUNT; i++)
    339     {
    340       if (99 == i % 100)
    341 	fprintf (stderr, ".");
    342       c = curl_easy_init ();
    343       cbc.pos = 0;
    344       buf[0] = '\0';
    345       sprintf (url, "http://127.0.0.1:1082/hw%d", i);
    346       curl_easy_setopt (c, CURLOPT_URL, url);
    347       curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
    348       curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
    349       curl_easy_setopt (c, CURLOPT_POSTFIELDS, POST_DATA);
    350       curl_easy_setopt (c, CURLOPT_POSTFIELDSIZE, strlen (POST_DATA));
    351       curl_easy_setopt (c, CURLOPT_POST, 1L);
    352       curl_easy_setopt (c, CURLOPT_FAILONERROR, 1);
    353       curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L);
    354       if (oneone)
    355         curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
    356       else
    357         curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
    358       curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L);
    359       // NOTE: use of CONNECTTIMEOUT without also
    360       //   setting NOSIGNAL results in really weird
    361       //   crashes on my system!
    362       curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1);
    363       mret = curl_multi_add_handle (multi, c);
    364       if (mret != CURLM_OK)
    365         {
    366           curl_multi_cleanup (multi);
    367           curl_easy_cleanup (c);
    368           MHD_stop_daemon (d);
    369           return 1024;
    370         }
    371       start = time (NULL);
    372       while ((time (NULL) - start < 5) && (multi != NULL))
    373         {
    374           max = 0;
    375           FD_ZERO (&rs);
    376           FD_ZERO (&ws);
    377           FD_ZERO (&es);
    378           while (CURLM_CALL_MULTI_PERFORM ==
    379                  curl_multi_perform (multi, &running));
    380           mret = curl_multi_fdset (multi, &rs, &ws, &es, &max);
    381           if (mret != CURLM_OK)
    382             {
    383               curl_multi_remove_handle (multi, c);
    384               curl_multi_cleanup (multi);
    385               curl_easy_cleanup (c);
    386               MHD_stop_daemon (d);
    387               return 2048;
    388             }
    389           if (MHD_YES != MHD_get_fdset (d, &rs, &ws, &es, &max))
    390             {
    391               curl_multi_remove_handle (multi, c);
    392               curl_multi_cleanup (multi);
    393               curl_easy_cleanup (c);
    394               MHD_stop_daemon (d);
    395               return 4096;
    396             }
    397           if (MHD_NO == MHD_get_timeout (d, &timeout))
    398             timeout = 100;      /* 100ms == INFTY -- CURL bug... */
    399           if ((CURLM_OK == curl_multi_timeout (multi, &ctimeout)) &&
    400               (ctimeout < timeout) && (ctimeout >= 0))
    401             timeout = ctimeout;
    402 	  if ( (c == NULL) || (running == 0) )
    403 	    timeout = 0; /* terminate quickly... */
    404           tv.tv_sec = timeout / 1000;
    405           tv.tv_usec = (timeout % 1000) * 1000;
    406           if (-1 == select (max + 1, &rs, &ws, &es, &tv))
    407 	    {
    408 	      if (EINTR == errno)
    409 		continue;
    410 	      fprintf (stderr,
    411 		       "select failed: %s\n",
    412 		       strerror (errno));
    413 	      break;
    414 	    }
    415           while (CURLM_CALL_MULTI_PERFORM ==
    416                  curl_multi_perform (multi, &running));
    417           if (running == 0)
    418             {
    419               msg = curl_multi_info_read (multi, &running);
    420               if (msg == NULL)
    421                 break;
    422               if (msg->msg == CURLMSG_DONE)
    423                 {
    424                   if (msg->data.result != CURLE_OK)
    425                     printf ("%s failed at %s:%d: `%s'\n",
    426                             "curl_multi_perform",
    427                             __FILE__,
    428                             __LINE__, curl_easy_strerror (msg->data.result));
    429                   curl_multi_remove_handle (multi, c);
    430                   curl_easy_cleanup (c);
    431                   c = NULL;
    432                 }
    433             }
    434           MHD_run (d);
    435         }
    436       if (c != NULL)
    437         {
    438           curl_multi_remove_handle (multi, c);
    439           curl_easy_cleanup (c);
    440         }
    441       if ((buf[0] != 'O') || (buf[1] != 'K'))
    442         {
    443           curl_multi_cleanup (multi);
    444           MHD_stop_daemon (d);
    445           return 8192;
    446         }
    447     }
    448   curl_multi_cleanup (multi);
    449   MHD_stop_daemon (d);
    450   if (LOOPCOUNT >= 99)
    451     fprintf (stderr, "\n");
    452   return 0;
    453 }
    454 
    455 
    456 /**
    457  * Time this round was started.
    458  */
    459 static unsigned long long start_time;
    460 
    461 
    462 /**
    463  * Get the current timestamp
    464  *
    465  * @return current time in ms
    466  */
    467 static unsigned long long
    468 now ()
    469 {
    470   struct timeval tv;
    471 
    472   gettimeofday (&tv, NULL);
    473   return (((unsigned long long) tv.tv_sec * 1000LL) +
    474 	  ((unsigned long long) tv.tv_usec / 1000LL));
    475 }
    476 
    477 
    478 int
    479 main (int argc, char *const *argv)
    480 {
    481   unsigned int errorCount = 0;
    482 
    483   oneone = (NULL != strrchr (argv[0], (int) '/')) ?
    484     (NULL != strstr (strrchr (argv[0], (int) '/'), "11")) : 0;
    485   if (0 != curl_global_init (CURL_GLOBAL_WIN32))
    486     return 2;
    487   start_time = now();
    488   errorCount += testInternalPost ();
    489   fprintf (stderr,
    490 	   oneone ? "%s: Sequential POSTs (http/1.1) %f/s\n" : "%s: Sequential POSTs (http/1.0) %f/s\n",
    491 	   "internal select",
    492 	   (double) 1000 * LOOPCOUNT / (now() - start_time + 1.0));
    493   GAUGER ("internal select",
    494 	  oneone ? "Sequential POSTs (http/1.1)" : "Sequential POSTs (http/1.0)",
    495 	  (double) 1000 * LOOPCOUNT / (now() - start_time + 1.0),
    496 	  "requests/s");
    497   start_time = now();
    498   errorCount += testMultithreadedPost ();
    499   fprintf (stderr,
    500 	   oneone ? "%s: Sequential POSTs (http/1.1) %f/s\n" : "%s: Sequential POSTs (http/1.0) %f/s\n",
    501 	   "multithreaded post",
    502 	   (double) 1000 * LOOPCOUNT / (now() - start_time + 1.0));
    503   GAUGER ("Multithreaded select",
    504 	  oneone ? "Sequential POSTs (http/1.1)" : "Sequential POSTs (http/1.0)",
    505 	  (double) 1000 * LOOPCOUNT / (now() - start_time + 1.0),
    506 	  "requests/s");
    507   start_time = now();
    508   errorCount += testMultithreadedPoolPost ();
    509   fprintf (stderr,
    510 	   oneone ? "%s: Sequential POSTs (http/1.1) %f/s\n" : "%s: Sequential POSTs (http/1.0) %f/s\n",
    511 	   "thread with pool",
    512 	   (double) 1000 * LOOPCOUNT / (now() - start_time + 1.0));
    513   GAUGER ("thread with pool",
    514 	  oneone ? "Sequential POSTs (http/1.1)" : "Sequential POSTs (http/1.0)",
    515 	  (double) 1000 * LOOPCOUNT / (now() - start_time + 1.0),
    516 	  "requests/s");
    517   start_time = now();
    518   errorCount += testExternalPost ();
    519   fprintf (stderr,
    520 	   oneone ? "%s: Sequential POSTs (http/1.1) %f/s\n" : "%s: Sequential POSTs (http/1.0) %f/s\n",
    521 	   "external select",
    522 	   (double) 1000 * LOOPCOUNT / (now() - start_time + 1.0));
    523   GAUGER ("external select",
    524 	  oneone ? "Sequential POSTs (http/1.1)" : "Sequential POSTs (http/1.0)",
    525 	  (double) 1000 * LOOPCOUNT / (now() - start_time + 1.0),
    526 	  "requests/s");
    527   if (errorCount != 0)
    528     fprintf (stderr, "Error (code: %u)\n", errorCount);
    529   curl_global_cleanup ();
    530   return errorCount != 0;       /* 0 == pass */
    531 }
    532