diff options
author | Evgeny Grin (Karlson2k) <k2k@narod.ru> | 2021-12-26 12:02:04 +0300 |
---|---|---|
committer | Evgeny Grin (Karlson2k) <k2k@narod.ru> | 2021-12-26 12:41:49 +0300 |
commit | 73c43a7ef7bdccda1b6b6714d2d64b1714e9cdad (patch) | |
tree | ee25bbc5b0ff1c9d773178e7905cff169c53d4d7 /src | |
parent | 0dd7f1732a7623929691d12e937211303d6dbd71 (diff) | |
download | libmicrohttpd-73c43a7ef7bdccda1b6b6714d2d64b1714e9cdad.tar.gz libmicrohttpd-73c43a7ef7bdccda1b6b6714d2d64b1714e9cdad.zip |
Added workaround for system clock jumps back
Same OSes (namely OpenBSD) has system clocks that must be monotonic,
but in practice clocks may jump forward and back up to 500 milliseconds.
Diffstat (limited to 'src')
-rw-r--r-- | src/microhttpd/connection.c | 69 | ||||
-rw-r--r-- | src/microhttpd/daemon.c | 113 |
2 files changed, 116 insertions, 66 deletions
diff --git a/src/microhttpd/connection.c b/src/microhttpd/connection.c index 02fc0e71..84bfc662 100644 --- a/src/microhttpd/connection.c +++ b/src/microhttpd/connection.c | |||
@@ -4181,6 +4181,56 @@ MHD_connection_handle_write (struct MHD_Connection *connection) | |||
4181 | 4181 | ||
4182 | 4182 | ||
4183 | /** | 4183 | /** |
4184 | * Check whether connection has timed out. | ||
4185 | * @param c the connection to check | ||
4186 | * @return true if connection has timeout and needs to be closed, | ||
4187 | * false otherwise. | ||
4188 | */ | ||
4189 | static bool | ||
4190 | connection_check_timedout (struct MHD_Connection *c) | ||
4191 | { | ||
4192 | const uint64_t timeout = c->connection_timeout_ms; | ||
4193 | uint64_t now; | ||
4194 | uint64_t since_actv; | ||
4195 | |||
4196 | if (c->suspended) | ||
4197 | return false; | ||
4198 | if (0 == timeout) | ||
4199 | return false; | ||
4200 | now = MHD_monotonic_msec_counter (); | ||
4201 | since_actv = now - c->last_activity; | ||
4202 | /* Keep the next lines in sync with #connection_get_wait() to avoid | ||
4203 | * undesired side-effects like busy-waiting. */ | ||
4204 | if (timeout < since_actv) | ||
4205 | { | ||
4206 | if (UINT64_MAX / 2 < since_actv) | ||
4207 | { | ||
4208 | const uint64_t jump_back = c->last_activity - now; | ||
4209 | /* Very unlikely that it is more than quarter-million years pause. | ||
4210 | * More likely that system clock jumps back. */ | ||
4211 | if (5000 >= jump_back) | ||
4212 | { | ||
4213 | #ifdef HAVE_MESSAGES | ||
4214 | MHD_DLOG (c->daemon, | ||
4215 | _ ("Detected system clock %u milliseconds jump back.\n"), | ||
4216 | (unsigned int) jump_back); | ||
4217 | #endif | ||
4218 | return false; | ||
4219 | } | ||
4220 | #ifdef HAVE_MESSAGES | ||
4221 | MHD_DLOG (c->daemon, | ||
4222 | _ ("Detected too large system clock %" PRIu64 " milliseconds " | ||
4223 | "jump back.\n"), | ||
4224 | jump_back); | ||
4225 | #endif | ||
4226 | } | ||
4227 | return true; | ||
4228 | } | ||
4229 | return false; | ||
4230 | } | ||
4231 | |||
4232 | |||
4233 | /** | ||
4184 | * Clean up the state of the given connection and move it into the | 4234 | * Clean up the state of the given connection and move it into the |
4185 | * clean up queue for final disposal. | 4235 | * clean up queue for final disposal. |
4186 | * @remark To be called only from thread that process connection's | 4236 | * @remark To be called only from thread that process connection's |
@@ -4799,21 +4849,12 @@ MHD_connection_handle_idle (struct MHD_Connection *connection) | |||
4799 | } | 4849 | } |
4800 | break; | 4850 | break; |
4801 | } | 4851 | } |
4802 | if (! connection->suspended) | 4852 | if (connection_check_timedout (connection)) |
4803 | { | 4853 | { |
4804 | uint64_t timeout; | 4854 | MHD_connection_close_ (connection, |
4805 | timeout = connection->connection_timeout_ms; | 4855 | MHD_REQUEST_TERMINATED_TIMEOUT_REACHED); |
4806 | /* Keep the next lines in sync with #MHD_get_timeout() to avoid | 4856 | connection->in_idle = false; |
4807 | * undesired side-effects like busy-waiting. */ | 4857 | return MHD_YES; |
4808 | if ( (0 != timeout) && | ||
4809 | (timeout < (MHD_monotonic_msec_counter () | ||
4810 | - connection->last_activity)) ) | ||
4811 | { | ||
4812 | MHD_connection_close_ (connection, | ||
4813 | MHD_REQUEST_TERMINATED_TIMEOUT_REACHED); | ||
4814 | connection->in_idle = false; | ||
4815 | return MHD_YES; | ||
4816 | } | ||
4817 | } | 4858 | } |
4818 | MHD_connection_update_event_loop_info (connection); | 4859 | MHD_connection_update_event_loop_info (connection); |
4819 | ret = MHD_YES; | 4860 | ret = MHD_YES; |
diff --git a/src/microhttpd/daemon.c b/src/microhttpd/daemon.c index d4064dd7..5ca612ea 100644 --- a/src/microhttpd/daemon.c +++ b/src/microhttpd/daemon.c | |||
@@ -1858,6 +1858,54 @@ thread_main_connection_upgrade (struct MHD_Connection *con) | |||
1858 | 1858 | ||
1859 | 1859 | ||
1860 | /** | 1860 | /** |
1861 | * Get maximum wait period for the connection (the amount of time left before | ||
1862 | * connection time out) | ||
1863 | * @param c the connection to check | ||
1864 | * @return the maximum wait period before the connection must be processed | ||
1865 | * again. | ||
1866 | */ | ||
1867 | static uint64_t | ||
1868 | connection_get_wait (struct MHD_Connection *c) | ||
1869 | { | ||
1870 | const uint64_t now = MHD_monotonic_msec_counter (); | ||
1871 | const uint64_t since_actv = now - c->last_activity; | ||
1872 | const uint64_t timeout = c->connection_timeout_ms; | ||
1873 | uint64_t mseconds_left; | ||
1874 | |||
1875 | mhd_assert (0 != timeout); | ||
1876 | /* Keep the next lines in sync with #connection_check_timedout() to avoid | ||
1877 | * undesired side-effects like busy-waiting. */ | ||
1878 | if (timeout < since_actv) | ||
1879 | { | ||
1880 | if (UINT64_MAX / 2 < since_actv) | ||
1881 | { | ||
1882 | const uint64_t jump_back = c->last_activity - now; | ||
1883 | /* Very unlikely that it is more than quarter-million years pause. | ||
1884 | * More likely that system clock jumps back. */ | ||
1885 | if (5000 >= jump_back) | ||
1886 | { /* Jump back is less than 5 seconds, try to recover. */ | ||
1887 | return 100; /* Set wait time to 0.1 seconds */ | ||
1888 | } | ||
1889 | /* Too large jump back */ | ||
1890 | } | ||
1891 | return 0; /* Connection has timed out */ | ||
1892 | } | ||
1893 | else if (since_actv == timeout) | ||
1894 | { | ||
1895 | /* Exact match for timeout and time from last activity. | ||
1896 | * Maybe this is just a precise match or this happens because the timer | ||
1897 | * resolution is too low. | ||
1898 | * Set wait time to 0.1 seconds to avoid busy-waiting with low | ||
1899 | * timer resolution as connection is not timed-out yet. */ | ||
1900 | return 100; | ||
1901 | } | ||
1902 | mseconds_left = timeout - since_actv; | ||
1903 | |||
1904 | return mseconds_left; | ||
1905 | } | ||
1906 | |||
1907 | |||
1908 | /** | ||
1861 | * Main function of the thread that handles an individual | 1909 | * Main function of the thread that handles an individual |
1862 | * connection when #MHD_USE_THREAD_PER_CONNECTION is set. | 1910 | * connection when #MHD_USE_THREAD_PER_CONNECTION is set. |
1863 | * | 1911 | * |
@@ -1995,35 +2043,16 @@ thread_main_handle_connection (void *data) | |||
1995 | if ( (NULL == tvp) && | 2043 | if ( (NULL == tvp) && |
1996 | (timeout > 0) ) | 2044 | (timeout > 0) ) |
1997 | { | 2045 | { |
1998 | const uint64_t since_actv = MHD_monotonic_msec_counter () | 2046 | const uint64_t mseconds_left = connection_get_wait (con); |
1999 | - con->last_activity; | 2047 | #if (SIZEOF_UINT64_T - 2) >= SIZEOF_STRUCT_TIMEVAL_TV_SEC |
2000 | if (since_actv > timeout) | 2048 | if (mseconds_left / 1000 > TIMEVAL_TV_SEC_MAX) |
2001 | { | 2049 | tv.tv_sec = TIMEVAL_TV_SEC_MAX; |
2002 | tv.tv_sec = 0; | ||
2003 | tv.tv_usec = 0; | ||
2004 | } | ||
2005 | else if (since_actv == timeout) | ||
2006 | { | ||
2007 | /* Exact match for timeout and time from last activity. | ||
2008 | * Maybe this is just a precise match or this happens because the timer | ||
2009 | * resolution is too low. | ||
2010 | * Set wait time to 0.1 seconds to avoid busy-waiting with low | ||
2011 | * timer resolution as connection is not yet timed-out */ | ||
2012 | tv.tv_sec = 0; | ||
2013 | tv.tv_usec = 100 * 1000; | ||
2014 | } | ||
2015 | else | 2050 | else |
2016 | { | 2051 | #endif /* (SIZEOF_UINT64_T - 2) >= SIZEOF_STRUCT_TIMEVAL_TV_SEC */ |
2017 | const uint64_t mseconds_left = timeout - since_actv; | 2052 | tv.tv_sec = (_MHD_TIMEVAL_TV_SEC_TYPE) mseconds_left / 1000; |
2018 | #if (SIZEOF_UINT64_T - 1) >= SIZEOF_STRUCT_TIMEVAL_TV_SEC | 2053 | |
2019 | if (mseconds_left / 1000 > TIMEVAL_TV_SEC_MAX) | 2054 | tv.tv_usec = (mseconds_left % 1000) * 1000; |
2020 | tv.tv_sec = TIMEVAL_TV_SEC_MAX; | ||
2021 | else | ||
2022 | #endif /* (SIZEOF_UINT64_T - 1) >= SIZEOF_STRUCT_TIMEVAL_TV_SEC */ | ||
2023 | tv.tv_sec = (_MHD_TIMEVAL_TV_SEC_TYPE) mseconds_left / 1000; | ||
2024 | 2055 | ||
2025 | tv.tv_usec = (mseconds_left % 1000) * 1000; | ||
2026 | } | ||
2027 | tvp = &tv; | 2056 | tvp = &tv; |
2028 | } | 2057 | } |
2029 | if (! use_poll) | 2058 | if (! use_poll) |
@@ -3930,33 +3959,13 @@ MHD_get_timeout (struct MHD_Daemon *daemon, | |||
3930 | 3959 | ||
3931 | if (NULL != earliest_tmot_conn) | 3960 | if (NULL != earliest_tmot_conn) |
3932 | { | 3961 | { |
3933 | const uint64_t since_actv = MHD_monotonic_msec_counter () | 3962 | const uint64_t mssecond_left = connection_get_wait (earliest_tmot_conn); |
3934 | - earliest_tmot_conn->last_activity; | ||
3935 | /* Keep the next lines in sync with #MHD_connection_handle_idle() and | ||
3936 | * with #thread_main_handle_connection(). */ | ||
3937 | if (since_actv > earliest_tmot_conn->connection_timeout_ms) | ||
3938 | *timeout = 0; | ||
3939 | else if (since_actv == earliest_tmot_conn->connection_timeout_ms) | ||
3940 | { | ||
3941 | /* Exact match for timeout and time from last activity. | ||
3942 | * Maybe this is just a precise match or this happens because the timer | ||
3943 | * resolution is too low. | ||
3944 | * Set wait time to 0.1 seconds to avoid busy-waiting with low | ||
3945 | * timer resolution as connection is not yet timed-out */ | ||
3946 | *timeout = 100; | ||
3947 | } | ||
3948 | else | ||
3949 | { | ||
3950 | const uint64_t mssecond_left = earliest_tmot_conn->connection_timeout_ms | ||
3951 | - since_actv; | ||
3952 | |||
3953 | #if SIZEOF_UINT64_T > SIZEOF_UNSIGNED_LONG_LONG | 3963 | #if SIZEOF_UINT64_T > SIZEOF_UNSIGNED_LONG_LONG |
3954 | if (mssecond_left > ULLONG_MAX) | 3964 | if (mssecond_left > ULLONG_MAX) |
3955 | *timeout = ULLONG_MAX; | 3965 | *timeout = ULLONG_MAX; |
3956 | else | 3966 | else |
3957 | #endif /* UINT64 != ULLONG_MAX */ | 3967 | #endif /* UINT64 != ULLONG_MAX */ |
3958 | *timeout = (unsigned long long) mssecond_left; | 3968 | *timeout = (unsigned long long) mssecond_left; |
3959 | } | ||
3960 | return MHD_YES; | 3969 | return MHD_YES; |
3961 | } | 3970 | } |
3962 | return MHD_NO; | 3971 | return MHD_NO; |