libmicrohttpd

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

test_raw_requests.c (67420B)


      1 /*
      2      This file is part of libmicrohttpd
      3      Copyright (C) 2026 Christian Grothoff
      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_raw_requests.c
     22  * @brief  Data-driven raw HTTP request corpus, replayed at every possible
     23  *         TCP stream split point and under a sweep of connection memory
     24  *         limits.
     25  * @author Christian Grothoff
     26  *
     27  * ## Why this test exists
     28  *
     29  * The MHD request parser is *incremental*: it is re-entered every time more
     30  * bytes arrive from the network and has to decide, at each entry, whether it
     31  * already holds a complete line / chunk header / chunk / body.  Off-by-N
     32  * errors, misplaced `break; /_* need more data *_/` statements and
     33  * state-machine desynchronisations live exactly in that logic and they are
     34  * invisible when the whole request arrives in a single `read()`.
     35  *
     36  * This test therefore takes every corpus entry and replays it over a fresh TCP
     37  * connection once per possible split offset, forcing the server to re-enter
     38  * the parser at that offset.  The parse result (method, URL, version, upload
     39  * body, headers, GET arguments, footers, number of complete requests, response
     40  * status, connection re-use) must be *identical* for every split offset.  Any
     41  * divergence is a bug, no matter what the declared expectations say.
     42  *
     43  * The whole corpus is additionally replayed for a sweep of
     44  * #MHD_OPTION_CONNECTION_MEMORY_LIMIT values.  Values below #MHD_BUF_INC_SIZE
     45  * (1500) unlock the read-buffer "shift back" and buffer-grow code paths that
     46  * are never reached with the default pool size.
     47  *
     48  * ## How to add a corpus entry
     49  *
     50  * Add exactly one initialiser to the @a corpus[] table below.  Nothing else
     51  * has to be touched.  The fields are:
     52  *
     53  * * `.name`  - short identifier, printed on failure;
     54  * * `.desc`  - one line saying which behaviour the entry pins down;
     55  * * `.raw`   - the raw request byte stream (may contain several pipelined
     56  *              requests);  `.raw_len` may stay 0, then `strlen()` is used;
     57  * * `.n_req` - how many complete requests MHD *must* parse out of `.raw`
     58  *              (this is the request-smuggling / desync detector: a stream
     59  *              that must yield 2 requests may never yield 1 or 3);
     60  * * `.reqs[]`- the per-request expectations.  `.hdrs`, `.args` and `.foot`
     61  *              are newline separated `key=value` lists in the order in which
     62  *              MHD reports them; a NULL value (what a query argument without
     63  *              '=' produces) is written as `<NULL>`;
     64  * * `.n_resp`- how many HTTP responses the client must see;
     65  * * `.status`- expected status code of the *first* response, 0 for "any
     66  *              well-formed response";
     67  * * `.alive` - 1 if the connection must stay alive afterwards, 0 if MHD must
     68  *              close it;
     69  * * `.deep`  - non-zero to additionally run the (much slower) three-way split
     70  *              pass over this entry.
     71  *
     72  * The escaping used by the observation format has to be applied to the
     73  * expectation strings as well: every byte outside the printable ASCII range
     74  * appears as `\\xNN` (so a TAB inside a header value is written as `\\x09`).
     75  *
     76  * If the entry does not fit into the connection memory pool of the current
     77  * sweep step, MHD answers 431/413/414/500 without ever invoking the request
     78  * handler, or it cannot even build the reply header and closes the connection
     79  * without an answer, or it serves only the first of several pipelined
     80  * requests.  All three are legitimate resource limitations; the test detects
     81  * them and falls back to the relaxed check "every request that MHD did parse
     82  * matches the expectation for its index, and MHD never parses more requests
     83  * than the stream contains".  A 400 Bad Request is never accepted as a
     84  * resource limitation.
     85  *
     86  * ## The daemon option matrix
     87  *
     88  * The test honours the matrix of mhd_opt_matrix.h: when one of the MHD_TEST_*
     89  * environment variables selects a profile, the built-in memory limit sweep is
     90  * replaced by the single memory limit of the profile and the threading mode,
     91  * the polling backend and the client discipline level of the profile are
     92  * applied.  Without those variables nothing changes, so a stock "make check"
     93  * runs exactly the sweep described above.
     94  *
     95  * The declared expectations of a corpus entry are only valid for a range of
     96  * client discipline levels - the levels are precisely the knob that switches
     97  * the parser leniency the entries pin down.  Every entry whose expectation
     98  * depends on the level is therefore listed in @a discp_limits[] with the
     99  * range in which it is meaningful, and is skipped outside of it.  At the
    100  * default level 0 nothing is ever skipped.
    101  *
    102  * ## Command line
    103  *
    104  * * `-v`            - report the memory limit sweep and degraded entries;
    105  * * `-v -v`         - additionally print the MHD error log;
    106  * * `--entry=NAME`  - restrict the run to a single corpus entry (`NAME` may
    107  *                     also be given as a bare argument).
    108  *
    109  * Exit codes: 0 = pass, 77 = skip, 99 = hard setup error, 1 = test failure.
    110  */
    111 #include "MHD_config.h"
    112 #include "platform.h"
    113 #include <microhttpd.h>
    114 #include <stdlib.h>
    115 #include <string.h>
    116 #include <stdarg.h>
    117 #include <time.h>
    118 #include <stdint.h>
    119 #include <errno.h>
    120 
    121 #ifdef HAVE_STRINGS_H
    122 #include <strings.h>
    123 #endif /* HAVE_STRINGS_H */
    124 
    125 #ifdef _WIN32
    126 #ifndef WIN32_LEAN_AND_MEAN
    127 #define WIN32_LEAN_AND_MEAN 1
    128 #endif /* !WIN32_LEAN_AND_MEAN */
    129 #include <windows.h>
    130 #endif
    131 
    132 #ifndef WINDOWS
    133 #include <unistd.h>
    134 #include <sys/socket.h>
    135 #endif
    136 
    137 #ifdef HAVE_LIMITS_H
    138 #include <limits.h>
    139 #endif /* HAVE_LIMITS_H */
    140 
    141 #ifdef HAVE_SIGNAL_H
    142 #include <signal.h>
    143 #endif /* HAVE_SIGNAL_H */
    144 
    145 #include <stdio.h>
    146 
    147 #include "mhd_sockets.h" /* only macros used */
    148 #include "mhd_opt_matrix.h"
    149 
    150 #ifndef MHD_STATICSTR_LEN_
    151 /**
    152  * Determine length of static string / macro strings at compile time.
    153  */
    154 #define MHD_STATICSTR_LEN_(macro) (sizeof(macro) / sizeof(char) - 1)
    155 #endif /* ! MHD_STATICSTR_LEN_ */
    156 
    157 /**
    158  * Convenience shorthand used all over the corpus table.
    159  */
    160 #define CRLF "\r\n"
    161 
    162 /**
    163  * Maximum number of pipelined requests a single corpus entry may contain.
    164  */
    165 #define MAX_REQS 4
    166 
    167 /**
    168  * Size of the textual observation buffers.
    169  */
    170 #define OBS_SIZE 8192
    171 
    172 /**
    173  * Size of the client receive buffer.
    174  */
    175 #define RX_SIZE 16384
    176 
    177 /**
    178  * Milliseconds to wait between two parts of a split request so that the
    179  * server really gets to process the partial input.
    180  */
    181 #define SPLIT_DELAY_MS 2
    182 
    183 /**
    184  * Hard upper bound (ms) for reading the complete server answer.
    185  */
    186 #define READ_DEADLINE_MS 400
    187 
    188 /**
    189  * Silence (ms) after the expected number of responses that is accepted as
    190  * "the server is done and keeps the connection alive".  Only used for the
    191  * unsplit reference run.
    192  */
    193 #define READ_GRACE_MS 12
    194 
    195 /**
    196  * Milliseconds to wait for the server side to finish tearing down the
    197  * connection (and thus for the observations to become stable).
    198  */
    199 #define CLOSE_WAIT_MS 2000
    200 
    201 /**
    202  * Split offsets 1 .. SPLIT_DENSE_LIMIT are all exercised, beyond that only
    203  * every SPLIT_STRIDE-th offset is used.
    204  */
    205 #define SPLIT_DENSE_LIMIT 512
    206 
    207 /**
    208  * Stride for sampling split offsets of long requests.
    209  */
    210 #define SPLIT_STRIDE 7
    211 
    212 /**
    213  * Maximum number of three-way split combinations per entry and pool size.
    214  */
    215 #define DEEP_SPLIT_MAX 24
    216 
    217 /**
    218  * Give up after that many reported failures.
    219  */
    220 #define MAX_FAILURES 10
    221 
    222 
    223 #if defined(HAVE___FUNC__)
    224 #define externalErrorExit(ignore) \
    225         _externalErrorExit_func (NULL, __func__, __LINE__)
    226 #define externalErrorExitDesc(errDesc) \
    227         _externalErrorExit_func (errDesc, __func__, __LINE__)
    228 #elif defined(HAVE___FUNCTION__)
    229 #define externalErrorExit(ignore) \
    230         _externalErrorExit_func (NULL, __FUNCTION__, __LINE__)
    231 #define externalErrorExitDesc(errDesc) \
    232         _externalErrorExit_func (errDesc, __FUNCTION__, __LINE__)
    233 #else
    234 #define externalErrorExit(ignore) _externalErrorExit_func (NULL, NULL, __LINE__)
    235 #define externalErrorExitDesc(errDesc) \
    236         _externalErrorExit_func (errDesc, NULL, __LINE__)
    237 #endif
    238 
    239 
    240 /* Forward declaration, needed by the error exit helpers. */
    241 static void
    242 print_context (FILE *out);
    243 
    244 
    245 _MHD_NORETURN static void
    246 _externalErrorExit_func (const char *errDesc, const char *funcName, int lineNum)
    247 {
    248   if ((NULL != errDesc) && (0 != errDesc[0]))
    249     fprintf (stderr, "%s", errDesc);
    250   else
    251     fprintf (stderr, "System or external library call failed");
    252   if ((NULL != funcName) && (0 != funcName[0]))
    253     fprintf (stderr, " in %s", funcName);
    254   if (0 < lineNum)
    255     fprintf (stderr, " at line %d", lineNum);
    256 
    257   fprintf (stderr, ".\nLast errno value: %d (%s)\n", (int) errno,
    258            strerror (errno));
    259 #ifdef MHD_WINSOCK_SOCKETS
    260   fprintf (stderr, "WSAGetLastError() value: %d\n", (int) WSAGetLastError ());
    261 #endif /* MHD_WINSOCK_SOCKETS */
    262   print_context (stderr);
    263   fflush (stderr);
    264   exit (99);
    265 }
    266 
    267 
    268 /**
    269  * Pause execution for specified number of milliseconds.
    270  * @param ms the number of milliseconds to sleep
    271  */
    272 static void
    273 _MHD_sleep (uint32_t ms)
    274 {
    275 #if defined(_WIN32)
    276   Sleep (ms);
    277 #elif defined(HAVE_NANOSLEEP)
    278   struct timespec slp = {(time_t) (ms / 1000), (long) ((ms % 1000) * 1000000)};
    279   struct timespec rmn;
    280   int num_retries = 0;
    281   while (0 != nanosleep (&slp, &rmn))
    282   {
    283     if (EINTR != errno)
    284       externalErrorExit ();
    285     if (num_retries++ > 8)
    286       break;
    287     slp = rmn;
    288   }
    289 #elif defined(HAVE_USLEEP)
    290   uint64_t us = ms * 1000;
    291   do
    292   {
    293     uint64_t this_sleep;
    294     if (999999 < us)
    295       this_sleep = 999999;
    296     else
    297       this_sleep = us;
    298     /* Ignore return value as it could be void */
    299     usleep ((useconds_t) this_sleep);
    300     us -= this_sleep;
    301   } while (us > 0);
    302 #else
    303   externalErrorExitDesc ("No sleep function available on this system");
    304 #endif
    305 }
    306 
    307 
    308 /**
    309  * Monotonic-ish millisecond clock.
    310  */
    311 static uint64_t
    312 now_ms (void)
    313 {
    314 #if defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_MONOTONIC)
    315   struct timespec ts;
    316 
    317   if (0 == clock_gettime (CLOCK_MONOTONIC, &ts))
    318     return ((uint64_t) ts.tv_sec) * 1000 + (uint64_t) (ts.tv_nsec / 1000000);
    319 #endif /* HAVE_CLOCK_GETTIME && CLOCK_MONOTONIC */
    320   return ((uint64_t) time (NULL)) * 1000;
    321 }
    322 
    323 
    324 /* ------------------------------------------------------------------ */
    325 /* Textual observation buffers                                        */
    326 /* ------------------------------------------------------------------ */
    327 
    328 /**
    329  * A bounded, always NUL-terminated text accumulator.  All observations
    330  * (expected as well as actual) are rendered into such a buffer and the
    331  * comparison is a plain strcmp(), which also gives readable diagnostics.
    332  */
    333 struct obs_buf
    334 {
    335   char d[OBS_SIZE];
    336   size_t len;
    337   int overflow;
    338 };
    339 
    340 
    341 static void
    342 obs_reset (struct obs_buf *b)
    343 {
    344   b->len = 0;
    345   b->overflow = 0;
    346   b->d[0] = 0;
    347 }
    348 
    349 
    350 static void
    351 obs_addf (struct obs_buf *b, const char *fmt, ...)
    352 {
    353   va_list ap;
    354   int r;
    355   size_t space;
    356 
    357   if (b->len >= sizeof(b->d) - 1)
    358   {
    359     b->overflow = 1;
    360     return;
    361   }
    362   space = sizeof(b->d) - b->len;
    363   va_start (ap, fmt);
    364   r = vsnprintf (b->d + b->len, space, fmt, ap);
    365   va_end (ap);
    366   if (0 > r)
    367   {
    368     b->overflow = 1;
    369     return;
    370   }
    371   if ((size_t) r >= space)
    372   {
    373     b->len = sizeof(b->d) - 1;
    374     b->overflow = 1;
    375     return;
    376   }
    377   b->len += (size_t) r;
    378 }
    379 
    380 
    381 /**
    382  * Append @a data with all non-printable bytes escaped as \\xNN.
    383  */
    384 static void
    385 obs_add_escaped (struct obs_buf *b, const char *data, size_t size)
    386 {
    387   size_t i;
    388 
    389   for (i = 0; i < size; ++i)
    390   {
    391     const unsigned char c = (unsigned char) data[i];
    392 
    393     if ((0x20 <= c) && (0x7e >= c) && ('\\' != c))
    394       obs_addf (b, "%c", (char) c);
    395     else if ('\\' == c)
    396       obs_addf (b, "\\\\");
    397     else
    398       obs_addf (b, "\\x%02x", (unsigned int) c);
    399   }
    400 }
    401 
    402 
    403 /* ------------------------------------------------------------------ */
    404 /* The corpus                                                         */
    405 /* ------------------------------------------------------------------ */
    406 
    407 /**
    408  * Expectations for a single (possibly pipelined) request inside a stream.
    409  */
    410 struct exp_req
    411 {
    412   const char *method;
    413   const char *url;
    414   const char *version;
    415   /**
    416    * Expected upload body, NULL for "no body at all".
    417    */
    418   const char *body;
    419   /**
    420    * Length of @a body, 0 means strlen().
    421    */
    422   size_t body_len;
    423   /**
    424    * Newline separated list of expected #MHD_HEADER_KIND elements.
    425    */
    426   const char *hdrs;
    427   /**
    428    * Newline separated list of expected #MHD_GET_ARGUMENT_KIND elements.
    429    */
    430   const char *args;
    431   /**
    432    * Newline separated list of expected #MHD_FOOTER_KIND elements.
    433    */
    434   const char *foot;
    435 };
    436 
    437 
    438 struct corpus_entry
    439 {
    440   const char *name;
    441   const char *desc;
    442   const char *raw;
    443   size_t raw_len;               /**< 0 -> strlen (raw) */
    444   unsigned int n_req;           /**< complete requests MHD must parse */
    445   unsigned int n_resp;          /**< responses the client must see */
    446   int status;                   /**< first response status, 0 = any */
    447   int alive;                    /**< 1: keep-alive, 0: MHD must close */
    448   int deep;                     /**< also run three-way splits */
    449   struct exp_req reqs[MAX_REQS];
    450 };
    451 
    452 
    453 #define H_LOCAL "Host: localhost" CRLF
    454 #define E_HOST "Host=localhost"
    455 
    456 static const struct corpus_entry corpus[] = {
    457   /* ---- request line basics -------------------------------------- */
    458   { "get_11_host",
    459     "baseline: request line + one header, incremental line assembly",
    460     "GET / HTTP/1.1" CRLF H_LOCAL CRLF, 0, 1, 1, 200, 1, 1,
    461     { { "GET", "/", "HTTP/1.1", NULL, 0, E_HOST, "", "" } } },
    462   { "get_10_nohost",
    463     "HTTP/1.0 without Host: no header lines at all, must not keep alive",
    464     "GET / HTTP/1.0" CRLF CRLF, 0, 1, 1, 200, 0, 1,
    465     { { "GET", "/", "HTTP/1.0", NULL, 0, "", "", "" } } },
    466   { "head_11",
    467     "HEAD: reply must carry no body, parser path is the same",
    468     "HEAD / HTTP/1.1" CRLF H_LOCAL CRLF, 0, 1, 1, 200, 1, 0,
    469     { { "HEAD", "/", "HTTP/1.1", NULL, 0, E_HOST, "", "" } } },
    470 
    471   /* ---- query string handling ------------------------------------ */
    472   { "q_bare",
    473     "single query argument without '=' -> NULL value",
    474     "GET /?a HTTP/1.1" CRLF H_LOCAL CRLF, 0, 1, 1, 200, 1, 0,
    475     { { "GET", "/", "HTTP/1.1", NULL, 0, E_HOST, "a=<NULL>", "" } } },
    476   { "q_two_bare",
    477     "two arguments, both without '='",
    478     "GET /?a&b HTTP/1.1" CRLF H_LOCAL CRLF, 0, 1, 1, 200, 1, 0,
    479     { { "GET", "/", "HTTP/1.1", NULL, 0, E_HOST, "a=<NULL>\nb=<NULL>", "" } } },
    480   { "q_mixed",
    481     "'=' in the first argument only",
    482     "GET /?a=1&b HTTP/1.1" CRLF H_LOCAL CRLF, 0, 1, 1, 200, 1, 0,
    483     { { "GET", "/", "HTTP/1.1", NULL, 0, E_HOST, "a=1\nb=<NULL>", "" } } },
    484   { "q_empty_val",
    485     "'a=' must give an empty (not NULL) value",
    486     "GET /?a= HTTP/1.1" CRLF H_LOCAL CRLF, 0, 1, 1, 200, 1, 0,
    487     { { "GET", "/", "HTTP/1.1", NULL, 0, E_HOST, "a=", "" } } },
    488   { "q_trailing_amp",
    489     "trailing '&' must not create a second, empty argument",
    490     "GET /?a& HTTP/1.1" CRLF H_LOCAL CRLF, 0, 1, 1, 200, 1, 0,
    491     { { "GET", "/", "HTTP/1.1", NULL, 0, E_HOST, "a=<NULL>", "" } } },
    492   { "q_no_key",
    493     "'=v' must give an empty key with value 'v'",
    494     "GET /?=v HTTP/1.1" CRLF H_LOCAL CRLF, 0, 1, 1, 200, 1, 0,
    495     { { "GET", "/", "HTTP/1.1", NULL, 0, E_HOST, "=v", "" } } },
    496   { "q_pct",
    497     "percent decoding of both key and value happens in place",
    498     "GET /?a%20b=c%26d HTTP/1.1" CRLF H_LOCAL CRLF, 0, 1, 1, 200, 1, 0,
    499     { { "GET", "/", "HTTP/1.1", NULL, 0, E_HOST, "a b=c&d", "" } } },
    500   { "q_frag",
    501     "'#' is not special for a server: it stays part of the argument",
    502     "GET /?a#frag HTTP/1.1" CRLF H_LOCAL CRLF, 0, 1, 1, 200, 1, 0,
    503     { { "GET", "/", "HTTP/1.1", NULL, 0, E_HOST, "a#frag=<NULL>", "" } } },
    504   { "q_plus",
    505     "'+' is decoded to a space in query arguments",
    506     "GET /?a+b=c+d HTTP/1.1" CRLF H_LOCAL CRLF, 0, 1, 1, 200, 1, 0,
    507     { { "GET", "/", "HTTP/1.1", NULL, 0, E_HOST, "a b=c d", "" } } },
    508   { "q_pct_url",
    509     "percent decoding of the path part",
    510     "GET /a%2Fb%20c HTTP/1.1" CRLF H_LOCAL CRLF, 0, 1, 1, 200, 1, 0,
    511     { { "GET", "/a/b c", "HTTP/1.1", NULL, 0, E_HOST, "", "" } } },
    512   /* The next two entries have *no* header line at all, so the last parsed
    513      element is a query argument with a NULL value.  With a connection memory
    514      limit below MHD_BUF_INC_SIZE this is the precondition of the read buffer
    515      "shift back" underflow fixed in 29eaa56b. */
    516   { "q_only_10_nohdr",
    517     "no headers at all + trailing arg without '=': read-buffer shift back",
    518     "GET /x?a HTTP/1.0" CRLF CRLF, 0, 1, 1, 200, 0, 1,
    519     { { "GET", "/x", "HTTP/1.0", NULL, 0, "", "a=<NULL>", "" } } },
    520   { "q_only_10_nohdr2",
    521     "same, but two NULL-valued args (tail element is the second one)",
    522     "GET /y?a&b HTTP/1.0" CRLF CRLF, 0, 1, 1, 200, 0, 1,
    523     { { "GET", "/y", "HTTP/1.0", NULL, 0, "", "a=<NULL>\nb=<NULL>", "" } } },
    524   { "q_only_10_nohdr_val",
    525     "control case: no headers but a trailing arg *with* '='",
    526     "GET /z?a=1 HTTP/1.0" CRLF CRLF, 0, 1, 1, 200, 0, 0,
    527     { { "GET", "/z", "HTTP/1.0", NULL, 0, "", "a=1", "" } } },
    528 
    529   /* ---- Content-Length bodies ------------------------------------ */
    530   { "cl_exact",
    531     "Content-Length body, delivered incrementally to the handler",
    532     "POST /p HTTP/1.1" CRLF H_LOCAL "Content-Length: 5" CRLF CRLF "Hello",
    533     0, 1, 1, 200, 1, 1,
    534     { { "POST", "/p", "HTTP/1.1", "Hello", 0,
    535         E_HOST "\nContent-Length=5", "", "" } } },
    536   { "cl_zero",
    537     "Content-Length: 0 must not wait for body bytes",
    538     "POST /p HTTP/1.1" CRLF H_LOCAL "Content-Length: 0" CRLF CRLF,
    539     0, 1, 1, 200, 1, 0,
    540     { { "POST", "/p", "HTTP/1.1", "", 0,
    541         E_HOST "\nContent-Length=0", "", "" } } },
    542   { "cl_long",
    543     "longer Content-Length body, forces several read-buffer rounds",
    544     "POST /p HTTP/1.1" CRLF H_LOCAL "Content-Length: 40" CRLF CRLF
    545     "0123456789abcdefghijklmnopqrstuvwxyz+-*/",
    546     0, 1, 1, 200, 1, 0,
    547     { { "POST", "/p", "HTTP/1.1",
    548         "0123456789abcdefghijklmnopqrstuvwxyz+-*/", 0,
    549         E_HOST "\nContent-Length=40", "", "" } } },
    550 
    551   /* ---- chunked bodies ------------------------------------------- */
    552   { "chunk_plain",
    553     "plain chunked body without extensions",
    554     "POST /c HTTP/1.1" CRLF H_LOCAL "Transfer-Encoding: chunked" CRLF CRLF
    555     "5" CRLF "Hello" CRLF "0" CRLF CRLF,
    556     0, 1, 1, 200, 1, 1,
    557     { { "POST", "/c", "HTTP/1.1", "Hello", 0,
    558         E_HOST "\nTransfer-Encoding=chunked", "", "" } } },
    559   /* The chunk-extension entries pin down that the CRLF that terminates the
    560      chunk size line is consumed *together with* the extension.  Failing to do
    561      so (bug fixed in c13f4c64) shifts the whole chunk body by two bytes and
    562      desynchronises the stream -> request smuggling. */
    563   { "chunk_ext_val",
    564     "chunk extension 'ext=val': CRLF after the extension must be consumed",
    565     "POST /c HTTP/1.1" CRLF H_LOCAL "Transfer-Encoding: chunked" CRLF CRLF
    566     "5;ext=val" CRLF "Hello" CRLF "0" CRLF CRLF,
    567     0, 1, 1, 200, 1, 1,
    568     { { "POST", "/c", "HTTP/1.1", "Hello", 0,
    569         E_HOST "\nTransfer-Encoding=chunked", "", "" } } },
    570   { "chunk_ext_noval",
    571     "chunk extension without a value",
    572     "POST /c HTTP/1.1" CRLF H_LOCAL "Transfer-Encoding: chunked" CRLF CRLF
    573     "5;ext" CRLF "Hello" CRLF "0" CRLF CRLF,
    574     0, 1, 1, 200, 1, 1,
    575     { { "POST", "/c", "HTTP/1.1", "Hello", 0,
    576         E_HOST "\nTransfer-Encoding=chunked", "", "" } } },
    577   { "chunk_ext_quoted",
    578     "quoted chunk extension value containing ';'",
    579     "POST /c HTTP/1.1" CRLF H_LOCAL "Transfer-Encoding: chunked" CRLF CRLF
    580     "5;ext=\"quoted;value\"" CRLF "Hello" CRLF "0" CRLF CRLF,
    581     0, 1, 1, 200, 1, 1,
    582     { { "POST", "/c", "HTTP/1.1", "Hello", 0,
    583         E_HOST "\nTransfer-Encoding=chunked", "", "" } } },
    584   { "chunk_ext_bws",
    585     "'bad whitespace' between chunk size and ';' (lenient mode)",
    586     "POST /c HTTP/1.1" CRLF H_LOCAL "Transfer-Encoding: chunked" CRLF CRLF
    587     "5 ;ext=val" CRLF "Hello" CRLF "0" CRLF CRLF,
    588     0, 1, 1, 200, 1, 1,
    589     { { "POST", "/c", "HTTP/1.1", "Hello", 0,
    590         E_HOST "\nTransfer-Encoding=chunked", "", "" } } },
    591   { "chunk_ext_last",
    592     "chunk extension on the terminating chunk",
    593     "POST /c HTTP/1.1" CRLF H_LOCAL "Transfer-Encoding: chunked" CRLF CRLF
    594     "5;a=b" CRLF "Hello" CRLF "0;z=y" CRLF CRLF,
    595     0, 1, 1, 200, 1, 1,
    596     { { "POST", "/c", "HTTP/1.1", "Hello", 0,
    597         E_HOST "\nTransfer-Encoding=chunked", "", "" } } },
    598   { "chunk_multi",
    599     "several chunks are concatenated into one upload body",
    600     "POST /c HTTP/1.1" CRLF H_LOCAL "Transfer-Encoding: chunked" CRLF CRLF
    601     "3" CRLF "abc" CRLF "2" CRLF "de" CRLF "1" CRLF "f" CRLF "0" CRLF CRLF,
    602     0, 1, 1, 200, 1, 1,
    603     { { "POST", "/c", "HTTP/1.1", "abcdef", 0,
    604         E_HOST "\nTransfer-Encoding=chunked", "", "" } } },
    605   { "chunk_lead_zeros",
    606     "chunk size with leading zeros",
    607     "POST /c HTTP/1.1" CRLF H_LOCAL "Transfer-Encoding: chunked" CRLF CRLF
    608     "0005" CRLF "Hello" CRLF "000" CRLF CRLF,
    609     0, 1, 1, 200, 1, 0,
    610     { { "POST", "/c", "HTTP/1.1", "Hello", 0,
    611         E_HOST "\nTransfer-Encoding=chunked", "", "" } } },
    612   { "chunk_trailers",
    613     "trailer (footer) fields after the terminating chunk",
    614     "POST /c HTTP/1.1" CRLF H_LOCAL "Transfer-Encoding: chunked" CRLF CRLF
    615     "5" CRLF "Hello" CRLF "0" CRLF "X-Trailer: v" CRLF "X-T2: w" CRLF CRLF,
    616     0, 1, 1, 200, 1, 1,
    617     { { "POST", "/c", "HTTP/1.1", "Hello", 0,
    618         E_HOST "\nTransfer-Encoding=chunked", "", "X-Trailer=v\nX-T2=w" } } },
    619   { "chunk_fold_trailer",
    620     "obs-fold continuation inside a trailer field",
    621     "POST /c HTTP/1.1" CRLF H_LOCAL "Transfer-Encoding: chunked" CRLF CRLF
    622     "5" CRLF "Hello" CRLF "0" CRLF "X-T: a" CRLF " b" CRLF CRLF,
    623     0, 1, 1, 200, 1, 1,
    624     { { "POST", "/c", "HTTP/1.1", "Hello", 0,
    625         E_HOST "\nTransfer-Encoding=chunked", "", "X-T=a   b" } } },
    626   { "chunk_ext_trailers",
    627     "chunk extension *and* trailers in the same message",
    628     "POST /c HTTP/1.1" CRLF H_LOCAL "Transfer-Encoding: chunked" CRLF CRLF
    629     "5;ext=val" CRLF "Hello" CRLF "0" CRLF "X-Trailer: v" CRLF CRLF,
    630     0, 1, 1, 200, 1, 1,
    631     { { "POST", "/c", "HTTP/1.1", "Hello", 0,
    632         E_HOST "\nTransfer-Encoding=chunked", "", "X-Trailer=v" } } },
    633   { "chunk_empty",
    634     "only the terminating chunk: empty upload body",
    635     "POST /c HTTP/1.1" CRLF H_LOCAL "Transfer-Encoding: chunked" CRLF CRLF
    636     "0" CRLF CRLF,
    637     0, 1, 1, 200, 1, 0,
    638     { { "POST", "/c", "HTTP/1.1", "", 0,
    639         E_HOST "\nTransfer-Encoding=chunked", "", "" } } },
    640 
    641   /* MHD keeps room for a chunk "header" of MHD_CHUNK_HEADER_REASONABLE_LEN
    642      (24) bytes in the read buffer; a longer chunk extension forces the
    643      buffer-grow path in the middle of the chunk size line. */
    644   { "chunk_ext_long",
    645     "chunk extension longer than the reserved chunk header space",
    646     "POST /c HTTP/1.1" CRLF H_LOCAL "Transfer-Encoding: chunked" CRLF CRLF
    647     "5;ext=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" CRLF "Hello" CRLF
    648     "0" CRLF CRLF,
    649     0, 1, 1, 200, 1, 1,
    650     { { "POST", "/c", "HTTP/1.1", "Hello", 0,
    651         E_HOST "\nTransfer-Encoding=chunked", "", "" } } },
    652   { "chunk_multi_ext",
    653     "a chunk extension on every single chunk",
    654     "POST /c HTTP/1.1" CRLF H_LOCAL "Transfer-Encoding: chunked" CRLF CRLF
    655     "3;a=1" CRLF "abc" CRLF "2;b=2" CRLF "de" CRLF "1;c" CRLF "f" CRLF
    656     "0;d=4" CRLF CRLF,
    657     0, 1, 1, 200, 1, 1,
    658     { { "POST", "/c", "HTTP/1.1", "abcdef", 0,
    659         E_HOST "\nTransfer-Encoding=chunked", "", "" } } },
    660 
    661   /* ---- pipelining / desynchronisation --------------------------- */
    662   { "pipe2",
    663     "two pipelined requests must yield exactly two parsed requests",
    664     "GET /1 HTTP/1.1" CRLF H_LOCAL CRLF
    665     "GET /2 HTTP/1.1" CRLF H_LOCAL CRLF,
    666     0, 2, 2, 200, 1, 1,
    667     { { "GET", "/1", "HTTP/1.1", NULL, 0, E_HOST, "", "" },
    668       { "GET", "/2", "HTTP/1.1", NULL, 0, E_HOST, "", "" } } },
    669   { "pipe3",
    670     "three pipelined requests, never two and never four",
    671     "GET /1 HTTP/1.1" CRLF H_LOCAL CRLF
    672     "GET /2?x=1 HTTP/1.1" CRLF H_LOCAL CRLF
    673     "GET /3 HTTP/1.1" CRLF H_LOCAL CRLF,
    674     0, 3, 3, 200, 1, 0,
    675     { { "GET", "/1", "HTTP/1.1", NULL, 0, E_HOST, "", "" },
    676       { "GET", "/2", "HTTP/1.1", NULL, 0, E_HOST, "x=1", "" },
    677       { "GET", "/3", "HTTP/1.1", NULL, 0, E_HOST, "", "" } } },
    678   { "pipe_chunk_get",
    679     "chunked POST followed by a GET: the smuggling detector",
    680     "POST /c HTTP/1.1" CRLF H_LOCAL "Transfer-Encoding: chunked" CRLF CRLF
    681     "5" CRLF "Hello" CRLF "0" CRLF CRLF
    682     "GET /after HTTP/1.1" CRLF H_LOCAL CRLF,
    683     0, 2, 2, 200, 1, 1,
    684     { { "POST", "/c", "HTTP/1.1", "Hello", 0,
    685         E_HOST "\nTransfer-Encoding=chunked", "", "" },
    686       { "GET", "/after", "HTTP/1.1", NULL, 0, E_HOST, "", "" } } },
    687   { "pipe_chunkext_get",
    688     "chunk *extension* + pipelined GET: smuggling detector for c13f4c64",
    689     "POST /c HTTP/1.1" CRLF H_LOCAL "Transfer-Encoding: chunked" CRLF CRLF
    690     "5;ext=val" CRLF "Hello" CRLF "0" CRLF CRLF
    691     "GET /after HTTP/1.1" CRLF H_LOCAL CRLF,
    692     0, 2, 2, 200, 1, 1,
    693     { { "POST", "/c", "HTTP/1.1", "Hello", 0,
    694         E_HOST "\nTransfer-Encoding=chunked", "", "" },
    695       { "GET", "/after", "HTTP/1.1", NULL, 0, E_HOST, "", "" } } },
    696   { "pipe_cl_get",
    697     "Content-Length POST followed by a GET",
    698     "POST /p HTTP/1.1" CRLF H_LOCAL "Content-Length: 5" CRLF CRLF "Hello"
    699     "GET /after HTTP/1.1" CRLF H_LOCAL CRLF,
    700     0, 2, 2, 200, 1, 0,
    701     { { "POST", "/p", "HTTP/1.1", "Hello", 0,
    702         E_HOST "\nContent-Length=5", "", "" },
    703       { "GET", "/after", "HTTP/1.1", NULL, 0, E_HOST, "", "" } } },
    704 
    705   /* ---- header line edge cases ----------------------------------- */
    706   { "hdr_obs_fold",
    707     "obs-fold continuation: CR, LF and the fold space each become one space",
    708     "GET / HTTP/1.1" CRLF H_LOCAL "X-Fold: one" CRLF " two" CRLF CRLF,
    709     0, 1, 1, 200, 1, 1,
    710     { { "GET", "/", "HTTP/1.1", NULL, 0,
    711         E_HOST "\nX-Fold=one   two", "", "" } } },
    712   /* Note: the CR and the LF of the folded line are each replaced by a space
    713      and the leading whitespace of the continuation line is kept verbatim, so
    714      a TAB survives as a TAB. */
    715   { "hdr_fold_multi",
    716     "two consecutive obs-fold continuation lines, second one folded with TAB",
    717     "GET / HTTP/1.1" CRLF H_LOCAL "X-F: one" CRLF " two" CRLF "\ttree" CRLF
    718     CRLF,
    719     0, 1, 1, 200, 1, 1,
    720     { { "GET", "/", "HTTP/1.1", NULL, 0,
    721         E_HOST "\nX-F=one   two  \\x09tree", "", "" } } },
    722   { "hdr_empty_val",
    723     "header with an empty value",
    724     "GET / HTTP/1.1" CRLF H_LOCAL "X-Empty:" CRLF CRLF,
    725     0, 1, 1, 200, 1, 0,
    726     { { "GET", "/", "HTTP/1.1", NULL, 0, E_HOST "\nX-Empty=", "", "" } } },
    727   { "hdr_dup",
    728     "duplicated header names are reported twice, in order",
    729     "GET / HTTP/1.1" CRLF H_LOCAL "X-Dup: 1" CRLF "X-Dup: 2" CRLF CRLF,
    730     0, 1, 1, 200, 1, 0,
    731     { { "GET", "/", "HTTP/1.1", NULL, 0,
    732         E_HOST "\nX-Dup=1\nX-Dup=2", "", "" } } },
    733   { "hdr_bare_lf",
    734     "bare LF line terminators (lenient mode) must parse identically",
    735     "GET / HTTP/1.1\n" H_LOCAL "X-B: v\n\n",
    736     0, 1, 1, 200, 1, 1,
    737     { { "GET", "/", "HTTP/1.1", NULL, 0, E_HOST "\nX-B=v", "", "" } } },
    738   { "hdr_bare_cr",
    739     "a bare CR inside a header value is rejected with 400 at the default "
    740     "client discipline level",
    741     "GET / HTTP/1.1" CRLF H_LOCAL "X-Cr: a\rb" CRLF CRLF,
    742     0, 0, 1, 400, 0, 1,
    743     { { NULL, NULL, NULL, NULL, 0, NULL, NULL, NULL } } },
    744   { "hdr_trail_ws",
    745     "trailing whitespace in a header value must be stripped",
    746     "GET / HTTP/1.1" CRLF H_LOCAL "X-Ws: val   " CRLF CRLF,
    747     0, 1, 1, 200, 1, 0,
    748     { { "GET", "/", "HTTP/1.1", NULL, 0, E_HOST "\nX-Ws=val", "", "" } } },
    749   { "hdr_lead_ws",
    750     "several spaces after the colon are not part of the value",
    751     "GET / HTTP/1.1" CRLF H_LOCAL "X-Lw:    val" CRLF CRLF,
    752     0, 1, 1, 200, 1, 0,
    753     { { "GET", "/", "HTTP/1.1", NULL, 0, E_HOST "\nX-Lw=val", "", "" } } },
    754   { "hdr_many",
    755     "many small headers: exercises pool allocation and buffer growth",
    756     "GET / HTTP/1.1" CRLF H_LOCAL
    757     "A: 1" CRLF "B: 2" CRLF "C: 3" CRLF "D: 4" CRLF "E: 5" CRLF
    758     "F: 6" CRLF "G: 7" CRLF "H: 8" CRLF CRLF,
    759     0, 1, 1, 200, 1, 0,
    760     { { "GET", "/", "HTTP/1.1", NULL, 0,
    761         E_HOST "\nA=1\nB=2\nC=3\nD=4\nE=5\nF=6\nG=7\nH=8", "", "" } } },
    762   /* Bare LF is accepted as a line terminator in the request header but NOT
    763      inside the chunked framing (process_request_body() requires discipline
    764      level < -2 for that), so this must be a clean 400 - and it must be a 400
    765      no matter where the stream is cut. */
    766   { "hdr_bare_lf_chunk",
    767     "bare LF inside the chunked framing is rejected with 400",
    768     "POST /c HTTP/1.1\n" H_LOCAL "Transfer-Encoding: chunked\n\n"
    769     "5;ext=val\nHello\n0\n\n",
    770     0, 0, 1, 400, 0, 1,
    771     { { NULL, NULL, NULL, NULL, 0, NULL, NULL, NULL } } }
    772 };
    773 
    774 #define CORPUS_SIZE (sizeof(corpus) / sizeof(corpus[0]))
    775 
    776 /**
    777  * Sweep of #MHD_OPTION_CONNECTION_MEMORY_LIMIT values; 0 means "use the MHD
    778  * default".  Everything below #MHD_BUF_INC_SIZE (1500) unlocks read-buffer
    779  * code paths that are otherwise never taken.
    780  */
    781 static const size_t mem_limits[] = { 256, 512, 1024, 1499, 4096, 0 };
    782 
    783 #define MEM_LIMITS_SIZE (sizeof(mem_limits) / sizeof(mem_limits[0]))
    784 
    785 /**
    786  * The range of client discipline levels in which the declared expectation of
    787  * a corpus entry holds.
    788  *
    789  * Every entry that is not listed here is level independent.  The bounds
    790  * follow the predicates of connection.c directly:
    791  *
    792  * * a bare LF is a line terminator only while `MHD_ALLOW_BARE_LF_AS_CRLF_`
    793  *   (0 >= level) holds, and an obs-fold continuation only while
    794  *   `allow_folded` (0 >= level) holds, so both are meaningless above 0;
    795  * * `allow_bws` is (2 > level), so the "bad whitespace" chunk-size line is
    796  *   meaningless above 1;
    797  * * a bare CR is turned into a space by `bare_cr_as_sp` (-1 >= level) and
    798  *   kept by `bare_cr_keep` (-3 >= level), so the entry that expects a 400
    799  *   only holds from level 0 upwards;
    800  * * inside the chunked framing `bare_lf_as_crlf` is (-2 > level), so the
    801  *   entry that expects a 400 for a bare LF there only holds from -2 upwards.
    802  */
    803 struct entry_discp_limit
    804 {
    805   const char *name;
    806   int min_discp;
    807   int max_discp;
    808 };
    809 
    810 static const struct entry_discp_limit discp_limits[] = {
    811   { "hdr_bare_lf",        -3, 0 },
    812   { "hdr_obs_fold",       -3, 0 },
    813   { "hdr_fold_multi",     -3, 0 },
    814   { "chunk_fold_trailer", -3, 0 },
    815   { "chunk_ext_bws",      -3, 1 },
    816   { "hdr_bare_cr",         0, 3 },
    817   { "hdr_bare_lf_chunk",  -2, 3 }
    818 };
    819 
    820 #define DISCP_LIMITS_SIZE \
    821         (sizeof(discp_limits) / sizeof(discp_limits[0]))
    822 
    823 /**
    824  * The profile selected by the environment, NULL if the built-in sweep is
    825  * used (which is the case for a stock "make check").
    826  */
    827 static const struct MHD_OptMatrixProfile *test_prof;
    828 
    829 /**
    830  * The (possibly adjusted) copy of the profile @a test_prof points to.
    831  */
    832 static struct MHD_OptMatrixProfile test_prof_copy;
    833 
    834 /**
    835  * The client discipline level really in effect, see
    836  * mhd_opt_matrix_effective_discipline().
    837  */
    838 static int test_discp;
    839 
    840 
    841 /**
    842  * Check whether the declared expectation of @a name holds at the client
    843  * discipline level currently in effect.
    844  *
    845  * @param name the name of the corpus entry
    846  * @return non-zero if the entry may be run
    847  */
    848 static int
    849 entry_in_discp_range (const char *name)
    850 {
    851   size_t i;
    852 
    853   for (i = 0; i < DISCP_LIMITS_SIZE; ++i)
    854   {
    855     if (0 == strcmp (name, discp_limits[i].name))
    856       return ((test_discp >= discp_limits[i].min_discp) &&
    857               (test_discp <= discp_limits[i].max_discp)) ? 1 : 0;
    858   }
    859   return 1;
    860 }
    861 
    862 
    863 /* ------------------------------------------------------------------ */
    864 /* Global state                                                       */
    865 /* ------------------------------------------------------------------ */
    866 
    867 static int verbose;
    868 
    869 /** Current corpus entry under test (the test is strictly serialised). */
    870 static const struct corpus_entry *cur_entry;
    871 /** Human readable description of the current split. */
    872 static char cur_split[128];
    873 /** Current connection memory limit. */
    874 static size_t cur_mem_limit;
    875 /** Non-zero while the (slow) three-way split pass is enabled. */
    876 static int deep_enabled;
    877 
    878 static uint16_t global_port;
    879 
    880 /** Server side observations of the connection currently under test. */
    881 static struct obs_buf srv_obs;
    882 /** Number of completed requests seen by the request handler. */
    883 static volatile unsigned int srv_n_req;
    884 /** Set if the handler noticed something structurally impossible. */
    885 static volatile unsigned int srv_error;
    886 
    887 static volatile unsigned int conn_started;
    888 static volatile unsigned int conn_closed;
    889 
    890 /** Total number of (entry x split x memory limit) combinations executed. */
    891 static unsigned long combos;
    892 /** Number of combinations that could not fit into the memory pool. */
    893 static unsigned long combos_pool_limited;
    894 /** Number of failures found. */
    895 static unsigned long failures;
    896 
    897 
    898 static void
    899 print_context (FILE *out)
    900 {
    901   fprintf (out, "  context: entry='%s' split=%s mem_limit=",
    902            (NULL != cur_entry) ? cur_entry->name : "(none)",
    903            cur_split);
    904   if (0 == cur_mem_limit)
    905     fprintf (out, "default\n");
    906   else
    907     fprintf (out, "%u\n", (unsigned int) cur_mem_limit);
    908   if (NULL != cur_entry)
    909     fprintf (out, "  entry purpose: %s\n", cur_entry->desc);
    910 }
    911 
    912 
    913 #if defined(HAVE_SIGNAL_H) && defined(SIGABRT)
    914 /**
    915  * Report the corpus entry and split offset when the library aborts (e.g.
    916  * from an internal assertion), instead of dying anonymously.
    917  */
    918 static void
    919 abort_handler (int sig)
    920 {
    921   (void) sig;
    922   fprintf (stderr,
    923            "\nFATAL: the library aborted while running a corpus entry.\n");
    924   print_context (stderr);
    925   fflush (stderr);
    926   _exit (1);
    927 }
    928 
    929 
    930 #endif /* HAVE_SIGNAL_H && SIGABRT */
    931 
    932 
    933 /**
    934  * Panic callback: a library panic must be reported as a test failure that
    935  * names the corpus entry and split offset.
    936  */
    937 static void
    938 test_panic (void *cls, const char *file, unsigned int line, const char *reason)
    939 {
    940   (void) cls;
    941   fprintf (stderr,
    942            "\nFATAL: MHD panic at %s:%u: %s\n",
    943            (NULL != file) ? file : "(unknown)",
    944            line,
    945            (NULL != reason) ? reason : "(no reason given)");
    946   print_context (stderr);
    947   fflush (stderr);
    948   _exit (1);
    949 }
    950 
    951 
    952 /* ------------------------------------------------------------------ */
    953 /* Request handler: records the observations                          */
    954 /* ------------------------------------------------------------------ */
    955 
    956 struct req_state
    957 {
    958   int marker;
    959   unsigned int idx;
    960   struct obs_buf body;
    961 };
    962 
    963 
    964 struct iter_ctx
    965 {
    966   struct obs_buf *out;
    967   unsigned int num;
    968 };
    969 
    970 
    971 static enum MHD_Result
    972 value_iterator (void *cls,
    973                 enum MHD_ValueKind kind,
    974                 const char *key,
    975                 size_t key_size,
    976                 const char *value,
    977                 size_t value_size)
    978 {
    979   struct iter_ctx *const ctx = (struct iter_ctx *) cls;
    980 
    981   (void) kind;
    982   if (0 != ctx->num)
    983     obs_addf (ctx->out, "\n");
    984   ctx->num++;
    985   if (NULL != key)
    986     obs_add_escaped (ctx->out, key, key_size);
    987   else
    988     obs_addf (ctx->out, "<NULLKEY>");
    989   obs_addf (ctx->out, "=");
    990   if (NULL == value)
    991     obs_addf (ctx->out, "<NULL>");
    992   else
    993     obs_add_escaped (ctx->out, value, value_size);
    994   return MHD_YES;
    995 }
    996 
    997 
    998 static void
    999 dump_values (struct MHD_Connection *c,
   1000              enum MHD_ValueKind kind,
   1001              const char *label,
   1002              struct obs_buf *out)
   1003 {
   1004   struct iter_ctx ctx;
   1005 
   1006   ctx.out = out;
   1007   ctx.num = 0;
   1008   obs_addf (out, "  %s=[", label);
   1009   MHD_get_connection_values_n (c, kind, &value_iterator, &ctx);
   1010   obs_addf (out, "]\n");
   1011 }
   1012 
   1013 
   1014 static enum MHD_Result
   1015 ahc_record (void *cls,
   1016             struct MHD_Connection *connection,
   1017             const char *url,
   1018             const char *method,
   1019             const char *version,
   1020             const char *upload_data,
   1021             size_t *upload_data_size,
   1022             void **req_cls)
   1023 {
   1024   /* The reply body is intentionally empty: that makes the reply of a HEAD
   1025      request byte-identical to the reply of a GET request and keeps the
   1026      client side response framing trivial. */
   1027   static const char rp_data[] = "";
   1028   struct req_state *rs;
   1029   struct MHD_Response *response;
   1030   enum MHD_Result ret;
   1031 
   1032   (void) cls;
   1033   if (NULL == *req_cls)
   1034   {
   1035     rs = (struct req_state *) malloc (sizeof (struct req_state));
   1036     if (NULL == rs)
   1037       externalErrorExitDesc ("malloc() failed");
   1038     rs->marker = 1;
   1039     rs->idx = srv_n_req;
   1040     obs_reset (&rs->body);
   1041     *req_cls = rs;
   1042     return MHD_YES;
   1043   }
   1044   rs = (struct req_state *) *req_cls;
   1045   if (0 != *upload_data_size)
   1046   {
   1047     obs_add_escaped (&rs->body, upload_data, *upload_data_size);
   1048     *upload_data_size = 0;
   1049     return MHD_YES;
   1050   }
   1051 
   1052   /* The request is complete: record everything we can observe. */
   1053   obs_addf (&srv_obs, "REQ %u\n", rs->idx);
   1054   obs_addf (&srv_obs, "  method=%s\n", (NULL != method) ? method : "(null)");
   1055   obs_addf (&srv_obs, "  url=");
   1056   if (NULL != url)
   1057     obs_add_escaped (&srv_obs, url, strlen (url));
   1058   else
   1059     obs_addf (&srv_obs, "(null)");
   1060   obs_addf (&srv_obs, "\n");
   1061   obs_addf (&srv_obs, "  version=%s\n",
   1062             (NULL != version) ? version : "(null)");
   1063   obs_addf (&srv_obs, "  body=[%s]\n", rs->body.d);
   1064   dump_values (connection, MHD_HEADER_KIND, "hdr", &srv_obs);
   1065   dump_values (connection, MHD_GET_ARGUMENT_KIND, "arg", &srv_obs);
   1066   dump_values (connection, MHD_FOOTER_KIND, "foot", &srv_obs);
   1067 
   1068   free (rs);
   1069   *req_cls = NULL;
   1070   srv_n_req++;
   1071 
   1072   response =
   1073     MHD_create_response_from_buffer (MHD_STATICSTR_LEN_ (rp_data),
   1074                                      (void *) rp_data,
   1075                                      MHD_RESPMEM_PERSISTENT);
   1076   if (NULL == response)
   1077   {
   1078     srv_error++;
   1079     return MHD_NO;
   1080   }
   1081   ret = MHD_queue_response (connection, MHD_HTTP_OK, response);
   1082   MHD_destroy_response (response);
   1083   if (MHD_YES != ret)
   1084     srv_error++;
   1085   return ret;
   1086 }
   1087 
   1088 
   1089 /**
   1090  * MHD is started with #MHD_USE_ERROR_LOG so that library complaints are
   1091  * available, but they are only printed with '-v -v' to keep the (very
   1092  * repetitive) output of the sweep readable.
   1093  */
   1094 static void
   1095 test_log (void *cls, const char *fmt, va_list ap)
   1096 {
   1097   (void) cls;
   1098   if (1 < verbose)
   1099   {
   1100     vfprintf (stderr, fmt, ap);
   1101     fflush (stderr);
   1102   }
   1103 }
   1104 
   1105 
   1106 static void
   1107 conn_notify (void *cls,
   1108              struct MHD_Connection *c,
   1109              void **socket_context,
   1110              enum MHD_ConnectionNotificationCode toe)
   1111 {
   1112   (void) cls; (void) c; (void) socket_context;
   1113   if (MHD_CONNECTION_NOTIFY_STARTED == toe)
   1114     conn_started++;
   1115   else if (MHD_CONNECTION_NOTIFY_CLOSED == toe)
   1116     conn_closed++;
   1117 }
   1118 
   1119 
   1120 static void
   1121 req_completed (void *cls,
   1122                struct MHD_Connection *c,
   1123                void **req_cls,
   1124                enum MHD_RequestTerminationCode term_code)
   1125 {
   1126   (void) cls; (void) c; (void) term_code;
   1127   if ((NULL != req_cls) && (NULL != *req_cls))
   1128   {
   1129     free (*req_cls);
   1130     *req_cls = NULL;
   1131   }
   1132 }
   1133 
   1134 
   1135 /* ------------------------------------------------------------------ */
   1136 /* Raw client                                                         */
   1137 /* ------------------------------------------------------------------ */
   1138 
   1139 struct client_result
   1140 {
   1141   unsigned int n_resp;
   1142   int first_status;
   1143   int peer_closed;
   1144   int timed_out;
   1145   size_t rx_len;
   1146   char rx[RX_SIZE];
   1147 };
   1148 
   1149 
   1150 /**
   1151  * Case-insensitive comparison of @a size bytes (ASCII only).
   1152  */
   1153 static int
   1154 mem_equal_ci (const char *a, const char *b, size_t size)
   1155 {
   1156   size_t i;
   1157 
   1158   for (i = 0; i < size; ++i)
   1159   {
   1160     char ca = a[i];
   1161     char cb = b[i];
   1162 
   1163     if (('A' <= ca) && ('Z' >= ca))
   1164       ca = (char) (ca - 'A' + 'a');
   1165     if (('A' <= cb) && ('Z' >= cb))
   1166       cb = (char) (cb - 'A' + 'a');
   1167     if (ca != cb)
   1168       return 0;
   1169   }
   1170   return 1;
   1171 }
   1172 
   1173 
   1174 /**
   1175  * Count the number of complete HTTP responses in @a buf.
   1176  *
   1177  * @param buf the received bytes
   1178  * @param len the number of bytes in @a buf
   1179  * @param[out] first_status set to the status code of the first response
   1180  * @return the number of *complete* responses found
   1181  */
   1182 static unsigned int
   1183 count_responses (const char *buf, size_t len, int *first_status)
   1184 {
   1185   size_t pos = 0;
   1186   unsigned int num = 0;
   1187 
   1188   *first_status = 0;
   1189   while (pos < len)
   1190   {
   1191     const char *hdr_end;
   1192     const char *cl;
   1193     size_t hdr_len;
   1194     size_t body_len = 0;
   1195     int status;
   1196     size_t i;
   1197 
   1198     if (7 > len - pos)
   1199       break;
   1200     if (0 != memcmp (buf + pos, "HTTP/1.", 7))
   1201       break; /* Not a well-formed response */
   1202     hdr_end = NULL;
   1203     for (i = pos; i + 3 < len; ++i)
   1204     {
   1205       if (('\r' == buf[i]) && ('\n' == buf[i + 1]) &&
   1206           ('\r' == buf[i + 2]) && ('\n' == buf[i + 3]))
   1207       {
   1208         hdr_end = buf + i + 4;
   1209         break;
   1210       }
   1211     }
   1212     if (NULL == hdr_end)
   1213       break; /* Incomplete header */
   1214     hdr_len = (size_t) (hdr_end - (buf + pos));
   1215     status = 0;
   1216     if ((pos + 12 <= len) &&
   1217         ('0' <= buf[pos + 9]) && ('9' >= buf[pos + 9]))
   1218       status = (buf[pos + 9] - '0') * 100
   1219                + (buf[pos + 10] - '0') * 10
   1220                + (buf[pos + 11] - '0');
   1221     if (0 == num)
   1222       *first_status = status;
   1223     /* Find "Content-Length:" inside this response header. */
   1224     cl = NULL;
   1225     for (i = pos; i + 15 <= pos + hdr_len; ++i)
   1226     {
   1227       if (mem_equal_ci (buf + i, "content-length:", 15))
   1228       {
   1229         cl = buf + i + 15;
   1230         break;
   1231       }
   1232     }
   1233     if (NULL != cl)
   1234     {
   1235       while ((cl < buf + pos + hdr_len) && (' ' == *cl))
   1236         cl++;
   1237       body_len = 0;
   1238       while ((cl < buf + pos + hdr_len) && ('0' <= *cl) && ('9' >= *cl))
   1239       {
   1240         body_len = body_len * 10 + (size_t) (*cl - '0');
   1241         cl++;
   1242       }
   1243     }
   1244     if (pos + hdr_len + body_len > len)
   1245       break; /* Body incomplete */
   1246     pos += hdr_len + body_len;
   1247     num++;
   1248   }
   1249   return num;
   1250 }
   1251 
   1252 
   1253 static MHD_socket
   1254 client_connect (void)
   1255 {
   1256   MHD_socket sk;
   1257   struct sockaddr_in sa;
   1258   const MHD_SCKT_OPT_BOOL_ on_val = 1;
   1259 
   1260   sk = socket (AF_INET, SOCK_STREAM, IPPROTO_TCP);
   1261   if (MHD_INVALID_SOCKET == sk)
   1262     externalErrorExitDesc ("Cannot create the client socket");
   1263 #ifdef MHD_socket_nosignal_
   1264   if (! MHD_socket_nosignal_ (sk))
   1265     externalErrorExitDesc ("Cannot suppress SIGPIPE on the client socket");
   1266 #endif /* MHD_socket_nosignal_ */
   1267   if (0 != setsockopt (sk, IPPROTO_TCP, TCP_NODELAY,
   1268                        (const void *) &on_val, sizeof (on_val)))
   1269     externalErrorExitDesc ("Cannot set TCP_NODELAY option");
   1270 
   1271   memset (&sa, 0, sizeof (sa));
   1272   sa.sin_family = AF_INET;
   1273   sa.sin_port = htons (global_port);
   1274   sa.sin_addr.s_addr = htonl (INADDR_LOOPBACK);
   1275   if (0 != connect (sk, (struct sockaddr *) &sa, sizeof (sa)))
   1276     externalErrorExitDesc ("Cannot connect() to the daemon");
   1277   return sk;
   1278 }
   1279 
   1280 
   1281 static int
   1282 client_send_all (MHD_socket sk, const char *data, size_t size)
   1283 {
   1284   size_t off = 0;
   1285 
   1286   while (off < size)
   1287   {
   1288     ssize_t s = MHD_send_ (sk, data + off, size - off);
   1289 
   1290     if (0 > s)
   1291     {
   1292       const int err = MHD_socket_get_error_ ();
   1293 
   1294       if (MHD_SCKT_ERR_IS_EINTR_ (err) || MHD_SCKT_ERR_IS_EAGAIN_ (err))
   1295         continue;
   1296       return 0; /* Server closed the connection early, that is an observation */
   1297     }
   1298     off += (size_t) s;
   1299   }
   1300   return 1;
   1301 }
   1302 
   1303 
   1304 /**
   1305  * Wait for a socket to become readable.
   1306  *
   1307  * @return 1 readable, 0 timeout, -1 error
   1308  */
   1309 static int
   1310 wait_readable (MHD_socket sk, unsigned int timeout_ms)
   1311 {
   1312   fd_set rs;
   1313   struct timeval tv;
   1314   int r;
   1315 
   1316   FD_ZERO (&rs);
   1317   FD_SET (sk, &rs);
   1318   tv.tv_sec = (time_t) (timeout_ms / 1000);
   1319   tv.tv_usec = (long) ((timeout_ms % 1000) * 1000);
   1320   r = select ((int) sk + 1, &rs, NULL, NULL, &tv);
   1321   if (0 > r)
   1322   {
   1323     if (EINTR == errno)
   1324       return 0;
   1325     return -1;
   1326   }
   1327   return (0 == r) ? 0 : 1;
   1328 }
   1329 
   1330 
   1331 /**
   1332  * Read the server answer.
   1333  *
   1334  * @param sk the socket to read from
   1335  * @param[out] r the result
   1336  * @param min_resp stop once that many complete responses have been read
   1337  * @param want_close keep reading until the peer closes the connection
   1338  * @param grace_ms extra silence to wait for after @a min_resp responses
   1339  */
   1340 static void
   1341 client_read (MHD_socket sk,
   1342              struct client_result *r,
   1343              unsigned int min_resp,
   1344              int want_close,
   1345              unsigned int grace_ms)
   1346 {
   1347   const uint64_t start = now_ms ();
   1348 
   1349   memset (r, 0, sizeof (*r));
   1350   while (1)
   1351   {
   1352     const uint64_t el = now_ms () - start;
   1353     unsigned int tmo;
   1354     int wr;
   1355     ssize_t got;
   1356 
   1357     if (el >= READ_DEADLINE_MS)
   1358     {
   1359       r->timed_out = 1;
   1360       break;
   1361     }
   1362     r->n_resp = count_responses (r->rx, r->rx_len, &r->first_status);
   1363     if ((r->n_resp >= min_resp) && (! want_close))
   1364       tmo = grace_ms;
   1365     else
   1366       tmo = (unsigned int) (READ_DEADLINE_MS - el);
   1367     wr = wait_readable (sk, tmo);
   1368     if (0 > wr)
   1369     {
   1370       r->peer_closed = 1;
   1371       break;
   1372     }
   1373     if (0 == wr)
   1374     {
   1375       if ((r->n_resp >= min_resp) && (! want_close))
   1376         break; /* Done, connection stays alive */
   1377       r->timed_out = 1;
   1378       break;
   1379     }
   1380     if (r->rx_len >= sizeof (r->rx))
   1381       break; /* Way too much data, will be reported as a mismatch */
   1382     got = MHD_recv_ (sk, r->rx + r->rx_len, sizeof (r->rx) - r->rx_len);
   1383     if (0 > got)
   1384     {
   1385       const int err = MHD_socket_get_error_ ();
   1386 
   1387       if (MHD_SCKT_ERR_IS_EINTR_ (err) || MHD_SCKT_ERR_IS_EAGAIN_ (err))
   1388         continue;
   1389       r->peer_closed = 1; /* Connection reset counts as "closed" */
   1390       break;
   1391     }
   1392     if (0 == got)
   1393     {
   1394       r->peer_closed = 1;
   1395       break;
   1396     }
   1397     r->rx_len += (size_t) got;
   1398   }
   1399   r->n_resp = count_responses (r->rx, r->rx_len, &r->first_status);
   1400 }
   1401 
   1402 
   1403 /**
   1404  * Wait until the daemon has finished tearing down all connections, so that
   1405  * the server side observations are stable.
   1406  *
   1407  * @return 1 on success, 0 on timeout
   1408  */
   1409 static int
   1410 wait_conns_closed (void)
   1411 {
   1412   const uint64_t start = now_ms ();
   1413   unsigned int spin = 0;
   1414 
   1415   while (conn_closed != conn_started)
   1416   {
   1417     if (1000 > spin)
   1418       spin++;
   1419     else
   1420       _MHD_sleep (1);
   1421     if (now_ms () - start > CLOSE_WAIT_MS)
   1422       return 0;
   1423   }
   1424   return 1;
   1425 }
   1426 
   1427 
   1428 /* ------------------------------------------------------------------ */
   1429 /* One replay of one corpus entry                                     */
   1430 /* ------------------------------------------------------------------ */
   1431 
   1432 /**
   1433  * Send @a e over a fresh connection, cut into @a n_cuts + 1 pieces at the
   1434  * offsets given in @a cuts, and render all observations into @a out.
   1435  */
   1436 static void
   1437 run_stream (const struct corpus_entry *e,
   1438             const size_t *cuts,
   1439             unsigned int n_cuts,
   1440             unsigned int min_resp,
   1441             int want_close,
   1442             unsigned int grace_ms,
   1443             struct obs_buf *out)
   1444 {
   1445   const size_t len = (0 != e->raw_len) ? e->raw_len : strlen (e->raw);
   1446   struct client_result cr;
   1447   MHD_socket sk;
   1448   size_t off = 0;
   1449   unsigned int i;
   1450   int send_ok = 1;
   1451 
   1452   obs_reset (&srv_obs);
   1453   srv_n_req = 0;
   1454   srv_error = 0;
   1455 
   1456   sk = client_connect ();
   1457   for (i = 0; i <= n_cuts; ++i)
   1458   {
   1459     const size_t end = (i < n_cuts) ? cuts[i] : len;
   1460 
   1461     if (end > off)
   1462     {
   1463       if (! client_send_all (sk, e->raw + off, end - off))
   1464       {
   1465         send_ok = 0;
   1466         break;
   1467       }
   1468       off = end;
   1469     }
   1470     if (i < n_cuts)
   1471       _MHD_sleep (SPLIT_DELAY_MS);
   1472   }
   1473   client_read (sk, &cr, min_resp, want_close, grace_ms);
   1474   (void) MHD_socket_close_ (sk);
   1475   if (! wait_conns_closed ())
   1476   {
   1477     fprintf (stderr, "WARNING: timeout waiting for connection teardown.\n");
   1478     print_context (stderr);
   1479   }
   1480 
   1481   obs_reset (out);
   1482   obs_addf (out, "%s", srv_obs.d);
   1483   obs_addf (out, "nreq=%u\n", srv_n_req);
   1484   obs_addf (out, "nresp=%u status=%d closed=%d\n",
   1485             cr.n_resp, cr.first_status, cr.peer_closed);
   1486   if (cr.timed_out)
   1487     obs_addf (out, "TIMED_OUT\n");
   1488   /* Note: whether the *client* managed to push out the rest of the request
   1489      before MHD closed the connection is inherently racy, so it must not be
   1490      part of the compared observation; 'closed=' already carries the relevant
   1491      information.  It is only reported in verbose mode. */
   1492   if ((! send_ok) && (1 < verbose))
   1493   {
   1494     printf ("    (send aborted by the server for entry '%s' split %s)\n",
   1495             e->name, cur_split);
   1496     fflush (stdout);
   1497   }
   1498   if (0 != srv_error)
   1499     obs_addf (out, "SERVER_ERROR=%u\n", srv_error);
   1500   if (srv_obs.overflow || out->overflow)
   1501     obs_addf (out, "OBS_OVERFLOW\n");
   1502 }
   1503 
   1504 
   1505 /**
   1506  * Render the declared expectations of @a e into the very same format that
   1507  * run_stream() produces, so that a single strcmp() does the whole check.
   1508  */
   1509 static void
   1510 build_expected (const struct corpus_entry *e, struct obs_buf *out)
   1511 {
   1512   unsigned int i;
   1513 
   1514   obs_reset (out);
   1515   for (i = 0; i < e->n_req; ++i)
   1516   {
   1517     const struct exp_req *const r = &e->reqs[i];
   1518     size_t bl;
   1519 
   1520     obs_addf (out, "REQ %u\n", i);
   1521     obs_addf (out, "  method=%s\n", r->method);
   1522     obs_addf (out, "  url=");
   1523     obs_add_escaped (out, r->url, strlen (r->url));
   1524     obs_addf (out, "\n");
   1525     obs_addf (out, "  version=%s\n", r->version);
   1526     obs_addf (out, "  body=[");
   1527     if (NULL != r->body)
   1528     {
   1529       bl = (0 != r->body_len) ? r->body_len : strlen (r->body);
   1530       obs_add_escaped (out, r->body, bl);
   1531     }
   1532     obs_addf (out, "]\n");
   1533     obs_addf (out, "  hdr=[%s]\n", r->hdrs);
   1534     obs_addf (out, "  arg=[%s]\n", r->args);
   1535     obs_addf (out, "  foot=[%s]\n", r->foot);
   1536   }
   1537   obs_addf (out, "nreq=%u\n", e->n_req);
   1538   obs_addf (out, "nresp=%u status=%d closed=%d\n",
   1539             e->n_resp, e->status, e->alive ? 0 : 1);
   1540 }
   1541 
   1542 
   1543 static void
   1544 hexdump (FILE *out, const char *data, size_t size)
   1545 {
   1546   size_t i;
   1547 
   1548   for (i = 0; i < size; i += 16)
   1549   {
   1550     size_t j;
   1551 
   1552     fprintf (out, "    %04x  ", (unsigned int) i);
   1553     for (j = 0; j < 16; ++j)
   1554     {
   1555       if (i + j < size)
   1556         fprintf (out, "%02x ", (unsigned char) data[i + j]);
   1557       else
   1558         fprintf (out, "   ");
   1559     }
   1560     fprintf (out, " |");
   1561     for (j = 0; (j < 16) && (i + j < size); ++j)
   1562     {
   1563       const unsigned char c = (unsigned char) data[i + j];
   1564 
   1565       fprintf (out, "%c", ((0x20 <= c) && (0x7e >= c)) ? (char) c : '.');
   1566     }
   1567     fprintf (out, "|\n");
   1568   }
   1569 }
   1570 
   1571 
   1572 static void
   1573 report_failure (const struct corpus_entry *e,
   1574                 const char *what,
   1575                 const char *expected,
   1576                 const char *actual)
   1577 {
   1578   const size_t len = (0 != e->raw_len) ? e->raw_len : strlen (e->raw);
   1579 
   1580   failures++;
   1581   fprintf (stderr, "\n==== FAILURE: %s ====\n", what);
   1582   fprintf (stderr, "  entry:      %s (%s)\n", e->name, e->desc);
   1583   fprintf (stderr, "  split:      %s\n", cur_split);
   1584   fprintf (stderr, "  mem_limit:  ");
   1585   if (0 == cur_mem_limit)
   1586     fprintf (stderr, "default\n");
   1587   else
   1588     fprintf (stderr, "%u\n", (unsigned int) cur_mem_limit);
   1589   fprintf (stderr, "  --- expected ---\n%s", expected);
   1590   fprintf (stderr, "  --- observed ---\n%s", actual);
   1591   fprintf (stderr, "  --- raw request (%u bytes) ---\n", (unsigned int) len);
   1592   hexdump (stderr, e->raw, len);
   1593   fflush (stderr);
   1594 }
   1595 
   1596 
   1597 /**
   1598  * Length of the leading "REQ ..." section of an observation, i.e. everything
   1599  * that describes the requests that MHD actually parsed.
   1600  */
   1601 static size_t
   1602 obs_reqs_len (const struct obs_buf *o)
   1603 {
   1604   const char *const p = strstr (o->d, "nreq=");
   1605 
   1606   return (NULL != p) ? (size_t) (p - o->d) : o->len;
   1607 }
   1608 
   1609 
   1610 /**
   1611  * Read an unsigned decimal number that follows @a key in the observation.
   1612  */
   1613 static unsigned int
   1614 obs_get_uint (const struct obs_buf *o, const char *key)
   1615 {
   1616   const char *const p = strstr (o->d, key);
   1617 
   1618   if (NULL == p)
   1619     return 0;
   1620   return (unsigned int) strtoul (p + strlen (key), NULL, 10);
   1621 }
   1622 
   1623 
   1624 /**
   1625  * Decide whether a deviation from the declared expectations is a legitimate
   1626  * *resource* limitation of an artificially small connection memory pool
   1627  * rather than a parser problem.
   1628  *
   1629  * Only these three signatures are accepted, and only when a non-default
   1630  * #MHD_OPTION_CONNECTION_MEMORY_LIMIT is configured:
   1631  *  - 431/413/414/500 without a single completed request (the request header
   1632  *    does not fit into the pool),
   1633  *  - no reply at all and MHD closed the connection (it could not even build
   1634  *    the reply header),
   1635  *  - fewer requests than the stream contains but a successful reply (a
   1636  *    pipelined stream that was only served partially).
   1637  *
   1638  * In particular a 400 Bad Request is *never* accepted as a resource
   1639  * limitation: a well-formed corpus entry that suddenly becomes malformed is
   1640  * exactly the kind of parser bug this test hunts for.
   1641  */
   1642 static int
   1643 is_resource_degradation (const struct obs_buf *o,
   1644                          const struct corpus_entry *e)
   1645 {
   1646   const unsigned int nreq = obs_get_uint (o, "nreq=");
   1647   const unsigned int nresp = obs_get_uint (o, "nresp=");
   1648   const unsigned int status = obs_get_uint (o, "status=");
   1649 
   1650   if (0 == cur_mem_limit)
   1651     return 0;
   1652   if (NULL != strstr (o->d, "TIMED_OUT"))
   1653     return 0;
   1654   if ((0 == nreq) &&
   1655       ((431 == status) || (413 == status) ||
   1656        (414 == status) || (500 == status)))
   1657     return 1;
   1658   if ((0 == nresp) && (0 == status))
   1659     return 1;
   1660   if ((nreq < e->n_req) && ((200 == status) || (0 == status)))
   1661     return 1;
   1662   return 0;
   1663 }
   1664 
   1665 
   1666 /**
   1667  * Status codes that a run under memory pressure may legitimately show.
   1668  *
   1669  * 0 means "no reply at all" (MHD could not build the reply header), 200 is
   1670  * the normal answer, the 4xx/5xx codes are the resource errors.  A 400 is
   1671  * deliberately *not* in this set: a well-formed corpus entry that MHD
   1672  * suddenly considers malformed is a parser bug, not a resource problem.
   1673  */
   1674 static int
   1675 status_acceptable_degraded (const struct obs_buf *o,
   1676                             const struct corpus_entry *e)
   1677 {
   1678   const unsigned int status = obs_get_uint (o, "status=");
   1679 
   1680   if ((unsigned int) e->status == status)
   1681     return 1;
   1682   switch (status)
   1683   {
   1684   case 0:
   1685   case 200:
   1686   case 413:
   1687   case 414:
   1688   case 431:
   1689   case 500:
   1690     return 1;
   1691   default:
   1692     break;
   1693   }
   1694   return 0;
   1695 }
   1696 
   1697 
   1698 /**
   1699  * Relaxed check used when the connection memory pool is too small for the
   1700  * corpus entry.
   1701  *
   1702  * Under memory pressure MHD may legitimately stop early: it may answer
   1703  * 431/413, it may fail to build a reply header, or it may serve only the
   1704  * first of several pipelined requests.  What may *never* happen is that a
   1705  * request which MHD does parse is parsed differently, or that MHD conjures up
   1706  * more requests than the byte stream contains (that would be smuggling).
   1707  *
   1708  * So the requests that were parsed must form an exact prefix of the declared
   1709  * expectations.
   1710  *
   1711  * @return 1 if acceptable, 0 on a real mismatch
   1712  */
   1713 static int
   1714 check_relaxed (const struct obs_buf *exp, const struct obs_buf *got)
   1715 {
   1716   const size_t gl = obs_reqs_len (got);
   1717   const size_t el = obs_reqs_len (exp);
   1718 
   1719   if (gl > el)
   1720     return 0; /* More requests parsed than the stream contains */
   1721   return 0 == memcmp (exp->d, got->d, gl);
   1722 }
   1723 
   1724 
   1725 /**
   1726  * Build the list of split offsets that will be exercised for a request of
   1727  * @a len bytes.
   1728  *
   1729  * @return the number of offsets stored in @a offs
   1730  */
   1731 static unsigned int
   1732 build_split_offsets (size_t len, size_t *offs, unsigned int max_offs)
   1733 {
   1734   unsigned int n = 0;
   1735   size_t s;
   1736 
   1737   for (s = 1; (s < len) && (n < max_offs); ++s)
   1738   {
   1739     if ((s <= SPLIT_DENSE_LIMIT) || (0 == (s % SPLIT_STRIDE)))
   1740       offs[n++] = s;
   1741   }
   1742   return n;
   1743 }
   1744 
   1745 
   1746 /* ------------------------------------------------------------------ */
   1747 /* Main test driver                                                   */
   1748 /* ------------------------------------------------------------------ */
   1749 
   1750 static struct MHD_Daemon *
   1751 start_daemon_with_limit (size_t limit)
   1752 {
   1753   struct MHD_Daemon *d;
   1754   struct MHD_OptMatrixProfile prof;
   1755   struct MHD_OptionItem ops[8];
   1756   unsigned int flags;
   1757 
   1758   /* The profile (if any) supplies the discipline level, the threading mode
   1759      and the polling backend; the memory limit is the one of the sweep, which
   1760      the caller has already taken from the profile. */
   1761   if (NULL != test_prof)
   1762     prof = *test_prof;
   1763   else
   1764   {
   1765     memset (&prof, 0, sizeof (prof));
   1766     prof.name = "built-in";
   1767     prof.threading = MHD_OPT_MATRIX_THR_INTERNAL;
   1768     prof.poll_backend = MHD_OPT_MATRIX_POLL_SELECT;
   1769   }
   1770   prof.mem_limit = limit;
   1771   if (0 == mhd_opt_matrix_fill_options (&prof, ops,
   1772                                         (unsigned int) (sizeof (ops)
   1773                                                         / sizeof (ops[0]))))
   1774     externalErrorExitDesc ("The daemon option array is too small");
   1775   /* The client of this test is a plain blocking socket client, so external
   1776      polling is served with an internal polling thread instead. */
   1777   flags = mhd_opt_matrix_daemon_flags (&prof, MHD_USE_ERROR_LOG, 0);
   1778   d = MHD_start_daemon (flags,
   1779                         0, NULL, NULL,
   1780                         &ahc_record, NULL,
   1781                         MHD_OPTION_ARRAY, ops,
   1782                         MHD_OPTION_EXTERNAL_LOGGER, &test_log, NULL,
   1783                         MHD_OPTION_NOTIFY_CONNECTION, &conn_notify, NULL,
   1784                         MHD_OPTION_NOTIFY_COMPLETED, &req_completed, NULL,
   1785                         MHD_OPTION_CONNECTION_TIMEOUT,
   1786                         (unsigned int) 8,
   1787                         MHD_OPTION_END);
   1788   return d;
   1789 }
   1790 
   1791 
   1792 /**
   1793  * Run one corpus entry through the full split sweep at the current memory
   1794  * limit.
   1795  */
   1796 static void
   1797 run_entry (const struct corpus_entry *e, unsigned long *split_counter)
   1798 {
   1799   const size_t len = (0 != e->raw_len) ? e->raw_len : strlen (e->raw);
   1800   struct obs_buf expected;
   1801   struct obs_buf ref;
   1802   struct obs_buf got;
   1803   size_t *offs;
   1804   unsigned int n_offs;
   1805   unsigned int i;
   1806   unsigned int stride;
   1807   int degraded;
   1808   int ref_bad = 0;
   1809   const struct obs_buf *cmp;
   1810   unsigned int ref_resp;
   1811   unsigned int min_resp;
   1812   int want_close;
   1813 
   1814   cur_entry = e;
   1815   build_expected (e, &expected);
   1816 
   1817   /* 1) Unsplit reference run. */
   1818   snprintf (cur_split, sizeof (cur_split), "none (unsplit)");
   1819   run_stream (e, NULL, 0, e->n_resp, ! e->alive, READ_GRACE_MS, &ref);
   1820   combos++;
   1821   (*split_counter)++;
   1822 
   1823   degraded = (0 != strcmp (expected.d, ref.d)) &&
   1824              is_resource_degradation (&ref, e);
   1825   if ((0 != strcmp (expected.d, ref.d)) && (! degraded))
   1826   {
   1827     /* Either the default connection memory pool is in use, or the deviation
   1828        does not look like a resource limitation: in both cases the corpus
   1829        entry must match its declared expectations exactly. */
   1830     report_failure (e, "unsplit run does not match the expectations",
   1831                     expected.d, ref.d);
   1832     /* The reference is useless as a comparison base now; keep checking the
   1833        split runs against the declared expectations so that the report names
   1834        the concrete split offsets that are affected. */
   1835     ref_bad = 1;
   1836   }
   1837   else if (degraded)
   1838   {
   1839     combos_pool_limited++;
   1840     if (! check_relaxed (&expected, &ref))
   1841     {
   1842       report_failure (e,
   1843                       "unsplit run with a small memory pool parsed a request "
   1844                       "differently than expected",
   1845                       expected.d, ref.d);
   1846       return;
   1847     }
   1848     if (verbose)
   1849     {
   1850       printf ("  %-22s degraded by the memory pool, relaxed checking\n",
   1851               e->name);
   1852       fflush (stdout);
   1853     }
   1854   }
   1855 
   1856   /* Derive the read strategy for the split runs from the reference run, so
   1857      that the split runs never have to wait for a timeout. */
   1858   ref_resp = 0;
   1859   if (1)
   1860   {
   1861     const char *p = strstr (ref.d, "nresp=");
   1862 
   1863     if (NULL != p)
   1864       ref_resp = (unsigned int) strtoul (p + 6, NULL, 10);
   1865   }
   1866   if (! degraded)
   1867   {
   1868     const char *p = strstr (ref.d, "closed=");
   1869 
   1870     min_resp = ref_resp;
   1871     want_close = (NULL != p) ? ('0' != p[7]) : 0;
   1872     stride = 1;
   1873   }
   1874   else
   1875   {
   1876     /* The client side numbers are not reproducible under memory pressure;
   1877        stop as soon as the first reply is complete (or as soon as MHD closes
   1878        the connection if it does not reply at all) and only compare the
   1879        server side observations. */
   1880     min_resp = (0 != ref_resp) ? 1 : 0;
   1881     want_close = (0 != ref_resp) ? 0 : 1;
   1882     stride = 3; /* Sample: these runs are slower and less informative */
   1883   }
   1884 
   1885   cmp = ref_bad ? &expected : &ref;
   1886 
   1887   /* 2) Two-way splits. */
   1888   offs = (size_t *) malloc (sizeof (size_t) * (len + 1));
   1889   if (NULL == offs)
   1890     externalErrorExitDesc ("malloc() failed");
   1891   n_offs = build_split_offsets (len, offs, (unsigned int) len + 1);
   1892   for (i = 0; i < n_offs; i += stride)
   1893   {
   1894     snprintf (cur_split, sizeof (cur_split), "%u", (unsigned int) offs[i]);
   1895     run_stream (e, offs + i, 1, min_resp, want_close, 0, &got);
   1896     combos++;
   1897     (*split_counter)++;
   1898     if (degraded)
   1899     {
   1900       combos_pool_limited++;
   1901       if ((! check_relaxed (&expected, &got)) ||
   1902           (! status_acceptable_degraded (&got, e)) ||
   1903           (NULL != strstr (got.d, "TIMED_OUT")))
   1904         report_failure (e,
   1905                         "split point with a small memory pool parsed a "
   1906                         "request differently than expected",
   1907                         expected.d, got.d);
   1908     }
   1909     else if (0 != strcmp (cmp->d, got.d))
   1910       report_failure (e,
   1911                       ref_bad ?
   1912                       "split point does not match the expectations" :
   1913                       "split point yields a different parse than the "
   1914                       "unsplit run",
   1915                       cmp->d, got.d);
   1916     if (MAX_FAILURES < failures)
   1917     {
   1918       free (offs);
   1919       return;
   1920     }
   1921   }
   1922 
   1923   /* 3) Three-way splits for the marked entries. */
   1924   if (e->deep && deep_enabled && (! degraded) && (2 < len))
   1925   {
   1926     unsigned int done = 0;
   1927     unsigned int a;
   1928     const unsigned int step =
   1929       (unsigned int) (((len * len) / (2 * DEEP_SPLIT_MAX)) + 1);
   1930     unsigned int ctr = 0;
   1931 
   1932     for (a = 1; (a + 1 < (unsigned int) len) && (done < DEEP_SPLIT_MAX); ++a)
   1933     {
   1934       unsigned int b;
   1935 
   1936       for (b = a + 1; (b < (unsigned int) len) && (done < DEEP_SPLIT_MAX); ++b)
   1937       {
   1938         size_t two[2];
   1939 
   1940         if (0 != (ctr++ % step))
   1941           continue;
   1942         two[0] = a;
   1943         two[1] = b;
   1944         snprintf (cur_split, sizeof (cur_split), "%u+%u", a, b);
   1945         run_stream (e, two, 2, min_resp, want_close, 0, &got);
   1946         combos++;
   1947         (*split_counter)++;
   1948         done++;
   1949         if (0 != strcmp (cmp->d, got.d))
   1950           report_failure (e,
   1951                           ref_bad ?
   1952                           "three-way split does not match the expectations" :
   1953                           "three-way split yields a different parse than "
   1954                           "the unsplit run",
   1955                           cmp->d, got.d);
   1956         if (MAX_FAILURES < failures)
   1957         {
   1958           free (offs);
   1959           return;
   1960         }
   1961       }
   1962     }
   1963   }
   1964   free (offs);
   1965 }
   1966 
   1967 
   1968 int
   1969 main (int argc, char *const *argv)
   1970 {
   1971   const uint64_t t_start = now_ms ();
   1972   unsigned int mi;
   1973   int i;
   1974   const char *only = NULL;
   1975   unsigned long total_splits = 0;
   1976   size_t sweep[MEM_LIMITS_SIZE];
   1977   unsigned int sweep_size;
   1978   unsigned long entries_skipped = 0;
   1979 
   1980   for (i = 1; i < argc; ++i)
   1981   {
   1982     if ((0 == strcmp (argv[i], "-v")) || (0 == strcmp (argv[i], "--verbose")))
   1983       verbose++;
   1984     else if (0 == strncmp (argv[i], "--entry=", 8))
   1985       only = argv[i] + 8;
   1986     else if ('-' != argv[i][0])
   1987       only = argv[i];
   1988   }
   1989 
   1990   strcpy (cur_split, "(not started)");
   1991 
   1992 #if defined(HAVE_SIGNAL_H) && defined(SIGPIPE)
   1993   if (MHD_YES != MHD_is_feature_supported (MHD_FEATURE_AUTOSUPPRESS_SIGPIPE))
   1994   {
   1995     if (SIG_ERR == signal (SIGPIPE, SIG_IGN))
   1996       externalErrorExitDesc ("Error suppressing SIGPIPE signal");
   1997   }
   1998 #endif /* HAVE_SIGNAL_H && SIGPIPE */
   1999 #if defined(HAVE_SIGNAL_H) && defined(SIGABRT)
   2000   (void) signal (SIGABRT, &abort_handler);
   2001 #endif /* HAVE_SIGNAL_H && SIGABRT */
   2002 
   2003   /* Deliberately NOT mhd_panic_tripwire.h: this test installs its own
   2004      panic and SIGABRT handlers, which print the failing input and the
   2005      daemon options along with the diagnostic.  That context is worth
   2006      more here than the generic tripwire, and the two would collide
   2007      (whichever runs last wins).  See TESTING.md, P5. */
   2008   MHD_set_panic_func (&test_panic, NULL);
   2009   mhd_opt_matrix_print_notice ("test_raw_requests");
   2010 
   2011   test_prof = mhd_opt_matrix_from_env ();
   2012   if (NULL != test_prof)
   2013   {
   2014     char desc[256];
   2015 
   2016     test_prof_copy = *test_prof;
   2017     test_prof = &test_prof_copy;
   2018     if (! mhd_opt_matrix_profile_supported (test_prof))
   2019     {
   2020       printf ("test_raw_requests: the selected profile %s is not supported "
   2021               "by this build, the test is skipped.\n",
   2022               mhd_opt_matrix_describe (test_prof, desc, sizeof (desc)));
   2023       return 77;
   2024     }
   2025     /* One profile pins one memory limit; the built-in sweep is replaced. */
   2026     sweep[0] = test_prof_copy.mem_limit;
   2027     sweep_size = 1;
   2028   }
   2029   else
   2030   {
   2031     for (mi = 0; mi < MEM_LIMITS_SIZE; ++mi)
   2032       sweep[mi] = mem_limits[mi];
   2033     sweep_size = (unsigned int) MEM_LIMITS_SIZE;
   2034   }
   2035   test_discp = mhd_opt_matrix_effective_discipline (test_prof);
   2036 
   2037   for (mi = 0; mi < sweep_size; ++mi)
   2038   {
   2039     struct MHD_Daemon *d;
   2040     const union MHD_DaemonInfo *dinfo;
   2041     size_t ci;
   2042 
   2043     cur_mem_limit = sweep[mi];
   2044     /* The three-way split pass is expensive; run it for the smallest pool
   2045        size that is not permanently degraded and for the default pool. */
   2046     deep_enabled = ((1 == sweep_size) || (1 == mi) || (sweep_size - 1 == mi));
   2047     conn_started = 0;
   2048     conn_closed = 0;
   2049     d = start_daemon_with_limit (cur_mem_limit);
   2050     if (NULL == d)
   2051     {
   2052       fprintf (stderr, "Failed to start the daemon with memory limit %u.\n",
   2053                (unsigned int) cur_mem_limit);
   2054       return 99;
   2055     }
   2056     dinfo = MHD_get_daemon_info (d, MHD_DAEMON_INFO_BIND_PORT);
   2057     if ((NULL == dinfo) || (0 == dinfo->port))
   2058     {
   2059       MHD_stop_daemon (d);
   2060       fprintf (stderr, "Cannot get the daemon port.\n");
   2061       return 99;
   2062     }
   2063     global_port = dinfo->port;
   2064 
   2065     if (verbose)
   2066     {
   2067       printf ("--- connection memory limit: ");
   2068       if (0 == cur_mem_limit)
   2069         printf ("default ---\n");
   2070       else
   2071         printf ("%u ---\n", (unsigned int) cur_mem_limit);
   2072       fflush (stdout);
   2073     }
   2074 
   2075     for (ci = 0; ci < CORPUS_SIZE; ++ci)
   2076     {
   2077       unsigned long before = total_splits;
   2078 
   2079       if ((NULL != only) && (0 != strcmp (only, corpus[ci].name)))
   2080         continue;
   2081       if (! entry_in_discp_range (corpus[ci].name))
   2082       {
   2083         entries_skipped++;
   2084         if (verbose)
   2085           printf ("  %-22s skipped: its expectation is only valid for a "
   2086                   "different client discipline level\n", corpus[ci].name);
   2087         continue;
   2088       }
   2089       run_entry (&corpus[ci], &total_splits);
   2090       if (1 < verbose)
   2091       {
   2092         printf ("  %-22s %lu runs\n", corpus[ci].name,
   2093                 total_splits - before);
   2094         fflush (stdout);
   2095       }
   2096       if (MAX_FAILURES < failures)
   2097         break;
   2098     }
   2099     MHD_stop_daemon (d);
   2100     cur_entry = NULL;
   2101     if (MAX_FAILURES < failures)
   2102       break;
   2103   }
   2104 
   2105   printf ("\ntest_raw_requests: %u corpus entries, %u memory limits, "
   2106           "%lu combinations executed (%lu pool-limited, %lu entry runs "
   2107           "skipped as out of the discipline range), %.1f s\n",
   2108           (unsigned int) ((NULL == only) ? CORPUS_SIZE : 1),
   2109           sweep_size,
   2110           combos,
   2111           combos_pool_limited,
   2112           entries_skipped,
   2113           (double) (now_ms () - t_start) / 1000.0);
   2114   if (0 != failures)
   2115   {
   2116     printf ("test_raw_requests: FAILED (%lu failure(s))\n", failures);
   2117     fflush (stdout);
   2118     return 1;
   2119   }
   2120   printf ("test_raw_requests: PASSED\n");
   2121   fflush (stdout);
   2122   return 0;
   2123 }