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