libmicrohttpd

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

test_timeout.c (11401B)


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