Repository: urllib3/urllib3

Issue:
Support `Content-Encoding: x-gzip`
### Context

I'm using an API that returns a gzipped response, but the reported `Content-Encoding` is `x-gzip` rather than `gzip`, so urllib3 fails to decompress it.

According to [RFC 9110](https://www.rfc-editor.org/rfc/rfc9110.html#section-8.4.1.3):

>  A recipient SHOULD consider "x-gzip" to be equivalent to "gzip".

Therefore, I would expect e.g. `Response.read(decode_content=True)` to automatically decompress such responses.

Unfortunately, I can't find a public server that would produce such responses, so I can't provide a simple reproducer.

### Alternatives

I can set `decode_content=False` and gunzip the content manually.

### Duplicate

> Has the feature been requested before?

Not that I could find.

### Contribution

> Would you be willing to submit a PR?

Probably not.



Discussion:

Patch:
diff --git a/changelog/3174.bugfix.rst b/changelog/3174.bugfix.rst
new file mode 100644
index 00000000..cf3e458e
--- /dev/null
+++ b/changelog/3174.bugfix.rst
@@ -0,0 +1,1 @@
+Fixed decoding Gzip-encoded responses which specified ``x-gzip`` content-encoding.
diff --git a/src/urllib3/response.py b/src/urllib3/response.py
index 367ccfe4..37936f93 100644
--- a/src/urllib3/response.py
+++ b/src/urllib3/response.py
@@ -208,7 +208,9 @@ def _get_decoder(mode: str) -> ContentDecoder:
     if "," in mode:
         return MultiDecoder(mode)
 
-    if mode == "gzip":
+    # According to RFC 9110 section 8.4.1.3, recipients should
+    # consider x-gzip equivalent to gzip
+    if mode in ("gzip", "x-gzip"):
         return GzipDecoder()
 
     if brotli is not None and mode == "br":
@@ -280,7 +282,7 @@ class BytesQueueBuffer:
 
 
 class BaseHTTPResponse(io.IOBase):
-    CONTENT_DECODERS = ["gzip", "deflate"]
+    CONTENT_DECODERS = ["gzip", "x-gzip", "deflate"]
     if brotli is not None:
         CONTENT_DECODERS += ["br"]
     if zstd is not None:


Test patch:
diff --git a/test/test_response.py b/test/test_response.py
index 44033077..98ae544e 100644
--- a/test/test_response.py
+++ b/test/test_response.py
@@ -229,14 +229,15 @@ class TestResponse:
         assert r.read() == b""
         assert r.read() == b""
 
-    def test_chunked_decoding_gzip(self) -> None:
+    @pytest.mark.parametrize("content_encoding", ["gzip", "x-gzip"])
+    def test_chunked_decoding_gzip(self, content_encoding: str) -> None:
         compress = zlib.compressobj(6, zlib.DEFLATED, 16 + zlib.MAX_WBITS)
         data = compress.compress(b"foo")
         data += compress.flush()
 
         fp = BytesIO(data)
         r = HTTPResponse(
-            fp, headers={"content-encoding": "gzip"}, preload_content=False
+            fp, headers={"content-encoding": content_encoding}, preload_content=False
         )
 
         assert r.read(1) == b"f"