Pārlūkot izejas kodu

Ignore an SSE retry field that is not all digits (#2591)

parse_sse_line checked only the error code of from_chars, which accepts
a leading '-' and stops at the first non-digit, so retry: -1 made the
client reconnect without waiting and retry: 10s set 10 ms. The SSE spec
ignores a retry value that is not all ASCII digits.
KBS 1 nedēļu atpakaļ
vecāks
revīzija
3330d0eb06
2 mainītis faili ar 60 papildinājumiem un 1 dzēšanām
  1. 2 1
      httplib.h
  2. 58 0
      test/test.cc

+ 2 - 1
httplib.h

@@ -4914,7 +4914,8 @@ inline bool SSEClient::parse_sse_line(const std::string &line, SSEMessage &msg,
     msg.id = value;
   } else if (field == "retry") {
     // Parse retry interval in milliseconds
-    {
+    // Per the SSE spec, a value that is not all ASCII digits is ignored.
+    if (detail::is_numeric(value)) {
       int v = 0;
       auto res =
           detail::from_chars(value.data(), value.data() + value.size(), v);

+ 58 - 0
test/test.cc

@@ -22647,6 +22647,64 @@ TEST_F(SSEIntegrationTest, AutoReconnectAfterDisconnect) {
   EXPECT_GE(message_count.load(), 2);
 }
 
+// Test: A retry field that is not all ASCII digits is ignored
+TEST_F(SSEIntegrationTest, NonDigitRetryFieldIgnored) {
+  std::atomic<int> connection_count{0};
+
+  server_->Get("/bad-retry",
+               [&connection_count](const Request &, Response &res) {
+                 connection_count.fetch_add(1);
+                 res.set_chunked_content_provider(
+                     "text/event-stream", [](size_t offset, DataSink &sink) {
+                       if (offset == 0) {
+                         std::string event = "retry: -1\ndata: hello\n\n";
+                         sink.write(event.data(), event.size());
+                       }
+                       return false;
+                     });
+               });
+
+  Client client("localhost", get_port());
+  sse::SSEClient sse(client, "/bad-retry");
+
+  sse.set_reconnect_interval(10000);
+  sse.start_async();
+
+  std::this_thread::sleep_for(std::chrono::milliseconds(500));
+  sse.stop();
+
+  EXPECT_EQ(connection_count.load(), 1);
+}
+
+// Test: A retry field of all ASCII digits sets the reconnection time
+TEST_F(SSEIntegrationTest, DigitRetryFieldApplied) {
+  std::atomic<int> connection_count{0};
+
+  server_->Get("/zero-retry",
+               [&connection_count](const Request &, Response &res) {
+                 connection_count.fetch_add(1);
+                 res.set_chunked_content_provider(
+                     "text/event-stream", [](size_t offset, DataSink &sink) {
+                       if (offset == 0) {
+                         std::string event = "retry: 0\ndata: hello\n\n";
+                         sink.write(event.data(), event.size());
+                       }
+                       return false;
+                     });
+               });
+
+  Client client("localhost", get_port());
+  sse::SSEClient sse(client, "/zero-retry");
+
+  sse.set_reconnect_interval(10000);
+  sse.start_async();
+
+  std::this_thread::sleep_for(std::chrono::milliseconds(500));
+  sse.stop();
+
+  EXPECT_GE(connection_count.load(), 2);
+}
+
 // Test: Last-Event-ID sent on reconnect
 TEST_F(SSEIntegrationTest, LastEventIdSentOnReconnect) {
   std::atomic<int> connection_count{0};