test_get_chunked.c (22226B)
1 /* 2 This file is part of libmicrohttpd 3 Copyright (C) 2007 Christian Grothoff 4 Copyright (C) 2014-2022 Evgeny Grin (Karlson2k) 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 /** 23 * @file test_get_chunked.c 24 * @brief Testcase for libmicrohttpd GET operations with chunked content encoding 25 * @author Christian Grothoff 26 * @author Karlson2k (Evgeny Grin) 27 */ 28 29 #include "MHD_config.h" 30 #include "platform.h" 31 #include <curl/curl.h> 32 #include <microhttpd.h> 33 #include <stdlib.h> 34 #include <string.h> 35 #include <time.h> 36 #include <errno.h> 37 38 #ifndef WINDOWS 39 #include <unistd.h> 40 #endif 41 42 #include "mhd_has_in_name.h" 43 44 /* Turn any MHD_PANIC() or failing mhd_assert() reached from this 45 test into a marked, classifiable test error (TESTING.md, P5). */ 46 #include "mhd_panic_tripwire.h" 47 48 #if defined(MHD_CPU_COUNT) && (MHD_CPU_COUNT + 0) < 2 49 #undef MHD_CPU_COUNT 50 #endif 51 #if ! defined(MHD_CPU_COUNT) 52 #define MHD_CPU_COUNT 2 53 #endif 54 55 #define HDR_CHUNKED_ENCODING MHD_HTTP_HEADER_TRANSFER_ENCODING ": chunked" 56 #define RESP_FOOTER_NAME "Footer" 57 #define RESP_FOOTER_VALUE "working" 58 #define RESP_FOOTER RESP_FOOTER_NAME ": " RESP_FOOTER_VALUE 59 60 #define RESP_BLOCK_SIZE 128 61 #define RESP_BLOCK_QUANTIY 10 62 #define RESP_SIZE (RESP_BLOCK_SIZE * RESP_BLOCK_QUANTIY) 63 64 /** 65 * Use "Connection: close" header? 66 */ 67 static int conn_close; 68 69 /** 70 * Use static string response instead of callback-generated? 71 */ 72 static int resp_string; 73 74 /** 75 * Use response with known size? 76 */ 77 static int resp_sized; 78 79 /** 80 * Use empty (zero-sized) response? 81 */ 82 static int resp_empty; 83 84 /** 85 * Force chunked response by response header? 86 */ 87 static int chunked_forced; 88 89 /** 90 * MHD port used for testing 91 */ 92 static uint16_t port_global; 93 94 95 struct headers_check_result 96 { 97 int found_chunked; 98 int found_footer; 99 }; 100 101 static size_t 102 lcurl_hdr_callback (char *buffer, size_t size, size_t nitems, 103 void *userdata) 104 { 105 const size_t data_size = size * nitems; 106 struct headers_check_result *check_res = 107 (struct headers_check_result *) userdata; 108 109 if ((data_size == strlen (HDR_CHUNKED_ENCODING) + 2) && 110 (0 == memcmp (buffer, HDR_CHUNKED_ENCODING "\r\n", data_size))) 111 check_res->found_chunked = 1; 112 if ((data_size == strlen (RESP_FOOTER) + 2) && 113 (0 == memcmp (buffer, RESP_FOOTER "\r\n", data_size))) 114 check_res->found_footer = 1; 115 116 return data_size; 117 } 118 119 120 struct CBC 121 { 122 char *buf; 123 size_t pos; 124 size_t size; 125 }; 126 127 128 static size_t 129 copyBuffer (void *ptr, 130 size_t size, 131 size_t nmemb, 132 void *ctx) 133 { 134 struct CBC *cbc = ctx; 135 136 if (cbc->pos + size * nmemb > cbc->size) 137 return 0; /* overflow */ 138 memcpy (&cbc->buf[cbc->pos], ptr, size * nmemb); 139 cbc->pos += size * nmemb; 140 return size * nmemb; 141 } 142 143 144 /** 145 * MHD content reader callback that returns data in chunks. 146 */ 147 static ssize_t 148 crc (void *cls, 149 uint64_t pos, 150 char *buf, 151 size_t max) 152 { 153 struct MHD_Response **responseptr = cls; 154 155 if (resp_empty || (pos == RESP_SIZE - RESP_BLOCK_SIZE)) 156 { /* Add footer with the last block */ 157 if (MHD_YES != MHD_add_response_footer (*responseptr, 158 RESP_FOOTER_NAME, 159 RESP_FOOTER_VALUE)) 160 abort (); 161 } 162 if (resp_empty || (pos == RESP_SIZE)) 163 return MHD_CONTENT_READER_END_OF_STREAM; 164 165 if (max < RESP_BLOCK_SIZE) 166 abort (); /* should not happen in this testcase... */ 167 memset (buf, 168 'A' + (char) (unsigned char) (pos / RESP_BLOCK_SIZE), 169 RESP_BLOCK_SIZE); 170 return RESP_BLOCK_SIZE; 171 } 172 173 174 /** 175 * Dummy function that frees the "responseptr". 176 */ 177 static void 178 crcf (void *ptr) 179 { 180 free (ptr); 181 } 182 183 184 static enum MHD_Result 185 ahc_echo (void *cls, 186 struct MHD_Connection *connection, 187 const char *url, 188 const char *method, 189 const char *version, 190 const char *upload_data, size_t *upload_data_size, void **req_cls) 191 { 192 static int aptr; 193 struct MHD_Response *response; 194 enum MHD_Result ret; 195 196 (void) cls; 197 (void) url; 198 (void) version; /* Unused. Silent compiler warning. */ 199 (void) upload_data; 200 (void) upload_data_size; /* Unused. Silent compiler warning. */ 201 202 if (0 != strcmp (MHD_HTTP_METHOD_GET, method)) 203 return MHD_NO; /* unexpected method */ 204 if (&aptr != *req_cls) 205 { 206 /* do never respond on first call */ 207 *req_cls = &aptr; 208 return MHD_YES; 209 } 210 if (! resp_string) 211 { 212 struct MHD_Response **responseptr; 213 responseptr = malloc (sizeof (struct MHD_Response *)); 214 if (NULL == responseptr) 215 _exit (99); 216 217 response = MHD_create_response_from_callback (resp_sized ? 218 RESP_SIZE : MHD_SIZE_UNKNOWN, 219 1024, 220 &crc, 221 responseptr, 222 &crcf); 223 *responseptr = response; 224 } 225 else 226 { 227 if (! resp_empty) 228 { 229 size_t pos; 230 static const size_t resp_size = RESP_SIZE; 231 char *buf = malloc (resp_size); 232 if (NULL == buf) 233 _exit (99); 234 for (pos = 0; pos < resp_size; pos += RESP_BLOCK_SIZE) 235 memset (buf + pos, 236 'A' + (char) (unsigned char) (pos / RESP_BLOCK_SIZE), 237 RESP_BLOCK_SIZE); 238 239 response = MHD_create_response_from_buffer_copy (resp_size, buf); 240 free (buf); 241 } 242 else 243 response = MHD_create_response_empty (MHD_RF_NONE); 244 } 245 if (NULL == response) 246 abort (); 247 if (chunked_forced) 248 { 249 if (MHD_NO == MHD_add_response_header (response, 250 MHD_HTTP_HEADER_TRANSFER_ENCODING, 251 "chunked")) 252 abort (); 253 } 254 if (MHD_NO == MHD_add_response_header (response, 255 MHD_HTTP_HEADER_TRAILER, 256 RESP_FOOTER_NAME)) 257 abort (); 258 259 if (resp_string || (resp_sized && resp_empty)) 260 { 261 /* There is no chance to add footer later */ 262 if (MHD_YES != MHD_add_response_footer (response, 263 RESP_FOOTER_NAME, 264 RESP_FOOTER_VALUE)) 265 abort (); 266 } 267 268 ret = MHD_queue_response (connection, 269 MHD_HTTP_OK, 270 response); 271 MHD_destroy_response (response); 272 return ret; 273 } 274 275 276 static unsigned int 277 validate (struct CBC cbc, unsigned int ebase) 278 { 279 int i; 280 char buf[RESP_BLOCK_SIZE]; 281 282 if (resp_empty) 283 { 284 if (0 != cbc.pos) 285 { 286 fprintf (stderr, 287 "Got %u bytes instead of zero!\n", 288 (unsigned int) cbc.pos); 289 return 1; 290 } 291 return 0; 292 } 293 294 if (cbc.pos != RESP_SIZE) 295 { 296 fprintf (stderr, 297 "Got %u bytes instead of 1280!\n", 298 (unsigned int) cbc.pos); 299 return ebase; 300 } 301 302 for (i = 0; i < RESP_BLOCK_QUANTIY; i++) 303 { 304 memset (buf, 'A' + i, RESP_BLOCK_SIZE); 305 if (0 != memcmp (buf, &cbc.buf[i * RESP_BLOCK_SIZE], RESP_BLOCK_SIZE)) 306 { 307 fprintf (stderr, 308 "Got `%.*s'\nWant `%.*s'\n", 309 RESP_BLOCK_SIZE, &cbc.buf[i * RESP_BLOCK_SIZE], 310 RESP_BLOCK_SIZE, buf); 311 return ebase * 2; 312 } 313 } 314 return 0; 315 } 316 317 318 static unsigned int 319 testInternalGet (void) 320 { 321 struct MHD_Daemon *d; 322 CURL *c; 323 char buf[2048]; 324 struct CBC cbc; 325 CURLcode errornum; 326 uint16_t port; 327 struct curl_slist *h_list = NULL; 328 struct headers_check_result hdr_check; 329 330 port = port_global; 331 cbc.buf = buf; 332 cbc.size = 2048; 333 cbc.pos = 0; 334 d = MHD_start_daemon (MHD_USE_INTERNAL_POLLING_THREAD | MHD_USE_ERROR_LOG, 335 port, NULL, NULL, &ahc_echo, NULL, MHD_OPTION_END); 336 if (d == NULL) 337 return 1; 338 if (0 == port) 339 { 340 const union MHD_DaemonInfo *dinfo; 341 dinfo = MHD_get_daemon_info (d, MHD_DAEMON_INFO_BIND_PORT); 342 if ((NULL == dinfo) || (0 == dinfo->port) ) 343 { 344 MHD_stop_daemon (d); return 32; 345 } 346 port = dinfo->port; 347 if (0 == port_global) 348 port_global = port; /* Re-use the same port for all checks */ 349 } 350 hdr_check.found_chunked = 0; 351 hdr_check.found_footer = 0; 352 c = curl_easy_init (); 353 curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1/hello_world"); 354 curl_easy_setopt (c, CURLOPT_PORT, (long) port); 355 curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, ©Buffer); 356 curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc); 357 curl_easy_setopt (c, CURLOPT_FAILONERROR, 1L); 358 curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L); 359 curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L); 360 curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1); 361 curl_easy_setopt (c, CURLOPT_HEADERFUNCTION, lcurl_hdr_callback); 362 curl_easy_setopt (c, CURLOPT_HEADERDATA, &hdr_check); 363 curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1L); 364 if (conn_close) 365 { 366 h_list = curl_slist_append (h_list, "Connection: close"); 367 if (NULL == h_list) 368 abort (); 369 curl_easy_setopt (c, CURLOPT_HTTPHEADER, h_list); 370 } 371 if (CURLE_OK != (errornum = curl_easy_perform (c))) 372 { 373 fprintf (stderr, 374 "curl_easy_perform failed: `%s'\n", 375 curl_easy_strerror (errornum)); 376 curl_easy_cleanup (c); 377 curl_slist_free_all (h_list); 378 MHD_stop_daemon (d); 379 return 2; 380 } 381 curl_easy_cleanup (c); 382 curl_slist_free_all (h_list); 383 MHD_stop_daemon (d); 384 if (1 != hdr_check.found_chunked) 385 { 386 fprintf (stderr, 387 "Chunked encoding header was not found in the response\n"); 388 return 8; 389 } 390 if (1 != hdr_check.found_footer) 391 { 392 fprintf (stderr, 393 "The specified footer was not found in the response\n"); 394 return 16; 395 } 396 return validate (cbc, 4); 397 } 398 399 400 static unsigned int 401 testMultithreadedGet (void) 402 { 403 struct MHD_Daemon *d; 404 CURL *c; 405 char buf[2048]; 406 struct CBC cbc; 407 CURLcode errornum; 408 uint16_t port; 409 struct curl_slist *h_list = NULL; 410 struct headers_check_result hdr_check; 411 412 port = port_global; 413 cbc.buf = buf; 414 cbc.size = 2048; 415 cbc.pos = 0; 416 d = MHD_start_daemon (MHD_USE_THREAD_PER_CONNECTION 417 | MHD_USE_INTERNAL_POLLING_THREAD | MHD_USE_ERROR_LOG, 418 port, NULL, NULL, &ahc_echo, NULL, MHD_OPTION_END); 419 if (d == NULL) 420 return 16; 421 if (0 == port) 422 { 423 const union MHD_DaemonInfo *dinfo; 424 dinfo = MHD_get_daemon_info (d, MHD_DAEMON_INFO_BIND_PORT); 425 if ((NULL == dinfo) || (0 == dinfo->port) ) 426 { 427 MHD_stop_daemon (d); return 32; 428 } 429 port = dinfo->port; 430 if (0 == port_global) 431 port_global = port; /* Re-use the same port for all checks */ 432 } 433 c = curl_easy_init (); 434 curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1/hello_world"); 435 curl_easy_setopt (c, CURLOPT_PORT, (long) port); 436 curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, ©Buffer); 437 curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc); 438 curl_easy_setopt (c, CURLOPT_FAILONERROR, 1L); 439 curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L); 440 curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1); 441 curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L); 442 curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1L); 443 hdr_check.found_chunked = 0; 444 hdr_check.found_footer = 0; 445 curl_easy_setopt (c, CURLOPT_HEADERFUNCTION, lcurl_hdr_callback); 446 curl_easy_setopt (c, CURLOPT_HEADERDATA, &hdr_check); 447 if (conn_close) 448 { 449 h_list = curl_slist_append (h_list, "Connection: close"); 450 if (NULL == h_list) 451 abort (); 452 curl_easy_setopt (c, CURLOPT_HTTPHEADER, h_list); 453 } 454 if (CURLE_OK != (errornum = curl_easy_perform (c))) 455 { 456 fprintf (stderr, 457 "curl_easy_perform failed: `%s'\n", 458 curl_easy_strerror (errornum)); 459 curl_easy_cleanup (c); 460 curl_slist_free_all (h_list); 461 MHD_stop_daemon (d); 462 return 32; 463 } 464 curl_easy_cleanup (c); 465 curl_slist_free_all (h_list); 466 MHD_stop_daemon (d); 467 if (1 != hdr_check.found_chunked) 468 { 469 fprintf (stderr, 470 "Chunked encoding header was not found in the response\n"); 471 return 8; 472 } 473 if (1 != hdr_check.found_footer) 474 { 475 fprintf (stderr, 476 "The specified footer was not found in the response\n"); 477 return 16; 478 } 479 return validate (cbc, 64); 480 } 481 482 483 static unsigned int 484 testMultithreadedPoolGet (void) 485 { 486 struct MHD_Daemon *d; 487 CURL *c; 488 char buf[2048]; 489 struct CBC cbc; 490 CURLcode errornum; 491 uint16_t port; 492 struct curl_slist *h_list = NULL; 493 struct headers_check_result hdr_check; 494 495 port = port_global; 496 cbc.buf = buf; 497 cbc.size = 2048; 498 cbc.pos = 0; 499 d = MHD_start_daemon (MHD_USE_INTERNAL_POLLING_THREAD | MHD_USE_ERROR_LOG, 500 port, NULL, NULL, &ahc_echo, NULL, 501 MHD_OPTION_THREAD_POOL_SIZE, MHD_CPU_COUNT, 502 MHD_OPTION_END); 503 if (d == NULL) 504 return 16; 505 if (0 == port) 506 { 507 const union MHD_DaemonInfo *dinfo; 508 dinfo = MHD_get_daemon_info (d, MHD_DAEMON_INFO_BIND_PORT); 509 if ((NULL == dinfo) || (0 == dinfo->port) ) 510 { 511 MHD_stop_daemon (d); return 32; 512 } 513 port = dinfo->port; 514 if (0 == port_global) 515 port_global = port; /* Re-use the same port for all checks */ 516 } 517 c = curl_easy_init (); 518 curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1/hello_world"); 519 curl_easy_setopt (c, CURLOPT_PORT, (long) port); 520 curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, ©Buffer); 521 curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc); 522 curl_easy_setopt (c, CURLOPT_FAILONERROR, 1L); 523 curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L); 524 curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1); 525 curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L); 526 curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1L); 527 hdr_check.found_chunked = 0; 528 hdr_check.found_footer = 0; 529 curl_easy_setopt (c, CURLOPT_HEADERFUNCTION, lcurl_hdr_callback); 530 curl_easy_setopt (c, CURLOPT_HEADERDATA, &hdr_check); 531 if (conn_close) 532 { 533 h_list = curl_slist_append (h_list, "Connection: close"); 534 if (NULL == h_list) 535 abort (); 536 curl_easy_setopt (c, CURLOPT_HTTPHEADER, h_list); 537 } 538 if (CURLE_OK != (errornum = curl_easy_perform (c))) 539 { 540 fprintf (stderr, 541 "curl_easy_perform failed: `%s'\n", 542 curl_easy_strerror (errornum)); 543 curl_easy_cleanup (c); 544 curl_slist_free_all (h_list); 545 MHD_stop_daemon (d); 546 return 32; 547 } 548 curl_easy_cleanup (c); 549 curl_slist_free_all (h_list); 550 MHD_stop_daemon (d); 551 if (1 != hdr_check.found_chunked) 552 { 553 fprintf (stderr, 554 "Chunked encoding header was not found in the response\n"); 555 return 8; 556 } 557 if (1 != hdr_check.found_footer) 558 { 559 fprintf (stderr, 560 "The specified footer was not found in the response\n"); 561 return 16; 562 } 563 return validate (cbc, 64); 564 } 565 566 567 static unsigned int 568 testExternalGet (void) 569 { 570 struct MHD_Daemon *d; 571 CURL *c; 572 char buf[2048]; 573 struct CBC cbc; 574 CURLM *multi; 575 CURLMcode mret; 576 fd_set rs; 577 fd_set ws; 578 fd_set es; 579 MHD_socket maxsock; 580 #ifdef MHD_WINSOCK_SOCKETS 581 int maxposixs; /* Max socket number unused on W32 */ 582 #else /* MHD_POSIX_SOCKETS */ 583 #define maxposixs maxsock 584 #endif /* MHD_POSIX_SOCKETS */ 585 int running; 586 struct CURLMsg *msg; 587 time_t start; 588 struct timeval tv; 589 uint16_t port; 590 struct curl_slist *h_list = NULL; 591 struct headers_check_result hdr_check; 592 593 port = port_global; 594 multi = NULL; 595 cbc.buf = buf; 596 cbc.size = 2048; 597 cbc.pos = 0; 598 d = MHD_start_daemon (MHD_USE_ERROR_LOG | MHD_USE_NO_THREAD_SAFETY, 599 port, NULL, NULL, &ahc_echo, NULL, 600 MHD_OPTION_APP_FD_SETSIZE, (int) FD_SETSIZE, 601 MHD_OPTION_END); 602 if (d == NULL) 603 return 256; 604 if (0 == port) 605 { 606 const union MHD_DaemonInfo *dinfo; 607 dinfo = MHD_get_daemon_info (d, MHD_DAEMON_INFO_BIND_PORT); 608 if ((NULL == dinfo) || (0 == dinfo->port) ) 609 { 610 MHD_stop_daemon (d); return 32; 611 } 612 port = dinfo->port; 613 if (0 == port_global) 614 port_global = port; /* Re-use the same port for all checks */ 615 } 616 c = curl_easy_init (); 617 curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1/hello_world"); 618 curl_easy_setopt (c, CURLOPT_PORT, (long) port); 619 curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, ©Buffer); 620 curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc); 621 curl_easy_setopt (c, CURLOPT_FAILONERROR, 1L); 622 curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1); 623 curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L); 624 curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 5L); 625 curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1L); 626 hdr_check.found_chunked = 0; 627 hdr_check.found_footer = 0; 628 curl_easy_setopt (c, CURLOPT_HEADERFUNCTION, lcurl_hdr_callback); 629 curl_easy_setopt (c, CURLOPT_HEADERDATA, &hdr_check); 630 if (conn_close) 631 { 632 h_list = curl_slist_append (h_list, "Connection: close"); 633 if (NULL == h_list) 634 abort (); 635 curl_easy_setopt (c, CURLOPT_HTTPHEADER, h_list); 636 } 637 638 multi = curl_multi_init (); 639 if (multi == NULL) 640 { 641 curl_easy_cleanup (c); 642 curl_slist_free_all (h_list); 643 MHD_stop_daemon (d); 644 return 512; 645 } 646 mret = curl_multi_add_handle (multi, c); 647 if (mret != CURLM_OK) 648 { 649 curl_multi_cleanup (multi); 650 curl_easy_cleanup (c); 651 curl_slist_free_all (h_list); 652 MHD_stop_daemon (d); 653 return 1024; 654 } 655 start = time (NULL); 656 while ((time (NULL) - start < 5) && (multi != NULL)) 657 { 658 maxsock = MHD_INVALID_SOCKET; 659 maxposixs = -1; 660 FD_ZERO (&rs); 661 FD_ZERO (&ws); 662 FD_ZERO (&es); 663 curl_multi_perform (multi, &running); 664 mret = curl_multi_fdset (multi, &rs, &ws, &es, &maxposixs); 665 if (mret != CURLM_OK) 666 { 667 curl_multi_remove_handle (multi, c); 668 curl_multi_cleanup (multi); 669 curl_easy_cleanup (c); 670 curl_slist_free_all (h_list); 671 MHD_stop_daemon (d); 672 return 2048; 673 } 674 if (MHD_YES != MHD_get_fdset (d, &rs, &ws, &es, &maxsock)) 675 { 676 curl_multi_remove_handle (multi, c); 677 curl_multi_cleanup (multi); 678 curl_easy_cleanup (c); 679 curl_slist_free_all (h_list); 680 MHD_stop_daemon (d); 681 return 4096; 682 } 683 tv.tv_sec = 0; 684 tv.tv_usec = 1000; 685 if (-1 == select (maxposixs + 1, &rs, &ws, &es, &tv)) 686 { 687 #ifdef MHD_POSIX_SOCKETS 688 if (EINTR != errno) 689 { 690 fprintf (stderr, "Unexpected select() error: %d. Line: %d\n", 691 (int) errno, __LINE__); 692 fflush (stderr); 693 exit (99); 694 } 695 #else 696 if ((WSAEINVAL != WSAGetLastError ()) || 697 (0 != rs.fd_count) || (0 != ws.fd_count) || (0 != es.fd_count) ) 698 { 699 fprintf (stderr, "Unexpected select() error: %d. Line: %d\n", 700 (int) WSAGetLastError (), __LINE__); 701 fflush (stderr); 702 exit (99); 703 } 704 Sleep (1); 705 #endif 706 } 707 curl_multi_perform (multi, &running); 708 if (0 == running) 709 { 710 int pending; 711 int curl_fine = 0; 712 while (NULL != (msg = curl_multi_info_read (multi, &pending))) 713 { 714 if (msg->msg == CURLMSG_DONE) 715 { 716 if (msg->data.result == CURLE_OK) 717 curl_fine = 1; 718 else 719 { 720 fprintf (stderr, 721 "%s failed at %s:%d: `%s'\n", 722 "curl_multi_perform", 723 __FILE__, 724 __LINE__, curl_easy_strerror (msg->data.result)); 725 abort (); 726 } 727 } 728 } 729 if (! curl_fine) 730 { 731 fprintf (stderr, "libcurl haven't returned OK code\n"); 732 abort (); 733 } 734 curl_multi_remove_handle (multi, c); 735 curl_multi_cleanup (multi); 736 curl_easy_cleanup (c); 737 curl_slist_free_all (h_list); 738 h_list = NULL; 739 c = NULL; 740 multi = NULL; 741 } 742 MHD_run (d); 743 } 744 MHD_stop_daemon (d); 745 if (multi != NULL) 746 { 747 curl_multi_remove_handle (multi, c); 748 curl_easy_cleanup (c); 749 curl_multi_cleanup (multi); 750 } 751 curl_slist_free_all (h_list); 752 if (1 != hdr_check.found_chunked) 753 { 754 fprintf (stderr, 755 "Chunked encoding header was not found in the response\n"); 756 return 8; 757 } 758 if (1 != hdr_check.found_footer) 759 { 760 fprintf (stderr, 761 "The specified footer was not found in the response\n"); 762 return 16; 763 } 764 return validate (cbc, 8192); 765 } 766 767 768 int 769 main (int argc, char *const *argv) 770 { 771 unsigned int errorCount = 0; 772 (void) argc; (void) argv; /* Unused. Silent compiler warning. */ 773 774 if (0 != curl_global_init (CURL_GLOBAL_WIN32)) 775 return 2; 776 conn_close = has_in_name (argv[0], "_close"); 777 resp_string = has_in_name (argv[0], "_string"); 778 resp_sized = has_in_name (argv[0], "_sized"); 779 resp_empty = has_in_name (argv[0], "_empty"); 780 chunked_forced = has_in_name (argv[0], "_forced"); 781 if (resp_string) 782 resp_sized = ! 0; 783 if (resp_sized) 784 chunked_forced = ! 0; 785 786 if (MHD_NO != MHD_is_feature_supported (MHD_FEATURE_AUTODETECT_BIND_PORT)) 787 port_global = 0; 788 else 789 { 790 port_global = 4100; 791 if (conn_close) 792 port_global += 1 << 0; 793 if (resp_string) 794 port_global += 1 << 1; 795 if (resp_sized) 796 port_global += 1 << 2; 797 if (resp_empty) 798 port_global += 1 << 3; 799 if (chunked_forced) 800 port_global += 1 << 4; 801 } 802 803 if (MHD_YES == MHD_is_feature_supported (MHD_FEATURE_THREADS)) 804 { 805 errorCount += testInternalGet (); 806 errorCount += testMultithreadedGet (); 807 errorCount += testMultithreadedPoolGet (); 808 } 809 errorCount += testExternalGet (); 810 if (errorCount != 0) 811 fprintf (stderr, "Error (code: %u)\n", errorCount); 812 curl_global_cleanup (); 813 return (0 == errorCount) ? 0 : 1; /* 0 == pass */ 814 }