Skip to content

Commit d9ca125

Browse files
kklingenbergKludex
authored andcommitted
Bypass GZipMiddleware when response includes Content-Encoding (#1901)
Co-authored-by: Marcelo Trylesinski <marcelotryle@gmail.com>
1 parent 161cc54 commit d9ca125

3 files changed

Lines changed: 34 additions & 0 deletions

File tree

docs/middleware.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,8 @@ The following arguments are supported:
180180

181181
* `minimum_size` - Do not GZip responses that are smaller than this minimum size in bytes. Defaults to `500`.
182182

183+
The middleware won't GZip responses that already have a `Content-Encoding` set, to prevent them from being encoded twice.
184+
183185
## BaseHTTPMiddleware
184186

185187
An abstract class that allows you to write ASGI middleware against a request/response

starlette/middleware/gzip.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ def __init__(self, app: ASGIApp, minimum_size: int, compresslevel: int = 9) -> N
3333
self.send: Send = unattached_send
3434
self.initial_message: Message = {}
3535
self.started = False
36+
self.content_encoding_set = False
3637
self.gzip_buffer = io.BytesIO()
3738
self.gzip_file = gzip.GzipFile(
3839
mode="wb", fileobj=self.gzip_buffer, compresslevel=compresslevel
@@ -48,6 +49,13 @@ async def send_with_gzip(self, message: Message) -> None:
4849
# Don't send the initial message until we've determined how to
4950
# modify the outgoing headers correctly.
5051
self.initial_message = message
52+
headers = Headers(raw=self.initial_message["headers"])
53+
self.content_encoding_set = "content-encoding" in headers
54+
elif message_type == "http.response.body" and self.content_encoding_set:
55+
if not self.started:
56+
self.started = True
57+
await self.send(self.initial_message)
58+
await self.send(message)
5159
elif message_type == "http.response.body" and not self.started:
5260
self.started = True
5361
body = message.get("body", b"")

tests/middleware/test_gzip.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,3 +76,27 @@ async def generator(bytes, count):
7676
assert response.text == "x" * 4000
7777
assert response.headers["Content-Encoding"] == "gzip"
7878
assert "Content-Length" not in response.headers
79+
80+
81+
def test_gzip_ignored_for_responses_with_encoding_set(test_client_factory):
82+
def homepage(request):
83+
async def generator(bytes, count):
84+
for index in range(count):
85+
yield bytes
86+
87+
streaming = generator(bytes=b"x" * 400, count=10)
88+
return StreamingResponse(
89+
streaming, status_code=200, headers={"Content-Encoding": "br"}
90+
)
91+
92+
app = Starlette(
93+
routes=[Route("/", endpoint=homepage)],
94+
middleware=[Middleware(GZipMiddleware)],
95+
)
96+
97+
client = test_client_factory(app)
98+
response = client.get("/", headers={"accept-encoding": "gzip, br"})
99+
assert response.status_code == 200
100+
assert response.text == "x" * 4000
101+
assert response.headers["Content-Encoding"] == "br"
102+
assert "Content-Length" not in response.headers

0 commit comments

Comments
 (0)