libmicrohttpd

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

test_client_put_stop.c (69729B)


      1 /*
      2      This file is part of libmicrohttpd
      3      Copyright (C) 2021-2024 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_client_put_stop.c
     22  * @brief  Testcase for handling of clients aborts
     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 <stdint.h>
     33 #include <errno.h>
     34 
     35 #ifdef HAVE_STRINGS_H
     36 #include <strings.h>
     37 #endif /* HAVE_STRINGS_H */
     38 
     39 #ifdef _WIN32
     40 #ifndef WIN32_LEAN_AND_MEAN
     41 #define WIN32_LEAN_AND_MEAN 1
     42 #endif /* !WIN32_LEAN_AND_MEAN */
     43 #include <windows.h>
     44 #endif
     45 
     46 #ifndef WINDOWS
     47 #include <unistd.h>
     48 #include <sys/socket.h>
     49 #endif
     50 
     51 #ifdef HAVE_LIMITS_H
     52 #include <limits.h>
     53 #endif /* HAVE_LIMITS_H */
     54 
     55 #ifdef HAVE_SIGNAL_H
     56 #include <signal.h>
     57 #endif /* HAVE_SIGNAL_H */
     58 
     59 #ifdef HAVE_SYSCTL
     60 #ifdef HAVE_SYS_TYPES_H
     61 #include <sys/types.h>
     62 #endif /* HAVE_SYS_TYPES_H */
     63 #ifdef HAVE_SYS_SYSCTL_H
     64 #include <sys/sysctl.h>
     65 #endif /* HAVE_SYS_SYSCTL_H */
     66 #ifdef HAVE_SYS_SOCKET_H
     67 #include <sys/socket.h>
     68 #endif /* HAVE_SYS_SOCKET_H */
     69 #ifdef HAVE_NETINET_IN_SYSTM_H
     70 #include <netinet/in_systm.h>
     71 #endif /* HAVE_NETINET_IN_SYSTM_H */
     72 #ifdef HAVE_NETINET_IN_H
     73 #include <netinet/in.h>
     74 #endif /* HAVE_NETINET_IN_H */
     75 #ifdef HAVE_NETINET_IP_H
     76 #include <netinet/ip.h>
     77 #endif /* HAVE_NETINET_IP_H */
     78 #ifdef HAVE_NETINET_IP_ICMP_H
     79 #include <netinet/ip_icmp.h>
     80 #endif /* HAVE_NETINET_IP_ICMP_H */
     81 #ifdef HAVE_NETINET_ICMP_VAR_H
     82 #include <netinet/icmp_var.h>
     83 #endif /* HAVE_NETINET_ICMP_VAR_H */
     84 #endif /* HAVE_SYSCTL */
     85 
     86 #include <stdio.h>
     87 
     88 #include "mhd_sockets.h" /* only macros used */
     89 #include "test_helpers.h"
     90 #include "mhd_assert.h"
     91 
     92 /* Turn any MHD_PANIC() or failing mhd_assert() reached from this
     93    test into a marked, classifiable test error (TESTING.md, P5). */
     94 #include "mhd_panic_tripwire.h"
     95 
     96 #if defined(MHD_CPU_COUNT) && (MHD_CPU_COUNT + 0) < 2
     97 #undef MHD_CPU_COUNT
     98 #endif
     99 #if ! defined(MHD_CPU_COUNT)
    100 #define MHD_CPU_COUNT 2
    101 #endif
    102 #if MHD_CPU_COUNT > 32
    103 #undef MHD_CPU_COUNT
    104 /* Limit to reasonable value */
    105 #define MHD_CPU_COUNT 32
    106 #endif /* MHD_CPU_COUNT > 32 */
    107 
    108 #ifndef MHD_STATICSTR_LEN_
    109 /**
    110  * Determine length of static string / macro strings at compile time.
    111  */
    112 #define MHD_STATICSTR_LEN_(macro) (sizeof(macro) / sizeof(char) - 1)
    113 #endif /* ! MHD_STATICSTR_LEN_ */
    114 
    115 #ifndef _MHD_INSTRMACRO
    116 /* Quoted macro parameter */
    117 #define _MHD_INSTRMACRO(a) #a
    118 #endif /* ! _MHD_INSTRMACRO */
    119 #ifndef _MHD_STRMACRO
    120 /* Quoted expanded macro parameter */
    121 #define _MHD_STRMACRO(a) _MHD_INSTRMACRO (a)
    122 #endif /* ! _MHD_STRMACRO */
    123 
    124 
    125 /* Could be increased to facilitate debugging */
    126 #define TIMEOUTS_VAL 5
    127 
    128 /* Time in ms to wait for final packets to be delivered */
    129 #define FINAL_PACKETS_MS 20
    130 
    131 #define EXPECTED_URI_BASE_PATH  "/a"
    132 
    133 #define REQ_HOST "localhost"
    134 
    135 #define REQ_METHOD "PUT"
    136 
    137 #define REQ_BODY "Some content data."
    138 
    139 #define REQ_LINE_END "\r\n"
    140 
    141 /* Mandatory request headers */
    142 #define REQ_HEADER_HOST_NAME "Host"
    143 #define REQ_HEADER_HOST_VALUE REQ_HOST
    144 #define REQ_HEADER_HOST \
    145   REQ_HEADER_HOST_NAME ": " REQ_HEADER_HOST_VALUE REQ_LINE_END
    146 #define REQ_HEADER_UA_NAME "User-Agent"
    147 #define REQ_HEADER_UA_VALUE "dummyclient/0.9"
    148 #define REQ_HEADER_UA REQ_HEADER_UA_NAME ": " REQ_HEADER_UA_VALUE REQ_LINE_END
    149 
    150 /* Optional request headers */
    151 #define REQ_HEADER_CT_NAME "Content-Type"
    152 #define REQ_HEADER_CT_VALUE "text/plain"
    153 #define REQ_HEADER_CT REQ_HEADER_CT_NAME ": " REQ_HEADER_CT_VALUE REQ_LINE_END
    154 
    155 
    156 #if defined(HAVE___FUNC__)
    157 #define externalErrorExit(ignore) \
    158     _externalErrorExit_func(NULL, __func__, __LINE__)
    159 #define externalErrorExitDesc(errDesc) \
    160     _externalErrorExit_func(errDesc, __func__, __LINE__)
    161 #define mhdErrorExit(ignore) \
    162     _mhdErrorExit_func(NULL, __func__, __LINE__)
    163 #define mhdErrorExitDesc(errDesc) \
    164     _mhdErrorExit_func(errDesc, __func__, __LINE__)
    165 #elif defined(HAVE___FUNCTION__)
    166 #define externalErrorExit(ignore) \
    167     _externalErrorExit_func(NULL, __FUNCTION__, __LINE__)
    168 #define externalErrorExitDesc(errDesc) \
    169     _externalErrorExit_func(errDesc, __FUNCTION__, __LINE__)
    170 #define mhdErrorExit(ignore) \
    171     _mhdErrorExit_func(NULL, __FUNCTION__, __LINE__)
    172 #define mhdErrorExitDesc(errDesc) \
    173     _mhdErrorExit_func(errDesc, __FUNCTION__, __LINE__)
    174 #else
    175 #define externalErrorExit(ignore) _externalErrorExit_func(NULL, NULL, __LINE__)
    176 #define externalErrorExitDesc(errDesc) \
    177   _externalErrorExit_func(errDesc, NULL, __LINE__)
    178 #define mhdErrorExit(ignore) _mhdErrorExit_func(NULL, NULL, __LINE__)
    179 #define mhdErrorExitDesc(errDesc) _mhdErrorExit_func(errDesc, NULL, __LINE__)
    180 #endif
    181 
    182 
    183 _MHD_NORETURN static void
    184 _externalErrorExit_func (const char *errDesc, const char *funcName, int lineNum)
    185 {
    186   if ((NULL != errDesc) && (0 != errDesc[0]))
    187     fprintf (stderr, "%s", errDesc);
    188   else
    189     fprintf (stderr, "System or external library call failed");
    190   if ((NULL != funcName) && (0 != funcName[0]))
    191     fprintf (stderr, " in %s", funcName);
    192   if (0 < lineNum)
    193     fprintf (stderr, " at line %d", lineNum);
    194 
    195   fprintf (stderr, ".\nLast errno value: %d (%s)\n", (int) errno,
    196            strerror (errno));
    197 #ifdef MHD_WINSOCK_SOCKETS
    198   fprintf (stderr, "WSAGetLastError() value: %d\n", (int) WSAGetLastError ());
    199 #endif /* MHD_WINSOCK_SOCKETS */
    200   fflush (stderr);
    201   exit (99);
    202 }
    203 
    204 
    205 _MHD_NORETURN static void
    206 _mhdErrorExit_func (const char *errDesc, const char *funcName, int lineNum)
    207 {
    208   if ((NULL != errDesc) && (0 != errDesc[0]))
    209     fprintf (stderr, "%s", errDesc);
    210   else
    211     fprintf (stderr, "MHD unexpected error");
    212   if ((NULL != funcName) && (0 != funcName[0]))
    213     fprintf (stderr, " in %s", funcName);
    214   if (0 < lineNum)
    215     fprintf (stderr, " at line %d", lineNum);
    216 
    217   fprintf (stderr, ".\nLast errno value: %d (%s)\n", (int) errno,
    218            strerror (errno));
    219 
    220   fflush (stderr);
    221   exit (8);
    222 }
    223 
    224 
    225 /* Global generic functions */
    226 
    227 void
    228 _MHD_sleep (uint32_t ms);
    229 
    230 
    231 /**
    232  * Pause execution for specified number of milliseconds.
    233  * @param ms the number of milliseconds to sleep
    234  */
    235 void
    236 _MHD_sleep (uint32_t ms)
    237 {
    238 #if defined(_WIN32)
    239   Sleep (ms);
    240 #elif defined(HAVE_NANOSLEEP)
    241   struct timespec slp = {ms / 1000, (ms % 1000) * 1000000};
    242   struct timespec rmn;
    243   int num_retries = 0;
    244   while (0 != nanosleep (&slp, &rmn))
    245   {
    246     if (EINTR != errno)
    247       externalErrorExit ();
    248     if (num_retries++ > 8)
    249       break;
    250     slp = rmn;
    251   }
    252 #elif defined(HAVE_USLEEP)
    253   uint64_t us = ms * 1000;
    254   do
    255   {
    256     uint64_t this_sleep;
    257     if (999999 < us)
    258       this_sleep = 999999;
    259     else
    260       this_sleep = us;
    261     /* Ignore return value as it could be void */
    262     usleep (this_sleep);
    263     us -= this_sleep;
    264   } while (us > 0);
    265 #else
    266   externalErrorExitDesc ("No sleep function available on this system");
    267 #endif
    268 }
    269 
    270 
    271 /* Global parameters */
    272 static int verbose;                 /**< Be verbose */
    273 static int oneone;                  /**< If false use HTTP/1.0 for requests*/
    274 static uint16_t global_port;        /**< MHD daemons listen port number */
    275 
    276 static int use_shutdown;            /**< Use shutdown at client side */
    277 static int use_close;               /**< Use socket close at client side */
    278 static int use_hard_close;          /**< Use socket close with RST at client side */
    279 static int use_stress_os;           /**< Stress OS by RST before getting ACKs for sent packets */
    280 static int by_step;                 /**< Send request byte-by-byte */
    281 static int upl_chunked;             /**< Use chunked encoding for request body */
    282 
    283 static unsigned int rate_limiter;   /**< Maximum number of checks per second */
    284 
    285 static void
    286 test_global_init (void)
    287 {
    288   rate_limiter = 0;
    289   if (use_hard_close)
    290   {
    291 #ifdef HAVE_SYSCTLBYNAME
    292     if (1)
    293     {
    294       int blck_hl;
    295       size_t blck_hl_size = sizeof (blck_hl);
    296       if (0 == sysctlbyname ("net.inet.tcp.blackhole", &blck_hl, &blck_hl_size,
    297                              NULL, 0))
    298       {
    299         if (2 <= blck_hl)
    300         {
    301           fprintf (stderr, "'sysctl net.inet.tcp.blackhole = %d', test is "
    302                    "unreliable with this system setting, skipping.\n", blck_hl);
    303           exit (77);
    304         }
    305       }
    306       else
    307       {
    308         if (ENOENT != errno)
    309           externalErrorExitDesc ("Cannot get 'net.inet.tcp.blackhole' value");
    310       }
    311     }
    312 #endif
    313 #if defined(HAVE_SYSCTL) && defined(HAVE_DECL_CTL_NET) && \
    314     defined(HAVE_DECL_PF_INET) && defined(HAVE_DECL_IPPROTO_ICMP) && \
    315     defined(HAVE_DECL_ICMPCTL_ICMPLIM)
    316     /* Macros may have zero values */
    317 #if HAVE_DECL_CTL_NET && HAVE_DECL_PF_INET && HAVE_DECL_IPPROTO_ICMP && \
    318     HAVE_DECL_ICMPCTL_ICMPLIM
    319     if (1)
    320     {
    321       int mib[4];
    322       int limit;
    323       size_t limit_size = sizeof(limit);
    324       mib[0] = CTL_NET;
    325       mib[1] = PF_INET;
    326       mib[2] = IPPROTO_ICMP;
    327       mib[3] = ICMPCTL_ICMPLIM;
    328       if (0 != sysctl (mib, 4, &limit, &limit_size, NULL, 0))
    329       {
    330         if (ENOENT == errno)
    331           limit = 0; /* No such parameter (new Darwin versions) */
    332         else
    333           externalErrorExitDesc ("Cannot get RST rate limit value");
    334       }
    335       else if (sizeof(limit) != limit_size)
    336         externalErrorExitDesc ("Cannot get RST rate limit value");
    337       if (limit > 0)
    338       {
    339 #ifndef _MHD_HEAVY_TESTS
    340         fprintf (stderr, "This system has limits on number of RST packets"
    341                  " per second (%d).\nThis test will be used only if configured "
    342                  "with '--enable-heavy-test'.\n", limit);
    343         exit (77);
    344 #else  /* _MHD_HEAVY_TESTS */
    345         int test_limit; /**< Maximum number of checks per second */
    346 
    347         if (use_stress_os)
    348         {
    349           fprintf (stderr, "This system has limits on number of RST packet"
    350                    " per second (%d).\n'_stress_os' is not possible.\n", limit);
    351           exit (77);
    352         }
    353         test_limit = limit - limit / 10; /* Add some space to not hit the limiter */
    354         test_limit /= 4;   /* Assume that all four tests with 'hard_close' run in parallel */
    355         test_limit -= 5;   /* Add some more space to not hit the limiter */
    356         test_limit /= 3;   /* Use only one third of available limit */
    357         if (test_limit <= 0)
    358         {
    359           fprintf (stderr, "System limit for 'net.inet.icmp.icmplim' is "
    360                    "too strict for this test (value: %d).\n", limit);
    361           exit (77);
    362         }
    363         if (verbose)
    364         {
    365           printf ("Limiting number of checks to %d checks/second.\n",
    366                   test_limit);
    367           fflush (stdout);
    368         }
    369         rate_limiter = (unsigned int) test_limit;
    370 #if ! defined(HAVE_USLEEP) && ! defined(HAVE_NANOSLEEP) && ! defined(_WIN32)
    371         fprintf (stderr, "Sleep function is required for this test, "
    372                  "but not available on this system.\n");
    373         exit (77);
    374 #endif
    375 #endif /* _MHD_HEAVY_TESTS */
    376       }
    377     }
    378 #endif /* HAVE_DECL_CTL_NET && HAVE_DECL_PF_INET && HAVE_DECL_IPPROTO_ICMP && \
    379           HAVE_DECL_ICMPCTL_ICMPLIM */
    380 #endif /* HAVE_SYSCTL && HAVE_DECL_CTL_NET && HAVE_DECL_PF_INET &&
    381           HAVE_DECL_IPPROTO_ICMP && HAVE_DECL_ICMPCTL_ICMPLIM */
    382   }
    383   if (MHD_YES != MHD_is_feature_supported (MHD_FEATURE_AUTOSUPPRESS_SIGPIPE))
    384   {
    385 #if defined(HAVE_SIGNAL_H) && defined(SIGPIPE)
    386     if (SIG_ERR == signal (SIGPIPE, SIG_IGN))
    387       externalErrorExitDesc ("Error suppressing SIGPIPE signal");
    388 #else /* ! HAVE_SIGNAL_H || ! SIGPIPE */
    389     fprintf (stderr, "Cannot suppress SIGPIPE signal.\n");
    390     /* exit (77); */
    391 #endif
    392   }
    393 }
    394 
    395 
    396 static void
    397 test_global_cleanup (void)
    398 {
    399 }
    400 
    401 
    402 /**
    403  * Change socket to blocking.
    404  *
    405  * @param fd the socket to manipulate
    406  */
    407 static void
    408 make_blocking (MHD_socket fd)
    409 {
    410 #if defined(MHD_POSIX_SOCKETS)
    411   int flags;
    412 
    413   flags = fcntl (fd, F_GETFL);
    414   if (-1 == flags)
    415     externalErrorExitDesc ("Cannot make socket non-blocking");
    416   if ((flags & ~O_NONBLOCK) != flags)
    417   {
    418     if (-1 == fcntl (fd, F_SETFL, flags & ~O_NONBLOCK))
    419       externalErrorExitDesc ("Cannot make socket non-blocking");
    420   }
    421 #elif defined(MHD_WINSOCK_SOCKETS)
    422   unsigned long flags = 0;
    423 
    424   if (0 != ioctlsocket (fd, (int) FIONBIO, &flags))
    425     externalErrorExitDesc ("Cannot make socket non-blocking");
    426 #endif /* MHD_WINSOCK_SOCKETS */
    427 }
    428 
    429 
    430 /**
    431  * Change socket to non-blocking.
    432  *
    433  * @param fd the socket to manipulate
    434  */
    435 static void
    436 make_nonblocking (MHD_socket fd)
    437 {
    438 #if defined(MHD_POSIX_SOCKETS)
    439   int flags;
    440 
    441   flags = fcntl (fd, F_GETFL);
    442   if (-1 == flags)
    443     externalErrorExitDesc ("Cannot make socket non-blocking");
    444   if ((flags | O_NONBLOCK) != flags)
    445   {
    446     if (-1 == fcntl (fd, F_SETFL, flags | O_NONBLOCK))
    447       externalErrorExitDesc ("Cannot make socket non-blocking");
    448   }
    449 #elif defined(MHD_WINSOCK_SOCKETS)
    450   unsigned long flags = 1;
    451 
    452   if (0 != ioctlsocket (fd, (int) FIONBIO, &flags))
    453     externalErrorExitDesc ("Cannot make socket non-blocking");
    454 #endif /* MHD_WINSOCK_SOCKETS */
    455 }
    456 
    457 
    458 /* DumbClient API */
    459 struct _MHD_dumbClient *
    460 _MHD_dumbClient_create (uint16_t port, const char *method, const char *url,
    461                         const char *add_headers,
    462                         const uint8_t *req_body, size_t req_body_size,
    463                         int chunked);
    464 
    465 void
    466 _MHD_dumbClient_set_send_limits (struct _MHD_dumbClient *clnt,
    467                                  size_t step_size, size_t max_total_send);
    468 
    469 void
    470 _MHD_dumbClient_start_connect (struct _MHD_dumbClient *clnt);
    471 
    472 int
    473 _MHD_dumbClient_is_req_sent (struct _MHD_dumbClient *clnt);
    474 
    475 
    476 /**
    477  * Process the client data with send()/recv() as needed.
    478  * @param clnt the client to process
    479  * @return non-zero if client finished processing the request,
    480  *         zero otherwise.
    481  */
    482 int
    483 _MHD_dumbClient_process (struct _MHD_dumbClient *clnt);
    484 
    485 
    486 void
    487 _MHD_dumbClient_get_fdsets (struct _MHD_dumbClient *clnt,
    488                             MHD_socket *maxsckt,
    489                             fd_set *rs, fd_set *ws, fd_set *es);
    490 
    491 /**
    492  * Process the client data with send()/recv() as needed based on
    493  * information in fd_sets.
    494  * @param clnt the client to process
    495  * @return non-zero if client finished processing the request,
    496  *         zero otherwise.
    497  */
    498 int
    499 _MHD_dumbClient_process_from_fdsets (struct _MHD_dumbClient *clnt,
    500                                      fd_set *rs, fd_set *ws, fd_set *es);
    501 
    502 
    503 /**
    504  * Perform full request.
    505  * @param clnt the client to run
    506  * @return zero if client finished processing the request,
    507  *         non-zero if timeout is reached.
    508  */
    509 int
    510 _MHD_dumbClient_perform (struct _MHD_dumbClient *clnt);
    511 
    512 
    513 /**
    514  * Close the client and free internally allocated resources.
    515  * @param clnt the client to close
    516  */
    517 void
    518 _MHD_dumbClient_close (struct _MHD_dumbClient *clnt);
    519 
    520 
    521 /* DumbClient implementation */
    522 
    523 enum _MHD_clientStage
    524 {
    525   DUMB_CLIENT_INIT = 0,
    526   DUMB_CLIENT_CONNECTING,
    527   DUMB_CLIENT_CONNECTED,
    528   DUMB_CLIENT_REQ_SENDING,
    529   DUMB_CLIENT_REQ_SENT,
    530   DUMB_CLIENT_HEADER_RECVEIVING,
    531   DUMB_CLIENT_HEADER_RECVEIVED,
    532   DUMB_CLIENT_BODY_RECVEIVING,
    533   DUMB_CLIENT_BODY_RECVEIVED,
    534   DUMB_CLIENT_FINISHING,
    535   DUMB_CLIENT_FINISHED
    536 };
    537 
    538 struct _MHD_dumbClient
    539 {
    540   MHD_socket sckt; /**< the socket to communicate */
    541 
    542   int sckt_nonblock;  /**< non-zero if socket is non-blocking */
    543 
    544   uint16_t port; /**< the port to connect to */
    545 
    546   const char *send_buf; /**< the buffer for the request, malloced */
    547 
    548   void *buf; /**< the buffer location */
    549 
    550   size_t req_size; /**< the size of the request, including header */
    551 
    552   size_t send_off; /**< the number of bytes already sent */
    553 
    554   enum _MHD_clientStage stage;
    555 
    556   /* the test-specific variables */
    557   size_t single_send_size; /**< the maximum number of bytes to be sent by
    558                                 single send() */
    559   size_t send_size_limit;  /**< the total number of send bytes limit */
    560 };
    561 
    562 struct _MHD_dumbClient *
    563 _MHD_dumbClient_create (uint16_t port, const char *method, const char *url,
    564                         const char *add_headers,
    565                         const uint8_t *req_body, size_t req_body_size,
    566                         int chunked)
    567 {
    568   struct _MHD_dumbClient *clnt;
    569   size_t method_size;
    570   size_t url_size;
    571   size_t add_hdrs_size;
    572   size_t buf_alloc_size;
    573   char *send_buf;
    574   mhd_assert (0 != port);
    575   mhd_assert (NULL != req_body || 0 == req_body_size);
    576   mhd_assert (0 == req_body_size || NULL != req_body);
    577 
    578   clnt = (struct _MHD_dumbClient *) malloc (sizeof(struct _MHD_dumbClient));
    579   if (NULL == clnt)
    580     externalErrorExit ();
    581   memset (clnt, 0, sizeof(struct _MHD_dumbClient));
    582   clnt->sckt = socket (AF_INET, SOCK_STREAM, IPPROTO_TCP);
    583   if (MHD_INVALID_SOCKET == clnt->sckt)
    584     externalErrorExitDesc ("Cannot create the client socket");
    585 
    586 #ifdef MHD_socket_nosignal_
    587   if (! MHD_socket_nosignal_ (clnt->sckt))
    588     externalErrorExitDesc ("Cannot suppress SIGPIPE on the client socket");
    589 #endif /* MHD_socket_nosignal_ */
    590 
    591   clnt->sckt_nonblock = 0;
    592   if (clnt->sckt_nonblock)
    593     make_nonblocking (clnt->sckt);
    594   else
    595     make_blocking (clnt->sckt);
    596 
    597   if (1)
    598   { /* Always set TCP NODELAY */
    599     const MHD_SCKT_OPT_BOOL_ on_val = 1;
    600 
    601     if (0 != setsockopt (clnt->sckt, IPPROTO_TCP, TCP_NODELAY,
    602                          (const void *) &on_val, sizeof (on_val)))
    603       externalErrorExitDesc ("Cannot set TCP_NODELAY option");
    604   }
    605 
    606   clnt->port = port;
    607 
    608   if (NULL != method)
    609     method_size = strlen (method);
    610   else
    611   {
    612     method = MHD_HTTP_METHOD_GET;
    613     method_size = MHD_STATICSTR_LEN_ (MHD_HTTP_METHOD_GET);
    614   }
    615   mhd_assert (0 != method_size);
    616   if (NULL != url)
    617     url_size = strlen (url);
    618   else
    619   {
    620     url = "/";
    621     url_size = 1;
    622   }
    623   mhd_assert (0 != url_size);
    624   add_hdrs_size = (NULL == add_headers) ? 0 : strlen (add_headers);
    625   buf_alloc_size = 1024 + method_size + url_size
    626                    + add_hdrs_size + req_body_size;
    627   send_buf = (char *) malloc (buf_alloc_size);
    628   if (NULL == send_buf)
    629     externalErrorExit ();
    630 
    631   clnt->req_size = 0;
    632   /* Form the request line */
    633   memcpy (send_buf + clnt->req_size, method, method_size);
    634   clnt->req_size += method_size;
    635   send_buf[clnt->req_size++] = ' ';
    636   memcpy (send_buf + clnt->req_size, url, url_size);
    637   clnt->req_size += url_size;
    638   send_buf[clnt->req_size++] = ' ';
    639   memcpy (send_buf + clnt->req_size, MHD_HTTP_VERSION_1_1,
    640           MHD_STATICSTR_LEN_ (MHD_HTTP_VERSION_1_1));
    641   clnt->req_size += MHD_STATICSTR_LEN_ (MHD_HTTP_VERSION_1_1);
    642   send_buf[clnt->req_size++] = '\r';
    643   send_buf[clnt->req_size++] = '\n';
    644   /* Form the header */
    645   memcpy (send_buf + clnt->req_size, REQ_HEADER_HOST,
    646           MHD_STATICSTR_LEN_ (REQ_HEADER_HOST));
    647   clnt->req_size += MHD_STATICSTR_LEN_ (REQ_HEADER_HOST);
    648   memcpy (send_buf + clnt->req_size, REQ_HEADER_UA,
    649           MHD_STATICSTR_LEN_ (REQ_HEADER_UA));
    650   clnt->req_size += MHD_STATICSTR_LEN_ (REQ_HEADER_UA);
    651   if ((NULL != req_body) || chunked)
    652   {
    653     if (! chunked)
    654     {
    655       int prn_size;
    656       memcpy (send_buf + clnt->req_size, MHD_HTTP_HEADER_CONTENT_LENGTH ": ",
    657               MHD_STATICSTR_LEN_ (MHD_HTTP_HEADER_CONTENT_LENGTH ": "));
    658       clnt->req_size +=
    659         MHD_STATICSTR_LEN_ (MHD_HTTP_HEADER_CONTENT_LENGTH ": ");
    660       prn_size = snprintf (send_buf + clnt->req_size,
    661                            (buf_alloc_size - clnt->req_size),
    662                            "%u", (unsigned int) req_body_size);
    663       if (0 >= prn_size)
    664         externalErrorExit ();
    665       if ((unsigned int) prn_size >= buf_alloc_size - clnt->req_size)
    666         externalErrorExit ();
    667       clnt->req_size += (unsigned int) prn_size;
    668       send_buf[clnt->req_size++] = '\r';
    669       send_buf[clnt->req_size++] = '\n';
    670     }
    671     else
    672     {
    673       memcpy (send_buf + clnt->req_size,
    674               MHD_HTTP_HEADER_TRANSFER_ENCODING ": chunked\r\n",
    675               MHD_STATICSTR_LEN_ (MHD_HTTP_HEADER_TRANSFER_ENCODING \
    676                                   ": chunked\r\n"));
    677       clnt->req_size += MHD_STATICSTR_LEN_ (MHD_HTTP_HEADER_TRANSFER_ENCODING \
    678                                             ": chunked\r\n");
    679     }
    680   }
    681   if (0 != add_hdrs_size)
    682   {
    683     memcpy (send_buf + clnt->req_size, add_headers, add_hdrs_size);
    684     clnt->req_size += add_hdrs_size;
    685   }
    686   /* Terminate header */
    687   send_buf[clnt->req_size++] = '\r';
    688   send_buf[clnt->req_size++] = '\n';
    689 
    690   /* Add body (if any) */
    691   if (! chunked)
    692   {
    693     if (0 != req_body_size)
    694     {
    695       memcpy (send_buf + clnt->req_size, req_body, req_body_size);
    696       clnt->req_size += req_body_size;
    697     }
    698   }
    699   else
    700   {
    701     if (0 != req_body_size)
    702     {
    703       int prn_size;
    704       prn_size = snprintf (send_buf + clnt->req_size,
    705                            (buf_alloc_size - clnt->req_size),
    706                            "%x", (unsigned int) req_body_size);
    707       if (0 >= prn_size)
    708         externalErrorExit ();
    709       if ((unsigned int) prn_size >= buf_alloc_size - clnt->req_size)
    710         externalErrorExit ();
    711       clnt->req_size += (unsigned int) prn_size;
    712       send_buf[clnt->req_size++] = '\r';
    713       send_buf[clnt->req_size++] = '\n';
    714       memcpy (send_buf + clnt->req_size, req_body, req_body_size);
    715       clnt->req_size += req_body_size;
    716       send_buf[clnt->req_size++] = '\r';
    717       send_buf[clnt->req_size++] = '\n';
    718     }
    719     send_buf[clnt->req_size++] = '0';
    720     send_buf[clnt->req_size++] = '\r';
    721     send_buf[clnt->req_size++] = '\n';
    722     send_buf[clnt->req_size++] = '\r';
    723     send_buf[clnt->req_size++] = '\n';
    724   }
    725   mhd_assert (clnt->req_size < buf_alloc_size);
    726   clnt->buf = send_buf;
    727   clnt->send_buf = send_buf;
    728 
    729   return clnt;
    730 }
    731 
    732 
    733 void
    734 _MHD_dumbClient_set_send_limits (struct _MHD_dumbClient *clnt,
    735                                  size_t step_size, size_t max_total_send)
    736 {
    737   clnt->single_send_size = step_size;
    738   clnt->send_size_limit = max_total_send;
    739 }
    740 
    741 
    742 /* internal */
    743 static void
    744 _MHD_dumbClient_connect_init (struct _MHD_dumbClient *clnt)
    745 {
    746   struct sockaddr_in sa;
    747   mhd_assert (DUMB_CLIENT_INIT == clnt->stage);
    748 
    749   sa.sin_family = AF_INET;
    750   sa.sin_port = htons ((uint16_t) clnt->port);
    751   sa.sin_addr.s_addr = htonl (INADDR_LOOPBACK);
    752 
    753   if (0 != connect (clnt->sckt, (struct sockaddr *) &sa, sizeof(sa)))
    754   {
    755     const int err = MHD_socket_get_error_ ();
    756     if ( (MHD_SCKT_ERR_IS_ (err, MHD_SCKT_EINPROGRESS_)) ||
    757          (MHD_SCKT_ERR_IS_EAGAIN_ (err)))
    758       clnt->stage = DUMB_CLIENT_CONNECTING;
    759     else
    760       externalErrorExitDesc ("Cannot 'connect()' the client socket");
    761   }
    762   else
    763     clnt->stage = DUMB_CLIENT_CONNECTED;
    764 }
    765 
    766 
    767 void
    768 _MHD_dumbClient_start_connect (struct _MHD_dumbClient *clnt)
    769 {
    770   mhd_assert (DUMB_CLIENT_INIT == clnt->stage);
    771   _MHD_dumbClient_connect_init (clnt);
    772 }
    773 
    774 
    775 /* internal */
    776 static void
    777 _MHD_dumbClient_connect_finish (struct _MHD_dumbClient *clnt)
    778 {
    779   int err = 0;
    780   socklen_t err_size = sizeof(err);
    781   mhd_assert (DUMB_CLIENT_CONNECTING == clnt->stage);
    782   if (0 != getsockopt (clnt->sckt, SOL_SOCKET, SO_ERROR,
    783                        (void *) &err, &err_size))
    784     externalErrorExitDesc ("'getsockopt()' call failed");
    785   if (0 != err)
    786     externalErrorExitDesc ("Socket connect() failed");
    787   clnt->stage = DUMB_CLIENT_CONNECTED;
    788 }
    789 
    790 
    791 /* internal */
    792 static void
    793 _MHD_dumbClient_send_req (struct _MHD_dumbClient *clnt)
    794 {
    795   size_t send_size;
    796   ssize_t res;
    797   mhd_assert (DUMB_CLIENT_CONNECTED <= clnt->stage);
    798   mhd_assert (DUMB_CLIENT_REQ_SENT > clnt->stage);
    799   mhd_assert (clnt->req_size > clnt->send_off);
    800 
    801   send_size = (((0 != clnt->send_size_limit) &&
    802                 (clnt->req_size > clnt->send_size_limit)) ?
    803                clnt->send_size_limit : clnt->req_size) - clnt->send_off;
    804   mhd_assert (0 != send_size);
    805   if ((0 != clnt->single_send_size) &&
    806       (clnt->single_send_size < send_size))
    807     send_size = clnt->single_send_size;
    808 
    809   res = MHD_send_ (clnt->sckt, clnt->send_buf + clnt->send_off, send_size);
    810 
    811   if (res < 0)
    812   {
    813     const int err = MHD_socket_get_error_ ();
    814     if (MHD_SCKT_ERR_IS_EAGAIN_ (err))
    815       return;
    816     if (MHD_SCKT_ERR_IS_EINTR_ (err))
    817       return;
    818     if (MHD_SCKT_ERR_IS_REMOTE_DISCNN_ (err))
    819       mhdErrorExitDesc ("The connection was aborted by MHD");
    820     if (MHD_SCKT_ERR_IS_ (err, MHD_SCKT_EPIPE_))
    821       mhdErrorExitDesc ("The connection was shut down on MHD side");
    822     externalErrorExitDesc ("Unexpected network error");
    823   }
    824   clnt->send_off += (size_t) res;
    825   mhd_assert (clnt->send_off <= clnt->req_size);
    826   mhd_assert (clnt->send_off <= clnt->send_size_limit || \
    827               0 == clnt->send_size_limit);
    828   if (clnt->req_size == clnt->send_off)
    829     clnt->stage = DUMB_CLIENT_REQ_SENT;
    830   if ((0 != clnt->send_size_limit) &&
    831       (clnt->send_size_limit == clnt->send_off))
    832     clnt->stage = DUMB_CLIENT_FINISHING;
    833 }
    834 
    835 
    836 /* internal */
    837 _MHD_NORETURN /* Declared as 'noreturn' until it is implemented */
    838 static void
    839 _MHD_dumbClient_recv_reply (struct _MHD_dumbClient *clnt)
    840 {
    841   (void) clnt;
    842   externalErrorExitDesc ("Not implemented for this test");
    843 }
    844 
    845 
    846 int
    847 _MHD_dumbClient_is_req_sent (struct _MHD_dumbClient *clnt)
    848 {
    849   return DUMB_CLIENT_REQ_SENT <= clnt->stage;
    850 }
    851 
    852 
    853 /* internal */
    854 static void
    855 _MHD_dumbClient_socket_close (struct _MHD_dumbClient *clnt)
    856 {
    857   if (MHD_INVALID_SOCKET != clnt->sckt)
    858   {
    859     if (use_hard_close)
    860     {
    861 #ifdef SO_LINGER
    862       static const struct linger hard_close = {1, 0};
    863       mhd_assert (0 == hard_close.l_linger);
    864       if (0 != setsockopt (clnt->sckt, SOL_SOCKET, SO_LINGER,
    865                            (const void *) &hard_close, sizeof (hard_close)))
    866 #endif /* SO_LINGER */
    867       externalErrorExitDesc ("Failed to set SO_LINGER option");
    868     }
    869     if (! MHD_socket_close_ (clnt->sckt))
    870       externalErrorExitDesc ("Unexpected error while closing " \
    871                              "the client socket");
    872     clnt->sckt = MHD_INVALID_SOCKET;
    873   }
    874 }
    875 
    876 
    877 /* internal */
    878 static void
    879 _MHD_dumbClient_finalize (struct _MHD_dumbClient *clnt)
    880 {
    881   if (MHD_INVALID_SOCKET != clnt->sckt)
    882   {
    883     if (use_shutdown)
    884     {
    885       if (0 != shutdown (clnt->sckt, SHUT_WR))
    886       {
    887         const int err = MHD_socket_get_error_ ();
    888         if (! MHD_SCKT_ERR_IS_ (err, MHD_SCKT_ENOTCONN_) &&
    889             ! MHD_SCKT_ERR_IS_REMOTE_DISCNN_ (err))
    890           mhdErrorExitDesc ("Unexpected error when shutting down " \
    891                             "the client socket");
    892       }
    893     }
    894     else if (use_close)
    895     {
    896       _MHD_dumbClient_socket_close (clnt);
    897     }
    898     else
    899       mhd_assert (0);
    900   }
    901   clnt->stage = DUMB_CLIENT_FINISHED;
    902 }
    903 
    904 
    905 /* internal */
    906 static int
    907 _MHD_dumbClient_needs_send (const struct _MHD_dumbClient *clnt)
    908 {
    909   return ((DUMB_CLIENT_CONNECTING <= clnt->stage) &&
    910           (DUMB_CLIENT_REQ_SENT > clnt->stage)) ||
    911          (DUMB_CLIENT_FINISHING == clnt->stage);
    912 }
    913 
    914 
    915 /* internal */
    916 static int
    917 _MHD_dumbClient_needs_recv (const struct _MHD_dumbClient *clnt)
    918 {
    919   return (DUMB_CLIENT_HEADER_RECVEIVING <= clnt->stage) &&
    920          (DUMB_CLIENT_BODY_RECVEIVED > clnt->stage);
    921 }
    922 
    923 
    924 /* internal */
    925 /**
    926  * Check whether the client needs unconditionally process the data.
    927  * @param clnt the client to check
    928  * @return non-zero if client needs unconditionally process the data,
    929  *         zero otherwise.
    930  */
    931 static int
    932 _MHD_dumbClient_needs_process (const struct _MHD_dumbClient *clnt)
    933 {
    934   switch (clnt->stage)
    935   {
    936   case DUMB_CLIENT_INIT:
    937   case DUMB_CLIENT_REQ_SENT:
    938   case DUMB_CLIENT_HEADER_RECVEIVED:
    939   case DUMB_CLIENT_BODY_RECVEIVED:
    940   case DUMB_CLIENT_FINISHED:
    941     return ! 0;
    942   case DUMB_CLIENT_CONNECTING:
    943   case DUMB_CLIENT_CONNECTED:
    944   case DUMB_CLIENT_REQ_SENDING:
    945   case DUMB_CLIENT_HEADER_RECVEIVING:
    946   case DUMB_CLIENT_BODY_RECVEIVING:
    947   case DUMB_CLIENT_FINISHING:
    948   default:
    949     break;
    950   }
    951   return 0;
    952 }
    953 
    954 
    955 /**
    956  * Process the client data with send()/recv() as needed.
    957  * @param clnt the client to process
    958  * @return non-zero if client finished processing the request,
    959  *         zero otherwise.
    960  */
    961 int
    962 _MHD_dumbClient_process (struct _MHD_dumbClient *clnt)
    963 {
    964   do
    965   {
    966     switch (clnt->stage)
    967     {
    968     case DUMB_CLIENT_INIT:
    969       _MHD_dumbClient_connect_init (clnt);
    970       break;
    971     case DUMB_CLIENT_CONNECTING:
    972       _MHD_dumbClient_connect_finish (clnt);
    973       break;
    974     case DUMB_CLIENT_CONNECTED:
    975     case DUMB_CLIENT_REQ_SENDING:
    976       _MHD_dumbClient_send_req (clnt);
    977       break;
    978     case DUMB_CLIENT_REQ_SENT:
    979       mhd_assert (0);
    980       clnt->stage = DUMB_CLIENT_HEADER_RECVEIVING;
    981       break;
    982     case DUMB_CLIENT_HEADER_RECVEIVING:
    983       _MHD_dumbClient_recv_reply (clnt);
    984       break;
    985     case DUMB_CLIENT_HEADER_RECVEIVED:
    986       clnt->stage = DUMB_CLIENT_BODY_RECVEIVING;
    987       break;
    988     case DUMB_CLIENT_BODY_RECVEIVING:
    989       _MHD_dumbClient_recv_reply (clnt);
    990       break;
    991     case DUMB_CLIENT_BODY_RECVEIVED:
    992       clnt->stage = DUMB_CLIENT_FINISHING;
    993       break;
    994     case DUMB_CLIENT_FINISHING:
    995       _MHD_dumbClient_finalize (clnt);
    996       break;
    997     case DUMB_CLIENT_FINISHED:
    998       return ! 0;
    999     default:
   1000       mhd_assert (0);
   1001       mhdErrorExit ();
   1002     }
   1003   } while (_MHD_dumbClient_needs_process (clnt));
   1004   return DUMB_CLIENT_FINISHED == clnt->stage;
   1005 }
   1006 
   1007 
   1008 void
   1009 _MHD_dumbClient_get_fdsets (struct _MHD_dumbClient *clnt,
   1010                             MHD_socket *maxsckt,
   1011                             fd_set *rs, fd_set *ws, fd_set *es)
   1012 {
   1013   mhd_assert (NULL != rs);
   1014   mhd_assert (NULL != ws);
   1015   mhd_assert (NULL != es);
   1016   if (DUMB_CLIENT_FINISHED > clnt->stage)
   1017   {
   1018     if (MHD_INVALID_SOCKET != clnt->sckt)
   1019     {
   1020       if ( (MHD_INVALID_SOCKET == *maxsckt) ||
   1021            (clnt->sckt > *maxsckt) )
   1022         *maxsckt = clnt->sckt;
   1023       if (_MHD_dumbClient_needs_recv (clnt))
   1024         FD_SET (clnt->sckt, rs);
   1025       if (_MHD_dumbClient_needs_send (clnt))
   1026         FD_SET (clnt->sckt, ws);
   1027       FD_SET (clnt->sckt, es);
   1028     }
   1029   }
   1030 }
   1031 
   1032 
   1033 /**
   1034  * Process the client data with send()/recv() as needed based on
   1035  * information in fd_sets.
   1036  * @param clnt the client to process
   1037  * @return non-zero if client finished processing the request,
   1038  *         zero otherwise.
   1039  */
   1040 int
   1041 _MHD_dumbClient_process_from_fdsets (struct _MHD_dumbClient *clnt,
   1042                                      fd_set *rs, fd_set *ws, fd_set *es)
   1043 {
   1044   if (_MHD_dumbClient_needs_process (clnt))
   1045     return _MHD_dumbClient_process (clnt);
   1046   else if (MHD_INVALID_SOCKET != clnt->sckt)
   1047   {
   1048     if (_MHD_dumbClient_needs_recv (clnt) && FD_ISSET (clnt->sckt, rs))
   1049       return _MHD_dumbClient_process (clnt);
   1050     else if (_MHD_dumbClient_needs_send (clnt) && FD_ISSET (clnt->sckt, ws))
   1051       return _MHD_dumbClient_process (clnt);
   1052     else if (FD_ISSET (clnt->sckt, es))
   1053       return _MHD_dumbClient_process (clnt);
   1054   }
   1055   return DUMB_CLIENT_FINISHED == clnt->stage;
   1056 }
   1057 
   1058 
   1059 /**
   1060  * Perform full request.
   1061  * @param clnt the client to run
   1062  * @return zero if client finished processing the request,
   1063  *         non-zero if timeout is reached.
   1064  */
   1065 int
   1066 _MHD_dumbClient_perform (struct _MHD_dumbClient *clnt)
   1067 {
   1068   time_t start;
   1069   time_t now;
   1070   start = time (NULL);
   1071   now = start;
   1072   do
   1073   {
   1074     fd_set rs;
   1075     fd_set ws;
   1076     fd_set es;
   1077     MHD_socket maxMhdSk;
   1078     struct timeval tv;
   1079 
   1080     FD_ZERO (&rs);
   1081     FD_ZERO (&ws);
   1082     FD_ZERO (&es);
   1083 
   1084     if (! _MHD_dumbClient_needs_process (clnt))
   1085     {
   1086       maxMhdSk = MHD_INVALID_SOCKET;
   1087       _MHD_dumbClient_get_fdsets (clnt, &maxMhdSk, &rs, &ws, &es);
   1088       mhd_assert (now >= start);
   1089 #if ! defined(_WIN32) || defined(__CYGWIN__)
   1090       tv.tv_sec = (time_t) (TIMEOUTS_VAL * 2 - (now - start) + 1);
   1091 #else  /* Native W32 */
   1092       tv.tv_sec = (long) (TIMEOUTS_VAL * 2 - (now - start) + 1);
   1093 #endif /* Native W32 */
   1094       tv.tv_usec = 250 * 1000;
   1095       if (-1 == select ((int) maxMhdSk + 1, &rs, &ws, &es, &tv))
   1096       {
   1097 #ifdef MHD_POSIX_SOCKETS
   1098         if (EINTR != errno)
   1099           externalErrorExitDesc ("Unexpected select() error");
   1100 #else  /* ! MHD_POSIX_SOCKETS */
   1101         mhd_assert ((0 != rs.fd_count) || (0 != ws.fd_count) || \
   1102                     (0 != es.fd_count));
   1103         externalErrorExitDesc ("Unexpected select() error");
   1104         Sleep ((DWORD) (tv.tv_sec * 1000 + tv.tv_usec / 1000));
   1105 #endif /* ! MHD_POSIX_SOCKETS */
   1106         continue;
   1107       }
   1108       if (_MHD_dumbClient_process_from_fdsets (clnt, &rs, &ws, &es))
   1109         return 0;
   1110     }
   1111     /* Use double timeout value here as MHD must catch timeout situations
   1112      * in this test. Timeout in client as a last resort. */
   1113   } while ((now = time (NULL)) - start <= (TIMEOUTS_VAL * 2));
   1114   return 1;
   1115 }
   1116 
   1117 
   1118 /**
   1119  * Close the client and free internally allocated resources.
   1120  * @param clnt the client to close
   1121  */
   1122 void
   1123 _MHD_dumbClient_close (struct _MHD_dumbClient *clnt)
   1124 {
   1125   if (DUMB_CLIENT_FINISHED != clnt->stage)
   1126     _MHD_dumbClient_finalize (clnt);
   1127   _MHD_dumbClient_socket_close (clnt);
   1128   if (NULL != clnt->send_buf)
   1129   {
   1130     mhd_assert (clnt->send_buf == clnt->buf);
   1131     free (clnt->buf);
   1132     clnt->buf = NULL;
   1133     clnt->send_buf = NULL;
   1134   }
   1135   free (clnt);
   1136 }
   1137 
   1138 
   1139 struct sckt_notif_cb_param
   1140 {
   1141   volatile unsigned int num_started;
   1142   volatile unsigned int num_finished;
   1143 };
   1144 
   1145 static void
   1146 socket_cb (void *cls,
   1147            struct MHD_Connection *c,
   1148            void **socket_context,
   1149            enum MHD_ConnectionNotificationCode toe)
   1150 {
   1151   struct sckt_notif_cb_param *param = (struct sckt_notif_cb_param *) cls;
   1152   if (NULL == socket_context)
   1153     mhdErrorExitDesc ("'socket_context' pointer is NULL");
   1154   if (NULL == c)
   1155     mhdErrorExitDesc ("'connection' pointer is NULL");
   1156   if (NULL == param)
   1157     mhdErrorExitDesc ("'cls' pointer is NULL");
   1158 
   1159   if (MHD_CONNECTION_NOTIFY_STARTED == toe)
   1160     param->num_started++;
   1161   else if (MHD_CONNECTION_NOTIFY_CLOSED == toe)
   1162     param->num_finished++;
   1163   else
   1164     mhdErrorExitDesc ("Unknown 'toe' value");
   1165 }
   1166 
   1167 
   1168 struct term_notif_cb_param
   1169 {
   1170   volatile int term_reason;
   1171   volatile unsigned int num_called;
   1172 };
   1173 
   1174 
   1175 static void
   1176 term_cb (void *cls,
   1177          struct MHD_Connection *c,
   1178          void **req_cls,
   1179          enum MHD_RequestTerminationCode term_code)
   1180 {
   1181   struct term_notif_cb_param *param = (struct term_notif_cb_param *) cls;
   1182   if (NULL == req_cls)
   1183     mhdErrorExitDesc ("'req_cls' pointer is NULL");
   1184   if (NULL == c)
   1185     mhdErrorExitDesc ("'connection' pointer is NULL");
   1186   if (NULL == param)
   1187     mhdErrorExitDesc ("'cls' pointer is NULL");
   1188   param->term_reason = (int) term_code;
   1189   param->num_called++;
   1190 }
   1191 
   1192 
   1193 static const char *
   1194 term_reason_str (enum MHD_RequestTerminationCode term_code)
   1195 {
   1196   switch ((int) term_code)
   1197   {
   1198   case MHD_REQUEST_TERMINATED_COMPLETED_OK:
   1199     return "COMPLETED_OK";
   1200   case MHD_REQUEST_TERMINATED_WITH_ERROR:
   1201     return "TERMINATED_WITH_ERROR";
   1202   case MHD_REQUEST_TERMINATED_TIMEOUT_REACHED:
   1203     return "TIMEOUT_REACHED";
   1204   case MHD_REQUEST_TERMINATED_DAEMON_SHUTDOWN:
   1205     return "DAEMON_SHUTDOWN";
   1206   case MHD_REQUEST_TERMINATED_READ_ERROR:
   1207     return "READ_ERROR";
   1208   case MHD_REQUEST_TERMINATED_CLIENT_ABORT:
   1209     return "CLIENT_ABORT";
   1210   case -1:
   1211     return "(not called)";
   1212   default:
   1213     break;
   1214   }
   1215   return "(unknown code)";
   1216 }
   1217 
   1218 
   1219 struct check_uri_cls
   1220 {
   1221   const char *volatile uri;
   1222   volatile unsigned int cb_called;
   1223 };
   1224 
   1225 static void *
   1226 check_uri_cb (void *cls,
   1227               const char *uri,
   1228               struct MHD_Connection *con)
   1229 {
   1230   struct check_uri_cls *param = (struct check_uri_cls *) cls;
   1231 
   1232   if (NULL == con)
   1233     mhdErrorExitDesc ("The 'con' pointer is NULL");
   1234 
   1235   param->cb_called++;
   1236 
   1237   if (0 != strcmp (param->uri,
   1238                    uri))
   1239   {
   1240     fprintf (stderr, "Wrong URI: '%s'\n", uri);
   1241     mhdErrorExit ();
   1242   }
   1243   return NULL;
   1244 }
   1245 
   1246 
   1247 struct mhd_header_checker_param
   1248 {
   1249   int found_header_host; /**< the number of 'Host' headers */
   1250   int found_header_ua;   /**< the number of 'User-Agent' headers */
   1251   int found_header_ct;   /**< the number of 'Content-Type' headers */
   1252   int found_header_cl;   /**< the number of 'Content-Length' headers */
   1253   int found_header_te;   /**< the number of 'Transfer-Encoding' headers */
   1254 };
   1255 
   1256 static enum MHD_Result
   1257 headerCheckerInterator (void *cls,
   1258                         enum MHD_ValueKind kind,
   1259                         const char *key,
   1260                         size_t key_size,
   1261                         const char *value,
   1262                         size_t value_size)
   1263 {
   1264   struct mhd_header_checker_param *const param =
   1265     (struct mhd_header_checker_param *) cls;
   1266 
   1267   if (NULL == param)
   1268     mhdErrorExitDesc ("cls parameter is NULL");
   1269 
   1270   if (MHD_HEADER_KIND != kind)
   1271     return MHD_YES; /* Continue iteration */
   1272 
   1273   if (0 == key_size)
   1274     mhdErrorExitDesc ("Zero key length");
   1275 
   1276   if ((strlen (REQ_HEADER_HOST_NAME) == key_size) &&
   1277       (0 == memcmp (key, REQ_HEADER_HOST_NAME, key_size)))
   1278   {
   1279     if ((strlen (REQ_HEADER_HOST_VALUE) == value_size) &&
   1280         (0 == memcmp (value, REQ_HEADER_HOST_VALUE, value_size)))
   1281       param->found_header_host++;
   1282     else
   1283       fprintf (stderr, "Unexpected header value: '%.*s', expected: '%s'\n",
   1284                (int) value_size, value, REQ_HEADER_HOST_VALUE);
   1285   }
   1286   else if ((strlen (REQ_HEADER_UA_NAME) == key_size) &&
   1287            (0 == memcmp (key, REQ_HEADER_UA_NAME, key_size)))
   1288   {
   1289     if ((strlen (REQ_HEADER_UA_VALUE) == value_size) &&
   1290         (0 == memcmp (value, REQ_HEADER_UA_VALUE, value_size)))
   1291       param->found_header_ua++;
   1292     else
   1293       fprintf (stderr, "Unexpected header value: '%.*s', expected: '%s'\n",
   1294                (int) value_size, value, REQ_HEADER_UA_VALUE);
   1295   }
   1296   else if ((strlen (REQ_HEADER_CT_NAME) == key_size) &&
   1297            (0 == memcmp (key, REQ_HEADER_CT_NAME, key_size)))
   1298   {
   1299     if ((strlen (REQ_HEADER_CT_VALUE) == value_size) &&
   1300         (0 == memcmp (value, REQ_HEADER_CT_VALUE, value_size)))
   1301       param->found_header_ct++;
   1302     else
   1303       fprintf (stderr, "Unexpected header value: '%.*s', expected: '%s'\n",
   1304                (int) value_size, value, REQ_HEADER_CT_VALUE);
   1305   }
   1306   else if ((strlen (MHD_HTTP_HEADER_CONTENT_LENGTH) == key_size) &&
   1307            (0 == memcmp (key, MHD_HTTP_HEADER_CONTENT_LENGTH, key_size)))
   1308   {
   1309     /* do not check value of the header here for simplicity */
   1310     param->found_header_cl++;
   1311   }
   1312   else if ((strlen (MHD_HTTP_HEADER_TRANSFER_ENCODING) == key_size) &&
   1313            (0 == memcmp (key, MHD_HTTP_HEADER_TRANSFER_ENCODING, key_size)))
   1314   {
   1315     if ((strlen ("chunked") == value_size) &&
   1316         (0 == memcmp (value, "chunked", value_size)))
   1317       param->found_header_te++;
   1318     else
   1319       fprintf (stderr, "Unexpected header value: '%.*s', expected: '%s'\n",
   1320                (int) value_size, value, "chunked");
   1321   }
   1322   return MHD_YES;
   1323 }
   1324 
   1325 
   1326 struct ahc_cls_type
   1327 {
   1328   const char *volatile rp_data;
   1329   volatile size_t rp_data_size;
   1330   const char *volatile rq_method;
   1331   const char *volatile rq_url;
   1332   const char *volatile req_body;
   1333   volatile unsigned int cb_called; /* Non-zero indicates that callback was called at least one time */
   1334   size_t req_body_size; /**< The number of bytes in @a req_body */
   1335   size_t req_body_uploaded; /* Updated by callback */
   1336 };
   1337 
   1338 
   1339 static enum MHD_Result
   1340 ahcCheck (void *cls,
   1341           struct MHD_Connection *connection,
   1342           const char *url,
   1343           const char *method,
   1344           const char *version,
   1345           const char *upload_data, size_t *upload_data_size,
   1346           void **req_cls)
   1347 {
   1348   static int marker;
   1349   enum MHD_Result ret;
   1350   struct mhd_header_checker_param header_check_param;
   1351   struct ahc_cls_type *const param = (struct ahc_cls_type *) cls;
   1352 
   1353   if (NULL == param)
   1354     mhdErrorExitDesc ("cls parameter is NULL");
   1355   param->cb_called++;
   1356 
   1357   if (0 != strcmp (version, MHD_HTTP_VERSION_1_1))
   1358     mhdErrorExitDesc ("Unexpected HTTP version");
   1359 
   1360   if (0 != strcmp (url, param->rq_url))
   1361     mhdErrorExitDesc ("Unexpected URI");
   1362 
   1363   if (0 != strcmp (param->rq_method, method))
   1364     mhdErrorExitDesc ("Unexpected request method");
   1365 
   1366   if (NULL == upload_data_size)
   1367     mhdErrorExitDesc ("'upload_data_size' pointer is NULL");
   1368 
   1369   if (0 != *upload_data_size)
   1370   {
   1371     const char *const upload_body = param->req_body;
   1372     if (NULL == upload_data)
   1373       mhdErrorExitDesc ("'upload_data' is NULL while " \
   1374                         "'*upload_data_size' value is not zero");
   1375     if (NULL == upload_body)
   1376       mhdErrorExitDesc ("'*upload_data_size' value is not zero " \
   1377                         "while no request body is expected");
   1378     if (param->req_body_uploaded + *upload_data_size > param->req_body_size)
   1379     {
   1380       fprintf (stderr, "Too large upload body received. Got %u, expected %u",
   1381                (unsigned int) (param->req_body_uploaded + *upload_data_size),
   1382                (unsigned int) param->req_body_size);
   1383       mhdErrorExit ();
   1384     }
   1385     if (0 != memcmp (upload_data, upload_body + param->req_body_uploaded,
   1386                      *upload_data_size))
   1387     {
   1388       fprintf (stderr, "Unexpected request body at offset %u: " \
   1389                "'%.*s', expected: '%.*s'\n",
   1390                (unsigned int) param->req_body_uploaded,
   1391                (int) *upload_data_size, upload_data,
   1392                (int) *upload_data_size, upload_body + param->req_body_uploaded);
   1393       mhdErrorExit ();
   1394     }
   1395     param->req_body_uploaded += *upload_data_size;
   1396     *upload_data_size = 0;
   1397   }
   1398 
   1399   if (&marker != *req_cls)
   1400   {
   1401     /* The first call of the callback for this connection */
   1402     mhd_assert (NULL == upload_data);
   1403     param->req_body_uploaded = 0;
   1404 
   1405     *req_cls = &marker;
   1406     return MHD_YES;
   1407   }
   1408 
   1409   memset (&header_check_param, 0, sizeof(header_check_param));
   1410   if (1 > MHD_get_connection_values_n (connection, MHD_HEADER_KIND,
   1411                                        &headerCheckerInterator,
   1412                                        &header_check_param))
   1413     mhdErrorExitDesc ("Wrong number of headers in the request");
   1414   if (1 != header_check_param.found_header_host)
   1415     mhdErrorExitDesc ("'Host' header has not been detected in request");
   1416   if (1 != header_check_param.found_header_ua)
   1417     mhdErrorExitDesc ("'User-Agent' header has not been detected in request");
   1418   if (1 != header_check_param.found_header_ct)
   1419     mhdErrorExitDesc ("'Content-Type' header has not been detected in request");
   1420   if (! upl_chunked && (1 != header_check_param.found_header_cl))
   1421     mhdErrorExitDesc ("'Content-Length' header has not been detected "
   1422                       "in request");
   1423   if (upl_chunked && (1 != header_check_param.found_header_te))
   1424     mhdErrorExitDesc ("'Transfer-Encoding' header has not been detected "
   1425                       "in request");
   1426 
   1427   if (NULL != upload_data)
   1428     return MHD_YES; /* Full request has not been received so far */
   1429 
   1430 #if 0 /* Code unused in this test */
   1431   struct MHD_Response *response;
   1432   response = MHD_create_response_from_buffer (param->rp_data_size,
   1433                                               (void *) param->rp_data,
   1434                                               MHD_RESPMEM_MUST_COPY);
   1435   if (NULL == response)
   1436     mhdErrorExitDesc ("Failed to create response");
   1437 
   1438   ret = MHD_queue_response (connection,
   1439                             MHD_HTTP_OK,
   1440                             response);
   1441   MHD_destroy_response (response);
   1442   if (MHD_YES != ret)
   1443     mhdErrorExitDesc ("Failed to queue response");
   1444 #else
   1445   if (NULL == upload_data)
   1446     mhdErrorExitDesc ("Full request received, " \
   1447                       "while incomplete request expected");
   1448   ret = MHD_NO;
   1449 #endif
   1450 
   1451   return ret;
   1452 }
   1453 
   1454 
   1455 struct simpleQueryParams
   1456 {
   1457   /* Destination path for HTTP query */
   1458   const char *queryPath;
   1459 
   1460   /* Custom query method, NULL for default */
   1461   const char *method;
   1462 
   1463   /* Destination port for HTTP query */
   1464   uint16_t queryPort;
   1465 
   1466   /* Additional request headers, static */
   1467   const char *headers;
   1468 
   1469   /* NULL for request without body */
   1470   const uint8_t *req_body;
   1471   size_t req_body_size;
   1472 
   1473   /* Non-zero to use chunked encoding for request body */
   1474   int chunked;
   1475 
   1476   /* Max size of data for single 'send()' call */
   1477   size_t step_size;
   1478 
   1479   /* Limit for total amount of sent data */
   1480   size_t total_send_max;
   1481 
   1482   /* HTTP query result error flag */
   1483   volatile int queryError;
   1484 
   1485   /* Response HTTP code, zero if no response */
   1486   volatile int responseCode;
   1487 };
   1488 
   1489 
   1490 /* returns non-zero if timed-out */
   1491 static int
   1492 performQueryExternal (struct MHD_Daemon *d, struct _MHD_dumbClient *clnt)
   1493 {
   1494   time_t start;
   1495   struct timeval tv;
   1496   int ret;
   1497   const union MHD_DaemonInfo *di;
   1498   MHD_socket lstn_sk;
   1499   int client_accepted;
   1500   int full_req_recieved;
   1501   int full_req_sent;
   1502   int some_data_recieved;
   1503 
   1504   di = MHD_get_daemon_info (d, MHD_DAEMON_INFO_LISTEN_FD);
   1505   if (NULL == di)
   1506     mhdErrorExitDesc ("Cannot get lister socket");
   1507   lstn_sk = di->listen_fd;
   1508 
   1509   ret = 1; /* will be replaced with real result */
   1510   client_accepted = 0;
   1511 
   1512   _MHD_dumbClient_start_connect (clnt);
   1513 
   1514   full_req_recieved = 0;
   1515   some_data_recieved = 0;
   1516   start = time (NULL);
   1517   do
   1518   {
   1519     fd_set rs;
   1520     fd_set ws;
   1521     fd_set es;
   1522     MHD_socket maxMhdSk;
   1523     int num_ready;
   1524     int do_client; /**< Process data in client */
   1525 
   1526     maxMhdSk = MHD_INVALID_SOCKET;
   1527     FD_ZERO (&rs);
   1528     FD_ZERO (&ws);
   1529     FD_ZERO (&es);
   1530     if (NULL == clnt)
   1531     {
   1532       /* client has finished, check whether MHD is still
   1533        * processing any connections */
   1534       full_req_sent = 1;
   1535       do_client = 0;
   1536       if (client_accepted && (0 > MHD_get_timeout64s (d)))
   1537       {
   1538         ret = 0;
   1539         break; /* MHD finished as well */
   1540       }
   1541     }
   1542     else
   1543     {
   1544       full_req_sent = _MHD_dumbClient_is_req_sent (clnt);
   1545       if (! full_req_sent)
   1546         do_client = 1; /* Request hasn't been sent yet, send the data */
   1547       else
   1548       {
   1549         /* All request data has been sent.
   1550          * Client will close the socket as the next step. */
   1551         if (full_req_recieved)
   1552         {
   1553           /* All data has been received by the MHD */
   1554           do_client = 1; /* Close the client socket */
   1555         }
   1556         else if (some_data_recieved &&
   1557                  (! use_hard_close || ((0 == rate_limiter) && use_stress_os)))
   1558         {
   1559           /* No RST rate limiter or no "hard close", no need to avoid extra RST
   1560            * and at least something was received by the MHD */
   1561           /* In case of 'hard close' this can stress the OS, especially
   1562            * if 'by_step' is enabled as several ACKs (for delivered packets
   1563            * containing the request) from the server may arrive to the client
   1564            * when the client has closed port and may be reflected by several
   1565            * RSTs from the client side to the server side (when ACK received
   1566            * without active connection then RST packet should be sent).
   1567            * When listening socket receives RST packets, it may block
   1568            * the sender preventing the next connection. */
   1569           do_client = 1; /* Proceed with the closure of the client socket */
   1570         }
   1571         else
   1572         {
   1573           /* When rate limiter is enabled, all sent packets must be received
   1574            * before client closes connection to avoid RST for every ACK.
   1575            * When rate limiter is not enabled, the MHD must receive at
   1576            * least something before closing the connection. */
   1577           do_client = 0; /* Do not close the client socket yet */
   1578         }
   1579       }
   1580 
   1581       if (do_client)
   1582         _MHD_dumbClient_get_fdsets (clnt, &maxMhdSk, &rs, &ws, &es);
   1583     }
   1584     if (MHD_YES != MHD_get_fdset (d, &rs, &ws, &es, &maxMhdSk))
   1585       mhdErrorExitDesc ("MHD_get_fdset() failed");
   1586     if (do_client)
   1587     {
   1588       tv.tv_sec = 1;
   1589       tv.tv_usec = 250 * 1000;
   1590     }
   1591     else
   1592     { /* Request completely sent but not yet fully received */
   1593       tv.tv_sec = 0;
   1594       tv.tv_usec = FINAL_PACKETS_MS * 1000;
   1595     }
   1596     num_ready = select ((int) maxMhdSk + 1, &rs, &ws, &es, &tv);
   1597     if (-1 == num_ready)
   1598     {
   1599 #ifdef MHD_POSIX_SOCKETS
   1600       if (EINTR != errno)
   1601         externalErrorExitDesc ("Unexpected select() error");
   1602 #else
   1603       if ((WSAEINVAL != WSAGetLastError ()) ||
   1604           (0 != rs.fd_count) || (0 != ws.fd_count) || (0 != es.fd_count) )
   1605         externalErrorExitDesc ("Unexpected select() error");
   1606       Sleep ((DWORD) (tv.tv_sec * 1000 + tv.tv_usec / 1000));
   1607 #endif
   1608       continue;
   1609     }
   1610     if (0 == num_ready)
   1611     { /* select() finished by timeout, looks like no more packets are pending */
   1612       if (do_client)
   1613         externalErrorExitDesc ("Timeout waiting for sockets");
   1614       if (full_req_sent && (! full_req_recieved))
   1615         full_req_recieved = 1;
   1616     }
   1617     if (MHD_YES != MHD_run_from_select (d, &rs, &ws, &es))
   1618       mhdErrorExitDesc ("MHD_run_from_select() failed");
   1619     if (! client_accepted)
   1620       client_accepted = FD_ISSET (lstn_sk, &rs);
   1621     else
   1622     { /* Client connection was already accepted by MHD */
   1623       if (! some_data_recieved)
   1624       {
   1625         if (! do_client)
   1626         {
   1627           if (0 != num_ready)
   1628           { /* Connection was accepted before, "ready" socket means data */
   1629             some_data_recieved = 1;
   1630           }
   1631         }
   1632         else
   1633         {
   1634           if (2 == num_ready)
   1635             some_data_recieved = 1;
   1636           else if ((1 == num_ready) &&
   1637                    ((MHD_INVALID_SOCKET == clnt->sckt) ||
   1638                     ! FD_ISSET (clnt->sckt, &ws)))
   1639             some_data_recieved = 1;
   1640         }
   1641       }
   1642     }
   1643     if (do_client)
   1644     {
   1645       if (_MHD_dumbClient_process_from_fdsets (clnt, &rs, &ws, &es))
   1646         clnt = NULL;
   1647     }
   1648     /* Use double timeout value here so MHD would be able to catch timeout
   1649      * internally */
   1650   } while (time (NULL) - start <= (TIMEOUTS_VAL * 2));
   1651 
   1652   return ret;
   1653 }
   1654 
   1655 
   1656 /* Returns zero for successful response and non-zero for failed response */
   1657 static int
   1658 doClientQueryInThread (struct MHD_Daemon *d,
   1659                        struct simpleQueryParams *p)
   1660 {
   1661   const union MHD_DaemonInfo *dinfo;
   1662   struct _MHD_dumbClient *c;
   1663   int errornum;
   1664   int use_external_poll;
   1665 
   1666   dinfo = MHD_get_daemon_info (d, MHD_DAEMON_INFO_FLAGS);
   1667   if (NULL == dinfo)
   1668     mhdErrorExitDesc ("MHD_get_daemon_info() failed");
   1669   use_external_poll = (0 == (dinfo->flags
   1670                              & MHD_USE_INTERNAL_POLLING_THREAD));
   1671 
   1672   if (0 == p->queryPort)
   1673     externalErrorExit ();
   1674 
   1675   c = _MHD_dumbClient_create (p->queryPort, p->method, p->queryPath,
   1676                               p->headers, p->req_body, p->req_body_size,
   1677                               p->chunked);
   1678   _MHD_dumbClient_set_send_limits (c, p->step_size, p->total_send_max);
   1679 
   1680   /* 'internal' polling should not be used in this test */
   1681   mhd_assert (use_external_poll);
   1682   if (! use_external_poll)
   1683     errornum = _MHD_dumbClient_perform (c);
   1684   else
   1685     errornum = performQueryExternal (d, c);
   1686 
   1687   if (errornum)
   1688     fprintf (stderr, "Request timeout out.\n");
   1689 
   1690   _MHD_dumbClient_close (c);
   1691 
   1692   return errornum;
   1693 }
   1694 
   1695 
   1696 static void
   1697 printTestResults (FILE *stream,
   1698                   struct simpleQueryParams *qParam,
   1699                   struct ahc_cls_type *ahc_param,
   1700                   struct check_uri_cls *uri_cb_param,
   1701                   struct term_notif_cb_param *term_result,
   1702                   struct sckt_notif_cb_param *sckt_result)
   1703 {
   1704   if (stderr != stream)
   1705     fflush (stderr);
   1706   fprintf (stream, " Request aborted at %u byte%s.",
   1707            (unsigned int) qParam->total_send_max,
   1708            1 == qParam->total_send_max ? "" : "s");
   1709   if ((1 == sckt_result->num_started) && (1 == sckt_result->num_finished))
   1710     fprintf (stream, " One socket has been accepted and then closed.");
   1711   else
   1712     fprintf (stream, " Sockets have been accepted %u time%s"
   1713              " and closed %u time%s.", sckt_result->num_started,
   1714              (1 == sckt_result->num_started) ? "" : "s",
   1715              sckt_result->num_finished,
   1716              (1 == sckt_result->num_finished) ? "" : "s");
   1717   if (0 == uri_cb_param->cb_called)
   1718     fprintf (stream, " URI callback has NOT been called.");
   1719   else
   1720     fprintf (stream, " URI callback has been called %u time%s.",
   1721              uri_cb_param->cb_called,
   1722              1 == uri_cb_param->cb_called ? "" : "s");
   1723   if (0 == ahc_param->cb_called)
   1724     fprintf (stream, " Access handler callback has NOT been called.");
   1725   else
   1726     fprintf (stream, " Access handler callback has been called %u time%s.",
   1727              ahc_param->cb_called,
   1728              1 == ahc_param->cb_called ? "" : "s");
   1729   if (0 == term_result->num_called)
   1730     fprintf (stream, " Final notification callback has NOT been called.");
   1731   else
   1732     fprintf (stream, " Final notification callback has been called %u time%s "
   1733              "with %s code.", term_result->num_called,
   1734              (1 == term_result->num_called) ? "" : "s",
   1735              term_reason_str ((enum MHD_RequestTerminationCode)
   1736                               term_result->term_reason));
   1737   fprintf (stream, "\n");
   1738   fflush (stream);
   1739 }
   1740 
   1741 
   1742 /* Perform test queries, shut down MHD daemon, and free parameters */
   1743 static unsigned int
   1744 performTestQueries (struct MHD_Daemon *d, uint16_t d_port,
   1745                     struct ahc_cls_type *ahc_param,
   1746                     struct check_uri_cls *uri_cb_param,
   1747                     struct term_notif_cb_param *term_result,
   1748                     struct sckt_notif_cb_param *sckt_result)
   1749 {
   1750   struct simpleQueryParams qParam;
   1751   time_t start;
   1752   unsigned int ret = 0;          /* Return value */
   1753   size_t req_total_size;
   1754   size_t limit_send_size;
   1755   size_t inc_size;
   1756   int expected_reason;
   1757   int found_right_reason;
   1758 
   1759   /* Common parameters, to be individually overridden by specific test cases
   1760    * if needed */
   1761   qParam.queryPort = d_port;
   1762   qParam.method = MHD_HTTP_METHOD_PUT;
   1763   qParam.queryPath = EXPECTED_URI_BASE_PATH;
   1764   qParam.headers = REQ_HEADER_CT;
   1765   qParam.req_body = (const uint8_t *) REQ_BODY;
   1766   qParam.req_body_size = MHD_STATICSTR_LEN_ (REQ_BODY);
   1767   qParam.chunked = upl_chunked;
   1768   qParam.step_size = by_step ? 1 : 0;
   1769 
   1770   uri_cb_param->uri = EXPECTED_URI_BASE_PATH;
   1771 
   1772   ahc_param->rq_url = EXPECTED_URI_BASE_PATH;
   1773   ahc_param->rq_method = MHD_HTTP_METHOD_PUT;
   1774   ahc_param->rp_data = "~";
   1775   ahc_param->rp_data_size = 1;
   1776   ahc_param->req_body = (const char *) qParam.req_body;
   1777   ahc_param->req_body_size = qParam.req_body_size;
   1778 
   1779   do
   1780   {
   1781     struct _MHD_dumbClient *test_c;
   1782     struct simpleQueryParams *p = &qParam;
   1783     test_c = _MHD_dumbClient_create (p->queryPort, p->method, p->queryPath,
   1784                                      p->headers, p->req_body, p->req_body_size,
   1785                                      p->chunked);
   1786     req_total_size = test_c->req_size;
   1787     _MHD_dumbClient_close (test_c);
   1788   } while (0);
   1789 
   1790   expected_reason = use_hard_close ?
   1791                     MHD_REQUEST_TERMINATED_READ_ERROR :
   1792                     MHD_REQUEST_TERMINATED_CLIENT_ABORT;
   1793   found_right_reason = 0;
   1794   if (0 != rate_limiter)
   1795   {
   1796     if (verbose)
   1797     {
   1798       printf ("Pausing for rate limiter...");
   1799       fflush (stdout);
   1800     }
   1801     _MHD_sleep (1150); /* Just a bit more than one second */
   1802     if (verbose)
   1803     {
   1804       printf (" OK\n");
   1805       fflush (stdout);
   1806     }
   1807     inc_size = ((req_total_size - 1) + (rate_limiter - 1)) / rate_limiter;
   1808     if (0 == inc_size)
   1809       inc_size = 1;
   1810   }
   1811   else
   1812     inc_size = 1;
   1813 
   1814   start = time (NULL);
   1815   for (limit_send_size = 1; limit_send_size < req_total_size;
   1816        limit_send_size += inc_size)
   1817   {
   1818     int test_succeed;
   1819     test_succeed = 0;
   1820     /* Make sure that maximum size is tested */
   1821     if (req_total_size - inc_size < limit_send_size)
   1822       limit_send_size = req_total_size - 1;
   1823     qParam.total_send_max = limit_send_size;
   1824     /* To be updated by callbacks */
   1825     ahc_param->cb_called = 0;
   1826     uri_cb_param->cb_called = 0;
   1827     term_result->num_called = 0;
   1828     term_result->term_reason = -1;
   1829     sckt_result->num_started = 0;
   1830     sckt_result->num_finished = 0;
   1831 
   1832     if (0 != doClientQueryInThread (d, &qParam))
   1833       fprintf (stderr, "FAILED: connection has NOT been closed by MHD.");
   1834     else
   1835     {
   1836       if ((-1 != term_result->term_reason) &&
   1837           (MHD_REQUEST_TERMINATED_READ_ERROR != term_result->term_reason) &&
   1838           (MHD_REQUEST_TERMINATED_CLIENT_ABORT != term_result->term_reason) )
   1839         fprintf (stderr, "FAILED: Wrong termination code.");
   1840       else if ((0 == term_result->num_called) &&
   1841                ((0 != uri_cb_param->cb_called) || (0 != ahc_param->cb_called)))
   1842         fprintf (stderr, "FAILED: Missing required call of final notification "
   1843                  "callback.");
   1844       else if (1 < uri_cb_param->cb_called)
   1845         fprintf (stderr, "FAILED: Too many URI callbacks.");
   1846       else if ((0 != ahc_param->cb_called) && (0 == uri_cb_param->cb_called))
   1847         fprintf (stderr, "FAILED: URI callback has NOT been called "
   1848                  "while Access Handler callback has been called.");
   1849       else if (1 < term_result->num_called)
   1850         fprintf (stderr, "FAILED: Too many final callbacks.");
   1851       else if (1 != sckt_result->num_started)
   1852         fprintf (stderr, "FAILED: Wrong number of sockets accepted.");
   1853       else if (1 != sckt_result->num_finished)
   1854         fprintf (stderr, "FAILED: Wrong number of sockets closed.");
   1855       else
   1856       {
   1857         test_succeed = 1;
   1858         if (expected_reason == term_result->term_reason)
   1859           found_right_reason = 1;
   1860       }
   1861     }
   1862 
   1863     if (! test_succeed)
   1864     {
   1865       ret = 1;
   1866       printTestResults (stderr,
   1867                         &qParam, ahc_param, uri_cb_param,
   1868                         term_result, sckt_result);
   1869     }
   1870     else if (verbose)
   1871     {
   1872       printf ("SUCCEED:");
   1873       printTestResults (stdout,
   1874                         &qParam, ahc_param, uri_cb_param,
   1875                         term_result, sckt_result);
   1876     }
   1877 
   1878     if (time (NULL) - start >
   1879         (time_t) ((TIMEOUTS_VAL * 25)
   1880                   + (rate_limiter * FINAL_PACKETS_MS) / 1000 + 1))
   1881     {
   1882       ret |= 1 << 2;
   1883       fprintf (stderr, "FAILED: Test total time exceeded.\n");
   1884       break;
   1885     }
   1886   }
   1887 
   1888   MHD_stop_daemon (d);
   1889   free (uri_cb_param);
   1890   free (ahc_param);
   1891   free (term_result);
   1892   free (sckt_result);
   1893 
   1894   if (! found_right_reason)
   1895   {
   1896 #ifndef __gnu_hurd__
   1897     fprintf (stderr, "FAILED: termination callback was not called with "
   1898              "expected (%s) reason.\n",
   1899              term_reason_str ((enum MHD_RequestTerminationCode)
   1900                               expected_reason));
   1901     fflush (stderr);
   1902     ret |= 1 << 1;
   1903 #endif /* ! __gnu_hurd__ */
   1904     (void) 0;
   1905   }
   1906 
   1907   return ret;
   1908 }
   1909 
   1910 
   1911 enum testMhdThreadsType
   1912 {
   1913   testMhdThreadExternal              = 0,
   1914   testMhdThreadInternal              = MHD_USE_INTERNAL_POLLING_THREAD,
   1915   testMhdThreadInternalPerConnection = MHD_USE_THREAD_PER_CONNECTION
   1916                                        | MHD_USE_INTERNAL_POLLING_THREAD,
   1917   testMhdThreadInternalPool
   1918 };
   1919 
   1920 enum testMhdPollType
   1921 {
   1922   testMhdPollBySelect = 0,
   1923   testMhdPollByPoll   = MHD_USE_POLL,
   1924   testMhdPollByEpoll  = MHD_USE_EPOLL,
   1925   testMhdPollAuto     = MHD_USE_AUTO
   1926 };
   1927 
   1928 /* Get number of threads for thread pool depending
   1929  * on used poll function and test type. */
   1930 static unsigned int
   1931 testNumThreadsForPool (enum testMhdPollType pollType)
   1932 {
   1933   unsigned int numThreads = MHD_CPU_COUNT;
   1934   (void) pollType; /* Don't care about pollType for this test */
   1935   return numThreads; /* No practical limit for non-cleanup test */
   1936 }
   1937 
   1938 
   1939 static struct MHD_Daemon *
   1940 startTestMhdDaemon (enum testMhdThreadsType thrType,
   1941                     enum testMhdPollType pollType, uint16_t *pport,
   1942                     struct ahc_cls_type **ahc_param,
   1943                     struct check_uri_cls **uri_cb_param,
   1944                     struct term_notif_cb_param **term_result,
   1945                     struct sckt_notif_cb_param **sckt_result)
   1946 {
   1947   struct MHD_Daemon *d;
   1948   const union MHD_DaemonInfo *dinfo;
   1949 
   1950   if ((NULL == ahc_param) || (NULL == uri_cb_param) || (NULL == term_result))
   1951     externalErrorExit ();
   1952 
   1953   *ahc_param = (struct ahc_cls_type *) malloc (sizeof(struct ahc_cls_type));
   1954   if (NULL == *ahc_param)
   1955     externalErrorExit ();
   1956   *uri_cb_param =
   1957     (struct check_uri_cls *) malloc (sizeof(struct check_uri_cls));
   1958   if (NULL == *uri_cb_param)
   1959     externalErrorExit ();
   1960   *term_result =
   1961     (struct term_notif_cb_param *) malloc (sizeof(struct term_notif_cb_param));
   1962   if (NULL == *term_result)
   1963     externalErrorExit ();
   1964   *sckt_result =
   1965     (struct sckt_notif_cb_param *) malloc (sizeof(struct sckt_notif_cb_param));
   1966   if (NULL == *sckt_result)
   1967     externalErrorExit ();
   1968 
   1969   if ( (0 == *pport) &&
   1970        (MHD_NO == MHD_is_feature_supported (MHD_FEATURE_AUTODETECT_BIND_PORT)) )
   1971   {
   1972     *pport = 4170;
   1973     if (use_shutdown)
   1974       *pport = (uint16_t) (*pport + 0);
   1975     if (use_close)
   1976       *pport = (uint16_t) (*pport + 1);
   1977     if (use_hard_close)
   1978       *pport = (uint16_t) (*pport + 1);
   1979     if (by_step)
   1980       *pport = (uint16_t) (*pport + (1 << 2));
   1981     if (upl_chunked)
   1982       *pport = (uint16_t) (*pport + (1 << 3));
   1983     if (! oneone)
   1984       *pport = (uint16_t) (*pport + (1 << 4));
   1985   }
   1986 
   1987   if (testMhdThreadExternal == thrType)
   1988     d = MHD_start_daemon (((unsigned int) pollType)
   1989                           | (verbose ? MHD_USE_ERROR_LOG : 0)
   1990                           | MHD_USE_NO_THREAD_SAFETY,
   1991                           *pport, NULL, NULL,
   1992                           &ahcCheck, *ahc_param,
   1993                           MHD_OPTION_URI_LOG_CALLBACK, &check_uri_cb,
   1994                           *uri_cb_param,
   1995                           MHD_OPTION_NOTIFY_COMPLETED, &term_cb, *term_result,
   1996                           MHD_OPTION_NOTIFY_CONNECTION, &socket_cb,
   1997                           *sckt_result,
   1998                           MHD_OPTION_CONNECTION_TIMEOUT,
   1999                           (unsigned) TIMEOUTS_VAL,
   2000                           MHD_OPTION_APP_FD_SETSIZE, (int) FD_SETSIZE,
   2001                           MHD_OPTION_END);
   2002   else if (testMhdThreadInternalPool != thrType)
   2003     d = MHD_start_daemon (((unsigned int) thrType) | ((unsigned int) pollType)
   2004                           | (verbose ? MHD_USE_ERROR_LOG : 0),
   2005                           *pport, NULL, NULL,
   2006                           &ahcCheck, *ahc_param,
   2007                           MHD_OPTION_URI_LOG_CALLBACK, &check_uri_cb,
   2008                           *uri_cb_param,
   2009                           MHD_OPTION_NOTIFY_COMPLETED, &term_cb, *term_result,
   2010                           MHD_OPTION_NOTIFY_CONNECTION, &socket_cb,
   2011                           *sckt_result,
   2012                           MHD_OPTION_CONNECTION_TIMEOUT,
   2013                           (unsigned) TIMEOUTS_VAL,
   2014                           MHD_OPTION_END);
   2015   else
   2016     d = MHD_start_daemon (MHD_USE_INTERNAL_POLLING_THREAD
   2017                           | ((unsigned int) pollType)
   2018                           | (verbose ? MHD_USE_ERROR_LOG : 0),
   2019                           *pport, NULL, NULL,
   2020                           &ahcCheck, *ahc_param,
   2021                           MHD_OPTION_THREAD_POOL_SIZE,
   2022                           testNumThreadsForPool (pollType),
   2023                           MHD_OPTION_URI_LOG_CALLBACK, &check_uri_cb,
   2024                           *uri_cb_param,
   2025                           MHD_OPTION_NOTIFY_COMPLETED, &term_cb, *term_result,
   2026                           MHD_OPTION_NOTIFY_CONNECTION, &socket_cb,
   2027                           *sckt_result,
   2028                           MHD_OPTION_CONNECTION_TIMEOUT,
   2029                           (unsigned) TIMEOUTS_VAL,
   2030                           MHD_OPTION_END);
   2031 
   2032   if (NULL == d)
   2033     mhdErrorExitDesc ("Failed to start MHD daemon");
   2034 
   2035   if (0 == *pport)
   2036   {
   2037     dinfo = MHD_get_daemon_info (d, MHD_DAEMON_INFO_BIND_PORT);
   2038     if ((NULL == dinfo) || (0 == dinfo->port))
   2039       mhdErrorExitDesc ("MHD_get_daemon_info() failed");
   2040     *pport = dinfo->port;
   2041     if (0 == global_port)
   2042       global_port = *pport; /* Reuse the same port for all tests */
   2043   }
   2044 
   2045   return d;
   2046 }
   2047 
   2048 
   2049 /* Test runners */
   2050 
   2051 
   2052 static unsigned int
   2053 testExternalGet (void)
   2054 {
   2055   struct MHD_Daemon *d;
   2056   uint16_t d_port = global_port; /* Daemon's port */
   2057   struct ahc_cls_type *ahc_param;
   2058   struct check_uri_cls *uri_cb_param;
   2059   struct term_notif_cb_param *term_result;
   2060   struct sckt_notif_cb_param *sckt_result;
   2061 
   2062   d = startTestMhdDaemon (testMhdThreadExternal, testMhdPollBySelect, &d_port,
   2063                           &ahc_param, &uri_cb_param, &term_result,
   2064                           &sckt_result);
   2065 
   2066   return performTestQueries (d, d_port, ahc_param, uri_cb_param, term_result,
   2067                              sckt_result);
   2068 }
   2069 
   2070 
   2071 #if 0 /* disabled runners, not suitable for this test */
   2072 static int
   2073 testInternalGet (enum testMhdPollType pollType)
   2074 {
   2075   struct MHD_Daemon *d;
   2076   uint16_t d_port = global_port; /* Daemon's port */
   2077   struct ahc_cls_type *ahc_param;
   2078   struct check_uri_cls *uri_cb_param;
   2079   struct term_notif_cb_param *term_result;
   2080 
   2081   d = startTestMhdDaemon (testMhdThreadInternal, pollType, &d_port,
   2082                           &ahc_param, &uri_cb_param, &term_result);
   2083 
   2084   return performTestQueries (d, d_port, ahc_param, uri_cb_param, term_result);
   2085 }
   2086 
   2087 
   2088 static int
   2089 testMultithreadedGet (enum testMhdPollType pollType)
   2090 {
   2091   struct MHD_Daemon *d;
   2092   uint16_t d_port = global_port; /* Daemon's port */
   2093   struct ahc_cls_type *ahc_param;
   2094   struct check_uri_cls *uri_cb_param;
   2095   struct term_notif_cb_param *term_result;
   2096 
   2097   d = startTestMhdDaemon (testMhdThreadInternalPerConnection, pollType, &d_port,
   2098                           &ahc_param, &uri_cb_param);
   2099   return performTestQueries (d, d_port, ahc_param, uri_cb_param, term_result);
   2100 }
   2101 
   2102 
   2103 static int
   2104 testMultithreadedPoolGet (enum testMhdPollType pollType)
   2105 {
   2106   struct MHD_Daemon *d;
   2107   uint16_t d_port = global_port; /* Daemon's port */
   2108   struct ahc_cls_type *ahc_param;
   2109   struct check_uri_cls *uri_cb_param;
   2110   struct term_notif_cb_param *term_result;
   2111 
   2112   d = startTestMhdDaemon (testMhdThreadInternalPool, pollType, &d_port,
   2113                           &ahc_param, &uri_cb_param);
   2114   return performTestQueries (d, d_port, ahc_param, uri_cb_param, term_result);
   2115 }
   2116 
   2117 
   2118 #endif /* disabled runners, not suitable for this test */
   2119 
   2120 int
   2121 main (int argc, char *const *argv)
   2122 {
   2123   unsigned int errorCount = 0;
   2124   unsigned int test_result = 0;
   2125   verbose = 0;
   2126 
   2127   if ((NULL == argv) || (0 == argv[0]))
   2128     return 99;
   2129   oneone = ! has_in_name (argv[0], "10");
   2130   use_shutdown = has_in_name (argv[0], "_shutdown") ? 1 : 0;
   2131   use_close = has_in_name (argv[0], "_close") ? 1 : 0;
   2132   use_hard_close = has_in_name (argv[0], "_hard_close") ? 1 : 0;
   2133   use_stress_os = has_in_name (argv[0], "_stress_os") ? 1 : 0;
   2134   by_step = has_in_name (argv[0], "_steps") ? 1 : 0;
   2135   upl_chunked = has_in_name (argv[0], "_chunked") ? 1 : 0;
   2136 #ifndef SO_LINGER
   2137   if (use_hard_close)
   2138   {
   2139     fprintf (stderr, "This test requires SO_LINGER socket option support.\n");
   2140     return 77;
   2141   }
   2142 #endif /* ! SO_LINGER */
   2143   if (1 != use_shutdown + use_close)
   2144     return 99;
   2145   verbose = ! (has_param (argc, argv, "-q") ||
   2146                has_param (argc, argv, "--quiet") ||
   2147                has_param (argc, argv, "-s") ||
   2148                has_param (argc, argv, "--silent"));
   2149   if (use_stress_os && ! use_hard_close)
   2150     return 99;
   2151 
   2152   test_global_init ();
   2153 
   2154   /* Could be set to non-zero value to enforce using specific port
   2155    * in the test */
   2156   global_port = 0;
   2157   test_result = testExternalGet ();
   2158   if (test_result)
   2159     fprintf (stderr, "FAILED: testExternalGet (). Result: %u.\n", test_result);
   2160   else if (verbose)
   2161     printf ("PASSED: testExternalGet ().\n");
   2162   errorCount += test_result;
   2163 #if 0 /* disabled runners, not suitable for this test */
   2164   if (MHD_YES == MHD_is_feature_supported (MHD_FEATURE_THREADS))
   2165   {
   2166     test_result = testInternalGet (testMhdPollAuto);
   2167     if (test_result)
   2168       fprintf (stderr, "FAILED: testInternalGet (testMhdPollAuto). "
   2169                "Result: %u.\n",
   2170                test_result);
   2171     else if (verbose)
   2172       printf ("PASSED: testInternalGet (testMhdPollBySelect).\n");
   2173     errorCount += test_result;
   2174 #ifdef _MHD_HEAVY_TESTS
   2175     /* Actually tests are not heavy, but took too long to complete while
   2176      * not really provide any additional results. */
   2177     test_result = testInternalGet (testMhdPollBySelect);
   2178     if (test_result)
   2179       fprintf (stderr, "FAILED: testInternalGet (testMhdPollBySelect). "
   2180                "Result: %u.\n",
   2181                test_result);
   2182     else if (verbose)
   2183       printf ("PASSED: testInternalGet (testMhdPollBySelect).\n");
   2184     errorCount += test_result;
   2185     test_result = testMultithreadedPoolGet (testMhdPollBySelect);
   2186     if (test_result)
   2187       fprintf (stderr,
   2188                "FAILED: testMultithreadedPoolGet (testMhdPollBySelect). "
   2189                "Result: %u.\n",
   2190                test_result);
   2191     else if (verbose)
   2192       printf ("PASSED: testMultithreadedPoolGet (testMhdPollBySelect).\n");
   2193     errorCount += test_result;
   2194     test_result = testMultithreadedGet (testMhdPollBySelect);
   2195     if (test_result)
   2196       fprintf (stderr,
   2197                "FAILED: testMultithreadedGet (testMhdPollBySelect). "
   2198                "Result: %u.\n",
   2199                test_result);
   2200     else if (verbose)
   2201       printf ("PASSED: testMultithreadedGet (testMhdPollBySelect).\n");
   2202     errorCount += test_result;
   2203     if (MHD_YES == MHD_is_feature_supported (MHD_FEATURE_POLL))
   2204     {
   2205       test_result = testInternalGet (testMhdPollByPoll);
   2206       if (test_result)
   2207         fprintf (stderr, "FAILED: testInternalGet (testMhdPollByPoll). "
   2208                  "Result: %u.\n",
   2209                  test_result);
   2210       else if (verbose)
   2211         printf ("PASSED: testInternalGet (testMhdPollByPoll).\n");
   2212       errorCount += test_result;
   2213     }
   2214     if (MHD_YES == MHD_is_feature_supported (MHD_FEATURE_EPOLL))
   2215     {
   2216       test_result = testInternalGet (testMhdPollByEpoll);
   2217       if (test_result)
   2218         fprintf (stderr, "FAILED: testInternalGet (testMhdPollByEpoll). "
   2219                  "Result: %u.\n",
   2220                  test_result);
   2221       else if (verbose)
   2222         printf ("PASSED: testInternalGet (testMhdPollByEpoll).\n");
   2223       errorCount += test_result;
   2224     }
   2225 #else
   2226     /* Mute compiler warnings */
   2227     (void) testMultithreadedGet;
   2228     (void) testMultithreadedPoolGet;
   2229 #endif /* _MHD_HEAVY_TESTS */
   2230   }
   2231 #endif /* disabled runners, not suitable for this test */
   2232   if (0 != errorCount)
   2233     fprintf (stderr,
   2234              "Error (code: %u)\n",
   2235              errorCount);
   2236   else if (verbose)
   2237     printf ("All tests passed.\n");
   2238 
   2239   test_global_cleanup ();
   2240 
   2241   return (errorCount == 0) ? 0 : 1;       /* 0 == pass */
   2242 }