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