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