]> git.ipfire.org Git - thirdparty/starlette.git/commitdiff
Don't compress on server sent events (#2871)
authorMarcelo Trylesinski <marcelotryle@gmail.com>
Sat, 22 Feb 2025 12:54:55 +0000 (09:54 -0300)
committerGitHub <noreply@github.com>
Sat, 22 Feb 2025 12:54:55 +0000 (09:54 -0300)
starlette/middleware/gzip.py
tests/middleware/test_gzip.py

index b677063da3702bc0d966b324edb9dc23dcff046e..fc63e91b660abb55e459dd7634f7d84d0fc15ad4 100644 (file)
@@ -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)
index b20a7cb84b57478ac090ca5c8bdd2d20161f5226..38a4e1e35b7fcb982ae6523b165daf67cd2252aa 100644 (file)
@@ -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