test_large_put.c (28236B)
1 /* 2 This file is part of libmicrohttpd 3 Copyright (C) 2007, 2008 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_large_put.c 24 * @brief Testcase for libmicrohttpd PUT operations 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 #include "mhd_has_param.h" 44 45 /* Turn any MHD_PANIC() or failing mhd_assert() reached from this 46 test into a marked, classifiable test error (TESTING.md, P5). */ 47 #include "mhd_panic_tripwire.h" 48 49 #if defined(MHD_CPU_COUNT) && (MHD_CPU_COUNT + 0) < 2 50 #undef MHD_CPU_COUNT 51 #endif 52 #if ! defined(MHD_CPU_COUNT) 53 #define MHD_CPU_COUNT 2 54 #endif 55 56 57 #if defined(HAVE___FUNC__) 58 #define externalErrorExit(ignore) \ 59 _externalErrorExit_func(NULL, __func__, __LINE__) 60 #define externalErrorExitDesc(errDesc) \ 61 _externalErrorExit_func(errDesc, __func__, __LINE__) 62 #define libcurlErrorExit(ignore) \ 63 _libcurlErrorExit_func(NULL, __func__, __LINE__) 64 #define libcurlErrorExitDesc(errDesc) \ 65 _libcurlErrorExit_func(errDesc, __func__, __LINE__) 66 #define mhdErrorExit(ignore) \ 67 _mhdErrorExit_func(NULL, __func__, __LINE__) 68 #define mhdErrorExitDesc(errDesc) \ 69 _mhdErrorExit_func(errDesc, __func__, __LINE__) 70 #elif defined(HAVE___FUNCTION__) 71 #define externalErrorExit(ignore) \ 72 _externalErrorExit_func(NULL, __FUNCTION__, __LINE__) 73 #define externalErrorExitDesc(errDesc) \ 74 _externalErrorExit_func(errDesc, __FUNCTION__, __LINE__) 75 #define libcurlErrorExit(ignore) \ 76 _libcurlErrorExit_func(NULL, __FUNCTION__, __LINE__) 77 #define libcurlErrorExitDesc(errDesc) \ 78 _libcurlErrorExit_func(errDesc, __FUNCTION__, __LINE__) 79 #define mhdErrorExit(ignore) \ 80 _mhdErrorExit_func(NULL, __FUNCTION__, __LINE__) 81 #define mhdErrorExitDesc(errDesc) \ 82 _mhdErrorExit_func(errDesc, __FUNCTION__, __LINE__) 83 #else 84 #define externalErrorExit(ignore) _externalErrorExit_func(NULL, NULL, __LINE__) 85 #define externalErrorExitDesc(errDesc) \ 86 _externalErrorExit_func(errDesc, NULL, __LINE__) 87 #define libcurlErrorExit(ignore) _libcurlErrorExit_func(NULL, NULL, __LINE__) 88 #define libcurlErrorExitDesc(errDesc) \ 89 _libcurlErrorExit_func(errDesc, NULL, __LINE__) 90 #define mhdErrorExit(ignore) _mhdErrorExit_func(NULL, NULL, __LINE__) 91 #define mhdErrorExitDesc(errDesc) _mhdErrorExit_func(errDesc, NULL, __LINE__) 92 #endif 93 94 95 _MHD_NORETURN static void 96 _externalErrorExit_func (const char *errDesc, const char *funcName, int lineNum) 97 { 98 if ((NULL != errDesc) && (0 != errDesc[0])) 99 fprintf (stderr, "%s", errDesc); 100 else 101 fprintf (stderr, "System or external library call failed"); 102 if ((NULL != funcName) && (0 != funcName[0])) 103 fprintf (stderr, " in %s", funcName); 104 if (0 < lineNum) 105 fprintf (stderr, " at line %d", lineNum); 106 107 fprintf (stderr, ".\nLast errno value: %d (%s)\n", (int) errno, 108 strerror (errno)); 109 #ifdef MHD_WINSOCK_SOCKETS 110 fprintf (stderr, "WSAGetLastError() value: %d\n", (int) WSAGetLastError ()); 111 #endif /* MHD_WINSOCK_SOCKETS */ 112 fflush (stderr); 113 exit (99); 114 } 115 116 117 static char libcurl_errbuf[CURL_ERROR_SIZE] = ""; 118 119 _MHD_NORETURN static void 120 _libcurlErrorExit_func (const char *errDesc, const char *funcName, int lineNum) 121 { 122 if ((NULL != errDesc) && (0 != errDesc[0])) 123 fprintf (stderr, "%s", errDesc); 124 else 125 fprintf (stderr, "CURL 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 if (0 != libcurl_errbuf[0]) 134 fprintf (stderr, "Last libcurl error details: %s\n", libcurl_errbuf); 135 136 fflush (stderr); 137 exit (99); 138 } 139 140 141 _MHD_NORETURN static void 142 _mhdErrorExit_func (const char *errDesc, const char *funcName, int lineNum) 143 { 144 if ((NULL != errDesc) && (0 != errDesc[0])) 145 fprintf (stderr, "%s", errDesc); 146 else 147 fprintf (stderr, "MHD unexpected error"); 148 if ((NULL != funcName) && (0 != funcName[0])) 149 fprintf (stderr, " in %s", funcName); 150 if (0 < lineNum) 151 fprintf (stderr, " at line %d", lineNum); 152 153 fprintf (stderr, ".\nLast errno value: %d (%s)\n", (int) errno, 154 strerror (errno)); 155 156 fflush (stderr); 157 exit (8); 158 } 159 160 161 static int oneone; 162 static int incr_read; /* Use incremental read */ 163 static int verbose; /* Be verbose */ 164 165 #define PUT_SIZE (256 * 1024) 166 167 static char *put_buffer; 168 169 struct CBC 170 { 171 char *buf; 172 size_t pos; 173 size_t size; 174 }; 175 176 static char * 177 alloc_init (size_t buf_size) 178 { 179 static const char template[] = 180 "ABCDEFGHIJKLMNOPQRSTUVWXYZ[]^_`abcdefghijklmnopqrstuvwxyz"; 181 static const size_t templ_size = sizeof(template) / sizeof(char) - 1; 182 char *buf; 183 char *fill_ptr; 184 size_t to_fill; 185 186 buf = malloc (buf_size); 187 if (NULL == buf) 188 externalErrorExit (); 189 190 fill_ptr = buf; 191 to_fill = buf_size; 192 while (to_fill > 0) 193 { 194 const size_t to_copy = to_fill > templ_size ? templ_size : to_fill; 195 memcpy (fill_ptr, template, to_copy); 196 fill_ptr += to_copy; 197 to_fill -= to_copy; 198 } 199 return buf; 200 } 201 202 203 static size_t 204 putBuffer (void *stream, size_t size, size_t nmemb, void *ptr) 205 { 206 size_t *pos = (size_t *) ptr; 207 size_t wrt; 208 209 wrt = size * nmemb; 210 /* Check for overflow. */ 211 if (wrt / size != nmemb) 212 libcurlErrorExitDesc ("Too large buffer size"); 213 if (wrt > PUT_SIZE - (*pos)) 214 wrt = PUT_SIZE - (*pos); 215 memcpy (stream, &put_buffer[*pos], wrt); 216 (*pos) += wrt; 217 return wrt; 218 } 219 220 221 static size_t 222 copyBuffer (void *ptr, size_t size, size_t nmemb, void *ctx) 223 { 224 struct CBC *cbc = ctx; 225 226 if (cbc->pos + size * nmemb > cbc->size) 227 libcurlErrorExitDesc ("Too large buffer size"); 228 memcpy (&cbc->buf[cbc->pos], ptr, size * nmemb); 229 cbc->pos += size * nmemb; 230 return size * nmemb; 231 } 232 233 234 static enum MHD_Result 235 ahc_echo (void *cls, 236 struct MHD_Connection *connection, 237 const char *url, 238 const char *method, 239 const char *version, 240 const char *upload_data, size_t *upload_data_size, 241 void **req_cls) 242 { 243 int *done = cls; 244 struct MHD_Response *response; 245 enum MHD_Result ret; 246 static size_t processed; 247 248 if (NULL == cls) 249 mhdErrorExitDesc ("cls parameter is NULL"); 250 251 if (0 != strcmp (version, oneone ? 252 MHD_HTTP_VERSION_1_1 : MHD_HTTP_VERSION_1_0)) 253 mhdErrorExitDesc ("Unexpected HTTP version"); 254 255 if (NULL == url) 256 mhdErrorExitDesc ("url parameter is NULL"); 257 258 if (NULL == upload_data_size) 259 mhdErrorExitDesc ("'upload_data_size' pointer is NULL"); 260 261 if (0 != strcmp ("PUT", method)) 262 mhdErrorExitDesc ("Unexpected request method"); /* unexpected method */ 263 264 if ((*done) == 0) 265 { 266 size_t *pproc; 267 if (NULL == *req_cls) 268 { 269 processed = 0; 270 /* Safe as long as only one parallel request served. */ 271 *req_cls = &processed; 272 } 273 pproc = (size_t *) *req_cls; 274 275 if (0 == *upload_data_size) 276 return MHD_YES; /* No data to process. */ 277 278 if (*pproc + *upload_data_size > PUT_SIZE) 279 mhdErrorExitDesc ("Incoming data larger than expected"); 280 281 if ( (! incr_read) && (*upload_data_size != PUT_SIZE) ) 282 return MHD_YES; /* Wait until whole request is received. */ 283 284 if (0 != memcmp (upload_data, put_buffer + (*pproc), *upload_data_size)) 285 mhdErrorExitDesc ("Incoming data does not match sent data"); 286 287 *pproc += *upload_data_size; 288 *upload_data_size = 0; /* Current block of data is fully processed. */ 289 290 if (PUT_SIZE == *pproc) 291 *done = 1; /* Whole request is processed. */ 292 return MHD_YES; 293 } 294 response = 295 MHD_create_response_from_buffer_copy (strlen (url), 296 (const void *) url); 297 if (NULL == response) 298 mhdErrorExitDesc ("Failed to create response"); 299 ret = MHD_queue_response (connection, MHD_HTTP_OK, response); 300 MHD_destroy_response (response); 301 return ret; 302 } 303 304 305 static unsigned int 306 testPutInternalThread (unsigned int add_flag) 307 { 308 struct MHD_Daemon *d; 309 CURL *c; 310 struct CBC cbc; 311 size_t pos = 0; 312 int done_flag = 0; 313 CURLcode errornum; 314 char buf[2048]; 315 uint16_t port; 316 317 if (MHD_NO != MHD_is_feature_supported (MHD_FEATURE_AUTODETECT_BIND_PORT)) 318 port = 0; 319 else 320 { 321 port = 1270; 322 if (oneone) 323 port += 10; 324 if (incr_read) 325 port += 20; 326 } 327 328 cbc.buf = buf; 329 cbc.size = 2048; 330 cbc.pos = 0; 331 d = MHD_start_daemon (MHD_USE_INTERNAL_POLLING_THREAD | MHD_USE_ERROR_LOG 332 | add_flag, 333 port, 334 NULL, NULL, &ahc_echo, &done_flag, 335 MHD_OPTION_CONNECTION_MEMORY_LIMIT, 336 (size_t) (incr_read ? 1024 : (PUT_SIZE * 4 / 3)), 337 MHD_OPTION_END); 338 if (d == NULL) 339 mhdErrorExit (); 340 if (0 == port) 341 { 342 const union MHD_DaemonInfo *dinfo; 343 dinfo = MHD_get_daemon_info (d, MHD_DAEMON_INFO_BIND_PORT); 344 if ((NULL == dinfo) || (0 == dinfo->port) ) 345 mhdErrorExit (); 346 port = dinfo->port; 347 } 348 349 c = curl_easy_init (); 350 if (NULL == c) 351 { 352 fprintf (stderr, "curl_easy_init() failed.\n"); 353 externalErrorExit (); 354 } 355 if ((CURLE_OK != curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1L)) || 356 (CURLE_OK != curl_easy_setopt (c, CURLOPT_URL, 357 "http://127.0.0.1/hello_world")) || 358 (CURLE_OK != curl_easy_setopt (c, CURLOPT_PORT, (long) port)) || 359 (CURLE_OK != curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, 360 ©Buffer)) || 361 (CURLE_OK != curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc)) || 362 (CURLE_OK != curl_easy_setopt (c, CURLOPT_READFUNCTION, 363 &putBuffer)) || 364 (CURLE_OK != curl_easy_setopt (c, CURLOPT_READDATA, &pos)) || 365 (CURLE_OK != curl_easy_setopt (c, CURLOPT_UPLOAD, 1L)) || 366 (CURLE_OK != curl_easy_setopt (c, CURLOPT_INFILESIZE, (long) PUT_SIZE)) || 367 (CURLE_OK != curl_easy_setopt (c, CURLOPT_FAILONERROR, 1L)) || 368 (CURLE_OK != curl_easy_setopt (c, CURLOPT_ERRORBUFFER, libcurl_errbuf)) || 369 (CURLE_OK != curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 370 (long) 150)) || 371 (CURLE_OK != curl_easy_setopt (c, CURLOPT_TIMEOUT, 372 (long) 150)) || 373 (CURLE_OK != curl_easy_setopt (c, CURLOPT_HTTP_VERSION, 374 (oneone) ? 375 CURL_HTTP_VERSION_1_1 : 376 CURL_HTTP_VERSION_1_0))) 377 378 { 379 fprintf (stderr, "curl_easy_setopt() failed.\n"); 380 externalErrorExit (); 381 } 382 383 if (CURLE_OK != (errornum = curl_easy_perform (c))) 384 { 385 fprintf (stderr, 386 "curl_easy_perform failed: `%s'\n", 387 curl_easy_strerror (errornum)); 388 curl_easy_cleanup (c); 389 MHD_stop_daemon (d); 390 return 2; 391 } 392 curl_easy_cleanup (c); 393 MHD_stop_daemon (d); 394 if (cbc.pos != strlen ("/hello_world")) 395 { 396 fprintf (stderr, "Got %u bytes ('%.*s'), expected %u bytes. ", 397 (unsigned) cbc.pos, (int) cbc.pos, cbc.buf, 398 (unsigned) strlen ("/hello_world")); 399 mhdErrorExitDesc ("Wrong returned data length"); 400 } 401 if (0 != strncmp ("/hello_world", cbc.buf, strlen ("/hello_world"))) 402 { 403 fprintf (stderr, "Got invalid response '%.*s'. ", (int) cbc.pos, cbc.buf); 404 mhdErrorExitDesc ("Wrong returned data length"); 405 } 406 return 0; 407 } 408 409 410 static unsigned int 411 testPutThreadPerConn (unsigned int add_flag) 412 { 413 struct MHD_Daemon *d; 414 CURL *c; 415 struct CBC cbc; 416 size_t pos = 0; 417 int done_flag = 0; 418 CURLcode errornum; 419 char buf[2048]; 420 uint16_t port; 421 422 if (MHD_NO != MHD_is_feature_supported (MHD_FEATURE_AUTODETECT_BIND_PORT)) 423 port = 0; 424 else 425 { 426 port = 1271; 427 if (oneone) 428 port += 10; 429 if (incr_read) 430 port += 20; 431 } 432 433 cbc.buf = buf; 434 cbc.size = 2048; 435 cbc.pos = 0; 436 d = MHD_start_daemon (MHD_USE_THREAD_PER_CONNECTION 437 | MHD_USE_INTERNAL_POLLING_THREAD 438 | MHD_USE_ERROR_LOG | add_flag, 439 port, 440 NULL, NULL, &ahc_echo, &done_flag, 441 MHD_OPTION_CONNECTION_MEMORY_LIMIT, 442 (size_t) (incr_read ? 1024 : (PUT_SIZE * 4)), 443 MHD_OPTION_END); 444 if (d == NULL) 445 mhdErrorExit (); 446 if (0 == port) 447 { 448 const union MHD_DaemonInfo *dinfo; 449 dinfo = MHD_get_daemon_info (d, MHD_DAEMON_INFO_BIND_PORT); 450 if ((NULL == dinfo) || (0 == dinfo->port) ) 451 mhdErrorExit (); 452 port = dinfo->port; 453 } 454 455 c = curl_easy_init (); 456 if (NULL == c) 457 { 458 fprintf (stderr, "curl_easy_init() failed.\n"); 459 externalErrorExit (); 460 } 461 if ((CURLE_OK != curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1L)) || 462 (CURLE_OK != curl_easy_setopt (c, CURLOPT_URL, 463 "http://127.0.0.1/hello_world")) || 464 (CURLE_OK != curl_easy_setopt (c, CURLOPT_PORT, (long) port)) || 465 (CURLE_OK != curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, 466 ©Buffer)) || 467 (CURLE_OK != curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc)) || 468 (CURLE_OK != curl_easy_setopt (c, CURLOPT_READFUNCTION, 469 &putBuffer)) || 470 (CURLE_OK != curl_easy_setopt (c, CURLOPT_READDATA, &pos)) || 471 (CURLE_OK != curl_easy_setopt (c, CURLOPT_UPLOAD, 1L)) || 472 (CURLE_OK != curl_easy_setopt (c, CURLOPT_INFILESIZE, (long) PUT_SIZE)) || 473 (CURLE_OK != curl_easy_setopt (c, CURLOPT_FAILONERROR, 1L)) || 474 (CURLE_OK != curl_easy_setopt (c, CURLOPT_ERRORBUFFER, libcurl_errbuf)) || 475 (CURLE_OK != curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 476 (long) 150)) || 477 (CURLE_OK != curl_easy_setopt (c, CURLOPT_TIMEOUT, 478 (long) 150)) || 479 (CURLE_OK != curl_easy_setopt (c, CURLOPT_HTTP_VERSION, 480 (oneone) ? 481 CURL_HTTP_VERSION_1_1 : 482 CURL_HTTP_VERSION_1_0))) 483 { 484 fprintf (stderr, "curl_easy_setopt() failed.\n"); 485 externalErrorExit (); 486 } 487 488 if (CURLE_OK != (errornum = curl_easy_perform (c))) 489 { 490 fprintf (stderr, 491 "curl_easy_perform failed: `%s'\n", 492 curl_easy_strerror (errornum)); 493 curl_easy_cleanup (c); 494 MHD_stop_daemon (d); 495 return 32; 496 } 497 curl_easy_cleanup (c); 498 MHD_stop_daemon (d); 499 if (cbc.pos != strlen ("/hello_world")) 500 { 501 fprintf (stderr, "Got %u bytes ('%.*s'), expected %u bytes. ", 502 (unsigned) cbc.pos, (int) cbc.pos, cbc.buf, 503 (unsigned) strlen ("/hello_world")); 504 mhdErrorExitDesc ("Wrong returned data length"); 505 } 506 if (0 != strncmp ("/hello_world", cbc.buf, strlen ("/hello_world"))) 507 { 508 fprintf (stderr, "Got invalid response '%.*s'. ", (int) cbc.pos, cbc.buf); 509 mhdErrorExitDesc ("Wrong returned data length"); 510 } 511 return 0; 512 } 513 514 515 static unsigned int 516 testPutThreadPool (unsigned int add_flag) 517 { 518 struct MHD_Daemon *d; 519 CURL *c; 520 struct CBC cbc; 521 size_t pos = 0; 522 int done_flag = 0; 523 CURLcode errornum; 524 char buf[2048]; 525 uint16_t port; 526 527 if (MHD_NO != MHD_is_feature_supported (MHD_FEATURE_AUTODETECT_BIND_PORT)) 528 port = 0; 529 else 530 { 531 port = 1272; 532 if (oneone) 533 port += 10; 534 if (incr_read) 535 port += 20; 536 } 537 538 cbc.buf = buf; 539 cbc.size = 2048; 540 cbc.pos = 0; 541 d = MHD_start_daemon (MHD_USE_INTERNAL_POLLING_THREAD | MHD_USE_ERROR_LOG 542 | add_flag, 543 port, 544 NULL, NULL, &ahc_echo, &done_flag, 545 MHD_OPTION_THREAD_POOL_SIZE, MHD_CPU_COUNT, 546 MHD_OPTION_CONNECTION_MEMORY_LIMIT, 547 (size_t) (incr_read ? 1024 : (PUT_SIZE * 4)), 548 MHD_OPTION_END); 549 if (d == NULL) 550 mhdErrorExit (); 551 if (0 == port) 552 { 553 const union MHD_DaemonInfo *dinfo; 554 dinfo = MHD_get_daemon_info (d, MHD_DAEMON_INFO_BIND_PORT); 555 if ((NULL == dinfo) || (0 == dinfo->port) ) 556 mhdErrorExit (); 557 port = dinfo->port; 558 } 559 560 c = curl_easy_init (); 561 if (NULL == c) 562 { 563 fprintf (stderr, "curl_easy_init() failed.\n"); 564 externalErrorExit (); 565 } 566 if ((CURLE_OK != curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1L)) || 567 (CURLE_OK != curl_easy_setopt (c, CURLOPT_URL, 568 "http://127.0.0.1/hello_world")) || 569 (CURLE_OK != curl_easy_setopt (c, CURLOPT_PORT, (long) port)) || 570 (CURLE_OK != curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, 571 ©Buffer)) || 572 (CURLE_OK != curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc)) || 573 (CURLE_OK != curl_easy_setopt (c, CURLOPT_READFUNCTION, 574 &putBuffer)) || 575 (CURLE_OK != curl_easy_setopt (c, CURLOPT_READDATA, &pos)) || 576 (CURLE_OK != curl_easy_setopt (c, CURLOPT_UPLOAD, 1L)) || 577 (CURLE_OK != curl_easy_setopt (c, CURLOPT_INFILESIZE, (long) PUT_SIZE)) || 578 (CURLE_OK != curl_easy_setopt (c, CURLOPT_FAILONERROR, 1L)) || 579 (CURLE_OK != curl_easy_setopt (c, CURLOPT_ERRORBUFFER, libcurl_errbuf)) || 580 (CURLE_OK != curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 581 (long) 150)) || 582 (CURLE_OK != curl_easy_setopt (c, CURLOPT_TIMEOUT, 583 (long) 150)) || 584 (CURLE_OK != curl_easy_setopt (c, CURLOPT_HTTP_VERSION, 585 (oneone) ? 586 CURL_HTTP_VERSION_1_1 : 587 CURL_HTTP_VERSION_1_0))) 588 { 589 fprintf (stderr, "curl_easy_setopt() failed.\n"); 590 externalErrorExit (); 591 } 592 if (CURLE_OK != (errornum = curl_easy_perform (c))) 593 { 594 fprintf (stderr, 595 "curl_easy_perform failed: `%s'\n", 596 curl_easy_strerror (errornum)); 597 curl_easy_cleanup (c); 598 MHD_stop_daemon (d); 599 return 32; 600 } 601 curl_easy_cleanup (c); 602 MHD_stop_daemon (d); 603 if (cbc.pos != strlen ("/hello_world")) 604 { 605 fprintf (stderr, "Got %u bytes ('%.*s'), expected %u bytes. ", 606 (unsigned) cbc.pos, (int) cbc.pos, cbc.buf, 607 (unsigned) strlen ("/hello_world")); 608 mhdErrorExitDesc ("Wrong returned data length"); 609 } 610 if (0 != strncmp ("/hello_world", cbc.buf, strlen ("/hello_world"))) 611 { 612 fprintf (stderr, "Got invalid response '%.*s'. ", (int) cbc.pos, cbc.buf); 613 mhdErrorExitDesc ("Wrong returned data length"); 614 } 615 return 0; 616 } 617 618 619 static unsigned int 620 testPutExternal (void) 621 { 622 struct MHD_Daemon *d; 623 CURL *c; 624 struct CBC cbc; 625 CURLM *multi; 626 CURLMcode mret; 627 fd_set rs; 628 fd_set ws; 629 fd_set es; 630 int running; 631 struct CURLMsg *msg; 632 time_t start; 633 struct timeval tv; 634 size_t pos = 0; 635 int done_flag = 0; 636 char buf[2048]; 637 uint16_t port; 638 639 if (MHD_NO != MHD_is_feature_supported (MHD_FEATURE_AUTODETECT_BIND_PORT)) 640 port = 0; 641 else 642 { 643 port = 1273; 644 if (oneone) 645 port += 10; 646 if (incr_read) 647 port += 20; 648 } 649 650 cbc.buf = buf; 651 cbc.size = 2048; 652 cbc.pos = 0; 653 multi = NULL; 654 d = MHD_start_daemon (MHD_USE_ERROR_LOG | MHD_USE_NO_THREAD_SAFETY, 655 port, 656 NULL, NULL, &ahc_echo, &done_flag, 657 MHD_OPTION_CONNECTION_MEMORY_LIMIT, 658 (size_t) (incr_read ? 1024 : (PUT_SIZE * 4)), 659 MHD_OPTION_APP_FD_SETSIZE, (int) FD_SETSIZE, 660 MHD_OPTION_END); 661 if (d == NULL) 662 mhdErrorExit (); 663 if (0 == port) 664 { 665 const union MHD_DaemonInfo *dinfo; 666 dinfo = MHD_get_daemon_info (d, MHD_DAEMON_INFO_BIND_PORT); 667 if ((NULL == dinfo) || (0 == dinfo->port) ) 668 mhdErrorExit (); 669 port = dinfo->port; 670 } 671 672 c = curl_easy_init (); 673 if (NULL == c) 674 { 675 fprintf (stderr, "curl_easy_init() failed.\n"); 676 externalErrorExit (); 677 } 678 if ((CURLE_OK != curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1L)) || 679 (CURLE_OK != curl_easy_setopt (c, CURLOPT_URL, 680 "http://127.0.0.1/hello_world")) || 681 (CURLE_OK != curl_easy_setopt (c, CURLOPT_PORT, (long) port)) || 682 (CURLE_OK != curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, 683 ©Buffer)) || 684 (CURLE_OK != curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc)) || 685 (CURLE_OK != curl_easy_setopt (c, CURLOPT_READFUNCTION, 686 &putBuffer)) || 687 (CURLE_OK != curl_easy_setopt (c, CURLOPT_READDATA, &pos)) || 688 (CURLE_OK != curl_easy_setopt (c, CURLOPT_UPLOAD, 1L)) || 689 (CURLE_OK != curl_easy_setopt (c, CURLOPT_INFILESIZE, (long) PUT_SIZE)) || 690 (CURLE_OK != curl_easy_setopt (c, CURLOPT_FAILONERROR, 1L)) || 691 (CURLE_OK != curl_easy_setopt (c, CURLOPT_ERRORBUFFER, libcurl_errbuf)) || 692 (CURLE_OK != curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 693 (long) 150)) || 694 (CURLE_OK != curl_easy_setopt (c, CURLOPT_TIMEOUT, 695 (long) 150)) || 696 (CURLE_OK != curl_easy_setopt (c, CURLOPT_HTTP_VERSION, 697 (oneone) ? 698 CURL_HTTP_VERSION_1_1 : 699 CURL_HTTP_VERSION_1_0))) 700 { 701 fprintf (stderr, "curl_easy_setopt() failed.\n"); 702 externalErrorExit (); 703 } 704 705 multi = curl_multi_init (); 706 if (multi == NULL) 707 libcurlErrorExit (); 708 mret = curl_multi_add_handle (multi, c); 709 if (mret != CURLM_OK) 710 libcurlErrorExit (); 711 712 start = time (NULL); 713 while ((time (NULL) - start < 45) && (multi != NULL)) 714 { 715 MHD_socket maxMHDsock; 716 int maxcurlsock; 717 maxMHDsock = MHD_INVALID_SOCKET; 718 maxcurlsock = -1; 719 FD_ZERO (&rs); 720 FD_ZERO (&ws); 721 FD_ZERO (&es); 722 mret = curl_multi_perform (multi, &running); 723 if ((CURLM_OK != mret) && (CURLM_CALL_MULTI_PERFORM != mret)) 724 { 725 fprintf (stderr, "curl_multi_perform() failed. Error: '%s'. ", 726 curl_multi_strerror (mret)); 727 libcurlErrorExit (); 728 } 729 if (CURLM_OK != curl_multi_fdset (multi, &rs, &ws, &es, &maxcurlsock)) 730 libcurlErrorExitDesc ("curl_multi_fdset() failed"); 731 if (MHD_YES != MHD_get_fdset (d, &rs, &ws, &es, &maxMHDsock)) 732 mhdErrorExit (); 733 734 tv.tv_sec = 0; 735 tv.tv_usec = 1000; 736 #ifndef MHD_WINSOCK_SOCKETS 737 if (maxMHDsock > maxcurlsock) 738 maxcurlsock = maxMHDsock; 739 #endif /* MHD_WINSOCK_SOCKETS */ 740 if (-1 == select (maxcurlsock + 1, &rs, &ws, &es, &tv)) 741 { 742 #ifdef MHD_POSIX_SOCKETS 743 if (EINTR != errno) 744 externalErrorExitDesc ("Unexpected select() error"); 745 #else 746 if ((WSAEINVAL != WSAGetLastError ()) || 747 (0 != rs.fd_count) || (0 != ws.fd_count) || (0 != es.fd_count) ) 748 externalErrorExitDesc ("Unexpected select() error"); 749 Sleep ((DWORD) (tv.tv_sec * 1000 + tv.tv_usec / 1000)); 750 #endif 751 } 752 753 mret = curl_multi_perform (multi, &running); 754 if ((CURLM_OK != mret) && (CURLM_CALL_MULTI_PERFORM != mret)) 755 { 756 fprintf (stderr, "curl_multi_perform() failed. Error: '%s'. ", 757 curl_multi_strerror (mret)); 758 libcurlErrorExit (); 759 } 760 if (0 == running) 761 { 762 int pending; 763 int curl_fine = 0; 764 while (NULL != (msg = curl_multi_info_read (multi, &pending))) 765 { 766 if (msg->msg == CURLMSG_DONE) 767 { 768 if (msg->data.result == CURLE_OK) 769 curl_fine = 1; 770 else 771 { 772 fprintf (stderr, 773 "curl_multi_perform() failed: '%s' ", 774 curl_easy_strerror (msg->data.result)); 775 libcurlErrorExit (); 776 } 777 } 778 } 779 if (! curl_fine) 780 { 781 fprintf (stderr, "libcurl haven't returned OK code "); 782 mhdErrorExit (); 783 } 784 curl_multi_remove_handle (multi, c); 785 curl_multi_cleanup (multi); 786 curl_easy_cleanup (c); 787 c = NULL; 788 multi = NULL; 789 } 790 MHD_run (d); 791 } 792 if (multi != NULL) 793 mhdErrorExitDesc ("Request has been aborted by timeout"); 794 795 MHD_stop_daemon (d); 796 if (cbc.pos != strlen ("/hello_world")) 797 { 798 fprintf (stderr, "Got %u bytes ('%.*s'), expected %u bytes. ", 799 (unsigned) cbc.pos, (int) cbc.pos, cbc.buf, 800 (unsigned) strlen ("/hello_world")); 801 mhdErrorExitDesc ("Wrong returned data length"); 802 } 803 if (0 != strncmp ("/hello_world", cbc.buf, strlen ("/hello_world"))) 804 { 805 fprintf (stderr, "Got invalid response '%.*s'. ", (int) cbc.pos, cbc.buf); 806 mhdErrorExitDesc ("Wrong returned data length"); 807 } 808 return 0; 809 } 810 811 812 int 813 main (int argc, char *const *argv) 814 { 815 unsigned int errorCount = 0; 816 unsigned int lastErr; 817 818 oneone = has_in_name (argv[0], "11"); 819 incr_read = has_in_name (argv[0], "_inc"); 820 verbose = has_param (argc, argv, "-v"); 821 if (0 != curl_global_init (CURL_GLOBAL_WIN32)) 822 return 99; 823 put_buffer = alloc_init (PUT_SIZE); 824 if (NULL == put_buffer) 825 return 99; 826 lastErr = testPutExternal (); 827 if (verbose && (0 != lastErr)) 828 fprintf (stderr, "Error during testing with external select().\n"); 829 errorCount += lastErr; 830 if (MHD_YES == MHD_is_feature_supported (MHD_FEATURE_THREADS)) 831 { 832 lastErr = testPutInternalThread (0); 833 if (verbose && (0 != lastErr) ) 834 fprintf (stderr, 835 "Error during testing with internal thread with select().\n"); 836 errorCount += lastErr; 837 lastErr = testPutThreadPerConn (0); 838 if (verbose && (0 != lastErr) ) 839 fprintf (stderr, 840 "Error during testing with internal thread per connection with select().\n"); 841 errorCount += lastErr; 842 lastErr = testPutThreadPool (0); 843 if (verbose && (0 != lastErr) ) 844 fprintf (stderr, 845 "Error during testing with thread pool per connection with select().\n"); 846 errorCount += lastErr; 847 if (MHD_is_feature_supported (MHD_FEATURE_POLL)) 848 { 849 lastErr = testPutInternalThread (MHD_USE_POLL); 850 if (verbose && (0 != lastErr) ) 851 fprintf (stderr, 852 "Error during testing with internal thread with poll().\n"); 853 errorCount += lastErr; 854 lastErr = testPutThreadPerConn (MHD_USE_POLL); 855 if (verbose && (0 != lastErr) ) 856 fprintf (stderr, 857 "Error during testing with internal thread per connection with poll().\n"); 858 errorCount += lastErr; 859 lastErr = testPutThreadPool (MHD_USE_POLL); 860 if (verbose && (0 != lastErr) ) 861 fprintf (stderr, 862 "Error during testing with thread pool per connection with poll().\n"); 863 errorCount += lastErr; 864 } 865 if (MHD_is_feature_supported (MHD_FEATURE_EPOLL)) 866 { 867 lastErr = testPutInternalThread (MHD_USE_EPOLL); 868 if (verbose && (0 != lastErr) ) 869 fprintf (stderr, 870 "Error during testing with internal thread with epoll.\n"); 871 errorCount += lastErr; 872 lastErr = testPutThreadPool (MHD_USE_EPOLL); 873 if (verbose && (0 != lastErr) ) 874 fprintf (stderr, 875 "Error during testing with thread pool per connection with epoll.\n"); 876 errorCount += lastErr; 877 } 878 } 879 free (put_buffer); 880 if (errorCount != 0) 881 fprintf (stderr, "Error (code: %u)\n", errorCount); 882 else if (verbose) 883 printf ("All checks passed successfully.\n"); 884 curl_global_cleanup (); 885 return (errorCount == 0) ? 0 : 1; 886 }