libmicrohttpd

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

commit f586f2e7c1ebb471a76e24315783412dce2d0fec
parent 36f1c42d5f2c72e178033f676c2c24b638a067ae
Author: Evgeny Grin (Karlson2k) <k2k@narod.ru>
Date:   Mon, 10 Jul 2023 08:32:00 +0300

perf_replies: added options for response sharing types

Diffstat:
Msrc/tools/perf_replies.c | 502++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------------
1 file changed, 424 insertions(+), 78 deletions(-)

diff --git a/src/tools/perf_replies.c b/src/tools/perf_replies.c @@ -71,9 +71,6 @@ static const char *const build_revision = "" /* Dynamic variables */ static char self_name[500] = "perf_replies"; static uint16_t mhd_port = 0; -static struct MHD_Response **resps = NULL; -static unsigned int num_resps = 0; -static char *body_dyn = NULL; /* Non-static body data */ static void set_self_name (int argc, char *const *argv) @@ -350,6 +347,14 @@ show_help (void) printf (" -M, --medium medium response, 8 KB\n"); printf (" -L, --large large response, 1 MB\n"); printf ("\n"); + printf ("Response use options (mutually exclusive):\n"); + printf (" -S, --shared pool of pre-generated shared response\n" + " objects (default)\n"); + printf (" -I, --single single pre-generated response object\n" + " used for all requests\n"); + printf (" -U, --unique response object generated for every\n" + " request and used one time only\n"); + printf ("\n"); printf ("Other options:\n"); printf (" -c NUM, --connections=NUM reject more than NUM client \n" " connections\n"); @@ -378,6 +383,9 @@ struct PerfRepl_parameters int tiny; int medium; int large; + int shared; + int single; + int unique; unsigned int connections; unsigned int timeout; int date_header; @@ -401,6 +409,9 @@ static struct PerfRepl_parameters tool_params = { 0, 0, 0, + 0, + 0, + 0, 0 }; @@ -662,6 +673,69 @@ process_param__large (const char *param_name) } +static enum PerfRepl_param_result +process_param__shared (const char *param_name) +{ + if (tool_params.single) + { + fprintf (stderr, "Parameter '%s' cannot be used together " + "with '-I' or '--single'.\n", param_name); + return PERF_RPL_PARAM_ERROR; + } + if (tool_params.unique) + { + fprintf (stderr, "Parameter '%s' cannot be used together " + "with '-U' or '--unique'.\n", param_name); + return PERF_RPL_PARAM_ERROR; + } + tool_params.shared = ! 0; + return '-' == param_name[1] ? + PERF_RPL_PARAM_FULL_STR :PERF_RPL_PARAM_ONE_CHAR; +} + + +static enum PerfRepl_param_result +process_param__single (const char *param_name) +{ + if (tool_params.shared) + { + fprintf (stderr, "Parameter '%s' cannot be used together " + "with '-S' or '--shared'.\n", param_name); + return PERF_RPL_PARAM_ERROR; + } + if (tool_params.unique) + { + fprintf (stderr, "Parameter '%s' cannot be used together " + "with '-U' or '--unique'.\n", param_name); + return PERF_RPL_PARAM_ERROR; + } + tool_params.single = ! 0; + return '-' == param_name[1] ? + PERF_RPL_PARAM_FULL_STR :PERF_RPL_PARAM_ONE_CHAR; +} + + +static enum PerfRepl_param_result +process_param__unique (const char *param_name) +{ + if (tool_params.shared) + { + fprintf (stderr, "Parameter '%s' cannot be used together " + "with '-S' or '--shared'.\n", param_name); + return PERF_RPL_PARAM_ERROR; + } + if (tool_params.single) + { + fprintf (stderr, "Parameter '%s' cannot be used together " + "with '-I' or '--single'.\n", param_name); + return PERF_RPL_PARAM_ERROR; + } + tool_params.unique = ! 0; + return '-' == param_name[1] ? + PERF_RPL_PARAM_FULL_STR :PERF_RPL_PARAM_ONE_CHAR; +} + + /** * Process parameter '-c' or '--connections' * @param param_name the name of the parameter as specified in command line @@ -783,6 +857,12 @@ process_short_param (const char *param, const char *next_param) return process_param__medium ("-M"); else if ('L' == param_chr) return process_param__large ("-L"); + else if ('S' == param_chr) + return process_param__shared ("-S"); + else if ('I' == param_chr) + return process_param__single ("-I"); + else if ('U' == param_chr) + return process_param__unique ("-U"); else if ('c' == param_chr) return process_param__connections ("-c", param + 1, next_param); else if ('O' == param_chr) @@ -867,6 +947,15 @@ process_long_param (const char *param, const char *next_param) else if ((MHD_STATICSTR_LEN_ ("large") == param_len) && (0 == memcmp (param, "large", MHD_STATICSTR_LEN_ ("large")))) return process_param__large ("--large"); + else if ((MHD_STATICSTR_LEN_ ("shared") == param_len) && + (0 == memcmp (param, "shared", MHD_STATICSTR_LEN_ ("shared")))) + return process_param__shared ("--shared"); + else if ((MHD_STATICSTR_LEN_ ("single") == param_len) && + (0 == memcmp (param, "single", MHD_STATICSTR_LEN_ ("single")))) + return process_param__single ("--single"); + else if ((MHD_STATICSTR_LEN_ ("unique") == param_len) && + (0 == memcmp (param, "unique", MHD_STATICSTR_LEN_ ("unique")))) + return process_param__unique ("--unique"); else if ((MHD_STATICSTR_LEN_ ("connections") <= param_len) && (0 == memcmp (param, "connections", MHD_STATICSTR_LEN_ ("connections")))) @@ -1102,6 +1191,14 @@ check_param__empty_tiny_medium_large (void) } +static void +check_param__shared_single_unique (void) +{ + if (0 == (tool_params.shared | tool_params.single | tool_params.unique)) + tool_params.shared = ! 0; +} + + /* Must be called after 'check_apply_param__threads()' and 'check_apply_param__all_cpus()' */ /* non-zero - OK, zero - error */ @@ -1150,35 +1247,36 @@ check_apply_params (void) if (! check_param__poll ()) return PERF_RPL_ERR_CODE_BAD_PARAM; check_param__empty_tiny_medium_large (); + check_param__shared_single_unique (); if (! check_param__connections ()) return PERF_RPL_ERR_CODE_BAD_PARAM; return 0; } -static int -init_data (void) -{ - /* Use the same memory area to avoid multiple copies. - The system will keep it in cache. */ - static const char tiny_body[] = "Hi!"; - unsigned int i; - size_t body_dyn_size; +/* The pool of shared responses */ +static struct MHD_Response **resps = NULL; +static unsigned int num_resps = 0; +/* The single response */ +static struct MHD_Response *resp_single = NULL; - if (tool_params.medium) - body_dyn_size = 8U * 1024U; - else if (tool_params.large) - body_dyn_size = 1024U * 1024U; - else - body_dyn_size = 0; +/* Use the same memory area to avoid multiple copies. + The system will keep it in cache. */ +static const char tiny_body[] = "Hi!"; +static char *body_dyn = NULL; /* Non-static body data */ +size_t body_dyn_size; +/* Non-zero - success, zero - failure */ +static int +init_response_body_data (void) +{ if (0 != body_dyn_size) { body_dyn = (char *) malloc (body_dyn_size); if (NULL == body_dyn) { fprintf (stderr, "Failed to allocate memory.\n"); - return 25; + return 0; } if (tool_params.medium) { @@ -1218,10 +1316,76 @@ init_data (void) } } } + return ! 0; +} + + +static struct MHD_Response * +create_response_object (void) +{ +#if MHD_VERSION >= 0x00097701 + if (NULL != body_dyn) + return MHD_create_response_from_buffer_static (body_dyn_size, + body_dyn); + else if (tool_params.empty) + return MHD_create_response_empty (MHD_RF_NONE); + + return MHD_create_response_from_buffer_static (MHD_STATICSTR_LEN_ (tiny_body), + tiny_body); + +#else /* MHD_VERSION < 0x00097701 */ + if (NULL != body_dyn) + return MHD_create_response_from_buffer (body_dyn_size, + (void *) body_dyn, + MHD_RESPMEM_PERSISTENT); + else if (tool_params.empty) + return MHD_create_response_from_buffer (0, + (void *) tiny_body, + MHD_RESPMEM_PERSISTENT); + + return MHD_create_response_from_buffer (MHD_STATICSTR_LEN_ (tiny_body), + (void *) tiny_body, + MHD_RESPMEM_PERSISTENT); +#endif /* MHD_VERSION < 0x00097701 */ +} + + +static int +init_data (void) +{ + unsigned int i; + + if (tool_params.medium) + body_dyn_size = 8U * 1024U; + else if (tool_params.large) + body_dyn_size = 1024U * 1024U; + else + body_dyn_size = 0; + + if (! init_response_body_data ()) + return 25; + + if (tool_params.unique) + return 0; /* Responses are generated on-fly */ + + if (tool_params.single) + { + resp_single = create_response_object (); + if (NULL == resp_single) + { + fprintf (stderr, "Failed to create response.\n"); + return 25; + } + return 0; + } + + /* Use more responses to minimise waiting in threads while the response + used by other thread. */ + if (! tool_params.thread_per_conn) + num_resps = 16 * get_num_threads (); + else + num_resps = 16 * get_cpu_core_count (); - /* Use more responses to minimise waiting in threads to unlock - the response used by other thread. */ - num_resps = 16 * get_num_threads (); resps = (struct MHD_Response **) malloc ((sizeof(struct MHD_Response *)) * num_resps); if (NULL == resps) @@ -1236,33 +1400,7 @@ init_data (void) } for (i = 0; i < num_resps; ++i) { -#if MHD_VERSION >= 0x00097701 - if (NULL != body_dyn) - resps[i] = MHD_create_response_from_buffer_static (body_dyn_size, - body_dyn); - else if (tool_params.empty) - resps[i] = MHD_create_response_empty (MHD_RF_NONE); - else - resps[i] = - MHD_create_response_from_buffer_static (MHD_STATICSTR_LEN_ (tiny_body), - tiny_body); - -#else /* MHD_VERSION < 0x00097701 */ - if (NULL != body_dyn) - resps[i] = MHD_create_response_from_buffer (body_dyn_size, - (void *) body_dyn, - MHD_RESPMEM_PERSISTENT); - else if (tool_params.empty) - resps[i] = MHD_create_response_from_buffer (0, - (void *) tiny_body, - MHD_RESPMEM_PERSISTENT); - else if (tool_params.tiny) - resps[i] = - MHD_create_response_from_buffer (MHD_STATICSTR_LEN_ (tiny_body), - (void *) tiny_body, - MHD_RESPMEM_PERSISTENT); - else -#endif /* MHD_VERSION < 0x00097701 */ + resps[i] = create_response_object (); if (NULL == resps[i]) { fprintf (stderr, "Failed to create responses.\n"); @@ -1288,13 +1426,18 @@ init_data (void) static void deinit_data (void) { - unsigned int i; - for (i = 0; i < num_resps; ++i) - MHD_destroy_response (resps[i]); - - free (resps); + if (NULL != resp_single) + MHD_destroy_response (resp_single); + resp_single = NULL; + if (NULL != resps) + { + unsigned int i; + for (i = 0; i < num_resps; ++i) + MHD_destroy_response (resps[i]); + num_resps = 0; + free (resps); + } resps = NULL; - num_resps = 0; if (NULL != body_dyn) free (body_dyn); body_dyn = NULL; @@ -1338,6 +1481,192 @@ answer_shared_response (void *cls, } +static enum MHD_Result +answer_single_response (void *cls, + struct MHD_Connection *connection, + const char *url, + const char *method, + const char *version, + const char *upload_data, + size_t *upload_data_size, + void **req_cls) +{ + static int marker = 0; + (void) cls; /* Unused */ + (void) url; (void) version; /* Unused */ + (void) upload_data; (void) upload_data_size; /* Unused */ + + if (NULL == *req_cls) + { + /* The fist call */ + *req_cls = (void *) &marker; + /* Do not send reply yet. No error. */ + return MHD_YES; + } + if ((0 != strcmp (method, MHD_HTTP_METHOD_GET)) && + (0 != strcmp (method, MHD_HTTP_METHOD_HEAD))) + return MHD_NO; /* Unsupported method, close connection */ + + return MHD_queue_response (connection, MHD_HTTP_OK, resp_single); +} + + +static enum MHD_Result +answer_unique_empty_response (void *cls, + struct MHD_Connection *connection, + const char *url, + const char *method, + const char *version, + const char *upload_data, + size_t *upload_data_size, + void **req_cls) +{ + static int marker = 0; + struct MHD_Response *r; + enum MHD_Result ret; + (void) cls; /* Unused */ + (void) url; (void) version; /* Unused */ + (void) upload_data; (void) upload_data_size; /* Unused */ + + if (NULL == *req_cls) + { + /* The fist call */ + *req_cls = (void *) &marker; + /* Do not send reply yet. No error. */ + return MHD_YES; + } + if ((0 != strcmp (method, MHD_HTTP_METHOD_GET)) && + (0 != strcmp (method, MHD_HTTP_METHOD_HEAD))) + return MHD_NO; /* Unsupported method, close connection */ + +#if MHD_VERSION >= 0x00097701 + r = MHD_create_response_empty (MHD_RF_NONE); +#else /* MHD_VERSION < 0x00097701 */ + r = MHD_create_response_from_buffer (0, + NULL, + MHD_RESPMEM_PERSISTENT); +#endif /* MHD_VERSION < 0x00097701 */ + ret = MHD_queue_response (connection, MHD_HTTP_OK, r); + MHD_destroy_response (r); + return ret; +} + + +static enum MHD_Result +answer_unique_tiny_response (void *cls, + struct MHD_Connection *connection, + const char *url, + const char *method, + const char *version, + const char *upload_data, + size_t *upload_data_size, + void **req_cls) +{ + static int marker = 0; + struct MHD_Response *r; + enum MHD_Result ret; + (void) cls; /* Unused */ + (void) url; (void) version; /* Unused */ + (void) upload_data; (void) upload_data_size; /* Unused */ + + if (NULL == *req_cls) + { + /* The fist call */ + *req_cls = (void *) &marker; + /* Do not send reply yet. No error. */ + return MHD_YES; + } + if ((0 != strcmp (method, MHD_HTTP_METHOD_GET)) && + (0 != strcmp (method, MHD_HTTP_METHOD_HEAD))) + return MHD_NO; /* Unsupported method, close connection */ + +#if MHD_VERSION >= 0x00097701 + r = MHD_create_response_from_buffer_static (MHD_STATICSTR_LEN_ (tiny_body), + tiny_body); +#else /* MHD_VERSION < 0x00097701 */ + r = MHD_create_response_from_buffer (MHD_STATICSTR_LEN_ (tiny_body), + (void *) tiny_body, + MHD_RESPMEM_PERSISTENT); +#endif /* MHD_VERSION < 0x00097701 */ + ret = MHD_queue_response (connection, MHD_HTTP_OK, r); + MHD_destroy_response (r); + return ret; +} + + +static enum MHD_Result +answer_unique_dyn_response (void *cls, + struct MHD_Connection *connection, + const char *url, + const char *method, + const char *version, + const char *upload_data, + size_t *upload_data_size, + void **req_cls) +{ + static int marker = 0; + struct MHD_Response *r; + enum MHD_Result ret; + (void) cls; /* Unused */ + (void) url; (void) version; /* Unused */ + (void) upload_data; (void) upload_data_size; /* Unused */ + + if (NULL == *req_cls) + { + /* The fist call */ + *req_cls = (void *) &marker; + /* Do not send reply yet. No error. */ + return MHD_YES; + } + if ((0 != strcmp (method, MHD_HTTP_METHOD_GET)) && + (0 != strcmp (method, MHD_HTTP_METHOD_HEAD))) + return MHD_NO; /* Unsupported method, close connection */ + +#if MHD_VERSION >= 0x00097701 + r = MHD_create_response_from_buffer_static (body_dyn_size, + body_dyn); +#else /* MHD_VERSION < 0x00097701 */ + r = MHD_create_response_from_buffer (body_dyn_size, + (void *) body_dyn, + MHD_RESPMEM_PERSISTENT); +#endif /* MHD_VERSION < 0x00097701 */ + ret = MHD_queue_response (connection, MHD_HTTP_OK, r); + MHD_destroy_response (r); + return ret; +} + + +static void +print_perf_warnings (void) +{ + int newline_needed = 0; +#if defined (_DEBUG) + fprintf (stderr, "WARNING: Running with debug asserts enabled, " + "the performance is suboptimal.\n"); + newline_needed |= ! 0; +#endif /* _DEBUG */ +#if defined(__GNUC__) && ! defined (__OPTIMIZE__) + fprintf (stderr, "WARNING: The tools is compiled without enabled compiler " + "optimisations, the performance is suboptimal.\n"); + newline_needed |= ! 0; +#endif /* __GNUC__ && ! __OPTIMIZE__ */ +#if defined(__GNUC__) && defined (__OPTIMIZE_SIZE__) + fprintf (stderr, "WARNING: The tools is compiled with size-optimisations, " + "the performance is suboptimal.\n"); +#endif /* __GNUC__ && ! __OPTIMIZE__ */ +#if MHD_VERSION >= 0x00097701 + if (MHD_NO != MHD_is_feature_supported (MHD_FEATURE_DEBUG_BUILD)) + { + fprintf (stderr, "WARNING: The libmicrohttpd is compiled with " + "debug asserts enabled, the performance is suboptimal.\n"); + newline_needed |= ! 0; + } +#endif /* MHD_VERSION >= 0x00097701 */ + if (newline_needed) + printf ("\n"); +} + + /* Borrowed from daemon.c */ /* TODO: run-time detection */ /** @@ -1379,7 +1708,9 @@ get_mhd_response_size (void) static int run_mhd (void) { + MHD_AccessHandlerCallback reply_func; struct MHD_Daemon *d; + unsigned int use_num_threads; unsigned int flags = MHD_NO_FLAG; struct MHD_OptionItem opt_arr[16]; size_t opt_count = 0; @@ -1387,23 +1718,40 @@ run_mhd (void) const char *poll_mode; uint16_t port; -#if defined (_DEBUG) - fprintf (stderr, "WARNING: Running with debug asserts enabled, " - "the performance is suboptimal.\n"); -#endif /* _DEBUG */ -#if defined(__GNUC__) && ! defined (__OPTIMIZE__) - fprintf (stderr, "WARNING: The tools is compiled without enabled compiler " - "optimisations, the performance is suboptimal.\n"); -#endif /* __GNUC__ && ! __OPTIMIZE__ */ -#if defined(__GNUC__) && defined (__OPTIMIZE_SIZE__) - fprintf (stderr, "WARNING: The tools is compiled with size-optimisations, " - "the performance is suboptimal.\n"); -#endif /* __GNUC__ && ! __OPTIMIZE__ */ -#if MHD_VERSION >= 0x00097701 - if (MHD_NO != MHD_is_feature_supported (MHD_FEATURE_DEBUG_BUILD)) - fprintf (stderr, "WARNING: The libmicrohttpd is compiled with " - "debug asserts enabled, the performance is suboptimal.\n"); -#endif /* MHD_VERSION >= 0x00097701 */ + if (tool_params.thread_per_conn) + use_num_threads = 0; + else + use_num_threads = get_num_threads (); + printf ("\n"); + + print_perf_warnings (); + + printf ("Responses:\n"); + printf (" Sharing: "); + if (tool_params.shared) + { + reply_func = &answer_shared_response; + printf ("pre-generated shared pool with %u objects\n", num_resps); + } + else if (tool_params.single) + { + reply_func = &answer_single_response; + printf ("single pre-generated reused response object\n"); + } + else + { + /* Unique responses */ + if (tool_params.empty) + reply_func = &answer_unique_empty_response; + else if (tool_params.tiny) + reply_func = &answer_unique_tiny_response; + else + reply_func = &answer_unique_dyn_response; + printf ("one-time response object generated for every request\n"); + } + printf (" Body size: %s\n", + get_mhd_response_size ()); + flags |= MHD_USE_ERROR_LOG; flags |= MHD_USE_INTERNAL_POLLING_THREAD; if (tool_params.epoll) @@ -1427,10 +1775,10 @@ run_mhd (void) { MHD_OPTION_CONNECTION_LIMIT, (intptr_t) tool_params.connections, NULL }; opt_arr[opt_count++] = option; } - if (1 < get_num_threads ()) + if (1 < use_num_threads) { struct MHD_OptionItem option = - { MHD_OPTION_THREAD_POOL_SIZE, (intptr_t) get_num_threads (), NULL }; + { MHD_OPTION_THREAD_POOL_SIZE, (intptr_t) use_num_threads, NULL }; opt_arr[opt_count++] = option; } if (1) @@ -1447,8 +1795,8 @@ run_mhd (void) if (opt_count >= (sizeof(opt_arr) / sizeof(opt_arr[0]))) abort (); } - d = MHD_start_daemon (flags, mhd_port, NULL, NULL, &answer_shared_response, - NULL, MHD_OPTION_ARRAY, opt_arr, MHD_OPTION_END); + d = MHD_start_daemon (flags, mhd_port, NULL, NULL, reply_func, NULL, + MHD_OPTION_ARRAY, opt_arr, MHD_OPTION_END); if (NULL == d) { fprintf (stderr, "Error starting MHD daemon.\n"); @@ -1472,7 +1820,7 @@ run_mhd (void) fprintf (stderr, "Cannot detect port number. Consider specifying " "port number explicitly.\n"); - printf ("\nMHD is running.\n"); + printf ("MHD is running.\n"); printf (" Bind port: %u\n", (unsigned int) port); printf (" Polling function: %s\n", poll_mode); printf (" Threading: "); @@ -1487,8 +1835,6 @@ run_mhd (void) 0 == tool_params.timeout ? " (no timeout)" : ""); printf (" 'Date:' header: %s\n", tool_params.date_header ? "Yes" : "No"); - printf (" Response body size: %s\n", - get_mhd_response_size ()); printf ("To test with remote client use " "http://HOST_IP:%u/\n", (unsigned int) port); printf ("To test with client on the same host use "