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 }