libmicrohttpd2

HTTP server C library (MHD 2.x, alpha)
Log | Files | Refs | README | LICENSE

commit 72e0245ba97efbadf7d7c3689df4b405e442df0e
parent d08a352c08e3b4e9bb2743587dd3a2631e578f87
Author: Evgeny Grin (Karlson2k) <k2k@drgrin.dev>
Date:   Mon, 22 Dec 2025 16:18:13 +0100

Renamed daemon timeout option and info type: use milliseconds instead of seconds,
moved timeout-related functions to special file

Diffstat:
Msrc/examples2/demo.c | 2+-
Msrc/examples2/json_echo.c | 2+-
Msrc/include/d_options.rec | 27+++++++++++++++++++--------
Msrc/include/microhttpd2.h | 35+++++++++++++++++++++++------------
Msrc/include/microhttpd2_generated_daemon_options.h | 91++++++++++++++++++++++++++++++++++++++++++++++++++++++-------------------------
Msrc/include/microhttpd2_main.h.in | 8++++----
Msrc/mhd2/Makefile.am | 1+
Msrc/mhd2/conn_data_recv.c | 1+
Msrc/mhd2/conn_data_send.c | 4+++-
Asrc/mhd2/conn_timeout.c | 169+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/mhd2/conn_timeout.h | 92+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/mhd2/daemon_add_conn.c | 2+-
Msrc/mhd2/daemon_get_info.c | 10++++------
Msrc/mhd2/daemon_options.h | 6+++---
Msrc/mhd2/daemon_set_options.c | 4++--
Msrc/mhd2/daemon_start.c | 13++++++-------
Msrc/mhd2/events_process.c | 1+
Msrc/mhd2/mhd_daemon.h | 2+-
Msrc/mhd2/stream_funcs.c | 119+------------------------------------------------------------------------------
Msrc/mhd2/stream_funcs.h | 37-------------------------------------
Msrc/mhd2/stream_process_states.c | 1+
Msrc/tests/client_server/libtest.h | 2+-
Msrc/tools/perf_replies.c | 16+++++++++-------
23 files changed, 406 insertions(+), 239 deletions(-)

