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