perf_replies.c (61785B)
1 /* 2 This file is part of GNU libmicrohttpd 3 Copyright (C) 2023 Evgeny Grin (Karlson2k) 4 5 Redistribution and use in source and binary forms, with or without 6 modification, are permitted provided that the following conditions 7 are met: 8 1. Redistributions of source code must retain the above copyright 9 notice unmodified, this list of conditions and the following 10 disclaimer. 11 2. Redistributions in binary form must reproduce the above copyright 12 notice, this list of conditions and the following disclaimer in 13 the documentation and/or other materials provided with the 14 distribution. 15 16 THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR 17 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 18 OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 19 IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 20 INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 23 ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 25 THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 */ 27 28 /** 29 * @file tools/perf_replies.c 30 * @brief Implementation of HTTP server optimised for fast replies 31 * based on MHD. 32 * @author Karlson2k (Evgeny Grin) 33 */ 34 35 #include "mhd_options.h" 36 #include <stdio.h> 37 #include <stdlib.h> 38 #include <string.h> 39 #include <stdint.h> 40 #include "microhttpd.h" 41 #include "mhd_tool_str_to_uint.h" 42 #include "mhd_tool_get_cpu_count.h" 43 44 #if defined(MHD_REAL_CPU_COUNT) 45 #if MHD_REAL_CPU_COUNT == 0 46 #undef MHD_REAL_CPU_COUNT 47 #endif /* MHD_REAL_CPU_COUNT == 0 */ 48 #endif /* MHD_REAL_CPU_COUNT */ 49 50 #define PERF_RPL_ERR_CODE_BAD_PARAM 65 51 52 #ifndef MHD_STATICSTR_LEN_ 53 /** 54 * Determine length of static string / macro strings at compile time. 55 */ 56 #define MHD_STATICSTR_LEN_(macro) (sizeof(macro) / sizeof(char) - 1) 57 #endif /* ! MHD_STATICSTR_LEN_ */ 58 59 /* Static constants */ 60 static const char *const tool_copyright = 61 "Copyright (C) 2023 Evgeny Grin (Karlson2k)"; 62 63 /* Package or build specific string, like 64 "Debian 1.2.3-4" or "RevX, built by MSYS2" */ 65 static const char *const build_revision = "" 66 #ifdef MHD_BUILD_REV_STR 67 MHD_BUILD_REV_STR 68 #endif /* MHD_BUILD_REV_STR */ 69 ; 70 71 #define PERF_REPL_PORT_FALLBACK 48080 72 73 /* Dynamic variables */ 74 static char self_name[500] = "perf_replies"; 75 static uint16_t mhd_port = 0; 76 77 static void 78 set_self_name (int argc, char *const *argv) 79 { 80 if ((argc >= 1) && (NULL != argv[0])) 81 { 82 const char *last_dir_sep; 83 last_dir_sep = strrchr (argv[0], '/'); 84 #ifdef _WIN32 85 if (1) 86 { 87 const char *last_w32_dir_sep; 88 last_w32_dir_sep = strrchr (argv[0], '\\'); 89 if ((NULL == last_dir_sep) || 90 ((NULL != last_w32_dir_sep) && (last_w32_dir_sep > last_dir_sep))) 91 last_dir_sep = last_w32_dir_sep; 92 } 93 #endif /* _WIN32 */ 94 if (NULL != last_dir_sep) 95 { 96 size_t name_len; 97 name_len = strlen (last_dir_sep + 1); 98 if ((0 != name_len) && ((sizeof(self_name) / sizeof(char)) > name_len)) 99 { 100 strcpy (self_name, last_dir_sep + 1); 101 return; 102 } 103 } 104 } 105 /* Set default name */ 106 strcpy (self_name, "perf_replies"); 107 return; 108 } 109 110 111 static unsigned int 112 detect_cpu_core_count (void) 113 { 114 int sys_cpu_count; 115 sys_cpu_count = mhd_tool_get_system_cpu_count (); 116 if (0 >= sys_cpu_count) 117 { 118 int proc_cpu_count; 119 fprintf (stderr, "Failed to detect the number of logical CPU cores " 120 "available on the system.\n"); 121 proc_cpu_count = mhd_tool_get_proc_cpu_count (); 122 if (0 < proc_cpu_count) 123 { 124 fprintf (stderr, "The number of CPU cores available for this process " 125 "is used as a fallback.\n"); 126 sys_cpu_count = proc_cpu_count; 127 } 128 #ifdef MHD_REAL_CPU_COUNT 129 if (0 >= sys_cpu_count) 130 { 131 fprintf (stderr, "configure-detected hardcoded number is used " 132 "as a fallback.\n"); 133 sys_cpu_count = MHD_REAL_CPU_COUNT; 134 } 135 #endif 136 if (0 >= sys_cpu_count) 137 sys_cpu_count = 1; 138 printf ("Assuming %d logical CPU core%s on this system.\n", sys_cpu_count, 139 (1 == sys_cpu_count) ? "" : "s"); 140 } 141 else 142 { 143 printf ("Detected %d logical CPU core%s on this system.\n", sys_cpu_count, 144 (1 == sys_cpu_count) ? "" : "s"); 145 } 146 return (unsigned int) sys_cpu_count; 147 } 148 149 150 static unsigned int 151 get_cpu_core_count (void) 152 { 153 static unsigned int num_cpu_cores = 0; 154 if (0 == num_cpu_cores) 155 num_cpu_cores = detect_cpu_core_count (); 156 return num_cpu_cores; 157 } 158 159 160 static unsigned int 161 detect_process_cpu_core_count (void) 162 { 163 unsigned int num_proc_cpu_cores; 164 unsigned int sys_cpu_cores; 165 int res; 166 167 sys_cpu_cores = get_cpu_core_count (); 168 res = mhd_tool_get_proc_cpu_count (); 169 if (0 > res) 170 { 171 fprintf (stderr, "Cannot detect the number of logical CPU cores available " 172 "for this process.\n"); 173 if (1 != sys_cpu_cores) 174 printf ("Assuming all %u system logical CPU cores are available to run " 175 "threads of this process.\n", sys_cpu_cores); 176 else 177 printf ("Assuming single logical CPU core available for this process.\n"); 178 num_proc_cpu_cores = sys_cpu_cores; 179 } 180 else 181 { 182 printf ("Detected %d logical CPU core%s available to run threads " 183 "of this process.\n", res, (1 == res) ? "" : "s"); 184 num_proc_cpu_cores = (unsigned int) res; 185 } 186 if (num_proc_cpu_cores > sys_cpu_cores) 187 { 188 fprintf (stderr, "WARNING: Detected number of CPU cores available " 189 "for this process (%u) is larger than detected number " 190 "of CPU cores on the system (%u).\n", 191 num_proc_cpu_cores, sys_cpu_cores); 192 num_proc_cpu_cores = sys_cpu_cores; 193 fprintf (stderr, "Using %u as the number of logical CPU cores available " 194 "for this process.\n", num_proc_cpu_cores); 195 } 196 return num_proc_cpu_cores; 197 } 198 199 200 static unsigned int 201 get_process_cpu_core_count (void) 202 { 203 static unsigned int proc_num_cpu_cores = 0; 204 if (0 == proc_num_cpu_cores) 205 proc_num_cpu_cores = detect_process_cpu_core_count (); 206 return proc_num_cpu_cores; 207 } 208 209 210 static unsigned int num_threads = 0; 211 212 static unsigned int 213 get_num_threads (void) 214 { 215 #if 0 /* disabled code */ 216 static const unsigned int max_threads = 32; 217 #endif /* disabled code */ 218 if (0 < num_threads) 219 return num_threads; 220 221 num_threads = get_cpu_core_count () / 2; 222 if (0 == num_threads) 223 num_threads = 1; 224 else 225 { 226 unsigned int num_proc_cpus; 227 num_proc_cpus = get_process_cpu_core_count (); 228 if (num_proc_cpus >= num_threads) 229 { 230 printf ("Using half of all available CPU cores, assuming the other half " 231 "is used by client / requests generator.\n"); 232 } 233 else 234 { 235 printf ("Using all CPU cores available for this process as more than " 236 "half of CPU cores on this system are still available for use " 237 "by client / requests generator.\n"); 238 num_threads = num_proc_cpus; 239 } 240 } 241 #if 0 /* disabled code */ 242 if (max_threads < num_threads) 243 { 244 printf ("Number of threads is limited to %u as more threads " 245 "are unlikely to improve performance.\n", max_threads); 246 num_threads = max_threads; 247 } 248 #endif /* disabled code */ 249 250 return num_threads; 251 } 252 253 254 /** 255 * The result of short parameters processing 256 */ 257 enum PerfRepl_param_result 258 { 259 PERF_RPL_PARAM_ERROR, /**< Error processing parameter */ 260 PERF_RPL_PARAM_ONE_CHAR, /**< Processed exactly one character */ 261 PERF_RPL_PARAM_FULL_STR, /**< Processed current parameter completely */ 262 PERF_RPL_PARAM_STR_PLUS_NEXT /**< Current parameter completely and next parameter processed */ 263 }; 264 265 /** 266 * Extract parameter value 267 * @param param_name the name of the parameter 268 * @param param_tail the pointer to the character after parameter name in 269 * the parameter string 270 * @param next_param the pointer to the next parameter (if any) or NULL 271 * @param[out] param_value the pointer where to store resulting value 272 * @return enum value, the PERF_PERPL_SPARAM_ONE_CHAR is not used by 273 * this function 274 */ 275 static enum PerfRepl_param_result 276 get_param_value (const char *param_name, const char *param_tail, 277 const char *next_param, unsigned int *param_value) 278 { 279 const char *value_str; 280 size_t digits; 281 if (0 != param_tail[0]) 282 { 283 if ('=' != param_tail[0]) 284 value_str = param_tail; 285 else 286 value_str = param_tail + 1; 287 } 288 else 289 value_str = next_param; 290 291 if (NULL != value_str) 292 digits = mhd_tool_str_to_uint (value_str, param_value); 293 else 294 digits = 0; 295 296 if ((0 == digits) || (0 != value_str[digits])) 297 { 298 fprintf (stderr, "Parameter '%s' is not followed by valid number.\n", 299 param_name); 300 return PERF_RPL_PARAM_ERROR; 301 } 302 303 if (0 != param_tail[0]) 304 return PERF_RPL_PARAM_FULL_STR; 305 306 return PERF_RPL_PARAM_STR_PLUS_NEXT; 307 } 308 309 310 static void 311 show_help (void) 312 { 313 printf ("Usage: %s [OPTIONS] [PORT_NUMBER]\n", self_name); 314 printf ("Start MHD-based web-server optimised for fast replies.\n"); 315 printf ("\n"); 316 printf ("Threading options (mutually exclusive):\n"); 317 printf (" -A, --all-cpus use all available CPU cores (for \n" 318 " testing with remote client)\n"); 319 printf (" -t NUM, --threads=NUM use NUM threads\n"); 320 printf (" -P, --thread-per-conn use thread-per-connection mode,\n" 321 " the number of threads is limited only\n" 322 " by the number of connection\n"); 323 printf ("\n"); 324 printf ("Force polling function (mutually exclusive):\n"); 325 if (MHD_NO != MHD_is_feature_supported (MHD_FEATURE_EPOLL)) 326 printf (" -e, --epoll use 'epoll' functionality\n"); 327 if (MHD_NO != MHD_is_feature_supported (MHD_FEATURE_POLL)) 328 printf (" -p, --poll use poll() function\n"); 329 printf (" -s, --select use select() function\n"); 330 printf ("\n"); 331 printf ("Response body size options (mutually exclusive):\n"); 332 printf (" -E, --empty empty response, 0 bytes\n"); 333 printf (" -T, --tiny tiny response, 3 bytes (default)\n"); 334 printf (" -M, --medium medium response, 8 KiB\n"); 335 printf (" -L, --large large response, 1 MiB\n"); 336 printf (" -X, --xlarge extra large response, 8 MiB\n"); 337 printf (" -J, --jumbo jumbo response, 101 MiB\n"); 338 printf ("\n"); 339 printf ("Response use options (mutually exclusive):\n"); 340 printf (" -S, --shared pool of pre-generated shared response\n" 341 " objects (default)\n"); 342 printf (" -I, --single single pre-generated response object\n" 343 " used for all requests\n"); 344 printf (" -U, --unique response object generated for every\n" 345 " request and used one time only\n"); 346 printf ("\n"); 347 printf ("Other options:\n"); 348 printf (" -c NUM, --connections=NUM reject more than NUM client \n" 349 " connections\n"); 350 printf (" -O NUM, --timeout=NUM set connection timeout to NUM seconds,\n" 351 " zero means no timeout\n"); 352 printf (" --date-header use the 'Date:' header in every\n" 353 " reply\n"); 354 printf (" --help display this help and exit\n"); 355 printf (" -V, --version output version information and exit\n"); 356 printf ("\n"); 357 printf ("This tool is part of GNU libmicrohttpd suite.\n"); 358 printf ("%s\n", tool_copyright); 359 } 360 361 362 struct PerfRepl_parameters 363 { 364 int all_cpus; 365 unsigned int threads; 366 int thread_per_conn; 367 int epoll; 368 int poll; 369 int select; 370 int empty; 371 int tiny; 372 int medium; 373 int large; 374 int xlarge; 375 int jumbo; 376 int shared; 377 int single; 378 int unique; 379 unsigned int connections; 380 unsigned int timeout; 381 int date_header; 382 int help; 383 int version; 384 }; 385 386 static struct PerfRepl_parameters tool_params; 387 388 389 static enum PerfRepl_param_result 390 process_param__all_cpus (const char *param_name) 391 { 392 if (0 != tool_params.threads) 393 { 394 fprintf (stderr, "Parameter '%s' cannot be used together " 395 "with '-t' or '--threads'.\n", param_name); 396 return PERF_RPL_PARAM_ERROR; 397 } 398 if (tool_params.thread_per_conn) 399 { 400 fprintf (stderr, "Parameter '%s' cannot be used together " 401 "with '-P' or '--thread-per-conn'.\n", param_name); 402 return PERF_RPL_PARAM_ERROR; 403 } 404 tool_params.all_cpus = ! 0; 405 return '-' == param_name[1] ? 406 PERF_RPL_PARAM_FULL_STR :PERF_RPL_PARAM_ONE_CHAR; 407 } 408 409 410 /** 411 * Process parameter '-t' or '--threads' 412 * @param param_name the name of the parameter as specified in command line 413 * @param param_tail the pointer to the character after parameter name in 414 * the parameter string 415 * @param next_param the pointer to the next parameter (if any) or NULL 416 * @return enum value, the PERF_PERPL_SPARAM_ONE_CHAR is not used by 417 * this function 418 */ 419 static enum PerfRepl_param_result 420 process_param__threads (const char *param_name, const char *param_tail, 421 const char *next_param) 422 { 423 unsigned int param_value; 424 enum PerfRepl_param_result value_res; 425 426 if (tool_params.all_cpus) 427 { 428 fprintf (stderr, "Parameter '%s' cannot be used together " 429 "with '-A' or '--all-cpus'.\n", param_name); 430 return PERF_RPL_PARAM_ERROR; 431 } 432 if (tool_params.thread_per_conn) 433 { 434 fprintf (stderr, "Parameter '%s' cannot be used together " 435 "with '-P' or '--thread-per-conn'.\n", param_name); 436 return PERF_RPL_PARAM_ERROR; 437 } 438 value_res = get_param_value (param_name, param_tail, next_param, 439 ¶m_value); 440 if (PERF_RPL_PARAM_ERROR == value_res) 441 return value_res; 442 443 if (0 == param_value) 444 { 445 fprintf (stderr, "'0' is not valid value for parameter '%s'.\n", 446 param_name); 447 return PERF_RPL_PARAM_ERROR; 448 } 449 tool_params.threads = param_value; 450 return value_res; 451 } 452 453 454 static enum PerfRepl_param_result 455 process_param__thread_per_conn (const char *param_name) 456 { 457 if (tool_params.all_cpus) 458 { 459 fprintf (stderr, "Parameter '%s' cannot be used together " 460 "with '-A' or '--all-cpus'.\n", param_name); 461 return PERF_RPL_PARAM_ERROR; 462 } 463 if (0 != tool_params.threads) 464 { 465 fprintf (stderr, "Parameter '%s' cannot be used together " 466 "with '-t' or '--threads'.\n", param_name); 467 return PERF_RPL_PARAM_ERROR; 468 } 469 tool_params.thread_per_conn = ! 0; 470 return '-' == param_name[1] ? 471 PERF_RPL_PARAM_FULL_STR :PERF_RPL_PARAM_ONE_CHAR; 472 } 473 474 475 static enum PerfRepl_param_result 476 process_param__epoll (const char *param_name) 477 { 478 if (tool_params.poll) 479 { 480 fprintf (stderr, "Parameter '%s' cannot be used together " 481 "with '-p' or '--poll'.\n", param_name); 482 return PERF_RPL_PARAM_ERROR; 483 } 484 if (tool_params.select) 485 { 486 fprintf (stderr, "Parameter '%s' cannot be used together " 487 "with '-s' or '--select'.\n", param_name); 488 return PERF_RPL_PARAM_ERROR; 489 } 490 tool_params.epoll = ! 0; 491 return '-' == param_name[1] ? 492 PERF_RPL_PARAM_FULL_STR :PERF_RPL_PARAM_ONE_CHAR; 493 } 494 495 496 static enum PerfRepl_param_result 497 process_param__poll (const char *param_name) 498 { 499 if (tool_params.epoll) 500 { 501 fprintf (stderr, "Parameter '%s' cannot be used together " 502 "with '-e' or '--epoll'.\n", param_name); 503 return PERF_RPL_PARAM_ERROR; 504 } 505 if (tool_params.select) 506 { 507 fprintf (stderr, "Parameter '%s' cannot be used together " 508 "with '-s' or '--select'.\n", param_name); 509 return PERF_RPL_PARAM_ERROR; 510 } 511 tool_params.poll = ! 0; 512 return '-' == param_name[1] ? 513 PERF_RPL_PARAM_FULL_STR :PERF_RPL_PARAM_ONE_CHAR; 514 } 515 516 517 static enum PerfRepl_param_result 518 process_param__select (const char *param_name) 519 { 520 if (tool_params.epoll) 521 { 522 fprintf (stderr, "Parameter '%s' cannot be used together " 523 "with '-e' or '--epoll'.\n", param_name); 524 return PERF_RPL_PARAM_ERROR; 525 } 526 if (tool_params.poll) 527 { 528 fprintf (stderr, "Parameter '%s' cannot be used together " 529 "with '-p' or '--poll'.\n", param_name); 530 return PERF_RPL_PARAM_ERROR; 531 } 532 tool_params.select = ! 0; 533 return '-' == param_name[1] ? 534 PERF_RPL_PARAM_FULL_STR :PERF_RPL_PARAM_ONE_CHAR; 535 } 536 537 538 static enum PerfRepl_param_result 539 process_param__empty (const char *param_name) 540 { 541 if (tool_params.tiny) 542 { 543 fprintf (stderr, "Parameter '%s' cannot be used together " 544 "with '-T' or '--tiny'.\n", param_name); 545 return PERF_RPL_PARAM_ERROR; 546 } 547 if (tool_params.medium) 548 { 549 fprintf (stderr, "Parameter '%s' cannot be used together " 550 "with '-M' or '--medium'.\n", param_name); 551 return PERF_RPL_PARAM_ERROR; 552 } 553 if (tool_params.large) 554 { 555 fprintf (stderr, "Parameter '%s' cannot be used together " 556 "with '-L' or '--large'.\n", param_name); 557 return PERF_RPL_PARAM_ERROR; 558 } 559 if (tool_params.xlarge) 560 { 561 fprintf (stderr, "Parameter '%s' cannot be used together " 562 "with '-X' or '--xlarge'.\n", param_name); 563 return PERF_RPL_PARAM_ERROR; 564 } 565 if (tool_params.jumbo) 566 { 567 fprintf (stderr, "Parameter '%s' cannot be used together " 568 "with '-J' or '--jumbo'.\n", param_name); 569 return PERF_RPL_PARAM_ERROR; 570 } 571 tool_params.empty = ! 0; 572 return '-' == param_name[1] ? 573 PERF_RPL_PARAM_FULL_STR :PERF_RPL_PARAM_ONE_CHAR; 574 } 575 576 577 static enum PerfRepl_param_result 578 process_param__tiny (const char *param_name) 579 { 580 if (tool_params.empty) 581 { 582 fprintf (stderr, "Parameter '%s' cannot be used together " 583 "with '-E' or '--empty'.\n", param_name); 584 return PERF_RPL_PARAM_ERROR; 585 } 586 if (tool_params.medium) 587 { 588 fprintf (stderr, "Parameter '%s' cannot be used together " 589 "with '-M' or '--medium'.\n", param_name); 590 return PERF_RPL_PARAM_ERROR; 591 } 592 if (tool_params.large) 593 { 594 fprintf (stderr, "Parameter '%s' cannot be used together " 595 "with '-L' or '--large'.\n", param_name); 596 return PERF_RPL_PARAM_ERROR; 597 } 598 if (tool_params.xlarge) 599 { 600 fprintf (stderr, "Parameter '%s' cannot be used together " 601 "with '-X' or '--xlarge'.\n", param_name); 602 return PERF_RPL_PARAM_ERROR; 603 } 604 if (tool_params.jumbo) 605 { 606 fprintf (stderr, "Parameter '%s' cannot be used together " 607 "with '-J' or '--jumbo'.\n", param_name); 608 return PERF_RPL_PARAM_ERROR; 609 } 610 tool_params.tiny = ! 0; 611 return '-' == param_name[1] ? 612 PERF_RPL_PARAM_FULL_STR :PERF_RPL_PARAM_ONE_CHAR; 613 } 614 615 616 static enum PerfRepl_param_result 617 process_param__medium (const char *param_name) 618 { 619 if (tool_params.empty) 620 { 621 fprintf (stderr, "Parameter '%s' cannot be used together " 622 "with '-E' or '--empty'.\n", param_name); 623 return PERF_RPL_PARAM_ERROR; 624 } 625 if (tool_params.tiny) 626 { 627 fprintf (stderr, "Parameter '%s' cannot be used together " 628 "with '-T' or '--tiny'.\n", param_name); 629 return PERF_RPL_PARAM_ERROR; 630 } 631 if (tool_params.large) 632 { 633 fprintf (stderr, "Parameter '%s' cannot be used together " 634 "with '-L' or '--large'.\n", param_name); 635 return PERF_RPL_PARAM_ERROR; 636 } 637 if (tool_params.xlarge) 638 { 639 fprintf (stderr, "Parameter '%s' cannot be used together " 640 "with '-X' or '--xlarge'.\n", param_name); 641 return PERF_RPL_PARAM_ERROR; 642 } 643 if (tool_params.jumbo) 644 { 645 fprintf (stderr, "Parameter '%s' cannot be used together " 646 "with '-J' or '--jumbo'.\n", param_name); 647 return PERF_RPL_PARAM_ERROR; 648 } 649 tool_params.medium = ! 0; 650 return '-' == param_name[1] ? 651 PERF_RPL_PARAM_FULL_STR :PERF_RPL_PARAM_ONE_CHAR; 652 } 653 654 655 static enum PerfRepl_param_result 656 process_param__large (const char *param_name) 657 { 658 if (tool_params.empty) 659 { 660 fprintf (stderr, "Parameter '%s' cannot be used together " 661 "with '-E' or '--empty'.\n", param_name); 662 return PERF_RPL_PARAM_ERROR; 663 } 664 if (tool_params.tiny) 665 { 666 fprintf (stderr, "Parameter '%s' cannot be used together " 667 "with '-T' or '--tiny'.\n", param_name); 668 return PERF_RPL_PARAM_ERROR; 669 } 670 if (tool_params.medium) 671 { 672 fprintf (stderr, "Parameter '%s' cannot be used together " 673 "with '-M' or '--medium'.\n", param_name); 674 return PERF_RPL_PARAM_ERROR; 675 } 676 if (tool_params.xlarge) 677 { 678 fprintf (stderr, "Parameter '%s' cannot be used together " 679 "with '-X' or '--xlarge'.\n", param_name); 680 return PERF_RPL_PARAM_ERROR; 681 } 682 if (tool_params.jumbo) 683 { 684 fprintf (stderr, "Parameter '%s' cannot be used together " 685 "with '-J' or '--jumbo'.\n", param_name); 686 return PERF_RPL_PARAM_ERROR; 687 } 688 tool_params.large = ! 0; 689 return '-' == param_name[1] ? 690 PERF_RPL_PARAM_FULL_STR :PERF_RPL_PARAM_ONE_CHAR; 691 } 692 693 694 static enum PerfRepl_param_result 695 process_param__xlarge (const char *param_name) 696 { 697 if (tool_params.empty) 698 { 699 fprintf (stderr, "Parameter '%s' cannot be used together " 700 "with '-E' or '--empty'.\n", param_name); 701 return PERF_RPL_PARAM_ERROR; 702 } 703 if (tool_params.tiny) 704 { 705 fprintf (stderr, "Parameter '%s' cannot be used together " 706 "with '-T' or '--tiny'.\n", param_name); 707 return PERF_RPL_PARAM_ERROR; 708 } 709 if (tool_params.medium) 710 { 711 fprintf (stderr, "Parameter '%s' cannot be used together " 712 "with '-M' or '--medium'.\n", param_name); 713 return PERF_RPL_PARAM_ERROR; 714 } 715 if (tool_params.large) 716 { 717 fprintf (stderr, "Parameter '%s' cannot be used together " 718 "with '-L' or '--large'.\n", param_name); 719 return PERF_RPL_PARAM_ERROR; 720 } 721 if (tool_params.jumbo) 722 { 723 fprintf (stderr, "Parameter '%s' cannot be used together " 724 "with '-J' or '--jumbo'.\n", param_name); 725 return PERF_RPL_PARAM_ERROR; 726 } 727 tool_params.xlarge = ! 0; 728 return '-' == param_name[1] ? 729 PERF_RPL_PARAM_FULL_STR :PERF_RPL_PARAM_ONE_CHAR; 730 } 731 732 733 static enum PerfRepl_param_result 734 process_param__jumbo (const char *param_name) 735 { 736 if (tool_params.empty) 737 { 738 fprintf (stderr, "Parameter '%s' cannot be used together " 739 "with '-E' or '--empty'.\n", param_name); 740 return PERF_RPL_PARAM_ERROR; 741 } 742 if (tool_params.tiny) 743 { 744 fprintf (stderr, "Parameter '%s' cannot be used together " 745 "with '-T' or '--tiny'.\n", param_name); 746 return PERF_RPL_PARAM_ERROR; 747 } 748 if (tool_params.medium) 749 { 750 fprintf (stderr, "Parameter '%s' cannot be used together " 751 "with '-M' or '--medium'.\n", param_name); 752 return PERF_RPL_PARAM_ERROR; 753 } 754 if (tool_params.large) 755 { 756 fprintf (stderr, "Parameter '%s' cannot be used together " 757 "with '-L' or '--large'.\n", param_name); 758 return PERF_RPL_PARAM_ERROR; 759 } 760 if (tool_params.xlarge) 761 { 762 fprintf (stderr, "Parameter '%s' cannot be used together " 763 "with '-X' or '--xlarge'.\n", param_name); 764 return PERF_RPL_PARAM_ERROR; 765 } 766 tool_params.jumbo = ! 0; 767 return '-' == param_name[1] ? 768 PERF_RPL_PARAM_FULL_STR :PERF_RPL_PARAM_ONE_CHAR; 769 } 770 771 772 static enum PerfRepl_param_result 773 process_param__shared (const char *param_name) 774 { 775 if (tool_params.single) 776 { 777 fprintf (stderr, "Parameter '%s' cannot be used together " 778 "with '-I' or '--single'.\n", param_name); 779 return PERF_RPL_PARAM_ERROR; 780 } 781 if (tool_params.unique) 782 { 783 fprintf (stderr, "Parameter '%s' cannot be used together " 784 "with '-U' or '--unique'.\n", param_name); 785 return PERF_RPL_PARAM_ERROR; 786 } 787 tool_params.shared = ! 0; 788 return '-' == param_name[1] ? 789 PERF_RPL_PARAM_FULL_STR :PERF_RPL_PARAM_ONE_CHAR; 790 } 791 792 793 static enum PerfRepl_param_result 794 process_param__single (const char *param_name) 795 { 796 if (tool_params.shared) 797 { 798 fprintf (stderr, "Parameter '%s' cannot be used together " 799 "with '-S' or '--shared'.\n", param_name); 800 return PERF_RPL_PARAM_ERROR; 801 } 802 if (tool_params.unique) 803 { 804 fprintf (stderr, "Parameter '%s' cannot be used together " 805 "with '-U' or '--unique'.\n", param_name); 806 return PERF_RPL_PARAM_ERROR; 807 } 808 tool_params.single = ! 0; 809 return '-' == param_name[1] ? 810 PERF_RPL_PARAM_FULL_STR :PERF_RPL_PARAM_ONE_CHAR; 811 } 812 813 814 static enum PerfRepl_param_result 815 process_param__unique (const char *param_name) 816 { 817 if (tool_params.shared) 818 { 819 fprintf (stderr, "Parameter '%s' cannot be used together " 820 "with '-S' or '--shared'.\n", param_name); 821 return PERF_RPL_PARAM_ERROR; 822 } 823 if (tool_params.single) 824 { 825 fprintf (stderr, "Parameter '%s' cannot be used together " 826 "with '-I' or '--single'.\n", param_name); 827 return PERF_RPL_PARAM_ERROR; 828 } 829 tool_params.unique = ! 0; 830 return '-' == param_name[1] ? 831 PERF_RPL_PARAM_FULL_STR :PERF_RPL_PARAM_ONE_CHAR; 832 } 833 834 835 /** 836 * Process parameter '-c' or '--connections' 837 * @param param_name the name of the parameter as specified in command line 838 * @param param_tail the pointer to the character after parameter name in 839 * the parameter string 840 * @param next_param the pointer to the next parameter (if any) or NULL 841 * @return enum value, the PERF_PERPL_SPARAM_ONE_CHAR is not used by 842 * this function 843 */ 844 static enum PerfRepl_param_result 845 process_param__connections (const char *param_name, const char *param_tail, 846 const char *next_param) 847 { 848 unsigned int param_value; 849 enum PerfRepl_param_result value_res; 850 851 value_res = get_param_value (param_name, param_tail, next_param, 852 ¶m_value); 853 if (PERF_RPL_PARAM_ERROR == value_res) 854 return value_res; 855 856 if (0 == param_value) 857 { 858 fprintf (stderr, "'0' is not valid value for parameter '%s'.\n", 859 param_name); 860 return PERF_RPL_PARAM_ERROR; 861 } 862 tool_params.connections = param_value; 863 return value_res; 864 } 865 866 867 /** 868 * Process parameter '-O' or '--timeout' 869 * @param param_name the name of the parameter as specified in command line 870 * @param param_tail the pointer to the character after parameter name in 871 * the parameter string 872 * @param next_param the pointer to the next parameter (if any) or NULL 873 * @return enum value, the PERF_PERPL_SPARAM_ONE_CHAR is not used by 874 * this function 875 */ 876 static enum PerfRepl_param_result 877 process_param__timeout (const char *param_name, const char *param_tail, 878 const char *next_param) 879 { 880 unsigned int param_value; 881 enum PerfRepl_param_result value_res; 882 883 value_res = get_param_value (param_name, param_tail, next_param, 884 ¶m_value); 885 if (PERF_RPL_PARAM_ERROR == value_res) 886 return value_res; 887 888 tool_params.timeout = param_value; 889 return value_res; 890 } 891 892 893 static enum PerfRepl_param_result 894 process_param__date_header (const char *param_name) 895 { 896 tool_params.date_header = ! 0; 897 return '-' == param_name[1] ? 898 PERF_RPL_PARAM_FULL_STR :PERF_RPL_PARAM_ONE_CHAR; 899 } 900 901 902 static enum PerfRepl_param_result 903 process_param__help (const char *param_name) 904 { 905 /* Use only one of help | version */ 906 if (! tool_params.version) 907 tool_params.help = ! 0; 908 return '-' == param_name[1] ? 909 PERF_RPL_PARAM_FULL_STR :PERF_RPL_PARAM_ONE_CHAR; 910 } 911 912 913 static enum PerfRepl_param_result 914 process_param__version (const char *param_name) 915 { 916 /* Use only one of help | version */ 917 if (! tool_params.help) 918 tool_params.version = ! 0; 919 return '-' == param_name[1] ? 920 PERF_RPL_PARAM_FULL_STR :PERF_RPL_PARAM_ONE_CHAR; 921 } 922 923 924 /** 925 * Process "short" (one character) parameter. 926 * @param param the pointer to character after "-" or after another valid 927 * parameter 928 * @param next_param the pointer to the next parameter (if any) or 929 * NULL if no next parameter 930 * @return enum value with result 931 */ 932 static enum PerfRepl_param_result 933 process_short_param (const char *param, const char *next_param) 934 { 935 const char param_chr = param[0]; 936 if ('A' == param_chr) 937 return process_param__all_cpus ("-A"); 938 else if ('t' == param_chr) 939 return process_param__threads ("-t", param + 1, next_param); 940 else if ('P' == param_chr) 941 return process_param__thread_per_conn ("-P"); 942 else if ('e' == param_chr) 943 return process_param__epoll ("-e"); 944 else if ('p' == param_chr) 945 return process_param__poll ("-p"); 946 else if ('s' == param_chr) 947 return process_param__select ("-s"); 948 else if ('E' == param_chr) 949 return process_param__empty ("-E"); 950 else if ('T' == param_chr) 951 return process_param__tiny ("-T"); 952 else if ('M' == param_chr) 953 return process_param__medium ("-M"); 954 else if ('L' == param_chr) 955 return process_param__large ("-L"); 956 else if ('X' == param_chr) 957 return process_param__xlarge ("-X"); 958 else if ('J' == param_chr) 959 return process_param__jumbo ("-J"); 960 else if ('S' == param_chr) 961 return process_param__shared ("-S"); 962 else if ('I' == param_chr) 963 return process_param__single ("-I"); 964 else if ('U' == param_chr) 965 return process_param__unique ("-U"); 966 else if ('c' == param_chr) 967 return process_param__connections ("-c", param + 1, next_param); 968 else if ('O' == param_chr) 969 return process_param__timeout ("-O", param + 1, next_param); 970 else if ('V' == param_chr) 971 return process_param__version ("-V"); 972 973 fprintf (stderr, "Unrecognised parameter: -%c.\n", param_chr); 974 return PERF_RPL_PARAM_ERROR; 975 } 976 977 978 /** 979 * Process string of "short" (one character) parameters. 980 * @param params_str the pointer to first character after "-" 981 * @param next_param the pointer to the next parameter (if any) or 982 * NULL if no next parameter 983 * @return enum value with result 984 */ 985 static enum PerfRepl_param_result 986 process_short_params_str (const char *params_str, const char *next_param) 987 { 988 if (0 == params_str[0]) 989 { 990 fprintf (stderr, "Unrecognised parameter: -\n"); 991 return PERF_RPL_PARAM_ERROR; 992 } 993 do 994 { 995 enum PerfRepl_param_result param_res; 996 param_res = process_short_param (params_str, next_param); 997 if (PERF_RPL_PARAM_ONE_CHAR != param_res) 998 return param_res; 999 } while (0 != (++params_str)[0]); 1000 return PERF_RPL_PARAM_FULL_STR; 1001 } 1002 1003 1004 /** 1005 * Process "long" (--something) parameters. 1006 * @param param the pointer to first character after "--" 1007 * @param next_param the pointer to the next parameter (if any) or 1008 * NULL if no next parameter 1009 * @return enum value, the PERF_PERPL_SPARAM_ONE_CHAR is not used by 1010 * this function 1011 */ 1012 static enum PerfRepl_param_result 1013 process_long_param (const char *param, const char *next_param) 1014 { 1015 const size_t param_len = strlen (param); 1016 1017 if ((MHD_STATICSTR_LEN_ ("all-cpus") == param_len) && 1018 (0 == memcmp (param, "all-cpus", MHD_STATICSTR_LEN_ ("all-cpus")))) 1019 return process_param__all_cpus ("--all-cpus"); 1020 else if ((MHD_STATICSTR_LEN_ ("threads") <= param_len) && 1021 (0 == memcmp (param, "threads", MHD_STATICSTR_LEN_ ("threads")))) 1022 return process_param__threads ("--threads", 1023 param + MHD_STATICSTR_LEN_ ("threads"), 1024 next_param); 1025 else if ((MHD_STATICSTR_LEN_ ("thread-per-conn") == param_len) && 1026 (0 == memcmp (param, "thread-per-conn", 1027 MHD_STATICSTR_LEN_ ("thread-per-conn")))) 1028 return process_param__thread_per_conn ("--thread-per-conn"); 1029 else if ((MHD_STATICSTR_LEN_ ("epoll") == param_len) && 1030 (0 == memcmp (param, "epoll", MHD_STATICSTR_LEN_ ("epoll")))) 1031 return process_param__epoll ("--epoll"); 1032 else if ((MHD_STATICSTR_LEN_ ("poll") == param_len) && 1033 (0 == memcmp (param, "poll", MHD_STATICSTR_LEN_ ("poll")))) 1034 return process_param__poll ("--poll"); 1035 else if ((MHD_STATICSTR_LEN_ ("select") == param_len) && 1036 (0 == memcmp (param, "select", MHD_STATICSTR_LEN_ ("select")))) 1037 return process_param__select ("--select"); 1038 else if ((MHD_STATICSTR_LEN_ ("empty") == param_len) && 1039 (0 == memcmp (param, "empty", MHD_STATICSTR_LEN_ ("empty")))) 1040 return process_param__empty ("--empty"); 1041 else if ((MHD_STATICSTR_LEN_ ("tiny") == param_len) && 1042 (0 == memcmp (param, "tiny", MHD_STATICSTR_LEN_ ("tiny")))) 1043 return process_param__tiny ("--tiny"); 1044 else if ((MHD_STATICSTR_LEN_ ("medium") == param_len) && 1045 (0 == memcmp (param, "medium", MHD_STATICSTR_LEN_ ("medium")))) 1046 return process_param__medium ("--medium"); 1047 else if ((MHD_STATICSTR_LEN_ ("large") == param_len) && 1048 (0 == memcmp (param, "large", MHD_STATICSTR_LEN_ ("large")))) 1049 return process_param__large ("--large"); 1050 else if ((MHD_STATICSTR_LEN_ ("xlarge") == param_len) && 1051 (0 == memcmp (param, "xlarge", MHD_STATICSTR_LEN_ ("xlarge")))) 1052 return process_param__xlarge ("--xlarge"); 1053 else if ((MHD_STATICSTR_LEN_ ("jumbo") == param_len) && 1054 (0 == memcmp (param, "jumbo", MHD_STATICSTR_LEN_ ("jumbo")))) 1055 return process_param__jumbo ("--jumbo"); 1056 else if ((MHD_STATICSTR_LEN_ ("shared") == param_len) && 1057 (0 == memcmp (param, "shared", MHD_STATICSTR_LEN_ ("shared")))) 1058 return process_param__shared ("--shared"); 1059 else if ((MHD_STATICSTR_LEN_ ("single") == param_len) && 1060 (0 == memcmp (param, "single", MHD_STATICSTR_LEN_ ("single")))) 1061 return process_param__single ("--single"); 1062 else if ((MHD_STATICSTR_LEN_ ("unique") == param_len) && 1063 (0 == memcmp (param, "unique", MHD_STATICSTR_LEN_ ("unique")))) 1064 return process_param__unique ("--unique"); 1065 else if ((MHD_STATICSTR_LEN_ ("connections") <= param_len) && 1066 (0 == memcmp (param, "connections", 1067 MHD_STATICSTR_LEN_ ("connections")))) 1068 return process_param__connections ("--connections", 1069 param 1070 + MHD_STATICSTR_LEN_ ("connections"), 1071 next_param); 1072 else if ((MHD_STATICSTR_LEN_ ("timeout") <= param_len) && 1073 (0 == memcmp (param, "timeout", 1074 MHD_STATICSTR_LEN_ ("timeout")))) 1075 return process_param__timeout ("--timeout", 1076 param + MHD_STATICSTR_LEN_ ("timeout"), 1077 next_param); 1078 else if ((MHD_STATICSTR_LEN_ ("date-header") == param_len) && 1079 (0 == memcmp (param, "date-header", 1080 MHD_STATICSTR_LEN_ ("date-header")))) 1081 return process_param__date_header ("--date-header"); 1082 else if ((MHD_STATICSTR_LEN_ ("help") == param_len) && 1083 (0 == memcmp (param, "help", MHD_STATICSTR_LEN_ ("help")))) 1084 return process_param__help ("--help"); 1085 else if ((MHD_STATICSTR_LEN_ ("version") == param_len) && 1086 (0 == memcmp (param, "version", MHD_STATICSTR_LEN_ ("version")))) 1087 return process_param__version ("--version"); 1088 1089 fprintf (stderr, "Unrecognised parameter: --%s.\n", param); 1090 return PERF_RPL_PARAM_ERROR; 1091 } 1092 1093 1094 static int 1095 process_params (int argc, char *const *argv) 1096 { 1097 int proc_dash_param = ! 0; 1098 int i; 1099 for (i = 1; i < argc; ++i) 1100 { 1101 /** 1102 * The currently processed argument 1103 */ 1104 const char *const p = argv[i]; 1105 const char *const p_next = (argc == (i + 1)) ? NULL : (argv[i + 1]); 1106 if (NULL == p) 1107 { 1108 fprintf (stderr, "The NULL in the parameter number %d. " 1109 "The error in the C library?\n", i); 1110 continue; 1111 } 1112 else if (0 == p[0]) 1113 continue; /* Empty */ 1114 else if (proc_dash_param && ('-' == p[0])) 1115 { 1116 enum PerfRepl_param_result param_res; 1117 if ('-' == p[1]) 1118 { 1119 if (0 == p[2]) 1120 { 1121 proc_dash_param = 0; /* The '--' parameter */ 1122 continue; 1123 } 1124 param_res = process_long_param (p + 2, p_next); 1125 } 1126 else 1127 param_res = process_short_params_str (p + 1, p_next); 1128 1129 if (PERF_RPL_PARAM_ERROR == param_res) 1130 return PERF_RPL_ERR_CODE_BAD_PARAM; 1131 if (PERF_RPL_PARAM_STR_PLUS_NEXT == param_res) 1132 ++i; 1133 else if (PERF_RPL_PARAM_ONE_CHAR == param_res) 1134 abort (); 1135 continue; 1136 } 1137 else if (('0' <= p[0]) && ('9' >= p[0])) 1138 { 1139 /* Process the port number */ 1140 unsigned int read_port; 1141 size_t num_digits; 1142 num_digits = mhd_tool_str_to_uint (p, &read_port); 1143 if (0 != p[num_digits]) 1144 { 1145 fprintf (stderr, "Error in specified port number: %s\n", p); 1146 return PERF_RPL_ERR_CODE_BAD_PARAM; 1147 } 1148 else if (65535 < read_port) 1149 { 1150 fprintf (stderr, "Wrong port number: %s\n", p); 1151 return PERF_RPL_ERR_CODE_BAD_PARAM; 1152 } 1153 mhd_port = (uint16_t) read_port; 1154 } 1155 else 1156 { 1157 fprintf (stderr, "Unrecognised parameter: %s\n\n", p); 1158 return PERF_RPL_ERR_CODE_BAD_PARAM; 1159 } 1160 } 1161 return 0; 1162 } 1163 1164 1165 static void 1166 print_version (void) 1167 { 1168 printf ("%s (GNU libmicrohttpd", self_name); 1169 if (0 != build_revision[0]) 1170 printf ("; %s", build_revision); 1171 printf (") %s\n", MHD_get_version ()); 1172 printf ("%s\n", tool_copyright); 1173 } 1174 1175 1176 static void 1177 print_all_cores_used (void) 1178 { 1179 printf ("No CPU cores on this machine are left unused and available " 1180 "for the client / requests generator. " 1181 "Testing with remote client is recommended.\n"); 1182 } 1183 1184 1185 static void 1186 check_param_port (void) 1187 { 1188 if (0 != mhd_port) 1189 return; 1190 if (MHD_NO == MHD_is_feature_supported (MHD_FEATURE_AUTODETECT_BIND_PORT)) 1191 mhd_port = PERF_REPL_PORT_FALLBACK; 1192 } 1193 1194 1195 /** 1196 * Apply parameter '-A' or '--all-cpus' 1197 */ 1198 static void 1199 check_apply_param__all_cpus (void) 1200 { 1201 if (! tool_params.all_cpus) 1202 return; 1203 1204 num_threads = get_process_cpu_core_count (); 1205 printf ("Requested use of all available CPU cores for MHD threads.\n"); 1206 if (get_cpu_core_count () == num_threads) 1207 print_all_cores_used (); 1208 } 1209 1210 1211 /** 1212 * Apply parameter '-t' or '--threads' 1213 */ 1214 static void 1215 check_apply_param__threads (void) 1216 { 1217 if (0 == tool_params.threads) 1218 return; 1219 1220 num_threads = tool_params.threads; 1221 1222 if (get_process_cpu_core_count () < num_threads) 1223 { 1224 fprintf (stderr, "WARNING: The requested number of threads (%u) is " 1225 "higher than the number of detected available CPU cores (%u).\n", 1226 num_threads, get_process_cpu_core_count ()); 1227 fprintf (stderr, "This decreases the performance. " 1228 "Consider using fewer threads.\n"); 1229 } 1230 if (get_cpu_core_count () == num_threads) 1231 { 1232 printf ("The requested number of threads is equal to the number of " 1233 "detected CPU cores.\n"); 1234 print_all_cores_used (); 1235 } 1236 } 1237 1238 1239 /** 1240 * Apply parameter '-P' or '--thread-per-conn' 1241 * @return non-zero - OK, zero - error 1242 */ 1243 static int 1244 check_apply_param__thread_per_conn (void) 1245 { 1246 if (! tool_params.thread_per_conn) 1247 return ! 0; 1248 1249 if (tool_params.epoll) 1250 { 1251 fprintf (stderr, "'Thread-per-connection' mode cannot be used together " 1252 "with 'epoll'.\n"); 1253 return 0; 1254 } 1255 num_threads = 1; 1256 1257 return ! 0; 1258 } 1259 1260 1261 /* non-zero - OK, zero - error */ 1262 static int 1263 check_param__epoll (void) 1264 { 1265 if (! tool_params.epoll) 1266 return ! 0; 1267 if (MHD_NO == MHD_is_feature_supported (MHD_FEATURE_EPOLL)) 1268 { 1269 fprintf (stderr, "'epoll' was requested, but this MHD build does not " 1270 "support 'epoll' functionality.\n"); 1271 return 0; 1272 } 1273 return ! 0; 1274 } 1275 1276 1277 /* non-zero - OK, zero - error */ 1278 static int 1279 check_param__poll (void) 1280 { 1281 if (! tool_params.poll) 1282 return ! 0; 1283 if (MHD_NO == MHD_is_feature_supported (MHD_FEATURE_POLL)) 1284 { 1285 fprintf (stderr, "poll() was requested, but this MHD build does not " 1286 "support polling by poll().\n"); 1287 return 0; 1288 } 1289 return ! 0; 1290 } 1291 1292 1293 static void 1294 check_param__empty_tiny_medium_large_xlarge_jumbo (void) 1295 { 1296 if (0 == (tool_params.empty | tool_params.tiny | tool_params.medium 1297 | tool_params.large | tool_params.xlarge | tool_params.jumbo)) 1298 tool_params.tiny = ! 0; 1299 } 1300 1301 1302 static void 1303 check_param__shared_single_unique (void) 1304 { 1305 if (0 == (tool_params.shared | tool_params.single | tool_params.unique)) 1306 tool_params.shared = ! 0; 1307 } 1308 1309 1310 /* Must be called after 'check_apply_param__threads()' and 1311 'check_apply_param__all_cpus()' */ 1312 /* non-zero - OK, zero - error */ 1313 static int 1314 check_param__connections (void) 1315 { 1316 if (0 == tool_params.connections) 1317 return ! 0; 1318 if (get_num_threads () > tool_params.connections) 1319 { 1320 fprintf (stderr, "The connections number limit (%u) is less than number " 1321 "of threads used (%u). Use higher value for connections limit.\n", 1322 tool_params.connections, get_num_threads ()); 1323 return 0; 1324 } 1325 return ! 0; 1326 } 1327 1328 1329 /** 1330 * Apply decoded parameters 1331 * @return 0 if success, 1332 * positive error code if case of error, 1333 * -1 to exit program with success (0) error code. 1334 */ 1335 static int 1336 check_apply_params (void) 1337 { 1338 if (tool_params.help) 1339 { 1340 show_help (); 1341 return -1; 1342 } 1343 else if (tool_params.version) 1344 { 1345 print_version (); 1346 return -1; 1347 } 1348 check_param_port (); 1349 check_apply_param__all_cpus (); 1350 check_apply_param__threads (); 1351 if (! check_apply_param__thread_per_conn ()) 1352 return PERF_RPL_ERR_CODE_BAD_PARAM; 1353 if (! check_param__epoll ()) 1354 return PERF_RPL_ERR_CODE_BAD_PARAM; 1355 if (! check_param__poll ()) 1356 return PERF_RPL_ERR_CODE_BAD_PARAM; 1357 check_param__empty_tiny_medium_large_xlarge_jumbo (); 1358 check_param__shared_single_unique (); 1359 if (! check_param__connections ()) 1360 return PERF_RPL_ERR_CODE_BAD_PARAM; 1361 return 0; 1362 } 1363 1364 1365 static uint64_t 1366 mini_rnd (void) 1367 { 1368 /* Simple xoshiro256+ implementation */ 1369 static uint64_t s[4] = { 1370 0xE220A8397B1DCDAFuLL, 0x6E789E6AA1B965F4uLL, 1371 0x06C45D188009454FuLL, 0xF88BB8A8724C81ECuLL 1372 }; /* Good enough for static initialisation */ 1373 1374 const uint64_t ret = s[0] + s[3]; 1375 const uint64_t t = s[1] << 17; 1376 1377 s[2] ^= s[0]; 1378 s[3] ^= s[1]; 1379 s[1] ^= s[2]; 1380 s[0] ^= s[3]; 1381 s[2] ^= t; 1382 s[3] = ((s[3] << 45u) | (s[3] >> (64u - 45u))); 1383 1384 return ret; 1385 } 1386 1387 1388 /* The pool of shared responses */ 1389 static struct MHD_Response **resps = NULL; 1390 static unsigned int num_resps = 0; 1391 /* The single response */ 1392 static struct MHD_Response *resp_single = NULL; 1393 1394 /* Use the same memory area to avoid multiple copies. 1395 The system will keep it in cache. */ 1396 static const char tiny_body[] = "Hi!"; 1397 static char *body_dyn = NULL; /* Non-static body data */ 1398 static size_t body_dyn_size; 1399 1400 /* Non-zero - success, zero - failure */ 1401 static int 1402 init_response_body_data (void) 1403 { 1404 if (0 != body_dyn_size) 1405 { 1406 body_dyn = (char *) malloc (body_dyn_size); 1407 if (NULL == body_dyn) 1408 { 1409 fprintf (stderr, "Failed to allocate memory.\n"); 1410 return 0; 1411 } 1412 if (16u * 1024u >= body_dyn_size) 1413 { 1414 /* Fill the body with HTML-like content */ 1415 size_t pos; 1416 size_t filler_pos; 1417 static const char body_header[] = 1418 "<html>\n" 1419 "<head>\n<title>Sample page title</title>\n<head>\n" 1420 "<body>\n"; 1421 static const char body_filler[] = 1422 "The quick brown fox jumps over the lazy dog.<br>\n"; 1423 static const char body_footer[] = 1424 "</body>\n" 1425 "</html>\n"; 1426 pos = 0; 1427 memcpy (body_dyn + pos, body_header, MHD_STATICSTR_LEN_ (body_header)); 1428 pos += MHD_STATICSTR_LEN_ (body_header); 1429 for (filler_pos = 0; 1430 filler_pos < (body_dyn_size - (MHD_STATICSTR_LEN_ (body_header) 1431 + MHD_STATICSTR_LEN_ (body_footer))); 1432 ++filler_pos) 1433 { 1434 body_dyn[pos + filler_pos] = 1435 body_filler[filler_pos % MHD_STATICSTR_LEN_ (body_filler)]; 1436 } 1437 pos += filler_pos; 1438 memcpy (body_dyn + pos, body_footer, MHD_STATICSTR_LEN_ (body_footer)); 1439 } 1440 else if (2u * 1024u * 1024u >= body_dyn_size) 1441 { 1442 /* Fill the body with binary-like content */ 1443 size_t pos; 1444 for (pos = 0; pos < body_dyn_size; ++pos) 1445 { 1446 body_dyn[pos] = (char) (unsigned char) (255U - pos % 256U); 1447 } 1448 } 1449 else 1450 { 1451 /* Fill the body with pseudo-random binary-like content */ 1452 size_t pos; 1453 uint64_t rnd_data; 1454 for (pos = 0; pos < body_dyn_size - body_dyn_size % 8; 1455 pos += 8) 1456 { 1457 rnd_data = mini_rnd (); 1458 body_dyn[pos + 0] = (char) (unsigned char) (rnd_data >> 0); 1459 body_dyn[pos + 1] = (char) (unsigned char) (rnd_data >> 8); 1460 body_dyn[pos + 2] = (char) (unsigned char) (rnd_data >> 16); 1461 body_dyn[pos + 3] = (char) (unsigned char) (rnd_data >> 24); 1462 body_dyn[pos + 4] = (char) (unsigned char) (rnd_data >> 32); 1463 body_dyn[pos + 5] = (char) (unsigned char) (rnd_data >> 40); 1464 body_dyn[pos + 6] = (char) (unsigned char) (rnd_data >> 48); 1465 body_dyn[pos + 7] = (char) (unsigned char) (rnd_data >> 56); 1466 } 1467 rnd_data = mini_rnd (); 1468 for ((void) pos; pos < body_dyn_size; ++pos) 1469 { 1470 body_dyn[pos] = (char) (unsigned char) (rnd_data); 1471 rnd_data >>= 8u; 1472 } 1473 } 1474 } 1475 return ! 0; 1476 } 1477 1478 1479 static struct MHD_Response * 1480 create_response_object (void) 1481 { 1482 #if MHD_VERSION >= 0x00097701 1483 if (NULL != body_dyn) 1484 return MHD_create_response_from_buffer_static (body_dyn_size, 1485 body_dyn); 1486 else if (tool_params.empty) 1487 return MHD_create_response_empty (MHD_RF_NONE); 1488 1489 return MHD_create_response_from_buffer_static (MHD_STATICSTR_LEN_ (tiny_body), 1490 tiny_body); 1491 1492 #else /* MHD_VERSION < 0x00097701 */ 1493 if (NULL != body_dyn) 1494 return MHD_create_response_from_buffer (body_dyn_size, 1495 (void *) body_dyn, 1496 MHD_RESPMEM_PERSISTENT); 1497 else if (tool_params.empty) 1498 return MHD_create_response_from_buffer (0, 1499 (void *) tiny_body, 1500 MHD_RESPMEM_PERSISTENT); 1501 1502 return MHD_create_response_from_buffer (MHD_STATICSTR_LEN_ (tiny_body), 1503 (void *) tiny_body, 1504 MHD_RESPMEM_PERSISTENT); 1505 #endif /* MHD_VERSION < 0x00097701 */ 1506 } 1507 1508 1509 static int 1510 init_data (void) 1511 { 1512 unsigned int i; 1513 1514 if (tool_params.medium) 1515 body_dyn_size = 8U * 1024U; 1516 else if (tool_params.large) 1517 body_dyn_size = 1024U * 1024U; 1518 else if (tool_params.xlarge) 1519 body_dyn_size = 8U * 1024U * 1024U; 1520 else if (tool_params.jumbo) 1521 body_dyn_size = 101U * 1024U * 1024U; 1522 else 1523 body_dyn_size = 0; 1524 1525 if (! init_response_body_data ()) 1526 return 25; 1527 1528 if (tool_params.unique) 1529 return 0; /* Responses are generated on-fly */ 1530 1531 if (tool_params.single) 1532 { 1533 resp_single = create_response_object (); 1534 if (NULL == resp_single) 1535 { 1536 fprintf (stderr, "Failed to create response.\n"); 1537 return 25; 1538 } 1539 return 0; 1540 } 1541 1542 /* Use more responses to minimise waiting in threads while the response 1543 used by other thread. */ 1544 if (! tool_params.thread_per_conn) 1545 num_resps = 16 * get_num_threads (); 1546 else 1547 num_resps = 16 * get_cpu_core_count (); 1548 1549 resps = (struct MHD_Response **) 1550 malloc ((sizeof(struct MHD_Response *)) * num_resps); 1551 if (NULL == resps) 1552 { 1553 if (NULL != body_dyn) 1554 { 1555 free (body_dyn); 1556 body_dyn = NULL; 1557 } 1558 fprintf (stderr, "Failed to allocate memory.\n"); 1559 return 25; 1560 } 1561 for (i = 0; i < num_resps; ++i) 1562 { 1563 resps[i] = create_response_object (); 1564 if (NULL == resps[i]) 1565 { 1566 fprintf (stderr, "Failed to create responses.\n"); 1567 break; 1568 } 1569 } 1570 if (i == num_resps) 1571 return 0; /* Success */ 1572 1573 /* Cleanup */ 1574 while (i-- != 0) 1575 MHD_destroy_response (resps[i]); 1576 free (resps); 1577 resps = NULL; 1578 num_resps = 0; 1579 if (NULL != body_dyn) 1580 free (body_dyn); 1581 body_dyn = NULL; 1582 return 32; 1583 } 1584 1585 1586 static void 1587 deinit_data (void) 1588 { 1589 if (NULL != resp_single) 1590 MHD_destroy_response (resp_single); 1591 resp_single = NULL; 1592 if (NULL != resps) 1593 { 1594 unsigned int i; 1595 for (i = 0; i < num_resps; ++i) 1596 MHD_destroy_response (resps[i]); 1597 num_resps = 0; 1598 free (resps); 1599 } 1600 resps = NULL; 1601 if (NULL != body_dyn) 1602 free (body_dyn); 1603 body_dyn = NULL; 1604 } 1605 1606 1607 static enum MHD_Result 1608 answer_shared_response (void *cls, 1609 struct MHD_Connection *connection, 1610 const char *url, 1611 const char *method, 1612 const char *version, 1613 const char *upload_data, 1614 size_t *upload_data_size, 1615 void **req_cls) 1616 { 1617 static int marker = 0; 1618 unsigned int resp_index; 1619 static volatile unsigned int last_index = 0; 1620 (void) cls; /* Unused */ 1621 (void) url; (void) version; /* Unused */ 1622 (void) upload_data; (void) upload_data_size; /* Unused */ 1623 1624 if (NULL == *req_cls) 1625 { 1626 /* The first call */ 1627 *req_cls = (void *) ▮ 1628 /* Do not send reply yet. No error. */ 1629 return MHD_YES; 1630 } 1631 if ((0 != strcmp (method, MHD_HTTP_METHOD_GET)) && 1632 (0 != strcmp (method, MHD_HTTP_METHOD_HEAD))) 1633 return MHD_NO; /* Unsupported method, close connection */ 1634 1635 /* This kind of operation does not guarantee that numbers are not reused 1636 in parallel threads, when processed simultaneously, but this should not 1637 be a big problem, as it just slow down replies a bit due to 1638 response locking. */ 1639 resp_index = (last_index++) % num_resps; 1640 return MHD_queue_response (connection, MHD_HTTP_OK, resps[resp_index]); 1641 } 1642 1643 1644 static enum MHD_Result 1645 answer_single_response (void *cls, 1646 struct MHD_Connection *connection, 1647 const char *url, 1648 const char *method, 1649 const char *version, 1650 const char *upload_data, 1651 size_t *upload_data_size, 1652 void **req_cls) 1653 { 1654 static int marker = 0; 1655 (void) cls; /* Unused */ 1656 (void) url; (void) version; /* Unused */ 1657 (void) upload_data; (void) upload_data_size; /* Unused */ 1658 1659 if (NULL == *req_cls) 1660 { 1661 /* The first call */ 1662 *req_cls = (void *) ▮ 1663 /* Do not send reply yet. No error. */ 1664 return MHD_YES; 1665 } 1666 if ((0 != strcmp (method, MHD_HTTP_METHOD_GET)) && 1667 (0 != strcmp (method, MHD_HTTP_METHOD_HEAD))) 1668 return MHD_NO; /* Unsupported method, close connection */ 1669 1670 return MHD_queue_response (connection, MHD_HTTP_OK, resp_single); 1671 } 1672 1673 1674 static enum MHD_Result 1675 answer_unique_empty_response (void *cls, 1676 struct MHD_Connection *connection, 1677 const char *url, 1678 const char *method, 1679 const char *version, 1680 const char *upload_data, 1681 size_t *upload_data_size, 1682 void **req_cls) 1683 { 1684 static int marker = 0; 1685 struct MHD_Response *r; 1686 enum MHD_Result ret; 1687 (void) cls; /* Unused */ 1688 (void) url; (void) version; /* Unused */ 1689 (void) upload_data; (void) upload_data_size; /* Unused */ 1690 1691 if (NULL == *req_cls) 1692 { 1693 /* The first call */ 1694 *req_cls = (void *) ▮ 1695 /* Do not send reply yet. No error. */ 1696 return MHD_YES; 1697 } 1698 if ((0 != strcmp (method, MHD_HTTP_METHOD_GET)) && 1699 (0 != strcmp (method, MHD_HTTP_METHOD_HEAD))) 1700 return MHD_NO; /* Unsupported method, close connection */ 1701 1702 #if MHD_VERSION >= 0x00097701 1703 r = MHD_create_response_empty (MHD_RF_NONE); 1704 #else /* MHD_VERSION < 0x00097701 */ 1705 r = MHD_create_response_from_buffer (0, 1706 NULL, 1707 MHD_RESPMEM_PERSISTENT); 1708 #endif /* MHD_VERSION < 0x00097701 */ 1709 ret = MHD_queue_response (connection, MHD_HTTP_OK, r); 1710 MHD_destroy_response (r); 1711 return ret; 1712 } 1713 1714 1715 static enum MHD_Result 1716 answer_unique_tiny_response (void *cls, 1717 struct MHD_Connection *connection, 1718 const char *url, 1719 const char *method, 1720 const char *version, 1721 const char *upload_data, 1722 size_t *upload_data_size, 1723 void **req_cls) 1724 { 1725 static int marker = 0; 1726 struct MHD_Response *r; 1727 enum MHD_Result ret; 1728 (void) cls; /* Unused */ 1729 (void) url; (void) version; /* Unused */ 1730 (void) upload_data; (void) upload_data_size; /* Unused */ 1731 1732 if (NULL == *req_cls) 1733 { 1734 /* The first call */ 1735 *req_cls = (void *) ▮ 1736 /* Do not send reply yet. No error. */ 1737 return MHD_YES; 1738 } 1739 if ((0 != strcmp (method, MHD_HTTP_METHOD_GET)) && 1740 (0 != strcmp (method, MHD_HTTP_METHOD_HEAD))) 1741 return MHD_NO; /* Unsupported method, close connection */ 1742 1743 #if MHD_VERSION >= 0x00097701 1744 r = MHD_create_response_from_buffer_static (MHD_STATICSTR_LEN_ (tiny_body), 1745 tiny_body); 1746 #else /* MHD_VERSION < 0x00097701 */ 1747 r = MHD_create_response_from_buffer (MHD_STATICSTR_LEN_ (tiny_body), 1748 (void *) tiny_body, 1749 MHD_RESPMEM_PERSISTENT); 1750 #endif /* MHD_VERSION < 0x00097701 */ 1751 ret = MHD_queue_response (connection, MHD_HTTP_OK, r); 1752 MHD_destroy_response (r); 1753 return ret; 1754 } 1755 1756 1757 static enum MHD_Result 1758 answer_unique_dyn_response (void *cls, 1759 struct MHD_Connection *connection, 1760 const char *url, 1761 const char *method, 1762 const char *version, 1763 const char *upload_data, 1764 size_t *upload_data_size, 1765 void **req_cls) 1766 { 1767 static int marker = 0; 1768 struct MHD_Response *r; 1769 enum MHD_Result ret; 1770 (void) cls; /* Unused */ 1771 (void) url; (void) version; /* Unused */ 1772 (void) upload_data; (void) upload_data_size; /* Unused */ 1773 1774 if (NULL == *req_cls) 1775 { 1776 /* The first call */ 1777 *req_cls = (void *) ▮ 1778 /* Do not send reply yet. No error. */ 1779 return MHD_YES; 1780 } 1781 if ((0 != strcmp (method, MHD_HTTP_METHOD_GET)) && 1782 (0 != strcmp (method, MHD_HTTP_METHOD_HEAD))) 1783 return MHD_NO; /* Unsupported method, close connection */ 1784 1785 #if MHD_VERSION >= 0x00097701 1786 r = MHD_create_response_from_buffer_static (body_dyn_size, 1787 body_dyn); 1788 #else /* MHD_VERSION < 0x00097701 */ 1789 r = MHD_create_response_from_buffer (body_dyn_size, 1790 (void *) body_dyn, 1791 MHD_RESPMEM_PERSISTENT); 1792 #endif /* MHD_VERSION < 0x00097701 */ 1793 ret = MHD_queue_response (connection, MHD_HTTP_OK, r); 1794 MHD_destroy_response (r); 1795 return ret; 1796 } 1797 1798 1799 static void 1800 print_perf_warnings (void) 1801 { 1802 int newline_needed = 0; 1803 #if defined (_DEBUG) 1804 fprintf (stderr, "WARNING: Running with debug asserts enabled, " 1805 "the performance is suboptimal.\n"); 1806 newline_needed |= ! 0; 1807 #endif /* _DEBUG */ 1808 #if defined(__GNUC__) && ! defined (__OPTIMIZE__) 1809 fprintf (stderr, "WARNING: This tool is compiled without enabled compiler " 1810 "optimisations, the performance is suboptimal.\n"); 1811 newline_needed |= ! 0; 1812 #endif /* __GNUC__ && ! __OPTIMIZE__ */ 1813 #if defined(__GNUC__) && defined (__OPTIMIZE_SIZE__) 1814 fprintf (stderr, "WARNING: This tool is compiled with size-optimisations, " 1815 "the performance is suboptimal.\n"); 1816 #endif /* __GNUC__ && ! __OPTIMIZE__ */ 1817 #if MHD_VERSION >= 0x00097701 1818 if (MHD_NO != MHD_is_feature_supported (MHD_FEATURE_DEBUG_BUILD)) 1819 { 1820 fprintf (stderr, "WARNING: The libmicrohttpd is compiled with " 1821 "debug asserts enabled, the performance is suboptimal.\n"); 1822 newline_needed |= ! 0; 1823 } 1824 #endif /* MHD_VERSION >= 0x00097701 */ 1825 if (newline_needed) 1826 printf ("\n"); 1827 } 1828 1829 1830 /* Borrowed from daemon.c */ 1831 /* TODO: run-time detection */ 1832 /** 1833 * Default connection limit. 1834 */ 1835 #ifdef MHD_POSIX_SOCKETS 1836 #define MHD_MAX_CONNECTIONS_DEFAULT (FD_SETSIZE - 4) 1837 #else 1838 #define MHD_MAX_CONNECTIONS_DEFAULT (FD_SETSIZE - 2) 1839 #endif 1840 1841 static unsigned int 1842 get_mhd_conn_limit (struct MHD_Daemon *d) 1843 { 1844 /* TODO: implement run-time detection */ 1845 (void) d; /* Unused */ 1846 if (0 != tool_params.connections) 1847 return tool_params.connections; 1848 return (unsigned int) MHD_MAX_CONNECTIONS_DEFAULT; 1849 } 1850 1851 1852 static const char * 1853 get_mhd_response_size (void) 1854 { 1855 if (tool_params.empty) 1856 return "0 bytes (empty)"; 1857 else if (tool_params.tiny) 1858 return "3 bytes (tiny)"; 1859 else if (tool_params.medium) 1860 return "8 KiB (medium)"; 1861 else if (tool_params.large) 1862 return "1 MiB (large)"; 1863 else if (tool_params.xlarge) 1864 return "8 MiB (xlarge)"; 1865 else if (tool_params.jumbo) 1866 return "101 MiB (jumbo)"; 1867 return "!!internal error!!"; 1868 } 1869 1870 1871 static int 1872 run_mhd (void) 1873 { 1874 MHD_AccessHandlerCallback reply_func; 1875 struct MHD_Daemon *d; 1876 unsigned int use_num_threads; 1877 unsigned int flags = MHD_NO_FLAG; 1878 struct MHD_OptionItem opt_arr[16]; 1879 size_t opt_count = 0; 1880 const union MHD_DaemonInfo *d_info; 1881 const char *poll_mode; 1882 uint16_t port; 1883 1884 if (tool_params.thread_per_conn) 1885 use_num_threads = 0; 1886 else 1887 use_num_threads = get_num_threads (); 1888 printf ("\n"); 1889 1890 print_perf_warnings (); 1891 1892 printf ("Responses:\n"); 1893 printf (" Sharing: "); 1894 if (tool_params.shared) 1895 { 1896 reply_func = &answer_shared_response; 1897 printf ("pre-generated shared pool with %u objects\n", num_resps); 1898 } 1899 else if (tool_params.single) 1900 { 1901 reply_func = &answer_single_response; 1902 printf ("single pre-generated reused response object\n"); 1903 } 1904 else 1905 { 1906 /* Unique responses */ 1907 if (tool_params.empty) 1908 reply_func = &answer_unique_empty_response; 1909 else if (tool_params.tiny) 1910 reply_func = &answer_unique_tiny_response; 1911 else 1912 reply_func = &answer_unique_dyn_response; 1913 printf ("one-time response object generated for every request\n"); 1914 } 1915 printf (" Body size: %s\n", 1916 get_mhd_response_size ()); 1917 1918 flags |= MHD_USE_ERROR_LOG; 1919 flags |= MHD_USE_INTERNAL_POLLING_THREAD; 1920 if (tool_params.epoll) 1921 flags |= MHD_USE_EPOLL; 1922 else if (tool_params.poll) 1923 flags |= MHD_USE_POLL; 1924 else if (tool_params.select) 1925 (void) flags; /* No special additional flag */ 1926 else 1927 flags |= MHD_USE_AUTO; 1928 1929 if (tool_params.thread_per_conn) 1930 flags |= MHD_USE_THREAD_PER_CONNECTION; 1931 1932 if (! tool_params.date_header) 1933 flags |= MHD_USE_SUPPRESS_DATE_NO_CLOCK; 1934 1935 if (0 != tool_params.connections) 1936 { 1937 opt_arr[opt_count].option = MHD_OPTION_CONNECTION_LIMIT; 1938 opt_arr[opt_count].value = (intptr_t) tool_params.connections; 1939 opt_arr[opt_count].ptr_value = NULL; 1940 ++opt_count; 1941 } 1942 if (1 < use_num_threads) 1943 { 1944 opt_arr[opt_count].option = MHD_OPTION_THREAD_POOL_SIZE; 1945 opt_arr[opt_count].value = (intptr_t) use_num_threads; 1946 opt_arr[opt_count].ptr_value = NULL; 1947 ++opt_count; 1948 } 1949 if (1) 1950 { 1951 opt_arr[opt_count].option = MHD_OPTION_CONNECTION_TIMEOUT; 1952 opt_arr[opt_count].value = (intptr_t) tool_params.timeout; 1953 opt_arr[opt_count].ptr_value = NULL; 1954 ++opt_count; 1955 } 1956 if (1) 1957 { 1958 struct MHD_OptionItem option = { 1959 MHD_OPTION_END, 0, NULL 1960 }; 1961 1962 if (opt_count >= (sizeof(opt_arr) / sizeof(opt_arr[0]))) 1963 abort (); 1964 opt_arr[opt_count] = option; 1965 } 1966 d = MHD_start_daemon (flags, mhd_port, NULL, NULL, reply_func, NULL, 1967 MHD_OPTION_ARRAY, opt_arr, MHD_OPTION_END); 1968 if (NULL == d) 1969 { 1970 fprintf (stderr, "Error starting MHD daemon.\n"); 1971 return 15; 1972 } 1973 d_info = MHD_get_daemon_info (d, MHD_DAEMON_INFO_FLAGS); 1974 if (NULL == d_info) 1975 abort (); 1976 flags = (unsigned int) d_info->flags; 1977 if (0 != (flags & MHD_USE_POLL)) 1978 poll_mode = "poll()"; 1979 else if (0 != (flags & MHD_USE_EPOLL)) 1980 poll_mode = "epoll"; 1981 else 1982 poll_mode = "select()"; 1983 d_info = MHD_get_daemon_info (d, MHD_DAEMON_INFO_BIND_PORT); 1984 if (NULL == d_info) 1985 abort (); 1986 port = d_info->port; 1987 if (0 == port) 1988 fprintf (stderr, "Cannot detect port number. Consider specifying " 1989 "port number explicitly.\n"); 1990 1991 printf ("MHD is running.\n"); 1992 printf (" Bind port: %u\n", (unsigned int) port); 1993 printf (" Polling function: %s\n", poll_mode); 1994 printf (" Threading: "); 1995 if (MHD_USE_THREAD_PER_CONNECTION == (flags & MHD_USE_THREAD_PER_CONNECTION)) 1996 printf ("thread per connection\n"); 1997 else if (1 == get_num_threads ()) 1998 printf ("one MHD thread\n"); 1999 else 2000 printf ("%u MHD threads in thread pool\n", get_num_threads ()); 2001 printf (" Connections limit: %u\n", get_mhd_conn_limit (d)); 2002 printf (" Connection timeout: %u%s\n", tool_params.timeout, 2003 0 == tool_params.timeout ? " (no timeout)" : ""); 2004 printf (" 'Date:' header: %s\n", 2005 tool_params.date_header ? "Yes" : "No"); 2006 printf ("To test with remote client use " 2007 "http://HOST_IP:%u/\n", (unsigned int) port); 2008 printf ("To test with client on the same host use " 2009 "http://127.0.0.1:%u/\n", (unsigned int) port); 2010 printf ("\nPress ENTER to stop.\n"); 2011 if (1) 2012 { 2013 char buf[10]; 2014 (void) fgets (buf, sizeof(buf), stdin); 2015 } 2016 MHD_stop_daemon (d); 2017 return 0; 2018 } 2019 2020 2021 int 2022 main (int argc, char *const *argv) 2023 { 2024 int ret; 2025 set_self_name (argc, argv); 2026 ret = process_params (argc, argv); 2027 if (0 != ret) 2028 return ret; 2029 ret = check_apply_params (); 2030 if (0 > ret) 2031 return 0; 2032 if (0 != ret) 2033 return ret; 2034 ret = init_data (); 2035 if (0 != ret) 2036 return ret; 2037 ret = run_mhd (); 2038 deinit_data (); 2039 return ret; 2040 }