aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorEvgeny Grin (Karlson2k) <k2k@narod.ru>2021-12-26 12:02:04 +0300
committerEvgeny Grin (Karlson2k) <k2k@narod.ru>2021-12-26 12:41:49 +0300
commit73c43a7ef7bdccda1b6b6714d2d64b1714e9cdad (patch)
treeee25bbc5b0ff1c9d773178e7905cff169c53d4d7 /src
parent0dd7f1732a7623929691d12e937211303d6dbd71 (diff)
downloadlibmicrohttpd-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.c69
-rw-r--r--src/microhttpd/daemon.c113
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 */
4189static bool
4190connection_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 */
1867static uint64_t
1868connection_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;