test_toolarge.c (57417B)
1 /* 2 This file is part of libmicrohttpd 3 Copyright (C) 2014-2022 Evgeny Grin (Karlson2k) 4 Copyright (C) 2007, 2009, 2011 Christian Grothoff 5 6 libmicrohttpd is free software; you can redistribute it and/or modify 7 it under the terms of the GNU General Public License as published 8 by the Free Software Foundation; either version 2, or (at your 9 option) any later version. 10 11 libmicrohttpd is distributed in the hope that it will be useful, but 12 WITHOUT ANY WARRANTY; without even the implied warranty of 13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 General Public License for more details. 15 16 You should have received a copy of the GNU General Public License 17 along with libmicrohttpd; see the file COPYING. If not, write to the 18 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 19 Boston, MA 02110-1301, USA. 20 */ 21 /** 22 * @file test_toolarge.c 23 * @brief Testcase for handling of data larger then buffers. 24 * @details Testcases for handling of various situations when data cannot fit 25 * the buffers. Address sanitizers and debug asserts should increase 26 * number of problems detected by this test (detect actual overrun). 27 * Tests start with valid sizes to ensure that normal data is processed 28 * correctly and then sizes are monotonically increased to ensure that 29 * overflow is handled correctly at all stages in all codepaths. 30 * Tests with valid sizes are repeated several times to ensure that 31 * tests are failed because of overflow, but because of second run. 32 * @author Karlson2k (Evgeny Grin) 33 * @author Christian Grothoff 34 */ 35 #include "mhd_options.h" 36 #include "platform.h" 37 #include <curl/curl.h> 38 #include <microhttpd.h> 39 #include <stdlib.h> 40 #include <string.h> 41 #include <time.h> 42 #include <errno.h> 43 #include "mhd_has_in_name.h" 44 #include "mhd_has_param.h" 45 #include "mhd_sockets.h" /* only macros used */ 46 47 #ifdef HAVE_STRINGS_H 48 #include <strings.h> 49 #endif /* HAVE_STRINGS_H */ 50 51 #ifdef _WIN32 52 #ifndef WIN32_LEAN_AND_MEAN 53 #define WIN32_LEAN_AND_MEAN 1 54 #endif /* !WIN32_LEAN_AND_MEAN */ 55 #include <windows.h> 56 #endif 57 58 #ifndef WINDOWS 59 #include <unistd.h> 60 #include <sys/socket.h> 61 #endif 62 63 #ifdef HAVE_LIMITS_H 64 #include <limits.h> 65 #endif /* HAVE_LIMITS_H */ 66 67 #if defined(MHD_CPU_COUNT) && (MHD_CPU_COUNT + 0) < 2 68 #undef MHD_CPU_COUNT 69 #endif 70 #if ! defined(MHD_CPU_COUNT) 71 #define MHD_CPU_COUNT 2 72 #endif 73 #if MHD_CPU_COUNT > 32 74 #undef MHD_CPU_COUNT 75 /* Limit to reasonable value */ 76 #define MHD_CPU_COUNT 32 77 #endif /* MHD_CPU_COUNT > 32 */ 78 79 80 #if defined(HAVE___FUNC__) 81 #define externalErrorExit(ignore) \ 82 _externalErrorExit_func(NULL, __func__, __LINE__) 83 #define externalErrorExitDesc(errDesc) \ 84 _externalErrorExit_func(errDesc, __func__, __LINE__) 85 #define libcurlErrorExit(ignore) \ 86 _libcurlErrorExit_func(NULL, __func__, __LINE__) 87 #define libcurlErrorExitDesc(errDesc) \ 88 _libcurlErrorExit_func(errDesc, __func__, __LINE__) 89 #define mhdErrorExit(ignore) \ 90 _mhdErrorExit_func(NULL, __func__, __LINE__) 91 #define mhdErrorExitDesc(errDesc) \ 92 _mhdErrorExit_func(errDesc, __func__, __LINE__) 93 #elif defined(HAVE___FUNCTION__) 94 #define externalErrorExit(ignore) \ 95 _externalErrorExit_func(NULL, __FUNCTION__, __LINE__) 96 #define externalErrorExitDesc(errDesc) \ 97 _externalErrorExit_func(errDesc, __FUNCTION__, __LINE__) 98 #define libcurlErrorExit(ignore) \ 99 _libcurlErrorExit_func(NULL, __FUNCTION__, __LINE__) 100 #define libcurlErrorExitDesc(errDesc) \ 101 _libcurlErrorExit_func(errDesc, __FUNCTION__, __LINE__) 102 #define mhdErrorExit(ignore) \ 103 _mhdErrorExit_func(NULL, __FUNCTION__, __LINE__) 104 #define mhdErrorExitDesc(errDesc) \ 105 _mhdErrorExit_func(errDesc, __FUNCTION__, __LINE__) 106 #else 107 #define externalErrorExit(ignore) _externalErrorExit_func(NULL, NULL, __LINE__) 108 #define externalErrorExitDesc(errDesc) \ 109 _externalErrorExit_func(errDesc, NULL, __LINE__) 110 #define libcurlErrorExit(ignore) _libcurlErrorExit_func(NULL, NULL, __LINE__) 111 #define libcurlErrorExitDesc(errDesc) \ 112 _libcurlErrorExit_func(errDesc, NULL, __LINE__) 113 #define mhdErrorExit(ignore) _mhdErrorExit_func(NULL, NULL, __LINE__) 114 #define mhdErrorExitDesc(errDesc) _mhdErrorExit_func(errDesc, NULL, __LINE__) 115 #endif 116 117 118 _MHD_NORETURN static void 119 _externalErrorExit_func (const char *errDesc, const char *funcName, int lineNum) 120 { 121 if ((NULL != errDesc) && (0 != errDesc[0])) 122 fprintf (stderr, "%s", errDesc); 123 else 124 fprintf (stderr, "System or external library call failed"); 125 if ((NULL != funcName) && (0 != funcName[0])) 126 fprintf (stderr, " in %s", funcName); 127 if (0 < lineNum) 128 fprintf (stderr, " at line %d", lineNum); 129 130 fprintf (stderr, ".\nLast errno value: %d (%s)\n", (int) errno, 131 strerror (errno)); 132 #ifdef MHD_WINSOCK_SOCKETS 133 fprintf (stderr, "WSAGetLastError() value: %d\n", (int) WSAGetLastError ()); 134 #endif /* MHD_WINSOCK_SOCKETS */ 135 fflush (stderr); 136 exit (99); 137 } 138 139 140 static char libcurl_errbuf[CURL_ERROR_SIZE] = ""; 141 142 _MHD_NORETURN static void 143 _libcurlErrorExit_func (const char *errDesc, const char *funcName, int lineNum) 144 { 145 if ((NULL != errDesc) && (0 != errDesc[0])) 146 fprintf (stderr, "%s", errDesc); 147 else 148 fprintf (stderr, "CURL library call failed"); 149 if ((NULL != funcName) && (0 != funcName[0])) 150 fprintf (stderr, " in %s", funcName); 151 if (0 < lineNum) 152 fprintf (stderr, " at line %d", lineNum); 153 154 fprintf (stderr, ".\nLast errno value: %d (%s)\n", (int) errno, 155 strerror (errno)); 156 if (0 != libcurl_errbuf[0]) 157 fprintf (stderr, "Last libcurl error details: %s\n", libcurl_errbuf); 158 159 fflush (stderr); 160 exit (99); 161 } 162 163 164 _MHD_NORETURN static void 165 _mhdErrorExit_func (const char *errDesc, const char *funcName, int lineNum) 166 { 167 if ((NULL != errDesc) && (0 != errDesc[0])) 168 fprintf (stderr, "%s", errDesc); 169 else 170 fprintf (stderr, "MHD unexpected error"); 171 if ((NULL != funcName) && (0 != funcName[0])) 172 fprintf (stderr, " in %s", funcName); 173 if (0 < lineNum) 174 fprintf (stderr, " at line %d", lineNum); 175 176 fprintf (stderr, ".\nLast errno value: %d (%s)\n", (int) errno, 177 strerror (errno)); 178 179 fflush (stderr); 180 exit (8); 181 } 182 183 184 /* Could be increased to facilitate debugging */ 185 #define TIMEOUTS_VAL 5 186 187 #define EXPECTED_URI_BASE_PATH "/a" 188 189 #define URL_SCHEME_HOST "http:/" "/127.0.0.1" 190 191 #define N1_HEADER_NAME "n" 192 #define N1_HEADER_VALUE "1" 193 #define N1_HEADER N1_HEADER_NAME ": " N1_HEADER_VALUE 194 #define N1_HEADER_CRLF N1_HEADER "\r\n" 195 196 #define BUFFER_SIZE 1024 197 198 /* The size of the test element that must pass the test */ 199 #ifndef MHD_ASAN_POISON_ACTIVE 200 #define TEST_OK_SIZE (BUFFER_SIZE - 384) 201 #else /* MHD_ASAN_POISON_ACTIVE */ 202 #define TEST_OK_SIZE (BUFFER_SIZE - 384 - 80) 203 #endif /* MHD_ASAN_POISON_ACTIVE */ 204 205 /* The size of the test element where tests are started */ 206 #define TEST_START_SIZE (TEST_OK_SIZE - 16) 207 208 /* The size of the test element that must definitely fail */ 209 #define TEST_FAIL_SIZE (BUFFER_SIZE + 32) 210 211 /* Special value for request many headers test. 212 * MHD uses the same buffer to store headers strings and pointers to the strings 213 * so allocation is multiplied for small request header. */ 214 /* The size of the test element that must pass the test */ 215 #define TEST_RQ_N1_OK_SIZE 50 216 217 /* The size of the test element where tests are started */ 218 #define TEST_RQ_N1_START_SIZE (TEST_RQ_N1_OK_SIZE - 32) 219 220 /* Global parameters */ 221 static int verbose; /**< Be verbose */ 222 static int oneone; /**< If false use HTTP/1.0 for requests*/ 223 static uint16_t global_port; /**< MHD daemons listen port number */ 224 static int large_req_method; /**< Large request method */ 225 static int large_req_url; /**< Large request URL */ 226 static int large_req_header_name; /**< Large request single header name */ 227 static int large_req_header_value; /**< Large request single header value */ 228 static int large_req_headers; /**< Large request headers */ 229 static int large_rsp_header_name; /**< Large response single header name */ 230 static int large_rsp_header_value; /**< Large response single header value */ 231 static int large_rsp_headers; /**< Large response headers */ 232 static int response_timeout_val = TIMEOUTS_VAL; 233 234 /* Current test parameters */ 235 /* * Moved to local variables * */ 236 237 /* Static helper variables */ 238 /* * None for this test * */ 239 240 static void 241 test_global_init (void) 242 { 243 libcurl_errbuf[0] = 0; 244 245 if (0 != curl_global_init (CURL_GLOBAL_WIN32)) 246 externalErrorExit (); 247 } 248 249 250 static void 251 test_global_cleanup (void) 252 { 253 curl_global_cleanup (); 254 } 255 256 257 struct headers_check_result 258 { 259 unsigned int num_n1_headers; 260 size_t large_header_name_size; 261 size_t large_header_value_size; 262 int large_header_valid; 263 }; 264 265 static size_t 266 lcurl_hdr_callback (char *buffer, size_t size, size_t nitems, 267 void *userdata) 268 { 269 const size_t data_size = size * nitems; 270 struct headers_check_result *check_res = 271 (struct headers_check_result *) userdata; 272 273 if ((6 == data_size) && (0 == memcmp (N1_HEADER_CRLF, buffer, 6))) 274 check_res->num_n1_headers++; 275 else if ((5 <= data_size) && ('0' == buffer[0])) 276 { 277 const char *const col_ptr = memchr (buffer, ':', data_size); 278 if (0 != check_res->large_header_value_size) 279 mhdErrorExitDesc ("Expected only one large header, " \ 280 "but found two large headers in the reply"); 281 if (NULL == col_ptr) 282 check_res->large_header_valid = 0; 283 else if ((size_t) (col_ptr - buffer) >= data_size - 2) 284 check_res->large_header_valid = 0; 285 else if (*(col_ptr + 1) != ' ') 286 check_res->large_header_valid = 0; 287 else 288 { 289 const char *const name = buffer; 290 const size_t name_len = (size_t) (col_ptr - buffer); 291 const size_t val_pos = name_len + 2; 292 const size_t val_len = data_size - val_pos - 2; /* 2 = strlen("\r\n") */ 293 const char *const value = buffer + val_pos; 294 size_t i; 295 check_res->large_header_name_size = name_len; 296 check_res->large_header_value_size = val_len; 297 check_res->large_header_valid = 1; /* To be reset if any problem found */ 298 for (i = 1; i < name_len; i++) 299 if ('a' + (char) (i % ('z' - 'a' + 1)) != name[i]) 300 { 301 fprintf (stderr, "Wrong sequence in reply header name " \ 302 "at position %u. Expected '%c', got '%c'\n", 303 (unsigned int) i, 304 'a' + (char) (i % ('z' - 'a' + 1)), 305 name[i]); 306 check_res->large_header_valid = 0; 307 break; 308 } 309 for (i = 0; i < val_len; i++) 310 if ('Z' - (char) (i % ('Z' - 'A' + 1)) != value[i]) 311 { 312 fprintf (stderr, "Wrong sequence in reply header value " \ 313 "at position %u. Expected '%c', got '%c'\n", 314 (unsigned int) i, 315 'Z' - (char) (i % ('Z' - 'A' + 1)), 316 value[i]); 317 check_res->large_header_valid = 0; 318 break; 319 } 320 } 321 } 322 323 return data_size; 324 } 325 326 327 struct lcurl_data_cb_param 328 { 329 char *buf; 330 size_t pos; 331 size_t size; 332 }; 333 334 335 static size_t 336 copyBuffer (void *ptr, size_t size, size_t nmemb, void *ctx) 337 { 338 struct lcurl_data_cb_param *cbc = ctx; 339 340 if (cbc->pos + size * nmemb > cbc->size) 341 externalErrorExit (); /* overflow */ 342 memcpy (&cbc->buf[cbc->pos], ptr, size * nmemb); 343 cbc->pos += size * nmemb; 344 return size * nmemb; 345 } 346 347 348 struct check_uri_cls 349 { 350 const char *volatile uri; 351 }; 352 353 static void * 354 check_uri_cb (void *cls, 355 const char *uri, 356 struct MHD_Connection *con) 357 { 358 struct check_uri_cls *param = (struct check_uri_cls *) cls; 359 (void) con; 360 361 if (0 != strcmp (param->uri, 362 uri)) 363 { 364 fprintf (stderr, 365 "Wrong URI: `%s', line: %d\n", 366 uri, __LINE__); 367 exit (22); 368 } 369 return NULL; 370 } 371 372 373 struct mhd_header_checker_param 374 { 375 unsigned int num_n1_headers; 376 size_t large_header_name_size; 377 size_t large_header_value_size; 378 int large_header_valid; 379 }; 380 381 static enum MHD_Result 382 headerCheckerInterator (void *cls, 383 enum MHD_ValueKind kind, 384 const char *key, 385 size_t key_size, 386 const char *value, 387 size_t value_size) 388 { 389 struct mhd_header_checker_param *const param = 390 (struct mhd_header_checker_param *) cls; 391 392 if (NULL == param) 393 mhdErrorExitDesc ("cls parameter is NULL"); 394 395 if (MHD_HEADER_KIND != kind) 396 return MHD_YES; /* Continue iteration */ 397 398 if (0 == key_size) 399 mhdErrorExitDesc ("Zero key length"); 400 401 if ((1 == key_size) && (1 == value_size) && 402 ('n' == key[0]) && ('1' == value[0])) 403 param->num_n1_headers++; 404 else if ('0' == key[0]) 405 { /* Found 'large' header */ 406 size_t i; 407 param->large_header_name_size = key_size; 408 param->large_header_value_size = value_size; 409 param->large_header_valid = 1; 410 for (i = 1; i < key_size; i++) 411 if ('a' + (char) (i % ('z' - 'a' + 1)) != key[i]) 412 { 413 fprintf (stderr, "Wrong sequence in request header name " \ 414 "at position %u. Expected '%c', got '%c'\n", 415 (unsigned int) i, 416 'a' + (char) (i % ('z' - 'a' + 1)), 417 key[i]); 418 param->large_header_valid = 0; 419 break; 420 } 421 for (i = 0; i < value_size; i++) 422 if ('Z' - (char) (i % ('Z' - 'A' + 1)) != value[i]) 423 { 424 fprintf (stderr, "Wrong sequence in request header value " \ 425 "at position %u. Expected '%c', got '%c'\n", 426 (unsigned int) i, 427 'Z' - (char) (i % ('Z' - 'A' + 1)), 428 value[i]); 429 param->large_header_valid = 0; 430 break; 431 } 432 } 433 return MHD_YES; 434 } 435 436 437 struct ahc_cls_type 438 { 439 const char *volatile rp_data; 440 volatile size_t rp_data_size; 441 volatile size_t rp_num_n1_hdrs; 442 volatile size_t rp_large_hdr_name_size; 443 volatile size_t rp_large_hdr_value_size; 444 struct mhd_header_checker_param header_check_param; 445 const char *volatile rq_method; 446 const char *volatile rq_url; 447 }; 448 449 450 static enum MHD_Result 451 ahcCheck (void *cls, 452 struct MHD_Connection *connection, 453 const char *url, 454 const char *method, 455 const char *version, 456 const char *upload_data, size_t *upload_data_size, 457 void **req_cls) 458 { 459 static int ptr; 460 struct MHD_Response *response; 461 enum MHD_Result ret; 462 struct ahc_cls_type *const param = (struct ahc_cls_type *) cls; 463 size_t i; 464 465 if (NULL == param) 466 mhdErrorExitDesc ("cls parameter is NULL"); 467 468 if (0 != strcmp (version, MHD_HTTP_VERSION_1_1)) 469 mhdErrorExitDesc ("Unexpected HTTP version"); 470 471 if (0 != strcmp (url, param->rq_url)) 472 mhdErrorExitDesc ("Unexpected URI"); 473 474 if (NULL != upload_data) 475 mhdErrorExitDesc ("'upload_data' is not NULL"); 476 477 if (NULL == upload_data_size) 478 mhdErrorExitDesc ("'upload_data_size' pointer is NULL"); 479 480 if (0 != *upload_data_size) 481 mhdErrorExitDesc ("'*upload_data_size' value is not zero"); 482 483 if (0 != strcmp (param->rq_method, method)) 484 mhdErrorExitDesc ("Unexpected request method"); 485 486 if (&ptr != *req_cls) 487 { 488 *req_cls = &ptr; 489 return MHD_YES; 490 } 491 *req_cls = NULL; 492 493 if (1 > MHD_get_connection_values_n (connection, MHD_HEADER_KIND, 494 &headerCheckerInterator, 495 ¶m->header_check_param)) 496 mhdErrorExitDesc ("Wrong number of headers in the request"); 497 498 response = 499 MHD_create_response_from_buffer_copy (param->rp_data_size, 500 (const void *) param->rp_data); 501 if (NULL == response) 502 mhdErrorExitDesc ("Failed to create response"); 503 504 for (i = 0; i < param->rp_num_n1_hdrs; i++) 505 if (MHD_YES != MHD_add_response_header (response, 506 N1_HEADER_NAME, 507 N1_HEADER_VALUE)) 508 mhdErrorExitDesc ("Cannot add header"); 509 510 if (0 != param->rp_large_hdr_name_size) 511 { 512 const size_t large_hdr_name_size = param->rp_large_hdr_name_size; 513 char *large_hrd_name; 514 const size_t large_hdr_value_size = param->rp_large_hdr_value_size; 515 char *large_hrd_value; 516 517 large_hrd_name = malloc (large_hdr_name_size + 1); 518 if (NULL == large_hrd_name) 519 externalErrorExit (); 520 if (0 != large_hdr_value_size) 521 large_hrd_value = malloc (large_hdr_value_size + 1); 522 else 523 large_hrd_value = NULL; 524 525 if ((0 != large_hdr_value_size) && (NULL == large_hrd_value)) 526 externalErrorExit (); 527 large_hrd_name[0] = '0'; /* Name starts with zero for unique identification */ 528 for (i = 1; i < large_hdr_name_size; i++) 529 large_hrd_name[i] = 'a' + (char) (unsigned char) (i % ('z' - 'a' + 1)); 530 large_hrd_name[large_hdr_name_size] = 0; 531 for (i = 0; i < large_hdr_value_size; i++) 532 large_hrd_value[i] = 'Z' - (char) (unsigned char) (i % ('Z' - 'A' + 1)); 533 if (NULL != large_hrd_value) 534 large_hrd_value[large_hdr_value_size] = 0; 535 if (MHD_YES != MHD_add_response_header (response, 536 large_hrd_name, 537 large_hrd_value)) 538 mhdErrorExitDesc ("Cannot add large header"); 539 540 if (NULL != large_hrd_value) 541 free (large_hrd_value); 542 free (large_hrd_name); 543 } 544 545 ret = MHD_queue_response (connection, 546 MHD_HTTP_OK, 547 response); 548 MHD_destroy_response (response); 549 if (MHD_YES != ret) 550 mhdErrorExitDesc ("Failed to queue response"); 551 552 return ret; 553 } 554 555 556 static CURL * 557 curlEasyInitForTest (const char *queryPath, const char *method, 558 uint16_t port, 559 struct lcurl_data_cb_param *dcbp, 560 struct headers_check_result *hdr_chk_result, 561 struct curl_slist *headers) 562 { 563 CURL *c; 564 565 c = curl_easy_init (); 566 if (NULL == c) 567 libcurlErrorExitDesc ("curl_easy_init() failed"); 568 569 if ((CURLE_OK != curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1L)) || 570 (CURLE_OK != curl_easy_setopt (c, CURLOPT_URL, queryPath)) || 571 (CURLE_OK != curl_easy_setopt (c, CURLOPT_PORT, (long) port)) || 572 (CURLE_OK != curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, 573 ©Buffer)) || 574 (CURLE_OK != curl_easy_setopt (c, CURLOPT_WRITEDATA, dcbp)) || 575 (CURLE_OK != curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 576 (long) response_timeout_val)) || 577 (CURLE_OK != curl_easy_setopt (c, CURLOPT_TIMEOUT, 578 (long) response_timeout_val)) || 579 (CURLE_OK != curl_easy_setopt (c, CURLOPT_ERRORBUFFER, 580 libcurl_errbuf)) || 581 (CURLE_OK != curl_easy_setopt (c, CURLOPT_HEADERFUNCTION, 582 lcurl_hdr_callback)) || 583 (CURLE_OK != curl_easy_setopt (c, CURLOPT_HEADERDATA, 584 hdr_chk_result)) || 585 (CURLE_OK != curl_easy_setopt (c, CURLOPT_FAILONERROR, 1L)) || 586 (CURLE_OK != curl_easy_setopt (c, CURLOPT_HTTP_VERSION, 587 (oneone) ? 588 CURL_HTTP_VERSION_1_1 : 589 CURL_HTTP_VERSION_1_0))) 590 libcurlErrorExitDesc ("curl_easy_setopt() failed"); 591 592 if (CURLE_OK != curl_easy_setopt (c, CURLOPT_CUSTOMREQUEST, method)) 593 libcurlErrorExitDesc ("curl_easy_setopt() failed"); 594 595 if (CURLE_OK != curl_easy_setopt (c, CURLOPT_HTTPHEADER, headers)) 596 libcurlErrorExitDesc ("curl_easy_setopt() failed"); 597 598 return c; 599 } 600 601 602 static CURLcode 603 performQueryExternal (struct MHD_Daemon *d, CURL *c) 604 { 605 CURLM *multi; 606 time_t start; 607 struct timeval tv; 608 CURLcode ret; 609 610 ret = CURLE_FAILED_INIT; /* will be replaced with real result */ 611 multi = NULL; 612 multi = curl_multi_init (); 613 if (multi == NULL) 614 libcurlErrorExitDesc ("curl_multi_init() failed"); 615 if (CURLM_OK != curl_multi_add_handle (multi, c)) 616 libcurlErrorExitDesc ("curl_multi_add_handle() failed"); 617 618 start = time (NULL); 619 while (time (NULL) - start <= TIMEOUTS_VAL) 620 { 621 fd_set rs; 622 fd_set ws; 623 fd_set es; 624 MHD_socket maxMhdSk; 625 int maxCurlSk; 626 int running; 627 628 maxMhdSk = MHD_INVALID_SOCKET; 629 maxCurlSk = -1; 630 FD_ZERO (&rs); 631 FD_ZERO (&ws); 632 FD_ZERO (&es); 633 if (NULL != multi) 634 { 635 curl_multi_perform (multi, &running); 636 if (0 == running) 637 { 638 struct CURLMsg *msg; 639 int msgLeft; 640 int totalMsgs = 0; 641 do 642 { 643 msg = curl_multi_info_read (multi, &msgLeft); 644 if (NULL == msg) 645 libcurlErrorExitDesc ("curl_multi_info_read() failed"); 646 totalMsgs++; 647 if (CURLMSG_DONE == msg->msg) 648 ret = msg->data.result; 649 } while (msgLeft > 0); 650 if (1 != totalMsgs) 651 { 652 fprintf (stderr, 653 "curl_multi_info_read returned wrong " 654 "number of results (%d).\n", 655 totalMsgs); 656 externalErrorExit (); 657 } 658 curl_multi_remove_handle (multi, c); 659 curl_multi_cleanup (multi); 660 multi = NULL; 661 } 662 else 663 { 664 if (CURLM_OK != curl_multi_fdset (multi, &rs, &ws, &es, &maxCurlSk)) 665 libcurlErrorExitDesc ("curl_multi_fdset() failed"); 666 } 667 } 668 if (NULL == multi) 669 { /* libcurl has finished, check whether MHD still needs to perform cleanup */ 670 if (0 != MHD_get_timeout64s (d)) 671 break; /* MHD finished as well */ 672 } 673 if (MHD_YES != MHD_get_fdset (d, &rs, &ws, &es, &maxMhdSk)) 674 mhdErrorExitDesc ("MHD_get_fdset() failed"); 675 tv.tv_sec = 0; 676 tv.tv_usec = 1000; 677 #ifdef MHD_POSIX_SOCKETS 678 if (maxMhdSk > maxCurlSk) 679 maxCurlSk = maxMhdSk; 680 #endif /* MHD_POSIX_SOCKETS */ 681 if (-1 == select (maxCurlSk + 1, &rs, &ws, &es, &tv)) 682 { 683 #ifdef MHD_POSIX_SOCKETS 684 if (EINTR != errno) 685 externalErrorExitDesc ("Unexpected select() error"); 686 #else 687 if ((WSAEINVAL != WSAGetLastError ()) || 688 (0 != rs.fd_count) || (0 != ws.fd_count) || (0 != es.fd_count) ) 689 externalErrorExitDesc ("Unexpected select() error"); 690 Sleep (1); 691 #endif 692 } 693 if (MHD_YES != MHD_run_from_select (d, &rs, &ws, &es)) 694 mhdErrorExitDesc ("MHD_run_from_select() failed"); 695 } 696 697 return ret; 698 } 699 700 701 struct curlQueryParams 702 { 703 /* Destination path for CURL query */ 704 const char *queryPath; 705 706 /* Custom query method, NULL for default */ 707 const char *method; 708 709 /* Destination port for CURL query */ 710 uint16_t queryPort; 711 712 /* List of additional request headers */ 713 struct curl_slist *headers; 714 715 /* CURL query result error flag */ 716 volatile unsigned int queryError; 717 718 /* Response HTTP code, zero if no response */ 719 volatile int responseCode; 720 }; 721 722 723 /* Returns zero for successful response and non-zero for failed response */ 724 static unsigned int 725 doCurlQueryInThread (struct MHD_Daemon *d, 726 struct curlQueryParams *p, 727 struct headers_check_result *hdr_res, 728 const char *expected_data, 729 size_t expected_data_size) 730 { 731 const union MHD_DaemonInfo *dinfo; 732 CURL *c; 733 struct lcurl_data_cb_param dcbp; 734 CURLcode errornum; 735 int use_external_poll; 736 long resp_code; 737 738 dinfo = MHD_get_daemon_info (d, MHD_DAEMON_INFO_FLAGS); 739 if (NULL == dinfo) 740 mhdErrorExitDesc ("MHD_get_daemon_info() failed"); 741 use_external_poll = (0 == (dinfo->flags 742 & MHD_USE_INTERNAL_POLLING_THREAD)); 743 744 if (NULL == p->queryPath) 745 abort (); 746 747 if (0 == p->queryPort) 748 abort (); 749 750 /* Test must not fail due to test's internal buffer shortage */ 751 dcbp.size = TEST_FAIL_SIZE * 2 + 1; 752 dcbp.buf = malloc (dcbp.size); 753 if (NULL == dcbp.buf) 754 externalErrorExit (); 755 dcbp.pos = 0; 756 757 memset (hdr_res, 0, sizeof(*hdr_res)); 758 759 c = curlEasyInitForTest (p->queryPath, p->method, p->queryPort, 760 &dcbp, hdr_res, p->headers); 761 762 if (! use_external_poll) 763 errornum = curl_easy_perform (c); 764 else 765 errornum = performQueryExternal (d, c); 766 767 if (CURLE_OK != curl_easy_getinfo (c, CURLINFO_RESPONSE_CODE, &resp_code)) 768 libcurlErrorExitDesc ("curl_easy_getinfo() failed"); 769 770 p->responseCode = (int) resp_code; 771 if ((CURLE_OK == errornum) && (200 != resp_code)) 772 { 773 fprintf (stderr, 774 "Got reply with unexpected status code: %d\n", 775 p->responseCode); 776 mhdErrorExit (); 777 } 778 779 if (CURLE_OK != errornum) 780 { 781 if ((CURLE_GOT_NOTHING != errornum) && (CURLE_RECV_ERROR != errornum) 782 && (CURLE_HTTP_RETURNED_ERROR != errornum)) 783 { 784 if (CURLE_OPERATION_TIMEDOUT == errornum) 785 mhdErrorExitDesc ("Request was aborted due to timeout"); 786 fprintf (stderr, "libcurl returned unexpected error: %s\n", 787 curl_easy_strerror (errornum)); 788 mhdErrorExitDesc ("Request failed due to unexpected error"); 789 } 790 p->queryError = 1; 791 switch (resp_code) 792 { 793 case 0: /* No parsed response */ 794 case 413: /* "Content Too Large" */ 795 case 414: /* "URI Too Long" */ 796 case 431: /* "Request Header Fields Too Large" */ 797 case 501: /* "Not Implemented" */ 798 /* Expected error codes */ 799 break; 800 default: 801 fprintf (stderr, 802 "Got reply with unexpected status code: %ld\n", 803 resp_code); 804 mhdErrorExit (); 805 } 806 } 807 else 808 { 809 if (dcbp.pos != expected_data_size) 810 mhdErrorExit ("libcurl reports wrong size of MHD reply body data"); 811 else if (0 != memcmp (expected_data, dcbp.buf, 812 expected_data_size)) 813 mhdErrorExit ("libcurl reports wrong MHD reply body data"); 814 else 815 p->queryError = 0; 816 } 817 818 curl_easy_cleanup (c); 819 free (dcbp.buf); 820 821 return p->queryError; 822 } 823 824 825 /* Perform test queries, shut down MHD daemon, and free parameters */ 826 static unsigned int 827 performTestQueries (struct MHD_Daemon *d, uint16_t d_port, 828 struct ahc_cls_type *ahc_param, 829 struct check_uri_cls *uri_cb_param) 830 { 831 struct curlQueryParams qParam; 832 unsigned int ret = 0; /* Return value */ 833 struct headers_check_result rp_headers_check; 834 char *buf; 835 size_t i; 836 size_t first_failed_at = 0; 837 838 839 buf = malloc (TEST_FAIL_SIZE + 1 + strlen (URL_SCHEME_HOST)); 840 if (NULL == buf) 841 externalErrorExit (); 842 843 /* Common parameters, to be individually overridden by specific test cases */ 844 qParam.queryPort = d_port; 845 qParam.method = NULL; /* Use libcurl default: GET */ 846 qParam.queryPath = URL_SCHEME_HOST EXPECTED_URI_BASE_PATH; 847 qParam.headers = NULL; /* No additional headers */ 848 uri_cb_param->uri = EXPECTED_URI_BASE_PATH; 849 ahc_param->rq_url = EXPECTED_URI_BASE_PATH; 850 ahc_param->rq_method = "GET"; /* Default expected method */ 851 852 ahc_param->rp_data = "~"; 853 ahc_param->rp_data_size = 1; 854 ahc_param->rp_large_hdr_name_size = 0; 855 ahc_param->rp_large_hdr_value_size = 0; 856 ahc_param->rp_num_n1_hdrs = 0; 857 858 if (large_req_method) 859 { 860 for (i = 0; i < TEST_START_SIZE; i++) 861 buf[i] = 'A' + (char) (unsigned char) (i % ('Z' - 'A' + 1)); 862 for (; i <= TEST_FAIL_SIZE; i++) 863 { 864 buf[i] = 0; 865 866 qParam.method = buf; 867 ahc_param->rq_method = buf; 868 869 memset (&ahc_param->header_check_param, 0, 870 sizeof (ahc_param->header_check_param)); 871 if (0 != doCurlQueryInThread (d, &qParam, &rp_headers_check, 872 ahc_param->rp_data, 873 ahc_param->rp_data_size)) 874 { 875 if ((0 != qParam.responseCode) && (501 != qParam.responseCode)) 876 { 877 fprintf (stderr, 878 "Got reply with status code %d, " 879 "while code 501 (\"Not Implemented\") is expected.\n", 880 qParam.responseCode); 881 mhdErrorExit (); 882 } 883 if (TEST_OK_SIZE >= i) 884 { 885 fprintf (stderr, 886 "Request failed when running with the valid value size.\n"); 887 ret = 1; /* Failed too early */ 888 } 889 if (0 == first_failed_at) 890 { 891 if (verbose) 892 fprintf (stderr, "First failed size is %u.\n", (unsigned int) i); 893 first_failed_at = i; 894 } 895 } 896 else 897 { 898 if (TEST_FAIL_SIZE == i) 899 { 900 fprintf (stderr, "Request succeed with the largest size.\n"); 901 ret = 1; /* Succeed with largest value */ 902 } 903 } 904 if (0 != ahc_param->header_check_param.num_n1_headers) 905 mhdErrorExitDesc ("Detected unexpected request headers"); 906 if (0 != ahc_param->header_check_param.large_header_name_size) 907 mhdErrorExitDesc ("Detected unexpected large request header"); 908 if (0 != rp_headers_check.num_n1_headers) 909 mhdErrorExitDesc ("Detected unexpected reply headers"); 910 if (0 != rp_headers_check.large_header_name_size) 911 mhdErrorExitDesc ("Detected unexpected large reply header"); 912 913 buf[i] = 'A' + (char) (unsigned char) (i % ('Z' - 'A' + 1)); 914 } 915 } 916 else if (large_req_url) 917 { 918 const size_t base_size = strlen (URL_SCHEME_HOST); 919 char *const url = buf + base_size; 920 921 memcpy (buf, URL_SCHEME_HOST, base_size); 922 url[0] = '/'; 923 for (i = 1; i < TEST_START_SIZE; i++) 924 url[i] = 'a' + (char) (unsigned char) (i % ('z' - 'a' + 1)); 925 for (; i <= TEST_FAIL_SIZE; i++) 926 { 927 url[i] = 0; 928 929 qParam.queryPath = buf; 930 uri_cb_param->uri = url; 931 ahc_param->rq_url = url; 932 933 memset (&ahc_param->header_check_param, 0, 934 sizeof (ahc_param->header_check_param)); 935 if (0 != doCurlQueryInThread (d, &qParam, &rp_headers_check, 936 ahc_param->rp_data, 937 ahc_param->rp_data_size)) 938 { 939 if ((0 != qParam.responseCode) && (414 != qParam.responseCode)) 940 { 941 fprintf (stderr, 942 "Got reply with status code %d, " 943 "while code 414 (\"URI Too Long\") is expected.\n", 944 qParam.responseCode); 945 mhdErrorExit (); 946 } 947 if (TEST_OK_SIZE >= i) 948 { 949 fprintf (stderr, 950 "Request failed when running with the valid value size.\n"); 951 ret = 1; /* Failed too early */ 952 } 953 if (0 == first_failed_at) 954 { 955 if (verbose) 956 fprintf (stderr, "First failed size is %u.\n", (unsigned int) i); 957 first_failed_at = i; 958 } 959 } 960 else 961 { 962 if (TEST_FAIL_SIZE == i) 963 { 964 fprintf (stderr, "Request succeed with the largest size.\n"); 965 ret = 1; /* Succeed with largest value */ 966 } 967 } 968 if (0 != ahc_param->header_check_param.num_n1_headers) 969 mhdErrorExitDesc ("Detected unexpected request headers"); 970 if (0 != ahc_param->header_check_param.large_header_name_size) 971 mhdErrorExitDesc ("Detected unexpected large request header"); 972 if (0 != rp_headers_check.num_n1_headers) 973 mhdErrorExitDesc ("Detected unexpected reply headers"); 974 if (0 != rp_headers_check.large_header_name_size) 975 mhdErrorExitDesc ("Detected unexpected large reply header"); 976 977 url[i] = 'a' + (char) (unsigned char) (i % ('z' - 'a' + 1)); 978 } 979 } 980 else if (large_req_header_name) 981 { 982 buf[0] = '0'; /* Name starts with zero for unique identification */ 983 for (i = 1; i < TEST_START_SIZE; i++) 984 buf[i] = 'a' + (char) (unsigned char) (i % ('z' - 'a' + 1)); 985 for (; i <= TEST_FAIL_SIZE; i++) 986 { 987 struct curl_slist *curl_headers; 988 curl_headers = NULL; 989 990 memcpy (buf + i, ": Z", 3); /* Note: strlen(": Z") is less than strlen(URL_SCHEME_HOST) */ 991 buf[i + 3] = 0; 992 993 curl_headers = curl_slist_append (curl_headers, buf); 994 if (NULL == curl_headers) 995 externalErrorExit (); 996 997 qParam.headers = curl_headers; 998 999 memset (&ahc_param->header_check_param, 0, 1000 sizeof (ahc_param->header_check_param)); 1001 if (0 != doCurlQueryInThread (d, &qParam, &rp_headers_check, 1002 ahc_param->rp_data, 1003 ahc_param->rp_data_size)) 1004 { 1005 if ((0 != qParam.responseCode) && (431 != qParam.responseCode)) 1006 { 1007 fprintf (stderr, 1008 "Got reply with status code %d, while code 431 " 1009 "(\"Request Header Fields Too Large\") is expected.\n", 1010 qParam.responseCode); 1011 mhdErrorExit (); 1012 } 1013 if (0 != ahc_param->header_check_param.large_header_name_size) 1014 { /* If large header was processed, it must be valid */ 1015 if (i != ahc_param->header_check_param.large_header_name_size) 1016 mhdErrorExitDesc ("Detected wrong large request header name size"); 1017 if (1 != ahc_param->header_check_param.large_header_value_size) 1018 mhdErrorExitDesc ("Detected wrong large request header value size"); 1019 if (0 == ahc_param->header_check_param.large_header_valid) 1020 mhdErrorExitDesc ("Detected wrong large request header"); 1021 } 1022 if (TEST_OK_SIZE >= i) 1023 { 1024 fprintf (stderr, 1025 "Request failed when running with the valid value size.\n"); 1026 ret = 1; /* Failed too early */ 1027 } 1028 if (0 == first_failed_at) 1029 { 1030 if (verbose) 1031 fprintf (stderr, "First failed size is %u.\n", (unsigned int) i); 1032 first_failed_at = i; 1033 } 1034 } 1035 else 1036 { 1037 if (i != ahc_param->header_check_param.large_header_name_size) 1038 mhdErrorExitDesc ("Detected wrong large request header name size"); 1039 if (1 != ahc_param->header_check_param.large_header_value_size) 1040 mhdErrorExitDesc ("Detected wrong large request header value size"); 1041 if (0 == ahc_param->header_check_param.large_header_valid) 1042 mhdErrorExitDesc ("Detected wrong large request header"); 1043 if (TEST_FAIL_SIZE == i) 1044 { 1045 fprintf (stderr, "Request succeed with the largest size.\n"); 1046 ret = 1; /* Succeed with largest value */ 1047 } 1048 } 1049 if (0 != ahc_param->header_check_param.num_n1_headers) 1050 mhdErrorExitDesc ("Detected unexpected request headers"); 1051 if (0 != rp_headers_check.num_n1_headers) 1052 mhdErrorExitDesc ("Detected unexpected reply headers"); 1053 if (0 != rp_headers_check.large_header_name_size) 1054 mhdErrorExitDesc ("Detected unexpected large reply header"); 1055 1056 curl_slist_free_all (curl_headers); 1057 buf[i] = 'a' + (char) (unsigned char) (i % ('z' - 'a' + 1)); 1058 } 1059 } 1060 else if (large_req_header_value) 1061 { 1062 char *const hdr_value = buf + 3; 1063 /* Name starts with zero for unique identification */ 1064 memcpy (buf, "0: ", 3); /* Note: strlen(": Z") is less than strlen(URL_SCHEME_HOST) */ 1065 for (i = 0; i < TEST_START_SIZE; i++) 1066 hdr_value[i] = 'Z' - (char) (unsigned char) (i % ('Z' - 'A' + 1)); 1067 for (; i <= TEST_FAIL_SIZE; i++) 1068 { 1069 struct curl_slist *curl_headers; 1070 curl_headers = NULL; 1071 1072 hdr_value[i] = 0; 1073 1074 curl_headers = curl_slist_append (curl_headers, buf); 1075 if (NULL == curl_headers) 1076 externalErrorExit (); 1077 1078 qParam.headers = curl_headers; 1079 1080 memset (&ahc_param->header_check_param, 0, 1081 sizeof (ahc_param->header_check_param)); 1082 if (0 != doCurlQueryInThread (d, &qParam, &rp_headers_check, 1083 ahc_param->rp_data, 1084 ahc_param->rp_data_size)) 1085 { 1086 if ((0 != qParam.responseCode) && (431 != qParam.responseCode)) 1087 { 1088 fprintf (stderr, 1089 "Got reply with status code %d, while code 431 " 1090 "(\"Request Header Fields Too Large\") is expected.\n", 1091 qParam.responseCode); 1092 mhdErrorExit (); 1093 } 1094 if (0 != ahc_param->header_check_param.large_header_name_size) 1095 { /* If large header was processed, it must be valid */ 1096 if (1 != ahc_param->header_check_param.large_header_name_size) 1097 mhdErrorExitDesc ("Detected wrong large request header name size"); 1098 if (i != ahc_param->header_check_param.large_header_value_size) 1099 mhdErrorExitDesc ("Detected wrong large request header value size"); 1100 if (0 == ahc_param->header_check_param.large_header_valid) 1101 mhdErrorExitDesc ("Detected wrong large request header"); 1102 } 1103 if (TEST_OK_SIZE >= i) 1104 { 1105 fprintf (stderr, 1106 "Request failed when running with the valid value size.\n"); 1107 ret = 1; /* Failed too early */ 1108 } 1109 if (0 == first_failed_at) 1110 { 1111 if (verbose) 1112 fprintf (stderr, "First failed size is %u.\n", (unsigned int) i); 1113 first_failed_at = i; 1114 } 1115 } 1116 else 1117 { 1118 if (1 != ahc_param->header_check_param.large_header_name_size) 1119 mhdErrorExitDesc ("Detected wrong large request header name size"); 1120 if (i != ahc_param->header_check_param.large_header_value_size) 1121 mhdErrorExitDesc ("Detected wrong large request header value size"); 1122 if (0 == ahc_param->header_check_param.large_header_valid) 1123 mhdErrorExitDesc ("Detected wrong large request header"); 1124 if (TEST_FAIL_SIZE == i) 1125 { 1126 fprintf (stderr, "Request succeed with the largest size.\n"); 1127 ret = 1; /* Succeed with largest value */ 1128 } 1129 } 1130 if (0 != ahc_param->header_check_param.num_n1_headers) 1131 mhdErrorExitDesc ("Detected unexpected request headers"); 1132 if (0 != rp_headers_check.num_n1_headers) 1133 mhdErrorExitDesc ("Detected unexpected reply headers"); 1134 if (0 != rp_headers_check.large_header_name_size) 1135 mhdErrorExitDesc ("Detected unexpected large reply header"); 1136 1137 curl_slist_free_all (curl_headers); 1138 hdr_value[i] = 'Z' - (char) (unsigned char) (i % ('Z' - 'A' + 1)); 1139 } 1140 } 1141 else if (large_req_headers) 1142 { 1143 unsigned int num_hdrs = 0; 1144 struct curl_slist *curl_headers; 1145 const size_t hdr_size = strlen (N1_HEADER_CRLF); 1146 1147 curl_headers = NULL; 1148 1149 for (i = 0; i < TEST_RQ_N1_START_SIZE; i += hdr_size) 1150 { 1151 curl_headers = curl_slist_append (curl_headers, N1_HEADER); 1152 if (NULL == curl_headers) 1153 externalErrorExit (); 1154 num_hdrs++; 1155 } 1156 for (; i <= TEST_FAIL_SIZE; i += hdr_size) 1157 { 1158 qParam.headers = curl_headers; 1159 ahc_param->header_check_param.num_n1_headers = num_hdrs; 1160 1161 memset (&ahc_param->header_check_param, 0, 1162 sizeof (ahc_param->header_check_param)); 1163 if (0 != doCurlQueryInThread (d, &qParam, &rp_headers_check, 1164 ahc_param->rp_data, 1165 ahc_param->rp_data_size)) 1166 { 1167 if ((0 != qParam.responseCode) && (431 != qParam.responseCode)) 1168 { 1169 fprintf (stderr, 1170 "Got reply with status code %d, while code 431 " 1171 "(\"Request Header Fields Too Large\") is expected.\n", 1172 qParam.responseCode); 1173 mhdErrorExit (); 1174 } 1175 if (0 != ahc_param->header_check_param.num_n1_headers) 1176 { /* If headers were processed, they must be valid */ 1177 if (num_hdrs != ahc_param->header_check_param.num_n1_headers) 1178 mhdErrorExitDesc ("Detected wrong number of request headers"); 1179 } 1180 if (TEST_RQ_N1_OK_SIZE >= i) 1181 { 1182 fprintf (stderr, 1183 "Request failed when running with the valid value size.\n"); 1184 ret = 1; /* Failed too early */ 1185 } 1186 if (0 == first_failed_at) 1187 { 1188 if (verbose) 1189 fprintf (stderr, "First failed size is %u.\n", (unsigned int) i); 1190 first_failed_at = i; 1191 } 1192 } 1193 else 1194 { 1195 if (num_hdrs != ahc_param->header_check_param.num_n1_headers) 1196 mhdErrorExitDesc ("Detected wrong number of request headers"); 1197 if (TEST_FAIL_SIZE == i) 1198 { 1199 fprintf (stderr, "Request succeed with the largest size.\n"); 1200 ret = 1; /* Succeed with largest value */ 1201 } 1202 } 1203 if (0 != ahc_param->header_check_param.large_header_name_size) 1204 mhdErrorExitDesc ("Detected unexpected large request header"); 1205 if (0 != rp_headers_check.num_n1_headers) 1206 mhdErrorExitDesc ("Detected unexpected reply headers"); 1207 if (0 != rp_headers_check.large_header_name_size) 1208 mhdErrorExitDesc ("Detected unexpected large reply header"); 1209 1210 curl_headers = curl_slist_append (curl_headers, N1_HEADER); 1211 if (NULL == curl_headers) 1212 externalErrorExit (); 1213 num_hdrs++; 1214 } 1215 curl_slist_free_all (curl_headers); 1216 } 1217 else if (large_rsp_header_name) 1218 { 1219 for (i = TEST_START_SIZE; i <= TEST_FAIL_SIZE; i++) 1220 { 1221 ahc_param->rp_large_hdr_name_size = i; 1222 ahc_param->rp_large_hdr_value_size = 1; 1223 1224 memset (&ahc_param->header_check_param, 0, 1225 sizeof (ahc_param->header_check_param)); 1226 if (0 != doCurlQueryInThread (d, &qParam, &rp_headers_check, 1227 ahc_param->rp_data, 1228 ahc_param->rp_data_size)) 1229 { 1230 (void) qParam.responseCode; /* TODO: check for the right response code */ 1231 if (0 != rp_headers_check.large_header_name_size) 1232 mhdErrorExitDesc ("Detected unexpected large reply header"); 1233 if (TEST_OK_SIZE >= i) 1234 { 1235 fprintf (stderr, 1236 "Request failed when running with the valid value size.\n"); 1237 ret = 1; /* Failed too early */ 1238 } 1239 if (0 == first_failed_at) 1240 { 1241 if (verbose) 1242 fprintf (stderr, "First failed size is %u.\n", (unsigned int) i); 1243 first_failed_at = i; 1244 } 1245 } 1246 else 1247 { 1248 if (i != rp_headers_check.large_header_name_size) 1249 mhdErrorExitDesc ("Detected wrong large reply header name size"); 1250 if (1 != rp_headers_check.large_header_value_size) 1251 mhdErrorExitDesc ("Detected wrong large reply header value size"); 1252 if (0 == rp_headers_check.large_header_valid) 1253 mhdErrorExitDesc ("Detected wrong large reply header"); 1254 if (TEST_FAIL_SIZE == i) 1255 { 1256 fprintf (stderr, "Request succeed with the largest size.\n"); 1257 ret = 1; /* Succeed with largest value */ 1258 } 1259 } 1260 if (0 != ahc_param->header_check_param.num_n1_headers) 1261 mhdErrorExitDesc ("Detected unexpected request headers"); 1262 if (0 != ahc_param->header_check_param.large_header_name_size) 1263 mhdErrorExitDesc ("Detected unexpected large request header"); 1264 if (0 != rp_headers_check.num_n1_headers) 1265 mhdErrorExitDesc ("Detected unexpected reply headers"); 1266 } 1267 } 1268 else if (large_rsp_header_value) 1269 { 1270 for (i = TEST_START_SIZE; i <= TEST_FAIL_SIZE; i++) 1271 { 1272 ahc_param->rp_large_hdr_name_size = 1; 1273 ahc_param->rp_large_hdr_value_size = i; 1274 1275 memset (&ahc_param->header_check_param, 0, 1276 sizeof (ahc_param->header_check_param)); 1277 if (0 != doCurlQueryInThread (d, &qParam, &rp_headers_check, 1278 ahc_param->rp_data, 1279 ahc_param->rp_data_size)) 1280 { 1281 (void) qParam.responseCode; /* TODO: check for the right response code */ 1282 if (0 != rp_headers_check.large_header_name_size) 1283 mhdErrorExitDesc ("Detected unexpected large reply header"); 1284 if (TEST_OK_SIZE >= i) 1285 { 1286 fprintf (stderr, 1287 "Request failed when running with the valid value size.\n"); 1288 ret = 1; /* Failed too early */ 1289 } 1290 if (0 == first_failed_at) 1291 { 1292 if (verbose) 1293 fprintf (stderr, "First failed size is %u.\n", (unsigned int) i); 1294 first_failed_at = i; 1295 } 1296 } 1297 else 1298 { 1299 if (1 != rp_headers_check.large_header_name_size) 1300 mhdErrorExitDesc ("Detected wrong large reply header name size"); 1301 if (i != rp_headers_check.large_header_value_size) 1302 mhdErrorExitDesc ("Detected wrong large reply header value size"); 1303 if (0 == rp_headers_check.large_header_valid) 1304 mhdErrorExitDesc ("Detected wrong large reply header"); 1305 if (TEST_FAIL_SIZE == i) 1306 { 1307 fprintf (stderr, "Request succeed with the largest size.\n"); 1308 ret = 1; /* Succeed with largest value */ 1309 } 1310 } 1311 if (0 != ahc_param->header_check_param.num_n1_headers) 1312 mhdErrorExitDesc ("Detected unexpected request headers"); 1313 if (0 != ahc_param->header_check_param.large_header_name_size) 1314 mhdErrorExitDesc ("Detected unexpected large request header"); 1315 if (0 != rp_headers_check.num_n1_headers) 1316 mhdErrorExitDesc ("Detected unexpected reply headers"); 1317 } 1318 } 1319 else if (large_rsp_headers) 1320 { 1321 size_t num_hrds; 1322 const size_t hdr_size = strlen (N1_HEADER_CRLF); 1323 1324 for (num_hrds = TEST_START_SIZE / hdr_size; 1325 num_hrds * hdr_size <= TEST_FAIL_SIZE; num_hrds++) 1326 { 1327 i = num_hrds * hdr_size; 1328 ahc_param->rp_num_n1_hdrs = num_hrds; 1329 1330 memset (&ahc_param->header_check_param, 0, 1331 sizeof (ahc_param->header_check_param)); 1332 if (0 != doCurlQueryInThread (d, &qParam, &rp_headers_check, 1333 ahc_param->rp_data, 1334 ahc_param->rp_data_size)) 1335 { 1336 (void) qParam.responseCode; /* TODO: check for the right response code */ 1337 if (0 != rp_headers_check.num_n1_headers) 1338 mhdErrorExitDesc ("Detected unexpected reply headers"); 1339 if (TEST_OK_SIZE >= i) 1340 { 1341 fprintf (stderr, 1342 "Request failed when running with the valid value size.\n"); 1343 ret = 1; /* Failed too early */ 1344 } 1345 if (0 == first_failed_at) 1346 { 1347 if (verbose) 1348 fprintf (stderr, "First failed size is %u.\n", (unsigned int) i); 1349 first_failed_at = i; 1350 } 1351 } 1352 else 1353 { 1354 if (num_hrds != rp_headers_check.num_n1_headers) 1355 mhdErrorExitDesc ("Detected wrong number of reply headers"); 1356 if (TEST_FAIL_SIZE == i) 1357 { 1358 fprintf (stderr, "Request succeed with the largest size.\n"); 1359 ret = 1; /* Succeed with largest value */ 1360 } 1361 } 1362 if (0 != ahc_param->header_check_param.num_n1_headers) 1363 mhdErrorExitDesc ("Detected unexpected request headers"); 1364 if (0 != ahc_param->header_check_param.large_header_name_size) 1365 mhdErrorExitDesc ("Detected unexpected large request header"); 1366 if (0 != rp_headers_check.large_header_name_size) 1367 mhdErrorExitDesc ("Detected unexpected large reply header"); 1368 } 1369 } 1370 else 1371 externalErrorExitDesc ("No valid test test was selected"); 1372 1373 MHD_stop_daemon (d); 1374 free (buf); 1375 free (uri_cb_param); 1376 free (ahc_param); 1377 1378 return ret; 1379 } 1380 1381 1382 enum testMhdThreadsType 1383 { 1384 testMhdThreadExternal = 0, 1385 testMhdThreadInternal = MHD_USE_INTERNAL_POLLING_THREAD, 1386 testMhdThreadInternalPerConnection = MHD_USE_THREAD_PER_CONNECTION 1387 | MHD_USE_INTERNAL_POLLING_THREAD, 1388 testMhdThreadInternalPool 1389 }; 1390 1391 enum testMhdPollType 1392 { 1393 testMhdPollBySelect = 0, 1394 testMhdPollByPoll = MHD_USE_POLL, 1395 testMhdPollByEpoll = MHD_USE_EPOLL, 1396 testMhdPollAuto = MHD_USE_AUTO 1397 }; 1398 1399 /* Get number of threads for thread pool depending 1400 * on used poll function and test type. */ 1401 static unsigned int 1402 testNumThreadsForPool (enum testMhdPollType pollType) 1403 { 1404 unsigned int numThreads = MHD_CPU_COUNT; 1405 (void) pollType; /* Don't care about pollType for this test */ 1406 return numThreads; /* No practical limit for non-cleanup test */ 1407 } 1408 1409 1410 static struct MHD_Daemon * 1411 startTestMhdDaemon (enum testMhdThreadsType thrType, 1412 enum testMhdPollType pollType, uint16_t *pport, 1413 struct ahc_cls_type **ahc_param, 1414 struct check_uri_cls **uri_cb_param) 1415 { 1416 struct MHD_Daemon *d; 1417 const union MHD_DaemonInfo *dinfo; 1418 1419 if ((NULL == ahc_param) || (NULL == uri_cb_param)) 1420 abort (); 1421 1422 *ahc_param = (struct ahc_cls_type *) malloc (sizeof(struct ahc_cls_type)); 1423 if (NULL == *ahc_param) 1424 externalErrorExit (); 1425 *uri_cb_param = 1426 (struct check_uri_cls *) malloc (sizeof(struct check_uri_cls)); 1427 if (NULL == *uri_cb_param) 1428 externalErrorExit (); 1429 1430 if ( (0 == *pport) && 1431 (MHD_NO == MHD_is_feature_supported (MHD_FEATURE_AUTODETECT_BIND_PORT)) ) 1432 { 1433 *pport = 4100; 1434 if (large_req_method) 1435 *pport += 1; 1436 if (large_req_url) 1437 *pport += 2; 1438 if (large_req_header_name) 1439 *pport += 3; 1440 if (large_req_header_value) 1441 *pport += 4; 1442 if (large_req_headers) 1443 *pport += 5; 1444 if (large_rsp_header_name) 1445 *pport += 6; 1446 if (large_rsp_header_value) 1447 *pport += 7; 1448 if (large_rsp_headers) 1449 *pport += 8; 1450 if (! oneone) 1451 *pport += 16; 1452 } 1453 1454 if (testMhdThreadExternal == thrType) 1455 d = MHD_start_daemon (((unsigned int) thrType) | ((unsigned int) pollType) 1456 | (verbose ? MHD_USE_ERROR_LOG : 0) 1457 | MHD_USE_NO_THREAD_SAFETY, 1458 *pport, NULL, NULL, 1459 &ahcCheck, *ahc_param, 1460 MHD_OPTION_URI_LOG_CALLBACK, &check_uri_cb, 1461 *uri_cb_param, 1462 MHD_OPTION_CONNECTION_MEMORY_LIMIT, 1463 (size_t) BUFFER_SIZE, 1464 MHD_OPTION_APP_FD_SETSIZE, (int) FD_SETSIZE, 1465 MHD_OPTION_END); 1466 else if (testMhdThreadInternalPool != thrType) 1467 d = MHD_start_daemon (((unsigned int) thrType) | ((unsigned int) pollType) 1468 | (verbose ? MHD_USE_ERROR_LOG : 0), 1469 *pport, NULL, NULL, 1470 &ahcCheck, *ahc_param, 1471 MHD_OPTION_URI_LOG_CALLBACK, &check_uri_cb, 1472 *uri_cb_param, 1473 MHD_OPTION_CONNECTION_MEMORY_LIMIT, 1474 (size_t) BUFFER_SIZE, 1475 MHD_OPTION_END); 1476 else 1477 d = MHD_start_daemon (MHD_USE_INTERNAL_POLLING_THREAD 1478 | ((unsigned int) pollType) 1479 | (verbose ? MHD_USE_ERROR_LOG : 0), 1480 *pport, NULL, NULL, 1481 &ahcCheck, *ahc_param, 1482 MHD_OPTION_THREAD_POOL_SIZE, 1483 testNumThreadsForPool (pollType), 1484 MHD_OPTION_URI_LOG_CALLBACK, &check_uri_cb, 1485 *uri_cb_param, 1486 MHD_OPTION_CONNECTION_MEMORY_LIMIT, 1487 (size_t) BUFFER_SIZE, 1488 MHD_OPTION_END); 1489 1490 if (NULL == d) 1491 { 1492 fprintf (stderr, "Failed to start MHD daemon, errno=%d.\n", errno); 1493 abort (); 1494 } 1495 1496 if (0 == *pport) 1497 { 1498 dinfo = MHD_get_daemon_info (d, MHD_DAEMON_INFO_BIND_PORT); 1499 if ((NULL == dinfo) || (0 == dinfo->port) ) 1500 { 1501 fprintf (stderr, "MHD_get_daemon_info() failed.\n"); 1502 abort (); 1503 } 1504 *pport = dinfo->port; 1505 if (0 == global_port) 1506 global_port = *pport; /* Reuse the same port for all tests */ 1507 } 1508 1509 return d; 1510 } 1511 1512 1513 /* Test runners */ 1514 1515 1516 static unsigned int 1517 testExternalGet (void) 1518 { 1519 struct MHD_Daemon *d; 1520 uint16_t d_port = global_port; /* Daemon's port */ 1521 struct ahc_cls_type *ahc_param; 1522 struct check_uri_cls *uri_cb_param; 1523 1524 d = startTestMhdDaemon (testMhdThreadExternal, testMhdPollBySelect, &d_port, 1525 &ahc_param, &uri_cb_param); 1526 1527 return performTestQueries (d, d_port, ahc_param, uri_cb_param); 1528 } 1529 1530 1531 static unsigned int 1532 testInternalGet (enum testMhdPollType pollType) 1533 { 1534 struct MHD_Daemon *d; 1535 uint16_t d_port = global_port; /* Daemon's port */ 1536 struct ahc_cls_type *ahc_param; 1537 struct check_uri_cls *uri_cb_param; 1538 1539 d = startTestMhdDaemon (testMhdThreadInternal, pollType, &d_port, 1540 &ahc_param, &uri_cb_param); 1541 1542 return performTestQueries (d, d_port, ahc_param, uri_cb_param); 1543 } 1544 1545 1546 static unsigned int 1547 testMultithreadedGet (enum testMhdPollType pollType) 1548 { 1549 struct MHD_Daemon *d; 1550 uint16_t d_port = global_port; /* Daemon's port */ 1551 struct ahc_cls_type *ahc_param; 1552 struct check_uri_cls *uri_cb_param; 1553 1554 d = startTestMhdDaemon (testMhdThreadInternalPerConnection, pollType, &d_port, 1555 &ahc_param, &uri_cb_param); 1556 return performTestQueries (d, d_port, ahc_param, uri_cb_param); 1557 } 1558 1559 1560 static unsigned int 1561 testMultithreadedPoolGet (enum testMhdPollType pollType) 1562 { 1563 struct MHD_Daemon *d; 1564 uint16_t d_port = global_port; /* Daemon's port */ 1565 struct ahc_cls_type *ahc_param; 1566 struct check_uri_cls *uri_cb_param; 1567 1568 d = startTestMhdDaemon (testMhdThreadInternalPool, pollType, &d_port, 1569 &ahc_param, &uri_cb_param); 1570 return performTestQueries (d, d_port, ahc_param, uri_cb_param); 1571 } 1572 1573 1574 int 1575 main (int argc, char *const *argv) 1576 { 1577 unsigned int errorCount = 0; 1578 unsigned int test_result = 0; 1579 verbose = 0; 1580 1581 if ((NULL == argv) || (0 == argv[0])) 1582 return 99; 1583 oneone = ! has_in_name (argv[0], "10"); 1584 large_req_method = has_in_name (argv[0], "_method") ? 1 : 0; 1585 large_req_url = has_in_name (argv[0], "_url") ? 1 : 0; 1586 large_req_header_name = has_in_name (argv[0], "_request_header_name") ? 1587 1 : 0; 1588 large_req_header_value = has_in_name (argv[0], "_request_header_value") ? 1589 1 : 0; 1590 large_req_headers = has_in_name (argv[0], "_request_headers") ? 1 : 0; 1591 large_rsp_header_name = has_in_name (argv[0], "_reply_header_name") ? 1592 1 : 0; 1593 large_rsp_header_value = has_in_name (argv[0], "_reply_header_value") ? 1594 1 : 0; 1595 large_rsp_headers = has_in_name (argv[0], "_reply_headers") ? 1 : 0; 1596 if (large_req_method + large_req_url + large_req_header_name 1597 + large_req_header_value + large_req_headers + large_rsp_header_name 1598 + large_rsp_header_value + large_rsp_headers != 1) 1599 return 99; 1600 verbose = ! (has_param (argc, argv, "-q") || 1601 has_param (argc, argv, "--quiet") || 1602 has_param (argc, argv, "-s") || 1603 has_param (argc, argv, "--silent")); 1604 1605 test_global_init (); 1606 1607 /* Could be set to non-zero value to enforce using specific port 1608 * in the test */ 1609 global_port = 0; 1610 test_result = testExternalGet (); 1611 if (test_result) 1612 fprintf (stderr, "FAILED: testExternalGet () - %u.\n", test_result); 1613 else if (verbose) 1614 printf ("PASSED: testExternalGet ().\n"); 1615 errorCount += test_result; 1616 if (MHD_YES == MHD_is_feature_supported (MHD_FEATURE_THREADS)) 1617 { 1618 test_result = testInternalGet (testMhdPollAuto); 1619 if (test_result) 1620 fprintf (stderr, "FAILED: testInternalGet (testMhdPollAuto) - %u.\n", 1621 test_result); 1622 else if (verbose) 1623 printf ("PASSED: testInternalGet (testMhdPollBySelect).\n"); 1624 errorCount += test_result; 1625 #ifdef _MHD_HEAVY_TESTS 1626 /* Actually tests are not heavy, but took too long to complete while 1627 * not really provide any additional results. */ 1628 test_result = testInternalGet (testMhdPollBySelect); 1629 if (test_result) 1630 fprintf (stderr, "FAILED: testInternalGet (testMhdPollBySelect) - %u.\n", 1631 test_result); 1632 else if (verbose) 1633 printf ("PASSED: testInternalGet (testMhdPollBySelect).\n"); 1634 errorCount += test_result; 1635 test_result = testMultithreadedPoolGet (testMhdPollBySelect); 1636 if (test_result) 1637 fprintf (stderr, 1638 "FAILED: testMultithreadedPoolGet (testMhdPollBySelect) - %u.\n", 1639 test_result); 1640 else if (verbose) 1641 printf ("PASSED: testMultithreadedPoolGet (testMhdPollBySelect).\n"); 1642 errorCount += test_result; 1643 test_result = testMultithreadedGet (testMhdPollBySelect); 1644 if (test_result) 1645 fprintf (stderr, 1646 "FAILED: testMultithreadedGet (testMhdPollBySelect) - %u.\n", 1647 test_result); 1648 else if (verbose) 1649 printf ("PASSED: testMultithreadedGet (testMhdPollBySelect).\n"); 1650 errorCount += test_result; 1651 if (MHD_YES == MHD_is_feature_supported (MHD_FEATURE_POLL)) 1652 { 1653 test_result = testInternalGet (testMhdPollByPoll); 1654 if (test_result) 1655 fprintf (stderr, "FAILED: testInternalGet (testMhdPollByPoll) - %u.\n", 1656 test_result); 1657 else if (verbose) 1658 printf ("PASSED: testInternalGet (testMhdPollByPoll).\n"); 1659 errorCount += test_result; 1660 } 1661 if (MHD_YES == MHD_is_feature_supported (MHD_FEATURE_EPOLL)) 1662 { 1663 test_result = testInternalGet (testMhdPollByEpoll); 1664 if (test_result) 1665 fprintf (stderr, "FAILED: testInternalGet (testMhdPollByEpoll) - %u.\n", 1666 test_result); 1667 else if (verbose) 1668 printf ("PASSED: testInternalGet (testMhdPollByEpoll).\n"); 1669 errorCount += test_result; 1670 } 1671 #else 1672 /* Mute compiler warnings */ 1673 (void) testMultithreadedGet; 1674 (void) testMultithreadedPoolGet; 1675 #endif /* _MHD_HEAVY_TESTS */ 1676 } 1677 if (0 != errorCount) 1678 fprintf (stderr, 1679 "Error (code: %u)\n", 1680 errorCount); 1681 else if (verbose) 1682 printf ("All tests passed.\n"); 1683 1684 test_global_cleanup (); 1685 1686 return (errorCount == 0) ? 0 : 1; /* 0 == pass */ 1687 }