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