From a9a8dab0cc3cbd05dca37650fc392717b9fe5bbf Mon Sep 17 00:00:00 2001 From: Marcelo Trylesinski Date: Sat, 22 Feb 2025 09:54:55 -0300 Subject: [PATCH] Don't compress on server sent events (#2871) --- starlette/middleware/gzip.py | 6 +++++- tests/middleware/test_gzip.py | 22 ++++++++++++++++++++++ 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/starlette/middleware/gzip.py b/starlette/middleware/gzip.py index b677063d..fc63e91b 100644 --- a/starlette/middleware/gzip.py +++ b/starlette/middleware/gzip.py @@ -5,6 +5,8 @@ import typing from starlette.datastructures import Headers, MutableHeaders from starlette.types import ASGIApp, Message, Receive, Scope, Send +DEFAULT_EXCLUDED_CONTENT_TYPES = ("text/event-stream",) + class GZipMiddleware: def __init__(self, app: ASGIApp, minimum_size: int = 500, compresslevel: int = 9) -> None: @@ -30,6 +32,7 @@ class GZipResponder: self.initial_message: Message = {} self.started = False self.content_encoding_set = False + self.content_type_is_excluded = False self.gzip_buffer = io.BytesIO() self.gzip_file = gzip.GzipFile(mode="wb", fileobj=self.gzip_buffer, compresslevel=compresslevel) @@ -46,7 +49,8 @@ class GZipResponder: self.initial_message = message headers = Headers(raw=self.initial_message["headers"]) self.content_encoding_set = "content-encoding" in headers - elif message_type == "http.response.body" and self.content_encoding_set: + self.content_type_is_excluded = headers.get("content-type", "").startswith(DEFAULT_EXCLUDED_CONTENT_TYPES) + elif message_type == "http.response.body" and (self.content_encoding_set or self.content_type_is_excluded): if not self.started: self.started = True await self.send(self.initial_message) diff --git a/tests/middleware/test_gzip.py b/tests/middleware/test_gzip.py index b20a7cb8..38a4e1e3 100644 --- a/tests/middleware/test_gzip.py +++ b/tests/middleware/test_gzip.py @@ -104,3 +104,25 @@ def test_gzip_ignored_for_responses_with_encoding_set( assert response.text == "x" * 4000 assert response.headers["Content-Encoding"] == "text" assert "Content-Length" not in response.headers + + +def test_gzip_ignored_on_server_sent_events(test_client_factory: TestClientFactory) -> None: + def homepage(request: Request) -> StreamingResponse: + async def generator(bytes: bytes, count: int) -> ContentStream: + for _ in range(count): + yield bytes + + streaming = generator(bytes=b"x" * 400, count=10) + return StreamingResponse(streaming, status_code=200, media_type="text/event-stream") + + app = Starlette( + routes=[Route("/", endpoint=homepage)], + middleware=[Middleware(GZipMiddleware)], + ) + + client = test_client_factory(app) + response = client.get("/", headers={"accept-encoding": "gzip"}) + assert response.status_code == 200 + assert response.text == "x" * 4000 + assert "Content-Encoding" not in response.headers + assert "Content-Length" not in response.headers -- 2.47.2