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 daemontest_get_chunked.c
     23  * @brief  Testcase for libmicrohttpd GET operations with chunked content encoding
     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 struct CBC
     42 {
     43   char *buf;
     44   size_t pos;
     45   size_t size;
     46 };
     47 
     48 static size_t
     49 copyBuffer (void *ptr, size_t size, size_t nmemb, void *ctx)
     50 {
     51   struct CBC *cbc = ctx;
     52 
     53   if (cbc->pos + size * nmemb > cbc->size)
     54     return 0;                   /* overflow */
     55   memcpy (&cbc->buf[cbc->pos], ptr, size * nmemb);
     56   cbc->pos += size * nmemb;
     57   return size * nmemb;
     58 }
     59 
     60 /**
     61  * MHD content reader callback that returns
     62  * data in chunks.
     63  */
     64 static ssize_t
     65 crc (void *cls, uint64_t pos, char *buf, size_t max)
     66 {
     67   struct MHD_Response **responseptr = cls;
     68 
     69   if (pos == 128 * 10)
     70     {
     71       MHD_add_response_header (*responseptr, "Footer", "working");
     72       return MHD_CONTENT_READER_END_OF_STREAM;
     73     }
     74   if (max < 128)
     75     abort ();                   /* should not happen in this testcase... */
     76   memset (buf, 'A' + (pos / 128), 128);
     77   return 128;
     78 }
     79 
     80 /**
     81  * Dummy function that does nothing.
     82  */
     83 static void
     84 crcf (void *ptr)
     85 {
     86   free (ptr);
     87 }
     88 
     89 static int
     90 ahc_echo (void *cls,
     91           struct MHD_Connection *connection,
     92           const char *url,
     93           const char *method,
     94           const char *version,
     95           const char *upload_data, size_t *upload_data_size, void **ptr)
     96 {
     97   static int aptr;
     98   const char *me = cls;
     99   struct MHD_Response *response;
    100   struct MHD_Response **responseptr;
    101   int ret;
    102 
    103   if (0 != strcmp (me, method))
    104     return MHD_NO;              /* unexpected method */
    105   if (&aptr != *ptr)
    106     {
    107       /* do never respond on first call */
    108       *ptr = &aptr;
    109       return MHD_YES;
    110     }
    111   responseptr = malloc (sizeof (struct MHD_Response *));
    112   response = MHD_create_response_from_callback (MHD_SIZE_UNKNOWN,
    113                                                 1024,
    114                                                 &crc, responseptr, &crcf);
    115   *responseptr = response;
    116   ret = MHD_queue_response (connection, MHD_HTTP_OK, response);
    117   MHD_destroy_response (response);
    118   return ret;
    119 }
    120 
    121 static int
    122 testInternalGet ()
    123 {
    124   struct MHD_Daemon *d;
    125   CURL *c;
    126   char buf[2048];
    127   struct CBC cbc;
    128   int i;
    129 
    130   cbc.buf = buf;
    131   cbc.size = 2048;
    132   cbc.pos = 0;
    133   d = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY /* | MHD_USE_DEBUG */ ,
    134                         11080, NULL, NULL, &ahc_echo, "GET", MHD_OPTION_END);
    135   if (d == NULL)
    136     return 1;
    137   zzuf_socat_start ();
    138   for (i = 0; i < LOOP_COUNT; i++)
    139     {
    140       fprintf (stderr, ".");
    141       c = curl_easy_init ();
    142       curl_easy_setopt (c, CURLOPT_URL, "http://localhost:11081/hello_world");
    143       curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
    144       curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
    145       curl_easy_setopt (c, CURLOPT_FAILONERROR, 1);
    146       curl_easy_setopt (c, CURLOPT_TIMEOUT_MS, CURL_TIMEOUT);
    147       curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT_MS, CURL_TIMEOUT);
    148       curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
    149       // NOTE: use of CONNECTTIMEOUT without also
    150       //   setting NOSIGNAL results in really weird
    151       //   crashes on my system!
    152       curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1);
    153       curl_easy_perform (c);
    154       curl_easy_cleanup (c);
    155     }
    156   fprintf (stderr, "\n");
    157   zzuf_socat_stop ();
    158   MHD_stop_daemon (d);
    159   return 0;
    160 }
    161 
    162 static int
    163 testMultithreadedGet ()
    164 {
    165   struct MHD_Daemon *d;
    166   CURL *c;
    167   char buf[2048];
    168   struct CBC cbc;
    169   int i;
    170 
    171   cbc.buf = buf;
    172   cbc.size = 2048;
    173   cbc.pos = 0;
    174   d = MHD_start_daemon (MHD_USE_THREAD_PER_CONNECTION /* | MHD_USE_DEBUG */ ,
    175                         11080, NULL, NULL, &ahc_echo, "GET", MHD_OPTION_END);
    176   if (d == NULL)
    177     return 16;
    178   zzuf_socat_start ();
    179   for (i = 0; i < LOOP_COUNT; i++)
    180     {
    181       fprintf (stderr, ".");
    182       c = curl_easy_init ();
    183       curl_easy_setopt (c, CURLOPT_URL, "http://localhost:11081/hello_world");
    184       curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
    185       curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
    186       curl_easy_setopt (c, CURLOPT_FAILONERROR, 1);
    187       curl_easy_setopt (c, CURLOPT_TIMEOUT_MS, CURL_TIMEOUT);
    188       curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
    189       curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT_MS, CURL_TIMEOUT);
    190       // NOTE: use of CONNECTTIMEOUT without also
    191       //   setting NOSIGNAL results in really weird
    192       //   crashes on my system!
    193       curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1);
    194       curl_easy_perform (c);
    195       curl_easy_cleanup (c);
    196     }
    197   fprintf (stderr, "\n");
    198   zzuf_socat_stop ();
    199   MHD_stop_daemon (d);
    200   return 0;
    201 }
    202 
    203 
    204 static int
    205 testExternalGet ()
    206 {
    207   struct MHD_Daemon *d;
    208   CURL *c;
    209   char buf[2048];
    210   struct CBC cbc;
    211   CURLM *multi;
    212   CURLMcode mret;
    213   fd_set rs;
    214   fd_set ws;
    215   fd_set es;
    216   int max;
    217   int running;
    218   time_t start;
    219   struct timeval tv;
    220   int i;
    221 
    222   multi = NULL;
    223   cbc.buf = buf;
    224   cbc.size = 2048;
    225   cbc.pos = 0;
    226   d = MHD_start_daemon (MHD_NO_FLAG /* | MHD_USE_DEBUG */ ,
    227                         11080, NULL, NULL, &ahc_echo, "GET", MHD_OPTION_END);
    228   if (d == NULL)
    229     return 256;
    230   multi = curl_multi_init ();
    231   if (multi == NULL)
    232     {
    233       MHD_stop_daemon (d);
    234       return 512;
    235     }
    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       curl_easy_setopt (c, CURLOPT_FAILONERROR, 1);
    245       curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
    246       curl_easy_setopt (c, CURLOPT_TIMEOUT_MS, CURL_TIMEOUT);
    247       curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT_MS, CURL_TIMEOUT);
    248       // NOTE: use of CONNECTTIMEOUT without also
    249       //   setting NOSIGNAL results in really weird
    250       //   crashes on my system!
    251       curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1);
    252       mret = curl_multi_add_handle (multi, c);
    253       if (mret != CURLM_OK)
    254         {
    255           curl_multi_cleanup (multi);
    256           curl_easy_cleanup (c);
    257           zzuf_socat_stop ();
    258           MHD_stop_daemon (d);
    259           return 1024;
    260         }
    261       start = time (NULL);
    262       while ((time (NULL) - start < 5) && (c != NULL))
    263         {
    264           max = 0;
    265           FD_ZERO (&rs);
    266           FD_ZERO (&ws);
    267           FD_ZERO (&es);
    268           curl_multi_perform (multi, &running);
    269           mret = curl_multi_fdset (multi, &rs, &ws, &es, &max);
    270           if (mret != CURLM_OK)
    271             {
    272               curl_multi_remove_handle (multi, c);
    273               curl_multi_cleanup (multi);
    274               curl_easy_cleanup (c);
    275               zzuf_socat_stop ();
    276               MHD_stop_daemon (d);
    277               return 2048;
    278             }
    279           if (MHD_YES != MHD_get_fdset (d, &rs, &ws, &es, &max))
    280             {
    281               curl_multi_remove_handle (multi, c);
    282               curl_multi_cleanup (multi);
    283               curl_easy_cleanup (c);
    284               zzuf_socat_stop ();
    285               MHD_stop_daemon (d);
    286               return 4096;
    287             }
    288           tv.tv_sec = 0;
    289           tv.tv_usec = 1000;
    290           select (max + 1, &rs, &ws, &es, &tv);
    291           curl_multi_perform (multi, &running);
    292           if (running == 0)
    293             {
    294               curl_multi_info_read (multi, &running);
    295               curl_multi_remove_handle (multi, c);
    296               curl_easy_cleanup (c);
    297               c = NULL;
    298             }
    299           MHD_run (d);
    300         }
    301       if (c != NULL)
    302         {
    303           curl_multi_remove_handle (multi, c);
    304           curl_easy_cleanup (c);
    305         }
    306     }
    307   fprintf (stderr, "\n");
    308   curl_multi_cleanup (multi);
    309   zzuf_socat_stop ();
    310   MHD_stop_daemon (d);
    311   return 0;
    312 }
    313 
    314 
    315 
    316 int
    317 main (int argc, char *const *argv)
    318 {
    319   unsigned int errorCount = 0;
    320 
    321   if (0 != curl_global_init (CURL_GLOBAL_WIN32))
    322     return 2;
    323   errorCount += testInternalGet ();
    324   errorCount += testMultithreadedGet ();
    325   errorCount += testExternalGet ();
    326   if (errorCount != 0)
    327     fprintf (stderr, "Error (code: %u)\n", errorCount);
    328   curl_global_cleanup ();
    329   return errorCount != 0;       /* 0 == pass */
    330 }
    331