Skip to content

gh-43521: Add an opt-in gzip Content-Encoding handler to urllib.request - #154197

Open
soreavis wants to merge 6 commits into
python:mainfrom
soreavis:gh-43521-gzip-handler
Open

soreavis wants to merge 6 commits into
python:mainfrom
soreavis:gh-43521-gzip-handler

Conversation

@soreavis

@soreavis soreavis commented Jul 19, 2026 •

Copy link
Copy Markdown
Contributor

This adds urllib.request.HTTPGzipHandler, an opt-in handler that sends Accept-Encoding: gzip and transparently decodes a Content-Encoding: gzip response body. It's the long-standing request in this issue (originally bpo-1508475).

Opt-in: the handler isn't in build_opener()'s defaults, so urlopen() is unchanged unless you ask for it with build_opener(HTTPGzipHandler). Martin Panter (2014) asked what happens when someone downloads a .tar.gz, and suspected Range requests would break; opt-in keeps both out of the default path. Senthil Kumaran preferred on-by-default in 2012, and on the issue has since called opt-in "better and conservative".

No double-decode: the handler decodes only when it added Accept-Encoding itself. A request with a caller-supplied Accept-Encoding gets the response untouched, which answers the double-decode objection from Serhiy Storchaka (2012) and Martin Panter (2014). After decoding, Content-Encoding and Content-Length are removed, since they describe the compressed body.

Decoding: the response is wrapped in gzip.GzipFile(fileobj=response, mode="rb"), which decompresses as the caller reads and never more than one buffer ahead of a sized read. addclosehook closes the response, because GzipFile doesn't close the file it wraps. Concatenated members are decoded in full. As with gzip.open(), junk after the gzip data raises gzip.BadGzipFile and a truncated stream raises EOFError.

Scope: gzip only. deflate is left out because some servers send it without the zlib wrapper (RFC 9110, section 8.4.1.2); it can be a follow-up. Constructing the handler without zlib raises ImportError.

test_urllib2, test_urllib and test_urllib2_localnet pass.

….request

Add urllib.request.HTTPGzipHandler, an opt-in handler that requests
gzip-compressed responses and transparently decodes them.

The handler is not one of the default build_opener() handlers, so
urlopen()'s behaviour is unchanged unless it is added explicitly.  When
the request has no Accept-Encoding header, the handler adds
"Accept-Encoding: gzip"; if the response then carries
"Content-Encoding: gzip", the body is decoded incrementally as it is
read and the Content-Encoding and Content-Length headers, which describe
the compressed body, are removed.  A request that already carries an
Accept-Encoding header is left untouched, so a caller that means to
handle the compressed response itself still receives it.

Only gzip is handled, and the zlib module is required.
@read-the-docs-community

read-the-docs-community Bot commented Jul 19, 2026 •

Copy link
Copy Markdown

@bedevere-app bedevere-app Bot added the type-feature A feature request or enhancement label Oct 3, 2026
soreavis and others added 3 commits October 27, 2026 09:02
HTTPGzipHandler() raises ImportError without zlib, so on builds that
lack it (such as the MSan CI job) twelve of the tests errored instead
of being skipped.
Comment thread Lib/urllib/request.py Outdated
https_response = http_response


class _GzipReader(io.BufferedIOBase):

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Instead of creating a new _GzipReader class, can we not use https://docs.python.org/3/library/gzip.html gzip.GzipFile(fileobj=response, mode="rb")

This is a critical change that is required before we can merge this PR.

@orsenthil orsenthil left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Let us please use gzip directly instead of hand building it.

@bedevere-app

bedevere-app Bot commented Oct 29, 2026

Copy link
Copy Markdown

A Python core developer has requested some changes be made to your pull request before we can consider merging it. If you could please address their requests along with any other requests in other reviews from core developers that would be appreciated.

Once you have made the requested changes, please leave a comment on this pull request containing the phrase I have made the requested changes; please review again. I will then notify any core developers who have left a review that you're ready for them to take another look at this pull request.

Replace the hand-written _GzipReader with gzip.GzipFile, as requested
in review. Unlike _GzipReader, GzipFile bounds how much a sized read
decompresses. GzipFile does not close the file object it wraps, so
addclosehook closes the response.

gzip is imported inside the handler, so importing urllib.request does
not import it. Junk after the last gzip member now raises BadGzipFile,
as it does with gzip.open(), so the test that expected it to be
ignored is removed.
@soreavis

Copy link
Copy Markdown
Contributor Author

@orsenthil, done in aaaa85a. _GzipReader is gone and the response is wrapped in gzip.GzipFile(fileobj=response, mode="rb"). GzipFile doesn't close the file it wraps, so addclosehook closes the response.

This also fixes a memory bug in my reader: it decompressed each 128 KiB it read in one go, so a read(64) of gzipped zeros peaked at 257 MiB, against 0.4 MiB now. Errors now match gzip.open(), so junk after the gzip data raises BadGzipFile. I dropped the test that expected it to be ignored, and corrected the PR description to match.

I have made the requested changes; please review again

@bedevere-app

bedevere-app Bot commented Oct 29, 2026

Copy link
Copy Markdown

Thanks for making the requested changes!

@orsenthil: please review the changes made to this pull request.

@bedevere-app
bedevere-app Bot requested a review from orsenthil October 29, 2026 20:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

awaiting change review type-feature A feature request or enhancement

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants