libmicrohttpd

HTTP/1.x server C library (MHD 1.x, stable)
Log | Files | Refs | Submodules | README | LICENSE

test_timeout.c (11568B)


      1 /*
      2      This file is part of libmicrohttpd
      3      Copyright (C) 2007 Christian Grothoff
      4      Copyright (C) 2016-2022 Evgeny Grin (Karlson2k)
      5 
      6      libmicrohttpd is free software; you can redistribute it and/or modify
      7      it under the terms of the GNU General Public License as published
      8      by the Free Software Foundation; either version 2, or (at your
      9      option) any later version.
     10 
     11      libmicrohttpd is distributed in the hope that it will be useful, but
     12      WITHOUT ANY WARRANTY; without even the implied warranty of
     13      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     14      General Public License for more details.
     15 
     16      You should have received a copy of the GNU General Public License
     17      along with libmicrohttpd; see the file COPYING.  If not, write to the
     18      Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
     19      Boston, MA 02110-1301, USA.
     20 */
     21 
     22 /**
     23  * @file test_timeout.c
     24  * @brief  Testcase for libmicrohttpd PUT operations
     25  * @author Matthias Wachs
     26  * @author Karlson2k (Evgeny Grin)
     27  */
     28 
     29 #include "MHD_config.h"
     30 #include "platform.h"
     31 #include <curl/curl.h>
     32 #include <microhttpd.h>
     33 #include <stdlib.h>
     34 #include <string.h>
     35 #ifdef HAVE_UNISTD_H
     36 #include <unistd.h>
     37 #endif /* HAVE_UNISTD_H */
     38 #ifdef HAVE_TIME_H
     39 #include <time.h>
     40 #endif /* HAVE_TIME_H */
     41 #include "mhd_has_in_name.h"
     42 
     43 /* Turn any MHD_PANIC() or failing mhd_assert() reached from this
     44    test into a marked, classifiable test error (TESTING.md, P5). */
     45 #include "mhd_panic_tripwire.h"
     46 
     47 
     48 /**
     49  * Pause execution for specified number of milliseconds.
     50  * @param ms the number of milliseconds to sleep
     51  */
     52 static void
     53 _MHD_sleep (uint32_t ms)
     54 {
     55 #if defined(_WIN32)
     56   Sleep (ms);
     57 #elif defined(HAVE_NANOSLEEP)
     58   struct timespec slp = {ms / 1000, (ms % 1000) * 1000000};
     59   struct timespec rmn;
     60   int num_retries = 0;
     61   while (0 != nanosleep (&slp, &rmn))
     62   {
     63     if (num_retries++ > 8)
     64       break;
     65     slp = rmn;
     66   }
     67 #elif defined(HAVE_USLEEP)
     68   uint64_t us = ms * 1000;
     69   do
     70   {
     71     uint64_t this_sleep;
     72     if (999999 < us)
     73       this_sleep = 999999;
     74     else
     75       this_sleep = us;
     76     /* Ignore return value as it could be void */
     77     usleep (this_sleep);
     78     us -= this_sleep;
     79   } while (us > 0);
     80 #else
     81   fprintf (stderr, "No sleep function available on this system.\n");
     82 #endif
     83 }
     84 
     85 
     86 static int oneone;
     87 
     88 static int withTimeout = 0;
     89 
     90 static int withoutTimeout = 0;
     91 
     92 struct CBC
     93 {
     94   char *buf;
     95   size_t pos;
     96   size_t size;
     97 };
     98 
     99 
    100 static void
    101 termination_cb (void *cls,
    102                 struct MHD_Connection *connection,
    103                 void **req_cls,
    104                 enum MHD_RequestTerminationCode toe)
    105 {
    106   int *test = cls;
    107   (void) connection; (void) req_cls;       /* Unused. Silent compiler warning. */
    108 
    109   switch (toe)
    110   {
    111   case MHD_REQUEST_TERMINATED_COMPLETED_OK:
    112     if (test == &withoutTimeout)
    113     {
    114       withoutTimeout = 1;
    115     }
    116     else
    117     {
    118       fprintf (stderr, "Connection completed without errors while "
    119                "timeout is expected.\n");
    120     }
    121     break;
    122   case MHD_REQUEST_TERMINATED_WITH_ERROR:
    123     fprintf (stderr, "Connection terminated with error.\n");
    124     exit (4);
    125     break;
    126   case MHD_REQUEST_TERMINATED_READ_ERROR:
    127     fprintf (stderr, "Connection terminated with read error.\n");
    128     exit (4);
    129     break;
    130   case MHD_REQUEST_TERMINATED_TIMEOUT_REACHED:
    131     if (test == &withTimeout)
    132     {
    133       withTimeout = 1;
    134     }
    135     else
    136     {
    137       fprintf (stderr, "Connection terminated with timeout while expected "
    138                "to be successfully completed.\n");
    139     }
    140     break;
    141   case MHD_REQUEST_TERMINATED_DAEMON_SHUTDOWN:
    142     fprintf (stderr, "Connection terminated by daemon shutdown.\n");
    143     exit (4);
    144     break;
    145   case MHD_REQUEST_TERMINATED_CLIENT_ABORT:
    146     fprintf (stderr, "Connection terminated by client.\n");
    147     exit (4);
    148     break;
    149   }
    150 }
    151 
    152 
    153 static size_t
    154 putBuffer (void *stream, size_t size, size_t nmemb, void *ptr)
    155 {
    156   size_t *pos = ptr;
    157   size_t wrt;
    158 
    159   wrt = size * nmemb;
    160   if (wrt > 8 - (*pos))
    161     wrt = 8 - (*pos);
    162   memcpy (stream, &("Hello123"[*pos]), wrt);
    163   (*pos) += wrt;
    164   return wrt;
    165 }
    166 
    167 
    168 static size_t
    169 putBuffer_fail (void *stream, size_t size, size_t nmemb, void *ptr)
    170 {
    171   (void) stream; (void) size; (void) nmemb; (void) ptr;        /* Unused. Silent compiler warning. */
    172   _MHD_sleep (100); /* Avoid busy-waiting */
    173   return 0;
    174 }
    175 
    176 
    177 static size_t
    178 copyBuffer (void *ptr, size_t size, size_t nmemb, void *ctx)
    179 {
    180   struct CBC *cbc = ctx;
    181 
    182   if (cbc->pos + size * nmemb > cbc->size)
    183     return 0;                   /* overflow */
    184   memcpy (&cbc->buf[cbc->pos], ptr, size * nmemb);
    185   cbc->pos += size * nmemb;
    186   return size * nmemb;
    187 }
    188 
    189 
    190 static enum MHD_Result
    191 ahc_echo (void *cls,
    192           struct MHD_Connection *connection,
    193           const char *url,
    194           const char *method,
    195           const char *version,
    196           const char *upload_data, size_t *upload_data_size,
    197           void **req_cls)
    198 {
    199   int *done = cls;
    200   struct MHD_Response *response;
    201   enum MHD_Result ret;
    202   (void) version; (void) req_cls;   /* Unused. Silent compiler warning. */
    203 
    204   if (0 != strcmp (MHD_HTTP_METHOD_PUT, method))
    205     return MHD_NO;              /* unexpected method */
    206   if ((*done) == 0)
    207   {
    208     if (*upload_data_size != 8)
    209       return MHD_YES;           /* not yet ready */
    210     if (0 == memcmp (upload_data, "Hello123", 8))
    211     {
    212       *upload_data_size = 0;
    213     }
    214     else
    215     {
    216       printf ("Invalid upload data `%8s'!\n", upload_data);
    217       return MHD_NO;
    218     }
    219     *done = 1;
    220     return MHD_YES;
    221   }
    222   response = MHD_create_response_from_buffer_copy (strlen (url),
    223                                                    (const void *) url);
    224   ret = MHD_queue_response (connection, MHD_HTTP_OK, response);
    225   MHD_destroy_response (response);
    226   return ret;
    227 }
    228 
    229 
    230 static unsigned int
    231 testWithoutTimeout (void)
    232 {
    233   struct MHD_Daemon *d;
    234   CURL *c;
    235   char buf[2048];
    236   struct CBC cbc;
    237   size_t pos = 0;
    238   int done_flag = 0;
    239   CURLcode errornum;
    240   uint16_t port;
    241 
    242   if (MHD_NO != MHD_is_feature_supported (MHD_FEATURE_AUTODETECT_BIND_PORT))
    243     port = 0;
    244   else
    245   {
    246     port = 1500;
    247     if (oneone)
    248       port += 5;
    249   }
    250 
    251   cbc.buf = buf;
    252   cbc.size = 2048;
    253   cbc.pos = 0;
    254   d = MHD_start_daemon (MHD_USE_INTERNAL_POLLING_THREAD | MHD_USE_ERROR_LOG,
    255                         port,
    256                         NULL, NULL, &ahc_echo, &done_flag,
    257                         MHD_OPTION_CONNECTION_TIMEOUT, 2,
    258                         MHD_OPTION_NOTIFY_COMPLETED, &termination_cb,
    259                         &withoutTimeout,
    260                         MHD_OPTION_END);
    261   if (d == NULL)
    262     return 1;
    263   if (0 == port)
    264   {
    265     const union MHD_DaemonInfo *dinfo;
    266     dinfo = MHD_get_daemon_info (d, MHD_DAEMON_INFO_BIND_PORT);
    267     if ((NULL == dinfo) || (0 == dinfo->port) )
    268     {
    269       MHD_stop_daemon (d); return 32;
    270     }
    271     port = dinfo->port;
    272   }
    273   c = curl_easy_init ();
    274   curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1/hello_world");
    275   curl_easy_setopt (c, CURLOPT_PORT, (long) port);
    276   curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
    277   curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
    278   curl_easy_setopt (c, CURLOPT_READFUNCTION, &putBuffer);
    279   curl_easy_setopt (c, CURLOPT_READDATA, &pos);
    280   curl_easy_setopt (c, CURLOPT_UPLOAD, 1L);
    281   curl_easy_setopt (c, CURLOPT_INFILESIZE_LARGE, (curl_off_t) 8L);
    282   curl_easy_setopt (c, CURLOPT_FAILONERROR, 1L);
    283   curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L);
    284   if (oneone)
    285     curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
    286   else
    287     curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
    288   curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L);
    289   /* NOTE: use of CONNECTTIMEOUT without also
    290    *   setting NOSIGNAL results in really weird
    291    *   crashes on my system! */
    292   curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1L);
    293   withoutTimeout = 0;
    294   if (CURLE_OK != (errornum = curl_easy_perform (c)))
    295   {
    296     fprintf (stderr, "curl_easy_perform failed: '%s'\n",
    297              curl_easy_strerror (errornum));
    298     curl_easy_cleanup (c);
    299     MHD_stop_daemon (d);
    300     return 2;
    301   }
    302   curl_easy_cleanup (c);
    303   MHD_stop_daemon (d);
    304   if (0 == withoutTimeout)
    305   {
    306     fprintf (stderr, "Request wasn't processed successfully.\n");
    307     return 2;
    308   }
    309   if (cbc.pos != strlen ("/hello_world"))
    310     return 4;
    311   if (0 != strncmp ("/hello_world", cbc.buf, strlen ("/hello_world")))
    312     return 8;
    313   return 0;
    314 }
    315 
    316 
    317 static unsigned int
    318 testWithTimeout (void)
    319 {
    320   struct MHD_Daemon *d;
    321   CURL *c;
    322   char buf[2048];
    323   struct CBC cbc;
    324   int done_flag = 0;
    325   CURLcode errornum;
    326   uint16_t port;
    327 
    328   if (MHD_NO != MHD_is_feature_supported (MHD_FEATURE_AUTODETECT_BIND_PORT))
    329     port = 0;
    330   else
    331   {
    332     port = 1501;
    333     if (oneone)
    334       port += 5;
    335   }
    336 
    337   cbc.buf = buf;
    338   cbc.size = 2048;
    339   cbc.pos = 0;
    340   d = MHD_start_daemon (MHD_USE_INTERNAL_POLLING_THREAD | MHD_USE_ERROR_LOG,
    341                         port,
    342                         NULL, NULL, &ahc_echo, &done_flag,
    343                         MHD_OPTION_CONNECTION_TIMEOUT, 2,
    344                         MHD_OPTION_NOTIFY_COMPLETED, &termination_cb,
    345                         &withTimeout,
    346                         MHD_OPTION_END);
    347   if (d == NULL)
    348     return 16;
    349   if (0 == port)
    350   {
    351     const union MHD_DaemonInfo *dinfo;
    352     dinfo = MHD_get_daemon_info (d, MHD_DAEMON_INFO_BIND_PORT);
    353     if ((NULL == dinfo) || (0 == dinfo->port) )
    354     {
    355       MHD_stop_daemon (d); return 32;
    356     }
    357     port = dinfo->port;
    358   }
    359   c = curl_easy_init ();
    360   curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1/hello_world");
    361   curl_easy_setopt (c, CURLOPT_PORT, (long) port);
    362   curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
    363   curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
    364   curl_easy_setopt (c, CURLOPT_READFUNCTION, &putBuffer_fail);
    365   curl_easy_setopt (c, CURLOPT_READDATA, &testWithTimeout);
    366   curl_easy_setopt (c, CURLOPT_UPLOAD, 1L);
    367   curl_easy_setopt (c, CURLOPT_INFILESIZE_LARGE, (curl_off_t) 8L);
    368   curl_easy_setopt (c, CURLOPT_FAILONERROR, 1L);
    369   curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L);
    370   if (oneone)
    371     curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
    372   else
    373     curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
    374   curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L);
    375   /* NOTE: use of CONNECTTIMEOUT without also
    376    *   setting NOSIGNAL results in really weird
    377    *   crashes on my system! */
    378   curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1L);
    379   withTimeout = 0;
    380   if (CURLE_OK != (errornum = curl_easy_perform (c)))
    381   {
    382     curl_easy_cleanup (c);
    383     MHD_stop_daemon (d);
    384     if ((errornum == CURLE_GOT_NOTHING)
    385         || (CURLE_READ_ERROR == errornum))
    386     {
    387       if (0 != withTimeout)
    388       {
    389         /* mhd had the timeout */
    390         return 0;
    391       }
    392       else
    393       {
    394         fprintf (stderr, "Timeout wasn't detected.\n");
    395         return 8;
    396       }
    397     }
    398     else
    399     {
    400       fprintf (stderr, "libcurl reported error: %s (%u)\n",
    401                curl_easy_strerror (errornum),
    402                errornum);
    403       return 32;
    404     }
    405   }
    406   curl_easy_cleanup (c);
    407   MHD_stop_daemon (d);
    408   return 64;
    409 }
    410 
    411 
    412 int
    413 main (int argc, char *const *argv)
    414 {
    415   unsigned int errorCount = 0;
    416   (void) argc;   /* Unused. Silent compiler warning. */
    417 
    418   if ((NULL == argv) || (0 == argv[0]))
    419     return 99;
    420   oneone = has_in_name (argv[0], "11");
    421   if (0 != curl_global_init (CURL_GLOBAL_WIN32))
    422     return 16;
    423   errorCount += testWithoutTimeout ();
    424   errorCount += testWithTimeout ();
    425   if (errorCount != 0)
    426     fprintf (stderr,
    427              "Error during test execution (code: %u)\n",
    428              errorCount);
    429   curl_global_cleanup ();
    430   return (0 == errorCount) ? 0 : 1;       /* 0 == pass */
    431 }