libmicrohttpd

HTTP/1.x server C library (MHD 1.x, stable)
Log | Files | Refs | Submodules | README | LICENSE

perf_replies.c (61925B)


      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   unsigned int port;
    365   int all_cpus;
    366   unsigned int threads;
    367   int thread_per_conn;
    368   int epoll;
    369   int poll;
    370   int select;
    371   int empty;
    372   int tiny;
    373   int medium;
    374   int large;
    375   int xlarge;
    376   int jumbo;
    377   int shared;
    378   int single;
    379   int unique;
    380   unsigned int connections;
    381   unsigned int timeout;
    382   int date_header;
    383   int help;
    384   int version;
    385 };
    386 
    387 static struct PerfRepl_parameters tool_params = {
    388   0,
    389   0,
    390   0,
    391   0,
    392   0,
    393   0,
    394   0,
    395   0,
    396   0,
    397   0,
    398   0,
    399   0,
    400   0,
    401   0,
    402   0,
    403   0,
    404   0,
    405   0,
    406   0,
    407   0,
    408   0
    409 };
    410 
    411 
    412 static enum PerfRepl_param_result
    413 process_param__all_cpus (const char *param_name)
    414 {
    415   if (0 != tool_params.threads)
    416   {
    417     fprintf (stderr, "Parameter '%s' cannot be used together "
    418              "with '-t' or '--threads'.\n", param_name);
    419     return PERF_RPL_PARAM_ERROR;
    420   }
    421   if (tool_params.thread_per_conn)
    422   {
    423     fprintf (stderr, "Parameter '%s' cannot be used together "
    424              "with '-P' or '--thread-per-conn'.\n", param_name);
    425     return PERF_RPL_PARAM_ERROR;
    426   }
    427   tool_params.all_cpus = ! 0;
    428   return '-' == param_name[1] ?
    429          PERF_RPL_PARAM_FULL_STR :PERF_RPL_PARAM_ONE_CHAR;
    430 }
    431 
    432 
    433 /**
    434  * Process parameter '-t' or '--threads'
    435  * @param param_name the name of the parameter as specified in command line
    436  * @param param_tail the pointer to the character after parameter name in
    437  *                   the parameter string
    438  * @param next_param the pointer to the next parameter (if any) or NULL
    439  * @return enum value, the PERF_PERPL_SPARAM_ONE_CHAR is not used by
    440  *                     this function
    441  */
    442 static enum PerfRepl_param_result
    443 process_param__threads (const char *param_name, const char *param_tail,
    444                         const char *next_param)
    445 {
    446   unsigned int param_value;
    447   enum PerfRepl_param_result value_res;
    448 
    449   if (tool_params.all_cpus)
    450   {
    451     fprintf (stderr, "Parameter '%s' cannot be used together "
    452              "with '-A' or '--all-cpus'.\n", param_name);
    453     return PERF_RPL_PARAM_ERROR;
    454   }
    455   if (tool_params.thread_per_conn)
    456   {
    457     fprintf (stderr, "Parameter '%s' cannot be used together "
    458              "with '-P' or '--thread-per-conn'.\n", param_name);
    459     return PERF_RPL_PARAM_ERROR;
    460   }
    461   value_res = get_param_value (param_name, param_tail, next_param,
    462                                &param_value);
    463   if (PERF_RPL_PARAM_ERROR == value_res)
    464     return value_res;
    465 
    466   if (0 == param_value)
    467   {
    468     fprintf (stderr, "'0' is not valid value for parameter '%s'.\n",
    469              param_name);
    470     return PERF_RPL_PARAM_ERROR;
    471   }
    472   tool_params.threads = param_value;
    473   return value_res;
    474 }
    475 
    476 
    477 static enum PerfRepl_param_result
    478 process_param__thread_per_conn (const char *param_name)
    479 {
    480   if (tool_params.all_cpus)
    481   {
    482     fprintf (stderr, "Parameter '%s' cannot be used together "
    483              "with '-A' or '--all-cpus'.\n", param_name);
    484     return PERF_RPL_PARAM_ERROR;
    485   }
    486   if (0 != tool_params.threads)
    487   {
    488     fprintf (stderr, "Parameter '%s' cannot be used together "
    489              "with '-t' or '--threads'.\n", param_name);
    490     return PERF_RPL_PARAM_ERROR;
    491   }
    492   tool_params.thread_per_conn = ! 0;
    493   return '-' == param_name[1] ?
    494          PERF_RPL_PARAM_FULL_STR :PERF_RPL_PARAM_ONE_CHAR;
    495 }
    496 
    497 
    498 static enum PerfRepl_param_result
    499 process_param__epoll (const char *param_name)
    500 {
    501   if (tool_params.poll)
    502   {
    503     fprintf (stderr, "Parameter '%s' cannot be used together "
    504              "with '-p' or '--poll'.\n", param_name);
    505     return PERF_RPL_PARAM_ERROR;
    506   }
    507   if (tool_params.select)
    508   {
    509     fprintf (stderr, "Parameter '%s' cannot be used together "
    510              "with '-s' or '--select'.\n", param_name);
    511     return PERF_RPL_PARAM_ERROR;
    512   }
    513   tool_params.epoll = ! 0;
    514   return '-' == param_name[1] ?
    515          PERF_RPL_PARAM_FULL_STR :PERF_RPL_PARAM_ONE_CHAR;
    516 }
    517 
    518 
    519 static enum PerfRepl_param_result
    520 process_param__poll (const char *param_name)
    521 {
    522   if (tool_params.epoll)
    523   {
    524     fprintf (stderr, "Parameter '%s' cannot be used together "
    525              "with '-e' or '--epoll'.\n", param_name);
    526     return PERF_RPL_PARAM_ERROR;
    527   }
    528   if (tool_params.select)
    529   {
    530     fprintf (stderr, "Parameter '%s' cannot be used together "
    531              "with '-s' or '--select'.\n", param_name);
    532     return PERF_RPL_PARAM_ERROR;
    533   }
    534   tool_params.poll = ! 0;
    535   return '-' == param_name[1] ?
    536          PERF_RPL_PARAM_FULL_STR :PERF_RPL_PARAM_ONE_CHAR;
    537 }
    538 
    539 
    540 static enum PerfRepl_param_result
    541 process_param__select (const char *param_name)
    542 {
    543   if (tool_params.epoll)
    544   {
    545     fprintf (stderr, "Parameter '%s' cannot be used together "
    546              "with '-e' or '--epoll'.\n", param_name);
    547     return PERF_RPL_PARAM_ERROR;
    548   }
    549   if (tool_params.poll)
    550   {
    551     fprintf (stderr, "Parameter '%s' cannot be used together "
    552              "with '-p' or '--poll'.\n", param_name);
    553     return PERF_RPL_PARAM_ERROR;
    554   }
    555   tool_params.select = ! 0;
    556   return '-' == param_name[1] ?
    557          PERF_RPL_PARAM_FULL_STR :PERF_RPL_PARAM_ONE_CHAR;
    558 }
    559 
    560 
    561 static enum PerfRepl_param_result
    562 process_param__empty (const char *param_name)
    563 {
    564   if (tool_params.tiny)
    565   {
    566     fprintf (stderr, "Parameter '%s' cannot be used together "
    567              "with '-T' or '--tiny'.\n", param_name);
    568     return PERF_RPL_PARAM_ERROR;
    569   }
    570   if (tool_params.medium)
    571   {
    572     fprintf (stderr, "Parameter '%s' cannot be used together "
    573              "with '-M' or '--medium'.\n", param_name);
    574     return PERF_RPL_PARAM_ERROR;
    575   }
    576   if (tool_params.large)
    577   {
    578     fprintf (stderr, "Parameter '%s' cannot be used together "
    579              "with '-L' or '--large'.\n", param_name);
    580     return PERF_RPL_PARAM_ERROR;
    581   }
    582   if (tool_params.xlarge)
    583   {
    584     fprintf (stderr, "Parameter '%s' cannot be used together "
    585              "with '-X' or '--xlarge'.\n", param_name);
    586     return PERF_RPL_PARAM_ERROR;
    587   }
    588   if (tool_params.jumbo)
    589   {
    590     fprintf (stderr, "Parameter '%s' cannot be used together "
    591              "with '-J' or '--jumbo'.\n", param_name);
    592     return PERF_RPL_PARAM_ERROR;
    593   }
    594   tool_params.empty = ! 0;
    595   return '-' == param_name[1] ?
    596          PERF_RPL_PARAM_FULL_STR :PERF_RPL_PARAM_ONE_CHAR;
    597 }
    598 
    599 
    600 static enum PerfRepl_param_result
    601 process_param__tiny (const char *param_name)
    602 {
    603   if (tool_params.empty)
    604   {
    605     fprintf (stderr, "Parameter '%s' cannot be used together "
    606              "with '-E' or '--empty'.\n", param_name);
    607     return PERF_RPL_PARAM_ERROR;
    608   }
    609   if (tool_params.medium)
    610   {
    611     fprintf (stderr, "Parameter '%s' cannot be used together "
    612              "with '-M' or '--medium'.\n", param_name);
    613     return PERF_RPL_PARAM_ERROR;
    614   }
    615   if (tool_params.large)
    616   {
    617     fprintf (stderr, "Parameter '%s' cannot be used together "
    618              "with '-L' or '--large'.\n", param_name);
    619     return PERF_RPL_PARAM_ERROR;
    620   }
    621   if (tool_params.xlarge)
    622   {
    623     fprintf (stderr, "Parameter '%s' cannot be used together "
    624              "with '-X' or '--xlarge'.\n", param_name);
    625     return PERF_RPL_PARAM_ERROR;
    626   }
    627   if (tool_params.jumbo)
    628   {
    629     fprintf (stderr, "Parameter '%s' cannot be used together "
    630              "with '-J' or '--jumbo'.\n", param_name);
    631     return PERF_RPL_PARAM_ERROR;
    632   }
    633   tool_params.tiny = ! 0;
    634   return '-' == param_name[1] ?
    635          PERF_RPL_PARAM_FULL_STR :PERF_RPL_PARAM_ONE_CHAR;
    636 }
    637 
    638 
    639 static enum PerfRepl_param_result
    640 process_param__medium (const char *param_name)
    641 {
    642   if (tool_params.empty)
    643   {
    644     fprintf (stderr, "Parameter '%s' cannot be used together "
    645              "with '-E' or '--empty'.\n", param_name);
    646     return PERF_RPL_PARAM_ERROR;
    647   }
    648   if (tool_params.tiny)
    649   {
    650     fprintf (stderr, "Parameter '%s' cannot be used together "
    651              "with '-T' or '--tiny'.\n", param_name);
    652     return PERF_RPL_PARAM_ERROR;
    653   }
    654   if (tool_params.large)
    655   {
    656     fprintf (stderr, "Parameter '%s' cannot be used together "
    657              "with '-L' or '--large'.\n", param_name);
    658     return PERF_RPL_PARAM_ERROR;
    659   }
    660   if (tool_params.xlarge)
    661   {
    662     fprintf (stderr, "Parameter '%s' cannot be used together "
    663              "with '-X' or '--xlarge'.\n", param_name);
    664     return PERF_RPL_PARAM_ERROR;
    665   }
    666   if (tool_params.jumbo)
    667   {
    668     fprintf (stderr, "Parameter '%s' cannot be used together "
    669              "with '-J' or '--jumbo'.\n", param_name);
    670     return PERF_RPL_PARAM_ERROR;
    671   }
    672   tool_params.medium = ! 0;
    673   return '-' == param_name[1] ?
    674          PERF_RPL_PARAM_FULL_STR :PERF_RPL_PARAM_ONE_CHAR;
    675 }
    676 
    677 
    678 static enum PerfRepl_param_result
    679 process_param__large (const char *param_name)
    680 {
    681   if (tool_params.empty)
    682   {
    683     fprintf (stderr, "Parameter '%s' cannot be used together "
    684              "with '-E' or '--empty'.\n", param_name);
    685     return PERF_RPL_PARAM_ERROR;
    686   }
    687   if (tool_params.tiny)
    688   {
    689     fprintf (stderr, "Parameter '%s' cannot be used together "
    690              "with '-T' or '--tiny'.\n", param_name);
    691     return PERF_RPL_PARAM_ERROR;
    692   }
    693   if (tool_params.medium)
    694   {
    695     fprintf (stderr, "Parameter '%s' cannot be used together "
    696              "with '-M' or '--medium'.\n", param_name);
    697     return PERF_RPL_PARAM_ERROR;
    698   }
    699   if (tool_params.xlarge)
    700   {
    701     fprintf (stderr, "Parameter '%s' cannot be used together "
    702              "with '-X' or '--xlarge'.\n", param_name);
    703     return PERF_RPL_PARAM_ERROR;
    704   }
    705   if (tool_params.jumbo)
    706   {
    707     fprintf (stderr, "Parameter '%s' cannot be used together "
    708              "with '-J' or '--jumbo'.\n", param_name);
    709     return PERF_RPL_PARAM_ERROR;
    710   }
    711   tool_params.large = ! 0;
    712   return '-' == param_name[1] ?
    713          PERF_RPL_PARAM_FULL_STR :PERF_RPL_PARAM_ONE_CHAR;
    714 }
    715 
    716 
    717 static enum PerfRepl_param_result
    718 process_param__xlarge (const char *param_name)
    719 {
    720   if (tool_params.empty)
    721   {
    722     fprintf (stderr, "Parameter '%s' cannot be used together "
    723              "with '-E' or '--empty'.\n", param_name);
    724     return PERF_RPL_PARAM_ERROR;
    725   }
    726   if (tool_params.tiny)
    727   {
    728     fprintf (stderr, "Parameter '%s' cannot be used together "
    729              "with '-T' or '--tiny'.\n", param_name);
    730     return PERF_RPL_PARAM_ERROR;
    731   }
    732   if (tool_params.medium)
    733   {
    734     fprintf (stderr, "Parameter '%s' cannot be used together "
    735              "with '-M' or '--medium'.\n", param_name);
    736     return PERF_RPL_PARAM_ERROR;
    737   }
    738   if (tool_params.large)
    739   {
    740     fprintf (stderr, "Parameter '%s' cannot be used together "
    741              "with '-L' or '--large'.\n", param_name);
    742     return PERF_RPL_PARAM_ERROR;
    743   }
    744   if (tool_params.jumbo)
    745   {
    746     fprintf (stderr, "Parameter '%s' cannot be used together "
    747              "with '-J' or '--jumbo'.\n", param_name);
    748     return PERF_RPL_PARAM_ERROR;
    749   }
    750   tool_params.xlarge = ! 0;
    751   return '-' == param_name[1] ?
    752          PERF_RPL_PARAM_FULL_STR :PERF_RPL_PARAM_ONE_CHAR;
    753 }
    754 
    755 
    756 static enum PerfRepl_param_result
    757 process_param__jumbo (const char *param_name)
    758 {
    759   if (tool_params.empty)
    760   {
    761     fprintf (stderr, "Parameter '%s' cannot be used together "
    762              "with '-E' or '--empty'.\n", param_name);
    763     return PERF_RPL_PARAM_ERROR;
    764   }
    765   if (tool_params.tiny)
    766   {
    767     fprintf (stderr, "Parameter '%s' cannot be used together "
    768              "with '-T' or '--tiny'.\n", param_name);
    769     return PERF_RPL_PARAM_ERROR;
    770   }
    771   if (tool_params.medium)
    772   {
    773     fprintf (stderr, "Parameter '%s' cannot be used together "
    774              "with '-M' or '--medium'.\n", param_name);
    775     return PERF_RPL_PARAM_ERROR;
    776   }
    777   if (tool_params.large)
    778   {
    779     fprintf (stderr, "Parameter '%s' cannot be used together "
    780              "with '-L' or '--large'.\n", param_name);
    781     return PERF_RPL_PARAM_ERROR;
    782   }
    783   if (tool_params.xlarge)
    784   {
    785     fprintf (stderr, "Parameter '%s' cannot be used together "
    786              "with '-X' or '--xlarge'.\n", param_name);
    787     return PERF_RPL_PARAM_ERROR;
    788   }
    789   tool_params.jumbo = ! 0;
    790   return '-' == param_name[1] ?
    791          PERF_RPL_PARAM_FULL_STR :PERF_RPL_PARAM_ONE_CHAR;
    792 }
    793 
    794 
    795 static enum PerfRepl_param_result
    796 process_param__shared (const char *param_name)
    797 {
    798   if (tool_params.single)
    799   {
    800     fprintf (stderr, "Parameter '%s' cannot be used together "
    801              "with '-I' or '--single'.\n", param_name);
    802     return PERF_RPL_PARAM_ERROR;
    803   }
    804   if (tool_params.unique)
    805   {
    806     fprintf (stderr, "Parameter '%s' cannot be used together "
    807              "with '-U' or '--unique'.\n", param_name);
    808     return PERF_RPL_PARAM_ERROR;
    809   }
    810   tool_params.shared = ! 0;
    811   return '-' == param_name[1] ?
    812          PERF_RPL_PARAM_FULL_STR :PERF_RPL_PARAM_ONE_CHAR;
    813 }
    814 
    815 
    816 static enum PerfRepl_param_result
    817 process_param__single (const char *param_name)
    818 {
    819   if (tool_params.shared)
    820   {
    821     fprintf (stderr, "Parameter '%s' cannot be used together "
    822              "with '-S' or '--shared'.\n", param_name);
    823     return PERF_RPL_PARAM_ERROR;
    824   }
    825   if (tool_params.unique)
    826   {
    827     fprintf (stderr, "Parameter '%s' cannot be used together "
    828              "with '-U' or '--unique'.\n", param_name);
    829     return PERF_RPL_PARAM_ERROR;
    830   }
    831   tool_params.single = ! 0;
    832   return '-' == param_name[1] ?
    833          PERF_RPL_PARAM_FULL_STR :PERF_RPL_PARAM_ONE_CHAR;
    834 }
    835 
    836 
    837 static enum PerfRepl_param_result
    838 process_param__unique (const char *param_name)
    839 {
    840   if (tool_params.shared)
    841   {
    842     fprintf (stderr, "Parameter '%s' cannot be used together "
    843              "with '-S' or '--shared'.\n", param_name);
    844     return PERF_RPL_PARAM_ERROR;
    845   }
    846   if (tool_params.single)
    847   {
    848     fprintf (stderr, "Parameter '%s' cannot be used together "
    849              "with '-I' or '--single'.\n", param_name);
    850     return PERF_RPL_PARAM_ERROR;
    851   }
    852   tool_params.unique = ! 0;
    853   return '-' == param_name[1] ?
    854          PERF_RPL_PARAM_FULL_STR :PERF_RPL_PARAM_ONE_CHAR;
    855 }
    856 
    857 
    858 /**
    859  * Process parameter '-c' or '--connections'
    860  * @param param_name the name of the parameter as specified in command line
    861  * @param param_tail the pointer to the character after parameter name in
    862  *                   the parameter string
    863  * @param next_param the pointer to the next parameter (if any) or NULL
    864  * @return enum value, the PERF_PERPL_SPARAM_ONE_CHAR is not used by
    865  *                     this function
    866  */
    867 static enum PerfRepl_param_result
    868 process_param__connections (const char *param_name, const char *param_tail,
    869                             const char *next_param)
    870 {
    871   unsigned int param_value;
    872   enum PerfRepl_param_result value_res;
    873 
    874   value_res = get_param_value (param_name, param_tail, next_param,
    875                                &param_value);
    876   if (PERF_RPL_PARAM_ERROR == value_res)
    877     return value_res;
    878 
    879   if (0 == param_value)
    880   {
    881     fprintf (stderr, "'0' is not valid value for parameter '%s'.\n",
    882              param_name);
    883     return PERF_RPL_PARAM_ERROR;
    884   }
    885   tool_params.connections = param_value;
    886   return value_res;
    887 }
    888 
    889 
    890 /**
    891  * Process parameter '-O' or '--timeout'
    892  * @param param_name the name of the parameter as specified in command line
    893  * @param param_tail the pointer to the character after parameter name in
    894  *                   the parameter string
    895  * @param next_param the pointer to the next parameter (if any) or NULL
    896  * @return enum value, the PERF_PERPL_SPARAM_ONE_CHAR is not used by
    897  *                     this function
    898  */
    899 static enum PerfRepl_param_result
    900 process_param__timeout (const char *param_name, const char *param_tail,
    901                         const char *next_param)
    902 {
    903   unsigned int param_value;
    904   enum PerfRepl_param_result value_res;
    905 
    906   value_res = get_param_value (param_name, param_tail, next_param,
    907                                &param_value);
    908   if (PERF_RPL_PARAM_ERROR == value_res)
    909     return value_res;
    910 
    911   tool_params.timeout = param_value;
    912   return value_res;
    913 }
    914 
    915 
    916 static enum PerfRepl_param_result
    917 process_param__date_header (const char *param_name)
    918 {
    919   tool_params.date_header = ! 0;
    920   return '-' == param_name[1] ?
    921          PERF_RPL_PARAM_FULL_STR :PERF_RPL_PARAM_ONE_CHAR;
    922 }
    923 
    924 
    925 static enum PerfRepl_param_result
    926 process_param__help (const char *param_name)
    927 {
    928   /* Use only one of help | version */
    929   if (! tool_params.version)
    930     tool_params.help = ! 0;
    931   return '-' == param_name[1] ?
    932          PERF_RPL_PARAM_FULL_STR :PERF_RPL_PARAM_ONE_CHAR;
    933 }
    934 
    935 
    936 static enum PerfRepl_param_result
    937 process_param__version (const char *param_name)
    938 {
    939   /* Use only one of help | version */
    940   if (! tool_params.help)
    941     tool_params.version = ! 0;
    942   return '-' == param_name[1] ?
    943          PERF_RPL_PARAM_FULL_STR :PERF_RPL_PARAM_ONE_CHAR;
    944 }
    945 
    946 
    947 /**
    948  * Process "short" (one character) parameter.
    949  * @param param the pointer to character after "-" or after another valid
    950  *              parameter
    951  * @param next_param the pointer to the next parameter (if any) or
    952  *                   NULL if no next parameter
    953  * @return enum value with result
    954  */
    955 static enum PerfRepl_param_result
    956 process_short_param (const char *param, const char *next_param)
    957 {
    958   const char param_chr = param[0];
    959   if ('A' == param_chr)
    960     return process_param__all_cpus ("-A");
    961   else if ('t' == param_chr)
    962     return process_param__threads ("-t", param + 1, next_param);
    963   else if ('P' == param_chr)
    964     return process_param__thread_per_conn ("-P");
    965   else if ('e' == param_chr)
    966     return process_param__epoll ("-e");
    967   else if ('p' == param_chr)
    968     return process_param__poll ("-p");
    969   else if ('s' == param_chr)
    970     return process_param__select ("-s");
    971   else if ('E' == param_chr)
    972     return process_param__empty ("-E");
    973   else if ('T' == param_chr)
    974     return process_param__tiny ("-T");
    975   else if ('M' == param_chr)
    976     return process_param__medium ("-M");
    977   else if ('L' == param_chr)
    978     return process_param__large ("-L");
    979   else if ('X' == param_chr)
    980     return process_param__xlarge ("-X");
    981   else if ('J' == param_chr)
    982     return process_param__jumbo ("-J");
    983   else if ('S' == param_chr)
    984     return process_param__shared ("-S");
    985   else if ('I' == param_chr)
    986     return process_param__single ("-I");
    987   else if ('U' == param_chr)
    988     return process_param__unique ("-U");
    989   else if ('c' == param_chr)
    990     return process_param__connections ("-c", param + 1, next_param);
    991   else if ('O' == param_chr)
    992     return process_param__timeout ("-O", param + 1, next_param);
    993   else if ('V' == param_chr)
    994     return process_param__version ("-V");
    995 
    996   fprintf (stderr, "Unrecognised parameter: -%c.\n", param_chr);
    997   return PERF_RPL_PARAM_ERROR;
    998 }
    999 
   1000 
   1001 /**
   1002  * Process string of "short" (one character) parameters.
   1003  * @param params_str the pointer to first character after "-"
   1004  * @param next_param the pointer to the next parameter (if any) or
   1005  *                   NULL if no next parameter
   1006  * @return enum value with result
   1007  */
   1008 static enum PerfRepl_param_result
   1009 process_short_params_str (const char *params_str, const char *next_param)
   1010 {
   1011   if (0 == params_str[0])
   1012   {
   1013     fprintf (stderr, "Unrecognised parameter: -\n");
   1014     return PERF_RPL_PARAM_ERROR;
   1015   }
   1016   do
   1017   {
   1018     enum PerfRepl_param_result param_res;
   1019     param_res = process_short_param (params_str, next_param);
   1020     if (PERF_RPL_PARAM_ONE_CHAR != param_res)
   1021       return param_res;
   1022   } while (0 != (++params_str)[0]);
   1023   return PERF_RPL_PARAM_FULL_STR;
   1024 }
   1025 
   1026 
   1027 /**
   1028  * Process "long" (--something) parameters.
   1029  * @param param the pointer to first character after "--"
   1030  * @param next_param the pointer to the next parameter (if any) or
   1031  *                   NULL if no next parameter
   1032  * @return enum value, the PERF_PERPL_SPARAM_ONE_CHAR is not used by
   1033  *                     this function
   1034  */
   1035 static enum PerfRepl_param_result
   1036 process_long_param (const char *param, const char *next_param)
   1037 {
   1038   const size_t param_len = strlen (param);
   1039 
   1040   if ((MHD_STATICSTR_LEN_ ("all-cpus") == param_len) &&
   1041       (0 == memcmp (param, "all-cpus", MHD_STATICSTR_LEN_ ("all-cpus"))))
   1042     return process_param__all_cpus ("--all-cpus");
   1043   else if ((MHD_STATICSTR_LEN_ ("threads") <= param_len) &&
   1044            (0 == memcmp (param, "threads", MHD_STATICSTR_LEN_ ("threads"))))
   1045     return process_param__threads ("--threads",
   1046                                    param + MHD_STATICSTR_LEN_ ("threads"),
   1047                                    next_param);
   1048   else if ((MHD_STATICSTR_LEN_ ("thread-per-conn") == param_len) &&
   1049            (0 == memcmp (param, "thread-per-conn",
   1050                          MHD_STATICSTR_LEN_ ("thread-per-conn"))))
   1051     return process_param__thread_per_conn ("--thread-per-conn");
   1052   else if ((MHD_STATICSTR_LEN_ ("epoll") == param_len) &&
   1053            (0 == memcmp (param, "epoll", MHD_STATICSTR_LEN_ ("epoll"))))
   1054     return process_param__epoll ("--epoll");
   1055   else if ((MHD_STATICSTR_LEN_ ("poll") == param_len) &&
   1056            (0 == memcmp (param, "poll", MHD_STATICSTR_LEN_ ("poll"))))
   1057     return process_param__poll ("--poll");
   1058   else if ((MHD_STATICSTR_LEN_ ("select") == param_len) &&
   1059            (0 == memcmp (param, "select", MHD_STATICSTR_LEN_ ("select"))))
   1060     return process_param__select ("--select");
   1061   else if ((MHD_STATICSTR_LEN_ ("empty") == param_len) &&
   1062            (0 == memcmp (param, "empty", MHD_STATICSTR_LEN_ ("empty"))))
   1063     return process_param__empty ("--empty");
   1064   else if ((MHD_STATICSTR_LEN_ ("tiny") == param_len) &&
   1065            (0 == memcmp (param, "tiny", MHD_STATICSTR_LEN_ ("tiny"))))
   1066     return process_param__tiny ("--tiny");
   1067   else if ((MHD_STATICSTR_LEN_ ("medium") == param_len) &&
   1068            (0 == memcmp (param, "medium", MHD_STATICSTR_LEN_ ("medium"))))
   1069     return process_param__medium ("--medium");
   1070   else if ((MHD_STATICSTR_LEN_ ("large") == param_len) &&
   1071            (0 == memcmp (param, "large", MHD_STATICSTR_LEN_ ("large"))))
   1072     return process_param__large ("--large");
   1073   else if ((MHD_STATICSTR_LEN_ ("xlarge") == param_len) &&
   1074            (0 == memcmp (param, "xlarge", MHD_STATICSTR_LEN_ ("xlarge"))))
   1075     return process_param__xlarge ("--xlarge");
   1076   else if ((MHD_STATICSTR_LEN_ ("jumbo") == param_len) &&
   1077            (0 == memcmp (param, "jumbo", MHD_STATICSTR_LEN_ ("jumbo"))))
   1078     return process_param__jumbo ("--jumbo");
   1079   else if ((MHD_STATICSTR_LEN_ ("shared") == param_len) &&
   1080            (0 == memcmp (param, "shared", MHD_STATICSTR_LEN_ ("shared"))))
   1081     return process_param__shared ("--shared");
   1082   else if ((MHD_STATICSTR_LEN_ ("single") == param_len) &&
   1083            (0 == memcmp (param, "single", MHD_STATICSTR_LEN_ ("single"))))
   1084     return process_param__single ("--single");
   1085   else if ((MHD_STATICSTR_LEN_ ("unique") == param_len) &&
   1086            (0 == memcmp (param, "unique", MHD_STATICSTR_LEN_ ("unique"))))
   1087     return process_param__unique ("--unique");
   1088   else if ((MHD_STATICSTR_LEN_ ("connections") <= param_len) &&
   1089            (0 == memcmp (param, "connections",
   1090                          MHD_STATICSTR_LEN_ ("connections"))))
   1091     return process_param__connections ("--connections",
   1092                                        param
   1093                                        + MHD_STATICSTR_LEN_ ("connections"),
   1094                                        next_param);
   1095   else if ((MHD_STATICSTR_LEN_ ("timeout") <= param_len) &&
   1096            (0 == memcmp (param, "timeout",
   1097                          MHD_STATICSTR_LEN_ ("timeout"))))
   1098     return process_param__timeout ("--timeout",
   1099                                    param + MHD_STATICSTR_LEN_ ("timeout"),
   1100                                    next_param);
   1101   else if ((MHD_STATICSTR_LEN_ ("date-header") == param_len) &&
   1102            (0 == memcmp (param, "date-header",
   1103                          MHD_STATICSTR_LEN_ ("date-header"))))
   1104     return process_param__date_header ("--date-header");
   1105   else if ((MHD_STATICSTR_LEN_ ("help") == param_len) &&
   1106            (0 == memcmp (param, "help", MHD_STATICSTR_LEN_ ("help"))))
   1107     return process_param__help ("--help");
   1108   else if ((MHD_STATICSTR_LEN_ ("version") == param_len) &&
   1109            (0 == memcmp (param, "version", MHD_STATICSTR_LEN_ ("version"))))
   1110     return process_param__version ("--version");
   1111 
   1112   fprintf (stderr, "Unrecognised parameter: --%s.\n", param);
   1113   return PERF_RPL_PARAM_ERROR;
   1114 }
   1115 
   1116 
   1117 static int
   1118 process_params (int argc, char *const *argv)
   1119 {
   1120   int proc_dash_param = ! 0;
   1121   int i;
   1122   for (i = 1; i < argc; ++i)
   1123   {
   1124     /**
   1125      * The currently processed argument
   1126      */
   1127     const char *const p = argv[i];
   1128     const char *const p_next = (argc == (i + 1)) ? NULL : (argv[i + 1]);
   1129     if (NULL == p)
   1130     {
   1131       fprintf (stderr, "The NULL in the parameter number %d. "
   1132                "The error in the C library?\n", i);
   1133       continue;
   1134     }
   1135     else if (0 == p[0])
   1136       continue; /* Empty */
   1137     else if (proc_dash_param && ('-' == p[0]))
   1138     {
   1139       enum PerfRepl_param_result param_res;
   1140       if ('-' == p[1])
   1141       {
   1142         if (0 == p[2])
   1143         {
   1144           proc_dash_param = 0; /* The '--' parameter */
   1145           continue;
   1146         }
   1147         param_res = process_long_param (p + 2, p_next);
   1148       }
   1149       else
   1150         param_res = process_short_params_str (p + 1, p_next);
   1151 
   1152       if (PERF_RPL_PARAM_ERROR == param_res)
   1153         return PERF_RPL_ERR_CODE_BAD_PARAM;
   1154       if (PERF_RPL_PARAM_STR_PLUS_NEXT == param_res)
   1155         ++i;
   1156       else if (PERF_RPL_PARAM_ONE_CHAR == param_res)
   1157         abort ();
   1158       continue;
   1159     }
   1160     else if (('0' <= p[0]) && ('9' >= p[0]))
   1161     {
   1162       /* Process the port number */
   1163       unsigned int read_port;
   1164       size_t num_digits;
   1165       num_digits = mhd_tool_str_to_uint (p, &read_port);
   1166       if (0 != p[num_digits])
   1167       {
   1168         fprintf (stderr, "Error in specified port number: %s\n", p);
   1169         return PERF_RPL_ERR_CODE_BAD_PARAM;
   1170       }
   1171       else if (65535 < read_port)
   1172       {
   1173         fprintf (stderr, "Wrong port number: %s\n", p);
   1174         return PERF_RPL_ERR_CODE_BAD_PARAM;
   1175       }
   1176       mhd_port = (uint16_t) read_port;
   1177     }
   1178     else
   1179     {
   1180       fprintf (stderr, "Unrecognised parameter: %s\n\n", p);
   1181       return PERF_RPL_ERR_CODE_BAD_PARAM;
   1182     }
   1183   }
   1184   return 0;
   1185 }
   1186 
   1187 
   1188 static void
   1189 print_version (void)
   1190 {
   1191   printf ("%s (GNU libmicrohttpd", self_name);
   1192   if (0 != build_revision[0])
   1193     printf ("; %s", build_revision);
   1194   printf (") %s\n", MHD_get_version ());
   1195   printf ("%s\n", tool_copyright);
   1196 }
   1197 
   1198 
   1199 static void
   1200 print_all_cores_used (void)
   1201 {
   1202   printf ("No CPU cores on this machine are left unused and available "
   1203           "for the client / requests generator. "
   1204           "Testing with remote client is recommended.\n");
   1205 }
   1206 
   1207 
   1208 static void
   1209 check_param_port (void)
   1210 {
   1211   if (0 != tool_params.port)
   1212     return;
   1213   if (MHD_NO == MHD_is_feature_supported (MHD_FEATURE_AUTODETECT_BIND_PORT))
   1214     tool_params.port = PERF_REPL_PORT_FALLBACK;
   1215 }
   1216 
   1217 
   1218 /**
   1219  * Apply parameter '-A' or '--all-cpus'
   1220  */
   1221 static void
   1222 check_apply_param__all_cpus (void)
   1223 {
   1224   if (! tool_params.all_cpus)
   1225     return;
   1226 
   1227   num_threads = get_process_cpu_core_count ();
   1228   printf ("Requested use of all available CPU cores for MHD threads.\n");
   1229   if (get_cpu_core_count () == num_threads)
   1230     print_all_cores_used ();
   1231 }
   1232 
   1233 
   1234 /**
   1235  * Apply parameter '-t' or '--threads'
   1236  */
   1237 static void
   1238 check_apply_param__threads (void)
   1239 {
   1240   if (0 == tool_params.threads)
   1241     return;
   1242 
   1243   num_threads = tool_params.threads;
   1244 
   1245   if (get_process_cpu_core_count () < num_threads)
   1246   {
   1247     fprintf (stderr, "WARNING: The requested number of threads (%u) is "
   1248              "higher than the number of detected available CPU cores (%u).\n",
   1249              num_threads, get_process_cpu_core_count ());
   1250     fprintf (stderr, "This decreases the performance. "
   1251              "Consider using fewer threads.\n");
   1252   }
   1253   if (get_cpu_core_count () == num_threads)
   1254   {
   1255     printf ("The requested number of threads is equal to the number of "
   1256             "detected CPU cores.\n");
   1257     print_all_cores_used ();
   1258   }
   1259 }
   1260 
   1261 
   1262 /**
   1263  * Apply parameter '-P' or '--thread-per-conn'
   1264  * @return non-zero - OK, zero - error
   1265  */
   1266 static int
   1267 check_apply_param__thread_per_conn (void)
   1268 {
   1269   if (! tool_params.thread_per_conn)
   1270     return ! 0;
   1271 
   1272   if (tool_params.epoll)
   1273   {
   1274     fprintf (stderr, "'Thread-per-connection' mode cannot be used together "
   1275              "with 'epoll'.\n");
   1276     return 0;
   1277   }
   1278   num_threads = 1;
   1279 
   1280   return ! 0;
   1281 }
   1282 
   1283 
   1284 /* non-zero - OK, zero - error */
   1285 static int
   1286 check_param__epoll (void)
   1287 {
   1288   if (! tool_params.epoll)
   1289     return ! 0;
   1290   if (MHD_NO == MHD_is_feature_supported (MHD_FEATURE_EPOLL))
   1291   {
   1292     fprintf (stderr, "'epoll' was requested, but this MHD build does not "
   1293              "support 'epoll' functionality.\n");
   1294     return 0;
   1295   }
   1296   return ! 0;
   1297 }
   1298 
   1299 
   1300 /* non-zero - OK, zero - error */
   1301 static int
   1302 check_param__poll (void)
   1303 {
   1304   if (! tool_params.poll)
   1305     return ! 0;
   1306   if (MHD_NO == MHD_is_feature_supported (MHD_FEATURE_POLL))
   1307   {
   1308     fprintf (stderr, "poll() was requested, but this MHD build does not "
   1309              "support polling by poll().\n");
   1310     return 0;
   1311   }
   1312   return ! 0;
   1313 }
   1314 
   1315 
   1316 static void
   1317 check_param__empty_tiny_medium_large_xlarge_jumbo (void)
   1318 {
   1319   if (0 == (tool_params.empty | tool_params.tiny | tool_params.medium
   1320             | tool_params.large | tool_params.xlarge | tool_params.jumbo))
   1321     tool_params.tiny = ! 0;
   1322 }
   1323 
   1324 
   1325 static void
   1326 check_param__shared_single_unique (void)
   1327 {
   1328   if (0 == (tool_params.shared | tool_params.single | tool_params.unique))
   1329     tool_params.shared = ! 0;
   1330 }
   1331 
   1332 
   1333 /* Must be called after 'check_apply_param__threads()' and
   1334    'check_apply_param__all_cpus()' */
   1335 /* non-zero - OK, zero - error */
   1336 static int
   1337 check_param__connections (void)
   1338 {
   1339   if (0 == tool_params.connections)
   1340     return ! 0;
   1341   if (get_num_threads () > tool_params.connections)
   1342   {
   1343     fprintf (stderr, "The connections number limit (%u) is less than number "
   1344              "of threads used (%u). Use higher value for connections limit.\n",
   1345              tool_params.connections, get_num_threads ());
   1346     return 0;
   1347   }
   1348   return ! 0;
   1349 }
   1350 
   1351 
   1352 /**
   1353  * Apply decoded parameters
   1354  * @return 0 if success,
   1355  *         positive error code if case of error,
   1356  *         -1 to exit program with success (0) error code.
   1357  */
   1358 static int
   1359 check_apply_params (void)
   1360 {
   1361   if (tool_params.help)
   1362   {
   1363     show_help ();
   1364     return -1;
   1365   }
   1366   else if (tool_params.version)
   1367   {
   1368     print_version ();
   1369     return -1;
   1370   }
   1371   check_param_port ();
   1372   check_apply_param__all_cpus ();
   1373   check_apply_param__threads ();
   1374   if (! check_apply_param__thread_per_conn ())
   1375     return PERF_RPL_ERR_CODE_BAD_PARAM;
   1376   if (! check_param__epoll ())
   1377     return PERF_RPL_ERR_CODE_BAD_PARAM;
   1378   if (! check_param__poll ())
   1379     return PERF_RPL_ERR_CODE_BAD_PARAM;
   1380   check_param__empty_tiny_medium_large_xlarge_jumbo ();
   1381   check_param__shared_single_unique ();
   1382   if (! check_param__connections ())
   1383     return PERF_RPL_ERR_CODE_BAD_PARAM;
   1384   return 0;
   1385 }
   1386 
   1387 
   1388 static uint64_t
   1389 mini_rnd (void)
   1390 {
   1391   /* Simple xoshiro256+ implementation */
   1392   static uint64_t s[4] = {
   1393     0xE220A8397B1DCDAFuLL, 0x6E789E6AA1B965F4uLL,
   1394     0x06C45D188009454FuLL, 0xF88BB8A8724C81ECuLL
   1395   };     /* Good enough for static initialisation */
   1396 
   1397   const uint64_t ret = s[0] + s[3];
   1398   const uint64_t t = s[1] << 17;
   1399 
   1400   s[2] ^= s[0];
   1401   s[3] ^= s[1];
   1402   s[1] ^= s[2];
   1403   s[0] ^= s[3];
   1404   s[2] ^= t;
   1405   s[3] = ((s[3] << 45u) | (s[3] >> (64u - 45u)));
   1406 
   1407   return ret;
   1408 }
   1409 
   1410 
   1411 /* The pool of shared responses */
   1412 static struct MHD_Response **resps = NULL;
   1413 static unsigned int num_resps = 0;
   1414 /* The single response */
   1415 static struct MHD_Response *resp_single = NULL;
   1416 
   1417 /* Use the same memory area to avoid multiple copies.
   1418    The system will keep it in cache. */
   1419 static const char tiny_body[] = "Hi!";
   1420 static char *body_dyn = NULL; /* Non-static body data */
   1421 static size_t body_dyn_size;
   1422 
   1423 /* Non-zero - success, zero - failure */
   1424 static int
   1425 init_response_body_data (void)
   1426 {
   1427   if (0 != body_dyn_size)
   1428   {
   1429     body_dyn = (char *) malloc (body_dyn_size);
   1430     if (NULL == body_dyn)
   1431     {
   1432       fprintf (stderr, "Failed to allocate memory.\n");
   1433       return 0;
   1434     }
   1435     if (16u * 1024u >= body_dyn_size)
   1436     {
   1437       /* Fill the body with HTML-like content */
   1438       size_t pos;
   1439       size_t filler_pos;
   1440       static const char body_header[] =
   1441         "<html>\n"
   1442         "<head>\n<title>Sample page title</title>\n<head>\n"
   1443         "<body>\n";
   1444       static const char body_filler[] =
   1445         "The quick brown fox jumps over the lazy dog.<br>\n";
   1446       static const char body_footer[] =
   1447         "</body>\n"
   1448         "</html>\n";
   1449       pos = 0;
   1450       memcpy (body_dyn + pos, body_header, MHD_STATICSTR_LEN_ (body_header));
   1451       pos += MHD_STATICSTR_LEN_ (body_header);
   1452       for (filler_pos = 0;
   1453            filler_pos < (body_dyn_size - (MHD_STATICSTR_LEN_ (body_header)
   1454                                           + MHD_STATICSTR_LEN_ (body_footer)));
   1455            ++filler_pos)
   1456       {
   1457         body_dyn[pos + filler_pos] =
   1458           body_filler[filler_pos % MHD_STATICSTR_LEN_ (body_filler)];
   1459       }
   1460       pos += filler_pos;
   1461       memcpy (body_dyn + pos, body_footer, MHD_STATICSTR_LEN_ (body_footer));
   1462     }
   1463     else if (2u * 1024u * 1024u >= body_dyn_size)
   1464     {
   1465       /* Fill the body with binary-like content */
   1466       size_t pos;
   1467       for (pos = 0; pos < body_dyn_size; ++pos)
   1468       {
   1469         body_dyn[pos] = (char) (unsigned char) (255U - pos % 256U);
   1470       }
   1471     }
   1472     else
   1473     {
   1474       /* Fill the body with pseudo-random binary-like content */
   1475       size_t pos;
   1476       uint64_t rnd_data;
   1477       for (pos = 0; pos < body_dyn_size - body_dyn_size % 8;
   1478            pos += 8)
   1479       {
   1480         rnd_data = mini_rnd ();
   1481         body_dyn[pos + 0] = (char) (unsigned char) (rnd_data >> 0);
   1482         body_dyn[pos + 1] = (char) (unsigned char) (rnd_data >> 8);
   1483         body_dyn[pos + 2] = (char) (unsigned char) (rnd_data >> 16);
   1484         body_dyn[pos + 3] = (char) (unsigned char) (rnd_data >> 24);
   1485         body_dyn[pos + 4] = (char) (unsigned char) (rnd_data >> 32);
   1486         body_dyn[pos + 5] = (char) (unsigned char) (rnd_data >> 40);
   1487         body_dyn[pos + 6] = (char) (unsigned char) (rnd_data >> 48);
   1488         body_dyn[pos + 7] = (char) (unsigned char) (rnd_data >> 56);
   1489       }
   1490       rnd_data = mini_rnd ();
   1491       for ((void) pos; pos < body_dyn_size; ++pos)
   1492       {
   1493         body_dyn[pos] = (char) (unsigned char) (rnd_data);
   1494         rnd_data >>= 8u;
   1495       }
   1496     }
   1497   }
   1498   return ! 0;
   1499 }
   1500 
   1501 
   1502 static struct MHD_Response *
   1503 create_response_object (void)
   1504 {
   1505 #if MHD_VERSION >= 0x00097701
   1506   if (NULL != body_dyn)
   1507     return MHD_create_response_from_buffer_static (body_dyn_size,
   1508                                                    body_dyn);
   1509   else if (tool_params.empty)
   1510     return MHD_create_response_empty (MHD_RF_NONE);
   1511 
   1512   return MHD_create_response_from_buffer_static (MHD_STATICSTR_LEN_ (tiny_body),
   1513                                                  tiny_body);
   1514 
   1515 #else  /* MHD_VERSION < 0x00097701 */
   1516   if (NULL != body_dyn)
   1517     return MHD_create_response_from_buffer (body_dyn_size,
   1518                                             (void *) body_dyn,
   1519                                             MHD_RESPMEM_PERSISTENT);
   1520   else if (tool_params.empty)
   1521     return MHD_create_response_from_buffer (0,
   1522                                             (void *) tiny_body,
   1523                                             MHD_RESPMEM_PERSISTENT);
   1524 
   1525   return MHD_create_response_from_buffer (MHD_STATICSTR_LEN_ (tiny_body),
   1526                                           (void *) tiny_body,
   1527                                           MHD_RESPMEM_PERSISTENT);
   1528 #endif /* MHD_VERSION < 0x00097701 */
   1529 }
   1530 
   1531 
   1532 static int
   1533 init_data (void)
   1534 {
   1535   unsigned int i;
   1536 
   1537   if (tool_params.medium)
   1538     body_dyn_size = 8U * 1024U;
   1539   else if (tool_params.large)
   1540     body_dyn_size = 1024U * 1024U;
   1541   else if (tool_params.xlarge)
   1542     body_dyn_size = 8U * 1024U * 1024U;
   1543   else if (tool_params.jumbo)
   1544     body_dyn_size = 101U * 1024U * 1024U;
   1545   else
   1546     body_dyn_size = 0;
   1547 
   1548   if (! init_response_body_data ())
   1549     return 25;
   1550 
   1551   if (tool_params.unique)
   1552     return 0; /* Responses are generated on-fly */
   1553 
   1554   if (tool_params.single)
   1555   {
   1556     resp_single = create_response_object ();
   1557     if (NULL == resp_single)
   1558     {
   1559       fprintf (stderr, "Failed to create response.\n");
   1560       return 25;
   1561     }
   1562     return 0;
   1563   }
   1564 
   1565   /* Use more responses to minimise waiting in threads while the response
   1566      used by other thread. */
   1567   if (! tool_params.thread_per_conn)
   1568     num_resps = 16 * get_num_threads ();
   1569   else
   1570     num_resps = 16 * get_cpu_core_count ();
   1571 
   1572   resps = (struct MHD_Response **)
   1573           malloc ((sizeof(struct MHD_Response *)) * num_resps);
   1574   if (NULL == resps)
   1575   {
   1576     if (NULL != body_dyn)
   1577     {
   1578       free (body_dyn);
   1579       body_dyn = NULL;
   1580     }
   1581     fprintf (stderr, "Failed to allocate memory.\n");
   1582     return 25;
   1583   }
   1584   for (i = 0; i < num_resps; ++i)
   1585   {
   1586     resps[i] = create_response_object ();
   1587     if (NULL == resps[i])
   1588     {
   1589       fprintf (stderr, "Failed to create responses.\n");
   1590       break;
   1591     }
   1592   }
   1593   if (i == num_resps)
   1594     return 0; /* Success */
   1595 
   1596   /* Cleanup */
   1597   while (i-- != 0)
   1598     MHD_destroy_response (resps[i]);
   1599   free (resps);
   1600   resps = NULL;
   1601   num_resps = 0;
   1602   if (NULL != body_dyn)
   1603     free (body_dyn);
   1604   body_dyn = NULL;
   1605   return 32;
   1606 }
   1607 
   1608 
   1609 static void
   1610 deinit_data (void)
   1611 {
   1612   if (NULL != resp_single)
   1613     MHD_destroy_response (resp_single);
   1614   resp_single = NULL;
   1615   if (NULL != resps)
   1616   {
   1617     unsigned int i;
   1618     for (i = 0; i < num_resps; ++i)
   1619       MHD_destroy_response (resps[i]);
   1620     num_resps = 0;
   1621     free (resps);
   1622   }
   1623   resps = NULL;
   1624   if (NULL != body_dyn)
   1625     free (body_dyn);
   1626   body_dyn = NULL;
   1627 }
   1628 
   1629 
   1630 static enum MHD_Result
   1631 answer_shared_response (void *cls,
   1632                         struct MHD_Connection *connection,
   1633                         const char *url,
   1634                         const char *method,
   1635                         const char *version,
   1636                         const char *upload_data,
   1637                         size_t *upload_data_size,
   1638                         void **req_cls)
   1639 {
   1640   static int marker = 0;
   1641   unsigned int resp_index;
   1642   static volatile unsigned int last_index = 0;
   1643   (void) cls;  /* Unused */
   1644   (void) url; (void) version; /* Unused */
   1645   (void) upload_data; (void) upload_data_size; /* Unused */
   1646 
   1647   if (NULL == *req_cls)
   1648   {
   1649     /* The first call */
   1650     *req_cls = (void *) &marker;
   1651     /* Do not send reply yet. No error. */
   1652     return MHD_YES;
   1653   }
   1654   if ((0 != strcmp (method, MHD_HTTP_METHOD_GET)) &&
   1655       (0 != strcmp (method, MHD_HTTP_METHOD_HEAD)))
   1656     return MHD_NO; /* Unsupported method, close connection */
   1657 
   1658   /* This kind of operation does not guarantee that numbers are not reused
   1659      in parallel threads, when processed simultaneously, but this should not
   1660      be a big problem, as it just slow down replies a bit due to
   1661      response locking. */
   1662   resp_index = (last_index++) % num_resps;
   1663   return MHD_queue_response (connection, MHD_HTTP_OK, resps[resp_index]);
   1664 }
   1665 
   1666 
   1667 static enum MHD_Result
   1668 answer_single_response (void *cls,
   1669                         struct MHD_Connection *connection,
   1670                         const char *url,
   1671                         const char *method,
   1672                         const char *version,
   1673                         const char *upload_data,
   1674                         size_t *upload_data_size,
   1675                         void **req_cls)
   1676 {
   1677   static int marker = 0;
   1678   (void) cls;  /* Unused */
   1679   (void) url; (void) version; /* Unused */
   1680   (void) upload_data; (void) upload_data_size; /* Unused */
   1681 
   1682   if (NULL == *req_cls)
   1683   {
   1684     /* The first call */
   1685     *req_cls = (void *) &marker;
   1686     /* Do not send reply yet. No error. */
   1687     return MHD_YES;
   1688   }
   1689   if ((0 != strcmp (method, MHD_HTTP_METHOD_GET)) &&
   1690       (0 != strcmp (method, MHD_HTTP_METHOD_HEAD)))
   1691     return MHD_NO; /* Unsupported method, close connection */
   1692 
   1693   return MHD_queue_response (connection, MHD_HTTP_OK, resp_single);
   1694 }
   1695 
   1696 
   1697 static enum MHD_Result
   1698 answer_unique_empty_response (void *cls,
   1699                               struct MHD_Connection *connection,
   1700                               const char *url,
   1701                               const char *method,
   1702                               const char *version,
   1703                               const char *upload_data,
   1704                               size_t *upload_data_size,
   1705                               void **req_cls)
   1706 {
   1707   static int marker = 0;
   1708   struct MHD_Response *r;
   1709   enum MHD_Result ret;
   1710   (void) cls;  /* Unused */
   1711   (void) url; (void) version; /* Unused */
   1712   (void) upload_data; (void) upload_data_size; /* Unused */
   1713 
   1714   if (NULL == *req_cls)
   1715   {
   1716     /* The first call */
   1717     *req_cls = (void *) &marker;
   1718     /* Do not send reply yet. No error. */
   1719     return MHD_YES;
   1720   }
   1721   if ((0 != strcmp (method, MHD_HTTP_METHOD_GET)) &&
   1722       (0 != strcmp (method, MHD_HTTP_METHOD_HEAD)))
   1723     return MHD_NO; /* Unsupported method, close connection */
   1724 
   1725 #if MHD_VERSION >= 0x00097701
   1726   r = MHD_create_response_empty (MHD_RF_NONE);
   1727 #else  /* MHD_VERSION < 0x00097701 */
   1728   r = MHD_create_response_from_buffer (0,
   1729                                        NULL,
   1730                                        MHD_RESPMEM_PERSISTENT);
   1731 #endif /* MHD_VERSION < 0x00097701 */
   1732   ret = MHD_queue_response (connection, MHD_HTTP_OK, r);
   1733   MHD_destroy_response (r);
   1734   return ret;
   1735 }
   1736 
   1737 
   1738 static enum MHD_Result
   1739 answer_unique_tiny_response (void *cls,
   1740                              struct MHD_Connection *connection,
   1741                              const char *url,
   1742                              const char *method,
   1743                              const char *version,
   1744                              const char *upload_data,
   1745                              size_t *upload_data_size,
   1746                              void **req_cls)
   1747 {
   1748   static int marker = 0;
   1749   struct MHD_Response *r;
   1750   enum MHD_Result ret;
   1751   (void) cls;  /* Unused */
   1752   (void) url; (void) version; /* Unused */
   1753   (void) upload_data; (void) upload_data_size; /* Unused */
   1754 
   1755   if (NULL == *req_cls)
   1756   {
   1757     /* The first call */
   1758     *req_cls = (void *) &marker;
   1759     /* Do not send reply yet. No error. */
   1760     return MHD_YES;
   1761   }
   1762   if ((0 != strcmp (method, MHD_HTTP_METHOD_GET)) &&
   1763       (0 != strcmp (method, MHD_HTTP_METHOD_HEAD)))
   1764     return MHD_NO; /* Unsupported method, close connection */
   1765 
   1766 #if MHD_VERSION >= 0x00097701
   1767   r = MHD_create_response_from_buffer_static (MHD_STATICSTR_LEN_ (tiny_body),
   1768                                               tiny_body);
   1769 #else  /* MHD_VERSION < 0x00097701 */
   1770   r = MHD_create_response_from_buffer (MHD_STATICSTR_LEN_ (tiny_body),
   1771                                        (void *) tiny_body,
   1772                                        MHD_RESPMEM_PERSISTENT);
   1773 #endif /* MHD_VERSION < 0x00097701 */
   1774   ret = MHD_queue_response (connection, MHD_HTTP_OK, r);
   1775   MHD_destroy_response (r);
   1776   return ret;
   1777 }
   1778 
   1779 
   1780 static enum MHD_Result
   1781 answer_unique_dyn_response (void *cls,
   1782                             struct MHD_Connection *connection,
   1783                             const char *url,
   1784                             const char *method,
   1785                             const char *version,
   1786                             const char *upload_data,
   1787                             size_t *upload_data_size,
   1788                             void **req_cls)
   1789 {
   1790   static int marker = 0;
   1791   struct MHD_Response *r;
   1792   enum MHD_Result ret;
   1793   (void) cls;  /* Unused */
   1794   (void) url; (void) version; /* Unused */
   1795   (void) upload_data; (void) upload_data_size; /* Unused */
   1796 
   1797   if (NULL == *req_cls)
   1798   {
   1799     /* The first call */
   1800     *req_cls = (void *) &marker;
   1801     /* Do not send reply yet. No error. */
   1802     return MHD_YES;
   1803   }
   1804   if ((0 != strcmp (method, MHD_HTTP_METHOD_GET)) &&
   1805       (0 != strcmp (method, MHD_HTTP_METHOD_HEAD)))
   1806     return MHD_NO; /* Unsupported method, close connection */
   1807 
   1808 #if MHD_VERSION >= 0x00097701
   1809   r = MHD_create_response_from_buffer_static (body_dyn_size,
   1810                                               body_dyn);
   1811 #else  /* MHD_VERSION < 0x00097701 */
   1812   r = MHD_create_response_from_buffer (body_dyn_size,
   1813                                        (void *) body_dyn,
   1814                                        MHD_RESPMEM_PERSISTENT);
   1815 #endif /* MHD_VERSION < 0x00097701 */
   1816   ret = MHD_queue_response (connection, MHD_HTTP_OK, r);
   1817   MHD_destroy_response (r);
   1818   return ret;
   1819 }
   1820 
   1821 
   1822 static void
   1823 print_perf_warnings (void)
   1824 {
   1825   int newline_needed = 0;
   1826 #if defined (_DEBUG)
   1827   fprintf (stderr, "WARNING: Running with debug asserts enabled, "
   1828            "the performance is suboptimal.\n");
   1829   newline_needed |=  ! 0;
   1830 #endif /* _DEBUG */
   1831 #if defined(__GNUC__) && ! defined (__OPTIMIZE__)
   1832   fprintf (stderr, "WARNING: This tool is compiled without enabled compiler "
   1833            "optimisations, the performance is suboptimal.\n");
   1834   newline_needed |=  ! 0;
   1835 #endif /* __GNUC__ && ! __OPTIMIZE__ */
   1836 #if defined(__GNUC__) && defined (__OPTIMIZE_SIZE__)
   1837   fprintf (stderr, "WARNING: This tool is compiled with size-optimisations, "
   1838            "the performance is suboptimal.\n");
   1839 #endif /* __GNUC__ && ! __OPTIMIZE__ */
   1840 #if MHD_VERSION >= 0x00097701
   1841   if (MHD_NO != MHD_is_feature_supported (MHD_FEATURE_DEBUG_BUILD))
   1842   {
   1843     fprintf (stderr, "WARNING: The libmicrohttpd is compiled with "
   1844              "debug asserts enabled, the performance is suboptimal.\n");
   1845     newline_needed |=  ! 0;
   1846   }
   1847 #endif /* MHD_VERSION >= 0x00097701 */
   1848   if (newline_needed)
   1849     printf ("\n");
   1850 }
   1851 
   1852 
   1853 /* Borrowed from daemon.c */
   1854 /* TODO: run-time detection */
   1855 /**
   1856  * Default connection limit.
   1857  */
   1858 #ifdef MHD_POSIX_SOCKETS
   1859 #define MHD_MAX_CONNECTIONS_DEFAULT (FD_SETSIZE - 4)
   1860 #else
   1861 #define MHD_MAX_CONNECTIONS_DEFAULT (FD_SETSIZE - 2)
   1862 #endif
   1863 
   1864 static unsigned int
   1865 get_mhd_conn_limit (struct MHD_Daemon *d)
   1866 {
   1867   /* TODO: implement run-time detection */
   1868   (void) d; /* Unused */
   1869   if (0 != tool_params.connections)
   1870     return tool_params.connections;
   1871   return (unsigned int) MHD_MAX_CONNECTIONS_DEFAULT;
   1872 }
   1873 
   1874 
   1875 static const char *
   1876 get_mhd_response_size (void)
   1877 {
   1878   if (tool_params.empty)
   1879     return "0 bytes (empty)";
   1880   else if (tool_params.tiny)
   1881     return "3 bytes (tiny)";
   1882   else if (tool_params.medium)
   1883     return "8 KiB (medium)";
   1884   else if (tool_params.large)
   1885     return "1 MiB (large)";
   1886   else if (tool_params.xlarge)
   1887     return "8 MiB (xlarge)";
   1888   else if (tool_params.jumbo)
   1889     return "101 MiB (jumbo)";
   1890   return "!!internal error!!";
   1891 }
   1892 
   1893 
   1894 static int
   1895 run_mhd (void)
   1896 {
   1897   MHD_AccessHandlerCallback reply_func;
   1898   struct MHD_Daemon *d;
   1899   unsigned int use_num_threads;
   1900   unsigned int flags = MHD_NO_FLAG;
   1901   struct MHD_OptionItem opt_arr[16];
   1902   size_t opt_count = 0;
   1903   const union MHD_DaemonInfo *d_info;
   1904   const char *poll_mode;
   1905   uint16_t port;
   1906 
   1907   if (tool_params.thread_per_conn)
   1908     use_num_threads = 0;
   1909   else
   1910     use_num_threads = get_num_threads ();
   1911   printf ("\n");
   1912 
   1913   print_perf_warnings ();
   1914 
   1915   printf ("Responses:\n");
   1916   printf ("  Sharing:   ");
   1917   if (tool_params.shared)
   1918   {
   1919     reply_func = &answer_shared_response;
   1920     printf ("pre-generated shared pool with %u objects\n", num_resps);
   1921   }
   1922   else if (tool_params.single)
   1923   {
   1924     reply_func = &answer_single_response;
   1925     printf ("single pre-generated reused response object\n");
   1926   }
   1927   else
   1928   {
   1929     /* Unique responses */
   1930     if (tool_params.empty)
   1931       reply_func = &answer_unique_empty_response;
   1932     else if (tool_params.tiny)
   1933       reply_func = &answer_unique_tiny_response;
   1934     else
   1935       reply_func = &answer_unique_dyn_response;
   1936     printf ("one-time response object generated for every request\n");
   1937   }
   1938   printf ("  Body size: %s\n",
   1939           get_mhd_response_size ());
   1940 
   1941   flags |= MHD_USE_ERROR_LOG;
   1942   flags |= MHD_USE_INTERNAL_POLLING_THREAD;
   1943   if (tool_params.epoll)
   1944     flags |= MHD_USE_EPOLL;
   1945   else if (tool_params.poll)
   1946     flags |= MHD_USE_POLL;
   1947   else if (tool_params.select)
   1948     (void) flags; /* No special additional flag */
   1949   else
   1950     flags |= MHD_USE_AUTO;
   1951 
   1952   if (tool_params.thread_per_conn)
   1953     flags |= MHD_USE_THREAD_PER_CONNECTION;
   1954 
   1955   if (! tool_params.date_header)
   1956     flags |= MHD_USE_SUPPRESS_DATE_NO_CLOCK;
   1957 
   1958   if (0 != tool_params.connections)
   1959   {
   1960     opt_arr[opt_count].option = MHD_OPTION_CONNECTION_LIMIT;
   1961     opt_arr[opt_count].value = (intptr_t) tool_params.connections;
   1962     opt_arr[opt_count].ptr_value = NULL;
   1963     ++opt_count;
   1964   }
   1965   if (1 < use_num_threads)
   1966   {
   1967     opt_arr[opt_count].option = MHD_OPTION_THREAD_POOL_SIZE;
   1968     opt_arr[opt_count].value = (intptr_t) use_num_threads;
   1969     opt_arr[opt_count].ptr_value = NULL;
   1970     ++opt_count;
   1971   }
   1972   if (1)
   1973   {
   1974     opt_arr[opt_count].option = MHD_OPTION_CONNECTION_TIMEOUT;
   1975     opt_arr[opt_count].value = (intptr_t) tool_params.timeout;
   1976     opt_arr[opt_count].ptr_value = NULL;
   1977     ++opt_count;
   1978   }
   1979   if (1)
   1980   {
   1981     struct MHD_OptionItem option =
   1982     { MHD_OPTION_END, 0, NULL };
   1983     opt_arr[opt_count] = option;
   1984     if (opt_count >= (sizeof(opt_arr) / sizeof(opt_arr[0])))
   1985       abort ();
   1986   }
   1987   d = MHD_start_daemon (flags, mhd_port, NULL, NULL, reply_func, NULL,
   1988                         MHD_OPTION_ARRAY, opt_arr, MHD_OPTION_END);
   1989   if (NULL == d)
   1990   {
   1991     fprintf (stderr, "Error starting MHD daemon.\n");
   1992     return 15;
   1993   }
   1994   d_info = MHD_get_daemon_info (d, MHD_DAEMON_INFO_FLAGS);
   1995   if (NULL == d_info)
   1996     abort ();
   1997   flags = (unsigned int) d_info->flags;
   1998   if (0 != (flags & MHD_USE_POLL))
   1999     poll_mode = "poll()";
   2000   else if (0 != (flags & MHD_USE_EPOLL))
   2001     poll_mode = "epoll";
   2002   else
   2003     poll_mode = "select()";
   2004   d_info = MHD_get_daemon_info (d, MHD_DAEMON_INFO_BIND_PORT);
   2005   if (NULL == d_info)
   2006     abort ();
   2007   port = d_info->port;
   2008   if (0 == port)
   2009     fprintf (stderr, "Cannot detect port number. Consider specifying "
   2010              "port number explicitly.\n");
   2011 
   2012   printf ("MHD is running.\n");
   2013   printf ("  Bind port:          %u\n", (unsigned int) port);
   2014   printf ("  Polling function:   %s\n", poll_mode);
   2015   printf ("  Threading:          ");
   2016   if (MHD_USE_THREAD_PER_CONNECTION == (flags & MHD_USE_THREAD_PER_CONNECTION))
   2017     printf ("thread per connection\n");
   2018   else if (1 == get_num_threads ())
   2019     printf ("one MHD thread\n");
   2020   else
   2021     printf ("%u MHD threads in thread pool\n", get_num_threads ());
   2022   printf ("  Connections limit:  %u\n", get_mhd_conn_limit (d));
   2023   printf ("  Connection timeout: %u%s\n", tool_params.timeout,
   2024           0 == tool_params.timeout ? " (no timeout)" : "");
   2025   printf ("  'Date:' header:     %s\n",
   2026           tool_params.date_header ? "Yes" : "No");
   2027   printf ("To test with remote client use            "
   2028           "http://HOST_IP:%u/\n", (unsigned int) port);
   2029   printf ("To test with client on the same host use  "
   2030           "http://127.0.0.1:%u/\n", (unsigned int) port);
   2031   printf ("\nPress ENTER to stop.\n");
   2032   if (1)
   2033   {
   2034     char buf[10];
   2035     (void) fgets (buf, sizeof(buf), stdin);
   2036   }
   2037   MHD_stop_daemon (d);
   2038   return 0;
   2039 }
   2040 
   2041 
   2042 int
   2043 main (int argc, char *const *argv)
   2044 {
   2045   int ret;
   2046   set_self_name (argc, argv);
   2047   ret = process_params (argc, argv);
   2048   if (0 != ret)
   2049     return ret;
   2050   ret = check_apply_params ();
   2051   if (0 > ret)
   2052     return 0;
   2053   if (0 != ret)
   2054     return ret;
   2055   ret = init_data ();
   2056   if (0 != ret)
   2057     return ret;
   2058   ret = run_mhd ();
   2059   deinit_data ();
   2060   return ret;
   2061 }