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.
This commit is contained in:
KBS
2026-09-28 15:31:20 -04:00
committed by GitHub
parent c1c2b1f4b4
commit 3330d0eb06
2 changed files with 60 additions and 1 deletions
+2 -1
View File
@@ -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
View File
@@ -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};