test_delete.c (16278B)
1 /* 2 This file is part of libmicrohttpd 3 Copyright (C) 2007, 2016 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_delete.c 24 * @brief Testcase for libmicrohttpd DELETE 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 ("DELETE", 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 testInternalDelete (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 port = 1152; 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_CUSTOMREQUEST, "DELETE"); 174 curl_easy_setopt (c, CURLOPT_UPLOAD, 1L); 175 curl_easy_setopt (c, CURLOPT_INFILESIZE_LARGE, (curl_off_t) 8L); 176 curl_easy_setopt (c, CURLOPT_FAILONERROR, 1L); 177 curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L); 178 if (oneone) 179 curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1); 180 else 181 curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0); 182 curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L); 183 /* NOTE: use of CONNECTTIMEOUT without also 184 * setting NOSIGNAL results in really weird 185 * crashes on my system! */ 186 curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1L); 187 if (CURLE_OK != (errornum = curl_easy_perform (c))) 188 { 189 fprintf (stderr, 190 "curl_easy_perform failed: `%s'\n", 191 curl_easy_strerror (errornum)); 192 curl_easy_cleanup (c); 193 MHD_stop_daemon (d); 194 return 2; 195 } 196 curl_easy_cleanup (c); 197 MHD_stop_daemon (d); 198 if (cbc.pos != strlen ("/hello_world")) 199 return 4; 200 if (0 != strncmp ("/hello_world", cbc.buf, strlen ("/hello_world"))) 201 return 8; 202 return 0; 203 } 204 205 206 static unsigned int 207 testMultithreadedDelete (void) 208 { 209 struct MHD_Daemon *d; 210 CURL *c; 211 char buf[2048]; 212 struct CBC cbc; 213 size_t pos = 0; 214 int done_flag = 0; 215 CURLcode errornum; 216 uint16_t port; 217 218 if (MHD_NO != MHD_is_feature_supported (MHD_FEATURE_AUTODETECT_BIND_PORT)) 219 port = 0; 220 else 221 port = 1153; 222 223 cbc.buf = buf; 224 cbc.size = 2048; 225 cbc.pos = 0; 226 d = MHD_start_daemon (MHD_USE_THREAD_PER_CONNECTION 227 | MHD_USE_INTERNAL_POLLING_THREAD | MHD_USE_ERROR_LOG, 228 port, 229 NULL, NULL, &ahc_echo, &done_flag, MHD_OPTION_END); 230 if (d == NULL) 231 return 16; 232 if (0 == port) 233 { 234 const union MHD_DaemonInfo *dinfo; 235 dinfo = MHD_get_daemon_info (d, MHD_DAEMON_INFO_BIND_PORT); 236 if ((NULL == dinfo) || (0 == dinfo->port) ) 237 { 238 MHD_stop_daemon (d); return 32; 239 } 240 port = dinfo->port; 241 } 242 c = curl_easy_init (); 243 curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1/hello_world"); 244 curl_easy_setopt (c, CURLOPT_PORT, (long) port); 245 curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, ©Buffer); 246 curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc); 247 curl_easy_setopt (c, CURLOPT_READFUNCTION, &putBuffer); 248 curl_easy_setopt (c, CURLOPT_READDATA, &pos); 249 curl_easy_setopt (c, CURLOPT_CUSTOMREQUEST, "DELETE"); 250 curl_easy_setopt (c, CURLOPT_UPLOAD, 1L); 251 curl_easy_setopt (c, CURLOPT_INFILESIZE_LARGE, (curl_off_t) 8L); 252 curl_easy_setopt (c, CURLOPT_FAILONERROR, 1L); 253 curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L); 254 if (oneone) 255 curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1); 256 else 257 curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0); 258 curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L); 259 /* NOTE: use of CONNECTTIMEOUT without also 260 * setting NOSIGNAL results in really weird 261 * crashes on my system! */ 262 curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1L); 263 if (CURLE_OK != (errornum = curl_easy_perform (c))) 264 { 265 fprintf (stderr, 266 "curl_easy_perform failed: `%s'\n", 267 curl_easy_strerror (errornum)); 268 curl_easy_cleanup (c); 269 MHD_stop_daemon (d); 270 return 32; 271 } 272 curl_easy_cleanup (c); 273 MHD_stop_daemon (d); 274 if (cbc.pos != strlen ("/hello_world")) 275 return 64; 276 if (0 != strncmp ("/hello_world", cbc.buf, strlen ("/hello_world"))) 277 return 128; 278 279 return 0; 280 } 281 282 283 static unsigned int 284 testMultithreadedPoolDelete (void) 285 { 286 struct MHD_Daemon *d; 287 CURL *c; 288 char buf[2048]; 289 struct CBC cbc; 290 size_t pos = 0; 291 int done_flag = 0; 292 CURLcode errornum; 293 uint16_t port; 294 295 if (MHD_NO != MHD_is_feature_supported (MHD_FEATURE_AUTODETECT_BIND_PORT)) 296 port = 0; 297 else 298 port = 1154; 299 300 cbc.buf = buf; 301 cbc.size = 2048; 302 cbc.pos = 0; 303 d = MHD_start_daemon (MHD_USE_INTERNAL_POLLING_THREAD | MHD_USE_ERROR_LOG, 304 port, 305 NULL, NULL, &ahc_echo, &done_flag, 306 MHD_OPTION_THREAD_POOL_SIZE, MHD_CPU_COUNT, 307 MHD_OPTION_END); 308 if (d == NULL) 309 return 16; 310 if (0 == port) 311 { 312 const union MHD_DaemonInfo *dinfo; 313 dinfo = MHD_get_daemon_info (d, MHD_DAEMON_INFO_BIND_PORT); 314 if ((NULL == dinfo) || (0 == dinfo->port) ) 315 { 316 MHD_stop_daemon (d); return 32; 317 } 318 port = dinfo->port; 319 } 320 c = curl_easy_init (); 321 curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1/hello_world"); 322 curl_easy_setopt (c, CURLOPT_PORT, (long) port); 323 curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, ©Buffer); 324 curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc); 325 curl_easy_setopt (c, CURLOPT_READFUNCTION, &putBuffer); 326 curl_easy_setopt (c, CURLOPT_READDATA, &pos); 327 curl_easy_setopt (c, CURLOPT_CUSTOMREQUEST, "DELETE"); 328 curl_easy_setopt (c, CURLOPT_UPLOAD, 1L); 329 curl_easy_setopt (c, CURLOPT_INFILESIZE_LARGE, (curl_off_t) 8L); 330 curl_easy_setopt (c, CURLOPT_FAILONERROR, 1L); 331 curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L); 332 if (oneone) 333 curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1); 334 else 335 curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0); 336 curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L); 337 /* NOTE: use of CONNECTTIMEOUT without also 338 * setting NOSIGNAL results in really weird 339 * crashes on my system! */ 340 curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1L); 341 if (CURLE_OK != (errornum = curl_easy_perform (c))) 342 { 343 fprintf (stderr, 344 "curl_easy_perform failed: `%s'\n", 345 curl_easy_strerror (errornum)); 346 curl_easy_cleanup (c); 347 MHD_stop_daemon (d); 348 return 32; 349 } 350 curl_easy_cleanup (c); 351 MHD_stop_daemon (d); 352 if (cbc.pos != strlen ("/hello_world")) 353 return 64; 354 if (0 != strncmp ("/hello_world", cbc.buf, strlen ("/hello_world"))) 355 return 128; 356 357 return 0; 358 } 359 360 361 static unsigned int 362 testExternalDelete (void) 363 { 364 struct MHD_Daemon *d; 365 CURL *c; 366 char buf[2048]; 367 struct CBC cbc; 368 CURLM *multi; 369 CURLMcode mret; 370 fd_set rs; 371 fd_set ws; 372 fd_set es; 373 MHD_socket maxsock; 374 #ifdef MHD_WINSOCK_SOCKETS 375 int maxposixs; /* Max socket number unused on W32 */ 376 #else /* MHD_POSIX_SOCKETS */ 377 #define maxposixs maxsock 378 #endif /* MHD_POSIX_SOCKETS */ 379 int running; 380 struct CURLMsg *msg; 381 time_t start; 382 struct timeval tv; 383 size_t pos = 0; 384 int done_flag = 0; 385 uint16_t port; 386 387 if (MHD_NO != MHD_is_feature_supported (MHD_FEATURE_AUTODETECT_BIND_PORT)) 388 port = 0; 389 else 390 port = 1154; 391 392 multi = NULL; 393 cbc.buf = buf; 394 cbc.size = 2048; 395 cbc.pos = 0; 396 d = MHD_start_daemon (MHD_USE_ERROR_LOG | MHD_USE_NO_THREAD_SAFETY, 397 port, 398 NULL, NULL, &ahc_echo, &done_flag, 399 MHD_OPTION_APP_FD_SETSIZE, (int) FD_SETSIZE, 400 MHD_OPTION_END); 401 if (d == NULL) 402 return 256; 403 if (0 == port) 404 { 405 const union MHD_DaemonInfo *dinfo; 406 dinfo = MHD_get_daemon_info (d, MHD_DAEMON_INFO_BIND_PORT); 407 if ((NULL == dinfo) || (0 == dinfo->port) ) 408 { 409 MHD_stop_daemon (d); return 32; 410 } 411 port = dinfo->port; 412 } 413 c = curl_easy_init (); 414 curl_easy_setopt (c, CURLOPT_URL, "http://127.0.0.1/hello_world"); 415 curl_easy_setopt (c, CURLOPT_PORT, (long) port); 416 curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, ©Buffer); 417 curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc); 418 curl_easy_setopt (c, CURLOPT_READFUNCTION, &putBuffer); 419 curl_easy_setopt (c, CURLOPT_READDATA, &pos); 420 curl_easy_setopt (c, CURLOPT_CUSTOMREQUEST, "DELETE"); 421 curl_easy_setopt (c, CURLOPT_UPLOAD, 1L); 422 curl_easy_setopt (c, CURLOPT_INFILESIZE_LARGE, (curl_off_t) 8L); 423 curl_easy_setopt (c, CURLOPT_FAILONERROR, 1L); 424 curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L); 425 if (oneone) 426 curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1); 427 else 428 curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0); 429 curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L); 430 /* NOTE: use of CONNECTTIMEOUT without also 431 * setting NOSIGNAL results in really weird 432 * crashes on my system! */ 433 curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1L); 434 435 436 multi = curl_multi_init (); 437 if (multi == NULL) 438 { 439 curl_easy_cleanup (c); 440 MHD_stop_daemon (d); 441 return 512; 442 } 443 mret = curl_multi_add_handle (multi, c); 444 if (mret != CURLM_OK) 445 { 446 curl_multi_cleanup (multi); 447 curl_easy_cleanup (c); 448 MHD_stop_daemon (d); 449 return 1024; 450 } 451 start = time (NULL); 452 while ((time (NULL) - start < 5) && (multi != NULL)) 453 { 454 maxsock = MHD_INVALID_SOCKET; 455 maxposixs = -1; 456 FD_ZERO (&rs); 457 FD_ZERO (&ws); 458 FD_ZERO (&es); 459 curl_multi_perform (multi, &running); 460 mret = curl_multi_fdset (multi, &rs, &ws, &es, &maxposixs); 461 if (mret != CURLM_OK) 462 { 463 curl_multi_remove_handle (multi, c); 464 curl_multi_cleanup (multi); 465 curl_easy_cleanup (c); 466 MHD_stop_daemon (d); 467 return 2048; 468 } 469 if (MHD_YES != MHD_get_fdset (d, &rs, &ws, &es, &maxsock)) 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 4096; 476 } 477 tv.tv_sec = 0; 478 tv.tv_usec = 1000; 479 if (-1 == select (maxposixs + 1, &rs, &ws, &es, &tv)) 480 { 481 #ifdef MHD_POSIX_SOCKETS 482 if (EINTR != errno) 483 { 484 fprintf (stderr, "Unexpected select() error: %d. Line: %d\n", 485 (int) errno, __LINE__); 486 fflush (stderr); 487 exit (99); 488 } 489 #else 490 if ((WSAEINVAL != WSAGetLastError ()) || 491 (0 != rs.fd_count) || (0 != ws.fd_count) || (0 != es.fd_count) ) 492 { 493 fprintf (stderr, "Unexpected select() error: %d. Line: %d\n", 494 (int) WSAGetLastError (), __LINE__); 495 fflush (stderr); 496 exit (99); 497 } 498 Sleep (1); 499 #endif 500 } 501 curl_multi_perform (multi, &running); 502 if (0 == running) 503 { 504 int pending; 505 int curl_fine = 0; 506 while (NULL != (msg = curl_multi_info_read (multi, &pending))) 507 { 508 if (msg->msg == CURLMSG_DONE) 509 { 510 if (msg->data.result == CURLE_OK) 511 curl_fine = 1; 512 else 513 { 514 fprintf (stderr, 515 "%s failed at %s:%d: `%s'\n", 516 "curl_multi_perform", 517 __FILE__, 518 __LINE__, curl_easy_strerror (msg->data.result)); 519 abort (); 520 } 521 } 522 } 523 if (! curl_fine) 524 { 525 fprintf (stderr, "libcurl haven't returned OK code\n"); 526 abort (); 527 } 528 curl_multi_remove_handle (multi, c); 529 curl_multi_cleanup (multi); 530 curl_easy_cleanup (c); 531 c = NULL; 532 multi = NULL; 533 } 534 MHD_run (d); 535 } 536 if (multi != NULL) 537 { 538 curl_multi_remove_handle (multi, c); 539 curl_easy_cleanup (c); 540 curl_multi_cleanup (multi); 541 } 542 MHD_stop_daemon (d); 543 if (cbc.pos != strlen ("/hello_world")) 544 return 8192; 545 if (0 != strncmp ("/hello_world", cbc.buf, strlen ("/hello_world"))) 546 return 16384; 547 return 0; 548 } 549 550 551 int 552 main (int argc, char *const *argv) 553 { 554 unsigned int errorCount = 0; 555 (void) argc; /* Unused. Silent compiler warning. */ 556 557 if ((NULL == argv) || (0 == argv[0])) 558 return 99; 559 oneone = has_in_name (argv[0], "11"); 560 if (0 != curl_global_init (CURL_GLOBAL_WIN32)) 561 return 2; 562 if (MHD_YES == MHD_is_feature_supported (MHD_FEATURE_THREADS)) 563 { 564 errorCount += testInternalDelete (); 565 errorCount += testMultithreadedDelete (); 566 errorCount += testMultithreadedPoolDelete (); 567 } 568 errorCount += testExternalDelete (); 569 if (errorCount != 0) 570 fprintf (stderr, "Error (code: %u)\n", errorCount); 571 curl_global_cleanup (); 572 return (0 == errorCount) ? 0 : 1; /* 0 == pass */ 573 }