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