diff --git a/httplib.h b/httplib.h index c52ba153..7b717de0 100644 --- a/httplib.h +++ b/httplib.h @@ -4993,6 +4993,7 @@ inline void SSEClient::run_event_loop() { SSEMessage current_msg; auto has_data = false; auto has_id = false; + auto first_line = true; while (running_.load() && result.next()) { buffer.append(result.data(), result.size()); @@ -5010,6 +5011,12 @@ inline void SSEClient::run_event_loop() { // one without a colon, sees the same line if (!line.empty() && line.back() == '\r') { line.pop_back(); } + // A UTF-8 BOM at the start of the stream is ignored per the SSE spec + if (first_line) { + first_line = false; + if (line.compare(0, 3, "\xEF\xBB\xBF") == 0) { line.erase(0, 3); } + } + // Parse the line and check if event is complete auto event_complete = parse_sse_line( line, current_msg, reconnect_interval_ms_, has_data, has_id); diff --git a/test/test.cc b/test/test.cc index 0c3c6cd6..e5deedf0 100644 --- a/test/test.cc +++ b/test/test.cc @@ -22793,6 +22793,16 @@ TEST_F(SSEParsingTest, FieldNameOnlyWithCarriageReturn) { EXPECT_EQ(msgs[0].data, ""); } +TEST_F(SSEParsingTest, LeadingByteOrderMarkIgnored) { + // Only a BOM at the start of the stream is ignored + auto msgs = parse("\xEF\xBB\xBF" + "data: first\n\n" + "\xEF\xBB\xBF" + "data: second\n\n"); + ASSERT_EQ(msgs.size(), 1u); + EXPECT_EQ(msgs[0].data, "first"); +} + TEST_F(SSEParsingTest, UnknownFieldsIgnored) { auto msgs = parse("unknown: value\ndata: hello\n\n"); ASSERT_EQ(msgs.size(), 1u);