Skip to content

Commit a9a8dab

Browse files
authored
Don't compress on server sent events (#2871)
1 parent 4ae3213 commit a9a8dab

File tree

2 files changed

+27
-1
lines changed

2 files changed

+27
-1
lines changed

starlette/middleware/gzip.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
from starlette.datastructures import Headers, MutableHeaders
66
from starlette.types import ASGIApp, Message, Receive, Scope, Send
77

8+
DEFAULT_EXCLUDED_CONTENT_TYPES = ("text/event-stream",)
9+
810

911
class GZipMiddleware:
1012
def __init__(self, app: ASGIApp, minimum_size: int = 500, compresslevel: int = 9) -> None:
@@ -30,6 +32,7 @@ def __init__(self, app: ASGIApp, minimum_size: int, compresslevel: int = 9) -> N
3032
self.initial_message: Message = {}
3133
self.started = False
3234
self.content_encoding_set = False
35+
self.content_type_is_excluded = False
3336
self.gzip_buffer = io.BytesIO()
3437
self.gzip_file = gzip.GzipFile(mode="wb", fileobj=self.gzip_buffer, compresslevel=compresslevel)
3538

@@ -46,7 +49,8 @@ async def send_with_gzip(self, message: Message) -> None:
4649
self.initial_message = message
4750
headers = Headers(raw=self.initial_message["headers"])
4851
self.content_encoding_set = "content-encoding" in headers
49-
elif message_type == "http.response.body" and self.content_encoding_set:
52+
self.content_type_is_excluded = headers.get("content-type", "").startswith(DEFAULT_EXCLUDED_CONTENT_TYPES)
53+
elif message_type == "http.response.body" and (self.content_encoding_set or self.content_type_is_excluded):
5054
if not self.started:
5155
self.started = True
5256
await self.send(self.initial_message)

tests/middleware/test_gzip.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,3 +104,25 @@ async def generator(bytes: bytes, count: int) -> ContentStream:
104104
assert response.text == "x" * 4000
105105
assert response.headers["Content-Encoding"] == "text"
106106
assert "Content-Length" not in response.headers
107+
108+
109+
def test_gzip_ignored_on_server_sent_events(test_client_factory: TestClientFactory) -> None:
110+
def homepage(request: Request) -> StreamingResponse:
111+
async def generator(bytes: bytes, count: int) -> ContentStream:
112+
for _ in range(count):
113+
yield bytes
114+
115+
streaming = generator(bytes=b"x" * 400, count=10)
116+
return StreamingResponse(streaming, status_code=200, media_type="text/event-stream")
117+
118+
app = Starlette(
119+
routes=[Route("/", endpoint=homepage)],
120+
middleware=[Middleware(GZipMiddleware)],
121+
)
122+
123+
client = test_client_factory(app)
124+
response = client.get("/", headers={"accept-encoding": "gzip"})
125+
assert response.status_code == 200
126+
assert response.text == "x" * 4000
127+
assert "Content-Encoding" not in response.headers
128+
assert "Content-Length" not in response.headers

0 commit comments

Comments
 (0)