test_chunked_ext.c (29204B)
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_chunked_ext.c 22 * @brief Regression test for the handling of the chunk-extension part of 23 * the chunk size line of a chunked request body. 24 * 25 * When a chunk size line carries a chunk-extension (RFC 9112, Section 7.1.1) 26 * the length of that line used to be computed as the position of the CR (or 27 * of the bare LF) instead of the position just after the line delimiter. 28 * As a result the line delimiter was not consumed and became part of the 29 * chunk data, shifting all the following body parsing by two (or one) bytes. 30 * That both corrupts the data given to the application and de-synchronises 31 * the request/response stream, which can be abused for HTTP request 32 * smuggling. 33 * 34 * Every sub-case sends, in a single write, a chunked request using a 35 * chunk-extension immediately followed by a pipelined second request on the 36 * same connection. The test verifies the exact bytes received by the 37 * request handler, that exactly one chunked request was processed and that 38 * the pipelined request was parsed correctly. 39 * 40 * The test honours the daemon option matrix of mhd_opt_matrix.h: when one of 41 * the MHD_TEST_* environment variables selects a profile, its connection 42 * memory limit, threading mode and polling backend are applied to every 43 * daemon this test starts. The *client discipline level* is deliberately 44 * NOT taken from the profile: it is the very thing each sub-case varies (a 45 * bare LF is accepted as the chunk size line delimiter only at level -3), so 46 * the per-sub-case value always wins. Without those environment variables 47 * nothing changes. As the sub-cases need a pool that can still hold the 48 * request and the reply, a profile asking for less than #MIN_POOL_FOR_TEST 49 * bytes is raised to that value and the adjustment is reported. 50 * 51 * @author Christian Grothoff 52 */ 53 #include "MHD_config.h" 54 #include "platform.h" 55 #include <microhttpd.h> 56 #include <stdlib.h> 57 #include <string.h> 58 #include <stdio.h> 59 #include <errno.h> 60 61 #ifdef HAVE_STRINGS_H 62 #include <strings.h> 63 #endif /* HAVE_STRINGS_H */ 64 65 #ifndef WINDOWS 66 #include <unistd.h> 67 #endif /* ! WINDOWS */ 68 69 #ifdef _WIN32 70 #ifndef WIN32_LEAN_AND_MEAN 71 #define WIN32_LEAN_AND_MEAN 1 72 #endif /* !WIN32_LEAN_AND_MEAN */ 73 #include <windows.h> 74 #endif /* _WIN32 */ 75 76 #ifdef HAVE_SIGNAL_H 77 #include <signal.h> 78 #endif /* HAVE_SIGNAL_H */ 79 80 #include "mhd_sockets.h" /* only macros used */ 81 #include "mhd_opt_matrix.h" 82 /* Turn any MHD_PANIC() or failing mhd_assert() reached from this 83 test into a marked, classifiable test error (TESTING.md, P5). */ 84 #include "mhd_panic_tripwire.h" 85 86 /** 87 * The smallest connection memory pool this test can work with: the sub-cases 88 * send a chunked request plus a pipelined second request and expect two 89 * complete replies, which does not fit into a smaller pool. 90 */ 91 #define MIN_POOL_FOR_TEST 1024 92 93 #ifndef MHD_STATICSTR_LEN_ 94 /** 95 * Determine length of static string / macro strings at compile time. 96 */ 97 #define MHD_STATICSTR_LEN_(macro) (sizeof(macro) / sizeof(char) - 1) 98 #endif /* ! MHD_STATICSTR_LEN_ */ 99 100 #if MHD_VERSION >= 0x00097701 101 #define TEST_MK_RESPONSE(len,str) \ 102 MHD_create_response_from_buffer_static ((len), (str)) 103 #else /* MHD_VERSION < 0x00097701 */ 104 #define TEST_MK_RESPONSE(len,str) \ 105 MHD_create_response_from_buffer ((len), (void *) (str), \ 106 MHD_RESPMEM_PERSISTENT) 107 #endif /* MHD_VERSION < 0x00097701 */ 108 109 /** 110 * The exit code for a failed test. 111 */ 112 #define EXIT_FAILED_TEST 1 113 114 /** 115 * The exit code for a failure of the test framework itself. 116 */ 117 #define EXIT_HARD_ERROR 99 118 119 /** 120 * The maximum time (in seconds) to wait for any single socket operation. 121 */ 122 #define TEST_SOCKET_TIMEOUT_SEC 5 123 124 /** 125 * The time (in milliseconds) to wait between the two parts of a request 126 * which is intentionally sent in two writes. 127 */ 128 #define TEST_SPLIT_DELAY_MS 50 129 130 /** 131 * The body of the response sent by the test daemon. 132 */ 133 #define RESP_BODY "OK" 134 135 /** 136 * The URL of the chunked request. 137 */ 138 #define URL_FIRST "/first" 139 140 /** 141 * The URL of the pipelined request. 142 */ 143 #define URL_SECOND "/second" 144 145 /** 146 * The head of the chunked request, the chunked body is appended to it. 147 */ 148 #define REQ_FIRST_HEAD \ 149 "POST " URL_FIRST " HTTP/1.1\r\nHost: h\r\n" \ 150 "Transfer-Encoding: chunked\r\n\r\n" 151 152 /** 153 * The complete pipelined request, appended after the chunked body. 154 */ 155 #define REQ_SECOND \ 156 "GET " URL_SECOND " HTTP/1.1\r\nHost: h\r\nConnection: close\r\n\r\n" 157 158 /** 159 * The number of the requests that could be tracked. 160 */ 161 #define MAX_TRACKED_REQS 8 162 163 /** 164 * The maximum size of the tracked request body. 165 */ 166 #define MAX_TRACKED_BODY 256 167 168 /** 169 * The data tracked for a single request. 170 */ 171 struct ReqTrack 172 { 173 /** 174 * The URL of the request. 175 */ 176 char url[64]; 177 178 /** 179 * The body of the request as given to the request handler. 180 */ 181 char body[MAX_TRACKED_BODY]; 182 183 /** 184 * The number of bytes in @a body. 185 */ 186 size_t body_size; 187 188 /** 189 * Non-zero if the body of the request did not fit in @a body. 190 */ 191 int body_overflow; 192 193 /** 194 * Non-zero if the request has been fully received. 195 */ 196 int completed; 197 }; 198 199 /** 200 * The tracked requests. 201 * Written by the daemon thread only, read by the main thread only after 202 * MHD_stop_daemon() (which joins the daemon thread) has returned. 203 */ 204 static struct ReqTrack tracked[MAX_TRACKED_REQS]; 205 206 /** 207 * The number of the used elements in #tracked. 208 */ 209 static unsigned int num_tracked; 210 211 /** 212 * Be verbose. 213 */ 214 static int verbose; 215 216 /** 217 * The profile selected by the environment, NULL if the built-in daemon 218 * configuration is used (which is the case for a stock "make check"). 219 */ 220 static const struct MHD_OptMatrixProfile *test_prof; 221 222 /** 223 * The (possibly adjusted) copy of the profile @a test_prof points to. 224 */ 225 static struct MHD_OptMatrixProfile test_prof_copy; 226 227 228 _MHD_NORETURN static void 229 hard_error (const char *desc, 230 int line_num) 231 { 232 fprintf (stderr, 233 "HARD ERROR: %s at line %d. Last errno: %d (%s).\n", 234 desc, 235 line_num, 236 (int) errno, 237 strerror (errno)); 238 fflush (stderr); 239 exit (EXIT_HARD_ERROR); 240 } 241 242 243 #define hardErrorExit(desc) hard_error ((desc), __LINE__) 244 245 246 /** 247 * Pause the execution for the specified number of milliseconds. 248 * 249 * @param ms the number of the milliseconds to sleep 250 */ 251 static void 252 test_sleep_ms (unsigned int ms) 253 { 254 #if defined(_WIN32) && ! defined(__CYGWIN__) 255 Sleep ((DWORD) ms); 256 #elif defined(HAVE_NANOSLEEP) 257 struct timespec slp; 258 struct timespec rmn; 259 int num_retries = 0; 260 261 slp.tv_sec = (time_t) (ms / 1000); 262 slp.tv_nsec = (long) ((ms % 1000) * 1000000); 263 while (0 != nanosleep (&slp, &rmn)) 264 { 265 if (EINTR != errno) 266 break; 267 if (8 < num_retries++) 268 break; 269 slp = rmn; 270 } 271 #elif defined(HAVE_USLEEP) 272 usleep (((unsigned long) ms) * 1000); 273 #else /* ! HAVE_NANOSLEEP && ! HAVE_USLEEP */ 274 (void) ms; /* Mute compiler warning, the test works without the delay */ 275 #endif /* ! HAVE_NANOSLEEP && ! HAVE_USLEEP */ 276 } 277 278 279 /** 280 * The handler of the requests, tracks the URL and the exact body bytes. 281 */ 282 static enum MHD_Result 283 ahc_track (void *cls, 284 struct MHD_Connection *connection, 285 const char *url, 286 const char *method, 287 const char *version, 288 const char *upload_data, 289 size_t *upload_data_size, 290 void **req_cls) 291 { 292 struct ReqTrack *trk = (struct ReqTrack *) *req_cls; 293 struct MHD_Response *resp; 294 enum MHD_Result ret; 295 296 (void) cls; (void) method; (void) version; 297 298 if (NULL == trk) 299 { 300 if (MAX_TRACKED_REQS <= num_tracked) 301 return MHD_NO; /* Too many requests, the check in the main thread fails */ 302 trk = tracked + num_tracked++; 303 memset (trk, 304 0, 305 sizeof (*trk)); 306 if (sizeof (trk->url) <= strlen (url)) 307 return MHD_NO; 308 memcpy (trk->url, 309 url, 310 strlen (url) + 1); 311 *req_cls = trk; 312 return MHD_YES; 313 } 314 if (0 != *upload_data_size) 315 { 316 if (sizeof (trk->body) < trk->body_size + *upload_data_size) 317 trk->body_overflow = 1; 318 else 319 { 320 memcpy (trk->body + trk->body_size, 321 upload_data, 322 *upload_data_size); 323 trk->body_size += *upload_data_size; 324 } 325 *upload_data_size = 0; 326 return MHD_YES; 327 } 328 trk->completed = 1; 329 resp = TEST_MK_RESPONSE (MHD_STATICSTR_LEN_ (RESP_BODY), 330 RESP_BODY); 331 if (NULL == resp) 332 return MHD_NO; 333 ret = MHD_queue_response (connection, 334 MHD_HTTP_OK, 335 resp); 336 MHD_destroy_response (resp); 337 return ret; 338 } 339 340 341 /** 342 * Set send and receive timeouts on the socket so that the test cannot 343 * block indefinitely. 344 * 345 * @param sk the socket to set the timeouts on 346 * @return non-zero if succeed, zero if failed 347 */ 348 static int 349 set_socket_timeouts (MHD_socket sk) 350 { 351 #if defined(MHD_WINSOCK_SOCKETS) 352 DWORD tv = (DWORD) (TEST_SOCKET_TIMEOUT_SEC * 1000); 353 #else /* ! MHD_WINSOCK_SOCKETS */ 354 struct timeval tv; 355 356 tv.tv_sec = (time_t) TEST_SOCKET_TIMEOUT_SEC; 357 tv.tv_usec = 0; 358 #endif /* ! MHD_WINSOCK_SOCKETS */ 359 if (0 != setsockopt (sk, 360 SOL_SOCKET, 361 SO_RCVTIMEO, 362 (const void *) &tv, 363 (socklen_t) sizeof (tv))) 364 return 0; 365 if (0 != setsockopt (sk, 366 SOL_SOCKET, 367 SO_SNDTIMEO, 368 (const void *) &tv, 369 (socklen_t) sizeof (tv))) 370 return 0; 371 return ! 0; 372 } 373 374 375 /** 376 * Open a TCP connection to the given port on the loopback interface. 377 * 378 * @param port the port to connect to 379 * @return the connected socket, MHD_INVALID_SOCKET on failure 380 */ 381 static MHD_socket 382 connect_to_port (uint16_t port) 383 { 384 MHD_socket sk; 385 struct sockaddr_in sa; 386 387 sk = socket (PF_INET, 388 SOCK_STREAM, 389 0); 390 if (MHD_INVALID_SOCKET == sk) 391 return MHD_INVALID_SOCKET; 392 if (! set_socket_timeouts (sk)) 393 { 394 (void) MHD_socket_close_ (sk); 395 return MHD_INVALID_SOCKET; 396 } 397 memset (&sa, 398 0, 399 sizeof (sa)); 400 sa.sin_family = AF_INET; 401 sa.sin_port = htons (port); 402 sa.sin_addr.s_addr = htonl (INADDR_LOOPBACK); 403 if (0 != connect (sk, 404 (const struct sockaddr *) &sa, 405 (socklen_t) sizeof (sa))) 406 { 407 (void) MHD_socket_close_ (sk); 408 return MHD_INVALID_SOCKET; 409 } 410 return sk; 411 } 412 413 414 /** 415 * Send all the given data over the socket. 416 * 417 * @param sk the socket to use 418 * @param buf the data to send 419 * @param size the size of @a buf 420 * @return non-zero if succeed, zero if failed 421 */ 422 static int 423 send_all (MHD_socket sk, 424 const char *buf, 425 size_t size) 426 { 427 size_t off = 0; 428 429 while (off < size) 430 { 431 ssize_t res; 432 433 res = MHD_send_ (sk, 434 buf + off, 435 size - off); 436 if (0 > res) 437 { 438 if (MHD_SCKT_ERR_IS_EINTR_ (MHD_socket_get_error_ ())) 439 continue; 440 return 0; 441 } 442 if (0 == res) 443 return 0; 444 off += (size_t) res; 445 } 446 return ! 0; 447 } 448 449 450 /** 451 * Receive data from the socket until the remote side closes the connection, 452 * the buffer is full or the receive timeout expires. 453 * 454 * @param sk the socket to use 455 * @param[out] buf the buffer to fill 456 * @param buf_size the size of @a buf, the result is always zero-terminated, 457 * therefore at most @a buf_size minus one bytes are received 458 * @return the number of the received bytes 459 */ 460 static size_t 461 recv_all (MHD_socket sk, 462 char *buf, 463 size_t buf_size) 464 { 465 size_t off = 0; 466 467 while (off + 1 < buf_size) 468 { 469 ssize_t res; 470 471 res = MHD_recv_ (sk, 472 buf + off, 473 buf_size - 1 - off); 474 if (0 > res) 475 { 476 if (MHD_SCKT_ERR_IS_EINTR_ (MHD_socket_get_error_ ())) 477 continue; 478 break; /* Timeout or error */ 479 } 480 if (0 == res) 481 break; /* The remote side closed the connection */ 482 off += (size_t) res; 483 } 484 buf[off] = 0; 485 return off; 486 } 487 488 489 /** 490 * Count the number of the non-overlapping occurrences of @a needle 491 * in @a haystack. 492 * 493 * @param haystack the zero-terminated string to search in 494 * @param needle the zero-terminated string to search for 495 * @return the number of the found occurrences 496 */ 497 static unsigned int 498 count_substr (const char *haystack, 499 const char *needle) 500 { 501 const size_t needle_len = strlen (needle); 502 unsigned int num = 0; 503 const char *pos = haystack; 504 505 if (0 == needle_len) 506 return 0; 507 while (NULL != (pos = strstr (pos, needle))) 508 { 509 ++num; 510 pos += needle_len; 511 } 512 return num; 513 } 514 515 516 /** 517 * Print the data to the standard error stream, replacing the non-printable 518 * characters with their escaped hexadecimal form. 519 * 520 * @param data the data to print 521 * @param size the size of @a data 522 */ 523 static void 524 print_escaped (const char *data, 525 size_t size) 526 { 527 size_t i; 528 529 for (i = 0; i < size; ++i) 530 { 531 const unsigned char chr = (unsigned char) data[i]; 532 533 if ((0x20 <= chr) && (0x7e >= chr) && ('\\' != chr)) 534 fputc ((int) chr, 535 stderr); 536 else if ('\\' == chr) 537 fputs ("\\\\", 538 stderr); 539 else if ('\r' == chr) 540 fputs ("\\r", 541 stderr); 542 else if ('\n' == chr) 543 fputs ("\\n", 544 stderr); 545 else if ('\t' == chr) 546 fputs ("\\t", 547 stderr); 548 else 549 fprintf (stderr, 550 "\\x%02X", 551 (unsigned int) chr); 552 } 553 } 554 555 556 /** 557 * The description of a single sub-case. 558 */ 559 struct ChunkCase 560 { 561 /** 562 * The human readable name of the sub-case. 563 */ 564 const char *name; 565 566 /** 567 * The chunked body of the request, including the terminating chunk and 568 * the (empty) trailer section. 569 */ 570 const char *chunked; 571 572 /** 573 * The exact bytes that the request handler must receive. 574 */ 575 const char *expected; 576 577 /** 578 * The number of the bytes in @a expected. 579 */ 580 size_t expected_size; 581 582 /** 583 * If non-zero, the request is sent in two writes and this is the number of 584 * the bytes of @a chunked included in the first write. 585 */ 586 size_t split_at; 587 588 /** 589 * The value for MHD_OPTION_CLIENT_DISCIPLINE_LVL. 590 */ 591 int discipline; 592 593 /** 594 * Non-zero if the sub-case requires the support of the bare LF as the 595 * line delimiter of the chunk size line. 596 */ 597 int need_bare_lf; 598 }; 599 600 #define CHUNK_CASE(nm,ch,exp,spl,dsc,blf) \ 601 { (nm), (ch), (exp), MHD_STATICSTR_LEN_ (exp), (spl), (dsc), (blf) } 602 603 /** 604 * The sub-cases. 605 * All of them must be processed identically: the handler must receive 606 * exactly the expected bytes, and the pipelined request must be processed 607 * as the second (and the last) request on the connection. 608 */ 609 static const struct ChunkCase chunkcases[] = { 610 CHUNK_CASE ("no chunk-extension (control)", 611 "5\r\nHELLO\r\n0\r\n\r\n", 612 "HELLO", 0, 0, 0), 613 CHUNK_CASE ("chunk-extension with a value", 614 "5;ext=val\r\nHELLO\r\n0\r\n\r\n", 615 "HELLO", 0, 0, 0), 616 CHUNK_CASE ("chunk-extension without a value", 617 "5;ext\r\nHELLO\r\n0\r\n\r\n", 618 "HELLO", 0, 0, 0), 619 CHUNK_CASE ("chunk-extension after a bad whitespace (space)", 620 "5 ;ext=val\r\nHELLO\r\n0\r\n\r\n", 621 "HELLO", 0, 0, 0), 622 CHUNK_CASE ("chunk-extension after a bad whitespace (tab)", 623 "5\t;ext\r\nHELLO\r\n0\r\n\r\n", 624 "HELLO", 0, 0, 0), 625 CHUNK_CASE ("chunk-extension with a quoted-string value", 626 "5;ext=\"quoted; value\"\r\nHELLO\r\n0\r\n\r\n", 627 "HELLO", 0, 0, 0), 628 CHUNK_CASE ("several chunk-extensions", 629 "5;e1=v1;e2=\"v 2\";e3\r\nHELLO\r\n0\r\n\r\n", 630 "HELLO", 0, 0, 0), 631 CHUNK_CASE ("two chunks, both with a chunk-extension", 632 "3;a=b\r\nabc\r\n4;c=d\r\ndefg\r\n0\r\n\r\n", 633 "abcdefg", 0, 0, 0), 634 CHUNK_CASE ("two chunks, only the first with a chunk-extension", 635 "3;a=b\r\nabc\r\n4\r\ndefg\r\n0\r\n\r\n", 636 "abcdefg", 0, 0, 0), 637 CHUNK_CASE ("two chunks, only the second with a chunk-extension", 638 "3\r\nabc\r\n4;c=d\r\ndefg\r\n0\r\n\r\n", 639 "abcdefg", 0, 0, 0), 640 CHUNK_CASE ("chunk-extension on the terminating chunk as well", 641 "3;a=b\r\nabc\r\n4;c=d\r\ndefg\r\n0;last=1\r\n\r\n", 642 "abcdefg", 0, 0, 0), 643 CHUNK_CASE ("chunk data with an embedded CRLF", 644 "7;ext=val\r\nAB\r\nCDE\r\n0\r\n\r\n", 645 "AB\r\nCDE", 0, 0, 0), 646 CHUNK_CASE ("chunk-extension line split between two writes", 647 "5;ext=val\r\nHELLO\r\n0\r\n\r\n", 648 "HELLO", 4, 0, 0), 649 CHUNK_CASE ("chunk-extension line delimiter split between two writes", 650 "5;ext=val\r\nHELLO\r\n0\r\n\r\n", 651 "HELLO", 10, 0, 0), 652 CHUNK_CASE ("chunk-extension with a bare LF line delimiter", 653 "5;ext=val\nHELLO\r\n0\r\n\r\n", 654 "HELLO", 0, -3, 1), 655 CHUNK_CASE ("two chunks with a bare LF line delimiter", 656 "3;a=b\nabc\r\n4;c=d\ndefg\r\n0\r\n\r\n", 657 "abcdefg", 0, -3, 1) 658 }; 659 660 /** 661 * The sub-case used to detect whether this build accepts the bare LF as the 662 * line delimiter of the chunk size line. It does not use any 663 * chunk-extension, therefore it is not affected by the tested bug. 664 */ 665 static const struct ChunkCase bare_lf_probe = 666 CHUNK_CASE ("bare LF support probe", 667 "5\nHELLO\r\n0\r\n\r\n", 668 "HELLO", 0, -3, 0); 669 670 671 /** 672 * Run a single sub-case. 673 * 674 * @param cc the sub-case to run 675 * @param quiet if non-zero, do not report the failures 676 * @return non-zero if the sub-case succeeded, zero if it failed 677 */ 678 static int 679 run_subcase (const struct ChunkCase *cc, 680 int quiet) 681 { 682 struct MHD_Daemon *d; 683 const union MHD_DaemonInfo *dinfo; 684 uint16_t port; 685 MHD_socket sk; 686 char req[1024]; 687 char resp[4096]; 688 size_t req_size; 689 size_t chunked_size; 690 size_t resp_size; 691 int failed = 0; 692 int sent_ok; 693 struct MHD_OptMatrixProfile prof; 694 struct MHD_OptionItem ops[8]; 695 unsigned int flags; 696 697 chunked_size = strlen (cc->chunked); 698 req_size = MHD_STATICSTR_LEN_ (REQ_FIRST_HEAD) + chunked_size 699 + MHD_STATICSTR_LEN_ (REQ_SECOND); 700 if (sizeof (req) <= req_size) 701 hardErrorExit ("The request does not fit in the buffer"); 702 memcpy (req, 703 REQ_FIRST_HEAD, 704 MHD_STATICSTR_LEN_ (REQ_FIRST_HEAD)); 705 memcpy (req + MHD_STATICSTR_LEN_ (REQ_FIRST_HEAD), 706 cc->chunked, 707 chunked_size); 708 memcpy (req + MHD_STATICSTR_LEN_ (REQ_FIRST_HEAD) + chunked_size, 709 REQ_SECOND, 710 MHD_STATICSTR_LEN_ (REQ_SECOND) + 1); 711 712 num_tracked = 0; 713 memset (tracked, 714 0, 715 sizeof (tracked)); 716 717 /* The profile (if any) supplies the memory limit, the threading mode and 718 the polling backend. The client discipline level always comes from the 719 sub-case, see the file comment. */ 720 if (NULL != test_prof) 721 prof = *test_prof; 722 else 723 { 724 memset (&prof, 0, sizeof (prof)); 725 prof.name = "built-in"; 726 prof.threading = MHD_OPT_MATRIX_THR_INTERNAL; 727 prof.poll_backend = MHD_OPT_MATRIX_POLL_SELECT; 728 } 729 prof.discipline_lvl = 0; 730 prof.use_legacy_strict = 0; 731 if (0 == mhd_opt_matrix_fill_options (&prof, ops, 732 (unsigned int) (sizeof (ops) 733 / sizeof (ops[0])))) 734 hardErrorExit ("The daemon option array is too small"); 735 /* The client of this test is a plain blocking socket client, so external 736 polling is served with an internal polling thread instead. */ 737 flags = mhd_opt_matrix_daemon_flags (&prof, MHD_USE_ERROR_LOG, 0); 738 739 d = MHD_start_daemon (flags, 740 0, 741 NULL, NULL, 742 &ahc_track, NULL, 743 MHD_OPTION_ARRAY, ops, 744 MHD_OPTION_CLIENT_DISCIPLINE_LVL, cc->discipline, 745 MHD_OPTION_CONNECTION_TIMEOUT, 746 (unsigned int) TEST_SOCKET_TIMEOUT_SEC, 747 MHD_OPTION_END); 748 if (NULL == d) 749 hardErrorExit ("Cannot start the test daemon"); 750 dinfo = MHD_get_daemon_info (d, 751 MHD_DAEMON_INFO_BIND_PORT); 752 if ((NULL == dinfo) || (0 == dinfo->port)) 753 { 754 MHD_stop_daemon (d); 755 hardErrorExit ("Cannot detect the port used by the test daemon"); 756 } 757 port = (uint16_t) dinfo->port; 758 759 sk = connect_to_port (port); 760 if (MHD_INVALID_SOCKET == sk) 761 { 762 MHD_stop_daemon (d); 763 hardErrorExit ("Cannot connect to the test daemon"); 764 } 765 766 if (0 == cc->split_at) 767 sent_ok = send_all (sk, 768 req, 769 req_size); 770 else 771 { 772 const size_t first_part = 773 MHD_STATICSTR_LEN_ (REQ_FIRST_HEAD) + cc->split_at; 774 775 if (first_part >= req_size) 776 hardErrorExit ("Wrong 'split_at' value in the sub-case"); 777 sent_ok = send_all (sk, 778 req, 779 first_part); 780 if (sent_ok) 781 { 782 test_sleep_ms (TEST_SPLIT_DELAY_MS); 783 sent_ok = send_all (sk, 784 req + first_part, 785 req_size - first_part); 786 } 787 } 788 if (! sent_ok) 789 { 790 if (! quiet) 791 fprintf (stderr, 792 "FAILED: sub-case '%s': cannot send the request.\n", 793 cc->name); 794 (void) MHD_socket_close_ (sk); 795 MHD_stop_daemon (d); 796 return 0; 797 } 798 resp_size = recv_all (sk, 799 resp, 800 sizeof (resp)); 801 (void) MHD_socket_close_ (sk); 802 /* MHD_stop_daemon() joins the daemon thread, therefore all the data 803 tracked by the request handler is safely visible afterwards. */ 804 MHD_stop_daemon (d); 805 806 if (2 != num_tracked) 807 { 808 if (! quiet) 809 fprintf (stderr, 810 "FAILED: sub-case '%s': the number of the processed requests " 811 "is %u, while exactly 2 requests are expected.\n", 812 cc->name, 813 num_tracked); 814 failed = 1; 815 } 816 if (1 <= num_tracked) 817 { 818 const struct ReqTrack *const trk = tracked + 0; 819 820 if (0 != strcmp (trk->url, 821 URL_FIRST)) 822 { 823 if (! quiet) 824 fprintf (stderr, 825 "FAILED: sub-case '%s': the URL of the first request is " 826 "'%s', while '%s' is expected.\n", 827 cc->name, 828 trk->url, 829 URL_FIRST); 830 failed = 1; 831 } 832 if (0 != trk->body_overflow) 833 { 834 if (! quiet) 835 fprintf (stderr, 836 "FAILED: sub-case '%s': the body of the first request is " 837 "too large.\n", 838 cc->name); 839 failed = 1; 840 } 841 else if ((cc->expected_size != trk->body_size) || 842 (0 != memcmp (cc->expected, 843 trk->body, 844 cc->expected_size))) 845 { 846 if (! quiet) 847 { 848 fprintf (stderr, 849 "FAILED: sub-case '%s': the body of the first request is '", 850 cc->name); 851 print_escaped (trk->body, 852 trk->body_size); 853 fprintf (stderr, 854 "' (%u bytes), while '", 855 (unsigned) trk->body_size); 856 print_escaped (cc->expected, 857 cc->expected_size); 858 fprintf (stderr, 859 "' (%u bytes) is expected.\n", 860 (unsigned) cc->expected_size); 861 } 862 failed = 1; 863 } 864 if (0 == trk->completed) 865 { 866 if (! quiet) 867 fprintf (stderr, 868 "FAILED: sub-case '%s': the first request has not been " 869 "completed.\n", 870 cc->name); 871 failed = 1; 872 } 873 } 874 if (2 <= num_tracked) 875 { 876 const struct ReqTrack *const trk = tracked + 1; 877 878 if (0 != strcmp (trk->url, 879 URL_SECOND)) 880 { 881 if (! quiet) 882 fprintf (stderr, 883 "FAILED: sub-case '%s': the URL of the pipelined request is " 884 "'%s', while '%s' is expected. The request stream has been " 885 "de-synchronised.\n", 886 cc->name, 887 trk->url, 888 URL_SECOND); 889 failed = 1; 890 } 891 if ((0 != trk->body_size) || (0 != trk->body_overflow)) 892 { 893 if (! quiet) 894 fprintf (stderr, 895 "FAILED: sub-case '%s': the pipelined request has a body of " 896 "%u bytes, while no body is expected.\n", 897 cc->name, 898 (unsigned) trk->body_size); 899 failed = 1; 900 } 901 if (0 == trk->completed) 902 { 903 if (! quiet) 904 fprintf (stderr, 905 "FAILED: sub-case '%s': the pipelined request has not been " 906 "completed.\n", 907 cc->name); 908 failed = 1; 909 } 910 } 911 if (2 != count_substr (resp, 912 "HTTP/1.1 ")) 913 { 914 if (! quiet) 915 fprintf (stderr, 916 "FAILED: sub-case '%s': the number of the replies is %u, " 917 "while exactly 2 replies are expected. The reply stream is:\n" 918 "%.*s\n", 919 cc->name, 920 count_substr (resp, "HTTP/1.1 "), 921 (int) resp_size, 922 resp); 923 failed = 1; 924 } 925 else if (2 != count_substr (resp, 926 "HTTP/1.1 200 ")) 927 { 928 if (! quiet) 929 fprintf (stderr, 930 "FAILED: sub-case '%s': not all replies have the '200' status " 931 "code. The reply stream is:\n%.*s\n", 932 cc->name, 933 (int) resp_size, 934 resp); 935 failed = 1; 936 } 937 938 if ((0 == failed) && verbose) 939 printf ("PASSED: %s\n", 940 cc->name); 941 return ! failed; 942 } 943 944 945 int 946 main (int argc, char *const *argv) 947 { 948 unsigned int num_failed = 0; 949 unsigned int num_skipped = 0; 950 int bare_lf_supported; 951 size_t i; 952 953 verbose = 0; 954 for (i = 1; i < (size_t) argc; ++i) 955 { 956 if ((0 == strcmp (argv[i], "-v")) || 957 (0 == strcmp (argv[i], "--verbose"))) 958 verbose = 1; 959 } 960 961 if (MHD_YES != MHD_is_feature_supported (MHD_FEATURE_AUTOSUPPRESS_SIGPIPE)) 962 { 963 #if defined(HAVE_SIGNAL_H) && defined(SIGPIPE) 964 if (SIG_ERR == signal (SIGPIPE, SIG_IGN)) 965 hardErrorExit ("Cannot suppress the SIGPIPE signal"); 966 #else /* ! HAVE_SIGNAL_H || ! SIGPIPE */ 967 fprintf (stderr, 968 "Cannot suppress the SIGPIPE signal.\n"); 969 #endif /* ! HAVE_SIGNAL_H || ! SIGPIPE */ 970 } 971 972 mhd_opt_matrix_print_notice ("test_chunked_ext"); 973 test_prof = mhd_opt_matrix_from_env (); 974 if (NULL != test_prof) 975 { 976 char desc[256]; 977 978 test_prof_copy = *test_prof; 979 test_prof = &test_prof_copy; 980 if (! mhd_opt_matrix_profile_supported (test_prof)) 981 { 982 printf ("test_chunked_ext: the selected profile %s is not supported by " 983 "this build, the test is skipped.\n", 984 mhd_opt_matrix_describe (test_prof, desc, sizeof (desc))); 985 return 77; 986 } 987 if (mhd_opt_matrix_raise_mem_limit (&test_prof_copy, MIN_POOL_FOR_TEST)) 988 printf ("test_chunked_ext: the connection memory limit of the profile " 989 "was raised to %u, the smallest pool this test can work " 990 "with.\n", (unsigned) MIN_POOL_FOR_TEST); 991 } 992 993 /* Detect whether the bare LF is accepted as the line delimiter of the 994 chunk size line with the lowest client discipline level. */ 995 bare_lf_supported = run_subcase (&bare_lf_probe, 996 1); 997 if ((! bare_lf_supported) && verbose) 998 printf ("The bare LF as the chunk size line delimiter is not supported " 999 "by this build, the related sub-cases are skipped.\n"); 1000 1001 for (i = 0; i < sizeof (chunkcases) / sizeof (chunkcases[0]); ++i) 1002 { 1003 const struct ChunkCase *const cc = chunkcases + i; 1004 1005 if ((0 != cc->need_bare_lf) && (! bare_lf_supported)) 1006 { 1007 ++num_skipped; 1008 continue; 1009 } 1010 /* Report the sub-case before running it, so that the failing sub-case 1011 can be identified even if the daemon aborts the process. */ 1012 fprintf (stderr, 1013 "Running sub-case: %s\n", 1014 cc->name); 1015 fflush (stderr); 1016 if (! run_subcase (cc, 1017 0)) 1018 ++num_failed; 1019 } 1020 1021 if (0 != num_failed) 1022 { 1023 fprintf (stderr, 1024 "FAILED: %u sub-case(s) failed.\n", 1025 num_failed); 1026 return EXIT_FAILED_TEST; 1027 } 1028 if (verbose) 1029 printf ("All sub-cases passed (%u sub-case(s) skipped).\n", 1030 num_skipped); 1031 return 0; 1032 }