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