libmicrohttpd

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

test_get_response_cleanup.c (11251B)


      1 /*
      2      This file is part of libmicrohttpd
      3      Copyright (C) 2007, 2009 Christian Grothoff
      4      Copyright (C) 2014-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 daemontest_get_response_cleanup.c
     24  * @brief  Testcase for libmicrohttpd response cleanup
     25  * @author Christian Grothoff
     26  * @author Karlson2k (Evgeny Grin)
     27  */
     28 
     29 #include "MHD_config.h"
     30 #include "platform.h"
     31 #include <microhttpd.h>
     32 #include <stdlib.h>
     33 #include <unistd.h>
     34 #include <string.h>
     35 #include <time.h>
     36 #include <sys/types.h>
     37 #include <sys/wait.h>
     38 #include <fcntl.h>
     39 #include <errno.h>
     40 #ifndef _WIN32
     41 #include <signal.h>
     42 #endif /* _WIN32 */
     43 
     44 #ifndef WINDOWS
     45 #include <sys/socket.h>
     46 #include <unistd.h>
     47 #endif
     48 
     49 #ifdef _WIN32
     50 #ifndef WIN32_LEAN_AND_MEAN
     51 #define WIN32_LEAN_AND_MEAN 1
     52 #endif /* !WIN32_LEAN_AND_MEAN */
     53 #include <windows.h>
     54 #endif
     55 
     56 #include "mhd_has_in_name.h"
     57 
     58 #if defined(MHD_CPU_COUNT) && (MHD_CPU_COUNT + 0) < 2
     59 #undef MHD_CPU_COUNT
     60 #endif
     61 #if ! defined(MHD_CPU_COUNT)
     62 #define MHD_CPU_COUNT 2
     63 #endif
     64 
     65 static int oneone;
     66 
     67 static int ok;
     68 
     69 
     70 static pid_t
     71 fork_curl (const char *url)
     72 {
     73   pid_t ret;
     74 
     75   ret = fork ();
     76   if (ret != 0)
     77     return ret;
     78   execlp ("curl", "curl", "-s", "-N", "-o", "/dev/null", "-GET", url, NULL);
     79   fprintf (stderr,
     80            "Failed to exec curl: %s\n",
     81            strerror (errno));
     82   _exit (-1);
     83 }
     84 
     85 
     86 static void
     87 kill_curl (pid_t pid)
     88 {
     89   int status;
     90 
     91   /* fprintf (stderr, "Killing curl\n"); */
     92   kill (pid, SIGTERM);
     93   waitpid (pid, &status, 0);
     94 }
     95 
     96 
     97 static ssize_t
     98 push_callback (void *cls, uint64_t pos, char *buf, size_t max)
     99 {
    100   (void) cls; (void) pos;  /* Unused. Silent compiler warning. */
    101 
    102   if (max == 0)
    103     return 0;
    104   buf[0] = 'd';
    105   return 1;
    106 }
    107 
    108 
    109 static void
    110 push_free_callback (void *cls)
    111 {
    112   int *ok_p = cls;
    113 
    114   /* fprintf (stderr, "Cleanup callback called!\n"); */
    115   *ok_p = 0;
    116 }
    117 
    118 
    119 static enum MHD_Result
    120 ahc_echo (void *cls,
    121           struct MHD_Connection *connection,
    122           const char *url,
    123           const char *method,
    124           const char *version,
    125           const char *upload_data, size_t *upload_data_size,
    126           void **req_cls)
    127 {
    128   static int ptr;
    129   struct MHD_Response *response;
    130   enum MHD_Result ret;
    131   (void) cls;
    132   (void) url; (void) version;                      /* Unused. Silent compiler warning. */
    133   (void) upload_data; (void) upload_data_size;     /* Unused. Silent compiler warning. */
    134 
    135   if (0 != strcmp (MHD_HTTP_METHOD_GET, method))
    136     return MHD_NO;              /* unexpected method */
    137   if (&ptr != *req_cls)
    138   {
    139     *req_cls = &ptr;
    140     return MHD_YES;
    141   }
    142   *req_cls = NULL;
    143   response = MHD_create_response_from_callback (MHD_SIZE_UNKNOWN,
    144                                                 32 * 1024,
    145                                                 &push_callback,
    146                                                 &ok,
    147                                                 &push_free_callback);
    148   ret = MHD_queue_response (connection, MHD_HTTP_OK, response);
    149   MHD_destroy_response (response);
    150   if (ret == MHD_NO)
    151     abort ();
    152   return ret;
    153 }
    154 
    155 
    156 static unsigned int
    157 testInternalGet (void)
    158 {
    159   struct MHD_Daemon *d;
    160   pid_t curl;
    161   uint16_t port;
    162   char url[127];
    163 
    164   if (MHD_NO != MHD_is_feature_supported (MHD_FEATURE_AUTODETECT_BIND_PORT))
    165     port = 0;
    166   else
    167   {
    168     port = 1180;
    169     if (oneone)
    170       port += 10;
    171   }
    172 
    173   ok = 1;
    174   d = MHD_start_daemon (MHD_USE_INTERNAL_POLLING_THREAD | MHD_USE_ERROR_LOG,
    175                         port, NULL, NULL, &ahc_echo, NULL, MHD_OPTION_END);
    176   if (d == NULL)
    177     return 1;
    178   if (0 == port)
    179   {
    180     const union MHD_DaemonInfo *dinfo;
    181     dinfo = MHD_get_daemon_info (d, MHD_DAEMON_INFO_BIND_PORT);
    182     if ((NULL == dinfo) || (0 == dinfo->port) )
    183     {
    184       MHD_stop_daemon (d); return 32;
    185     }
    186     port = dinfo->port;
    187   }
    188   snprintf (url,
    189             sizeof (url),
    190             "http://127.0.0.1:%u/",
    191             (unsigned int) port);
    192   curl = fork_curl (url);
    193   (void) sleep (1);
    194   kill_curl (curl);
    195   (void) sleep (1);
    196   /* fprintf (stderr, "Stopping daemon!\n"); */
    197   MHD_stop_daemon (d);
    198   if (ok != 0)
    199     return 2;
    200   return 0;
    201 }
    202 
    203 
    204 static unsigned int
    205 testMultithreadedGet (void)
    206 {
    207   struct MHD_Daemon *d;
    208   pid_t curl;
    209   uint16_t port;
    210   char url[127];
    211 
    212   if (MHD_NO != MHD_is_feature_supported (MHD_FEATURE_AUTODETECT_BIND_PORT))
    213     port = 0;
    214   else
    215   {
    216     port = 1181;
    217     if (oneone)
    218       port += 10;
    219   }
    220 
    221   ok = 1;
    222   d = MHD_start_daemon (MHD_USE_THREAD_PER_CONNECTION
    223                         | MHD_USE_INTERNAL_POLLING_THREAD | MHD_USE_ERROR_LOG,
    224                         port, NULL, NULL, &ahc_echo, NULL,
    225                         MHD_OPTION_CONNECTION_TIMEOUT, (unsigned int) 2,
    226                         MHD_OPTION_END);
    227   if (d == NULL)
    228     return 16;
    229   if (0 == port)
    230   {
    231     const union MHD_DaemonInfo *dinfo;
    232     dinfo = MHD_get_daemon_info (d, MHD_DAEMON_INFO_BIND_PORT);
    233     if ((NULL == dinfo) || (0 == dinfo->port) )
    234     {
    235       MHD_stop_daemon (d); return 32;
    236     }
    237     port = dinfo->port;
    238   }
    239   snprintf (url,
    240             sizeof (url),
    241             "http://127.0.0.1:%u/",
    242             (unsigned int) port);
    243   /* fprintf (stderr, "Forking cURL!\n"); */
    244   curl = fork_curl (url);
    245   (void) sleep (1);
    246   kill_curl (curl);
    247   (void) sleep (1);
    248   curl = fork_curl (url);
    249   (void) sleep (1);
    250   if (ok != 0)
    251   {
    252     kill_curl (curl);
    253     MHD_stop_daemon (d);
    254     return 64;
    255   }
    256   kill_curl (curl);
    257   (void) sleep (1);
    258   /* fprintf (stderr, "Stopping daemon!\n"); */
    259   MHD_stop_daemon (d);
    260   if (ok != 0)
    261     return 32;
    262 
    263   return 0;
    264 }
    265 
    266 
    267 static unsigned int
    268 testMultithreadedPoolGet (void)
    269 {
    270   struct MHD_Daemon *d;
    271   pid_t curl;
    272   uint16_t port;
    273   char url[127];
    274 
    275   if (MHD_NO != MHD_is_feature_supported (MHD_FEATURE_AUTODETECT_BIND_PORT))
    276     port = 0;
    277   else
    278   {
    279     port = 1182;
    280     if (oneone)
    281       port += 10;
    282   }
    283 
    284   ok = 1;
    285   d = MHD_start_daemon (MHD_USE_INTERNAL_POLLING_THREAD | MHD_USE_ERROR_LOG,
    286                         port, NULL, NULL, &ahc_echo, NULL,
    287                         MHD_OPTION_THREAD_POOL_SIZE, MHD_CPU_COUNT,
    288                         MHD_OPTION_END);
    289   if (d == NULL)
    290     return 64;
    291   if (0 == port)
    292   {
    293     const union MHD_DaemonInfo *dinfo;
    294     dinfo = MHD_get_daemon_info (d, MHD_DAEMON_INFO_BIND_PORT);
    295     if ((NULL == dinfo) || (0 == dinfo->port) )
    296     {
    297       MHD_stop_daemon (d); return 32;
    298     }
    299     port = dinfo->port;
    300   }
    301   snprintf (url,
    302             sizeof (url),
    303             "http://127.0.0.1:%u/",
    304             (unsigned int) port);
    305   curl = fork_curl (url);
    306   (void) sleep (1);
    307   kill_curl (curl);
    308   (void) sleep (1);
    309   /* fprintf (stderr, "Stopping daemon!\n"); */
    310   MHD_stop_daemon (d);
    311   if (ok != 0)
    312     return 128;
    313   return 0;
    314 }
    315 
    316 
    317 static unsigned int
    318 testExternalGet (void)
    319 {
    320   struct MHD_Daemon *d;
    321   fd_set rs;
    322   fd_set ws;
    323   fd_set es;
    324   MHD_socket max;
    325   time_t start;
    326   struct timeval tv;
    327   pid_t curl;
    328   uint16_t port;
    329   char url[127];
    330 
    331   if (MHD_NO != MHD_is_feature_supported (MHD_FEATURE_AUTODETECT_BIND_PORT))
    332     port = 0;
    333   else
    334   {
    335     port = 1183;
    336     if (oneone)
    337       port += 10;
    338   }
    339 
    340   ok = 1;
    341   d = MHD_start_daemon (MHD_USE_ERROR_LOG,
    342                         port, NULL, NULL, &ahc_echo, NULL,
    343                         MHD_OPTION_APP_FD_SETSIZE, (int) FD_SETSIZE,
    344                         MHD_OPTION_END);
    345   if (d == NULL)
    346     return 256;
    347   if (0 == port)
    348   {
    349     const union MHD_DaemonInfo *dinfo;
    350     dinfo = MHD_get_daemon_info (d, MHD_DAEMON_INFO_BIND_PORT);
    351     if ((NULL == dinfo) || (0 == dinfo->port) )
    352     {
    353       MHD_stop_daemon (d); return 32;
    354     }
    355     port = dinfo->port;
    356   }
    357   snprintf (url,
    358             sizeof (url),
    359             "http://127.0.0.1:%u/",
    360             (unsigned int) port);
    361   curl = fork_curl (url);
    362 
    363   start = time (NULL);
    364   while ((time (NULL) - start < 2))
    365   {
    366     max = 0;
    367     FD_ZERO (&rs);
    368     FD_ZERO (&ws);
    369     FD_ZERO (&es);
    370     if (MHD_YES != MHD_get_fdset (d, &rs, &ws, &es, &max))
    371     {
    372       MHD_stop_daemon (d);
    373       return 4096;
    374     }
    375     tv.tv_sec = 0;
    376     tv.tv_usec = 1000;
    377     if (-1 == select (max + 1, &rs, &ws, &es, &tv))
    378     {
    379 #ifdef MHD_POSIX_SOCKETS
    380       if (EINTR != errno)
    381       {
    382         fprintf (stderr, "Unexpected select() error: %d. Line: %d\n",
    383                  (int) errno, __LINE__);
    384         fflush (stderr);
    385         exit (99);
    386       }
    387 #else
    388       if ((WSAEINVAL != WSAGetLastError ()) ||
    389           (0 != rs.fd_count) || (0 != ws.fd_count) || (0 != es.fd_count) )
    390       {
    391         fprintf (stderr, "Unexpected select() error: %d. Line: %d\n",
    392                  (int) WSAGetLastError (), __LINE__);
    393         fflush (stderr);
    394         exit (99);
    395       }
    396       Sleep (1);
    397 #endif
    398     }
    399     MHD_run (d);
    400   }
    401   kill_curl (curl);
    402   start = time (NULL);
    403   while ((time (NULL) - start < 2))
    404   {
    405     max = 0;
    406     FD_ZERO (&rs);
    407     FD_ZERO (&ws);
    408     FD_ZERO (&es);
    409     if (MHD_YES != MHD_get_fdset (d, &rs, &ws, &es, &max))
    410     {
    411       MHD_stop_daemon (d);
    412       return 4096;
    413     }
    414     tv.tv_sec = 0;
    415     tv.tv_usec = 1000;
    416     if (-1 == select (max + 1, &rs, &ws, &es, &tv))
    417     {
    418 #ifdef MHD_POSIX_SOCKETS
    419       if (EINTR != errno)
    420       {
    421         fprintf (stderr, "Unexpected select() error: %d. Line: %d\n",
    422                  (int) errno, __LINE__);
    423         fflush (stderr);
    424         exit (99);
    425       }
    426 #else
    427       if ((WSAEINVAL != WSAGetLastError ()) ||
    428           (0 != rs.fd_count) || (0 != ws.fd_count) || (0 != es.fd_count) )
    429       {
    430         fprintf (stderr, "Unexpected select() error: %d. Line: %d\n",
    431                  (int) WSAGetLastError (), __LINE__);
    432         fflush (stderr);
    433         exit (99);
    434       }
    435       Sleep (1);
    436 #endif
    437     }
    438     MHD_run (d);
    439   }
    440   /* fprintf (stderr, "Stopping daemon!\n"); */
    441   MHD_stop_daemon (d);
    442   if (ok != 0)
    443     return 1024;
    444   return 0;
    445 }
    446 
    447 
    448 int
    449 main (int argc, char *const *argv)
    450 {
    451   unsigned int errorCount = 0;
    452   (void) argc;   /* Unused. Silent compiler warning. */
    453 
    454 #ifndef _WIN32
    455   /* Solaris has no way to disable SIGPIPE on socket disconnect. */
    456   if (MHD_NO == MHD_is_feature_supported (MHD_FEATURE_AUTOSUPPRESS_SIGPIPE))
    457   {
    458     struct sigaction act;
    459 
    460     act.sa_handler = SIG_IGN;
    461     sigaction (SIGPIPE, &act, NULL);
    462   }
    463 #endif /* _WIN32 */
    464 
    465   if ((NULL == argv) || (0 == argv[0]))
    466     return 99;
    467   oneone = has_in_name (argv[0], "11");
    468   if (MHD_YES == MHD_is_feature_supported (MHD_FEATURE_THREADS))
    469   {
    470     errorCount += testInternalGet ();
    471     errorCount += testMultithreadedGet ();
    472     errorCount += testMultithreadedPoolGet ();
    473   }
    474   errorCount += testExternalGet ();
    475   if (errorCount != 0)
    476     fprintf (stderr, "Error (code: %u)\n", errorCount);
    477   return (0 == errorCount) ? 0 : 1;       /* 0 == pass */
    478 }