test_post.c (23746B)
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_post.c 24 * @brief Testcase for libmicrohttpd POST operations using URL-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 #ifdef _WIN32 43 #ifndef WIN32_LEAN_AND_MEAN 44 #define WIN32_LEAN_AND_MEAN 1 45 #endif /* !WIN32_LEAN_AND_MEAN */ 46 #include <windows.h> 47 #endif 48 49 #include "mhd_has_in_name.h" 50 51 /* Turn any MHD_PANIC() or failing mhd_assert() reached from this 52 test into a marked, classifiable test error (TESTING.md, P5). */ 53 #include "mhd_panic_tripwire.h" 54 55 #if defined(MHD_CPU_COUNT) && (MHD_CPU_COUNT + 0) < 2 56 #undef MHD_CPU_COUNT 57 #endif 58 #if ! defined(MHD_CPU_COUNT) 59 #define MHD_CPU_COUNT 2 60 #endif 61 62 #define POST_DATA "name=daniel&project=curl" 63 64 static int oneone; 65 66 struct CBC 67 { 68 char *buf; 69 size_t pos; 70 size_t size; 71 }; 72 73 74 static void 75 completed_cb (void *cls, 76 struct MHD_Connection *connection, 77 void **req_cls, 78 enum MHD_RequestTerminationCode toe) 79 { 80 struct MHD_PostProcessor *pp = *req_cls; 81 (void) cls; (void) connection; (void) toe; /* Unused. Silent compiler warning. */ 82 83 if (NULL != pp) 84 MHD_destroy_post_processor (pp); 85 *req_cls = NULL; 86 } 87 88 89 static size_t 90 copyBuffer (void *ptr, size_t size, size_t nmemb, void *ctx) 91 { 92 struct CBC *cbc = ctx; 93 94 if (cbc->pos + size * nmemb > cbc->size) 95 return 0; /* overflow */ 96 memcpy (&cbc->buf[cbc->pos], ptr, size * nmemb); 97 cbc->pos += size * nmemb; 98 return size * nmemb; 99 } 100 101 102 /** 103 * Note that this post_iterator is not perfect 104 * in that it fails to support incremental processing. 105 * (to be fixed in the future) 106 */ 107 static enum MHD_Result 108 post_iterator (void *cls, 109 enum MHD_ValueKind kind, 110 const char *key, 111 const char *filename, 112 const char *content_type, 113 const char *transfer_encoding, 114 const char *value, uint64_t off, size_t size) 115 { 116 int *eok = cls; 117 (void) kind; (void) filename; (void) content_type; /* Unused. Silent compiler warning. */ 118 (void) transfer_encoding; (void) off; /* Unused. Silent compiler warning. */ 119 120 if ((0 == strcmp (key, "name")) && 121 (size == strlen ("daniel")) && (0 == strncmp (value, "daniel", size))) 122 (*eok) |= 1; 123 if ((0 == strcmp (key, "project")) && 124 (size == strlen ("curl")) && (0 == strncmp (value, "curl", size))) 125 (*eok) |= 2; 126 return MHD_YES; 127 } 128 129 130 static enum MHD_Result 131 ahc_echo (void *cls, 132 struct MHD_Connection *connection, 133 const char *url, 134 const char *method, 135 const char *version, 136 const char *upload_data, size_t *upload_data_size, 137 void **req_cls) 138 { 139 static int eok; 140 struct MHD_Response *response; 141 struct MHD_PostProcessor *pp; 142 enum MHD_Result ret; 143 (void) cls; (void) version; /* Unused. Silent compiler warning. */ 144 145 if (0 != strcmp (MHD_HTTP_METHOD_POST, method)) 146 { 147 fprintf (stderr, "METHOD: %s\n", method); 148 return MHD_NO; /* unexpected method */ 149 } 150 pp = *req_cls; 151 if (pp == NULL) 152 { 153 eok = 0; 154 pp = MHD_create_post_processor (connection, 1024, &post_iterator, &eok); 155 *req_cls = pp; 156 } 157 MHD_post_process (pp, upload_data, *upload_data_size); 158 if ((eok == 3) && (0 == *upload_data_size)) 159 { 160 response = MHD_create_response_from_buffer_copy (strlen (url), 161 (const void *) url); 162 ret = MHD_queue_response (connection, MHD_HTTP_OK, response); 163 MHD_destroy_response (response); 164 MHD_destroy_post_processor (pp); 165 *req_cls = NULL; 166 return ret; 167 } 168 *upload_data_size = 0; 169 return MHD_YES; 170 } 171 172 173 static unsigned int 174 testInternalPost (void) 175 { 176 struct MHD_Daemon *d; 177 CURL *c; 178 char buf[2048]; 179 struct CBC cbc; 180 CURLcode errornum; 181 uint16_t port; 182 183 if (MHD_NO != MHD_is_feature_supported (MHD_FEATURE_AUTODETECT_BIND_PORT)) 184 port = 0; 185 else 186 { 187 port = 1370; 188 if (oneone) 189 port += 10; 190 } 191 192 cbc.buf = buf; 193 cbc.size = 2048; 194 cbc.pos = 0; 195 d = MHD_start_daemon (MHD_USE_INTERNAL_POLLING_THREAD | MHD_USE_ERROR_LOG, 196 port, NULL, NULL, &ahc_echo, NULL, 197 MHD_OPTION_NOTIFY_COMPLETED, &completed_cb, NULL, 198 MHD_OPTION_END); 199 if (d == NULL) 200 return 1; 201 if (0 == port) 202 { 203 const union MHD_DaemonInfo *dinfo; 204 dinfo = MHD_get_daemon_info (d, MHD_DAEMON_INFO_BIND_PORT); 205 if ((NULL == dinfo) || (0 == dinfo->port) ) 206 { 207 MHD_stop_daemon (d); return 32; 208 } 209 port = dinfo->port; 210 } 211 c = curl_easy_init (); 212 curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1/hello_world"); 213 curl_easy_setopt (c, CURLOPT_PORT, (long) port); 214 curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, ©Buffer); 215 curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc); 216 curl_easy_setopt (c, CURLOPT_POSTFIELDS, POST_DATA); 217 curl_easy_setopt (c, CURLOPT_POSTFIELDSIZE, strlen (POST_DATA)); 218 curl_easy_setopt (c, CURLOPT_POST, 1L); 219 curl_easy_setopt (c, CURLOPT_FAILONERROR, 1L); 220 curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L); 221 if (oneone) 222 curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1); 223 else 224 curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0); 225 curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L); 226 /* NOTE: use of CONNECTTIMEOUT without also 227 * setting NOSIGNAL results in really weird 228 * crashes on my system! */ 229 curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1L); 230 if (CURLE_OK != (errornum = curl_easy_perform (c))) 231 { 232 fprintf (stderr, 233 "curl_easy_perform failed: `%s'\n", 234 curl_easy_strerror (errornum)); 235 curl_easy_cleanup (c); 236 MHD_stop_daemon (d); 237 return 2; 238 } 239 curl_easy_cleanup (c); 240 MHD_stop_daemon (d); 241 if (cbc.pos != strlen ("/hello_world")) 242 return 4; 243 if (0 != strncmp ("/hello_world", cbc.buf, strlen ("/hello_world"))) 244 return 8; 245 return 0; 246 } 247 248 249 static unsigned int 250 testMultithreadedPost (void) 251 { 252 struct MHD_Daemon *d; 253 CURL *c; 254 char buf[2048]; 255 struct CBC cbc; 256 CURLcode errornum; 257 uint16_t port; 258 259 if (MHD_NO != MHD_is_feature_supported (MHD_FEATURE_AUTODETECT_BIND_PORT)) 260 port = 0; 261 else 262 { 263 port = 1371; 264 if (oneone) 265 port += 10; 266 } 267 268 cbc.buf = buf; 269 cbc.size = 2048; 270 cbc.pos = 0; 271 d = MHD_start_daemon (MHD_USE_THREAD_PER_CONNECTION 272 | MHD_USE_INTERNAL_POLLING_THREAD | MHD_USE_ERROR_LOG, 273 port, NULL, NULL, &ahc_echo, NULL, 274 MHD_OPTION_NOTIFY_COMPLETED, &completed_cb, NULL, 275 MHD_OPTION_END); 276 if (d == NULL) 277 return 16; 278 if (0 == port) 279 { 280 const union MHD_DaemonInfo *dinfo; 281 dinfo = MHD_get_daemon_info (d, MHD_DAEMON_INFO_BIND_PORT); 282 if ((NULL == dinfo) || (0 == dinfo->port) ) 283 { 284 MHD_stop_daemon (d); return 32; 285 } 286 port = dinfo->port; 287 } 288 c = curl_easy_init (); 289 curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1/hello_world"); 290 curl_easy_setopt (c, CURLOPT_PORT, (long) port); 291 curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, ©Buffer); 292 curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc); 293 curl_easy_setopt (c, CURLOPT_POSTFIELDS, POST_DATA); 294 curl_easy_setopt (c, CURLOPT_POSTFIELDSIZE, strlen (POST_DATA)); 295 curl_easy_setopt (c, CURLOPT_POST, 1L); 296 curl_easy_setopt (c, CURLOPT_FAILONERROR, 1L); 297 curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L); 298 if (oneone) 299 curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1); 300 else 301 curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0); 302 curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L); 303 /* NOTE: use of CONNECTTIMEOUT without also 304 * setting NOSIGNAL results in really weird 305 * crashes on my system! */ 306 curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1L); 307 if (CURLE_OK != (errornum = curl_easy_perform (c))) 308 { 309 fprintf (stderr, 310 "curl_easy_perform failed: `%s'\n", 311 curl_easy_strerror (errornum)); 312 curl_easy_cleanup (c); 313 MHD_stop_daemon (d); 314 return 32; 315 } 316 curl_easy_cleanup (c); 317 MHD_stop_daemon (d); 318 if (cbc.pos != strlen ("/hello_world")) 319 return 64; 320 if (0 != strncmp ("/hello_world", cbc.buf, strlen ("/hello_world"))) 321 return 128; 322 return 0; 323 } 324 325 326 static unsigned int 327 testMultithreadedPoolPost (void) 328 { 329 struct MHD_Daemon *d; 330 CURL *c; 331 char buf[2048]; 332 struct CBC cbc; 333 CURLcode errornum; 334 uint16_t port; 335 336 if (MHD_NO != MHD_is_feature_supported (MHD_FEATURE_AUTODETECT_BIND_PORT)) 337 port = 0; 338 else 339 { 340 port = 1372; 341 if (oneone) 342 port += 10; 343 } 344 345 cbc.buf = buf; 346 cbc.size = 2048; 347 cbc.pos = 0; 348 d = MHD_start_daemon (MHD_USE_INTERNAL_POLLING_THREAD | MHD_USE_ERROR_LOG, 349 port, NULL, NULL, &ahc_echo, NULL, 350 MHD_OPTION_THREAD_POOL_SIZE, MHD_CPU_COUNT, 351 MHD_OPTION_NOTIFY_COMPLETED, &completed_cb, NULL, 352 MHD_OPTION_END); 353 if (d == NULL) 354 return 16; 355 if (0 == port) 356 { 357 const union MHD_DaemonInfo *dinfo; 358 dinfo = MHD_get_daemon_info (d, MHD_DAEMON_INFO_BIND_PORT); 359 if ((NULL == dinfo) || (0 == dinfo->port) ) 360 { 361 MHD_stop_daemon (d); return 32; 362 } 363 port = dinfo->port; 364 } 365 c = curl_easy_init (); 366 curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1/hello_world"); 367 curl_easy_setopt (c, CURLOPT_PORT, (long) port); 368 curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, ©Buffer); 369 curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc); 370 curl_easy_setopt (c, CURLOPT_POSTFIELDS, POST_DATA); 371 curl_easy_setopt (c, CURLOPT_POSTFIELDSIZE, strlen (POST_DATA)); 372 curl_easy_setopt (c, CURLOPT_POST, 1L); 373 curl_easy_setopt (c, CURLOPT_FAILONERROR, 1L); 374 curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L); 375 if (oneone) 376 curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1); 377 else 378 curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0); 379 curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L); 380 /* NOTE: use of CONNECTTIMEOUT without also 381 * setting NOSIGNAL results in really weird 382 * crashes on my system! */ 383 curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1L); 384 if (CURLE_OK != (errornum = curl_easy_perform (c))) 385 { 386 fprintf (stderr, 387 "curl_easy_perform failed: `%s'\n", 388 curl_easy_strerror (errornum)); 389 curl_easy_cleanup (c); 390 MHD_stop_daemon (d); 391 return 32; 392 } 393 curl_easy_cleanup (c); 394 MHD_stop_daemon (d); 395 if (cbc.pos != strlen ("/hello_world")) 396 return 64; 397 if (0 != strncmp ("/hello_world", cbc.buf, strlen ("/hello_world"))) 398 return 128; 399 return 0; 400 } 401 402 403 static unsigned int 404 testExternalPost (void) 405 { 406 struct MHD_Daemon *d; 407 CURL *c; 408 char buf[2048]; 409 struct CBC cbc; 410 CURLM *multi; 411 CURLMcode mret; 412 fd_set rs; 413 fd_set ws; 414 fd_set es; 415 MHD_socket maxsock; 416 #ifdef MHD_WINSOCK_SOCKETS 417 int maxposixs; /* Max socket number unused on W32 */ 418 #else /* MHD_POSIX_SOCKETS */ 419 #define maxposixs maxsock 420 #endif /* MHD_POSIX_SOCKETS */ 421 int running; 422 struct CURLMsg *msg; 423 time_t start; 424 struct timeval tv; 425 uint16_t port; 426 427 if (MHD_NO != MHD_is_feature_supported (MHD_FEATURE_AUTODETECT_BIND_PORT)) 428 port = 0; 429 else 430 { 431 port = 1373; 432 if (oneone) 433 port += 10; 434 } 435 436 multi = NULL; 437 cbc.buf = buf; 438 cbc.size = 2048; 439 cbc.pos = 0; 440 d = MHD_start_daemon (MHD_USE_ERROR_LOG | MHD_USE_NO_THREAD_SAFETY, 441 port, NULL, NULL, &ahc_echo, NULL, 442 MHD_OPTION_NOTIFY_COMPLETED, &completed_cb, NULL, 443 MHD_OPTION_APP_FD_SETSIZE, (int) FD_SETSIZE, 444 MHD_OPTION_END); 445 if (d == NULL) 446 return 256; 447 if (0 == port) 448 { 449 const union MHD_DaemonInfo *dinfo; 450 dinfo = MHD_get_daemon_info (d, MHD_DAEMON_INFO_BIND_PORT); 451 if ((NULL == dinfo) || (0 == dinfo->port) ) 452 { 453 MHD_stop_daemon (d); return 32; 454 } 455 port = dinfo->port; 456 } 457 c = curl_easy_init (); 458 curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1/hello_world"); 459 curl_easy_setopt (c, CURLOPT_PORT, (long) port); 460 curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, ©Buffer); 461 curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc); 462 curl_easy_setopt (c, CURLOPT_POSTFIELDS, POST_DATA); 463 curl_easy_setopt (c, CURLOPT_POSTFIELDSIZE, strlen (POST_DATA)); 464 curl_easy_setopt (c, CURLOPT_POST, 1L); 465 curl_easy_setopt (c, CURLOPT_FAILONERROR, 1L); 466 curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L); 467 if (oneone) 468 curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1); 469 else 470 curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0); 471 curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L); 472 /* NOTE: use of CONNECTTIMEOUT without also 473 * setting NOSIGNAL results in really weird 474 * crashes on my system! */ 475 curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1L); 476 477 478 multi = curl_multi_init (); 479 if (multi == NULL) 480 { 481 curl_easy_cleanup (c); 482 MHD_stop_daemon (d); 483 return 512; 484 } 485 mret = curl_multi_add_handle (multi, c); 486 if (mret != CURLM_OK) 487 { 488 curl_multi_cleanup (multi); 489 curl_easy_cleanup (c); 490 MHD_stop_daemon (d); 491 return 1024; 492 } 493 start = time (NULL); 494 while ((time (NULL) - start < 5) && (multi != NULL)) 495 { 496 maxsock = MHD_INVALID_SOCKET; 497 maxposixs = -1; 498 FD_ZERO (&rs); 499 FD_ZERO (&ws); 500 FD_ZERO (&es); 501 curl_multi_perform (multi, &running); 502 mret = curl_multi_fdset (multi, &rs, &ws, &es, &maxposixs); 503 if (mret != CURLM_OK) 504 { 505 curl_multi_remove_handle (multi, c); 506 curl_multi_cleanup (multi); 507 curl_easy_cleanup (c); 508 MHD_stop_daemon (d); 509 return 2048; 510 } 511 if (MHD_YES != MHD_get_fdset (d, &rs, &ws, &es, &maxsock)) 512 { 513 curl_multi_remove_handle (multi, c); 514 curl_multi_cleanup (multi); 515 curl_easy_cleanup (c); 516 MHD_stop_daemon (d); 517 return 4096; 518 } 519 tv.tv_sec = 0; 520 tv.tv_usec = 1000; 521 if (-1 == select (maxposixs + 1, &rs, &ws, &es, &tv)) 522 { 523 #ifdef MHD_POSIX_SOCKETS 524 if (EINTR != errno) 525 { 526 fprintf (stderr, "Unexpected select() error: %d. Line: %d\n", 527 (int) errno, __LINE__); 528 fflush (stderr); 529 exit (99); 530 } 531 #else 532 if ((WSAEINVAL != WSAGetLastError ()) || 533 (0 != rs.fd_count) || (0 != ws.fd_count) || (0 != es.fd_count) ) 534 { 535 fprintf (stderr, "Unexpected select() error: %d. Line: %d\n", 536 (int) WSAGetLastError (), __LINE__); 537 fflush (stderr); 538 exit (99); 539 } 540 Sleep (1); 541 #endif 542 } 543 curl_multi_perform (multi, &running); 544 if (0 == running) 545 { 546 int pending; 547 int curl_fine = 0; 548 while (NULL != (msg = curl_multi_info_read (multi, &pending))) 549 { 550 if (msg->msg == CURLMSG_DONE) 551 { 552 if (msg->data.result == CURLE_OK) 553 curl_fine = 1; 554 else 555 { 556 fprintf (stderr, 557 "%s failed at %s:%d: `%s'\n", 558 "curl_multi_perform", 559 __FILE__, 560 __LINE__, curl_easy_strerror (msg->data.result)); 561 abort (); 562 } 563 } 564 } 565 if (! curl_fine) 566 { 567 fprintf (stderr, "libcurl haven't returned OK code\n"); 568 abort (); 569 } 570 curl_multi_remove_handle (multi, c); 571 curl_multi_cleanup (multi); 572 curl_easy_cleanup (c); 573 c = NULL; 574 multi = NULL; 575 } 576 MHD_run (d); 577 } 578 if (multi != NULL) 579 { 580 curl_multi_remove_handle (multi, c); 581 curl_easy_cleanup (c); 582 curl_multi_cleanup (multi); 583 } 584 MHD_stop_daemon (d); 585 if (cbc.pos != strlen ("/hello_world")) 586 return 8192; 587 if (0 != strncmp ("/hello_world", cbc.buf, strlen ("/hello_world"))) 588 return 16384; 589 return 0; 590 } 591 592 593 static enum MHD_Result 594 ahc_cancel (void *cls, 595 struct MHD_Connection *connection, 596 const char *url, 597 const char *method, 598 const char *version, 599 const char *upload_data, size_t *upload_data_size, 600 void **req_cls) 601 { 602 struct MHD_Response *response; 603 enum MHD_Result ret; 604 (void) cls; (void) url; (void) version; /* Unused. Silent compiler warning. */ 605 (void) upload_data; (void) upload_data_size; /* Unused. Silent compiler warning. */ 606 607 if (0 != strcmp ("POST", method)) 608 { 609 fprintf (stderr, 610 "Unexpected method `%s'\n", method); 611 return MHD_NO; 612 } 613 614 if (*req_cls == NULL) 615 { 616 static int marker = 1; 617 *req_cls = ▮ 618 /* We don't want the body. Send a 500. */ 619 response = MHD_create_response_empty (MHD_RF_NONE); 620 ret = MHD_queue_response (connection, 500, response); 621 if (ret != MHD_YES) 622 fprintf (stderr, "Failed to queue response\n"); 623 MHD_destroy_response (response); 624 return ret; 625 } 626 else 627 { 628 fprintf (stderr, 629 "In ahc_cancel again. This should not happen.\n"); 630 return MHD_NO; 631 } 632 } 633 634 635 struct CRBC 636 { 637 const char *buffer; 638 size_t size; 639 size_t pos; 640 }; 641 642 643 static size_t 644 readBuffer (void *p, size_t size, size_t nmemb, void *opaque) 645 { 646 struct CRBC *data = opaque; 647 size_t required = size * nmemb; 648 size_t left = data->size - data->pos; 649 650 if (required > left) 651 required = left; 652 653 memcpy (p, data->buffer + data->pos, required); 654 data->pos += required; 655 656 return required / size; 657 } 658 659 660 static size_t 661 slowReadBuffer (void *p, size_t size, size_t nmemb, void *opaque) 662 { 663 (void) sleep (1); 664 return readBuffer (p, size, nmemb, opaque); 665 } 666 667 668 #define FLAG_EXPECT_CONTINUE 1 669 #define FLAG_CHUNKED 2 670 #define FLAG_FORM_DATA 4 671 #define FLAG_SLOW_READ 8 672 #define FLAG_COUNT 16 673 674 675 static unsigned int 676 testMultithreadedPostCancelPart (int flags) 677 { 678 struct MHD_Daemon *d; 679 CURL *c; 680 char buf[2048]; 681 struct CBC cbc; 682 CURLcode errornum; 683 struct curl_slist *headers = NULL; 684 long response_code; 685 CURLcode cc; 686 unsigned int result = 0; 687 struct CRBC crbc; 688 uint16_t port; 689 690 if (MHD_NO != MHD_is_feature_supported (MHD_FEATURE_AUTODETECT_BIND_PORT)) 691 port = 0; 692 else 693 { 694 port = 1374; 695 if (oneone) 696 port += 10; 697 } 698 699 /* Don't test features that aren't available with HTTP/1.0 in 700 * HTTP/1.0 mode. */ 701 if (! oneone && (flags & (FLAG_EXPECT_CONTINUE | FLAG_CHUNKED))) 702 return 0; 703 704 cbc.buf = buf; 705 cbc.size = 2048; 706 cbc.pos = 0; 707 d = MHD_start_daemon (MHD_USE_THREAD_PER_CONNECTION 708 | MHD_USE_INTERNAL_POLLING_THREAD | MHD_USE_ERROR_LOG, 709 port, NULL, NULL, &ahc_cancel, NULL, 710 MHD_OPTION_END); 711 if (d == NULL) 712 return 32768; 713 if (0 == port) 714 { 715 const union MHD_DaemonInfo *dinfo; 716 dinfo = MHD_get_daemon_info (d, MHD_DAEMON_INFO_BIND_PORT); 717 if ((NULL == dinfo) || (0 == dinfo->port) ) 718 { 719 MHD_stop_daemon (d); return 32; 720 } 721 port = dinfo->port; 722 } 723 724 crbc.buffer = "Test content"; 725 crbc.size = strlen (crbc.buffer); 726 crbc.pos = 0; 727 728 c = curl_easy_init (); 729 curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1/hello_world"); 730 curl_easy_setopt (c, CURLOPT_PORT, (long) port); 731 curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, ©Buffer); 732 curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc); 733 curl_easy_setopt (c, CURLOPT_READFUNCTION, (flags & FLAG_SLOW_READ) ? 734 &slowReadBuffer : &readBuffer); 735 curl_easy_setopt (c, CURLOPT_READDATA, &crbc); 736 curl_easy_setopt (c, CURLOPT_POSTFIELDS, NULL); 737 curl_easy_setopt (c, CURLOPT_POSTFIELDSIZE, crbc.size); 738 curl_easy_setopt (c, CURLOPT_POST, 1L); 739 curl_easy_setopt (c, CURLOPT_FAILONERROR, 1L); 740 curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L); 741 if (oneone) 742 curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1); 743 else 744 curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0); 745 curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L); 746 /* NOTE: use of CONNECTTIMEOUT without also 747 * setting NOSIGNAL results in really weird 748 * crashes on my system! */ 749 curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1L); 750 751 if (flags & FLAG_CHUNKED) 752 headers = curl_slist_append (headers, "Transfer-Encoding: chunked"); 753 if (! (flags & FLAG_FORM_DATA)) 754 headers = curl_slist_append (headers, 755 "Content-Type: application/octet-stream"); 756 if (flags & FLAG_EXPECT_CONTINUE) 757 headers = curl_slist_append (headers, "Expect: 100-Continue"); 758 curl_easy_setopt (c, CURLOPT_HTTPHEADER, headers); 759 760 if (CURLE_HTTP_RETURNED_ERROR != (errornum = curl_easy_perform (c))) 761 { 762 #ifdef _WIN32 763 curl_version_info_data *curlverd = curl_version_info (CURLVERSION_NOW); 764 if ((0 != (flags & FLAG_SLOW_READ)) && (CURLE_RECV_ERROR == errornum) && 765 ((curlverd == NULL) || (curlverd->ares_num < 0x073100) ) ) 766 { /* libcurl up to version 7.49.0 didn't have workaround for WinSock bug */ 767 fprintf (stderr, 768 "Ignored curl_easy_perform expected failure on W32 with \"slow read\".\n"); 769 result = 0; 770 } 771 else 772 #else /* ! _WIN32 */ 773 if (1) 774 #endif /* ! _WIN32 */ 775 { 776 fprintf (stderr, 777 "flibbet curl_easy_perform didn't fail as expected: `%s' %u\n", 778 curl_easy_strerror (errornum), (unsigned int) errornum); 779 result = 65536; 780 } 781 curl_easy_cleanup (c); 782 MHD_stop_daemon (d); 783 curl_slist_free_all (headers); 784 return result; 785 } 786 787 if (CURLE_OK != (cc = curl_easy_getinfo (c, CURLINFO_RESPONSE_CODE, 788 &response_code))) 789 { 790 fprintf (stderr, "curl_easy_getinfo failed: '%s'\n", 791 curl_easy_strerror (cc)); 792 result = 65536; 793 } 794 795 if (! result && (response_code != 500)) 796 { 797 fprintf (stderr, "Unexpected response code: %ld\n", response_code); 798 result = 131072; 799 } 800 801 if (! result && (cbc.pos != 0)) 802 result = 262144; 803 804 curl_easy_cleanup (c); 805 MHD_stop_daemon (d); 806 curl_slist_free_all (headers); 807 return result; 808 } 809 810 811 static unsigned int 812 testMultithreadedPostCancel (void) 813 { 814 unsigned int result = 0; 815 int flags; 816 for (flags = 0; flags < FLAG_COUNT; ++flags) 817 result |= testMultithreadedPostCancelPart (flags); 818 return result; 819 } 820 821 822 int 823 main (int argc, char *const *argv) 824 { 825 unsigned int errorCount = 0; 826 (void) argc; /* Unused. Silent compiler warning. */ 827 828 if ((NULL == argv) || (0 == argv[0])) 829 return 99; 830 oneone = has_in_name (argv[0], "11"); 831 if (0 != curl_global_init (CURL_GLOBAL_WIN32)) 832 return 2; 833 if (MHD_YES == MHD_is_feature_supported (MHD_FEATURE_THREADS)) 834 { 835 errorCount += testMultithreadedPostCancel (); 836 errorCount += testInternalPost (); 837 errorCount += testMultithreadedPost (); 838 errorCount += testMultithreadedPoolPost (); 839 } 840 errorCount += testExternalPost (); 841 if (errorCount != 0) 842 fprintf (stderr, "Error (code: %u)\n", errorCount); 843 curl_global_cleanup (); 844 return (0 == errorCount) ? 0 : 1; /* 0 == pass */ 845 }