libmicrohttpd

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

test_set_panic.c (49753B)


      1 /*
      2      This file is part of libmicrohttpd
      3      Copyright (C) 2021-2022 Evgeny Grin (Karlson2k)
      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., 51 Franklin Street, Fifth Floor,
     18      Boston, MA 02110-1301, USA.
     19 */
     20 /**
     21  * @file test_set_panic.c
     22  * @brief  Testcase for MHD_set_panic_func()
     23  * @author Karlson2k (Evgeny Grin)
     24  * @author Christian Grothoff
     25  */
     26 #include "MHD_config.h"
     27 #include "platform.h"
     28 #include <microhttpd.h>
     29 #include <stdlib.h>
     30 #include <string.h>
     31 #include <time.h>
     32 #include <errno.h>
     33 
     34 #ifdef HAVE_STRINGS_H
     35 #include <strings.h>
     36 #endif /* HAVE_STRINGS_H */
     37 
     38 #ifdef _WIN32
     39 #ifndef WIN32_LEAN_AND_MEAN
     40 #define WIN32_LEAN_AND_MEAN 1
     41 #endif /* !WIN32_LEAN_AND_MEAN */
     42 #include <windows.h>
     43 #endif
     44 
     45 #ifndef WINDOWS
     46 #include <unistd.h>
     47 #include <sys/socket.h>
     48 #endif
     49 
     50 #ifdef HAVE_LIMITS_H
     51 #include <limits.h>
     52 #endif /* HAVE_LIMITS_H */
     53 
     54 #ifdef HAVE_SIGNAL_H
     55 #include <signal.h>
     56 #endif /* HAVE_SIGNAL_H */
     57 
     58 #ifdef HAVE_SYSCTL
     59 #ifdef HAVE_SYS_TYPES_H
     60 #include <sys/types.h>
     61 #endif /* HAVE_SYS_TYPES_H */
     62 #ifdef HAVE_SYS_SYSCTL_H
     63 #include <sys/sysctl.h>
     64 #endif /* HAVE_SYS_SYSCTL_H */
     65 #ifdef HAVE_SYS_SOCKET_H
     66 #include <sys/socket.h>
     67 #endif /* HAVE_SYS_SOCKET_H */
     68 #ifdef HAVE_NETINET_IN_H
     69 #include <netinet/in.h>
     70 #endif /* HAVE_NETINET_IN_H */
     71 #ifdef HAVE_NETINET_IP_H
     72 #include <netinet/ip.h>
     73 #endif /* HAVE_NETINET_IP_H */
     74 #ifdef HAVE_NETINET_IP_ICMP_H
     75 #include <netinet/ip_icmp.h>
     76 #endif /* HAVE_NETINET_IP_ICMP_H */
     77 #ifdef HAVE_NETINET_ICMP_VAR_H
     78 #include <netinet/icmp_var.h>
     79 #endif /* HAVE_NETINET_ICMP_VAR_H */
     80 #endif /* HAVE_SYSCTL */
     81 
     82 #include <stdio.h>
     83 
     84 #include "mhd_sockets.h" /* only macros used */
     85 #include "test_helpers.h"
     86 #include "mhd_assert.h"
     87 
     88 #if defined(MHD_CPU_COUNT) && (MHD_CPU_COUNT + 0) < 2
     89 #undef MHD_CPU_COUNT
     90 #endif
     91 #if ! defined(MHD_CPU_COUNT)
     92 #define MHD_CPU_COUNT 2
     93 #endif
     94 #if MHD_CPU_COUNT > 32
     95 #undef MHD_CPU_COUNT
     96 /* Limit to reasonable value */
     97 #define MHD_CPU_COUNT 32
     98 #endif /* MHD_CPU_COUNT > 32 */
     99 
    100 #ifndef MHD_STATICSTR_LEN_
    101 /**
    102  * Determine length of static string / macro strings at compile time.
    103  */
    104 #define MHD_STATICSTR_LEN_(macro) (sizeof(macro) / sizeof(char) - 1)
    105 #endif /* ! MHD_STATICSTR_LEN_ */
    106 
    107 #ifndef _MHD_INSTRMACRO
    108 /* Quoted macro parameter */
    109 #define _MHD_INSTRMACRO(a) #a
    110 #endif /* ! _MHD_INSTRMACRO */
    111 #ifndef _MHD_STRMACRO
    112 /* Quoted expanded macro parameter */
    113 #define _MHD_STRMACRO(a) _MHD_INSTRMACRO (a)
    114 #endif /* ! _MHD_STRMACRO */
    115 
    116 
    117 /* Could be increased to facilitate debugging */
    118 #define TIMEOUTS_VAL 5
    119 
    120 /* Time in ms to wait for final packets to be delivered */
    121 #define FINAL_PACKETS_MS 20
    122 
    123 #define EXPECTED_URI_BASE_PATH  "/a"
    124 
    125 #define REQ_HOST "localhost"
    126 
    127 #define REQ_METHOD "PUT"
    128 
    129 #define REQ_BODY "Some content data."
    130 
    131 #define REQ_LINE_END "\r\n"
    132 
    133 /* Mandatory request headers */
    134 #define REQ_HEADER_HOST_NAME "Host"
    135 #define REQ_HEADER_HOST_VALUE REQ_HOST
    136 #define REQ_HEADER_HOST \
    137   REQ_HEADER_HOST_NAME ": " REQ_HEADER_HOST_VALUE REQ_LINE_END
    138 #define REQ_HEADER_UA_NAME "User-Agent"
    139 #define REQ_HEADER_UA_VALUE "dummyclient/0.9"
    140 #define REQ_HEADER_UA REQ_HEADER_UA_NAME ": " REQ_HEADER_UA_VALUE REQ_LINE_END
    141 
    142 /* Optional request headers */
    143 #define REQ_HEADER_CT_NAME "Content-Type"
    144 #define REQ_HEADER_CT_VALUE "text/plain"
    145 #define REQ_HEADER_CT REQ_HEADER_CT_NAME ": " REQ_HEADER_CT_VALUE REQ_LINE_END
    146 
    147 
    148 #if defined(HAVE___FUNC__)
    149 #define externalErrorExit(ignore) \
    150   _externalErrorExit_func (NULL, __func__, __LINE__)
    151 #define externalErrorExitDesc(errDesc) \
    152   _externalErrorExit_func (errDesc, __func__, __LINE__)
    153 #define mhdErrorExit(ignore) \
    154   _mhdErrorExit_func (NULL, __func__, __LINE__)
    155 #define mhdErrorExitDesc(errDesc) \
    156   _mhdErrorExit_func (errDesc, __func__, __LINE__)
    157 #elif defined(HAVE___FUNCTION__)
    158 #define externalErrorExit(ignore) \
    159   _externalErrorExit_func (NULL, __FUNCTION__, __LINE__)
    160 #define externalErrorExitDesc(errDesc) \
    161   _externalErrorExit_func (errDesc, __FUNCTION__, __LINE__)
    162 #define mhdErrorExit(ignore) \
    163   _mhdErrorExit_func (NULL, __FUNCTION__, __LINE__)
    164 #define mhdErrorExitDesc(errDesc) \
    165   _mhdErrorExit_func (errDesc, __FUNCTION__, __LINE__)
    166 #else
    167 #define externalErrorExit(ignore) _externalErrorExit_func (NULL, NULL, __LINE__)
    168 #define externalErrorExitDesc(errDesc) \
    169   _externalErrorExit_func (errDesc, NULL, __LINE__)
    170 #define mhdErrorExit(ignore) _mhdErrorExit_func (NULL, NULL, __LINE__)
    171 #define mhdErrorExitDesc(errDesc) _mhdErrorExit_func (errDesc, NULL, __LINE__)
    172 #endif
    173 
    174 
    175 _MHD_NORETURN static void
    176 _externalErrorExit_func (const char *errDesc, const char *funcName, int lineNum)
    177 {
    178   if ((NULL != errDesc) && (0 != errDesc[0]))
    179     fprintf (stderr, "%s", errDesc);
    180   else
    181     fprintf (stderr, "System or external library call failed");
    182   if ((NULL != funcName) && (0 != funcName[0]))
    183     fprintf (stderr, " in %s", funcName);
    184   if (0 < lineNum)
    185     fprintf (stderr, " at line %d", lineNum);
    186 
    187   fprintf (stderr, ".\nLast errno value: %d (%s)\n", (int) errno,
    188            strerror (errno));
    189 #ifdef MHD_WINSOCK_SOCKETS
    190   fprintf (stderr, "WSAGetLastError() value: %d\n", (int) WSAGetLastError ());
    191 #endif /* MHD_WINSOCK_SOCKETS */
    192   fflush (stderr);
    193   exit (99);
    194 }
    195 
    196 
    197 _MHD_NORETURN static void
    198 _mhdErrorExit_func (const char *errDesc, const char *funcName, int lineNum)
    199 {
    200   if ((NULL != errDesc) && (0 != errDesc[0]))
    201     fprintf (stderr, "%s", errDesc);
    202   else
    203     fprintf (stderr, "MHD unexpected error");
    204   if ((NULL != funcName) && (0 != funcName[0]))
    205     fprintf (stderr, " in %s", funcName);
    206   if (0 < lineNum)
    207     fprintf (stderr, " at line %d", lineNum);
    208 
    209   fprintf (stderr, ".\nLast errno value: %d (%s)\n", (int) errno,
    210            strerror (errno));
    211 
    212   fflush (stderr);
    213   exit (8);
    214 }
    215 
    216 
    217 #ifndef MHD_POSIX_SOCKETS
    218 
    219 /**
    220  * Pause execution for specified number of milliseconds.
    221  * @param ms the number of milliseconds to sleep
    222  */
    223 static void
    224 _MHD_sleep (uint32_t ms)
    225 {
    226 #if defined(_WIN32)
    227   Sleep (ms);
    228 #elif defined(HAVE_NANOSLEEP)
    229   struct timespec slp = {ms / 1000, (ms % 1000) * 1000000};
    230   struct timespec rmn;
    231   int num_retries = 0;
    232   while (0 != nanosleep (&slp, &rmn))
    233   {
    234     if (EINTR != errno)
    235       externalErrorExit ();
    236     if (num_retries++ > 8)
    237       break;
    238     slp = rmn;
    239   }
    240 #elif defined(HAVE_USLEEP)
    241   uint64_t us = ms * 1000;
    242   do
    243   {
    244     uint64_t this_sleep;
    245     if (999999 < us)
    246       this_sleep = 999999;
    247     else
    248       this_sleep = us;
    249     /* Ignore return value as it could be void */
    250     usleep (this_sleep);
    251     us -= this_sleep;
    252   } while (us > 0);
    253 #else
    254   externalErrorExitDesc ("No sleep function available on this system");
    255 #endif
    256 }
    257 
    258 
    259 #endif /* ! MHD_POSIX_SOCKETS */
    260 
    261 /* Global parameters */
    262 static int verbose;                 /**< Be verbose */
    263 static uint16_t global_port;        /**< MHD daemons listen port number */
    264 
    265 static void
    266 test_global_init (void)
    267 {
    268   if (MHD_YES != MHD_is_feature_supported (MHD_FEATURE_AUTOSUPPRESS_SIGPIPE))
    269   {
    270 #if defined(HAVE_SIGNAL_H) && defined(SIGPIPE)
    271     if (SIG_ERR == signal (SIGPIPE, SIG_IGN))
    272       externalErrorExitDesc ("Error suppressing SIGPIPE signal");
    273 #else /* ! HAVE_SIGNAL_H || ! SIGPIPE */
    274     fprintf (stderr, "Cannot suppress SIGPIPE signal.\n");
    275     /* exit (77); */
    276 #endif
    277   }
    278 }
    279 
    280 
    281 static void
    282 test_global_cleanup (void)
    283 {
    284 }
    285 
    286 
    287 /**
    288  * Change socket to blocking.
    289  *
    290  * @param fd the socket to manipulate
    291  */
    292 static void
    293 make_blocking (MHD_socket fd)
    294 {
    295 #if defined(MHD_POSIX_SOCKETS)
    296   int flags;
    297 
    298   flags = fcntl (fd, F_GETFL);
    299   if (-1 == flags)
    300     externalErrorExitDesc ("Cannot make socket non-blocking");
    301   if ((flags & ~O_NONBLOCK) != flags)
    302   {
    303     if (-1 == fcntl (fd, F_SETFL, flags & ~O_NONBLOCK))
    304       externalErrorExitDesc ("Cannot make socket non-blocking");
    305   }
    306 #elif defined(MHD_WINSOCK_SOCKETS)
    307   unsigned long flags = 0;
    308 
    309   if (0 != ioctlsocket (fd, (int) FIONBIO, &flags))
    310     externalErrorExitDesc ("Cannot make socket non-blocking");
    311 #endif /* MHD_WINSOCK_SOCKETS */
    312 }
    313 
    314 
    315 /**
    316  * Change socket to non-blocking.
    317  *
    318  * @param fd the socket to manipulate
    319  */
    320 static void
    321 make_nonblocking (MHD_socket fd)
    322 {
    323 #if defined(MHD_POSIX_SOCKETS)
    324   int flags;
    325 
    326   flags = fcntl (fd, F_GETFL);
    327   if (-1 == flags)
    328     externalErrorExitDesc ("Cannot make socket non-blocking");
    329   if ((flags | O_NONBLOCK) != flags)
    330   {
    331     if (-1 == fcntl (fd, F_SETFL, flags | O_NONBLOCK))
    332       externalErrorExitDesc ("Cannot make socket non-blocking");
    333   }
    334 #elif defined(MHD_WINSOCK_SOCKETS)
    335   unsigned long flags = 1;
    336 
    337   if (0 != ioctlsocket (fd, (int) FIONBIO, &flags))
    338     externalErrorExitDesc ("Cannot make socket non-blocking");
    339 #endif /* MHD_WINSOCK_SOCKETS */
    340 }
    341 
    342 
    343 enum _MHD_clientStage
    344 {
    345   DUMB_CLIENT_INIT = 0,
    346   DUMB_CLIENT_CONNECTING,
    347   DUMB_CLIENT_CONNECTED,
    348   DUMB_CLIENT_REQ_SENDING,
    349   DUMB_CLIENT_REQ_SENT,
    350   DUMB_CLIENT_HEADER_RECVEIVING,
    351   DUMB_CLIENT_HEADER_RECVEIVED,
    352   DUMB_CLIENT_BODY_RECVEIVING,
    353   DUMB_CLIENT_BODY_RECVEIVED,
    354   DUMB_CLIENT_FINISHING,
    355   DUMB_CLIENT_FINISHED
    356 };
    357 
    358 struct _MHD_dumbClient
    359 {
    360   MHD_socket sckt; /**< the socket to communicate */
    361 
    362   int sckt_nonblock;  /**< non-zero if socket is non-blocking */
    363 
    364   uint16_t port; /**< the port to connect to */
    365 
    366   char *send_buf; /**< the buffer for the request, malloced */
    367 
    368   size_t req_size; /**< the size of the request, including header */
    369 
    370   size_t send_off; /**< the number of bytes already sent */
    371 
    372   enum _MHD_clientStage stage;
    373 
    374   /* the test-specific variables */
    375   size_t single_send_size; /**< the maximum number of bytes to be sent by
    376                                 single send() */
    377   size_t send_size_limit;  /**< the total number of send bytes limit */
    378 };
    379 
    380 struct _MHD_dumbClient *
    381 _MHD_dumbClient_create (uint16_t port, const char *method, const char *url,
    382                         const char *add_headers,
    383                         const uint8_t *req_body, size_t req_body_size,
    384                         int chunked);
    385 
    386 
    387 void
    388 _MHD_dumbClient_set_send_limits (struct _MHD_dumbClient *clnt,
    389                                  size_t step_size, size_t max_total_send);
    390 
    391 void
    392 _MHD_dumbClient_start_connect (struct _MHD_dumbClient *clnt);
    393 
    394 int
    395 _MHD_dumbClient_is_req_sent (struct _MHD_dumbClient *clnt);
    396 
    397 int
    398 _MHD_dumbClient_process (struct _MHD_dumbClient *clnt);
    399 
    400 void
    401 _MHD_dumbClient_get_fdsets (struct _MHD_dumbClient *clnt,
    402                             MHD_socket *maxsckt,
    403                             fd_set *rs, fd_set *ws, fd_set *es);
    404 
    405 
    406 /**
    407  * Process the client data with send()/recv() as needed based on
    408  * information in fd_sets.
    409  * @param clnt the client to process
    410  * @return non-zero if client finished processing the request,
    411  *         zero otherwise.
    412  */
    413 int
    414 _MHD_dumbClient_process_from_fdsets (struct _MHD_dumbClient *clnt,
    415                                      fd_set *rs, fd_set *ws, fd_set *es);
    416 
    417 /**
    418  * Perform full request.
    419  * @param clnt the client to run
    420  * @return zero if client finished processing the request,
    421  *         non-zero if timeout is reached.
    422  */
    423 int
    424 _MHD_dumbClient_perform (struct _MHD_dumbClient *clnt);
    425 
    426 
    427 /**
    428  * Close the client and free internally allocated resources.
    429  * @param clnt the client to close
    430  */
    431 void
    432 _MHD_dumbClient_close (struct _MHD_dumbClient *clnt);
    433 
    434 
    435 struct _MHD_dumbClient *
    436 _MHD_dumbClient_create (uint16_t port, const char *method, const char *url,
    437                         const char *add_headers,
    438                         const uint8_t *req_body, size_t req_body_size,
    439                         int chunked)
    440 {
    441   struct _MHD_dumbClient *clnt;
    442   size_t method_size;
    443   size_t url_size;
    444   size_t add_hdrs_size;
    445   size_t buf_alloc_size;
    446   char *send_buf;
    447 
    448   mhd_assert (0 != port);
    449   mhd_assert (NULL != req_body || 0 == req_body_size);
    450   mhd_assert (0 == req_body_size || NULL != req_body);
    451 
    452   clnt = (struct _MHD_dumbClient *) malloc (sizeof(struct _MHD_dumbClient));
    453   if (NULL == clnt)
    454     externalErrorExit ();
    455   memset (clnt, 0, sizeof(struct _MHD_dumbClient));
    456   clnt->sckt = socket (AF_INET, SOCK_STREAM, IPPROTO_TCP);
    457   if (MHD_INVALID_SOCKET == clnt->sckt)
    458     externalErrorExitDesc ("Cannot create the client socket");
    459 
    460 #ifdef MHD_socket_nosignal_
    461   if (! MHD_socket_nosignal_ (clnt->sckt))
    462     externalErrorExitDesc ("Cannot suppress SIGPIPE on the client socket");
    463 #endif /* MHD_socket_nosignal_ */
    464 
    465   clnt->sckt_nonblock = 0;
    466   if (clnt->sckt_nonblock)
    467     make_nonblocking (clnt->sckt);
    468   else
    469     make_blocking (clnt->sckt);
    470 
    471   if (1)
    472   { /* Always set TCP NODELAY */
    473     const MHD_SCKT_OPT_BOOL_ on_val = 1;
    474 
    475     if (0 != setsockopt (clnt->sckt, IPPROTO_TCP, TCP_NODELAY,
    476                          (const void *) &on_val, sizeof (on_val)))
    477       externalErrorExitDesc ("Cannot set TCP_NODELAY option");
    478   }
    479 
    480   clnt->port = port;
    481 
    482   if (NULL != method)
    483     method_size = strlen (method);
    484   else
    485   {
    486     method = MHD_HTTP_METHOD_GET;
    487     method_size = MHD_STATICSTR_LEN_ (MHD_HTTP_METHOD_GET);
    488   }
    489   mhd_assert (0 != method_size);
    490   if (NULL != url)
    491     url_size = strlen (url);
    492   else
    493   {
    494     url = "/";
    495     url_size = 1;
    496   }
    497   mhd_assert (0 != url_size);
    498   add_hdrs_size = (NULL == add_headers) ? 0 : strlen (add_headers);
    499   buf_alloc_size = 1024 + method_size + url_size
    500                    + add_hdrs_size + req_body_size;
    501   send_buf = (char *) malloc (buf_alloc_size);
    502   if (NULL == send_buf)
    503     externalErrorExit ();
    504 
    505   clnt->req_size = 0;
    506   /* Form the request line */
    507   memcpy (send_buf + clnt->req_size, method, method_size);
    508   clnt->req_size += method_size;
    509   send_buf[clnt->req_size++] = ' ';
    510   memcpy (send_buf + clnt->req_size, url, url_size);
    511   clnt->req_size += url_size;
    512   send_buf[clnt->req_size++] = ' ';
    513   memcpy (send_buf + clnt->req_size, MHD_HTTP_VERSION_1_1,
    514           MHD_STATICSTR_LEN_ (MHD_HTTP_VERSION_1_1));
    515   clnt->req_size += MHD_STATICSTR_LEN_ (MHD_HTTP_VERSION_1_1);
    516   send_buf[clnt->req_size++] = '\r';
    517   send_buf[clnt->req_size++] = '\n';
    518   /* Form the header */
    519   memcpy (send_buf + clnt->req_size, REQ_HEADER_HOST,
    520           MHD_STATICSTR_LEN_ (REQ_HEADER_HOST));
    521   clnt->req_size += MHD_STATICSTR_LEN_ (REQ_HEADER_HOST);
    522   memcpy (send_buf + clnt->req_size, REQ_HEADER_UA,
    523           MHD_STATICSTR_LEN_ (REQ_HEADER_UA));
    524   clnt->req_size += MHD_STATICSTR_LEN_ (REQ_HEADER_UA);
    525   if ((NULL != req_body) || chunked)
    526   {
    527     if (! chunked)
    528     {
    529       int prn_size;
    530 
    531       memcpy (send_buf + clnt->req_size,
    532               MHD_HTTP_HEADER_CONTENT_LENGTH ": ",
    533               MHD_STATICSTR_LEN_ (MHD_HTTP_HEADER_CONTENT_LENGTH ": "));
    534       clnt->req_size +=
    535         MHD_STATICSTR_LEN_ (MHD_HTTP_HEADER_CONTENT_LENGTH ": ");
    536       prn_size = snprintf (send_buf + clnt->req_size,
    537                            (buf_alloc_size - clnt->req_size),
    538                            "%u", (unsigned int) req_body_size);
    539       if (0 >= prn_size)
    540         externalErrorExit ();
    541       if ((unsigned int) prn_size >= buf_alloc_size - clnt->req_size)
    542         externalErrorExit ();
    543       clnt->req_size += (unsigned int) prn_size;
    544       send_buf[clnt->req_size++] = '\r';
    545       send_buf[clnt->req_size++] = '\n';
    546     }
    547     else
    548     {
    549       memcpy (send_buf + clnt->req_size,
    550               MHD_HTTP_HEADER_TRANSFER_ENCODING ": chunked\r\n",
    551               MHD_STATICSTR_LEN_ (MHD_HTTP_HEADER_TRANSFER_ENCODING \
    552                                   ": chunked\r\n"));
    553       clnt->req_size += MHD_STATICSTR_LEN_ (MHD_HTTP_HEADER_TRANSFER_ENCODING \
    554                                             ": chunked\r\n");
    555     }
    556   }
    557   if (0 != add_hdrs_size)
    558   {
    559     memcpy (send_buf + clnt->req_size, add_headers, add_hdrs_size);
    560     clnt->req_size += add_hdrs_size;
    561   }
    562   /* Terminate header */
    563   send_buf[clnt->req_size++] = '\r';
    564   send_buf[clnt->req_size++] = '\n';
    565 
    566   /* Add body (if any) */
    567   if (! chunked)
    568   {
    569     if (0 != req_body_size)
    570     {
    571       memcpy (send_buf + clnt->req_size, req_body, req_body_size);
    572       clnt->req_size += req_body_size;
    573     }
    574   }
    575   else
    576   {
    577     if (0 != req_body_size)
    578     {
    579       int prn_size;
    580       prn_size = snprintf (send_buf + clnt->req_size,
    581                            (buf_alloc_size - clnt->req_size),
    582                            "%x", (unsigned int) req_body_size);
    583       if (0 >= prn_size)
    584         externalErrorExit ();
    585       if ((unsigned int) prn_size >= buf_alloc_size - clnt->req_size)
    586         externalErrorExit ();
    587       clnt->req_size += (unsigned int) prn_size;
    588       send_buf[clnt->req_size++] = '\r';
    589       send_buf[clnt->req_size++] = '\n';
    590       memcpy (send_buf + clnt->req_size, req_body, req_body_size);
    591       clnt->req_size += req_body_size;
    592       send_buf[clnt->req_size++] = '\r';
    593       send_buf[clnt->req_size++] = '\n';
    594     }
    595     send_buf[clnt->req_size++] = '0';
    596     send_buf[clnt->req_size++] = '\r';
    597     send_buf[clnt->req_size++] = '\n';
    598     send_buf[clnt->req_size++] = '\r';
    599     send_buf[clnt->req_size++] = '\n';
    600   }
    601   mhd_assert (clnt->req_size < buf_alloc_size);
    602   clnt->send_buf = send_buf;
    603 
    604   return clnt;
    605 }
    606 
    607 
    608 void
    609 _MHD_dumbClient_set_send_limits (struct _MHD_dumbClient *clnt,
    610                                  size_t step_size, size_t max_total_send)
    611 {
    612   clnt->single_send_size = step_size;
    613   clnt->send_size_limit = max_total_send;
    614 }
    615 
    616 
    617 /* internal */
    618 static void
    619 _MHD_dumbClient_connect_init (struct _MHD_dumbClient *clnt)
    620 {
    621   struct sockaddr_in sa;
    622   mhd_assert (DUMB_CLIENT_INIT == clnt->stage);
    623 
    624   sa.sin_family = AF_INET;
    625   sa.sin_port = htons (clnt->port);
    626   sa.sin_addr.s_addr = htonl (INADDR_LOOPBACK);
    627 
    628   if (0 != connect (clnt->sckt, (struct sockaddr *) &sa, sizeof(sa)))
    629   {
    630     const int err = MHD_socket_get_error_ ();
    631     if ( (MHD_SCKT_ERR_IS_ (err, MHD_SCKT_EINPROGRESS_)) ||
    632          (MHD_SCKT_ERR_IS_EAGAIN_ (err)))
    633       clnt->stage = DUMB_CLIENT_CONNECTING;
    634     else
    635       externalErrorExitDesc ("Cannot 'connect()' the client socket");
    636   }
    637   else
    638     clnt->stage = DUMB_CLIENT_CONNECTED;
    639 }
    640 
    641 
    642 void
    643 _MHD_dumbClient_start_connect (struct _MHD_dumbClient *clnt)
    644 {
    645   mhd_assert (DUMB_CLIENT_INIT == clnt->stage);
    646   _MHD_dumbClient_connect_init (clnt);
    647 }
    648 
    649 
    650 /* internal */
    651 static void
    652 _MHD_dumbClient_connect_finish (struct _MHD_dumbClient *clnt)
    653 {
    654   int err = 0;
    655   socklen_t err_size = sizeof(err);
    656   mhd_assert (DUMB_CLIENT_CONNECTING == clnt->stage);
    657   if (0 != getsockopt (clnt->sckt, SOL_SOCKET, SO_ERROR,
    658                        (void *) &err, &err_size))
    659     externalErrorExitDesc ("'getsockopt()' call failed");
    660   if (0 != err)
    661     externalErrorExitDesc ("Socket connect() failed");
    662   clnt->stage = DUMB_CLIENT_CONNECTED;
    663 }
    664 
    665 
    666 /* internal */
    667 static void
    668 _MHD_dumbClient_send_req (struct _MHD_dumbClient *clnt)
    669 {
    670   size_t send_size;
    671   ssize_t res;
    672   mhd_assert (DUMB_CLIENT_CONNECTED <= clnt->stage);
    673   mhd_assert (DUMB_CLIENT_REQ_SENT > clnt->stage);
    674   mhd_assert (clnt->req_size > clnt->send_off);
    675 
    676   send_size = (((0 != clnt->send_size_limit) &&
    677                 (clnt->req_size > clnt->send_size_limit)) ?
    678                clnt->send_size_limit : clnt->req_size) - clnt->send_off;
    679   mhd_assert (0 != send_size);
    680   if ((0 != clnt->single_send_size) &&
    681       (clnt->single_send_size < send_size))
    682     send_size = clnt->single_send_size;
    683 
    684   res = MHD_send_ (clnt->sckt, clnt->send_buf + clnt->send_off, send_size);
    685 
    686   if (res < 0)
    687   {
    688     const int err = MHD_socket_get_error_ ();
    689     if (MHD_SCKT_ERR_IS_EAGAIN_ (err))
    690       return;
    691     if (MHD_SCKT_ERR_IS_EINTR_ (err))
    692       return;
    693     if (MHD_SCKT_ERR_IS_REMOTE_DISCNN_ (err))
    694       mhdErrorExitDesc ("The connection was aborted by MHD");
    695     if (MHD_SCKT_ERR_IS_ (err, MHD_SCKT_EPIPE_))
    696       mhdErrorExitDesc ("The connection was shut down on MHD side");
    697     externalErrorExitDesc ("Unexpected network error");
    698   }
    699   clnt->send_off += (size_t) res;
    700   mhd_assert (clnt->send_off <= clnt->req_size);
    701   mhd_assert (clnt->send_off <= clnt->send_size_limit || \
    702               0 == clnt->send_size_limit);
    703   if (clnt->req_size == clnt->send_off)
    704     clnt->stage = DUMB_CLIENT_REQ_SENT;
    705   if ((0 != clnt->send_size_limit) &&
    706       (clnt->send_size_limit == clnt->send_off))
    707     clnt->stage = DUMB_CLIENT_FINISHING;
    708 }
    709 
    710 
    711 /* internal */
    712 _MHD_NORETURN /* not implemented */
    713 static void
    714 _MHD_dumbClient_recv_reply (struct _MHD_dumbClient *clnt)
    715 {
    716   (void) clnt;
    717   externalErrorExitDesc ("Not implemented for this test");
    718 }
    719 
    720 
    721 int
    722 _MHD_dumbClient_is_req_sent (struct _MHD_dumbClient *clnt)
    723 {
    724   return DUMB_CLIENT_REQ_SENT <= clnt->stage;
    725 }
    726 
    727 
    728 /* internal */
    729 static void
    730 _MHD_dumbClient_socket_close (struct _MHD_dumbClient *clnt)
    731 {
    732   if (MHD_INVALID_SOCKET != clnt->sckt)
    733   {
    734     if (! MHD_socket_close_ (clnt->sckt))
    735       externalErrorExitDesc ("Unexpected error while closing " \
    736                              "the client socket");
    737     clnt->sckt = MHD_INVALID_SOCKET;
    738   }
    739 }
    740 
    741 
    742 /* internal */
    743 static void
    744 _MHD_dumbClient_finalize (struct _MHD_dumbClient *clnt)
    745 {
    746   if (MHD_INVALID_SOCKET != clnt->sckt)
    747   {
    748     if (0 != shutdown (clnt->sckt, SHUT_WR))
    749     {
    750       const int err = MHD_socket_get_error_ ();
    751       if (! MHD_SCKT_ERR_IS_ (err, MHD_SCKT_ENOTCONN_) &&
    752           ! MHD_SCKT_ERR_IS_REMOTE_DISCNN_ (err))
    753         mhdErrorExitDesc ("Unexpected error when shutting down " \
    754                           "the client socket");
    755     }
    756   }
    757   clnt->stage = DUMB_CLIENT_FINISHED;
    758 }
    759 
    760 
    761 /* internal */
    762 static int
    763 _MHD_dumbClient_needs_send (const struct _MHD_dumbClient *clnt)
    764 {
    765   return ((DUMB_CLIENT_CONNECTING <= clnt->stage) &&
    766           (DUMB_CLIENT_REQ_SENT > clnt->stage)) ||
    767          (DUMB_CLIENT_FINISHING == clnt->stage);
    768 }
    769 
    770 
    771 /* internal */
    772 static int
    773 _MHD_dumbClient_needs_recv (const struct _MHD_dumbClient *clnt)
    774 {
    775   return (DUMB_CLIENT_HEADER_RECVEIVING <= clnt->stage) &&
    776          (DUMB_CLIENT_BODY_RECVEIVED > clnt->stage);
    777 }
    778 
    779 
    780 /* internal */
    781 /**
    782  * Check whether the client needs unconditionally process the data.
    783  * @param clnt the client to check
    784  * @return non-zero if client needs unconditionally process the data,
    785  *         zero otherwise.
    786  */
    787 static int
    788 _MHD_dumbClient_needs_process (const struct _MHD_dumbClient *clnt)
    789 {
    790   switch (clnt->stage)
    791   {
    792   case DUMB_CLIENT_INIT:
    793   case DUMB_CLIENT_REQ_SENT:
    794   case DUMB_CLIENT_HEADER_RECVEIVED:
    795   case DUMB_CLIENT_BODY_RECVEIVED:
    796   case DUMB_CLIENT_FINISHED:
    797     return ! 0;
    798   case DUMB_CLIENT_CONNECTING:
    799   case DUMB_CLIENT_CONNECTED:
    800   case DUMB_CLIENT_REQ_SENDING:
    801   case DUMB_CLIENT_HEADER_RECVEIVING:
    802   case DUMB_CLIENT_BODY_RECVEIVING:
    803   case DUMB_CLIENT_FINISHING:
    804   default:
    805     return 0;
    806   }
    807   return 0; /* Should be unreachable */
    808 }
    809 
    810 
    811 /**
    812  * Process the client data with send()/recv() as needed.
    813  * @param clnt the client to process
    814  * @return non-zero if client finished processing the request,
    815  *         zero otherwise.
    816  */
    817 int
    818 _MHD_dumbClient_process (struct _MHD_dumbClient *clnt)
    819 {
    820   do
    821   {
    822     switch (clnt->stage)
    823     {
    824     case DUMB_CLIENT_INIT:
    825       _MHD_dumbClient_connect_init (clnt);
    826       break;
    827     case DUMB_CLIENT_CONNECTING:
    828       _MHD_dumbClient_connect_finish (clnt);
    829       break;
    830     case DUMB_CLIENT_CONNECTED:
    831     case DUMB_CLIENT_REQ_SENDING:
    832       _MHD_dumbClient_send_req (clnt);
    833       break;
    834     case DUMB_CLIENT_REQ_SENT:
    835       mhd_assert (0);
    836       clnt->stage = DUMB_CLIENT_HEADER_RECVEIVING;
    837       break;
    838     case DUMB_CLIENT_HEADER_RECVEIVING:
    839       _MHD_dumbClient_recv_reply (clnt);
    840       break;
    841     case DUMB_CLIENT_HEADER_RECVEIVED:
    842       clnt->stage = DUMB_CLIENT_BODY_RECVEIVING;
    843       break;
    844     case DUMB_CLIENT_BODY_RECVEIVING:
    845       _MHD_dumbClient_recv_reply (clnt);
    846       break;
    847     case DUMB_CLIENT_BODY_RECVEIVED:
    848       clnt->stage = DUMB_CLIENT_FINISHING;
    849       break;
    850     case DUMB_CLIENT_FINISHING:
    851       _MHD_dumbClient_finalize (clnt);
    852       break;
    853     case DUMB_CLIENT_FINISHED:
    854       return ! 0;
    855     default:
    856       mhd_assert (0);
    857       mhdErrorExit ();
    858     }
    859   } while (_MHD_dumbClient_needs_process (clnt));
    860   return DUMB_CLIENT_FINISHED == clnt->stage;
    861 }
    862 
    863 
    864 void
    865 _MHD_dumbClient_get_fdsets (struct _MHD_dumbClient *clnt,
    866                             MHD_socket *maxsckt,
    867                             fd_set *rs, fd_set *ws, fd_set *es)
    868 {
    869   mhd_assert (NULL != rs);
    870   mhd_assert (NULL != ws);
    871   mhd_assert (NULL != es);
    872   if (DUMB_CLIENT_FINISHED > clnt->stage)
    873   {
    874     if (MHD_INVALID_SOCKET != clnt->sckt)
    875     {
    876       if ( (MHD_INVALID_SOCKET == *maxsckt) ||
    877            (clnt->sckt > *maxsckt) )
    878         *maxsckt = clnt->sckt;
    879       if (_MHD_dumbClient_needs_recv (clnt))
    880         FD_SET (clnt->sckt, rs);
    881       if (_MHD_dumbClient_needs_send (clnt))
    882         FD_SET (clnt->sckt, ws);
    883       FD_SET (clnt->sckt, es);
    884     }
    885   }
    886 }
    887 
    888 
    889 /**
    890  * Process the client data with send()/recv() as needed based on
    891  * information in fd_sets.
    892  * @param clnt the client to process
    893  * @return non-zero if client finished processing the request,
    894  *         zero otherwise.
    895  */
    896 int
    897 _MHD_dumbClient_process_from_fdsets (struct _MHD_dumbClient *clnt,
    898                                      fd_set *rs, fd_set *ws, fd_set *es)
    899 {
    900   if (_MHD_dumbClient_needs_process (clnt))
    901     return _MHD_dumbClient_process (clnt);
    902   else if (MHD_INVALID_SOCKET != clnt->sckt)
    903   {
    904     if (_MHD_dumbClient_needs_recv (clnt) && FD_ISSET (clnt->sckt, rs))
    905       return _MHD_dumbClient_process (clnt);
    906     else if (_MHD_dumbClient_needs_send (clnt) && FD_ISSET (clnt->sckt, ws))
    907       return _MHD_dumbClient_process (clnt);
    908     else if (FD_ISSET (clnt->sckt, es))
    909       return _MHD_dumbClient_process (clnt);
    910   }
    911   return DUMB_CLIENT_FINISHED == clnt->stage;
    912 }
    913 
    914 
    915 /**
    916  * Perform full request.
    917  * @param clnt the client to run
    918  * @return zero if client finished processing the request,
    919  *         non-zero if timeout is reached.
    920  */
    921 int
    922 _MHD_dumbClient_perform (struct _MHD_dumbClient *clnt)
    923 {
    924   time_t start;
    925   time_t now;
    926   start = time (NULL);
    927   now = start;
    928   do
    929   {
    930     fd_set rs;
    931     fd_set ws;
    932     fd_set es;
    933     MHD_socket maxMhdSk;
    934     struct timeval tv;
    935 
    936     FD_ZERO (&rs);
    937     FD_ZERO (&ws);
    938     FD_ZERO (&es);
    939 
    940     if (! _MHD_dumbClient_needs_process (clnt))
    941     {
    942       maxMhdSk = MHD_INVALID_SOCKET;
    943       _MHD_dumbClient_get_fdsets (clnt, &maxMhdSk, &rs, &ws, &es);
    944       mhd_assert (now >= start);
    945 #ifndef _WIN32
    946       tv.tv_sec = (time_t) (TIMEOUTS_VAL * 2 - (now - start) + 1);
    947 #else
    948       tv.tv_sec = (long) (TIMEOUTS_VAL * 2 - (now - start) + 1);
    949 #endif
    950       tv.tv_usec = 250 * 1000;
    951       if (-1 == select ((int) (maxMhdSk + 1), &rs, &ws, &es, &tv))
    952       {
    953 #ifdef MHD_POSIX_SOCKETS
    954         if (EINTR != errno)
    955           externalErrorExitDesc ("Unexpected select() error");
    956 #else  /* ! MHD_POSIX_SOCKETS */
    957         mhd_assert ((0 != rs.fd_count) || (0 != ws.fd_count) || \
    958                     (0 != es.fd_count));
    959         externalErrorExitDesc ("Unexpected select() error");
    960         _MHD_sleep ((uint32_t) (tv.tv_sec * 1000 + tv.tv_usec / 1000));
    961 #endif /* ! MHD_POSIX_SOCKETS */
    962         continue;
    963       }
    964       if (_MHD_dumbClient_process_from_fdsets (clnt, &rs, &ws, &es))
    965         return 0;
    966     }
    967     /* Use double timeout value here as MHD must catch timeout situations
    968      * in this test. Timeout in client as a last resort. */
    969   } while ((now = time (NULL)) - start <= (TIMEOUTS_VAL * 2));
    970   return 1;
    971 }
    972 
    973 
    974 /**
    975  * Close the client and free internally allocated resources.
    976  * @param clnt the client to close
    977  */
    978 void
    979 _MHD_dumbClient_close (struct _MHD_dumbClient *clnt)
    980 {
    981   if (DUMB_CLIENT_FINISHED != clnt->stage)
    982     _MHD_dumbClient_finalize (clnt);
    983   _MHD_dumbClient_socket_close (clnt);
    984   if (NULL != clnt->send_buf)
    985   {
    986     free ((void *) clnt->send_buf);
    987     clnt->send_buf = NULL;
    988   }
    989   free (clnt);
    990 }
    991 
    992 
    993 _MHD_NORETURN static void
    994 socket_cb (void *cls,
    995            struct MHD_Connection *c,
    996            void **socket_context,
    997            enum MHD_ConnectionNotificationCode toe)
    998 {
    999   (void) cls;            /* Unused */
   1000   (void) socket_context; /* Unused */
   1001   (void) toe;            /* Unused */
   1002 
   1003   MHD_suspend_connection (c); /* Should trigger panic */
   1004   mhdErrorExitDesc ("Function \"MHD_suspend_connection()\" succeed, while " \
   1005                     "it must fail as daemon was started without MHD_ALLOW_SUSPEND_RESUME " \
   1006                     "flag");
   1007 }
   1008 
   1009 
   1010 struct ahc_cls_type
   1011 {
   1012   const char *volatile rp_data;
   1013   volatile size_t rp_data_size;
   1014   const char *volatile rq_method;
   1015   const char *volatile rq_url;
   1016   const char *volatile req_body;
   1017   volatile unsigned int cb_called; /* Non-zero indicates that callback was called at least one time */
   1018   size_t req_body_size; /**< The number of bytes in @a req_body */
   1019   size_t req_body_uploaded; /* Updated by callback */
   1020 };
   1021 
   1022 
   1023 static enum MHD_Result
   1024 ahcCheck (void *cls,
   1025           struct MHD_Connection *connection,
   1026           const char *url,
   1027           const char *method,
   1028           const char *version,
   1029           const char *upload_data, size_t *upload_data_size,
   1030           void **req_cls)
   1031 {
   1032   static int marker;
   1033   enum MHD_Result ret;
   1034   struct ahc_cls_type *const param = (struct ahc_cls_type *) cls;
   1035   (void) connection; /* Unused */
   1036 
   1037   if (NULL == param)
   1038     mhdErrorExitDesc ("cls parameter is NULL");
   1039   param->cb_called++;
   1040 
   1041   if (0 != strcmp (version, MHD_HTTP_VERSION_1_1))
   1042     mhdErrorExitDesc ("Unexpected HTTP version");
   1043 
   1044   if (0 != strcmp (url, param->rq_url))
   1045     mhdErrorExitDesc ("Unexpected URI");
   1046 
   1047   if (0 != strcmp (param->rq_method, method))
   1048     mhdErrorExitDesc ("Unexpected request method");
   1049 
   1050   if (NULL == upload_data_size)
   1051     mhdErrorExitDesc ("'upload_data_size' pointer is NULL");
   1052 
   1053   if (0 != *upload_data_size)
   1054   {
   1055     const char *const upload_body = param->req_body;
   1056     if (NULL == upload_data)
   1057       mhdErrorExitDesc ("'upload_data' is NULL while " \
   1058                         "'*upload_data_size' value is not zero");
   1059     if (NULL == upload_body)
   1060       mhdErrorExitDesc ("'*upload_data_size' value is not zero " \
   1061                         "while no request body is expected");
   1062     if (param->req_body_uploaded + *upload_data_size > param->req_body_size)
   1063     {
   1064       fprintf (stderr, "Too large upload body received. Got %u, expected %u",
   1065                (unsigned int) (param->req_body_uploaded + *upload_data_size),
   1066                (unsigned int) param->req_body_size);
   1067       mhdErrorExit ();
   1068     }
   1069     if (0 != memcmp (upload_data, upload_body + param->req_body_uploaded,
   1070                      *upload_data_size))
   1071     {
   1072       fprintf (stderr, "Unexpected request body at offset %u: " \
   1073                "'%.*s', expected: '%.*s'\n",
   1074                (unsigned int) param->req_body_uploaded,
   1075                (int) *upload_data_size, upload_data,
   1076                (int) *upload_data_size, upload_body + param->req_body_uploaded);
   1077       mhdErrorExit ();
   1078     }
   1079     param->req_body_uploaded += *upload_data_size;
   1080     *upload_data_size = 0;
   1081   }
   1082 
   1083   if (&marker != *req_cls)
   1084   {
   1085     /* The first call of the callback for this connection */
   1086     mhd_assert (NULL == upload_data);
   1087     param->req_body_uploaded = 0;
   1088 
   1089     *req_cls = &marker;
   1090     return MHD_YES;
   1091   }
   1092 
   1093   if (NULL != upload_data)
   1094     return MHD_YES; /* Full request has not been received so far */
   1095 
   1096 #if 0 /* Code unused in this test */
   1097   struct MHD_Response *response;
   1098   response = MHD_create_response_from_buffer (param->rp_data_size,
   1099                                               (void *) param->rp_data,
   1100                                               MHD_RESPMEM_MUST_COPY);
   1101   if (NULL == response)
   1102     mhdErrorExitDesc ("Failed to create response");
   1103 
   1104   ret = MHD_queue_response (connection,
   1105                             MHD_HTTP_OK,
   1106                             response);
   1107   MHD_destroy_response (response);
   1108   if (MHD_YES != ret)
   1109     mhdErrorExitDesc ("Failed to queue response");
   1110 #else
   1111   if (NULL == upload_data)
   1112     mhdErrorExitDesc ("Full request received, " \
   1113                       "while incomplete request expected");
   1114   ret = MHD_NO;
   1115 #endif
   1116 
   1117   return ret;
   1118 }
   1119 
   1120 
   1121 struct simpleQueryParams
   1122 {
   1123   /* Destination path for HTTP query */
   1124   const char *queryPath;
   1125 
   1126   /* Custom query method, NULL for default */
   1127   const char *method;
   1128 
   1129   /* Destination port for HTTP query */
   1130   uint16_t queryPort;
   1131 
   1132   /* Additional request headers, static */
   1133   const char *headers;
   1134 
   1135   /* NULL for request without body */
   1136   const uint8_t *req_body;
   1137   size_t req_body_size;
   1138 
   1139   /* Non-zero to use chunked encoding for request body */
   1140   int chunked;
   1141 
   1142   /* HTTP query result error flag */
   1143   volatile int queryError;
   1144 
   1145   /* Response HTTP code, zero if no response */
   1146   volatile int responseCode;
   1147 };
   1148 
   1149 
   1150 /* returns non-zero if timed-out */
   1151 static int
   1152 performQueryExternal (struct MHD_Daemon *d, struct _MHD_dumbClient *clnt)
   1153 {
   1154   time_t start;
   1155   struct timeval tv;
   1156   int ret;
   1157   const union MHD_DaemonInfo *di;
   1158   MHD_socket lstn_sk;
   1159   int client_accepted;
   1160   int full_req_recieved;
   1161   int full_req_sent;
   1162   int some_data_recieved;
   1163 
   1164   di = MHD_get_daemon_info (d, MHD_DAEMON_INFO_LISTEN_FD);
   1165   if (NULL == di)
   1166     mhdErrorExitDesc ("Cannot get listener socket");
   1167   lstn_sk = di->listen_fd;
   1168 
   1169   ret = 1; /* will be replaced with real result */
   1170   client_accepted = 0;
   1171 
   1172   _MHD_dumbClient_start_connect (clnt);
   1173 
   1174   full_req_recieved = 0;
   1175   some_data_recieved = 0;
   1176   start = time (NULL);
   1177   do
   1178   {
   1179     fd_set rs;
   1180     fd_set ws;
   1181     fd_set es;
   1182     MHD_socket maxMhdSk;
   1183     int num_ready;
   1184     int do_client; /**< Process data in client */
   1185 
   1186     maxMhdSk = MHD_INVALID_SOCKET;
   1187     FD_ZERO (&rs);
   1188     FD_ZERO (&ws);
   1189     FD_ZERO (&es);
   1190     if (NULL == clnt)
   1191     {
   1192       /* client has finished, check whether MHD is still
   1193        * processing any connections */
   1194       full_req_sent = 1;
   1195       do_client = 0;
   1196       if (client_accepted && (0 > MHD_get_timeout64s (d)))
   1197       {
   1198         ret = 0;
   1199         break; /* MHD finished as well */
   1200       }
   1201     }
   1202     else
   1203     {
   1204       full_req_sent = _MHD_dumbClient_is_req_sent (clnt);
   1205       if (! full_req_sent)
   1206         do_client = 1; /* Request hasn't been sent yet, send the data */
   1207       else
   1208       {
   1209         /* All request data has been sent.
   1210          * Client will close the socket as the next step. */
   1211         if (full_req_recieved)
   1212           do_client = 1; /* All data has been received by the MHD */
   1213         else if (some_data_recieved)
   1214         {
   1215           /* at least something was received by the MHD */
   1216           do_client = 1;
   1217         }
   1218         else
   1219         {
   1220           /* The MHD must receive at least something before closing
   1221            * the connection. */
   1222           do_client = 0;
   1223         }
   1224       }
   1225 
   1226       if (do_client)
   1227         _MHD_dumbClient_get_fdsets (clnt, &maxMhdSk, &rs, &ws, &es);
   1228     }
   1229     if (MHD_YES != MHD_get_fdset (d, &rs, &ws, &es, &maxMhdSk))
   1230       mhdErrorExitDesc ("MHD_get_fdset() failed");
   1231     if (do_client)
   1232     {
   1233       tv.tv_sec = 1;
   1234       tv.tv_usec = 250 * 1000;
   1235     }
   1236     else
   1237     { /* Request completely sent but not yet fully received */
   1238       tv.tv_sec = 0;
   1239       tv.tv_usec = FINAL_PACKETS_MS * 1000;
   1240     }
   1241     num_ready = select ((int) (maxMhdSk + 1), &rs, &ws, &es, &tv);
   1242     if (-1 == num_ready)
   1243     {
   1244 #ifdef MHD_POSIX_SOCKETS
   1245       if (EINTR != errno)
   1246         externalErrorExitDesc ("Unexpected select() error");
   1247 #else
   1248       if ((WSAEINVAL != WSAGetLastError ()) ||
   1249           (0 != rs.fd_count) || (0 != ws.fd_count) || (0 != es.fd_count) )
   1250         externalErrorExitDesc ("Unexpected select() error");
   1251       _MHD_sleep ((uint32_t) (tv.tv_sec * 1000 + tv.tv_usec / 1000));
   1252 #endif
   1253       continue;
   1254     }
   1255     if (0 == num_ready)
   1256     { /* select() finished by timeout, looks like no more packets are pending */
   1257       if (do_client)
   1258         externalErrorExitDesc ("Timeout waiting for sockets");
   1259       if (full_req_sent && (! full_req_recieved))
   1260         full_req_recieved = 1;
   1261     }
   1262     if (full_req_recieved)
   1263       mhdErrorExitDesc ("Full request has been received by MHD, while it "
   1264                         "must be aborted by the panic function");
   1265     if (MHD_YES != MHD_run_from_select (d, &rs, &ws, &es))
   1266       mhdErrorExitDesc ("MHD_run_from_select() failed");
   1267     if (! client_accepted)
   1268       client_accepted = FD_ISSET (lstn_sk, &rs);
   1269     else
   1270     { /* Client connection was already accepted by MHD */
   1271       if (! some_data_recieved)
   1272       {
   1273         if (! do_client)
   1274         {
   1275           if (0 != num_ready)
   1276           { /* Connection was accepted before, "ready" socket means data */
   1277             some_data_recieved = 1;
   1278           }
   1279         }
   1280         else
   1281         {
   1282           if (2 == num_ready)
   1283             some_data_recieved = 1;
   1284           else if ((1 == num_ready) &&
   1285                    ((MHD_INVALID_SOCKET == clnt->sckt) ||
   1286                     ! FD_ISSET (clnt->sckt, &ws)))
   1287             some_data_recieved = 1;
   1288         }
   1289       }
   1290     }
   1291     if (do_client)
   1292     {
   1293       if (_MHD_dumbClient_process_from_fdsets (clnt, &rs, &ws, &es))
   1294         clnt = NULL;
   1295     }
   1296     /* Use double timeout value here so MHD would be able to catch timeout
   1297      * internally */
   1298   } while (time (NULL) - start <= (TIMEOUTS_VAL * 2));
   1299 
   1300   return ret;
   1301 }
   1302 
   1303 
   1304 /* Returns zero for successful response and non-zero for failed response */
   1305 static int
   1306 doClientQueryInThread (struct MHD_Daemon *d,
   1307                        struct simpleQueryParams *p)
   1308 {
   1309   const union MHD_DaemonInfo *dinfo;
   1310   struct _MHD_dumbClient *c;
   1311   int errornum;
   1312   int use_external_poll;
   1313 
   1314   dinfo = MHD_get_daemon_info (d, MHD_DAEMON_INFO_FLAGS);
   1315   if (NULL == dinfo)
   1316     mhdErrorExitDesc ("MHD_get_daemon_info() failed");
   1317   use_external_poll = (0 == (dinfo->flags
   1318                              & MHD_USE_INTERNAL_POLLING_THREAD));
   1319 
   1320   if (0 == p->queryPort)
   1321     externalErrorExit ();
   1322 
   1323   c = _MHD_dumbClient_create (p->queryPort, p->method, p->queryPath,
   1324                               p->headers, p->req_body, p->req_body_size,
   1325                               p->chunked);
   1326   _MHD_dumbClient_set_send_limits (c, 1, 0);
   1327 
   1328   /* 'internal' polling should not be used in this test */
   1329   mhd_assert (use_external_poll);
   1330   if (! use_external_poll)
   1331     errornum = _MHD_dumbClient_perform (c);
   1332   else
   1333     errornum = performQueryExternal (d, c);
   1334 
   1335   if (errornum)
   1336     fprintf (stderr, "Request timeout out.\n");
   1337   else
   1338     mhdErrorExitDesc ("Request succeed, but it must fail");
   1339 
   1340   _MHD_dumbClient_close (c);
   1341 
   1342   return errornum;
   1343 }
   1344 
   1345 
   1346 /* Perform test queries, shut down MHD daemon, and free parameters */
   1347 static unsigned int
   1348 performTestQueries (struct MHD_Daemon *d, uint16_t d_port,
   1349                     struct ahc_cls_type *ahc_param)
   1350 {
   1351   struct simpleQueryParams qParam;
   1352 
   1353   /* Common parameters, to be individually overridden by specific test cases
   1354    * if needed */
   1355   qParam.queryPort = d_port;
   1356   qParam.method = MHD_HTTP_METHOD_PUT;
   1357   qParam.queryPath = EXPECTED_URI_BASE_PATH;
   1358   qParam.headers = REQ_HEADER_CT;
   1359   qParam.req_body = (const uint8_t *) REQ_BODY;
   1360   qParam.req_body_size = MHD_STATICSTR_LEN_ (REQ_BODY);
   1361   qParam.chunked = 0;
   1362 
   1363   ahc_param->rq_url = EXPECTED_URI_BASE_PATH;
   1364   ahc_param->rq_method = MHD_HTTP_METHOD_PUT;
   1365   ahc_param->rp_data = "~";
   1366   ahc_param->rp_data_size = 1;
   1367   ahc_param->req_body = (const char *) qParam.req_body;
   1368   ahc_param->req_body_size = qParam.req_body_size;
   1369 
   1370   /* Make sure that maximum size is tested */
   1371   /* To be updated by callbacks */
   1372   ahc_param->cb_called = 0;
   1373 
   1374   if (0 != doClientQueryInThread (d, &qParam))
   1375     fprintf (stderr, "FAILED: client query failed.");
   1376 
   1377   MHD_stop_daemon (d);
   1378   free (ahc_param);
   1379 
   1380   return 1; /* Always error if reached this point */
   1381 }
   1382 
   1383 
   1384 enum testMhdThreadsType
   1385 {
   1386   testMhdThreadExternal              = 0,
   1387   testMhdThreadInternal              = MHD_USE_INTERNAL_POLLING_THREAD,
   1388   testMhdThreadInternalPerConnection = MHD_USE_THREAD_PER_CONNECTION
   1389                                        | MHD_USE_INTERNAL_POLLING_THREAD,
   1390   testMhdThreadInternalPool
   1391 };
   1392 
   1393 enum testMhdPollType
   1394 {
   1395   testMhdPollBySelect = 0,
   1396   testMhdPollByPoll   = MHD_USE_POLL,
   1397   testMhdPollByEpoll  = MHD_USE_EPOLL,
   1398   testMhdPollAuto     = MHD_USE_AUTO
   1399 };
   1400 
   1401 /* Get number of threads for thread pool depending
   1402  * on used poll function and test type. */
   1403 static unsigned int
   1404 testNumThreadsForPool (enum testMhdPollType pollType)
   1405 {
   1406   unsigned int numThreads = MHD_CPU_COUNT;
   1407   (void) pollType; /* Don't care about pollType for this test */
   1408   return numThreads; /* No practical limit for non-cleanup test */
   1409 }
   1410 
   1411 
   1412 #define PANIC_MAGIC_CHECK 1133
   1413 
   1414 _MHD_NORETURN static void
   1415 myPanicCallback (void *cls,
   1416                  const char *file,
   1417                  unsigned int line,
   1418                  const char *reason)
   1419 {
   1420   int *const param = (int *) cls;
   1421   if (NULL == cls)
   1422     mhdErrorExitDesc ("The 'cls' parameter is NULL");
   1423   if (PANIC_MAGIC_CHECK != *param)
   1424     mhdErrorExitDesc ("Wrong '*cls' value");
   1425 #ifdef HAVE_MESSAGES
   1426   if (NULL == file)
   1427     mhdErrorExitDesc ("The 'file' parameter is NULL");
   1428   if (NULL == reason)
   1429     mhdErrorExitDesc ("The 'reason' parameter is NULL");
   1430 #else  /* ! HAVE_MESSAGES */
   1431   if (NULL != file)
   1432     mhdErrorExitDesc ("The 'file' parameter is not NULL");
   1433   if (NULL != reason)
   1434     mhdErrorExitDesc ("The 'reason' parameter is not NULL");
   1435 #endif /* ! HAVE_MESSAGES */
   1436   fflush (stderr);
   1437   fflush (stdout);
   1438   printf ("User panic function has been called from file '%s' at line '%u' "
   1439           "with the reason:\n%s", file, line,
   1440           ((NULL != reason) ? reason : "(NULL)\n"));
   1441   fflush (stdout);
   1442   exit (0);
   1443 }
   1444 
   1445 
   1446 static struct MHD_Daemon *
   1447 startTestMhdDaemon (enum testMhdThreadsType thrType,
   1448                     enum testMhdPollType pollType, uint16_t *pport,
   1449                     struct ahc_cls_type **ahc_param)
   1450 {
   1451   struct MHD_Daemon *d;
   1452   const union MHD_DaemonInfo *dinfo;
   1453   static int magic_panic_param = PANIC_MAGIC_CHECK;
   1454 
   1455   if (NULL == ahc_param)
   1456     externalErrorExit ();
   1457 
   1458   *ahc_param = (struct ahc_cls_type *) malloc (sizeof(struct ahc_cls_type));
   1459   if (NULL == *ahc_param)
   1460     externalErrorExit ();
   1461 
   1462   if ( (0 == *pport) &&
   1463        (MHD_NO == MHD_is_feature_supported (MHD_FEATURE_AUTODETECT_BIND_PORT)) )
   1464   {
   1465     *pport = 4190;
   1466   }
   1467 
   1468   MHD_set_panic_func (&myPanicCallback, (void *) &magic_panic_param);
   1469 
   1470   if (testMhdThreadExternal == thrType)
   1471     d = MHD_start_daemon (((unsigned int) pollType)
   1472                           | (verbose ? MHD_USE_ERROR_LOG : 0)
   1473                           | MHD_USE_NO_THREAD_SAFETY,
   1474                           *pport, NULL, NULL,
   1475                           &ahcCheck, *ahc_param,
   1476                           MHD_OPTION_NOTIFY_CONNECTION, &socket_cb,
   1477                           NULL,
   1478                           MHD_OPTION_CONNECTION_TIMEOUT,
   1479                           (unsigned) TIMEOUTS_VAL,
   1480                           MHD_OPTION_APP_FD_SETSIZE, (int) FD_SETSIZE,
   1481                           MHD_OPTION_END);
   1482   else if (testMhdThreadInternalPool != thrType)
   1483     d = MHD_start_daemon (((unsigned int) thrType) | ((unsigned int) pollType)
   1484                           | (verbose ? MHD_USE_ERROR_LOG : 0),
   1485                           *pport, NULL, NULL,
   1486                           &ahcCheck, *ahc_param,
   1487                           MHD_OPTION_NOTIFY_CONNECTION, &socket_cb,
   1488                           NULL,
   1489                           MHD_OPTION_CONNECTION_TIMEOUT,
   1490                           (unsigned) TIMEOUTS_VAL,
   1491                           MHD_OPTION_END);
   1492   else
   1493     d = MHD_start_daemon (MHD_USE_INTERNAL_POLLING_THREAD
   1494                           | ((unsigned int) pollType)
   1495                           | (verbose ? MHD_USE_ERROR_LOG : 0),
   1496                           *pport, NULL, NULL,
   1497                           &ahcCheck, *ahc_param,
   1498                           MHD_OPTION_THREAD_POOL_SIZE,
   1499                           testNumThreadsForPool (pollType),
   1500                           MHD_OPTION_NOTIFY_CONNECTION, &socket_cb,
   1501                           NULL,
   1502                           MHD_OPTION_CONNECTION_TIMEOUT,
   1503                           (unsigned) TIMEOUTS_VAL,
   1504                           MHD_OPTION_END);
   1505 
   1506   if (NULL == d)
   1507     mhdErrorExitDesc ("Failed to start MHD daemon");
   1508 
   1509   if (0 == *pport)
   1510   {
   1511     dinfo = MHD_get_daemon_info (d, MHD_DAEMON_INFO_BIND_PORT);
   1512     if ((NULL == dinfo) || (0 == dinfo->port))
   1513       mhdErrorExitDesc ("MHD_get_daemon_info() failed");
   1514     *pport = dinfo->port;
   1515     if (0 == global_port)
   1516       global_port = *pport; /* Reuse the same port for all tests */
   1517   }
   1518 
   1519   return d;
   1520 }
   1521 
   1522 
   1523 /* Test runners */
   1524 
   1525 
   1526 static unsigned int
   1527 testExternalGet (void)
   1528 {
   1529   struct MHD_Daemon *d;
   1530   uint16_t d_port = global_port; /* Daemon's port */
   1531   struct ahc_cls_type *ahc_param;
   1532 
   1533   d = startTestMhdDaemon (testMhdThreadExternal, testMhdPollBySelect, &d_port,
   1534                           &ahc_param);
   1535 
   1536   return performTestQueries (d, d_port, ahc_param);
   1537 }
   1538 
   1539 
   1540 #if 0 /* disabled runners, not suitable for this test */
   1541 static unsigned int
   1542 testInternalGet (enum testMhdPollType pollType)
   1543 {
   1544   struct MHD_Daemon *d;
   1545   uint16_t d_port = global_port; /* Daemon's port */
   1546   struct ahc_cls_type *ahc_param;
   1547   struct check_uri_cls *uri_cb_param;
   1548   struct term_notif_cb_param *term_result;
   1549 
   1550   d = startTestMhdDaemon (testMhdThreadInternal, pollType, &d_port,
   1551                           &ahc_param, &uri_cb_param, &term_result);
   1552 
   1553   return performTestQueries (d, d_port, ahc_param, uri_cb_param, term_result);
   1554 }
   1555 
   1556 
   1557 static int
   1558 testMultithreadedGet (enum testMhdPollType pollType)
   1559 {
   1560   struct MHD_Daemon *d;
   1561   uint16_t d_port = global_port; /* Daemon's port */
   1562   struct ahc_cls_type *ahc_param;
   1563   struct check_uri_cls *uri_cb_param;
   1564   struct term_notif_cb_param *term_result;
   1565 
   1566   d = startTestMhdDaemon (testMhdThreadInternalPerConnection, pollType, &d_port,
   1567                           &ahc_param, &uri_cb_param);
   1568   return performTestQueries (d, d_port, ahc_param, uri_cb_param, term_result);
   1569 }
   1570 
   1571 
   1572 static unsigned int
   1573 testMultithreadedPoolGet (enum testMhdPollType pollType)
   1574 {
   1575   struct MHD_Daemon *d;
   1576   uint16_t d_port = global_port; /* Daemon's port */
   1577   struct ahc_cls_type *ahc_param;
   1578   struct check_uri_cls *uri_cb_param;
   1579   struct term_notif_cb_param *term_result;
   1580 
   1581   d = startTestMhdDaemon (testMhdThreadInternalPool, pollType, &d_port,
   1582                           &ahc_param, &uri_cb_param);
   1583   return performTestQueries (d, d_port, ahc_param, uri_cb_param, term_result);
   1584 }
   1585 
   1586 
   1587 #endif /* disabled runners, not suitable for this test */
   1588 
   1589 int
   1590 main (int argc, char *const *argv)
   1591 {
   1592   unsigned int errorCount = 0;
   1593   unsigned int test_result = 0;
   1594   verbose = 0;
   1595 
   1596   (void) has_in_name; /* Unused, mute compiler warning */
   1597   if ((NULL == argv) || (0 == argv[0]))
   1598     return 99;
   1599   verbose = ! (has_param (argc, argv, "-q") ||
   1600                has_param (argc, argv, "--quiet") ||
   1601                has_param (argc, argv, "-s") ||
   1602                has_param (argc, argv, "--silent"));
   1603 
   1604   test_global_init ();
   1605 
   1606   /* Could be set to non-zero value to enforce using specific port
   1607    * in the test */
   1608   global_port = 0;
   1609   test_result = testExternalGet ();
   1610   if (test_result)
   1611     fprintf (stderr, "FAILED: testExternalGet (). Result: %u.\n", test_result);
   1612   else if (verbose)
   1613     printf ("PASSED: testExternalGet ().\n");
   1614   errorCount += test_result;
   1615 #if 0 /* disabled runners, not suitable for this test */
   1616   if (MHD_YES == MHD_is_feature_supported (MHD_FEATURE_THREADS))
   1617   {
   1618     test_result = testInternalGet (testMhdPollAuto);
   1619     if (test_result)
   1620       fprintf (stderr, "FAILED: testInternalGet (testMhdPollAuto). "
   1621                "Result: %u.\n",
   1622                test_result);
   1623     else if (verbose)
   1624       printf ("PASSED: testInternalGet (testMhdPollBySelect).\n");
   1625     errorCount += test_result;
   1626 #ifdef _MHD_HEAVY_TESTS
   1627     /* Actually tests are not heavy, but took too long to complete while
   1628      * not really provide any additional results. */
   1629     test_result = testInternalGet (testMhdPollBySelect);
   1630     if (test_result)
   1631       fprintf (stderr, "FAILED: testInternalGet (testMhdPollBySelect). "
   1632                "Result: %u.\n",
   1633                test_result);
   1634     else if (verbose)
   1635       printf ("PASSED: testInternalGet (testMhdPollBySelect).\n");
   1636     errorCount += test_result;
   1637     test_result = testMultithreadedPoolGet (testMhdPollBySelect);
   1638     if (test_result)
   1639       fprintf (stderr,
   1640                "FAILED: testMultithreadedPoolGet (testMhdPollBySelect). "
   1641                "Result: %u.\n",
   1642                test_result);
   1643     else if (verbose)
   1644       printf ("PASSED: testMultithreadedPoolGet (testMhdPollBySelect).\n");
   1645     errorCount += test_result;
   1646     test_result = testMultithreadedGet (testMhdPollBySelect);
   1647     if (test_result)
   1648       fprintf (stderr,
   1649                "FAILED: testMultithreadedGet (testMhdPollBySelect). "
   1650                "Result: %u.\n",
   1651                test_result);
   1652     else if (verbose)
   1653       printf ("PASSED: testMultithreadedGet (testMhdPollBySelect).\n");
   1654     errorCount += test_result;
   1655     if (MHD_YES == MHD_is_feature_supported (MHD_FEATURE_POLL))
   1656     {
   1657       test_result = testInternalGet (testMhdPollByPoll);
   1658       if (test_result)
   1659         fprintf (stderr, "FAILED: testInternalGet (testMhdPollByPoll). "
   1660                  "Result: %u.\n",
   1661                  test_result);
   1662       else if (verbose)
   1663         printf ("PASSED: testInternalGet (testMhdPollByPoll).\n");
   1664       errorCount += test_result;
   1665     }
   1666     if (MHD_YES == MHD_is_feature_supported (MHD_FEATURE_EPOLL))
   1667     {
   1668       test_result = testInternalGet (testMhdPollByEpoll);
   1669       if (test_result)
   1670         fprintf (stderr, "FAILED: testInternalGet (testMhdPollByEpoll). "
   1671                  "Result: %u.\n",
   1672                  test_result);
   1673       else if (verbose)
   1674         printf ("PASSED: testInternalGet (testMhdPollByEpoll).\n");
   1675       errorCount += test_result;
   1676     }
   1677 #else
   1678     /* Mute compiler warnings */
   1679     (void) testMultithreadedGet;
   1680     (void) testMultithreadedPoolGet;
   1681 #endif /* _MHD_HEAVY_TESTS */
   1682   }
   1683 #endif /* disabled runners, not suitable for this test */
   1684   if (0 != errorCount)
   1685     fprintf (stderr,
   1686              "Error (code: %u)\n",
   1687              errorCount);
   1688   else if (verbose)
   1689     printf ("All tests passed.\n");
   1690 
   1691   test_global_cleanup ();
   1692 
   1693   return (errorCount == 0) ? 0 : 1;       /* 0 == pass */
   1694 }