Jelajahi Sumber

Enforce a minimum SSE reconnect wait to avoid a busy loop (#2592)

SSEClient::wait_for_reconnect() sleeps in 100ms steps until the
reconnect interval has elapsed. With an interval of 0 (for example
"retry: 0" from the server, or set_reconnect_interval(0)) it never
slept at all, so a server that sends "retry: 0" and closes the stream
made the client reconnect in a tight loop. set_max_reconnect_attempts()
does not stop this either, because each successful connection resets
the attempt counter.

Always wait at least one step (100ms). Intervals of 1-99ms already
waited 100ms because of the step size, so only 0 and negative values
change behavior.
yhirose 1 Minggu lalu
induk
melakukan
0715c2739e
3 mengubah file dengan 12 tambahan dan 5 penghapusan
  1. 1 1
      README-sse.md
  2. 8 4
      httplib.h
  3. 3 0
      test/test.cc

+ 1 - 1
README-sse.md

@@ -69,7 +69,7 @@ sse.on_error([](httplib::Error err) { });
 #### Configuration
 
 ```cpp
-// Set reconnect interval (default: 3000ms)
+// Set reconnect interval (default: 3000ms, minimum: 100ms)
 sse.set_reconnect_interval(5000);
 
 // Set max reconnect attempts (default: 0 = unlimited)

+ 8 - 4
httplib.h

@@ -5051,11 +5051,15 @@ inline bool SSEClient::should_reconnect(int count) const {
 }
 
 inline void SSEClient::wait_for_reconnect() {
-  // Use small increments to check running_ flag frequently
+  // Use small increments to check running_ flag frequently.
+  // Always wait at least one increment, so that a zero interval (e.g.
+  // "retry: 0" from the server) cannot cause a busy reconnect loop.
+  const auto step_ms = 100;
+  auto interval_ms = (std::max)(reconnect_interval_ms_, step_ms);
   auto waited = 0;
-  while (running_.load() && waited < reconnect_interval_ms_) {
-    std::this_thread::sleep_for(std::chrono::milliseconds(100));
-    waited += 100;
+  while (running_.load() && waited < interval_ms) {
+    std::this_thread::sleep_for(std::chrono::milliseconds(step_ms));
+    waited += step_ms;
   }
 }
 

+ 3 - 0
test/test.cc

@@ -22702,7 +22702,10 @@ TEST_F(SSEIntegrationTest, DigitRetryFieldApplied) {
   std::this_thread::sleep_for(std::chrono::milliseconds(500));
   sse.stop();
 
+  // The server-supplied interval is applied, but never below 100ms, so
+  // "retry: 0" does not cause a busy reconnect loop
   EXPECT_GE(connection_count.load(), 2);
+  EXPECT_LE(connection_count.load(), 10);
 }
 
 // Test: Last-Event-ID sent on reconnect