diff --git a/httplib.h b/httplib.h index 7b717de0..ba3fa7e6 100644 --- a/httplib.h +++ b/httplib.h @@ -4918,9 +4918,12 @@ inline bool SSEClient::parse_sse_line(const std::string &line, SSEMessage &msg, msg.data += value; has_data = true; } else if (field == "id") { - // Empty id is valid (clears the last event ID) - msg.id = value; - has_id = true; + // An id that cannot be sent back as Last-Event-ID is ignored. Empty id is + // valid (clears the last event ID) + if (detail::fields::is_field_value(value)) { + msg.id = value; + has_id = true; + } } else if (field == "retry") { // Parse retry interval in milliseconds // Per the SSE spec, a value that is not all ASCII digits is ignored. diff --git a/test/test.cc b/test/test.cc index e5deedf0..a368a91f 100644 --- a/test/test.cc +++ b/test/test.cc @@ -22869,6 +22869,28 @@ TEST_F(SSEParsingTest, EmptyEventIdClearsLastEventId) { EXPECT_EQ(msgs[2].data, "not sent"); } +TEST_F(SSEParsingTest, UnsendableEventIdKeepsLastEventId) { + // The first connection sets an id and then sends ids that cannot be carried + // in a header; the second echoes back the Last-Event-ID it was reconnected + // with + std::atomic connection_count{0}; + server_->Get("/id-unsendable", [&](const Request &req, Response &res) { + if (connection_count++ == 0) { + res.set_content(std::string("id: 42\n\nid: a") + '\0' + + "b\n\nid: \x01\n\nid: 7\n\nid: 7 \n\n", + "text/event-stream"); + } else { + res.set_content("data: " + req.get_header_value("Last-Event-ID") + + "\n\nevent: end\ndata: end\n\n", + "text/event-stream"); + } + }); + + auto msgs = collect("/id-unsendable"); + ASSERT_EQ(msgs.size(), 1u); + EXPECT_EQ(msgs[0].data, "42"); +} + TEST_F(SSEParsingTest, CompleteEventParsing) { auto msgs = parse("event: notification\nid: evt-42\n" "data: {\"type\":\"alert\"}\nretry: 1000\n\n");