test_head.c (26044B)
1 /* 2 This file is part of GNU libmicrohttpd 3 Copyright (C) 2010 Christian Grothoff 4 Copyright (C) 2016-2022 Evgeny Grin (Karlson2k) 5 6 GNU 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 GNU 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 /** 23 * @file testcurl/test_head.c 24 * @brief Testcase for HEAD requests 25 * @author Karlson2k (Evgeny Grin) 26 */ 27 28 #include "mhd_options.h" 29 #include "platform.h" 30 #include <curl/curl.h> 31 #include <microhttpd.h> 32 #include <stdlib.h> 33 #include <string.h> 34 #include <time.h> 35 #include <errno.h> 36 37 #ifndef _WIN32 38 #include <sys/socket.h> 39 #include <unistd.h> 40 #else 41 #include <wincrypt.h> 42 #endif 43 44 #include "mhd_has_param.h" 45 #include "mhd_has_in_name.h" 46 47 /* Turn any MHD_PANIC() or failing mhd_assert() reached from this 48 test into a marked, classifiable test error (TESTING.md, P5). */ 49 #include "mhd_panic_tripwire.h" 50 51 #ifndef MHD_STATICSTR_LEN_ 52 /** 53 * Determine length of static string / macro strings at compile time. 54 */ 55 #define MHD_STATICSTR_LEN_(macro) (sizeof(macro) / sizeof(char) - 1) 56 #endif /* ! MHD_STATICSTR_LEN_ */ 57 58 #ifndef CURL_VERSION_BITS 59 #define CURL_VERSION_BITS(x,y,z) ((x) << 16 | (y) << 8 | (z)) 60 #endif /* ! CURL_VERSION_BITS */ 61 #ifndef CURL_AT_LEAST_VERSION 62 #define CURL_AT_LEAST_VERSION(x,y,z) \ 63 (LIBCURL_VERSION_NUM >= CURL_VERSION_BITS (x, y, z)) 64 #endif /* ! CURL_AT_LEAST_VERSION */ 65 66 #ifndef _MHD_INSTRMACRO 67 /* Quoted macro parameter */ 68 #define _MHD_INSTRMACRO(a) #a 69 #endif /* ! _MHD_INSTRMACRO */ 70 #ifndef _MHD_STRMACRO 71 /* Quoted expanded macro parameter */ 72 #define _MHD_STRMACRO(a) _MHD_INSTRMACRO (a) 73 #endif /* ! _MHD_STRMACRO */ 74 75 #if defined(HAVE___FUNC__) 76 #define externalErrorExit(ignore) \ 77 _externalErrorExit_func (NULL, __func__, __LINE__) 78 #define externalErrorExitDesc(errDesc) \ 79 _externalErrorExit_func (errDesc, __func__, __LINE__) 80 #define libcurlErrorExit(ignore) \ 81 _libcurlErrorExit_func (NULL, __func__, __LINE__) 82 #define libcurlErrorExitDesc(errDesc) \ 83 _libcurlErrorExit_func (errDesc, __func__, __LINE__) 84 #define mhdErrorExit(ignore) \ 85 _mhdErrorExit_func (NULL, __func__, __LINE__) 86 #define mhdErrorExitDesc(errDesc) \ 87 _mhdErrorExit_func (errDesc, __func__, __LINE__) 88 #define checkCURLE_OK(libcurlcall) \ 89 _checkCURLE_OK_func ((libcurlcall), _MHD_STRMACRO (libcurlcall), \ 90 __func__, __LINE__) 91 #elif defined(HAVE___FUNCTION__) 92 #define externalErrorExit(ignore) \ 93 _externalErrorExit_func (NULL, __FUNCTION__, __LINE__) 94 #define externalErrorExitDesc(errDesc) \ 95 _externalErrorExit_func (errDesc, __FUNCTION__, __LINE__) 96 #define libcurlErrorExit(ignore) \ 97 _libcurlErrorExit_func (NULL, __FUNCTION__, __LINE__) 98 #define libcurlErrorExitDesc(errDesc) \ 99 _libcurlErrorExit_func (errDesc, __FUNCTION__, __LINE__) 100 #define mhdErrorExit(ignore) \ 101 _mhdErrorExit_func (NULL, __FUNCTION__, __LINE__) 102 #define mhdErrorExitDesc(errDesc) \ 103 _mhdErrorExit_func (errDesc, __FUNCTION__, __LINE__) 104 #define checkCURLE_OK(libcurlcall) \ 105 _checkCURLE_OK_func ((libcurlcall), _MHD_STRMACRO (libcurlcall), \ 106 __FUNCTION__, __LINE__) 107 #else 108 #define externalErrorExit(ignore) _externalErrorExit_func (NULL, NULL, __LINE__) 109 #define externalErrorExitDesc(errDesc) \ 110 _externalErrorExit_func (errDesc, NULL, __LINE__) 111 #define libcurlErrorExit(ignore) _libcurlErrorExit_func (NULL, NULL, __LINE__) 112 #define libcurlErrorExitDesc(errDesc) \ 113 _libcurlErrorExit_func (errDesc, NULL, __LINE__) 114 #define mhdErrorExit(ignore) _mhdErrorExit_func (NULL, NULL, __LINE__) 115 #define mhdErrorExitDesc(errDesc) _mhdErrorExit_func (errDesc, NULL, __LINE__) 116 #define checkCURLE_OK(libcurlcall) \ 117 _checkCURLE_OK_func ((libcurlcall), _MHD_STRMACRO (libcurlcall), NULL, \ 118 __LINE__) 119 #endif 120 121 122 _MHD_NORETURN static void 123 _externalErrorExit_func (const char *errDesc, const char *funcName, int lineNum) 124 { 125 fflush (stdout); 126 if ((NULL != errDesc) && (0 != errDesc[0])) 127 fprintf (stderr, "%s", errDesc); 128 else 129 fprintf (stderr, "System or external library call failed"); 130 if ((NULL != funcName) && (0 != funcName[0])) 131 fprintf (stderr, " in %s", funcName); 132 if (0 < lineNum) 133 fprintf (stderr, " at line %d", lineNum); 134 135 fprintf (stderr, ".\nLast errno value: %d (%s)\n", (int) errno, 136 strerror (errno)); 137 #ifdef MHD_WINSOCK_SOCKETS 138 fprintf (stderr, "WSAGetLastError() value: %d\n", (int) WSAGetLastError ()); 139 #endif /* MHD_WINSOCK_SOCKETS */ 140 fflush (stderr); 141 exit (99); 142 } 143 144 145 static char libcurl_errbuf[CURL_ERROR_SIZE] = ""; 146 147 _MHD_NORETURN static void 148 _libcurlErrorExit_func (const char *errDesc, const char *funcName, int lineNum) 149 { 150 fflush (stdout); 151 if ((NULL != errDesc) && (0 != errDesc[0])) 152 fprintf (stderr, "%s", errDesc); 153 else 154 fprintf (stderr, "CURL library call failed"); 155 if ((NULL != funcName) && (0 != funcName[0])) 156 fprintf (stderr, " in %s", funcName); 157 if (0 < lineNum) 158 fprintf (stderr, " at line %d", lineNum); 159 160 fprintf (stderr, ".\nLast errno value: %d (%s)\n", (int) errno, 161 strerror (errno)); 162 #ifdef MHD_WINSOCK_SOCKETS 163 fprintf (stderr, "WSAGetLastError() value: %d\n", (int) WSAGetLastError ()); 164 #endif /* MHD_WINSOCK_SOCKETS */ 165 if (0 != libcurl_errbuf[0]) 166 fprintf (stderr, "Last libcurl error description: %s\n", libcurl_errbuf); 167 168 fflush (stderr); 169 exit (99); 170 } 171 172 173 _MHD_NORETURN static void 174 _mhdErrorExit_func (const char *errDesc, const char *funcName, int lineNum) 175 { 176 fflush (stdout); 177 if ((NULL != errDesc) && (0 != errDesc[0])) 178 fprintf (stderr, "%s", errDesc); 179 else 180 fprintf (stderr, "MHD unexpected error"); 181 if ((NULL != funcName) && (0 != funcName[0])) 182 fprintf (stderr, " in %s", funcName); 183 if (0 < lineNum) 184 fprintf (stderr, " at line %d", lineNum); 185 186 fprintf (stderr, ".\nLast errno value: %d (%s)\n", (int) errno, 187 strerror (errno)); 188 #ifdef MHD_WINSOCK_SOCKETS 189 fprintf (stderr, "WSAGetLastError() value: %d\n", (int) WSAGetLastError ()); 190 #endif /* MHD_WINSOCK_SOCKETS */ 191 192 fflush (stderr); 193 exit (8); 194 } 195 196 197 /* Could be increased to facilitate debugging */ 198 #define TIMEOUTS_VAL 5 199 200 #define EXPECTED_URI_BASE_PATH "/" 201 202 #define EXISTING_URI EXPECTED_URI_BASE_PATH 203 204 #define EXPECTED_URI_BASE_PATH_MISSING "/wrong_uri" 205 206 #define URL_SCHEME "http:/" "/" 207 208 #define URL_HOST "127.0.0.1" 209 210 #define URL_SCHEME_HOST URL_SCHEME URL_HOST 211 212 #define HEADER1_NAME "First" 213 #define HEADER1_VALUE "1st" 214 #define HEADER1 HEADER1_NAME ": " HEADER1_VALUE 215 #define HEADER1_CRLF HEADER1 "\r\n" 216 #define HEADER2_NAME "Normal" 217 #define HEADER2_VALUE "it's fine" 218 #define HEADER2 HEADER2_NAME ": " HEADER2_VALUE 219 #define HEADER2_CRLF HEADER2 "\r\n" 220 221 #define PAGE \ 222 "<html><head><title>libmicrohttpd demo page</title></head>" \ 223 "<body>Success!</body></html>" 224 225 #define PAGE_404 \ 226 "<html><head><title>404 error</title></head>" \ 227 "<body>Error 404: The requested URI does not exist</body></html>" 228 229 /* Global parameters */ 230 static int verbose; 231 static int oneone; /**< If false use HTTP/1.0 for requests*/ 232 233 static void 234 test_global_init (void) 235 { 236 libcurl_errbuf[0] = 0; 237 238 if (0 != curl_global_init (CURL_GLOBAL_WIN32)) 239 externalErrorExit (); 240 } 241 242 243 static void 244 test_global_cleanup (void) 245 { 246 curl_global_cleanup (); 247 } 248 249 250 struct headers_check_result 251 { 252 unsigned int expected_size; 253 int header1_found; 254 int header2_found; 255 int size_found; 256 }; 257 258 static size_t 259 lcurl_hdr_callback (char *buffer, size_t size, size_t nitems, 260 void *userdata) 261 { 262 const size_t data_size = size * nitems; 263 struct headers_check_result *check_res = 264 (struct headers_check_result *) userdata; 265 266 if ((MHD_STATICSTR_LEN_ (HEADER1_CRLF) == data_size) && 267 (0 == memcmp (HEADER1_CRLF, buffer, data_size))) 268 check_res->header1_found++; 269 else if ((MHD_STATICSTR_LEN_ (HEADER2_CRLF) == data_size) && 270 (0 == memcmp (HEADER2_CRLF, buffer, data_size))) 271 check_res->header2_found++; 272 else if ((MHD_STATICSTR_LEN_ (MHD_HTTP_HEADER_CONTENT_LENGTH ": ") 273 < data_size) && 274 (0 == 275 memcmp (MHD_HTTP_HEADER_CONTENT_LENGTH ": ", buffer, 276 MHD_STATICSTR_LEN_ (MHD_HTTP_HEADER_CONTENT_LENGTH ": ")))) 277 { 278 char cmpbuf[256]; 279 int res; 280 const unsigned int numbers_pos = 281 MHD_STATICSTR_LEN_ (MHD_HTTP_HEADER_CONTENT_LENGTH ": "); 282 res = snprintf (cmpbuf, sizeof(cmpbuf), "%u", check_res->expected_size); 283 if ((res <= 0) || (res > ((int) (sizeof(cmpbuf) - 1)))) 284 externalErrorExit (); 285 if (data_size - numbers_pos <= 2) 286 mhdErrorExitDesc ("Broken Content-Length"); 287 else if ((((size_t) res + 2) != data_size - numbers_pos) || 288 (0 != memcmp (buffer + numbers_pos, cmpbuf, (size_t) res))) 289 { 290 fprintf (stderr, "Wrong Content-Length.\n" 291 "Expected:\n%u\n" 292 "Received:\n%s", check_res->expected_size, 293 buffer + numbers_pos); 294 mhdErrorExitDesc ("Wrong Content-Length"); 295 } 296 else if (0 != memcmp ("\r\n", buffer + data_size - 2, 2)) 297 { 298 mhdErrorExitDesc ("The Content-Length header is not " \ 299 "terminated by CRLF"); 300 } 301 check_res->size_found++; 302 } 303 304 return data_size; 305 } 306 307 308 struct CBC 309 { 310 char *buf; 311 size_t pos; 312 size_t size; 313 }; 314 315 316 static size_t 317 copyBuffer (void *ptr, 318 size_t size, 319 size_t nmemb, 320 void *ctx) 321 { 322 (void) ptr; /* Unused, mute compiler warning */ 323 (void) ctx; /* Unused, mute compiler warning */ 324 if ((0 != size) && (0 != nmemb)) 325 libcurlErrorExitDesc ("Received unexpected body data"); 326 return size * nmemb; 327 } 328 329 330 struct ahc_cls_type 331 { 332 const char *rq_method; 333 const char *rq_url; 334 }; 335 336 337 static enum MHD_Result 338 ahcCheck (void *cls, 339 struct MHD_Connection *connection, 340 const char *url, 341 const char *method, 342 const char *version, 343 const char *upload_data, size_t *upload_data_size, 344 void **req_cls) 345 { 346 static int marker; 347 struct MHD_Response *response; 348 enum MHD_Result ret; 349 struct ahc_cls_type *const param = (struct ahc_cls_type *) cls; 350 unsigned int http_code; 351 352 if (NULL == param) 353 mhdErrorExitDesc ("cls parameter is NULL"); 354 355 if (oneone) 356 { 357 if (0 != strcmp (version, MHD_HTTP_VERSION_1_1)) 358 mhdErrorExitDesc ("Unexpected HTTP version"); 359 } 360 else 361 { 362 if (0 != strcmp (version, MHD_HTTP_VERSION_1_0)) 363 mhdErrorExitDesc ("Unexpected HTTP version"); 364 } 365 366 if (0 != strcmp (url, param->rq_url)) 367 mhdErrorExitDesc ("Unexpected URI"); 368 369 if (NULL != upload_data) 370 mhdErrorExitDesc ("'upload_data' is not NULL"); 371 372 if (NULL == upload_data_size) 373 mhdErrorExitDesc ("'upload_data_size' pointer is NULL"); 374 375 if (0 != *upload_data_size) 376 mhdErrorExitDesc ("'*upload_data_size' value is not zero"); 377 378 if (0 != strcmp (param->rq_method, method)) 379 mhdErrorExitDesc ("Unexpected request method"); 380 381 if (&marker != *req_cls) 382 { 383 *req_cls = ▮ 384 return MHD_YES; 385 } 386 *req_cls = NULL; 387 388 if (0 == strcmp (url, EXISTING_URI)) 389 { 390 response = 391 MHD_create_response_from_buffer_static (MHD_STATICSTR_LEN_ (PAGE), 392 PAGE); 393 http_code = MHD_HTTP_OK; 394 } 395 else 396 { 397 response = 398 MHD_create_response_from_buffer_static (MHD_STATICSTR_LEN_ (PAGE_404), 399 PAGE_404); 400 http_code = MHD_HTTP_NOT_FOUND; 401 } 402 if (NULL == response) 403 mhdErrorExitDesc ("Failed to create response"); 404 405 if (MHD_YES != MHD_add_response_header (response, 406 HEADER1_NAME, 407 HEADER1_VALUE)) 408 mhdErrorExitDesc ("Cannot add header1"); 409 if (MHD_YES != MHD_add_response_header (response, 410 HEADER2_NAME, 411 HEADER2_VALUE)) 412 mhdErrorExitDesc ("Cannot add header2"); 413 414 ret = MHD_queue_response (connection, 415 http_code, 416 response); 417 MHD_destroy_response (response); 418 if (MHD_YES != ret) 419 mhdErrorExitDesc ("Failed to queue response"); 420 421 return ret; 422 } 423 424 425 /** 426 * Set required URI for the request 427 * @param c the CURL handle to use 428 * @param uri_exist if non-zero use request for "existing" URI 429 */ 430 static void 431 setCURL_rq_path (CURL *c, int uri_exist) 432 { 433 if (uri_exist) 434 { 435 if (CURLE_OK != 436 curl_easy_setopt (c, CURLOPT_URL, 437 URL_SCHEME_HOST EXPECTED_URI_BASE_PATH)) 438 libcurlErrorExitDesc ("Cannot set request URL"); 439 } 440 else 441 { 442 if (CURLE_OK != 443 curl_easy_setopt (c, CURLOPT_URL, 444 URL_SCHEME_HOST EXPECTED_URI_BASE_PATH_MISSING)) 445 libcurlErrorExitDesc ("Cannot set request URL"); 446 } 447 } 448 449 450 static int 451 libcurl_debug_cb (CURL *handle, 452 curl_infotype type, 453 char *data, 454 size_t size, 455 void *userptr) 456 { 457 static const char excess_mark[] = "Excess found"; 458 static const size_t excess_mark_len = MHD_STATICSTR_LEN_ (excess_mark); 459 460 (void) handle; 461 (void) userptr; 462 463 #ifdef _DEBUG 464 switch (type) 465 { 466 case CURLINFO_TEXT: 467 fprintf (stderr, "* %.*s", (int) size, data); 468 break; 469 case CURLINFO_HEADER_IN: 470 fprintf (stderr, "< %.*s", (int) size, data); 471 break; 472 case CURLINFO_HEADER_OUT: 473 fprintf (stderr, "> %.*s", (int) size, data); 474 break; 475 case CURLINFO_DATA_IN: 476 #if 0 477 fprintf (stderr, "<| %.*s\n", (int) size, data); 478 #endif 479 break; 480 case CURLINFO_DATA_OUT: 481 case CURLINFO_SSL_DATA_IN: 482 case CURLINFO_SSL_DATA_OUT: 483 case CURLINFO_END: 484 default: 485 break; 486 } 487 #endif /* _DEBUG */ 488 if (CURLINFO_TEXT == type) 489 { 490 if ((size >= excess_mark_len) && 491 (0 == memcmp (data, excess_mark, excess_mark_len))) 492 mhdErrorExitDesc ("Extra data has been detected in MHD reply"); 493 } 494 return 0; 495 } 496 497 498 static CURL * 499 setupCURL (void *cbc, uint16_t port, 500 struct headers_check_result *hdr_chk_result) 501 { 502 CURL *c; 503 504 c = curl_easy_init (); 505 if (NULL == c) 506 libcurlErrorExitDesc ("curl_easy_init() failed"); 507 508 if ((CURLE_OK != curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1L)) || 509 (CURLE_OK != curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, 510 ©Buffer)) || 511 (CURLE_OK != curl_easy_setopt (c, CURLOPT_WRITEDATA, cbc)) || 512 (CURLE_OK != curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 513 ((long) TIMEOUTS_VAL))) || 514 (CURLE_OK != curl_easy_setopt (c, CURLOPT_HTTP_VERSION, 515 (oneone) ? 516 CURL_HTTP_VERSION_1_1 : 517 CURL_HTTP_VERSION_1_0)) || 518 (CURLE_OK != curl_easy_setopt (c, CURLOPT_TIMEOUT, 519 ((long) TIMEOUTS_VAL))) || 520 (CURLE_OK != curl_easy_setopt (c, CURLOPT_HEADERFUNCTION, 521 lcurl_hdr_callback)) || 522 (CURLE_OK != curl_easy_setopt (c, CURLOPT_HEADERDATA, 523 hdr_chk_result)) || 524 (CURLE_OK != curl_easy_setopt (c, CURLOPT_ERRORBUFFER, 525 libcurl_errbuf)) || 526 (CURLE_OK != curl_easy_setopt (c, CURLOPT_FAILONERROR, 0L)) || 527 #ifdef _DEBUG 528 (CURLE_OK != curl_easy_setopt (c, CURLOPT_VERBOSE, 1L)) || 529 #endif /* _DEBUG */ 530 (CURLE_OK != curl_easy_setopt (c, CURLOPT_DEBUGFUNCTION, 531 &libcurl_debug_cb)) || 532 #if CURL_AT_LEAST_VERSION (7, 85, 0) 533 (CURLE_OK != curl_easy_setopt (c, CURLOPT_PROTOCOLS_STR, "http")) || 534 #elif CURL_AT_LEAST_VERSION (7, 19, 4) 535 (CURLE_OK != curl_easy_setopt (c, CURLOPT_PROTOCOLS, CURLPROTO_HTTP)) || 536 #endif /* CURL_AT_LEAST_VERSION (7, 19, 4) */ 537 #if CURL_AT_LEAST_VERSION (7, 45, 0) 538 (CURLE_OK != curl_easy_setopt (c, CURLOPT_DEFAULT_PROTOCOL, "http")) || 539 #endif /* CURL_AT_LEAST_VERSION (7, 45, 0) */ 540 (CURLE_OK != curl_easy_setopt (c, CURLOPT_PORT, ((long) port)))) 541 libcurlErrorExitDesc ("curl_easy_setopt() failed"); 542 543 /* When 'CURLOPT_NOBODY' is set, libcurl should use HEAD request. */ 544 if (CURLE_OK != curl_easy_setopt (c, CURLOPT_NOBODY, (long) 1)) 545 libcurlErrorExitDesc ("curl_easy_setopt() failed"); 546 547 return c; 548 } 549 550 551 static CURLcode 552 performQueryExternal (struct MHD_Daemon *d, CURL *c, CURLM **multi_reuse) 553 { 554 CURLM *multi; 555 time_t start; 556 struct timeval tv; 557 CURLcode ret; 558 559 ret = CURLE_FAILED_INIT; /* will be replaced with real result */ 560 if (NULL != *multi_reuse) 561 multi = *multi_reuse; 562 else 563 { 564 multi = curl_multi_init (); 565 if (multi == NULL) 566 libcurlErrorExitDesc ("curl_multi_init() failed"); 567 *multi_reuse = multi; 568 } 569 if (CURLM_OK != curl_multi_add_handle (multi, c)) 570 libcurlErrorExitDesc ("curl_multi_add_handle() failed"); 571 572 start = time (NULL); 573 while (time (NULL) - start <= TIMEOUTS_VAL) 574 { 575 fd_set rs; 576 fd_set ws; 577 fd_set es; 578 MHD_socket maxMhdSk; 579 int maxCurlSk; 580 int running; 581 582 maxMhdSk = MHD_INVALID_SOCKET; 583 maxCurlSk = -1; 584 FD_ZERO (&rs); 585 FD_ZERO (&ws); 586 FD_ZERO (&es); 587 if (NULL != multi) 588 { 589 curl_multi_perform (multi, &running); 590 if (0 == running) 591 { 592 struct CURLMsg *msg; 593 int msgLeft; 594 int totalMsgs = 0; 595 do 596 { 597 msg = curl_multi_info_read (multi, &msgLeft); 598 if (NULL == msg) 599 libcurlErrorExitDesc ("curl_multi_info_read() failed"); 600 totalMsgs++; 601 if (CURLMSG_DONE == msg->msg) 602 ret = msg->data.result; 603 } while (msgLeft > 0); 604 if (1 != totalMsgs) 605 { 606 fprintf (stderr, 607 "curl_multi_info_read returned wrong " 608 "number of results (%d).\n", 609 totalMsgs); 610 externalErrorExit (); 611 } 612 curl_multi_remove_handle (multi, c); 613 multi = NULL; 614 } 615 else 616 { 617 if (CURLM_OK != curl_multi_fdset (multi, &rs, &ws, &es, &maxCurlSk)) 618 libcurlErrorExitDesc ("curl_multi_fdset() failed"); 619 } 620 } 621 if (NULL == multi) 622 { /* libcurl has finished, check whether MHD still needs to perform cleanup */ 623 if (0 != MHD_get_timeout64s (d)) 624 break; /* MHD finished as well */ 625 } 626 if (MHD_YES != MHD_get_fdset (d, &rs, &ws, &es, &maxMhdSk)) 627 mhdErrorExitDesc ("MHD_get_fdset() failed"); 628 tv.tv_sec = 0; 629 tv.tv_usec = 200000; 630 if (0 == MHD_get_timeout64s (d)) 631 tv.tv_usec = 0; 632 else 633 { 634 long curl_to = -1; 635 curl_multi_timeout (multi, &curl_to); 636 if (0 == curl_to) 637 tv.tv_usec = 0; 638 } 639 #ifdef MHD_POSIX_SOCKETS 640 if (maxMhdSk > maxCurlSk) 641 maxCurlSk = maxMhdSk; 642 #endif /* MHD_POSIX_SOCKETS */ 643 if (-1 == select (maxCurlSk + 1, &rs, &ws, &es, &tv)) 644 { 645 #ifdef MHD_POSIX_SOCKETS 646 if (EINTR != errno) 647 externalErrorExitDesc ("Unexpected select() error"); 648 #else 649 if ((WSAEINVAL != WSAGetLastError ()) || 650 (0 != rs.fd_count) || (0 != ws.fd_count) || (0 != es.fd_count) ) 651 externalErrorExitDesc ("Unexpected select() error"); 652 Sleep ((unsigned long) tv.tv_usec / 1000); 653 #endif 654 } 655 if (MHD_YES != MHD_run_from_select (d, &rs, &ws, &es)) 656 mhdErrorExitDesc ("MHD_run_from_select() failed"); 657 } 658 659 return ret; 660 } 661 662 663 /** 664 * Check request result 665 * @param curl_code the CURL easy return code 666 * @param pcbc the pointer struct CBC 667 * @return non-zero if success, zero if failed 668 */ 669 static unsigned int 670 check_result (CURLcode curl_code, CURL *c, long expected_code, 671 struct headers_check_result *hdr_res) 672 { 673 long code; 674 unsigned int ret; 675 676 if (CURLE_OK != curl_code) 677 { 678 fflush (stdout); 679 if (0 != libcurl_errbuf[0]) 680 fprintf (stderr, "Request failed. " 681 "libcurl error: '%s'.\n" 682 "libcurl error description: '%s'.\n", 683 curl_easy_strerror (curl_code), 684 libcurl_errbuf); 685 else 686 fprintf (stderr, "Request failed. " 687 "libcurl error: '%s'.\n", 688 curl_easy_strerror (curl_code)); 689 fflush (stderr); 690 return 0; 691 } 692 693 if (CURLE_OK != curl_easy_getinfo (c, CURLINFO_RESPONSE_CODE, &code)) 694 libcurlErrorExit (); 695 696 if (expected_code != code) 697 { 698 fprintf (stderr, "The response has wrong HTTP code: %ld\tExpected: %ld.\n", 699 code, expected_code); 700 return 0; 701 } 702 else if (verbose) 703 printf ("The response has expected HTTP code: %ld\n", expected_code); 704 705 ret = 1; 706 if (1 != hdr_res->header1_found) 707 { 708 if (0 == hdr_res->header1_found) 709 fprintf (stderr, "Response header1 was not found.\n"); 710 else 711 fprintf (stderr, "Response header1 was found %d times " 712 "instead of one time only.\n", hdr_res->header1_found); 713 ret = 0; 714 } 715 else if (verbose) 716 printf ("Header1 is present in the response.\n"); 717 if (1 != hdr_res->header2_found) 718 { 719 if (0 == hdr_res->header2_found) 720 fprintf (stderr, "Response header2 was not found.\n"); 721 else 722 fprintf (stderr, "Response header2 was found %d times " 723 "instead of one time only.\n", hdr_res->header2_found); 724 ret = 0; 725 } 726 else if (verbose) 727 printf ("Header2 is present in the response.\n"); 728 if (1 != hdr_res->size_found) 729 { 730 if (0 == hdr_res->size_found) 731 fprintf (stderr, "Response 'Content-Length' header was not found.\n"); 732 else 733 fprintf (stderr, "Response 'Content-Length' header was found %d times " 734 "instead of one time only.\n", hdr_res->size_found); 735 ret = 0; 736 } 737 else if (verbose) 738 printf ("'Content-Length' header with correct value " 739 "is present in the response.\n"); 740 741 return ret; 742 } 743 744 745 static unsigned int 746 testHead (void) 747 { 748 struct MHD_Daemon *d; 749 uint16_t port; 750 struct CBC cbc; 751 struct ahc_cls_type ahc_param; 752 struct headers_check_result rp_headers_check; 753 char buf[2048]; 754 CURL *c; 755 CURLM *multi_reuse; 756 int failed = 0; 757 758 if (MHD_NO != MHD_is_feature_supported (MHD_FEATURE_AUTODETECT_BIND_PORT)) 759 port = 0; 760 else 761 port = 4220 + oneone ? 0 : 1; 762 763 d = MHD_start_daemon (MHD_USE_ERROR_LOG | MHD_USE_NO_THREAD_SAFETY, 764 port, NULL, NULL, 765 &ahcCheck, &ahc_param, 766 MHD_OPTION_APP_FD_SETSIZE, (int) FD_SETSIZE, 767 MHD_OPTION_END); 768 if (d == NULL) 769 return 1; 770 if (0 == port) 771 { 772 const union MHD_DaemonInfo *dinfo; 773 774 dinfo = MHD_get_daemon_info (d, 775 MHD_DAEMON_INFO_BIND_PORT); 776 if ( (NULL == dinfo) || 777 (0 == dinfo->port) ) 778 mhdErrorExitDesc ("MHD_get_daemon_info() failed"); 779 port = dinfo->port; 780 } 781 782 /* First request */ 783 ahc_param.rq_method = MHD_HTTP_METHOD_HEAD; 784 ahc_param.rq_url = EXPECTED_URI_BASE_PATH; 785 rp_headers_check.expected_size = MHD_STATICSTR_LEN_ (PAGE); 786 rp_headers_check.header1_found = 0; 787 rp_headers_check.header2_found = 0; 788 rp_headers_check.size_found = 0; 789 cbc.buf = buf; 790 cbc.size = sizeof (buf); 791 cbc.pos = 0; 792 memset (cbc.buf, 0, cbc.size); 793 c = setupCURL (&cbc, port, &rp_headers_check); 794 setCURL_rq_path (c, 1); 795 multi_reuse = NULL; 796 /* First request */ 797 if (check_result (performQueryExternal (d, c, &multi_reuse), c, 798 MHD_HTTP_OK, &rp_headers_check)) 799 { 800 fflush (stderr); 801 if (verbose) 802 printf ("Got first expected response.\n"); 803 fflush (stdout); 804 } 805 else 806 { 807 fprintf (stderr, "First request FAILED.\n"); 808 fflush (stderr); 809 failed = 1; 810 } 811 /* Second request */ 812 rp_headers_check.expected_size = MHD_STATICSTR_LEN_ (PAGE_404); 813 rp_headers_check.header1_found = 0; 814 rp_headers_check.header2_found = 0; 815 rp_headers_check.size_found = 0; 816 cbc.pos = 0; /* Reset buffer position */ 817 ahc_param.rq_url = EXPECTED_URI_BASE_PATH_MISSING; 818 setCURL_rq_path (c, 0); 819 if (check_result (performQueryExternal (d, c, &multi_reuse), c, 820 MHD_HTTP_NOT_FOUND, &rp_headers_check)) 821 { 822 fflush (stderr); 823 if (verbose) 824 printf ("Got second expected response.\n"); 825 fflush (stdout); 826 } 827 else 828 { 829 fprintf (stderr, "Second request FAILED.\n"); 830 fflush (stderr); 831 failed = 1; 832 } 833 /* Third request */ 834 rp_headers_check.header1_found = 0; 835 rp_headers_check.header2_found = 0; 836 rp_headers_check.size_found = 0; 837 cbc.pos = 0; /* Reset buffer position */ 838 if (NULL != multi_reuse) 839 curl_multi_cleanup (multi_reuse); 840 multi_reuse = NULL; /* Force new connection */ 841 if (check_result (performQueryExternal (d, c, &multi_reuse), c, 842 MHD_HTTP_NOT_FOUND, &rp_headers_check)) 843 { 844 fflush (stderr); 845 if (verbose) 846 printf ("Got third expected response.\n"); 847 fflush (stdout); 848 } 849 else 850 { 851 fprintf (stderr, "Third request FAILED.\n"); 852 fflush (stderr); 853 failed = 1; 854 } 855 856 curl_easy_cleanup (c); 857 if (NULL != multi_reuse) 858 curl_multi_cleanup (multi_reuse); 859 860 MHD_stop_daemon (d); 861 return failed ? 1 : 0; 862 } 863 864 865 int 866 main (int argc, char *const *argv) 867 { 868 unsigned int errorCount = 0; 869 870 /* Test type and test parameters */ 871 verbose = ! (has_param (argc, argv, "-q") || 872 has_param (argc, argv, "--quiet") || 873 has_param (argc, argv, "-s") || 874 has_param (argc, argv, "--silent")); 875 oneone = ! has_in_name (argv[0], "10"); 876 877 test_global_init (); 878 879 errorCount += testHead (); 880 if (errorCount != 0) 881 fprintf (stderr, "Error (code: %u)\n", errorCount); 882 test_global_cleanup (); 883 return (0 == errorCount) ? 0 : 1; /* 0 == pass */ 884 }