diff --git a/src/examples2/demo.c b/src/examples2/demo.c @@ -1368,7 +1368,7 @@ main (int argc, d, MHD_D_OPTION_POLL_SYSCALL (MHD_SPS_AUTO), MHD_D_OPTION_WM_WORKER_THREADS (NUMBER_OF_THREADS), - MHD_D_OPTION_DEFAULT_TIMEOUT (120 /* seconds */), + MHD_D_OPTION_DEFAULT_TIMEOUT_MILSEC (120000u), MHD_D_OPTION_CONN_MEMORY_LIMIT (256 * 1024), MHD_D_OPTION_BIND_PORT (MHD_AF_AUTO, (uint_least16_t) port))) diff --git a/src/examples2/json_echo.c b/src/examples2/json_echo.c @@ -319,7 +319,7 @@ main (int argc, &sock_reg_update_cb, NULL), MHD_D_OPTION_FD_NUMBER_LIMIT (FD_SETSIZE), - MHD_D_OPTION_DEFAULT_TIMEOUT (120 /* seconds */), + MHD_D_OPTION_DEFAULT_TIMEOUT_MILSEC (120000u), MHD_D_OPTION_CONN_MEMORY_LIMIT (256 * 1024), MHD_D_OPTION_BIND_PORT (MHD_AF_AUTO, (uint_least16_t) port))) diff --git a/src/include/d_options.rec b/src/include/d_options.rec @@ -325,15 +325,26 @@ CustomSetter: /* custom setter */ # Connection handling -Name: DEFAULT_TIMEOUT +Name: DEFAULT_TIMEOUT_MILSEC Value: 160 -Comment: Specify inactivity timeout for connection. -+ When no activity for specified time on connection, it is closed -+ automatically. -+ Use zero for no timeout, which is also the (unsafe!) default. -+ Very large values (years) can be silently truncated to smaller values. -Argument1: unsigned int timeout -Description1: the in seconds, zero for no timeout +Comment: Specify the inactivity timeout for a connection in milliseconds. ++ If a connection remains idle (no activity) for this many ++ milliseconds, it is closed automatically. ++ Use zero for no timeout; this is also the (unsafe!) ++ default. ++ Values larger than 1209600000 (two weeks) are silently ++ clamped to 1209600000. ++ Precise closing time is not guaranteed and depends on ++ system clock granularity and amount of time spent on ++ processing other connections. Typical precision is ++ within +/- 30 milliseconds, while the worst case could ++ be greater than +/- 1 second. ++ Values below 1500 milliseconds are risky as they ++ may cause valid connections to be aborted and may ++ increase load the server load due to clients' repetitive ++ automatic retries. +Argument1: uint_fast32_t timeout +Description1: the timeout in milliseconds, zero for no timeout Name: GLOBAL_CONNECTION_LIMIT Value: 161 diff --git a/src/include/microhttpd2.h b/src/include/microhttpd2.h @@ -4425,17 +4425,28 @@ MHD_D_OPTION_TLS_OPENSSL_DEF_FILE ( ); /** - * Specify inactivity timeout for connection. - * When no activity for specified time on connection, it is closed - * automatically. - * Use zero for no timeout, which is also the (unsafe!) default. - * Very large values (years) can be silently truncated to smaller values. - * @param timeout the in seconds, zero for no timeout + * Specify the inactivity timeout for a connection in milliseconds. + * If a connection remains idle (no activity) for this many + * milliseconds, it is closed automatically. + * Use zero for no timeout; this is also the (unsafe!) + * default. + * Values larger than 1209600000 (two weeks) are silently + * clamped to 1209600000. + * Precise closing time is not guaranteed and depends on + * system clock granularity and amount of time spent on + * processing other connections. Typical precision is + * within +/- 30 milliseconds, while the worst case could + * be greater than +/- 1 second. + * Values below 1500 milliseconds are risky as they + * may cause valid connections to be aborted and may + * increase load the server load due to clients' repetitive + * automatic retries. + * @param timeout the timeout in milliseconds, zero for no timeout * @return structure with the requested setting */ struct MHD_DaemonOptionAndValue -MHD_D_OPTION_DEFAULT_TIMEOUT ( - unsigned int timeout +MHD_D_OPTION_DEFAULT_TIMEOUT_MILSEC ( + uint_fast32_t timeout ); /** @@ -9519,10 +9530,10 @@ enum MHD_DaemonInfoFixedType MHD_DAEMON_INFO_FIXED_TLS_BACKEND = 120 , /** - * Get the default inactivity timeout for connections. - * The result is placed in @a v_default_timeout_uint member. + * Get the default inactivity timeout for connections in milliseconds. + * The result is placed in @a v_default_timeout_milsec_uint32 member. */ - MHD_DAEMON_INFO_FIXED_DEFAULT_TIMEOUT = 160 + MHD_DAEMON_INFO_FIXED_DEFAULT_TIMEOUT_MILSEC = 160 , /** * Get the limit of number of simutaneous network connections served by @@ -9610,7 +9621,7 @@ union MHD_DaemonInfoFixedData /** * The data for the #MHD_DAEMON_INFO_FIXED_DEFAULT_TIMEOUT query */ - unsigned int v_default_timeout_uint; + uint_fast32_t v_default_timeout_milsec_uint32; /** * The data for the #MHD_DAEMON_INFO_FIXED_GLOBAL_CONNECTION_LIMIT query diff --git a/src/include/microhttpd2_generated_daemon_options.h b/src/include/microhttpd2_generated_daemon_options.h @@ -168,13 +168,24 @@ Works only when #MHD_D_OPTION_BIND_PORT() or #MHD_D_OPTION_BIND_SA() are used. , /** - * Specify inactivity timeout for connection. - * When no activity for specified time on connection, it is closed - * automatically. - * Use zero for no timeout, which is also the (unsafe!) default. - * Very large values (years) can be silently truncated to smaller values. - */ - MHD_D_O_DEFAULT_TIMEOUT = 160 + * Specify the inactivity timeout for a connection in milliseconds. + * If a connection remains idle (no activity) for this many + * milliseconds, it is closed automatically. + * Use zero for no timeout; this is also the (unsafe!) + * default. + * Values larger than 1209600000 (two weeks) are silently + * clamped to 1209600000. + * Precise closing time is not guaranteed and depends on + * system clock granularity and amount of time spent on + * processing other connections. Typical precision is + * within +/- 30 milliseconds, while the worst case could + * be greater than +/- 1 second. + * Values below 1500 milliseconds are risky as they + * may cause valid connections to be aborted and may + * increase load the server load due to clients' repetitive + * automatic retries. + */ + MHD_D_O_DEFAULT_TIMEOUT_MILSEC = 160 , /** @@ -797,10 +808,10 @@ union MHD_DaemonOptionValue struct MHD_DaemonOptionValueTlsOsslDefFile tls_openssl_def_file; /** - * Value for #MHD_D_O_DEFAULT_TIMEOUT. - * the in seconds, zero for no timeout + * Value for #MHD_D_O_DEFAULT_TIMEOUT_MILSEC. + * the timeout in milliseconds, zero for no timeout */ - unsigned int default_timeout; + uint_fast32_t default_timeout_milsec; /** * Value for #MHD_D_O_GLOBAL_CONNECTION_LIMIT. @@ -1279,20 +1290,31 @@ Works only when #MHD_D_OPTION_BIND_PORT() or #MHD_D_OPTION_BIND_SA() are used. } \ MHD_RESTORE_WARN_COMPOUND_LITERALS_ MHD_RESTORE_WARN_AGGR_DYN_INIT_ /** - * Specify inactivity timeout for connection. - * When no activity for specified time on connection, it is closed - * automatically. - * Use zero for no timeout, which is also the (unsafe!) default. - * Very large values (years) can be silently truncated to smaller values. - * @param timeout the in seconds, zero for no timeout + * Specify the inactivity timeout for a connection in milliseconds. + * If a connection remains idle (no activity) for this many + * milliseconds, it is closed automatically. + * Use zero for no timeout; this is also the (unsafe!) + * default. + * Values larger than 1209600000 (two weeks) are silently + * clamped to 1209600000. + * Precise closing time is not guaranteed and depends on + * system clock granularity and amount of time spent on + * processing other connections. Typical precision is + * within +/- 30 milliseconds, while the worst case could + * be greater than +/- 1 second. + * Values below 1500 milliseconds are risky as they + * may cause valid connections to be aborted and may + * increase load the server load due to clients' repetitive + * automatic retries. + * @param timeout the timeout in milliseconds, zero for no timeout * @return structure with the requested setting */ -# define MHD_D_OPTION_DEFAULT_TIMEOUT(timeout) \ +# define MHD_D_OPTION_DEFAULT_TIMEOUT_MILSEC(timeout) \ MHD_NOWARN_COMPOUND_LITERALS_ MHD_NOWARN_AGGR_DYN_INIT_ \ (const struct MHD_DaemonOptionAndValue) \ { \ - .opt = MHD_D_O_DEFAULT_TIMEOUT, \ - .val.default_timeout = (timeout) \ + .opt = MHD_D_O_DEFAULT_TIMEOUT_MILSEC, \ + .val.default_timeout_milsec = (timeout) \ } \ MHD_RESTORE_WARN_COMPOUND_LITERALS_ MHD_RESTORE_WARN_AGGR_DYN_INIT_ /** @@ -2131,23 +2153,34 @@ MHD_D_OPTION_TLS_OPENSSL_DEF_FILE ( /** - * Specify inactivity timeout for connection. - * When no activity for specified time on connection, it is closed - * automatically. - * Use zero for no timeout, which is also the (unsafe!) default. - * Very large values (years) can be silently truncated to smaller values. - * @param timeout the in seconds, zero for no timeout + * Specify the inactivity timeout for a connection in milliseconds. + * If a connection remains idle (no activity) for this many + * milliseconds, it is closed automatically. + * Use zero for no timeout; this is also the (unsafe!) + * default. + * Values larger than 1209600000 (two weeks) are silently + * clamped to 1209600000. + * Precise closing time is not guaranteed and depends on + * system clock granularity and amount of time spent on + * processing other connections. Typical precision is + * within +/- 30 milliseconds, while the worst case could + * be greater than +/- 1 second. + * Values below 1500 milliseconds are risky as they + * may cause valid connections to be aborted and may + * increase load the server load due to clients' repetitive + * automatic retries. + * @param timeout the timeout in milliseconds, zero for no timeout * @return structure with the requested setting */ static MHD_INLINE struct MHD_DaemonOptionAndValue -MHD_D_OPTION_DEFAULT_TIMEOUT ( - unsigned int timeout +MHD_D_OPTION_DEFAULT_TIMEOUT_MILSEC ( + uint_fast32_t timeout ) { struct MHD_DaemonOptionAndValue opt_val; - opt_val.opt = MHD_D_O_DEFAULT_TIMEOUT; - opt_val.val.default_timeout = timeout; + opt_val.opt = MHD_D_O_DEFAULT_TIMEOUT_MILSEC; + opt_val.val.default_timeout_milsec = timeout; return opt_val; } diff --git a/src/include/microhttpd2_main.h.in b/src/include/microhttpd2_main.h.in @@ -4655,10 +4655,10 @@ enum MHD_DaemonInfoFixedType MHD_DAEMON_INFO_FIXED_TLS_BACKEND = 120 , /** - * Get the default inactivity timeout for connections. - * The result is placed in @a v_default_timeout_uint member. + * Get the default inactivity timeout for connections in milliseconds. + * The result is placed in @a v_default_timeout_milsec_uint32 member. */ - MHD_DAEMON_INFO_FIXED_DEFAULT_TIMEOUT = 160 + MHD_DAEMON_INFO_FIXED_DEFAULT_TIMEOUT_MILSEC = 160 , /** * Get the limit of number of simutaneous network connections served by @@ -4746,7 +4746,7 @@ union MHD_DaemonInfoFixedData /** * The data for the #MHD_DAEMON_INFO_FIXED_DEFAULT_TIMEOUT query */ - unsigned int v_default_timeout_uint; + uint_fast32_t v_default_timeout_milsec_uint32; /** * The data for the #MHD_DAEMON_INFO_FIXED_GLOBAL_CONNECTION_LIMIT query diff --git a/src/mhd2/Makefile.am b/src/mhd2/Makefile.am @@ -85,6 +85,7 @@ libmicrohttpd2_la_SOURCES = \ daemon_add_conn.c daemon_add_conn.h \ daemon_funcs.c daemon_funcs.h \ daemon_event_update.c extr_events_funcs.c extr_events_funcs.h \ + conn_timeout.c conn_timeout.h \ conn_data_process.c conn_data_process.h \ conn_data_recv.c conn_data_recv.h \ conn_data_send.c conn_data_send.h \ diff --git a/src/mhd2/conn_data_recv.c b/src/mhd2/conn_data_recv.c @@ -55,6 +55,7 @@ #include "mhd_connection.h" +#include "conn_timeout.h" #include "stream_funcs.h" #include "mhd_socket_error_funcs.h" #include "sckt_recv.h" diff --git a/src/mhd2/conn_data_send.c b/src/mhd2/conn_data_send.c @@ -63,9 +63,11 @@ #include "mhd_socket_error_funcs.h" -#include "mhd_send.h" +#include "conn_timeout.h" #include "stream_funcs.h" +#include "mhd_send.h" + /** * Check if we are done sending the write-buffer. diff --git a/src/mhd2/conn_timeout.c b/src/mhd2/conn_timeout.c @@ -0,0 +1,169 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later OR (GPL-2.0-or-later WITH eCos-exception-2.0) */ +/* + This file is part of GNU libmicrohttpd. + Copyright (C) 2025 Evgeny Grin (Karlson2k) + + GNU libmicrohttpd is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + GNU libmicrohttpd is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + Alternatively, you can redistribute GNU libmicrohttpd and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version, together + with the eCos exception, as follows: + + As a special exception, if other files instantiate templates or + use macros or inline functions from this file, or you compile this + file and link it with other works to produce a work based on this + file, this file does not by itself cause the resulting work to be + covered by the GNU General Public License. However the source code + for this file must still be made available in accordance with + section (3) of the GNU General Public License v2. + + This exception does not invalidate any other reasons why a work + based on this file might be covered by the GNU General Public + License. + + You should have received copies of the GNU Lesser General Public + License and the GNU General Public License along with this library; + if not, see <https://www.gnu.org/licenses/>. +*/ + +/** + * @file src/mhd2/conn_timeout.c + * @brief The definitions of connection timeout handling functions + * @author Karlson2k (Evgeny Grin) + */ + +#include "mhd_sys_options.h" + +#include "sys_bool_type.h" + +#include "mhd_mono_clock.h" + +#include "mhd_daemon.h" +#include "mhd_connection.h" + +#include "daemon_logger.h" + +#include "conn_timeout.h" + +MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ bool +mhd_stream_is_timeout_expired (struct MHD_Connection *restrict c) +{ + const uint_fast64_t timeout = c->connection_timeout_ms; + uint_fast64_t now; + uint_fast64_t since_actv; + + mhd_assert (! c->suspended); + + if (0 == timeout) + return false; + + now = mhd_monotonic_msec_counter (); // TODO: Get and use timer value one time only per round + since_actv = now - c->last_activity; + /* Keep the next lines in sync with #connection_get_wait() to avoid + * undesired side-effects like busy-waiting. */ + if (timeout < since_actv) + { + const uint_fast64_t jump_back = c->last_activity - now; + if (jump_back < since_actv) + { + /* Very unlikely that it is more than quarter-million years pause. + * More likely that system clock jumps back. */ + if (4000 >= jump_back) + { + c->last_activity = now; /* Avoid repetitive messages. + Warn: the order of connections sorted + by timeout is not updated. */ + mhd_LOG_PRINT (c->daemon, MHD_SC_SYS_CLOCK_JUMP_BACK_CORRECTED, \ + mhd_LOG_FMT ("Detected system clock %u " \ + "milliseconds jump back."), + (unsigned int) jump_back); + return false; + } + mhd_LOG_PRINT (c->daemon, MHD_SC_SYS_CLOCK_JUMP_BACK_LARGE, \ + mhd_LOG_FMT ("Detected too large system clock %" \ + PRIuFAST64 " milliseconds jump back"), + jump_back); + } + return true; + } + return false; +} + + +MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ void +mhd_stream_update_activity_mark (struct MHD_Connection *restrict c) +{ + struct MHD_Daemon *const restrict d = c->daemon; +#if defined(MHD_SUPPORT_THREADS) + mhd_assert (! mhd_D_HAS_WORKERS (d)); +#endif /* MHD_SUPPORT_THREADS */ + + mhd_assert (! c->suspended); + + if (0 == c->connection_timeout_ms) + return; /* Skip update of activity for connections + without timeout timer. */ + + c->last_activity = mhd_monotonic_msec_counter (); // TODO: Get and use time value one time per round + if (mhd_D_HAS_THR_PER_CONN (d)) + return; /* each connection has personal timeout */ + + if (c->connection_timeout_ms != d->conns.cfg.timeout_milsec) + return; /* custom timeout, no need to move it in "normal" DLL */ + + /* move connection to head of timeout list (by remove + add operation) */ + mhd_DLINKEDL_DEL_D (&(d->conns.def_timeout), c, by_timeout); + mhd_DLINKEDL_INS_FIRST_D (&(d->conns.def_timeout), c, by_timeout); +} + + +MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ void +mhd_stream_resumed_activity_mark (struct MHD_Connection *restrict c) +{ + struct MHD_Daemon *const restrict d = c->daemon; +#if defined(MHD_SUPPORT_THREADS) + mhd_assert (! mhd_D_HAS_WORKERS (d)); +#endif /* MHD_SUPPORT_THREADS */ + + mhd_assert (! c->suspended); + mhd_assert (c->resuming); + + /* Update of activity for connections without timeout timer unless + * no timeout is set */ + if (0 != c->connection_timeout_ms) + c->last_activity = mhd_monotonic_msec_counter (); // TODO: Get and use time value one time per round + + if (mhd_D_HAS_THR_PER_CONN (d)) + return; /* each connection has personal timeout */ + + if (c->connection_timeout_ms == d->conns.cfg.timeout_milsec) + mhd_DLINKEDL_INS_FIRST_D (&(d->conns.def_timeout), c, by_timeout); + else + mhd_DLINKEDL_INS_FIRST_D (&(d->conns.cust_timeout), c, by_timeout); +} + + +MHD_INTERNAL +MHD_FN_PAR_NONNULL_ALL_ void +mhd_conn_remove_from_timeout_lists (struct MHD_Connection *restrict c) +{ + if (mhd_D_HAS_THR_PER_CONN (c->daemon)) + return; + + if (c->connection_timeout_ms == c->daemon->conns.cfg.timeout_milsec) + mhd_DLINKEDL_DEL_D (&(c->daemon->conns.def_timeout), \ + c, by_timeout); + else + mhd_DLINKEDL_DEL_D (&(c->daemon->conns.cust_timeout), \ + c, by_timeout); +} diff --git a/src/mhd2/conn_timeout.h b/src/mhd2/conn_timeout.h @@ -0,0 +1,92 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later OR (GPL-2.0-or-later WITH eCos-exception-2.0) */ +/* + This file is part of GNU libmicrohttpd. + Copyright (C) 2025 Evgeny Grin (Karlson2k) + + GNU libmicrohttpd is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + GNU libmicrohttpd is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + Alternatively, you can redistribute GNU libmicrohttpd and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version, together + with the eCos exception, as follows: + + As a special exception, if other files instantiate templates or + use macros or inline functions from this file, or you compile this + file and link it with other works to produce a work based on this + file, this file does not by itself cause the resulting work to be + covered by the GNU General Public License. However the source code + for this file must still be made available in accordance with + section (3) of the GNU General Public License v2. + + This exception does not invalidate any other reasons why a work + based on this file might be covered by the GNU General Public + License. + + You should have received copies of the GNU Lesser General Public + License and the GNU General Public License along with this library; + if not, see <https://www.gnu.org/licenses/>. +*/ + +/** + * @file src/mhd2/conn_timeout.h + * @brief The declarations of connection timeout handling functions + * @author Karlson2k (Evgeny Grin) + */ + +#ifndef MHD_CONN_TIMEOUT_H +#define MHD_CONN_TIMEOUT_H 1 + +#include "mhd_sys_options.h" + +#include "sys_bool_type.h" + +struct MHD_Connection; /* Forward declaration */ + +/** + * Check whether connection's timeout is expired. + * @param c the connection to update + * @return 'true' if connection timeout expired and the connection needs to be + * closed, + * 'false' otherwise + */ +MHD_INTERNAL bool +mhd_stream_is_timeout_expired (struct MHD_Connection *restrict c) +MHD_FN_PAR_NONNULL_ALL_; + + +/** + * Update last activity mark to the current time. + * @param c the connection to update + */ +MHD_INTERNAL void +mhd_stream_update_activity_mark (struct MHD_Connection *restrict c) +MHD_FN_PAR_NONNULL_ALL_; + + +/** + * Update last activity mark on the resumed connection + * @param c the connection to update + */ +MHD_INTERNAL void +mhd_stream_resumed_activity_mark (struct MHD_Connection *restrict c) +MHD_FN_PAR_NONNULL_ALL_; + + +/** + * Remove connection from time-out lists + * @param c the connection to process + */ +MHD_INTERNAL void +mhd_conn_remove_from_timeout_lists (struct MHD_Connection *restrict c) +MHD_FN_PAR_NONNULL_ALL_; + +#endif /* ! MHD_CONN_TIMEOUT_H */ diff --git a/src/mhd2/daemon_add_conn.c b/src/mhd2/daemon_add_conn.c @@ -316,7 +316,7 @@ new_connection_prepare_ (struct MHD_Daemon *restrict daemon, mhd_thread_handle_ID_set_invalid (&c->tid); #endif /* MHD_SUPPORT_THREADS */ c->daemon = daemon; - c->connection_timeout_ms = daemon->conns.cfg.timeout_ms; + c->connection_timeout_ms = daemon->conns.cfg.timeout_milsec; c->event_loop_info = MHD_EVENT_LOOP_INFO_RECV; #ifdef MHD_SUPPORT_HTTPS diff --git a/src/mhd2/daemon_get_info.c b/src/mhd2/daemon_get_info.c @@ -191,13 +191,11 @@ MHD_daemon_get_info_fixed_sz ( #endif } return MHD_SC_OK; - case MHD_DAEMON_INFO_FIXED_DEFAULT_TIMEOUT: - if (sizeof(output_buf->v_default_timeout_uint) > output_buf_size) + case MHD_DAEMON_INFO_FIXED_DEFAULT_TIMEOUT_MILSEC: + if (sizeof(output_buf->v_default_timeout_milsec_uint32) > output_buf_size) return MHD_SC_INFO_GET_BUFF_TOO_SMALL; - output_buf->v_default_timeout_uint = - (unsigned int) (daemon->conns.cfg.timeout_ms / 1000u); - mhd_assert (output_buf->v_default_timeout_uint * 1000u == \ - daemon->conns.cfg.timeout_ms); + output_buf->v_default_timeout_milsec_uint32 = + daemon->conns.cfg.timeout_milsec; return MHD_SC_OK; case MHD_DAEMON_INFO_FIXED_GLOBAL_CONNECTION_LIMIT: if (sizeof(output_buf->v_global_connection_limit_uint) > output_buf_size) diff --git a/src/mhd2/daemon_options.h b/src/mhd2/daemon_options.h @@ -150,10 +150,10 @@ struct DaemonOptions /** - * Value for #MHD_D_O_DEFAULT_TIMEOUT. - * the in seconds, zero for no timeout + * Value for #MHD_D_O_DEFAULT_TIMEOUT_MILSEC. + * the timeout in milliseconds, zero for no timeout */ - unsigned int default_timeout; + uint_fast32_t default_timeout_milsec; /** diff --git a/src/mhd2/daemon_set_options.c b/src/mhd2/daemon_set_options.c @@ -188,8 +188,8 @@ MHD_daemon_set_options ( len + 1u); } continue; - case MHD_D_O_DEFAULT_TIMEOUT: - settings->default_timeout = option->val.default_timeout; + case MHD_D_O_DEFAULT_TIMEOUT_MILSEC: + settings->default_timeout_milsec = option->val.default_timeout_milsec; continue; case MHD_D_O_GLOBAL_CONNECTION_LIMIT: settings->global_connection_limit = option->val.global_connection_limit; diff --git a/src/mhd2/daemon_start.c b/src/mhd2/daemon_start.c @@ -47,6 +47,8 @@ #include "mhd_assert.h" #include "mhd_unreachable.h" +#include "mhd_constexpr.h" + #include "sys_bool_type.h" #include "sys_base_types.h" #include "sys_malloc.h" @@ -152,8 +154,7 @@ MHD_FN_MUST_CHECK_RESULT_ enum MHD_StatusCode daemon_set_basic_settings (struct MHD_Daemon *restrict d, struct DaemonOptions *restrict s) { - static const uint_fast64_t max_timeout_ms_value = - ((uint_fast64_t) ~((uint_fast64_t) 0)) / 8; + mhd_constexpr uint_fast64_t max_timeout_ms_value = 1209600000u; #ifdef MHD_SUPPORT_HTTP2 // TODO: make it configurable @@ -169,11 +170,9 @@ daemon_set_basic_settings (struct MHD_Daemon *restrict d, d->req_cfg.suppress_date = (MHD_NO != s->suppress_date_header); - d->conns.cfg.timeout_ms = ((uint_fast64_t) s->default_timeout) * 1000u; - if (d->conns.cfg.timeout_ms / 1000u != s->default_timeout) - d->conns.cfg.timeout_ms = max_timeout_ms_value; - else if (max_timeout_ms_value < d->conns.cfg.timeout_ms) - d->conns.cfg.timeout_ms = max_timeout_ms_value; + d->conns.cfg.timeout_milsec = s->default_timeout_milsec; + if (max_timeout_ms_value < d->conns.cfg.timeout_milsec) + d->conns.cfg.timeout_milsec = max_timeout_ms_value; d->conns.cfg.per_ip_limit = s->per_ip_limit; diff --git a/src/mhd2/events_process.c b/src/mhd2/events_process.c @@ -74,6 +74,7 @@ #include "mhd_daemon.h" #include "mhd_connection.h" +#include "conn_timeout.h" #include "conn_mark_ready.h" #include "daemon_logger.h" #include "daemon_add_conn.h" diff --git a/src/mhd2/mhd_daemon.h b/src/mhd2/mhd_daemon.h @@ -953,7 +953,7 @@ struct mhd_DaemonConnectionsSettings /** * Connection's default timeout value (in milliseconds) */ - uint_fast64_t timeout_ms; + uint_fast32_t timeout_milsec; /** * Connection's memory pool size diff --git a/src/mhd2/stream_funcs.c b/src/mhd2/stream_funcs.c @@ -73,6 +73,7 @@ #include "mhd_mono_clock.h" #include "daemon_logger.h" #include "daemon_funcs.h" +#include "conn_timeout.h" #include "conn_mark_ready.h" #include "stream_process_reply.h" #include "extr_events_funcs.h" @@ -637,124 +638,6 @@ mhd_stream_finish_req_serving (struct MHD_Connection *restrict c, } -MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ bool -mhd_stream_is_timeout_expired (struct MHD_Connection *restrict c) -{ - const uint_fast64_t timeout = c->connection_timeout_ms; - uint_fast64_t now; - uint_fast64_t since_actv; - - mhd_assert (! c->suspended); - - if (0 == timeout) - return false; - - now = mhd_monotonic_msec_counter (); // TODO: Get and use timer value one time only per round - since_actv = now - c->last_activity; - /* Keep the next lines in sync with #connection_get_wait() to avoid - * undesired side-effects like busy-waiting. */ - if (timeout < since_actv) - { - const uint_fast64_t jump_back = c->last_activity - now; - if (jump_back < since_actv) - { - /* Very unlikely that it is more than quarter-million years pause. - * More likely that system clock jumps back. */ - if (4000 >= jump_back) - { - c->last_activity = now; /* Avoid repetitive messages. - Warn: the order of connections sorted - by timeout is not updated. */ - mhd_LOG_PRINT (c->daemon, MHD_SC_SYS_CLOCK_JUMP_BACK_CORRECTED, \ - mhd_LOG_FMT ("Detected system clock %u " \ - "milliseconds jump back."), - (unsigned int) jump_back); - return false; - } - mhd_LOG_PRINT (c->daemon, MHD_SC_SYS_CLOCK_JUMP_BACK_LARGE, \ - mhd_LOG_FMT ("Detected too large system clock %" \ - PRIuFAST64 " milliseconds jump back"), - jump_back); - } - return true; - } - return false; -} - - -/** - * Update last activity mark to the current time.. - * @param c the connection to update - */ -MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ void -mhd_stream_update_activity_mark (struct MHD_Connection *restrict c) -{ - struct MHD_Daemon *const restrict d = c->daemon; -#if defined(MHD_SUPPORT_THREADS) - mhd_assert (! mhd_D_HAS_WORKERS (d)); -#endif /* MHD_SUPPORT_THREADS */ - - mhd_assert (! c->suspended); - - if (0 == c->connection_timeout_ms) - return; /* Skip update of activity for connections - without timeout timer. */ - - c->last_activity = mhd_monotonic_msec_counter (); // TODO: Get and use time value one time per round - if (mhd_D_HAS_THR_PER_CONN (d)) - return; /* each connection has personal timeout */ - - if (c->connection_timeout_ms != d->conns.cfg.timeout_ms) - return; /* custom timeout, no need to move it in "normal" DLL */ - - /* move connection to head of timeout list (by remove + add operation) */ - mhd_DLINKEDL_DEL_D (&(d->conns.def_timeout), c, by_timeout); - mhd_DLINKEDL_INS_FIRST_D (&(d->conns.def_timeout), c, by_timeout); -} - - -MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ void -mhd_stream_resumed_activity_mark (struct MHD_Connection *restrict c) -{ - struct MHD_Daemon *const restrict d = c->daemon; -#if defined(MHD_SUPPORT_THREADS) - mhd_assert (! mhd_D_HAS_WORKERS (d)); -#endif /* MHD_SUPPORT_THREADS */ - - mhd_assert (! c->suspended); - mhd_assert (c->resuming); - - /* Update of activity for connections without timeout timer unless - * no timeout is set */ - if (0 != c->connection_timeout_ms) - c->last_activity = mhd_monotonic_msec_counter (); // TODO: Get and use time value one time per round - - if (mhd_D_HAS_THR_PER_CONN (d)) - return; /* each connection has personal timeout */ - - if (c->connection_timeout_ms == d->conns.cfg.timeout_ms) - mhd_DLINKEDL_INS_FIRST_D (&(d->conns.def_timeout), c, by_timeout); - else - mhd_DLINKEDL_INS_FIRST_D (&(d->conns.cust_timeout), c, by_timeout); -} - - -MHD_INTERNAL -MHD_FN_PAR_NONNULL_ALL_ void -mhd_conn_remove_from_timeout_lists (struct MHD_Connection *restrict c) -{ - if (mhd_D_HAS_THR_PER_CONN (c->daemon)) - return; - - if (c->connection_timeout_ms == c->daemon->conns.cfg.timeout_ms) - mhd_DLINKEDL_DEL_D (&(c->daemon->conns.def_timeout), \ - c, by_timeout); - else - mhd_DLINKEDL_DEL_D (&(c->daemon->conns.cust_timeout), \ - c, by_timeout); -} - - /* return 'true' is lingering needed, 'false' is lingering is not needed */ static MHD_FN_PAR_NONNULL_ALL_ bool conn_start_socket_closing (struct MHD_Connection *restrict c, diff --git a/src/mhd2/stream_funcs.h b/src/mhd2/stream_funcs.h @@ -153,43 +153,6 @@ mhd_stream_finish_req_serving (struct MHD_Connection *restrict c, MHD_FN_PAR_NONNULL_ALL_; /** - * Update last activity mark to the current time. - * @param c the connection to update - */ -MHD_INTERNAL void -mhd_stream_update_activity_mark (struct MHD_Connection *restrict c) -MHD_FN_PAR_NONNULL_ALL_; - - -/** - * Update last activity mark on the resumed connection - * @param c the connection to update - */ -MHD_INTERNAL void -mhd_stream_resumed_activity_mark (struct MHD_Connection *restrict c) -MHD_FN_PAR_NONNULL_ALL_; - - -/** - * Remove connection from time-out lists - * @param c the connection to process - */ -MHD_INTERNAL void -mhd_conn_remove_from_timeout_lists (struct MHD_Connection *restrict c) -MHD_FN_PAR_NONNULL_ALL_; - -/** - * Check whether connection's timeout is expired. - * @param c the connection to update - * @return 'true' if connection timeout expired and connection needs to be - * closed, - * 'false' otherwise - */ -MHD_INTERNAL bool -mhd_stream_is_timeout_expired (struct MHD_Connection *restrict c) -MHD_FN_PAR_NONNULL_ALL_; - -/** * The reason to close the connection */ enum mhd_ConnCloseReason diff --git a/src/mhd2/stream_process_states.c b/src/mhd2/stream_process_states.c @@ -73,6 +73,7 @@ #include "stream_process_reply.h" #include "conn_mark_ready.h" +#include "conn_timeout.h" #ifdef MHD_SUPPORT_UPGRADE # include "upgrade_proc.h" diff --git a/src/tests/client_server/libtest.h b/src/tests/client_server/libtest.h @@ -135,7 +135,7 @@ struct MHDT_Phase * How long is the phase allowed to run at most before * timing out. 0 for no timeout. */ - unsigned int timeout_ms; + uint_fast32_t timeout_ms; /** * How many clients should be run in parallel. diff --git a/src/tools/perf_replies.c b/src/tools/perf_replies.c @@ -1904,16 +1904,16 @@ get_mhd_conn_limit (struct MHD_Daemon *d) } -static unsigned int +static unsigned long get_mhd_def_timeout (struct MHD_Daemon *d) { union MHD_DaemonInfoFixedData d_info; if (MHD_SC_OK != MHD_daemon_get_info_fixed (d, - MHD_DAEMON_INFO_FIXED_DEFAULT_TIMEOUT, + MHD_DAEMON_INFO_FIXED_DEFAULT_TIMEOUT_MILSEC, &d_info)) abort (); - return d_info.v_default_timeout_uint; + return (unsigned long) d_info.v_default_timeout_milsec_uint32; } @@ -2020,7 +2020,9 @@ run_mhd (void) opt_arr[opt_count++] = MHD_D_OPTION_GLOBAL_CONNECTION_LIMIT (tool_params.connections); - opt_arr[opt_count++] = MHD_D_OPTION_DEFAULT_TIMEOUT (tool_params.timeout); + opt_arr[opt_count++] = + MHD_D_OPTION_DEFAULT_TIMEOUT_MILSEC (tool_params.timeout + * (uint_fast32_t) 1000u); if (opt_count > (sizeof(opt_arr) / sizeof(opt_arr[0]))) abort (); @@ -2071,9 +2073,9 @@ run_mhd (void) printf (" Connections limit: %u\n", get_mhd_conn_limit (d)); if (1) { - unsigned int def_timeout = get_mhd_def_timeout (d); - printf (" Connection timeout: %u%s\n", def_timeout, - 0 == def_timeout ? " (no timeout)" : ""); + unsigned long def_timeout = get_mhd_def_timeout (d); + printf (" Connection timeout: %lu %s\n", def_timeout, + 0 == def_timeout ? "(no timeout)" : "milliseconds"); } printf (" 'Date:' header: %s\n", (! get_mhd_suppr_date (d)) ? "Yes" : "No");