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