mirror of
https://github.com/yhirose/cpp-httplib.git
synced 2026-10-24 12:04:40 +00:00
Fix SSE parsing of CRLF field names and data-less events
A field line without a colon kept the \r of a CRLF line ending in its name, so "data\r\n" was not recognized. Strip the \r once per line before parsing instead of from each value. An event without a data field was neither dispatched nor cleared, so its event type leaked into the next event and its id only reached last_event_id after a later event was dispatched. Reset the message on every blank line and record the id even when nothing is dispatched. The parsing tests exercised a copy of parse_sse_line that had drifted from the real one. Run them through SSEClient against a local server instead, and add regression tests for the cases above.
This commit is contained in:
1 parent
dd71728110
commit
3d40dfc727
2 files changed
+195
-233
No files matched your search
@@ -4889,10 +4889,10 @@ inline void SSEClient::stop() {
|
||||
inline bool SSEClient::parse_sse_line(const std::string &line, SSEMessage &msg,
|
||||
int &retry_ms, bool &has_data) {
|
||||
// Blank line signals end of event
|
||||
if (line.empty() || line == "\r") { return true; }
|
||||
if (line.empty()) { return true; }
|
||||
|
||||
// Lines starting with ':' are comments (ignored)
|
||||
if (!line.empty() && line[0] == ':') { return false; }
|
||||
if (line[0] == ':') { return false; }
|
||||
|
||||
// Find the colon separator
|
||||
auto colon_pos = line.find(':');
|
||||
@@ -4904,8 +4904,6 @@ inline bool SSEClient::parse_sse_line(const std::string &line, SSEMessage &msg,
|
||||
auto value_start = colon_pos + 1;
|
||||
if (line[value_start] == ' ') { value_start++; }
|
||||
value = line.substr(value_start);
|
||||
// Remove trailing \r if present
|
||||
if (!value.empty() && value.back() == '\r') { value.pop_back(); }
|
||||
}
|
||||
|
||||
// Handle known fields
|
||||
@@ -5003,17 +5001,23 @@ inline void SSEClient::run_event_loop() {
|
||||
auto line = buffer.substr(line_start, newline_pos - line_start);
|
||||
line_start = newline_pos + 1;
|
||||
|
||||
// Strip the \r of a CRLF line ending so that every field, including
|
||||
// one without a colon, sees the same line
|
||||
if (!line.empty() && line.back() == '\r') { line.pop_back(); }
|
||||
|
||||
// Parse the line and check if event is complete
|
||||
auto event_complete =
|
||||
parse_sse_line(line, current_msg, reconnect_interval_ms_, has_data);
|
||||
|
||||
if (event_complete && has_data) {
|
||||
// Update last_event_id for reconnection
|
||||
if (event_complete) {
|
||||
// Update last_event_id for reconnection, even for an event that
|
||||
// has no data
|
||||
if (!current_msg.id.empty()) { last_event_id_ = current_msg.id; }
|
||||
|
||||
// Dispatch event to appropriate handler
|
||||
dispatch_event(current_msg);
|
||||
// An event without a data field is not dispatched
|
||||
if (has_data) { dispatch_event(current_msg); }
|
||||
|
||||
// Reset the message for the next event either way
|
||||
current_msg.clear();
|
||||
has_data = false;
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user