Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 18 additions & 5 deletions fastapi/sse.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,20 @@ class EventSourceResponse(StreamingResponse):
media_type = "text/event-stream"


def _check_id_no_null(v: str | None) -> str | None:
def _check_single_line(v: str | None, field_name: str) -> str | None:
if v is not None and ("\r" in v or "\n" in v):
raise ValueError(f"SSE '{field_name}' must be a single line")
return v


def _check_event_single_line(v: str | None) -> str | None:
return _check_single_line(v, "event")


def _check_id_valid(v: str | None) -> str | None:
if v is not None and "\0" in v:
raise ValueError("SSE 'id' must not contain null characters")
return v
return _check_single_line(v, "id")


class ServerSentEvent(BaseModel):
Expand Down Expand Up @@ -86,24 +96,27 @@ class ServerSentEvent(BaseModel):
] = None
event: Annotated[
str | None,
AfterValidator(_check_event_single_line),
Doc(
"""
Optional event type name.

Maps to `addEventListener(event, ...)` on the browser. When omitted,
the browser dispatches on the generic `message` event.
the browser dispatches on the generic `message` event. Must be a
single line.
"""
),
] = None
id: Annotated[
str | None,
AfterValidator(_check_id_no_null),
AfterValidator(_check_id_valid),
Doc(
"""
Optional event ID.

The browser sends this value back as the `Last-Event-ID` header on
automatic reconnection. **Must not contain null (`\\0`) characters.**
automatic reconnection. **Must be a single line** and must not contain
null (`\\0`) characters.
"""
),
] = None
Expand Down
9 changes: 9 additions & 0 deletions tests/test_sse.py
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,15 @@ def test_server_sent_event_null_id_rejected():
ServerSentEvent(data="test", id="has\0null")


@pytest.mark.parametrize("field_name", ["event", "id"])
@pytest.mark.parametrize("value", ["first\nsecond", "first\rsecond", "first\r\nsecond"])
def test_server_sent_event_single_line_fields_reject_newlines(
field_name: str, value: str
):
with pytest.raises(ValueError, match=f"SSE '{field_name}' must be a single line"):
ServerSentEvent(data="test", **{field_name: value})


def test_server_sent_event_negative_retry_rejected():
with pytest.raises(ValueError):
ServerSentEvent(data="test", retry=-1)
Expand Down
Loading