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_timeout.c
     23  * @brief  Testcase for libmicrohttpd PUT operations
     24  * @author Matthias Wachs
     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 static int oneone;
     40 
     41 static int withTimeout = 1;
     42 
     43 static int withoutTimeout = 1;
     44 
     45 struct CBC
     46 {
     47   char *buf;
     48   size_t pos;
     49   size_t size;
     50 };
     51 
     52 
     53 static void
     54 termination_cb (void *cls,
     55 		struct MHD_Connection *connection,
     56 		void **con_cls,
     57 		enum MHD_RequestTerminationCode toe)
     58 {
     59   int *test = cls;
     60 
     61   switch (toe)
     62     {
     63     case MHD_REQUEST_TERMINATED_COMPLETED_OK :
     64       if (test == &withoutTimeout)
     65 	{
     66 	  withoutTimeout = 0;
     67 	}
     68       break;
     69     case MHD_REQUEST_TERMINATED_WITH_ERROR :
     70     case MHD_REQUEST_TERMINATED_READ_ERROR :
     71       break;
     72     case MHD_REQUEST_TERMINATED_TIMEOUT_REACHED :
     73       if (test == &withTimeout)
     74 	{
     75 	  withTimeout = 0;
     76 	}
     77       break;
     78     case MHD_REQUEST_TERMINATED_DAEMON_SHUTDOWN:
     79       break;
     80     case MHD_REQUEST_TERMINATED_CLIENT_ABORT:
     81       break;
     82     }
     83 }
     84 
     85 
     86 static size_t
     87 putBuffer (void *stream, size_t size, size_t nmemb, void *ptr)
     88 {
     89   unsigned int *pos = ptr;
     90   unsigned int wrt;
     91 
     92   wrt = size * nmemb;
     93   if (wrt > 8 - (*pos))
     94 	wrt = 8 - (*pos);
     95   memcpy (stream, &("Hello123"[*pos]), wrt);
     96   (*pos) += wrt;
     97   return wrt;
     98 }
     99 
    100 
    101 static size_t
    102 putBuffer_fail (void *stream, size_t size, size_t nmemb, void *ptr)
    103 {
    104   return 0;
    105 }
    106 
    107 
    108 static size_t
    109 copyBuffer (void *ptr, size_t size, size_t nmemb, void *ctx)
    110 {
    111   struct CBC *cbc = ctx;
    112 
    113   if (cbc->pos + size * nmemb > cbc->size)
    114     return 0;                   /* overflow */
    115   memcpy (&cbc->buf[cbc->pos], ptr, size * nmemb);
    116   cbc->pos += size * nmemb;
    117   return size * nmemb;
    118 }
    119 
    120 
    121 static int
    122 ahc_echo (void *cls,
    123           struct MHD_Connection *connection,
    124           const char *url,
    125           const char *method,
    126           const char *version,
    127           const char *upload_data, size_t *upload_data_size,
    128           void **unused)
    129 {
    130   int *done = cls;
    131   struct MHD_Response *response;
    132   int ret;
    133 
    134   if (0 != strcmp ("PUT", method))
    135     return MHD_NO;              /* unexpected method */
    136   if ((*done) == 0)
    137     {
    138       if (*upload_data_size != 8)
    139         return MHD_YES;         /* not yet ready */
    140       if (0 == memcmp (upload_data, "Hello123", 8))
    141         {
    142           *upload_data_size = 0;
    143         }
    144       else
    145         {
    146           printf ("Invalid upload data `%8s'!\n", upload_data);
    147           return MHD_NO;
    148         }
    149       *done = 1;
    150       return MHD_YES;
    151     }
    152   response = MHD_create_response_from_buffer (strlen (url),
    153 					      (void *) url,
    154 					      MHD_RESPMEM_MUST_COPY);
    155   ret = MHD_queue_response (connection, MHD_HTTP_OK, response);
    156   MHD_destroy_response (response);
    157   return ret;
    158 }
    159 
    160 
    161 static int
    162 testWithoutTimeout ()
    163 {
    164   struct MHD_Daemon *d;
    165   CURL *c;
    166   char buf[2048];
    167   struct CBC cbc;
    168   unsigned int pos = 0;
    169   int done_flag = 0;
    170   CURLcode errornum;
    171 
    172   cbc.buf = buf;
    173   cbc.size = 2048;
    174   cbc.pos = 0;
    175   d = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY | MHD_USE_DEBUG,
    176                         1080,
    177                         NULL, NULL, &ahc_echo, &done_flag,
    178                         MHD_OPTION_CONNECTION_TIMEOUT, 2,
    179                         MHD_OPTION_NOTIFY_COMPLETED, &termination_cb, &withTimeout,
    180                         MHD_OPTION_END);
    181   if (d == NULL)
    182     return 1;
    183   c = curl_easy_init ();
    184   curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1:1080/hello_world");
    185   curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
    186   curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
    187   curl_easy_setopt (c, CURLOPT_READFUNCTION, &putBuffer);
    188   curl_easy_setopt (c, CURLOPT_READDATA, &pos);
    189   curl_easy_setopt (c, CURLOPT_UPLOAD, 1L);
    190   curl_easy_setopt (c, CURLOPT_INFILESIZE_LARGE, (curl_off_t) 8L);
    191   curl_easy_setopt (c, CURLOPT_FAILONERROR, 1);
    192   curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L);
    193   if (oneone)
    194     curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
    195   else
    196     curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
    197   curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L);
    198   // NOTE: use of CONNECTTIMEOUT without also
    199   //   setting NOSIGNAL results in really weird
    200   //   crashes on my system!
    201   curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1);
    202   if (CURLE_OK != (errornum = curl_easy_perform (c)))
    203     {
    204       curl_easy_cleanup (c);
    205       MHD_stop_daemon (d);
    206       return 2;
    207     }
    208   curl_easy_cleanup (c);
    209   MHD_stop_daemon (d);
    210   if (cbc.pos != strlen ("/hello_world"))
    211     return 4;
    212   if (0 != strncmp ("/hello_world", cbc.buf, strlen ("/hello_world")))
    213     return 8;
    214   return 0;
    215 }
    216 
    217 static int
    218 testWithTimeout ()
    219 {
    220   struct MHD_Daemon *d;
    221   CURL *c;
    222   char buf[2048];
    223   struct CBC cbc;
    224   int done_flag = 0;
    225   CURLcode errornum;
    226 
    227   cbc.buf = buf;
    228   cbc.size = 2048;
    229   cbc.pos = 0;
    230   d = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY | MHD_USE_DEBUG,
    231                         1080,
    232                         NULL, NULL, &ahc_echo, &done_flag,
    233                         MHD_OPTION_CONNECTION_TIMEOUT, 2,
    234                         MHD_OPTION_NOTIFY_COMPLETED, &termination_cb, &withoutTimeout,
    235                         MHD_OPTION_END);
    236   if (d == NULL)
    237     return 16;
    238   c = curl_easy_init ();
    239   curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1:1080/hello_world");
    240   curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
    241   curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
    242   curl_easy_setopt (c, CURLOPT_READFUNCTION, &putBuffer_fail);
    243   curl_easy_setopt (c, CURLOPT_READDATA, &testWithTimeout);
    244   curl_easy_setopt (c, CURLOPT_UPLOAD, 1L);
    245   curl_easy_setopt (c, CURLOPT_INFILESIZE_LARGE, (curl_off_t) 8L);
    246   curl_easy_setopt (c, CURLOPT_FAILONERROR, 1);
    247   curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L);
    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, 150L);
    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   if (CURLE_OK != (errornum = curl_easy_perform (c)))
    258     {
    259       curl_easy_cleanup (c);
    260       MHD_stop_daemon (d);
    261       if (errornum == CURLE_GOT_NOTHING)
    262     	  /* mhd had the timeout */
    263     	  return 0;
    264       else
    265     	  /* curl had the timeout first */
    266     	  return 32;
    267     }
    268   curl_easy_cleanup (c);
    269   MHD_stop_daemon (d);
    270   return 64;
    271 }
    272 
    273 
    274 int
    275 main (int argc, char *const *argv)
    276 {
    277   unsigned int errorCount = 0;
    278 
    279   oneone = (NULL != strrchr (argv[0], (int) '/')) ?
    280     (NULL != strstr (strrchr (argv[0], (int) '/'), "11")) : 0;
    281   if (0 != curl_global_init (CURL_GLOBAL_WIN32))
    282     return 16;
    283   errorCount += testWithoutTimeout ();
    284   errorCount += testWithTimeout ();
    285   if (errorCount != 0)
    286     fprintf (stderr,
    287 	     "Error during test execution (code: %u)\n",
    288 	     errorCount);
    289   curl_global_cleanup ();
    290   if ((withTimeout == 0) && (withoutTimeout == 0))
    291     return 0;
    292   else
    293     return errorCount;       /* 0 == pass */
    294 }
    295