Home | History | Annotate | Download | only in testcurl
      1 /*
      2      This file is part of libmicrohttpd
      3      Copyright (C) 2007, 2009 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_sendfile.c
     23  * @brief  Testcase for libmicrohttpd response from FD
     24  * @author Christian Grothoff
     25  */
     26 
     27 #include "MHD_config.h"
     28 #include "platform.h"
     29 #include "platform_interface.h"
     30 #include <curl/curl.h>
     31 #include <microhttpd.h>
     32 #include <stdlib.h>
     33 #include <string.h>
     34 #include <time.h>
     35 #include <sys/types.h>
     36 #include <fcntl.h>
     37 
     38 #ifndef WINDOWS
     39 #include <sys/socket.h>
     40 #include <unistd.h>
     41 #endif
     42 
     43 #if defined(CPU_COUNT) && (CPU_COUNT+0) < 2
     44 #undef CPU_COUNT
     45 #endif
     46 #if !defined(CPU_COUNT)
     47 #define CPU_COUNT 2
     48 #endif
     49 
     50 #define TESTSTR "This is the content of the test file we are sending using sendfile (if available)"
     51 
     52 char *sourcefile;
     53 
     54 static int oneone;
     55 
     56 struct CBC
     57 {
     58   char *buf;
     59   size_t pos;
     60   size_t size;
     61 };
     62 
     63 static size_t
     64 copyBuffer (void *ptr, size_t size, size_t nmemb, void *ctx)
     65 {
     66   struct CBC *cbc = ctx;
     67 
     68   if (cbc->pos + size * nmemb > cbc->size)
     69     return 0;                   /* overflow */
     70   memcpy (&cbc->buf[cbc->pos], ptr, size * nmemb);
     71   cbc->pos += size * nmemb;
     72   return size * nmemb;
     73 }
     74 
     75 
     76 static int
     77 ahc_echo (void *cls,
     78           struct MHD_Connection *connection,
     79           const char *url,
     80           const char *method,
     81           const char *version,
     82           const char *upload_data, size_t *upload_data_size,
     83           void **unused)
     84 {
     85   static int ptr;
     86   const char *me = cls;
     87   struct MHD_Response *response;
     88   int ret;
     89   int fd;
     90 
     91   if (0 != strcmp (me, method))
     92     return MHD_NO;              /* unexpected method */
     93   if (&ptr != *unused)
     94     {
     95       *unused = &ptr;
     96       return MHD_YES;
     97     }
     98   *unused = NULL;
     99   fd = open (sourcefile, O_RDONLY);
    100   if (fd == -1)
    101     {
    102       fprintf (stderr, "Failed to open `%s': %s\n",
    103 	       sourcefile,
    104 	       MHD_strerror_ (errno));
    105       exit (1);
    106     }
    107   response = MHD_create_response_from_fd (strlen (TESTSTR), fd);
    108   ret = MHD_queue_response (connection, MHD_HTTP_OK, response);
    109   MHD_destroy_response (response);
    110   if (ret == MHD_NO)
    111     abort ();
    112   return ret;
    113 }
    114 
    115 
    116 static int
    117 testInternalGet ()
    118 {
    119   struct MHD_Daemon *d;
    120   CURL *c;
    121   char buf[2048];
    122   struct CBC cbc;
    123   CURLcode errornum;
    124 
    125   cbc.buf = buf;
    126   cbc.size = 2048;
    127   cbc.pos = 0;
    128   d = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY | MHD_USE_DEBUG,
    129                         11080, NULL, NULL, &ahc_echo, "GET", MHD_OPTION_END);
    130   if (d == NULL)
    131     return 1;
    132   c = curl_easy_init ();
    133   curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1:11080/");
    134   curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
    135   curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
    136   curl_easy_setopt (c, CURLOPT_FAILONERROR, 1);
    137   curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L);
    138   curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L);
    139   if (oneone)
    140     curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
    141   else
    142     curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
    143   /* NOTE: use of CONNECTTIMEOUT without also
    144      setting NOSIGNAL results in really weird
    145      crashes on my system!*/
    146   curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1);
    147   if (CURLE_OK != (errornum = curl_easy_perform (c)))
    148     {
    149       fprintf (stderr,
    150                "curl_easy_perform failed: `%s'\n",
    151                curl_easy_strerror (errornum));
    152       curl_easy_cleanup (c);
    153       MHD_stop_daemon (d);
    154       return 2;
    155     }
    156   curl_easy_cleanup (c);
    157   MHD_stop_daemon (d);
    158   if (cbc.pos != strlen (TESTSTR))
    159     return 4;
    160   if (0 != strncmp (TESTSTR, cbc.buf, strlen (TESTSTR)))
    161     return 8;
    162   return 0;
    163 }
    164 
    165 static int
    166 testMultithreadedGet ()
    167 {
    168   struct MHD_Daemon *d;
    169   CURL *c;
    170   char buf[2048];
    171   struct CBC cbc;
    172   CURLcode errornum;
    173 
    174   cbc.buf = buf;
    175   cbc.size = 2048;
    176   cbc.pos = 0;
    177   d = MHD_start_daemon (MHD_USE_THREAD_PER_CONNECTION | MHD_USE_DEBUG,
    178                         1081, NULL, NULL, &ahc_echo, "GET", MHD_OPTION_END);
    179   if (d == NULL)
    180     return 16;
    181   c = curl_easy_init ();
    182   curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1:1081/");
    183   curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
    184   curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
    185   curl_easy_setopt (c, CURLOPT_FAILONERROR, 1);
    186   curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L);
    187   if (oneone)
    188     curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
    189   else
    190     curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
    191   curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L);
    192   /* NOTE: use of CONNECTTIMEOUT without also
    193      setting NOSIGNAL results in really weird
    194      crashes on my system! */
    195   curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1);
    196   if (CURLE_OK != (errornum = curl_easy_perform (c)))
    197     {
    198       fprintf (stderr,
    199                "curl_easy_perform failed: `%s'\n",
    200                curl_easy_strerror (errornum));
    201       curl_easy_cleanup (c);
    202       MHD_stop_daemon (d);
    203       return 32;
    204     }
    205   curl_easy_cleanup (c);
    206   MHD_stop_daemon (d);
    207   if (cbc.pos != strlen (TESTSTR))
    208     return 64;
    209   if (0 != strncmp (TESTSTR, cbc.buf, strlen (TESTSTR)))
    210     return 128;
    211   return 0;
    212 }
    213 
    214 static int
    215 testMultithreadedPoolGet ()
    216 {
    217   struct MHD_Daemon *d;
    218   CURL *c;
    219   char buf[2048];
    220   struct CBC cbc;
    221   CURLcode errornum;
    222 
    223   cbc.buf = buf;
    224   cbc.size = 2048;
    225   cbc.pos = 0;
    226   d = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY | MHD_USE_DEBUG,
    227                         1081, NULL, NULL, &ahc_echo, "GET",
    228                         MHD_OPTION_THREAD_POOL_SIZE, CPU_COUNT, MHD_OPTION_END);
    229   if (d == NULL)
    230     return 16;
    231   c = curl_easy_init ();
    232   curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1:1081/");
    233   curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
    234   curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
    235   curl_easy_setopt (c, CURLOPT_FAILONERROR, 1);
    236   curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L);
    237   if (oneone)
    238     curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
    239   else
    240     curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
    241   curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L);
    242   /* NOTE: use of CONNECTTIMEOUT without also
    243      setting NOSIGNAL results in really weird
    244      crashes on my system!*/
    245   curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1);
    246   if (CURLE_OK != (errornum = curl_easy_perform (c)))
    247     {
    248       fprintf (stderr,
    249                "curl_easy_perform failed: `%s'\n",
    250                curl_easy_strerror (errornum));
    251       curl_easy_cleanup (c);
    252       MHD_stop_daemon (d);
    253       return 32;
    254     }
    255   curl_easy_cleanup (c);
    256   MHD_stop_daemon (d);
    257   if (cbc.pos != strlen (TESTSTR))
    258     return 64;
    259   if (0 != strncmp (TESTSTR, cbc.buf, strlen (TESTSTR)))
    260     return 128;
    261   return 0;
    262 }
    263 
    264 static int
    265 testExternalGet ()
    266 {
    267   struct MHD_Daemon *d;
    268   CURL *c;
    269   char buf[2048];
    270   struct CBC cbc;
    271   CURLM *multi;
    272   CURLMcode mret;
    273   fd_set rs;
    274   fd_set ws;
    275   fd_set es;
    276   MHD_socket max;
    277   int running;
    278   struct CURLMsg *msg;
    279   time_t start;
    280   struct timeval tv;
    281 
    282   multi = NULL;
    283   cbc.buf = buf;
    284   cbc.size = 2048;
    285   cbc.pos = 0;
    286   d = MHD_start_daemon (MHD_USE_DEBUG,
    287                         1082, NULL, NULL, &ahc_echo, "GET", MHD_OPTION_END);
    288   if (d == NULL)
    289     return 256;
    290   c = curl_easy_init ();
    291   curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1:1082/");
    292   curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
    293   curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
    294   curl_easy_setopt (c, CURLOPT_FAILONERROR, 1);
    295   if (oneone)
    296     curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
    297   else
    298     curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
    299   curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L);
    300   curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L);
    301   /* NOTE: use of CONNECTTIMEOUT without also
    302      setting NOSIGNAL results in really weird
    303      crashes on my system! */
    304   curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1);
    305 
    306 
    307   multi = curl_multi_init ();
    308   if (multi == NULL)
    309     {
    310       curl_easy_cleanup (c);
    311       MHD_stop_daemon (d);
    312       return 512;
    313     }
    314   mret = curl_multi_add_handle (multi, c);
    315   if (mret != CURLM_OK)
    316     {
    317       curl_multi_cleanup (multi);
    318       curl_easy_cleanup (c);
    319       MHD_stop_daemon (d);
    320       return 1024;
    321     }
    322   start = time (NULL);
    323   while ((time (NULL) - start < 5) && (multi != NULL))
    324     {
    325       max = 0;
    326       FD_ZERO (&rs);
    327       FD_ZERO (&ws);
    328       FD_ZERO (&es);
    329       curl_multi_perform (multi, &running);
    330       mret = curl_multi_fdset (multi, &rs, &ws, &es, &max);
    331       if (mret != CURLM_OK)
    332         {
    333           curl_multi_remove_handle (multi, c);
    334           curl_multi_cleanup (multi);
    335           curl_easy_cleanup (c);
    336           MHD_stop_daemon (d);
    337           return 2048;
    338         }
    339       if (MHD_YES != MHD_get_fdset (d, &rs, &ws, &es, &max))
    340         {
    341           curl_multi_remove_handle (multi, c);
    342           curl_multi_cleanup (multi);
    343           curl_easy_cleanup (c);
    344           MHD_stop_daemon (d);
    345           return 4096;
    346         }
    347       tv.tv_sec = 0;
    348       tv.tv_usec = 1000;
    349       select (max + 1, &rs, &ws, &es, &tv);
    350       curl_multi_perform (multi, &running);
    351       if (running == 0)
    352         {
    353           msg = curl_multi_info_read (multi, &running);
    354           if (msg == NULL)
    355             break;
    356           if (msg->msg == CURLMSG_DONE)
    357             {
    358               if (msg->data.result != CURLE_OK)
    359                 printf ("%s failed at %s:%d: `%s'\n",
    360                         "curl_multi_perform",
    361                         __FILE__,
    362                         __LINE__, curl_easy_strerror (msg->data.result));
    363               curl_multi_remove_handle (multi, c);
    364               curl_multi_cleanup (multi);
    365               curl_easy_cleanup (c);
    366               c = NULL;
    367               multi = NULL;
    368             }
    369         }
    370       MHD_run (d);
    371     }
    372   if (multi != NULL)
    373     {
    374       curl_multi_remove_handle (multi, c);
    375       curl_easy_cleanup (c);
    376       curl_multi_cleanup (multi);
    377     }
    378   MHD_stop_daemon (d);
    379   if (cbc.pos != strlen (TESTSTR))
    380     return 8192;
    381   if (0 != strncmp (TESTSTR, cbc.buf, strlen (TESTSTR)))
    382     return 16384;
    383   return 0;
    384 }
    385 
    386 static int
    387 testUnknownPortGet ()
    388 {
    389   struct MHD_Daemon *d;
    390   const union MHD_DaemonInfo *di;
    391   CURL *c;
    392   char buf[2048];
    393   struct CBC cbc;
    394   CURLcode errornum;
    395 
    396   struct sockaddr_in addr;
    397   socklen_t addr_len = sizeof(addr);
    398   memset(&addr, 0, sizeof(addr));
    399   addr.sin_family = AF_INET;
    400   addr.sin_port = 0;
    401   addr.sin_addr.s_addr = INADDR_ANY;
    402 
    403   cbc.buf = buf;
    404   cbc.size = 2048;
    405   cbc.pos = 0;
    406   d = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY | MHD_USE_DEBUG,
    407                         1, NULL, NULL, &ahc_echo, "GET",
    408                         MHD_OPTION_SOCK_ADDR, &addr,
    409                         MHD_OPTION_END);
    410   if (d == NULL)
    411     return 32768;
    412 
    413   di = MHD_get_daemon_info (d, MHD_DAEMON_INFO_LISTEN_FD);
    414   if (di == NULL)
    415     return 65536;
    416 
    417   if (0 != getsockname(di->listen_fd, (struct sockaddr *) &addr, &addr_len))
    418     return 131072;
    419 
    420   if (addr.sin_family != AF_INET)
    421     return 26214;
    422 
    423   snprintf(buf, sizeof(buf), "http://127.0.0.1:%hu/",
    424            ntohs(addr.sin_port));
    425 
    426   c = curl_easy_init ();
    427   curl_easy_setopt (c, CURLOPT_URL, buf);
    428   curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
    429   curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
    430   curl_easy_setopt (c, CURLOPT_FAILONERROR, 1);
    431   curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L);
    432   curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L);
    433   if (oneone)
    434     curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
    435   else
    436     curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
    437   /* NOTE: use of CONNECTTIMEOUT without also
    438      setting NOSIGNAL results in really weird
    439      crashes on my system! */
    440   curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1);
    441   if (CURLE_OK != (errornum = curl_easy_perform (c)))
    442     {
    443       fprintf (stderr,
    444                "curl_easy_perform failed: `%s'\n",
    445                curl_easy_strerror (errornum));
    446       curl_easy_cleanup (c);
    447       MHD_stop_daemon (d);
    448       return 524288;
    449     }
    450   curl_easy_cleanup (c);
    451   MHD_stop_daemon (d);
    452   if (cbc.pos != strlen (TESTSTR))
    453     return 1048576;
    454   if (0 != strncmp (TESTSTR, cbc.buf, strlen (TESTSTR)))
    455     return 2097152;
    456   return 0;
    457 }
    458 
    459 
    460 int
    461 main (int argc, char *const *argv)
    462 {
    463   unsigned int errorCount = 0;
    464   const char *tmp;
    465   FILE *f;
    466 
    467   if ( (NULL == (tmp = getenv ("TMPDIR"))) &&
    468        (NULL == (tmp = getenv ("TMP"))) &&
    469        (NULL == (tmp = getenv ("TEMP"))) )
    470     tmp = "/tmp";
    471   sourcefile = malloc (strlen (tmp) + 32);
    472   sprintf (sourcefile,
    473 	   "%s/%s",
    474 	   tmp,
    475 	   "test-mhd-sendfile");
    476   f = fopen (sourcefile, "w");
    477   if (NULL == f)
    478     {
    479       fprintf (stderr, "failed to write test file\n");
    480       free (sourcefile);
    481       return 1;
    482     }
    483   fwrite (TESTSTR, strlen (TESTSTR), 1, f);
    484   fclose (f);
    485   oneone = (NULL != strrchr (argv[0], (int) '/')) ?
    486     (NULL != strstr (strrchr (argv[0], (int) '/'), "11")) : 0;
    487   if (0 != curl_global_init (CURL_GLOBAL_WIN32))
    488     return 2;
    489   errorCount += testInternalGet ();
    490   errorCount += testMultithreadedGet ();
    491   errorCount += testMultithreadedPoolGet ();
    492   errorCount += testExternalGet ();
    493   errorCount += testUnknownPortGet ();
    494   if (errorCount != 0)
    495     fprintf (stderr, "Error (code: %u)\n", errorCount);
    496   curl_global_cleanup ();
    497   unlink (sourcefile);
    498   free (sourcefile);
    499   return errorCount != 0;       /* 0 == pass */
    500 }
    501