libmicrohttpd

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

test_client_put_stop.c (69562B)


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