Hello!

I discovered a problem with closing connections, when 
MHD_OPTION_CONNECTION_TIMEOUT is used. When I set the timeout option to 3 
seconds and connect with a browser, I get a timeout value from 
MHD_get_timeout() of 3000 ms returned. I then sleep the desired time and call 
MHD_run again(). Then, MHD_get_timeout() returns repetitive a timeout of 0 
which causes thousands of calls to MHD_run(), taking up several hundred 
milliseconds and burning CPU time.

I use an external event loop with MHD_DAEMON_INFO_EPOLL_FD.

As reason I found that the condition  timeout < (MHD_monotonic_sec_counter () - 
connection->last_activity) will not be true, after waiting the timeout time. 
The right side will be equal to timeout, so the connection is not closed, 
MHD_get_timeout() continues to return a timeout value of 0, and this runs 
repetitively in a loop for some thousand iterations, until the time jumps to 
the next second.

In the attachment please find a small patch for fixing this issue. It's just 
turning the < into a <=.

Regards,
Alexander Irion

-----------------
Siemens Electronic Design Automation GmbH; Anschrift: Arnulfstraße 201, 80634 
München; Gesellschaft mit beschränkter Haftung; Geschäftsführer: Thomas 
Heurung, Frank Thürauf; Sitz der Gesellschaft: München; Registergericht 
München, HRB 106955
From eb6e3c9c7b6c50ac71c2922d95c35f5faeed9b85 Mon Sep 17 00:00:00 2001
From: Alexander Irion <alexander_ir...@mentor.com>
Date: Wed, 11 Aug 2021 17:13:12 +0200
Subject: [PATCH] Fix busy waiting up to one second using connection timeout

MHD_daemon_get_timeout computes the time to wait as:
timeout = (last_activity + connection_timeout - MHD_monotonic_sec_counter ()) * 1000

After sleeping timeout milliseconds, MHD_monotonic_sec_counter () will be:

MHD_monotonic_sec_counter () = last_activity + connection_timeout

With that (timeout < (MHD_monotonic_sec_counter () - connection->last_activity)) evaluates to false and the MHD_connection_close_ is not done.
For the next cycle, MHD_daemon_get_timeout() will return 0 and this goes on, until MHD_monotonic_sec_counter jumps to the next second.

This bug causes thousands of unnecessary calls to MHD_run, when a connection timed out.
---
 src/lib/connection_call_handlers.c | 4 ++--
 src/microhttpd/connection.c        | 4 ++--
 2 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/src/lib/connection_call_handlers.c b/src/lib/connection_call_handlers.c
index aa082ebb..5f2b9d24 100644
--- a/src/lib/connection_call_handlers.c
+++ b/src/lib/connection_call_handlers.c
@@ -3582,8 +3582,8 @@ MHD_request_handle_idle_ (struct MHD_Request *request)
     time_t timeout;
     timeout = connection->connection_timeout;
     if ( (0 != timeout) &&
-         (timeout < (MHD_monotonic_sec_counter ()
-                     - connection->last_activity)) )
+         (timeout <= (MHD_monotonic_sec_counter ()
+                      - connection->last_activity)) )
     {
       MHD_connection_close_ (connection,
                              MHD_REQUEST_TERMINATED_TIMEOUT_REACHED);
diff --git a/src/microhttpd/connection.c b/src/microhttpd/connection.c
index b2d285e3..f4cd774b 100644
--- a/src/microhttpd/connection.c
+++ b/src/microhttpd/connection.c
@@ -4304,8 +4304,8 @@ MHD_connection_handle_idle (struct MHD_Connection *connection)
     time_t timeout;
     timeout = connection->connection_timeout;
     if ( (0 != timeout) &&
-         (timeout < (MHD_monotonic_sec_counter ()
-                     - connection->last_activity)) )
+         (timeout <= (MHD_monotonic_sec_counter ()
+                      - connection->last_activity)) )
     {
       MHD_connection_close_ (connection,
                              MHD_REQUEST_TERMINATED_TIMEOUT_REACHED);
-- 
2.30.2

Reply via email to