libmicrohttpd

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

test_option_matrix.c (45411B)


      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_option_matrix.c
     22  * @brief  A battery of raw HTTP requests replayed against every supported
     23  *         point of the daemon option matrix.
     24  * @author Christian Grothoff
     25  *
     26  * ## Why this test exists
     27  *
     28  * Every other test in this directory pins one daemon configuration, so the
     29  * suite as a whole only ever visits a single point of a fairly large
     30  * configuration space: connection memory limit x client discipline level x
     31  * threading mode x polling backend.  Yet the code that a request travels
     32  * through depends on all four: a small connection pool unlocks the read
     33  * buffer "shift back" and buffer-grow paths, the discipline level switches
     34  * whole families of parser leniency, and the threading/polling mode decides
     35  * which of the four event loops re-enters the parser.
     36  *
     37  * This test walks the matrix defined in mhd_opt_matrix.c and replays the same
     38  * small battery of hand-written raw requests against every point of it.  The
     39  * requests are sent over raw sockets because several of them (chunk
     40  * extensions, pipelining, a whitespace-prefixed header line, an early close
     41  * in the middle of a request) cannot be produced with libcurl.
     42  *
     43  * Every failure names the profile it came from, so a report is directly
     44  * actionable.
     45  *
     46  * ## Command line
     47  *
     48  * * `-v`            - report every profile and every scenario;
     49  * * `-v -v`         - additionally print the MHD error log;
     50  * * `--profile=N`   - restrict the run to one profile (name or index), the
     51  *                     same as MHD_TEST_PROFILE=N in the environment;
     52  * * `--list-profiles` - print one profile name per line and exit; this is
     53  *                     what contrib/run-option-matrix.sh uses, so that the
     54  *                     matrix is defined in exactly one place;
     55  * * `--scenario=NAME` - restrict the run to one scenario.  Useful to walk
     56  *                     the whole battery of a profile that aborts the process
     57  *                     in the middle of it.
     58  *
     59  * Exit codes: 0 = pass, 77 = skip, 99 = hard setup error, 1 = test failure.
     60  */
     61 #include "MHD_config.h"
     62 #include "platform.h"
     63 #include <microhttpd.h>
     64 #include <stdlib.h>
     65 #include <string.h>
     66 #include <stdarg.h>
     67 #include <time.h>
     68 #include <stdint.h>
     69 #include <errno.h>
     70 
     71 #ifdef _WIN32
     72 #ifndef WIN32_LEAN_AND_MEAN
     73 #define WIN32_LEAN_AND_MEAN 1
     74 #endif /* !WIN32_LEAN_AND_MEAN */
     75 #include <windows.h>
     76 #endif
     77 
     78 #ifndef WINDOWS
     79 #include <unistd.h>
     80 #include <sys/socket.h>
     81 #endif
     82 
     83 #ifdef HAVE_SIGNAL_H
     84 #include <signal.h>
     85 #endif /* HAVE_SIGNAL_H */
     86 
     87 #include <stdio.h>
     88 
     89 #include "mhd_sockets.h" /* only macros used */
     90 #include "mhd_opt_matrix.h"
     91 
     92 #ifndef MHD_STATICSTR_LEN_
     93 /**
     94  * Determine length of static string / macro strings at compile time.
     95  */
     96 #define MHD_STATICSTR_LEN_(macro) (sizeof(macro) / sizeof(char) - 1)
     97 #endif /* ! MHD_STATICSTR_LEN_ */
     98 
     99 /**
    100  * Convenience shorthand used all over the scenario table.
    101  */
    102 #define CRLF "\r\n"
    103 
    104 /**
    105  * The Host header used by (almost) every scenario.
    106  */
    107 #define H_LOCAL "Host: localhost" CRLF
    108 
    109 /**
    110  * Size of the textual observation buffer.
    111  */
    112 #define OBS_SIZE 4096
    113 
    114 /**
    115  * Size of the client receive buffer.
    116  */
    117 #define RX_SIZE 8192
    118 
    119 /**
    120  * Hard upper bound (ms) for reading the complete server answer.
    121  */
    122 #define READ_DEADLINE_MS 700
    123 
    124 /**
    125  * Silence (ms) accepted as "the server is done and keeps the connection
    126  * alive".
    127  */
    128 #define READ_GRACE_MS 10
    129 
    130 /**
    131  * Milliseconds to wait for the server side to finish tearing down the
    132  * connection, so that the observations become stable.
    133  */
    134 #define CLOSE_WAIT_MS 1500
    135 
    136 /**
    137  * Milliseconds the daemon is left running after a scenario that closes the
    138  * connection without reading an answer, so that a wrongly parsed request
    139  * still has a chance to show up.
    140  */
    141 #define SETTLE_MS 60
    142 
    143 /**
    144  * The connection timeout of the test daemons, in seconds.
    145  */
    146 #define DAEMON_TIMEOUT 8
    147 
    148 /**
    149  * Give up after that many reported failures.
    150  */
    151 #define MAX_FAILURES 12
    152 
    153 
    154 /* ------------------------------------------------------------------ */
    155 /* Error exits                                                        */
    156 /* ------------------------------------------------------------------ */
    157 
    158 /* Forward declaration, needed by the error exit helpers. */
    159 static void
    160 print_context (FILE *out);
    161 
    162 
    163 _MHD_NORETURN static void
    164 external_error_exit (const char *desc, int line)
    165 {
    166   fprintf (stderr,
    167            "%s at line %d.\nLast errno value: %d (%s)\n",
    168            (NULL != desc) ? desc : "System or external library call failed",
    169            line,
    170            (int) errno,
    171            strerror (errno));
    172   print_context (stderr);
    173   fflush (stderr);
    174   exit (99);
    175 }
    176 
    177 
    178 #define hard_error(desc) external_error_exit (desc, __LINE__)
    179 
    180 
    181 /**
    182  * Pause execution for the given number of milliseconds.
    183  *
    184  * @param ms the number of milliseconds to sleep
    185  */
    186 static void
    187 sleep_ms (unsigned int ms)
    188 {
    189 #if defined(_WIN32)
    190   Sleep (ms);
    191 #elif defined(HAVE_NANOSLEEP)
    192   struct timespec slp = {(time_t) (ms / 1000), (long) ((ms % 1000) * 1000000)};
    193   struct timespec rmn;
    194   int num_retries = 0;
    195 
    196   while (0 != nanosleep (&slp, &rmn))
    197   {
    198     if (EINTR != errno)
    199       hard_error ("nanosleep() failed");
    200     if (num_retries++ > 8)
    201       break;
    202     slp = rmn;
    203   }
    204 #elif defined(HAVE_USLEEP)
    205   usleep ((useconds_t) (ms * 1000));
    206 #else
    207   hard_error ("No sleep function available on this system");
    208 #endif
    209 }
    210 
    211 
    212 /**
    213  * Monotonic-ish millisecond clock.
    214  */
    215 static uint64_t
    216 now_ms (void)
    217 {
    218 #if defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_MONOTONIC)
    219   struct timespec ts;
    220 
    221   if (0 == clock_gettime (CLOCK_MONOTONIC, &ts))
    222     return ((uint64_t) ts.tv_sec) * 1000 + (uint64_t) (ts.tv_nsec / 1000000);
    223 #endif /* HAVE_CLOCK_GETTIME && CLOCK_MONOTONIC */
    224   return ((uint64_t) time (NULL)) * 1000;
    225 }
    226 
    227 
    228 /* ------------------------------------------------------------------ */
    229 /* Textual observation buffer                                         */
    230 /* ------------------------------------------------------------------ */
    231 
    232 /**
    233  * A bounded, always NUL-terminated text accumulator.  Both the expected and
    234  * the actual observation are rendered into such a buffer, so that a single
    235  * strcmp() performs the whole check and also yields readable diagnostics.
    236  */
    237 struct obs_buf
    238 {
    239   char d[OBS_SIZE];
    240   size_t len;
    241   int overflow;
    242 };
    243 
    244 
    245 static void
    246 obs_reset (struct obs_buf *b)
    247 {
    248   b->len = 0;
    249   b->overflow = 0;
    250   b->d[0] = 0;
    251 }
    252 
    253 
    254 static void
    255 obs_addf (struct obs_buf *b, const char *fmt, ...)
    256 {
    257   va_list ap;
    258   int r;
    259   size_t space;
    260 
    261   if (b->len >= sizeof(b->d) - 1)
    262   {
    263     b->overflow = 1;
    264     return;
    265   }
    266   space = sizeof(b->d) - b->len;
    267   va_start (ap, fmt);
    268   r = vsnprintf (b->d + b->len, space, fmt, ap);
    269   va_end (ap);
    270   if (0 > r)
    271   {
    272     b->overflow = 1;
    273     return;
    274   }
    275   if ((size_t) r >= space)
    276   {
    277     b->len = sizeof(b->d) - 1;
    278     b->overflow = 1;
    279     return;
    280   }
    281   b->len += (size_t) r;
    282 }
    283 
    284 
    285 /**
    286  * Append @a data with all non-printable bytes escaped as \\xNN.
    287  */
    288 static void
    289 obs_add_escaped (struct obs_buf *b, const char *data, size_t size)
    290 {
    291   size_t i;
    292 
    293   for (i = 0; i < size; ++i)
    294   {
    295     const unsigned char c = (unsigned char) data[i];
    296 
    297     if ((0x20 <= c) && (0x7e >= c) && ('\\' != c))
    298       obs_addf (b, "%c", (char) c);
    299     else if ('\\' == c)
    300       obs_addf (b, "\\\\");
    301     else
    302       obs_addf (b, "\\x%02x", (unsigned int) c);
    303   }
    304 }
    305 
    306 
    307 /* ------------------------------------------------------------------ */
    308 /* The scenario battery                                               */
    309 /* ------------------------------------------------------------------ */
    310 
    311 /**
    312  * One request scenario.
    313  *
    314  * The expectation @e exp is the concatenation of one line per request that
    315  * MHD must parse, in the format
    316  *
    317  *     "METHOD URL [BODY] {ARG=VAL,ARG=VAL}\n"
    318  *
    319  * with every byte outside the printable ASCII range escaped as \\xNN and a
    320  * query argument without a value rendered as "ARG=<NULL>".
    321  */
    322 struct scenario
    323 {
    324   /**
    325    * Short identifier, printed on failure.
    326    */
    327   const char *name;
    328 
    329   /**
    330    * One line saying which behaviour this scenario pins down.
    331    */
    332   const char *desc;
    333 
    334   /**
    335    * The raw request byte stream (may hold several pipelined requests).
    336    */
    337   const char *raw;
    338 
    339   /**
    340    * The number of complete requests MHD must parse out of @e raw.
    341    */
    342   unsigned int n_req;
    343 
    344   /**
    345    * The number of complete responses the client must see.
    346    */
    347   unsigned int n_resp;
    348 
    349   /**
    350    * The expected status code of the first response, zero for "any".
    351    */
    352   int status;
    353 
    354   /**
    355    * 1 if the connection must stay alive afterwards, 0 if MHD must close it.
    356    */
    357   int alive;
    358 
    359   /**
    360    * If non-zero, the client closes the connection right after sending and
    361    * does not read any answer at all.
    362    */
    363   int close_after_send;
    364 
    365   /**
    366    * If non-zero, the only requirement is that the daemon survives: neither
    367    * the number of parsed requests nor the answer is checked.  Used by the
    368    * probes whose declared purpose is to reach a defect.
    369    */
    370   int loose;
    371 
    372   /**
    373    * The lowest client discipline level at which this scenario is meaningful.
    374    */
    375   int min_discp;
    376 
    377   /**
    378    * The highest client discipline level at which this scenario is
    379    * meaningful.
    380    */
    381   int max_discp;
    382 
    383   /**
    384    * The expected server side observation, see above.
    385    */
    386   const char *exp;
    387 };
    388 
    389 
    390 /**
    391  * A header block of ten headers with values of moderate length.  Kept small
    392  * enough to still fit into a connection memory pool of
    393  * 1024 bytes, so that the scenario is a real check rather than a permanent
    394  * "degraded by the pool" case.
    395  */
    396 #define MANY_HDRS \
    397         "A-One: 1" CRLF "B-Two: 22" CRLF "C-Three: 333" CRLF \
    398         "D-Four: 4444" CRLF "E-Five: 55555" CRLF "F-Six: 666666" CRLF \
    399         "G-Seven: 7777777" CRLF "H-Eight: 88888888" CRLF \
    400         "I-Nine: 999999999" CRLF "J-Ten: aaaaaaaaaa" CRLF
    401 
    402 /**
    403  * Forty characters of chunk-extension filler.
    404  */
    405 #define EXT_40 "qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq"
    406 
    407 /**
    408  * Four hundred characters of chunk-extension filler: longer than the whole
    409  * read buffer of a connection memory pool of 512 bytes, so that MHD has to
    410  * take the "chunk-size line does not fit" path, and comfortably shorter than
    411  * the read buffer of a 1024-byte pool, where the line still fits.
    412  */
    413 #define EXT_400 \
    414         EXT_40 EXT_40 EXT_40 EXT_40 EXT_40 \
    415         EXT_40 EXT_40 EXT_40 EXT_40 EXT_40
    416 
    417 /**
    418  * The expected rendering of #MANY_HDRS as GET arguments: none, the block is
    419  * a header block.  Only listed here to keep the table readable.
    420  */
    421 #define E_MANY "GET /h [] {}\n"
    422 
    423 static const struct scenario scenarios[] = {
    424   { "get_plain",
    425     "baseline: request line, one header, empty reply",
    426     "GET / HTTP/1.1" CRLF H_LOCAL CRLF,
    427     1, 1, 200, 1, 0, 0, -3, 3,
    428     "GET / [] {}\n" },
    429   { "get_query",
    430     "query arguments: '=' value, bare argument, '+' decoding",
    431     "GET /q?a=1&b&c=x+y HTTP/1.1" CRLF H_LOCAL CRLF,
    432     1, 1, 200, 1, 0, 0, -3, 3,
    433     "GET /q [] {a=1,b=<NULL>,c=x y}\n" },
    434   { "post_cl",
    435     "Content-Length body, delivered incrementally to the handler",
    436     "POST /p HTTP/1.1" CRLF H_LOCAL "Content-Length: 11" CRLF CRLF
    437     "Hello World",
    438     1, 1, 200, 1, 0, 0, -3, 3,
    439     "POST /p [Hello World] {}\n" },
    440   { "post_chunked",
    441     "chunked body without any chunk extension",
    442     "POST /c HTTP/1.1" CRLF H_LOCAL "Transfer-Encoding: chunked" CRLF CRLF
    443     "5" CRLF "Hello" CRLF "6" CRLF " World" CRLF "0" CRLF CRLF,
    444     1, 1, 200, 1, 0, 0, -3, 3,
    445     "POST /c [Hello World] {}\n" },
    446   { "post_chunk_ext",
    447     "chunk extensions, incl. a quoted value holding a ';' (commit c13f4c64)",
    448     "POST /c HTTP/1.1" CRLF H_LOCAL "Transfer-Encoding: chunked" CRLF CRLF
    449     "5;a=b" CRLF "Hello" CRLF "6;q=\"x;y\"" CRLF " World" CRLF "0;z" CRLF CRLF,
    450     1, 1, 200, 1, 0, 0, -3, 3,
    451     "POST /c [Hello World] {}\n" },
    452   /* With a connection memory limit of about 512 or less this chunk-size
    453      line does not fit into the read buffer. */
    454   { "post_chunk_ext_long",
    455     "chunk extension longer than the space reserved for the chunk header",
    456     "POST /c HTTP/1.1" CRLF H_LOCAL "Transfer-Encoding: chunked" CRLF CRLF
    457     "5;ext=\"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\"" CRLF
    458     "Hello" CRLF "0" CRLF CRLF,
    459     1, 1, 200, 1, 0, 0, -3, 3,
    460     "POST /c [Hello] {}\n" },
    461   /* Regression test for commits 68c83f22 and e04eb218: the chunk-size line
    462      is longer than the read buffer of a small connection memory pool, so MHD
    463      has to take handle_req_chunk_size_line_no_space().  With a pool of 1024
    464      bytes or more the line fits and the scenario is an ordinary
    465      chunk-extension check. */
    466   { "post_chunk_ext_huge",
    467     "chunk extension longer than the whole read buffer of a small pool",
    468     "POST /c HTTP/1.1" CRLF H_LOCAL "Transfer-Encoding: chunked" CRLF CRLF
    469     "5;ext=\"" EXT_400 "\"" CRLF "Hello" CRLF "0" CRLF CRLF,
    470     1, 1, 200, 1, 0, 0, -3, 3,
    471     "POST /c [Hello] {}\n" },
    472   /* The same, but with the over-long chunk-size line on the *second* chunk,
    473      so that the upload callback has already run once when MHD runs out of
    474      read buffer space.  That is the extra precondition of the assertion
    475      fixed in 68c83f22 (rq.some_payload_processed still describes the
    476      previous read). */
    477   { "post_chunk_ext_huge_2nd",
    478     "over-long chunk-size line after a chunk that was already delivered",
    479     "POST /c HTTP/1.1" CRLF H_LOCAL "Transfer-Encoding: chunked" CRLF CRLF
    480     "5" CRLF "Hello" CRLF
    481     "5;ext=\"" EXT_400 "\"" CRLF "World" CRLF "0" CRLF CRLF,
    482     1, 1, 200, 1, 0, 0, -3, 3,
    483     "POST /c [HelloWorld] {}\n" },
    484   { "pipelined",
    485     "two pipelined requests must yield exactly two parsed requests",
    486     "GET /1 HTTP/1.1" CRLF H_LOCAL CRLF
    487     "GET /2?x=1 HTTP/1.1" CRLF H_LOCAL CRLF,
    488     2, 2, 200, 1, 0, 0, -3, 3,
    489     "GET /1 [] {}\nGET /2 [] {x=1}\n" },
    490   { "many_headers",
    491     "ten header lines: pool allocation and read buffer growth",
    492     "GET /h HTTP/1.1" CRLF H_LOCAL MANY_HDRS CRLF,
    493     1, 1, 200, 1, 0, 0, -3, 3,
    494     E_MANY },
    495   { "early_close",
    496     "the client vanishes in the middle of the request header",
    497     "GET /e HTTP/1.1" CRLF H_LOCAL "X-Partial: abc",
    498     0, 0, 0, 0, 1, 0, -3, 3,
    499     "" },
    500 
    501   /* ---- probes for the relaxed client discipline levels ------------ */
    502   /* The three "strict" variants below are the control cases: at the
    503      discipline levels at which MHD promises to reject the input, it must
    504      really answer 400 and must not parse a request.  The three "lax"
    505      variants are the regression tests for the assertions fixed in commits
    506      0b750975 and 6fcdfd43; they only require that the daemon survives, since
    507      what those modes do with the input is deliberately unspecified beyond
    508      "do not abort". */
    509   { "wsp_first_hdr_strict",
    510     "whitespace-prefixed first header line is rejected at level 0 and above",
    511     "GET / HTTP/1.1" CRLF " " H_LOCAL "X-A: b" CRLF CRLF,
    512     0, 1, 400, 0, 0, 0, 0, 3,
    513     "" },
    514   { "wsp_first_hdr_lax",
    515     "whitespace-prefixed first header line at level -1 or below (0b750975)",
    516     "GET / HTTP/1.1" CRLF " " H_LOCAL "X-A: b" CRLF CRLF,
    517     0, 0, 0, 0, 0, 1, -3, -1,
    518     "" },
    519   { "empty_hdr_name_strict",
    520     "an empty header name is rejected at level -1 and above",
    521     "GET / HTTP/1.1" CRLF H_LOCAL ": value" CRLF CRLF,
    522     0, 1, 400, 0, 0, 0, -1, 3,
    523     "" },
    524   { "empty_hdr_name_lax",
    525     "an empty header name at level -2 or below (0b750975)",
    526     "GET / HTTP/1.1" CRLF H_LOCAL ": value" CRLF CRLF,
    527     0, 0, 0, 0, 0, 1, -3, -2,
    528     "" },
    529   { "bare_cr_strict",
    530     "a bare CR in a header value is rejected at level 0 and above",
    531     "GET / HTTP/1.1" CRLF H_LOCAL "X-Cr: va\rlue" CRLF CRLF,
    532     0, 1, 400, 0, 0, 0, 0, 3,
    533     "" },
    534   { "bare_cr_as_sp",
    535     "a bare CR becomes a space at level -1 and -2",
    536     "GET / HTTP/1.1" CRLF H_LOCAL "X-Cr: va\rlue" CRLF CRLF,
    537     1, 1, 200, 1, 0, 0, -2, -1,
    538     "GET / [] {}\n" },
    539   { "bare_cr_keep",
    540     "a bare CR is kept in the header value at level -3 (6fcdfd43)",
    541     "GET / HTTP/1.1" CRLF H_LOCAL "X-Cr: va\rlue" CRLF CRLF,
    542     0, 0, 0, 0, 0, 1, -3, -3,
    543     "" }
    544 };
    545 
    546 #define NUM_SCENARIOS (sizeof(scenarios) / sizeof(scenarios[0]))
    547 
    548 
    549 /* ------------------------------------------------------------------ */
    550 /* Global state                                                       */
    551 /* ------------------------------------------------------------------ */
    552 
    553 static int verbose;
    554 
    555 /** The profile currently under test. */
    556 static const struct MHD_OptMatrixProfile *cur_prof;
    557 /** Rendered description of @a cur_prof. */
    558 static char cur_prof_desc[256];
    559 /** The scenario currently under test. */
    560 static const struct scenario *cur_scen;
    561 /** If not NULL, only the scenario with this name is run. */
    562 static const char *only_scen;
    563 
    564 /** The port of the running daemon. */
    565 static uint16_t global_port;
    566 /** The running daemon, only set when it has to be driven with MHD_run(). */
    567 static struct MHD_Daemon *extern_daemon;
    568 
    569 /** Server side observations of the connection currently under test. */
    570 static struct obs_buf srv_obs;
    571 /** Number of completed requests seen by the request handler. */
    572 static volatile unsigned int srv_n_req;
    573 /** Set if the handler noticed something structurally impossible. */
    574 static volatile unsigned int srv_error;
    575 
    576 static volatile unsigned int conn_started;
    577 static volatile unsigned int conn_closed;
    578 
    579 /** Number of (profile x scenario) combinations executed. */
    580 static unsigned long combos;
    581 /** Number of combinations that could not fit into the memory pool. */
    582 static unsigned long combos_degraded;
    583 /** Number of scenarios skipped because of the discipline level. */
    584 static unsigned long scen_skipped;
    585 /** Number of profiles skipped because this build cannot provide them. */
    586 static unsigned long prof_skipped;
    587 /** Number of failures found. */
    588 static unsigned long failures;
    589 
    590 
    591 static void
    592 print_context (FILE *out)
    593 {
    594   fprintf (out, "  context: profile %s scenario='%s'\n",
    595            cur_prof_desc,
    596            (NULL != cur_scen) ? cur_scen->name : "(none)");
    597   if (NULL != cur_scen)
    598     fprintf (out, "  scenario purpose: %s\n", cur_scen->desc);
    599 }
    600 
    601 
    602 #if defined(HAVE_SIGNAL_H) && defined(SIGABRT)
    603 /**
    604  * Report the profile and the scenario when the library aborts (e.g. from an
    605  * internal assertion) instead of dying anonymously.
    606  */
    607 static void
    608 abort_handler (int sig)
    609 {
    610   (void) sig;
    611   fprintf (stderr,
    612            "\nFATAL: the library aborted while running a scenario.\n");
    613   print_context (stderr);
    614   fflush (stderr);
    615   _exit (1);
    616 }
    617 
    618 
    619 #endif /* HAVE_SIGNAL_H && SIGABRT */
    620 
    621 
    622 /**
    623  * Panic callback: a library panic is a test failure that names the profile
    624  * and the scenario.
    625  */
    626 static void
    627 test_panic (void *cls, const char *file, unsigned int line, const char *reason)
    628 {
    629   (void) cls;
    630   fprintf (stderr,
    631            "\nFATAL: MHD panic at %s:%u: %s\n",
    632            (NULL != file) ? file : "(unknown)",
    633            line,
    634            (NULL != reason) ? reason : "(no reason given)");
    635   print_context (stderr);
    636   fflush (stderr);
    637   _exit (1);
    638 }
    639 
    640 
    641 /* ------------------------------------------------------------------ */
    642 /* Request handler: records the observations                          */
    643 /* ------------------------------------------------------------------ */
    644 
    645 struct req_state
    646 {
    647   struct obs_buf body;
    648 };
    649 
    650 
    651 struct iter_ctx
    652 {
    653   struct obs_buf *out;
    654   unsigned int num;
    655 };
    656 
    657 
    658 static enum MHD_Result
    659 value_iterator (void *cls,
    660                 enum MHD_ValueKind kind,
    661                 const char *key,
    662                 size_t key_size,
    663                 const char *value,
    664                 size_t value_size)
    665 {
    666   struct iter_ctx *const ctx = (struct iter_ctx *) cls;
    667 
    668   (void) kind;
    669   if (0 != ctx->num)
    670     obs_addf (ctx->out, ",");
    671   ctx->num++;
    672   if (NULL != key)
    673     obs_add_escaped (ctx->out, key, key_size);
    674   else
    675     obs_addf (ctx->out, "<NULLKEY>");
    676   obs_addf (ctx->out, "=");
    677   if (NULL == value)
    678     obs_addf (ctx->out, "<NULL>");
    679   else
    680     obs_add_escaped (ctx->out, value, value_size);
    681   return MHD_YES;
    682 }
    683 
    684 
    685 static enum MHD_Result
    686 ahc_record (void *cls,
    687             struct MHD_Connection *connection,
    688             const char *url,
    689             const char *method,
    690             const char *version,
    691             const char *upload_data,
    692             size_t *upload_data_size,
    693             void **req_cls)
    694 {
    695   /* The reply body is intentionally empty, which keeps the response framing
    696      on the client side trivial. */
    697   static const char rp_data[] = "";
    698   struct req_state *rs;
    699   struct MHD_Response *response;
    700   struct iter_ctx ctx;
    701   enum MHD_Result ret;
    702 
    703   (void) cls; (void) version;
    704   if (NULL == *req_cls)
    705   {
    706     rs = (struct req_state *) malloc (sizeof (struct req_state));
    707     if (NULL == rs)
    708       hard_error ("malloc() failed");
    709     obs_reset (&rs->body);
    710     *req_cls = rs;
    711     return MHD_YES;
    712   }
    713   rs = (struct req_state *) *req_cls;
    714   if (0 != *upload_data_size)
    715   {
    716     obs_add_escaped (&rs->body, upload_data, *upload_data_size);
    717     *upload_data_size = 0;
    718     return MHD_YES;
    719   }
    720 
    721   /* The request is complete: record everything that is observable. */
    722   obs_addf (&srv_obs, "%s ", (NULL != method) ? method : "(null)");
    723   if (NULL != url)
    724     obs_add_escaped (&srv_obs, url, strlen (url));
    725   else
    726     obs_addf (&srv_obs, "(null)");
    727   obs_addf (&srv_obs, " [%s] {", rs->body.d);
    728   ctx.out = &srv_obs;
    729   ctx.num = 0;
    730   MHD_get_connection_values_n (connection, MHD_GET_ARGUMENT_KIND,
    731                                &value_iterator, &ctx);
    732   obs_addf (&srv_obs, "}\n");
    733 
    734   free (rs);
    735   *req_cls = NULL;
    736   srv_n_req++;
    737 
    738   response =
    739     MHD_create_response_from_buffer (MHD_STATICSTR_LEN_ (rp_data),
    740                                      (void *) rp_data,
    741                                      MHD_RESPMEM_PERSISTENT);
    742   if (NULL == response)
    743   {
    744     srv_error++;
    745     return MHD_NO;
    746   }
    747   ret = MHD_queue_response (connection, MHD_HTTP_OK, response);
    748   MHD_destroy_response (response);
    749   if (MHD_YES != ret)
    750     srv_error++;
    751   return ret;
    752 }
    753 
    754 
    755 /**
    756  * The MHD error log, only printed with '-v -v': the matrix visits several
    757  * deliberately hostile configurations and the library complains a lot.
    758  */
    759 static void
    760 test_log (void *cls, const char *fmt, va_list ap)
    761 {
    762   (void) cls;
    763   if (1 < verbose)
    764   {
    765     vfprintf (stderr, fmt, ap);
    766     fflush (stderr);
    767   }
    768 }
    769 
    770 
    771 static void
    772 conn_notify (void *cls,
    773              struct MHD_Connection *c,
    774              void **socket_context,
    775              enum MHD_ConnectionNotificationCode toe)
    776 {
    777   (void) cls; (void) c; (void) socket_context;
    778   if (MHD_CONNECTION_NOTIFY_STARTED == toe)
    779     conn_started++;
    780   else if (MHD_CONNECTION_NOTIFY_CLOSED == toe)
    781     conn_closed++;
    782 }
    783 
    784 
    785 static void
    786 req_completed (void *cls,
    787                struct MHD_Connection *c,
    788                void **req_cls,
    789                enum MHD_RequestTerminationCode term_code)
    790 {
    791   (void) cls; (void) c; (void) term_code;
    792   if ((NULL != req_cls) && (NULL != *req_cls))
    793   {
    794     free (*req_cls);
    795     *req_cls = NULL;
    796   }
    797 }
    798 
    799 
    800 /* ------------------------------------------------------------------ */
    801 /* Raw client                                                         */
    802 /* ------------------------------------------------------------------ */
    803 
    804 struct client_result
    805 {
    806   unsigned int n_resp;
    807   int first_status;
    808   int peer_closed;
    809   int timed_out;
    810   size_t rx_len;
    811   char rx[RX_SIZE];
    812 };
    813 
    814 
    815 /**
    816  * Case-insensitive comparison of @a size bytes (ASCII only).
    817  */
    818 static int
    819 mem_equal_ci (const char *a, const char *b, size_t size)
    820 {
    821   size_t i;
    822 
    823   for (i = 0; i < size; ++i)
    824   {
    825     char ca = a[i];
    826     char cb = b[i];
    827 
    828     if (('A' <= ca) && ('Z' >= ca))
    829       ca = (char) (ca - 'A' + 'a');
    830     if (('A' <= cb) && ('Z' >= cb))
    831       cb = (char) (cb - 'A' + 'a');
    832     if (ca != cb)
    833       return 0;
    834   }
    835   return 1;
    836 }
    837 
    838 
    839 /**
    840  * Count the number of complete HTTP responses in @a buf.
    841  *
    842  * @param buf the received bytes
    843  * @param len the number of bytes in @a buf
    844  * @param[out] first_status set to the status code of the first response
    845  * @return the number of *complete* responses found
    846  */
    847 static unsigned int
    848 count_responses (const char *buf, size_t len, int *first_status)
    849 {
    850   size_t pos = 0;
    851   unsigned int num = 0;
    852 
    853   *first_status = 0;
    854   while (pos < len)
    855   {
    856     const char *hdr_end;
    857     const char *cl;
    858     size_t hdr_len;
    859     size_t body_len = 0;
    860     int status;
    861     size_t i;
    862 
    863     if (7 > len - pos)
    864       break;
    865     if (0 != memcmp (buf + pos, "HTTP/1.", 7))
    866       break; /* Not a well-formed response */
    867     hdr_end = NULL;
    868     for (i = pos; i + 3 < len; ++i)
    869     {
    870       if (('\r' == buf[i]) && ('\n' == buf[i + 1]) &&
    871           ('\r' == buf[i + 2]) && ('\n' == buf[i + 3]))
    872       {
    873         hdr_end = buf + i + 4;
    874         break;
    875       }
    876     }
    877     if (NULL == hdr_end)
    878       break; /* Incomplete header */
    879     hdr_len = (size_t) (hdr_end - (buf + pos));
    880     status = 0;
    881     if ((pos + 12 <= len) &&
    882         ('0' <= buf[pos + 9]) && ('9' >= buf[pos + 9]))
    883       status = (buf[pos + 9] - '0') * 100
    884                + (buf[pos + 10] - '0') * 10
    885                + (buf[pos + 11] - '0');
    886     if (0 == num)
    887       *first_status = status;
    888     cl = NULL;
    889     for (i = pos; i + 15 <= pos + hdr_len; ++i)
    890     {
    891       if (mem_equal_ci (buf + i, "content-length:", 15))
    892       {
    893         cl = buf + i + 15;
    894         break;
    895       }
    896     }
    897     if (NULL != cl)
    898     {
    899       while ((cl < buf + pos + hdr_len) && (' ' == *cl))
    900         cl++;
    901       body_len = 0;
    902       while ((cl < buf + pos + hdr_len) && ('0' <= *cl) && ('9' >= *cl))
    903       {
    904         body_len = body_len * 10 + (size_t) (*cl - '0');
    905         cl++;
    906       }
    907     }
    908     if (pos + hdr_len + body_len > len)
    909       break; /* Body incomplete */
    910     pos += hdr_len + body_len;
    911     num++;
    912   }
    913   return num;
    914 }
    915 
    916 
    917 /**
    918  * Switch a socket to the non-blocking mode.
    919  *
    920  * @param sk the socket to modify
    921  * @return non-zero on success
    922  */
    923 static int
    924 set_nonblocking (MHD_socket sk)
    925 {
    926 #if defined(MHD_POSIX_SOCKETS)
    927   int flags;
    928 
    929   flags = fcntl (sk, F_GETFL);
    930   if (-1 == flags)
    931     return 0;
    932   return (-1 != fcntl (sk, F_SETFL, flags | O_NONBLOCK)) ? ! 0 : 0;
    933 #else  /* ! MHD_POSIX_SOCKETS */
    934   unsigned long mode = 1;
    935 
    936   return (0 == ioctlsocket (sk, FIONBIO, &mode)) ? ! 0 : 0;
    937 #endif /* ! MHD_POSIX_SOCKETS */
    938 }
    939 
    940 
    941 static MHD_socket
    942 client_connect (void)
    943 {
    944   MHD_socket sk;
    945   struct sockaddr_in sa;
    946   const MHD_SCKT_OPT_BOOL_ on_val = 1;
    947 
    948   sk = socket (AF_INET, SOCK_STREAM, IPPROTO_TCP);
    949   if (MHD_INVALID_SOCKET == sk)
    950     hard_error ("Cannot create the client socket");
    951 #ifdef MHD_socket_nosignal_
    952   if (! MHD_socket_nosignal_ (sk))
    953     hard_error ("Cannot suppress SIGPIPE on the client socket");
    954 #endif /* MHD_socket_nosignal_ */
    955   if (0 != setsockopt (sk, IPPROTO_TCP, TCP_NODELAY,
    956                        (const void *) &on_val, sizeof (on_val)))
    957     hard_error ("Cannot set the TCP_NODELAY option");
    958 
    959   memset (&sa, 0, sizeof (sa));
    960   sa.sin_family = AF_INET;
    961   sa.sin_port = htons (global_port);
    962   sa.sin_addr.s_addr = htonl (INADDR_LOOPBACK);
    963   if (0 != connect (sk, (struct sockaddr *) &sa, sizeof (sa)))
    964     hard_error ("Cannot connect() to the test daemon");
    965   if (! set_nonblocking (sk))
    966     hard_error ("Cannot switch the client socket to the non-blocking mode");
    967   return sk;
    968 }
    969 
    970 
    971 /**
    972  * Send @a data and read the answer, driving the daemon with MHD_run() when
    973  * the profile asks for external polling.
    974  *
    975  * @param sk the client socket
    976  * @param data the request bytes
    977  * @param size the number of bytes in @a data
    978  * @param[out] r the result
    979  * @param min_resp stop once that many complete responses have been read
    980  * @param want_close keep reading until the peer closes the connection
    981  * @param read_answer if zero, close right after sending
    982  */
    983 static void
    984 client_exchange (MHD_socket sk,
    985                  const char *data,
    986                  size_t size,
    987                  struct client_result *r,
    988                  unsigned int min_resp,
    989                  int want_close,
    990                  int read_answer)
    991 {
    992   const uint64_t start = now_ms ();
    993   size_t sent = 0;
    994   uint64_t quiet_since = 0;
    995 
    996   memset (r, 0, sizeof (*r));
    997   while (1)
    998   {
    999     fd_set rs;
   1000     fd_set ws;
   1001     fd_set es;
   1002     struct timeval tv;
   1003     MHD_socket maxfd_mhd = MHD_INVALID_SOCKET;
   1004     int maxfd;
   1005     int sel;
   1006 
   1007     if (now_ms () - start >= READ_DEADLINE_MS)
   1008     {
   1009       r->timed_out = 1;
   1010       break;
   1011     }
   1012     if ((sent >= size) && (! read_answer))
   1013       break;
   1014     r->n_resp = count_responses (r->rx, r->rx_len, &r->first_status);
   1015     if ((sent >= size) && (! want_close) && (r->n_resp >= min_resp))
   1016     {
   1017       /* Everything that was expected has arrived; accept a short silence as
   1018          "the server is done and keeps the connection alive". */
   1019       if (0 == quiet_since)
   1020         quiet_since = now_ms ();
   1021       else if (now_ms () - quiet_since >= READ_GRACE_MS)
   1022         break;
   1023     }
   1024     else
   1025       quiet_since = 0;
   1026 
   1027     FD_ZERO (&rs);
   1028     FD_ZERO (&ws);
   1029     FD_ZERO (&es);
   1030 #if defined(MHD_POSIX_SOCKETS)
   1031     if (FD_SETSIZE <= (int) sk)
   1032       hard_error ("The client socket does not fit into fd_set");
   1033 #endif /* MHD_POSIX_SOCKETS */
   1034     if (! r->peer_closed)
   1035       FD_SET (sk, &rs);
   1036     if (sent < size)
   1037       FD_SET (sk, &ws);
   1038     maxfd = (int) sk;
   1039     if (NULL != extern_daemon)
   1040     {
   1041       if (MHD_YES != MHD_get_fdset (extern_daemon, &rs, &ws, &es, &maxfd_mhd))
   1042         hard_error ("MHD_get_fdset() failed");
   1043 #ifndef MHD_WINSOCK_SOCKETS
   1044       if ((int) maxfd_mhd > maxfd)
   1045         maxfd = (int) maxfd_mhd;
   1046 #endif /* ! MHD_WINSOCK_SOCKETS */
   1047     }
   1048     tv.tv_sec = 0;
   1049     tv.tv_usec = 2000;
   1050     sel = select (maxfd + 1, &rs, &ws, &es, &tv);
   1051     if ((0 > sel) && (EINTR != errno))
   1052       hard_error ("select() failed");
   1053     if (NULL != extern_daemon)
   1054       MHD_run (extern_daemon);
   1055 
   1056     if (sent < size)
   1057     {
   1058       ssize_t s = MHD_send_ (sk, data + sent, size - sent);
   1059 
   1060       if (0 > s)
   1061       {
   1062         const int err = MHD_socket_get_error_ ();
   1063 
   1064         if (! (MHD_SCKT_ERR_IS_EINTR_ (err) || MHD_SCKT_ERR_IS_EAGAIN_ (err)))
   1065         {
   1066           /* MHD closed the connection early; that is an observation, not an
   1067              error of the test. */
   1068           sent = size;
   1069           r->peer_closed = 1;
   1070         }
   1071       }
   1072       else
   1073         sent += (size_t) s;
   1074     }
   1075     if ((! r->peer_closed) && (r->rx_len < sizeof (r->rx)))
   1076     {
   1077       ssize_t got = MHD_recv_ (sk, r->rx + r->rx_len,
   1078                                sizeof (r->rx) - r->rx_len);
   1079 
   1080       if (0 > got)
   1081       {
   1082         const int err = MHD_socket_get_error_ ();
   1083 
   1084         if (! (MHD_SCKT_ERR_IS_EINTR_ (err) || MHD_SCKT_ERR_IS_EAGAIN_ (err)))
   1085           r->peer_closed = 1; /* A connection reset counts as "closed" */
   1086       }
   1087       else if (0 == got)
   1088         r->peer_closed = 1;
   1089       else
   1090       {
   1091         r->rx_len += (size_t) got;
   1092         quiet_since = 0;
   1093       }
   1094     }
   1095     if (r->peer_closed && (sent >= size))
   1096       break;
   1097   }
   1098   r->n_resp = count_responses (r->rx, r->rx_len, &r->first_status);
   1099 }
   1100 
   1101 
   1102 /**
   1103  * Let the daemon run for @a ms milliseconds without any client activity.
   1104  *
   1105  * @param ms the number of milliseconds to spend
   1106  */
   1107 static void
   1108 settle (unsigned int ms)
   1109 {
   1110   const uint64_t start = now_ms ();
   1111 
   1112   do
   1113   {
   1114     if (NULL != extern_daemon)
   1115       MHD_run (extern_daemon);
   1116     sleep_ms (1);
   1117   } while (now_ms () - start < (uint64_t) ms);
   1118 }
   1119 
   1120 
   1121 /**
   1122  * Wait until the daemon has finished tearing down all connections, so that
   1123  * the server side observations are stable.
   1124  *
   1125  * @return non-zero on success, zero on timeout
   1126  */
   1127 static int
   1128 wait_conns_closed (void)
   1129 {
   1130   const uint64_t start = now_ms ();
   1131 
   1132   while (conn_closed != conn_started)
   1133   {
   1134     if (NULL != extern_daemon)
   1135       MHD_run (extern_daemon);
   1136     else
   1137       sleep_ms (1);
   1138     if (now_ms () - start > CLOSE_WAIT_MS)
   1139       return 0;
   1140   }
   1141   return ! 0;
   1142 }
   1143 
   1144 
   1145 /* ------------------------------------------------------------------ */
   1146 /* One scenario against one profile                                   */
   1147 /* ------------------------------------------------------------------ */
   1148 
   1149 static void
   1150 report_failure (const char *what, const char *exp, const char *got)
   1151 {
   1152   failures++;
   1153   fprintf (stderr, "\nFAILED: %s\n", what);
   1154   print_context (stderr);
   1155   if (NULL != exp)
   1156     fprintf (stderr, "  expected:\n%s", exp);
   1157   if (NULL != got)
   1158     fprintf (stderr, "  observed:\n%s", got);
   1159   fflush (stderr);
   1160 }
   1161 
   1162 
   1163 /**
   1164  * Statuses with which MHD legitimately refuses a request that does not fit
   1165  * into the connection memory pool.  A 400 is never one of them.
   1166  *
   1167  * @param status the status code of the first response
   1168  * @return non-zero if the status is a resource limitation
   1169  */
   1170 static int
   1171 is_resource_status (int status)
   1172 {
   1173   return ((413 == status) || (414 == status) || (431 == status) ||
   1174           (500 == status) || (503 == status)) ? ! 0 : 0;
   1175 }
   1176 
   1177 
   1178 /**
   1179  * Run one scenario against the running daemon.
   1180  */
   1181 static void
   1182 run_scenario (const struct scenario *s)
   1183 {
   1184   struct client_result cr;
   1185   struct obs_buf got;
   1186   MHD_socket sk;
   1187   const size_t len = strlen (s->raw);
   1188   int degraded;
   1189 
   1190   cur_scen = s;
   1191   obs_reset (&srv_obs);
   1192   srv_n_req = 0;
   1193   srv_error = 0;
   1194   combos++;
   1195 
   1196   sk = client_connect ();
   1197   client_exchange (sk, s->raw, len, &cr,
   1198                    s->n_resp, (0 == s->alive) && (! s->close_after_send),
   1199                    s->close_after_send ? 0 : 1);
   1200   (void) MHD_socket_close_ (sk);
   1201   /* The server side observation is complete as soon as MHD has closed the
   1202      connection or has answered everything that was expected: the request
   1203      handler always runs before the reply is sent.  Waiting for the whole
   1204      connection teardown is therefore only useful when neither happened.
   1205      MHD keeps a connection around for a while after an error reply in the
   1206      'epoll' modes, so an unconditional wait would cost seconds per scenario
   1207      for no gain at all. */
   1208   if (s->close_after_send)
   1209     settle (SETTLE_MS); /* Give MHD a chance to mis-parse the truncated stream */
   1210   else if ((! s->loose) && (! cr.peer_closed) && (cr.n_resp < s->n_resp))
   1211   {
   1212     if (! wait_conns_closed ())
   1213     {
   1214       fprintf (stderr, "WARNING: timeout waiting for the connection teardown "
   1215                "(started=%u closed=%u).\n", conn_started, conn_closed);
   1216       print_context (stderr);
   1217     }
   1218   }
   1219 
   1220   obs_reset (&got);
   1221   obs_addf (&got, "%s", srv_obs.d);
   1222 
   1223   if (0 != srv_error)
   1224   {
   1225     report_failure ("the request handler could not queue a response",
   1226                     NULL, NULL);
   1227     return;
   1228   }
   1229   if (s->loose)
   1230   {
   1231     /* The daemon survived, which is all this probe asks for. */
   1232     if (1 < verbose)
   1233       printf ("    %-22s survived (loose probe)\n", s->name);
   1234     return;
   1235   }
   1236 
   1237   /* A connection memory pool that is too small for the request is a
   1238      legitimate resource limitation: MHD then answers 413/414/431/500 without
   1239      ever invoking the handler, or it cannot even build the reply header and
   1240      closes the connection without answering a request it did parse.
   1241      Everything MHD *did* parse must still match, and MHD may never parse
   1242      more requests than the stream contains.  A 400 Bad Request is never
   1243      accepted as a resource limitation. */
   1244   degraded = 0;
   1245   if (! s->close_after_send)
   1246   {
   1247     if ((0 != cr.n_resp) && (cr.n_resp < s->n_resp) &&
   1248         is_resource_status (cr.first_status))
   1249       degraded = 1;
   1250     else if ((0 != cr.n_resp) && (0 != strcmp (s->exp, got.d)) &&
   1251              is_resource_status (cr.first_status))
   1252       degraded = 1;
   1253     else if ((0 == cr.n_resp) && (0 != s->n_resp) && cr.peer_closed)
   1254       degraded = 1;
   1255   }
   1256   if (degraded)
   1257   {
   1258     combos_degraded++;
   1259     if (0 != memcmp (s->exp, got.d, strlen (got.d)))
   1260       report_failure ("a request parsed with a small memory pool differs "
   1261                       "from the expectation", s->exp, got.d);
   1262     else if (verbose)
   1263       printf ("    %-22s degraded by the memory pool, relaxed checking\n",
   1264               s->name);
   1265     return;
   1266   }
   1267 
   1268   if (0 != strcmp (s->exp, got.d))
   1269   {
   1270     report_failure ("the parsed requests do not match the expectation",
   1271                     s->exp, got.d);
   1272     return;
   1273   }
   1274   if (srv_n_req != s->n_req)
   1275   {
   1276     char msg[128];
   1277 
   1278     snprintf (msg, sizeof (msg),
   1279               "MHD parsed %u request(s), %u expected",
   1280               (unsigned int) srv_n_req, s->n_req);
   1281     report_failure (msg, NULL, NULL);
   1282     return;
   1283   }
   1284   if (s->close_after_send)
   1285   {
   1286     if (1 < verbose)
   1287       printf ("    %-22s ok (no answer expected)\n", s->name);
   1288     return;
   1289   }
   1290   if (cr.n_resp != s->n_resp)
   1291   {
   1292     char msg[160];
   1293 
   1294     snprintf (msg, sizeof (msg),
   1295               "the client saw %u response(s), %u expected%s",
   1296               cr.n_resp, s->n_resp, cr.timed_out ? " (timed out)" : "");
   1297     report_failure (msg, NULL, cr.rx_len ? cr.rx : "(nothing)");
   1298     return;
   1299   }
   1300   if ((0 != s->status) && (cr.first_status != s->status))
   1301   {
   1302     char msg[128];
   1303 
   1304     snprintf (msg, sizeof (msg),
   1305               "the first response has status %d, %d expected",
   1306               cr.first_status, s->status);
   1307     report_failure (msg, NULL, cr.rx);
   1308     return;
   1309   }
   1310   if (s->alive && cr.peer_closed)
   1311   {
   1312     report_failure ("MHD closed a connection that had to stay alive",
   1313                     NULL, NULL);
   1314     return;
   1315   }
   1316   if ((! s->alive) && (! cr.peer_closed))
   1317   {
   1318     report_failure ("MHD kept a connection that had to be closed",
   1319                     NULL, NULL);
   1320     return;
   1321   }
   1322   if (1 < verbose)
   1323     printf ("    %-22s ok\n", s->name);
   1324 }
   1325 
   1326 
   1327 /* ------------------------------------------------------------------ */
   1328 /* Main test driver                                                   */
   1329 /* ------------------------------------------------------------------ */
   1330 
   1331 /**
   1332  * Start a daemon for @a prof.
   1333  *
   1334  * @param prof the profile to apply
   1335  * @return the daemon, or NULL if it cannot be started
   1336  */
   1337 static struct MHD_Daemon *
   1338 start_daemon_for (const struct MHD_OptMatrixProfile *prof)
   1339 {
   1340   struct MHD_OptionItem ops[8];
   1341   struct MHD_Daemon *d;
   1342   unsigned int n;
   1343   unsigned int flags;
   1344 
   1345   n = mhd_opt_matrix_fill_options (prof, ops,
   1346                                    (unsigned int) (sizeof(ops)
   1347                                                    / sizeof(ops[0])));
   1348   if (0 == n)
   1349     hard_error ("The option array is too small");
   1350   if (mhd_opt_matrix_is_external (prof))
   1351   {
   1352     /* Only meaningful without an internal polling thread; MHD complains
   1353        about the option in every other mode. */
   1354     ops[n - 1].option = MHD_OPTION_APP_FD_SETSIZE;
   1355     ops[n - 1].value = (intptr_t) (FD_SETSIZE);
   1356     ops[n - 1].ptr_value = NULL;
   1357     ops[n].option = MHD_OPTION_END;
   1358     ops[n].value = 0;
   1359     ops[n].ptr_value = NULL;
   1360   }
   1361   flags = mhd_opt_matrix_daemon_flags (prof, MHD_USE_ERROR_LOG, ! 0);
   1362   /* MHD_OPTION_EXTERNAL_LOGGER has to be the first option, otherwise the
   1363      complaints about the options that follow it go to the standard logger. */
   1364   d = MHD_start_daemon (flags,
   1365                         0, NULL, NULL,
   1366                         &ahc_record, NULL,
   1367                         MHD_OPTION_EXTERNAL_LOGGER, &test_log, NULL,
   1368                         MHD_OPTION_ARRAY, ops,
   1369                         MHD_OPTION_NOTIFY_CONNECTION, &conn_notify, NULL,
   1370                         MHD_OPTION_NOTIFY_COMPLETED, &req_completed, NULL,
   1371                         MHD_OPTION_CONNECTION_TIMEOUT,
   1372                         (unsigned int) DAEMON_TIMEOUT,
   1373                         MHD_OPTION_END);
   1374   return d;
   1375 }
   1376 
   1377 
   1378 /**
   1379  * Run the whole battery against one profile.
   1380  *
   1381  * @param prof the profile to test
   1382  * @return zero on success, 99 if the daemon could not be started
   1383  */
   1384 static int
   1385 run_profile (const struct MHD_OptMatrixProfile *prof)
   1386 {
   1387   struct MHD_Daemon *d;
   1388   const union MHD_DaemonInfo *dinfo;
   1389   size_t i;
   1390   int discp;
   1391 
   1392   cur_prof = prof;
   1393   (void) mhd_opt_matrix_describe (prof, cur_prof_desc, sizeof(cur_prof_desc));
   1394   if (! mhd_opt_matrix_profile_supported (prof))
   1395   {
   1396     prof_skipped++;
   1397     if (verbose)
   1398       printf ("--- profile %s SKIPPED (not supported by this build) ---\n",
   1399               cur_prof_desc);
   1400     return 0;
   1401   }
   1402   if (verbose)
   1403   {
   1404     printf ("--- profile %s ---\n", cur_prof_desc);
   1405     fflush (stdout);
   1406   }
   1407 
   1408   conn_started = 0;
   1409   conn_closed = 0;
   1410   extern_daemon = NULL;
   1411   d = start_daemon_for (prof);
   1412   if (NULL == d)
   1413   {
   1414     fprintf (stderr, "Failed to start the daemon for profile %s.\n",
   1415              cur_prof_desc);
   1416     return 99;
   1417   }
   1418   if (mhd_opt_matrix_is_external (prof))
   1419     extern_daemon = d;
   1420   dinfo = MHD_get_daemon_info (d, MHD_DAEMON_INFO_BIND_PORT);
   1421   if ((NULL == dinfo) || (0 == dinfo->port))
   1422   {
   1423     MHD_stop_daemon (d);
   1424     fprintf (stderr, "Cannot get the port of the daemon for profile %s.\n",
   1425              cur_prof_desc);
   1426     return 99;
   1427   }
   1428   global_port = dinfo->port;
   1429 
   1430   /* Not prof->discipline_lvl: MHD_OPTION_STRICT_FOR_CLIENT maps its value,
   1431      so the "legacy-lax" profile really runs at level -3 although it asks for
   1432      -1.  Deciding what a request must do from the configured value instead of
   1433      from the effective one is a trap: MHD_OPTION_STRICT_FOR_CLIENT maps
   1434      every value of -1 or below to level -3. */
   1435   discp = mhd_opt_matrix_effective_discipline (prof);
   1436   for (i = 0; i < NUM_SCENARIOS; ++i)
   1437   {
   1438     const struct scenario *const s = scenarios + i;
   1439 
   1440     if ((NULL != only_scen) && (0 != strcmp (only_scen, s->name)))
   1441       continue;
   1442     if ((discp < s->min_discp) || (discp > s->max_discp))
   1443     {
   1444       scen_skipped++;
   1445       if (1 < verbose)
   1446         printf ("    %-22s skipped (needs discipline level %d..%d)\n",
   1447                 s->name, s->min_discp, s->max_discp);
   1448       continue;
   1449     }
   1450     run_scenario (s);
   1451     if (MAX_FAILURES < failures)
   1452       break;
   1453   }
   1454   cur_scen = NULL;
   1455   extern_daemon = NULL;
   1456   MHD_stop_daemon (d);
   1457   return 0;
   1458 }
   1459 
   1460 
   1461 int
   1462 main (int argc, char *const *argv)
   1463 {
   1464   const uint64_t t_start = now_ms ();
   1465   const struct MHD_OptMatrixProfile *env_prof;
   1466   const char *only = NULL;
   1467   unsigned int num_prof;
   1468   unsigned int i;
   1469   int ret;
   1470 
   1471   for (i = 1; i < (unsigned int) argc; ++i)
   1472   {
   1473     if ((0 == strcmp (argv[i], "-v")) || (0 == strcmp (argv[i], "--verbose")))
   1474       verbose++;
   1475     else if (0 == strncmp (argv[i], "--profile=", 10))
   1476       only = argv[i] + 10;
   1477     else if (0 == strncmp (argv[i], "--scenario=", 11))
   1478       only_scen = argv[i] + 11;
   1479     else if (0 == strcmp (argv[i], "--list-profiles"))
   1480     {
   1481       unsigned int p;
   1482 
   1483       for (p = 0; p < mhd_opt_matrix_num_profiles (); ++p)
   1484         printf ("%s\n", mhd_opt_matrix_profile (p)->name);
   1485       return 0;
   1486     }
   1487   }
   1488 
   1489   strcpy (cur_prof_desc, "(not started)");
   1490 
   1491 #if defined(HAVE_SIGNAL_H) && defined(SIGPIPE)
   1492   if (MHD_YES != MHD_is_feature_supported (MHD_FEATURE_AUTOSUPPRESS_SIGPIPE))
   1493   {
   1494     if (SIG_ERR == signal (SIGPIPE, SIG_IGN))
   1495       hard_error ("Cannot suppress the SIGPIPE signal");
   1496   }
   1497 #endif /* HAVE_SIGNAL_H && SIGPIPE */
   1498 #if defined(HAVE_SIGNAL_H) && defined(SIGABRT)
   1499   (void) signal (SIGABRT, &abort_handler);
   1500 #endif /* HAVE_SIGNAL_H && SIGABRT */
   1501 
   1502   /* Deliberately NOT mhd_panic_tripwire.h: this test installs its own
   1503      panic and SIGABRT handlers, which print the failing input and the
   1504      daemon options along with the diagnostic.  That context is worth
   1505      more here than the generic tripwire, and the two would collide
   1506      (whichever runs last wins).  See TESTING.md, P5. */
   1507   MHD_set_panic_func (&test_panic, NULL);
   1508   mhd_opt_matrix_print_notice ("test_option_matrix");
   1509 
   1510   env_prof = mhd_opt_matrix_from_env ();
   1511   if (NULL != only)
   1512   {
   1513     /* The command line wins over the environment. */
   1514     env_prof = mhd_opt_matrix_lookup (only);
   1515     if (NULL == env_prof)
   1516     {
   1517       fprintf (stderr, "Unknown profile '%s'.\n", only);
   1518       return 99;
   1519     }
   1520   }
   1521   if (NULL != env_prof)
   1522   {
   1523     if (! mhd_opt_matrix_profile_supported (env_prof))
   1524     {
   1525       char desc[256];
   1526 
   1527       printf ("test_option_matrix: the selected profile %s is not supported "
   1528               "by this build, the test is skipped.\n",
   1529               mhd_opt_matrix_describe (env_prof, desc, sizeof(desc)));
   1530       return 77;
   1531     }
   1532     num_prof = 1;
   1533     ret = run_profile (env_prof);
   1534     if (0 != ret)
   1535       return ret;
   1536   }
   1537   else
   1538   {
   1539     num_prof = mhd_opt_matrix_num_profiles ();
   1540     for (i = 0; i < num_prof; ++i)
   1541     {
   1542       ret = run_profile (mhd_opt_matrix_profile (i));
   1543       if (0 != ret)
   1544         return ret;
   1545       if (MAX_FAILURES < failures)
   1546         break;
   1547     }
   1548     if (num_prof == prof_skipped)
   1549     {
   1550       printf ("test_option_matrix: no profile of the matrix is supported by "
   1551               "this build, the test is skipped.\n");
   1552       return 77;
   1553     }
   1554   }
   1555 
   1556   printf ("\ntest_option_matrix: %u profile(s) (%lu skipped), "
   1557           "%u scenario(s), %lu combinations executed "
   1558           "(%lu pool-limited, %lu out of the discipline range), %.1f s\n",
   1559           num_prof,
   1560           prof_skipped,
   1561           (unsigned int) NUM_SCENARIOS,
   1562           combos,
   1563           combos_degraded,
   1564           scen_skipped,
   1565           (double) (now_ms () - t_start) / 1000.0);
   1566   if (0 != failures)
   1567   {
   1568     printf ("test_option_matrix: FAILED (%lu failure(s))\n", failures);
   1569     fflush (stdout);
   1570     return 1;
   1571   }
   1572   printf ("test_option_matrix: PASSED\n");
   1573   fflush (stdout);
   1574   return 0;
   1575 }