test_put.c (16176B)
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 daemontest_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 #include "mhd_has_in_name.h" 38 39 /* Turn any MHD_PANIC() or failing mhd_assert() reached from this 40 test into a marked, classifiable test error (TESTING.md, P5). */ 41 #include "mhd_panic_tripwire.h" 42 43 #ifndef WINDOWS 44 #include <unistd.h> 45 #endif 46 47 #if defined(MHD_CPU_COUNT) && (MHD_CPU_COUNT + 0) < 2 48 #undef MHD_CPU_COUNT 49 #endif 50 #if ! defined(MHD_CPU_COUNT) 51 #define MHD_CPU_COUNT 2 52 #endif 53 54 static int oneone; 55 56 struct CBC 57 { 58 char *buf; 59 size_t pos; 60 size_t size; 61 }; 62 63 static size_t 64 putBuffer (void *stream, size_t size, size_t nmemb, void *ptr) 65 { 66 size_t *pos = ptr; 67 size_t wrt; 68 69 wrt = size * nmemb; 70 if (wrt > 8 - (*pos)) 71 wrt = 8 - (*pos); 72 memcpy (stream, &("Hello123"[*pos]), wrt); 73 (*pos) += wrt; 74 return wrt; 75 } 76 77 78 static size_t 79 copyBuffer (void *ptr, size_t size, size_t nmemb, void *ctx) 80 { 81 struct CBC *cbc = ctx; 82 83 if (cbc->pos + size * nmemb > cbc->size) 84 return 0; /* overflow */ 85 memcpy (&cbc->buf[cbc->pos], ptr, size * nmemb); 86 cbc->pos += size * nmemb; 87 return size * nmemb; 88 } 89 90 91 static enum MHD_Result 92 ahc_echo (void *cls, 93 struct MHD_Connection *connection, 94 const char *url, 95 const char *method, 96 const char *version, 97 const char *upload_data, size_t *upload_data_size, 98 void **req_cls) 99 { 100 int *done = cls; 101 struct MHD_Response *response; 102 enum MHD_Result ret; 103 (void) version; (void) req_cls; /* Unused. Silent compiler warning. */ 104 105 if (0 != strcmp ("PUT", method)) 106 return MHD_NO; /* unexpected method */ 107 if ((*done) == 0) 108 { 109 if (*upload_data_size != 8) 110 return MHD_YES; /* not yet ready */ 111 if (0 == memcmp (upload_data, "Hello123", 8)) 112 { 113 *upload_data_size = 0; 114 } 115 else 116 { 117 printf ("Invalid upload data `%8s'!\n", upload_data); 118 return MHD_NO; 119 } 120 *done = 1; 121 return MHD_YES; 122 } 123 response = MHD_create_response_from_buffer_copy (strlen (url), 124 (const void *) url); 125 ret = MHD_queue_response (connection, MHD_HTTP_OK, response); 126 MHD_destroy_response (response); 127 return ret; 128 } 129 130 131 static unsigned int 132 testInternalPut (void) 133 { 134 struct MHD_Daemon *d; 135 CURL *c; 136 char buf[2048]; 137 struct CBC cbc; 138 size_t pos = 0; 139 int done_flag = 0; 140 CURLcode errornum; 141 uint16_t port; 142 143 if (MHD_NO != MHD_is_feature_supported (MHD_FEATURE_AUTODETECT_BIND_PORT)) 144 port = 0; 145 else 146 { 147 port = 1450; 148 if (oneone) 149 port += 10; 150 } 151 152 cbc.buf = buf; 153 cbc.size = 2048; 154 cbc.pos = 0; 155 d = MHD_start_daemon (MHD_USE_INTERNAL_POLLING_THREAD | MHD_USE_ERROR_LOG, 156 port, 157 NULL, NULL, &ahc_echo, &done_flag, MHD_OPTION_END); 158 if (d == NULL) 159 return 1; 160 if (0 == port) 161 { 162 const union MHD_DaemonInfo *dinfo; 163 dinfo = MHD_get_daemon_info (d, MHD_DAEMON_INFO_BIND_PORT); 164 if ((NULL == dinfo) || (0 == dinfo->port) ) 165 { 166 MHD_stop_daemon (d); return 32; 167 } 168 port = dinfo->port; 169 } 170 c = curl_easy_init (); 171 curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1/hello_world"); 172 curl_easy_setopt (c, CURLOPT_PORT, (long) port); 173 curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, ©Buffer); 174 curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc); 175 curl_easy_setopt (c, CURLOPT_READFUNCTION, &putBuffer); 176 curl_easy_setopt (c, CURLOPT_READDATA, &pos); 177 curl_easy_setopt (c, CURLOPT_UPLOAD, 1L); 178 curl_easy_setopt (c, CURLOPT_INFILESIZE_LARGE, (curl_off_t) 8L); 179 curl_easy_setopt (c, CURLOPT_FAILONERROR, 1L); 180 curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L); 181 if (oneone) 182 curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1); 183 else 184 curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0); 185 curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L); 186 /* NOTE: use of CONNECTTIMEOUT without also 187 * setting NOSIGNAL results in really weird 188 * crashes on my system! */ 189 curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1L); 190 if (CURLE_OK != (errornum = curl_easy_perform (c))) 191 { 192 fprintf (stderr, 193 "curl_easy_perform failed: `%s'\n", 194 curl_easy_strerror (errornum)); 195 curl_easy_cleanup (c); 196 MHD_stop_daemon (d); 197 return 2; 198 } 199 curl_easy_cleanup (c); 200 MHD_stop_daemon (d); 201 if (cbc.pos != strlen ("/hello_world")) 202 return 4; 203 if (0 != strncmp ("/hello_world", cbc.buf, strlen ("/hello_world"))) 204 return 8; 205 return 0; 206 } 207 208 209 static unsigned int 210 testMultithreadedPut (void) 211 { 212 struct MHD_Daemon *d; 213 CURL *c; 214 char buf[2048]; 215 struct CBC cbc; 216 size_t pos = 0; 217 int done_flag = 0; 218 CURLcode errornum; 219 uint16_t port; 220 221 if (MHD_NO != MHD_is_feature_supported (MHD_FEATURE_AUTODETECT_BIND_PORT)) 222 port = 0; 223 else 224 { 225 port = 1451; 226 if (oneone) 227 port += 10; 228 } 229 230 cbc.buf = buf; 231 cbc.size = 2048; 232 cbc.pos = 0; 233 d = MHD_start_daemon (MHD_USE_THREAD_PER_CONNECTION 234 | MHD_USE_INTERNAL_POLLING_THREAD | MHD_USE_ERROR_LOG, 235 port, 236 NULL, NULL, &ahc_echo, &done_flag, MHD_OPTION_END); 237 if (d == NULL) 238 return 16; 239 if (0 == port) 240 { 241 const union MHD_DaemonInfo *dinfo; 242 dinfo = MHD_get_daemon_info (d, MHD_DAEMON_INFO_BIND_PORT); 243 if ((NULL == dinfo) || (0 == dinfo->port) ) 244 { 245 MHD_stop_daemon (d); return 32; 246 } 247 port = dinfo->port; 248 } 249 c = curl_easy_init (); 250 curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1/hello_world"); 251 curl_easy_setopt (c, CURLOPT_PORT, (long) port); 252 curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, ©Buffer); 253 curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc); 254 curl_easy_setopt (c, CURLOPT_READFUNCTION, &putBuffer); 255 curl_easy_setopt (c, CURLOPT_READDATA, &pos); 256 curl_easy_setopt (c, CURLOPT_UPLOAD, 1L); 257 curl_easy_setopt (c, CURLOPT_INFILESIZE_LARGE, (curl_off_t) 8L); 258 curl_easy_setopt (c, CURLOPT_FAILONERROR, 1L); 259 curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L); 260 if (oneone) 261 curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1); 262 else 263 curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0); 264 curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L); 265 /* NOTE: use of CONNECTTIMEOUT without also 266 * setting NOSIGNAL results in really weird 267 * crashes on my system! */ 268 curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1L); 269 if (CURLE_OK != (errornum = curl_easy_perform (c))) 270 { 271 fprintf (stderr, 272 "curl_easy_perform failed: `%s'\n", 273 curl_easy_strerror (errornum)); 274 curl_easy_cleanup (c); 275 MHD_stop_daemon (d); 276 return 32; 277 } 278 curl_easy_cleanup (c); 279 MHD_stop_daemon (d); 280 if (cbc.pos != strlen ("/hello_world")) 281 return 64; 282 if (0 != strncmp ("/hello_world", cbc.buf, strlen ("/hello_world"))) 283 return 128; 284 285 return 0; 286 } 287 288 289 static unsigned int 290 testMultithreadedPoolPut (void) 291 { 292 struct MHD_Daemon *d; 293 CURL *c; 294 char buf[2048]; 295 struct CBC cbc; 296 size_t pos = 0; 297 int done_flag = 0; 298 CURLcode errornum; 299 uint16_t port; 300 301 if (MHD_NO != MHD_is_feature_supported (MHD_FEATURE_AUTODETECT_BIND_PORT)) 302 port = 0; 303 else 304 { 305 port = 1452; 306 if (oneone) 307 port += 10; 308 } 309 310 cbc.buf = buf; 311 cbc.size = 2048; 312 cbc.pos = 0; 313 d = MHD_start_daemon (MHD_USE_INTERNAL_POLLING_THREAD | MHD_USE_ERROR_LOG, 314 port, 315 NULL, NULL, &ahc_echo, &done_flag, 316 MHD_OPTION_THREAD_POOL_SIZE, MHD_CPU_COUNT, 317 MHD_OPTION_END); 318 if (d == NULL) 319 return 16; 320 if (0 == port) 321 { 322 const union MHD_DaemonInfo *dinfo; 323 dinfo = MHD_get_daemon_info (d, MHD_DAEMON_INFO_BIND_PORT); 324 if ((NULL == dinfo) || (0 == dinfo->port) ) 325 { 326 MHD_stop_daemon (d); return 32; 327 } 328 port = dinfo->port; 329 } 330 c = curl_easy_init (); 331 curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1/hello_world"); 332 curl_easy_setopt (c, CURLOPT_PORT, (long) port); 333 curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, ©Buffer); 334 curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc); 335 curl_easy_setopt (c, CURLOPT_READFUNCTION, &putBuffer); 336 curl_easy_setopt (c, CURLOPT_READDATA, &pos); 337 curl_easy_setopt (c, CURLOPT_UPLOAD, 1L); 338 curl_easy_setopt (c, CURLOPT_INFILESIZE_LARGE, (curl_off_t) 8L); 339 curl_easy_setopt (c, CURLOPT_FAILONERROR, 1L); 340 curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L); 341 if (oneone) 342 curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1); 343 else 344 curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0); 345 curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L); 346 /* NOTE: use of CONNECTTIMEOUT without also 347 * setting NOSIGNAL results in really weird 348 * crashes on my system! */ 349 curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1L); 350 if (CURLE_OK != (errornum = curl_easy_perform (c))) 351 { 352 fprintf (stderr, 353 "curl_easy_perform failed: `%s'\n", 354 curl_easy_strerror (errornum)); 355 curl_easy_cleanup (c); 356 MHD_stop_daemon (d); 357 return 32; 358 } 359 curl_easy_cleanup (c); 360 MHD_stop_daemon (d); 361 if (cbc.pos != strlen ("/hello_world")) 362 return 64; 363 if (0 != strncmp ("/hello_world", cbc.buf, strlen ("/hello_world"))) 364 return 128; 365 366 return 0; 367 } 368 369 370 static unsigned int 371 testExternalPut (void) 372 { 373 struct MHD_Daemon *d; 374 CURL *c; 375 char buf[2048]; 376 struct CBC cbc; 377 CURLM *multi; 378 CURLMcode mret; 379 fd_set rs; 380 fd_set ws; 381 fd_set es; 382 MHD_socket maxsock; 383 int maxposixs; /* Max socket number unused on W32 */ 384 int running; 385 struct CURLMsg *msg; 386 time_t start; 387 struct timeval tv; 388 size_t pos = 0; 389 int done_flag = 0; 390 uint16_t port; 391 392 if (MHD_NO != MHD_is_feature_supported (MHD_FEATURE_AUTODETECT_BIND_PORT)) 393 port = 0; 394 else 395 { 396 port = 1453; 397 if (oneone) 398 port += 10; 399 } 400 401 multi = NULL; 402 cbc.buf = buf; 403 cbc.size = 2048; 404 cbc.pos = 0; 405 d = MHD_start_daemon (MHD_USE_ERROR_LOG | MHD_USE_NO_THREAD_SAFETY, 406 port, 407 NULL, NULL, &ahc_echo, &done_flag, 408 MHD_OPTION_APP_FD_SETSIZE, (int) FD_SETSIZE, 409 MHD_OPTION_END); 410 if (d == NULL) 411 return 256; 412 if (0 == port) 413 { 414 const union MHD_DaemonInfo *dinfo; 415 dinfo = MHD_get_daemon_info (d, MHD_DAEMON_INFO_BIND_PORT); 416 if ((NULL == dinfo) || (0 == dinfo->port) ) 417 { 418 MHD_stop_daemon (d); return 32; 419 } 420 port = dinfo->port; 421 } 422 c = curl_easy_init (); 423 curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1/hello_world"); 424 curl_easy_setopt (c, CURLOPT_PORT, (long) port); 425 curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, ©Buffer); 426 curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc); 427 curl_easy_setopt (c, CURLOPT_READFUNCTION, &putBuffer); 428 curl_easy_setopt (c, CURLOPT_READDATA, &pos); 429 curl_easy_setopt (c, CURLOPT_UPLOAD, 1L); 430 curl_easy_setopt (c, CURLOPT_INFILESIZE_LARGE, (curl_off_t) 8L); 431 curl_easy_setopt (c, CURLOPT_FAILONERROR, 1L); 432 curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L); 433 if (oneone) 434 curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1); 435 else 436 curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0); 437 curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L); 438 /* NOTE: use of CONNECTTIMEOUT without also 439 * setting NOSIGNAL results in really weird 440 * crashes on my system! */ 441 curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1L); 442 443 444 multi = curl_multi_init (); 445 if (multi == NULL) 446 { 447 curl_easy_cleanup (c); 448 MHD_stop_daemon (d); 449 return 512; 450 } 451 mret = curl_multi_add_handle (multi, c); 452 if (mret != CURLM_OK) 453 { 454 curl_multi_cleanup (multi); 455 curl_easy_cleanup (c); 456 MHD_stop_daemon (d); 457 return 1024; 458 } 459 start = time (NULL); 460 while ((time (NULL) - start < 5) && (multi != NULL)) 461 { 462 maxsock = MHD_INVALID_SOCKET; 463 maxposixs = -1; 464 FD_ZERO (&rs); 465 FD_ZERO (&ws); 466 FD_ZERO (&es); 467 curl_multi_perform (multi, &running); 468 mret = curl_multi_fdset (multi, &rs, &ws, &es, &maxposixs); 469 if (mret != CURLM_OK) 470 { 471 curl_multi_remove_handle (multi, c); 472 curl_multi_cleanup (multi); 473 curl_easy_cleanup (c); 474 MHD_stop_daemon (d); 475 return 2048; 476 } 477 if (MHD_YES != MHD_get_fdset (d, &rs, &ws, &es, &maxsock)) 478 { 479 curl_multi_remove_handle (multi, c); 480 curl_multi_cleanup (multi); 481 curl_easy_cleanup (c); 482 MHD_stop_daemon (d); 483 return 4096; 484 } 485 #ifdef MHD_POSIX_SOCKETS 486 if (maxsock > maxposixs) 487 maxposixs = maxsock; 488 #endif /* MHD_POSIX_SOCKETS */ 489 tv.tv_sec = 0; 490 tv.tv_usec = 1000; 491 if (-1 == select (maxposixs + 1, &rs, &ws, &es, &tv)) 492 { 493 #ifdef MHD_POSIX_SOCKETS 494 if (EINTR != errno) 495 { 496 fprintf (stderr, "Unexpected select() error: %d. Line: %d\n", 497 (int) errno, __LINE__); 498 fflush (stderr); 499 exit (99); 500 } 501 #else 502 if ((WSAEINVAL != WSAGetLastError ()) || 503 (0 != rs.fd_count) || (0 != ws.fd_count) || (0 != es.fd_count) ) 504 { 505 fprintf (stderr, "Unexpected select() error: %d. Line: %d\n", 506 (int) WSAGetLastError (), __LINE__); 507 fflush (stderr); 508 exit (99); 509 } 510 Sleep (1); 511 #endif 512 } 513 curl_multi_perform (multi, &running); 514 if (0 == running) 515 { 516 int pending; 517 int curl_fine = 0; 518 while (NULL != (msg = curl_multi_info_read (multi, &pending))) 519 { 520 if (msg->msg == CURLMSG_DONE) 521 { 522 if (msg->data.result == CURLE_OK) 523 curl_fine = 1; 524 else 525 { 526 fprintf (stderr, 527 "%s failed at %s:%d: `%s'\n", 528 "curl_multi_perform", 529 __FILE__, 530 __LINE__, curl_easy_strerror (msg->data.result)); 531 abort (); 532 } 533 } 534 } 535 if (! curl_fine) 536 { 537 fprintf (stderr, "libcurl haven't returned OK code\n"); 538 abort (); 539 } 540 curl_multi_remove_handle (multi, c); 541 curl_multi_cleanup (multi); 542 curl_easy_cleanup (c); 543 c = NULL; 544 multi = NULL; 545 } 546 MHD_run (d); 547 } 548 if (multi != NULL) 549 { 550 curl_multi_remove_handle (multi, c); 551 curl_easy_cleanup (c); 552 curl_multi_cleanup (multi); 553 } 554 MHD_stop_daemon (d); 555 if (cbc.pos != strlen ("/hello_world")) 556 return 8192; 557 if (0 != strncmp ("/hello_world", cbc.buf, strlen ("/hello_world"))) 558 return 16384; 559 return 0; 560 } 561 562 563 int 564 main (int argc, char *const *argv) 565 { 566 unsigned int errorCount = 0; 567 (void) argc; /* Unused. Silent compiler warning. */ 568 569 if ((NULL == argv) || (0 == argv[0])) 570 return 99; 571 oneone = has_in_name (argv[0], "11"); 572 if (0 != curl_global_init (CURL_GLOBAL_WIN32)) 573 return 2; 574 if (MHD_YES == MHD_is_feature_supported (MHD_FEATURE_THREADS)) 575 { 576 errorCount += testInternalPut (); 577 errorCount += testMultithreadedPut (); 578 errorCount += testMultithreadedPoolPut (); 579 } 580 errorCount += testExternalPut (); 581 if (errorCount != 0) 582 fprintf (stderr, "Error (code: %u)\n", errorCount); 583 curl_global_cleanup (); 584 return (0 == errorCount) ? 0 : 1; /* 0 == pass */ 585 }