Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bypass GZipMiddleware when response includes Content-Encoding #1901

Merged
merged 2 commits into from Oct 12, 2022
Merged

Bypass GZipMiddleware when response includes Content-Encoding #1901

merged 2 commits into from Oct 12, 2022

Conversation

kklingenberg
Copy link
Contributor

This is a suggestion for GZipMiddleware to not encode responses that already include the Content-Encoding header. It is a minor issue for me when using the middleware in an application that includes regular routes and proxies based on this:

https://gist.github.com/tomchristie/5765e10a90a41c7e57470e2dc700f9db

If the proxied response already has an encoding set, starlette's middleware double-encodes the response.

I can get around it by writing the middleware on my own, such as shown in this PR, but I thought you might want to consider it also. I'm basically imitating Django's GZipMiddleware which claims to have this behaviour.

@Kludex Kludex added this to the Version 0.22.0 milestone Oct 12, 2022
Copy link
Sponsor Member

@Kludex Kludex left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is valid. Can you check my suggestion? 🙏

starlette/middleware/gzip.py Show resolved Hide resolved
starlette/middleware/gzip.py Outdated Show resolved Hide resolved
starlette/middleware/gzip.py Outdated Show resolved Hide resolved
starlette/middleware/gzip.py Outdated Show resolved Hide resolved
starlette/middleware/gzip.py Outdated Show resolved Hide resolved
starlette/middleware/gzip.py Outdated Show resolved Hide resolved
@Kludex Kludex mentioned this pull request Oct 12, 2022
kklingenberg and others added 2 commits October 12, 2022 10:53
Suggestions by Kludex

Co-authored-by: Marcelo Trylesinski <marcelotryle@gmail.com>
@kklingenberg
Copy link
Contributor Author

Great suggestions, they reduce the diff size and make it more readable. Thanks :)

Copy link
Sponsor Member

@Kludex Kludex left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @kklingenberg 🙏

@Kludex Kludex merged commit 858629f into encode:master Oct 12, 2022
@kklingenberg kklingenberg deleted the feat/bypass-gzip-middleware branch October 12, 2022 14:16
simonw added a commit to simonw/asgi-gzip that referenced this pull request Oct 13, 2022
@papb
Copy link

papb commented Nov 25, 2022

I was about to start working on a PR for this, thankfully I noticed that you already did so I just have to update my Starlette version. Thanks @kklingenberg!!

Edit: actually I'm using FastAPI and the latest version (0.87.0) still uses Starlette 0.21.0, so I will have to wait... Thankfully there is already a PR to update it to 0.22.0 so it will probably happen soon... Sorry for the noise.

Comment on lines +81 to +102
def test_gzip_ignored_for_responses_with_encoding_set(test_client_factory):
def homepage(request):
async def generator(bytes, count):
for index in range(count):
yield bytes

streaming = generator(bytes=b"x" * 400, count=10)
return StreamingResponse(
streaming, status_code=200, headers={"Content-Encoding": "br"}
)

app = Starlette(
routes=[Route("/", endpoint=homepage)],
middleware=[Middleware(GZipMiddleware)],
)

client = test_client_factory(app)
response = client.get("/", headers={"accept-encoding": "gzip, br"})
assert response.status_code == 200
assert response.text == "x" * 4000
assert response.headers["Content-Encoding"] == "br"
assert "Content-Length" not in response.headers
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wait, this test creates a StreamingResponse that claims to be encoded as Brotli but actually isn't, since its body is just b"x" * 4000, right? How is it possible that this test passes? Doesn't the client try to process brotli automatically on the response and fails?

>>> import brotli
>>> decompressor = brotli.Decompressor()
>>> decompressor.process(b"x" * 4000)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
brotli.error: BrotliDecoderDecompressStream failed while processing the stream

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've created what I think is a better test here. @Kludex If you're interested, I can submit a PR updating the test here as well.

Copy link
Sponsor Member

@Kludex Kludex Nov 25, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah... It was my bad. This test was modified, you can see how it is on master.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh indeed, I found it

@Kludex
Copy link
Sponsor Member

Kludex commented Nov 25, 2022

Gratitude is rare, and I don't consider it as noise. Thanks for the message :)

aminalaee pushed a commit that referenced this pull request Feb 13, 2023
Co-authored-by: Marcelo Trylesinski <marcelotryle@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants