Skip to content

Conversation

@dependabot
Copy link
Contributor

@dependabot dependabot bot commented on behalf of github Jan 8, 2026

Bumps urllib3 from 2.6.2 to 2.6.3.

Release notes

Sourced from urllib3's releases.

2.6.3

🚀 urllib3 is fundraising for HTTP/2 support

urllib3 is raising ~$40,000 USD to release HTTP/2 support and ensure long-term sustainable maintenance of the project after a sharp decline in financial support. If your company or organization uses Python and would benefit from HTTP/2 support in Requests, pip, cloud SDKs, and thousands of other projects please consider contributing financially to ensure HTTP/2 support is developed sustainably and maintained for the long-haul.

Thank you for your support.

Changes

Changelog

Sourced from urllib3's changelog.

2.6.3 (2026-01-07)

  • Fixed a high-severity security issue where decompression-bomb safeguards of the streaming API were bypassed when HTTP redirects were followed. (GHSA-38jv-5279-wg99 <https://github.com/urllib3/urllib3/security/advisories/GHSA-38jv-5279-wg99>__)
  • Started treating Retry-After times greater than 6 hours as 6 hours by default. ([#3743](https://github.com/urllib3/urllib3/issues/3743) <https://github.com/urllib3/urllib3/issues/3743>__)
  • Fixed urllib3.connection.VerifiedHTTPSConnection on Emscripten. ([#3752](https://github.com/urllib3/urllib3/issues/3752) <https://github.com/urllib3/urllib3/issues/3752>__)
Commits
  • 0248277 Release 2.6.3
  • 8864ac4 Merge commit from fork
  • 70cecb2 Fix Scorecard issues related to vulnerable dev dependencies (#3755)
  • 41f249a Move "v2.0 Migration Guide" to the end of the table of contents (#3747)
  • fd4dffd Patch VerifiedHTTPSConnection for Emscripten (#3752)
  • 13f0bfd Handle massive values in Retry-After when calculating time to sleep for (#3743)
  • 8c480bf Bump actions/upload-artifact from 5.0.0 to 6.0.0 (#3748)
  • 4b40616 Bump actions/cache from 4.3.0 to 5.0.1 (#3750)
  • 82b8479 Bump actions/download-artifact from 6.0.0 to 7.0.0 (#3749)
  • 34284cb Mention experimental features in the security policy (#3746)
  • Additional commits viewable in compare view

Dependabot compatibility score

Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting @dependabot rebase.


Dependabot commands and options

You can trigger Dependabot actions by commenting on this PR:

  • @dependabot rebase will rebase this PR
  • @dependabot recreate will recreate this PR, overwriting any edits that have been made to it
  • @dependabot merge will merge this PR after your CI passes on it
  • @dependabot squash and merge will squash and merge this PR after your CI passes on it
  • @dependabot cancel merge will cancel a previously requested merge and block automerging
  • @dependabot reopen will reopen this PR if it is closed
  • @dependabot close will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
  • @dependabot show <dependency name> ignore conditions will show all of the ignore conditions of the specified dependency
  • @dependabot ignore this major version will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
  • @dependabot ignore this minor version will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
  • @dependabot ignore this dependency will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
    You can disable automated security fix PRs for this repo from the Security Alerts page.

Bumps [urllib3](https://github.com/urllib3/urllib3) from 2.6.2 to 2.6.3.
- [Release notes](https://github.com/urllib3/urllib3/releases)
- [Changelog](https://github.com/urllib3/urllib3/blob/main/CHANGES.rst)
- [Commits](urllib3/urllib3@2.6.2...2.6.3)

---
updated-dependencies:
- dependency-name: urllib3
  dependency-version: 2.6.3
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
@dependabot dependabot bot added dependencies Pull requests that update a dependency file python Pull requests that update python code labels Jan 8, 2026
@github-actions
Copy link

github-actions bot commented Jan 8, 2026

[puLL-Merge] - urllib3/urllib3@2.6.2..2.6.3

Diff
diff --git .github/SECURITY.md .github/SECURITY.md
index ab04cb3c47..4d81060e5d 100644
--- .github/SECURITY.md
+++ .github/SECURITY.md
@@ -28,6 +28,22 @@ When reporting a potential vulnerability, confirm that it reproduces against
 the latest 2.x version.
 
 
+## Out of Scope Experimental Features
+
+urllib3 contains two experimental modules: `emscripten` and `http2`.
+
+- **http2**: Issues in the HTTP/2 module are not considered security
+  vulnerabilities and should be opened as public GitHub issues after
+  checking missing features tracked in issue
+  https://github.com/urllib3/urllib3/issues/3000.
+- **emscripten**: Potential security issues in the `emscripten` module should
+  be reported privately. Project maintainers will decide if they qualify as
+  security issues to be handled through the standard flow or if they should be
+  considered simple bugs and fixed publicly. Issues that would be considered
+  security vulnerabilities in the core package may not qualify as such for the
+  `emscripten` module.
+
+
 ## Our Process
 
 We follow the [Tidelift security process](https://support.tidelift.com/hc/en-us/articles/4406287910036-Security-process)
diff --git .github/workflows/ci.yml .github/workflows/ci.yml
index a82f616c38..11785fa585 100644
--- .github/workflows/ci.yml
+++ .github/workflows/ci.yml
@@ -136,12 +136,12 @@ jobs:
         uses: browser-actions/setup-firefox@5914774dda97099441f02628f8d46411fcfbd208 # v1.7.0
         if: ${{ matrix.nox-session == 'emscripten(firefox)' }}
       - name: "Install node.js"
-        uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0
+        uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0
         if: ${{ matrix.nox-session == 'emscripten(node)' }}
         with:
           node-version: 22
       - name: Cache pyodide downloads in nox cache
-        uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
+        uses: actions/cache@9255dc7a253b0ccc959486e2bca901246202afeb # v5.0.1
         if: ${{ startsWith(matrix.nox-session, 'emscripten') }}
         with:
           path: .nox/.cache
@@ -156,7 +156,7 @@ jobs:
           NOX_SESSION: ${{ matrix.nox-session != '' && matrix.nox-session || format('test-{0}', matrix.python-version) }}
 
       - name: "Upload coverage data"
-        uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0
+        uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
         with:
           name: coverage-data-${{ matrix.python-version }}-${{ matrix.os }}-${{ matrix.experimental }}-${{ matrix.nox-session }}
           path: ".coverage.*"
@@ -189,7 +189,7 @@ jobs:
         run: uv sync --dev --frozen
 
       - name: "Download coverage data"
-        uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6.0.0
+        uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0
         with:
           pattern: coverage-data-*
           merge-multiple: true
@@ -203,7 +203,7 @@ jobs:
 
       - if: ${{ failure() }}
         name: "Upload report if check failed"
-        uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0
+        uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
         with:
           name: coverage-report
           path: htmlcov
diff --git .github/workflows/publish.yml .github/workflows/publish.yml
index c5b9991cc6..eaeb29af29 100644
--- .github/workflows/publish.yml
+++ .github/workflows/publish.yml
@@ -45,7 +45,7 @@ jobs:
           cd dist && echo "hashes=$(sha256sum * | base64 -w0)" >> $GITHUB_OUTPUT
 
       - name: "Upload dists"
-        uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0
+        uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
         id: upload-artifact
         with:
           name: "dist"
@@ -66,7 +66,7 @@ jobs:
 
     steps:
     - name: "Download dists"
-      uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6.0.0
+      uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0
       with:
         artifact-ids: ${{ needs.build.outputs.artifact-id }}
         path: "dist/"
@@ -99,7 +99,7 @@ jobs:
 
     steps:
     - name: "Download dists"
-      uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6.0.0
+      uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0
       with:
         artifact-ids: ${{ needs.build.outputs.artifact-id }}
         path: "dist/"
diff --git CHANGES.rst CHANGES.rst
index 2de9f01637..36d7ed5dd6 100644
--- CHANGES.rst
+++ CHANGES.rst
@@ -1,3 +1,15 @@
+2.6.3 (2026-01-07)
+==================
+
+- Fixed a high-severity security issue where decompression-bomb safeguards of
+  the streaming API were bypassed when HTTP redirects were followed.
+  (`GHSA-38jv-5279-wg99 <https://github.com/urllib3/urllib3/security/advisories/GHSA-38jv-5279-wg99>`__)
+- Started treating ``Retry-After`` times greater than 6 hours as 6 hours by
+  default. (`#3743 <https://github.com/urllib3/urllib3/issues/3743>`__)
+- Fixed ``urllib3.connection.VerifiedHTTPSConnection`` on Emscripten.
+  (`#3752 <https://github.com/urllib3/urllib3/issues/3752>`__)
+
+
 2.6.2 (2025-12-11)
 ==================
 
diff --git README.md README.md
index 355cb0ff85..96dfa2f91f 100644
--- README.md
+++ README.md
@@ -16,8 +16,7 @@
   <a href="https://bestpractices.coreinfrastructure.org/projects/6227"><img alt="CII Best Practices" src="https://bestpractices.coreinfrastructure.org/projects/6227/badge" /></a>
 </p>
 
-urllib3 is a powerful, *user-friendly* HTTP client for Python. Much of the
-Python ecosystem already uses urllib3 and you should too.
+urllib3 is a powerful, *user-friendly* HTTP client for Python.
 urllib3 brings many critical features that are missing from the Python
 standard libraries:
 
@@ -30,16 +29,13 @@ standard libraries:
 - Proxy support for HTTP and SOCKS.
 - 100% test coverage.
 
-urllib3 is powerful and easy to use:
+... and many more features, but most importantly: Our maintainers have a 15+
+year track record of maintaining urllib3 with the highest code standards and
+attention to security and safety.
+
+[Much of the Python ecosystem already uses urllib3](https://urllib3.readthedocs.io/en/stable/#who-uses)
+and you should too.
 
-```python3
->>> import urllib3
->>> resp = urllib3.request("GET", "http://httpbin.org/robots.txt")
->>> resp.status
-200
->>> resp.data
-b"User-agent: *\nDisallow: /deny\n"
-```
 
 ## Installing
 
@@ -57,8 +53,18 @@ $ cd urllib3
 $ pip install .
 ```
 
+## Getting Started
 
-## Documentation
+urllib3 is easy to use:
+
+```python3
+>>> import urllib3
+>>> resp = urllib3.request("GET", "http://httpbin.org/robots.txt")
+>>> resp.status
+200
+>>> resp.data
+b"User-agent: *\nDisallow: /deny\n"
+```
 
 urllib3 has usage and reference documentation at [urllib3.readthedocs.io](https://urllib3.readthedocs.io).
 
@@ -85,7 +91,9 @@ Tidelift will coordinate the fix and disclosure with maintainers.
 
 ## Maintainers
 
-- Lead: [@illia-v](https://github.com/illia-v) (Illia Volochii)
+Meet our maintainers since 2008:
+
+- Current Lead: [@illia-v](https://github.com/illia-v) (Illia Volochii)
 - [@sethmlarson](https://github.com/sethmlarson) (Seth M. Larson)
 - [@pquentin](https://github.com/pquentin) (Quentin Pradet)
 - [@theacodes](https://github.com/theacodes) (Thea Flowers)
@@ -106,7 +114,7 @@ development](https://urllib3.readthedocs.io/en/latest/sponsors.html).
 ## For Enterprise
 
 Professional support for urllib3 is available as part of the [Tidelift
-Subscription][1].  Tidelift gives software development teams a single source for
+Subscription][1]. Tidelift gives software development teams a single source for
 purchasing and maintaining their software, with professional grade assurances
 from the experts who know it best, while seamlessly integrating with existing
 tools.
diff --git docs/index.rst docs/index.rst
index 1407431a5c..445bc7e9ec 100644
--- docs/index.rst
+++ docs/index.rst
@@ -7,13 +7,13 @@ urllib3
 
    For Enterprise <https://tidelift.com/subscription/pkg/pypi-urllib3?utm_source=pypi-urllib3&utm_medium=referral&utm_campaign=docs>
    Community Discord <https://discord.gg/urllib3>
-   v2-migration-guide
    sponsors
    user-guide
    advanced-usage
    reference/index
    contributing
    changelog
+   v2-migration-guide
 
 urllib3 is a powerful, *user-friendly* HTTP client for Python.
 :ref:`Much of the Python ecosystem already uses <who-uses>` urllib3 and you should too.
diff --git docs/v2-migration-guide.rst docs/v2-migration-guide.rst
index 39b1292fa4..7011caabe6 100644
--- docs/v2-migration-guide.rst
+++ docs/v2-migration-guide.rst
@@ -1,4 +1,4 @@
-v2.0 Migration Guide
+v2 Migration Guide
 ====================
 
 **urllib3 2.x is now available!** Read below for how to get started and what is contained in the new major release.
@@ -61,7 +61,7 @@ ssl module is compiled with OpenSSL 1.0.2.k-fips
 
 .. code-block:: text
 
-  ImportError: urllib3 v2.0 only supports OpenSSL 1.1.1+, currently the 'ssl' module is compiled with 'OpenSSL 1.0.2k-fips  26 Jan 2017'.
+  ImportError: urllib3 v2 only supports OpenSSL 1.1.1+, currently the 'ssl' module is compiled with 'OpenSSL 1.0.2k-fips  26 Jan 2017'.
   See: https://github.com/urllib3/urllib3/issues/2168
 
 Remediation depends on your system:
@@ -178,7 +178,7 @@ ensure your package allows for both urllib3 1.26.x and 2.x to be used:
     "urllib3>=1.26,<3"
   ]
 
-Next you should try installing urllib3 v2.0 locally and run your test suite.
+Next you should try installing urllib3 v2.x locally and run your test suite.
 
 .. code-block:: bash
 
@@ -335,7 +335,7 @@ to support to ensure high security for data traveling
 over the wire.
 
 If you still need to use TLS 1.0 or 1.1 in your application
-you can still upgrade to v2.0, you'll only need to set
+you can still upgrade to v2.x, you'll only need to set
 ``ssl_minimum_version`` to the proper value to continue using
 legacy TLS versions.
 
diff --git dummyserver/app.py dummyserver/app.py
index 0eeb93f739..5b82e9329d 100644
--- dummyserver/app.py
+++ dummyserver/app.py
@@ -233,10 +233,16 @@ async def redirect() -> ResponseReturnValue:
     values = await request.values
     target = values.get("target", "/")
     status = values.get("status", "303 See Other")
+    compressed = values.get("compressed") == "true"
     status_code = status.split(" ")[0]
 
     headers = [("Location", target)]
-    return await make_response("", status_code, headers)
+    if compressed:
+        headers.append(("Content-Encoding", "gzip"))
+        data = gzip.compress(b"foo")
+    else:
+        data = b""
+    return await make_response(data, status_code, headers)
 
 
 @hypercorn_app.route("/redirect_after")
diff --git a/osv-scanner.toml b/osv-scanner.toml
new file mode 100644
index 0000000000..8ea10d80ec
--- /dev/null
+++ osv-scanner.toml
@@ -0,0 +1,4 @@
+# https://google.github.io/osv-scanner/configuration/#override-packages
+[[PackageOverrides]]
+vulnerability.ignore = true
+reason = "Vulnerabilities in dev dependencies should not affect our OpenSSF score."
diff --git src/urllib3/contrib/emscripten/__init__.py src/urllib3/contrib/emscripten/__init__.py
index 8a3c5bebdc..e5b62b25e9 100644
--- src/urllib3/contrib/emscripten/__init__.py
+++ src/urllib3/contrib/emscripten/__init__.py
@@ -14,3 +14,4 @@ def inject_into_urllib3() -> None:
     HTTPSConnectionPool.ConnectionCls = EmscriptenHTTPSConnection
     urllib3.connection.HTTPConnection = EmscriptenHTTPConnection  # type: ignore[misc,assignment]
     urllib3.connection.HTTPSConnection = EmscriptenHTTPSConnection  # type: ignore[misc,assignment]
+    urllib3.connection.VerifiedHTTPSConnection = EmscriptenHTTPSConnection  # type: ignore[assignment]
diff --git src/urllib3/contrib/emscripten/connection.py src/urllib3/contrib/emscripten/connection.py
index a5a353f8e8..63f79dd3be 100644
--- src/urllib3/contrib/emscripten/connection.py
+++ src/urllib3/contrib/emscripten/connection.py
@@ -40,6 +40,7 @@ class EmscriptenHTTPConnection:
     is_verified: bool = False
     proxy_is_verified: bool | None = None
 
+    response_class: type[BaseHTTPResponse] = EmscriptenHttpResponseWrapper
     _response: EmscriptenResponse | None
 
     def __init__(
diff --git src/urllib3/http2/connection.py src/urllib3/http2/connection.py
index 7534b770a8..0a026da0a8 100644
--- src/urllib3/http2/connection.py
+++ src/urllib3/http2/connection.py
@@ -6,9 +6,9 @@
 import types
 import typing
 
-import h2.config  # type: ignore[import-untyped]
-import h2.connection  # type: ignore[import-untyped]
-import h2.events  # type: ignore[import-untyped]
+import h2.config
+import h2.connection
+import h2.events
 
 from .._base_connection import _TYPE_BODY
 from .._collections import HTTPHeaderDict
diff --git src/urllib3/response.py src/urllib3/response.py
index f6266f1a93..ff6d1f4911 100644
--- src/urllib3/response.py
+++ src/urllib3/response.py
@@ -797,7 +797,11 @@ def drain_conn(self) -> None:
         Unread data in the HTTPResponse connection blocks the connection from being released back to the pool.
         """
         try:
-            self.read()
+            self.read(
+                # Do not spend resources decoding the content unless
+                # decoding has already been initiated.
+                decode_content=self._has_decoded_content,
+            )
         except (HTTPError, OSError, BaseSSLError, HTTPException):
             pass
 
diff --git src/urllib3/util/retry.py src/urllib3/util/retry.py
index 0456cceba4..b21b4b64eb 100644
--- src/urllib3/util/retry.py
+++ src/urllib3/util/retry.py
@@ -178,6 +178,11 @@ class Retry:
         Sequence of headers to remove from the request when a response
         indicating a redirect is returned before firing off the redirected
         request.
+
+    :param int retry_after_max: Number of seconds to allow as the maximum for
+        Retry-After headers. Defaults to :attr:`Retry.DEFAULT_RETRY_AFTER_MAX`.
+        Any Retry-After headers larger than this value will be limited to this
+        value.
     """
 
     #: Default methods to be used for ``allowed_methods``
@@ -196,6 +201,10 @@ class Retry:
     #: Default maximum backoff time.
     DEFAULT_BACKOFF_MAX = 120
 
+    # This is undocumented in the RFC. Setting to 6 hours matches other popular libraries.
+    #: Default maximum allowed value for Retry-After headers in seconds
+    DEFAULT_RETRY_AFTER_MAX: typing.Final[int] = 21600
+
     # Backward compatibility; assigned outside of the class.
     DEFAULT: typing.ClassVar[Retry]
 
@@ -219,6 +228,7 @@ def __init__(
             str
         ] = DEFAULT_REMOVE_HEADERS_ON_REDIRECT,
         backoff_jitter: float = 0.0,
+        retry_after_max: int = DEFAULT_RETRY_AFTER_MAX,
     ) -> None:
         self.total = total
         self.connect = connect
@@ -235,6 +245,7 @@ def __init__(
         self.allowed_methods = allowed_methods
         self.backoff_factor = backoff_factor
         self.backoff_max = backoff_max
+        self.retry_after_max = retry_after_max
         self.raise_on_redirect = raise_on_redirect
         self.raise_on_status = raise_on_status
         self.history = history or ()
@@ -256,6 +267,7 @@ def new(self, **kw: typing.Any) -> Self:
             status_forcelist=self.status_forcelist,
             backoff_factor=self.backoff_factor,
             backoff_max=self.backoff_max,
+            retry_after_max=self.retry_after_max,
             raise_on_redirect=self.raise_on_redirect,
             raise_on_status=self.raise_on_status,
             history=self.history,
@@ -320,6 +332,10 @@ def parse_retry_after(self, retry_after: str) -> float:
 
         seconds = max(seconds, 0)
 
+        # Check the seconds do not exceed the specified maximum
+        if seconds > self.retry_after_max:
+            seconds = self.retry_after_max
+
         return seconds
 
     def get_retry_after(self, response: BaseHTTPResponse) -> float | None:
diff --git test/contrib/emscripten/test_emscripten.py test/contrib/emscripten/test_emscripten.py
index b4d8b213ce..5e5e7dd4b0 100644
--- test/contrib/emscripten/test_emscripten.py
+++ test/contrib/emscripten/test_emscripten.py
@@ -302,15 +302,20 @@ def test_timeout_in_worker_streaming(
     run_from_server.run_webworker(worker_code)
 
 
+@pytest.mark.parametrize(
+    "connection_cls", ["HTTPSConnection", "VerifiedHTTPSConnection"]
+)
 def test_index_https(
-    selenium_coverage: typing.Any, testserver_http: PyodideServerInfo
+    selenium_coverage: typing.Any,
+    testserver_http: PyodideServerInfo,
+    connection_cls: str,
 ) -> None:
     @run_in_pyodide  # type: ignore[misc]
-    def pyodide_test(selenium_coverage, host: str, port: int) -> None:  # type: ignore[no-untyped-def]
-        from urllib3.connection import HTTPSConnection
+    def pyodide_test(selenium_coverage, host: str, port: int, connection_cls: str) -> None:  # type: ignore[no-untyped-def]
+        from urllib3 import connection
         from urllib3.response import BaseHTTPResponse
 
-        conn = HTTPSConnection(host, port)
+        conn = getattr(connection, connection_cls)(host, port)
         conn.request("GET", f"https://{host}:{port}/")
         response = conn.getresponse()
         assert isinstance(response, BaseHTTPResponse)
@@ -318,7 +323,10 @@ def pyodide_test(selenium_coverage, host: str, port: int) -> None:  # type: igno
         assert data.decode("utf-8") == "Dummy server!"
 
     pyodide_test(
-        selenium_coverage, testserver_http.http_host, testserver_http.https_port
+        selenium_coverage,
+        testserver_http.http_host,
+        testserver_http.https_port,
+        connection_cls,
     )
 
 
diff --git test/test_http2_connection.py test/test_http2_connection.py
index 52c754ee83..9723dfdf3c 100644
--- test/test_http2_connection.py
+++ test/test_http2_connection.py
@@ -121,9 +121,9 @@ def test_send_bytes(self) -> None:
         conn.sock = mock.MagicMock(
             sendall=mock.Mock(return_value=None),
         )
-        conn._h2_conn._obj.data_to_send = mock.Mock(return_value=b"bar")
-        conn._h2_conn._obj.send_data = mock.Mock(return_value=None)
-        conn._h2_conn._obj.get_next_available_stream_id = mock.Mock(return_value=1)
+        conn._h2_conn._obj.data_to_send = mock.Mock(return_value=b"bar")  # type: ignore[method-assign]
+        conn._h2_conn._obj.send_data = mock.Mock(return_value=None)  # type: ignore[method-assign]
+        conn._h2_conn._obj.get_next_available_stream_id = mock.Mock(return_value=1)  # type: ignore[method-assign]
 
         conn.putrequest("GET", "/")
         conn.endheaders()
@@ -138,9 +138,9 @@ def test_send_str(self) -> None:
         conn.sock = mock.MagicMock(
             sendall=mock.Mock(return_value=None),
         )
-        conn._h2_conn._obj.data_to_send = mock.Mock(return_value=b"bar")
-        conn._h2_conn._obj.send_data = mock.Mock(return_value=None)
-        conn._h2_conn._obj.get_next_available_stream_id = mock.Mock(return_value=1)
+        conn._h2_conn._obj.data_to_send = mock.Mock(return_value=b"bar")  # type: ignore[method-assign]
+        conn._h2_conn._obj.send_data = mock.Mock(return_value=None)  # type: ignore[method-assign]
+        conn._h2_conn._obj.get_next_available_stream_id = mock.Mock(return_value=1)  # type: ignore[method-assign]
 
         conn.putrequest("GET", "/")
         conn.endheaders(message_body=b"foo")
@@ -155,10 +155,10 @@ def test_send_iter(self) -> None:
         conn.sock = mock.MagicMock(
             sendall=mock.Mock(return_value=None),
         )
-        conn._h2_conn._obj.data_to_send = mock.Mock(return_value=b"baz")
-        conn._h2_conn._obj.send_data = mock.Mock(return_value=None)
-        conn._h2_conn._obj.get_next_available_stream_id = mock.Mock(return_value=1)
-        conn._h2_conn._obj.end_stream = mock.Mock(return_value=None)
+        conn._h2_conn._obj.data_to_send = mock.Mock(return_value=b"baz")  # type: ignore[method-assign]
+        conn._h2_conn._obj.send_data = mock.Mock(return_value=None)  # type: ignore[method-assign]
+        conn._h2_conn._obj.get_next_available_stream_id = mock.Mock(return_value=1)  # type: ignore[method-assign]
+        conn._h2_conn._obj.end_stream = mock.Mock(return_value=None)  # type: ignore[method-assign]
 
         conn.putrequest("GET", "/")
         conn.endheaders(message_body=[b"foo", b"bar"])
@@ -191,10 +191,10 @@ def test_send_file_str(self) -> None:
             conn.sock = mock.MagicMock(
                 sendall=mock.Mock(return_value=None),
             )
-            conn._h2_conn._obj.data_to_send = mock.Mock(return_value=b"foo")
-            conn._h2_conn._obj.send_data = mock.Mock(return_value=None)
-            conn._h2_conn._obj.get_next_available_stream_id = mock.Mock(return_value=1)
-            conn._h2_conn._obj.end_stream = mock.Mock(return_value=None)
+            conn._h2_conn._obj.data_to_send = mock.Mock(return_value=b"foo")  # type: ignore[method-assign]
+            conn._h2_conn._obj.send_data = mock.Mock(return_value=None)  # type: ignore[method-assign]
+            conn._h2_conn._obj.get_next_available_stream_id = mock.Mock(return_value=1)  # type: ignore[method-assign]
+            conn._h2_conn._obj.end_stream = mock.Mock(return_value=None)  # type: ignore[method-assign]
 
             with open("foo") as body:
                 conn.putrequest("GET", "/")
@@ -215,10 +215,10 @@ def test_send_file_bytes(self) -> None:
             conn.sock = mock.MagicMock(
                 sendall=mock.Mock(return_value=None),
             )
-            conn._h2_conn._obj.data_to_send = mock.Mock(return_value=b"foo")
-            conn._h2_conn._obj.send_data = mock.Mock(return_value=None)
-            conn._h2_conn._obj.get_next_available_stream_id = mock.Mock(return_value=1)
-            conn._h2_conn._obj.end_stream = mock.Mock(return_value=None)
+            conn._h2_conn._obj.data_to_send = mock.Mock(return_value=b"foo")  # type: ignore[method-assign]
+            conn._h2_conn._obj.send_data = mock.Mock(return_value=None)  # type: ignore[method-assign]
+            conn._h2_conn._obj.get_next_available_stream_id = mock.Mock(return_value=1)  # type: ignore[method-assign]
+            conn._h2_conn._obj.end_stream = mock.Mock(return_value=None)  # type: ignore[method-assign]
 
             body = open("foo", "rb")
             conn.putrequest("GET", "/")
@@ -244,11 +244,11 @@ def test_request_GET(self) -> None:
             sendall=mock.Mock(return_value=None),
         )
         sendall = conn.sock.sendall
-        data_to_send = conn._h2_conn._obj.data_to_send = mock.Mock(return_value=b"foo")
-        send_headers = conn._h2_conn._obj.send_headers = mock.Mock(return_value=None)
-        conn._h2_conn._obj.send_data = mock.Mock(return_value=None)
-        conn._h2_conn._obj.get_next_available_stream_id = mock.Mock(return_value=1)
-        close_connection = conn._h2_conn._obj.close_connection = mock.Mock(
+        data_to_send = conn._h2_conn._obj.data_to_send = mock.Mock(return_value=b"foo")  # type: ignore[method-assign]
+        send_headers = conn._h2_conn._obj.send_headers = mock.Mock(return_value=None)  # type: ignore[method-assign]
+        conn._h2_conn._obj.send_data = mock.Mock(return_value=None)  # type: ignore[method-assign]
+        conn._h2_conn._obj.get_next_available_stream_id = mock.Mock(return_value=1)  # type: ignore[method-assign]
+        close_connection = conn._h2_conn._obj.close_connection = mock.Mock(  # type: ignore[method-assign]
             return_value=None
         )
 
@@ -277,11 +277,11 @@ def test_request_POST(self) -> None:
             sendall=mock.Mock(return_value=None),
         )
         sendall = conn.sock.sendall
-        data_to_send = conn._h2_conn._obj.data_to_send = mock.Mock(return_value=b"foo")
-        send_headers = conn._h2_conn._obj.send_headers = mock.Mock(return_value=None)
-        send_data = conn._h2_conn._obj.send_data = mock.Mock(return_value=None)
-        conn._h2_conn._obj.get_next_available_stream_id = mock.Mock(return_value=1)
-        close_connection = conn._h2_conn._obj.close_connection = mock.Mock(
+        data_to_send = conn._h2_conn._obj.data_to_send = mock.Mock(return_value=b"foo")  # type: ignore[method-assign]
+        send_headers = conn._h2_conn._obj.send_headers = mock.Mock(return_value=None)  # type: ignore[method-assign]
+        send_data = conn._h2_conn._obj.send_data = mock.Mock(return_value=None)  # type: ignore[method-assign]
+        conn._h2_conn._obj.get_next_available_stream_id = mock.Mock(return_value=1)  # type: ignore[method-assign]
+        close_connection = conn._h2_conn._obj.close_connection = mock.Mock(  # type: ignore[method-assign]
             return_value=None
         )
 
@@ -310,8 +310,8 @@ def test_close(self) -> None:
             sendall=mock.Mock(side_effect=Exception("foo")),
         )
         sendall = conn.sock.sendall
-        data_to_send = conn._h2_conn._obj.data_to_send = mock.Mock(return_value=b"foo")
-        close_connection = conn._h2_conn._obj.close_connection = mock.Mock(
+        data_to_send = conn._h2_conn._obj.data_to_send = mock.Mock(return_value=b"foo")  # type: ignore[method-assign]
+        close_connection = conn._h2_conn._obj.close_connection = mock.Mock(  # type: ignore[method-assign]
             return_value=None
         )
 
@@ -332,11 +332,11 @@ def test_request_ignore_chunked(self) -> None:
             sendall=mock.Mock(return_value=None),
         )
         sendall = conn.sock.sendall
-        data_to_send = conn._h2_conn._obj.data_to_send = mock.Mock(return_value=b"foo")
-        send_headers = conn._h2_conn._obj.send_headers = mock.Mock(return_value=None)
-        conn._h2_conn._obj.send_data = mock.Mock(return_value=None)
-        conn._h2_conn._obj.get_next_available_stream_id = mock.Mock(return_value=1)
-        close_connection = conn._h2_conn._obj.close_connection = mock.Mock(
+        data_to_send = conn._h2_conn._obj.data_to_send = mock.Mock(return_value=b"foo")  # type: ignore[method-assign]
+        send_headers = conn._h2_conn._obj.send_headers = mock.Mock(return_value=None)  # type: ignore[method-assign]
+        conn._h2_conn._obj.send_data = mock.Mock(return_value=None)  # type: ignore[method-assign]
+        conn._h2_conn._obj.get_next_available_stream_id = mock.Mock(return_value=1)  # type: ignore[method-assign]
+        close_connection = conn._h2_conn._obj.close_connection = mock.Mock(  # type: ignore[method-assign]
             return_value=None
         )
 
diff --git test/test_retry.py test/test_retry.py
index 85206b90ca..c46bcc0bb0 100644
--- test/test_retry.py
+++ test/test_retry.py
@@ -181,6 +181,18 @@ def test_configurable_backoff_max(self) -> None:
         retry = retry.increment(method="GET")
         assert retry.get_backoff_time() == max_backoff
 
+    def test_configurable_retry_after_max(self) -> None:
+        """Configurable retry after is computed correctly"""
+        max_retry_after = Retry.DEFAULT_RETRY_AFTER_MAX
+
+        retry = Retry()
+        assert retry.parse_retry_after(str(max_retry_after)) == max_retry_after
+        assert retry.parse_retry_after(str(max_retry_after + 1)) == max_retry_after
+
+        retry = Retry(retry_after_max=1)
+        assert retry.parse_retry_after(str(1)) == 1
+        assert retry.parse_retry_after(str(2)) == 1
+
     def test_backoff_jitter(self) -> None:
         """Backoff with jitter is computed correctly"""
         max_backoff = 1
diff --git test/with_dummyserver/test_connectionpool.py test/with_dummyserver/test_connectionpool.py
index ce165e24a1..8d6107aea0 100644
--- test/with_dummyserver/test_connectionpool.py
+++ test/with_dummyserver/test_connectionpool.py
@@ -508,6 +508,25 @@ def test_redirect(self) -> None:
             assert r.status == 200
             assert r.data == b"Dummy server!"
 
+    @mock.patch("urllib3.response.GzipDecoder.decompress")
+    def test_no_decoding_with_redirect_when_preload_disabled(
+        self, gzip_decompress: mock.MagicMock
+    ) -> None:
+        """
+        Test that urllib3 does not attempt to decode a gzipped redirect
+        response when `preload_content` is set to `False`.
+        """
+        with HTTPConnectionPool(self.host, self.port) as pool:
+            # Three requests are expected: two redirects and one final / 200 OK.
+            response = pool.request(
+                "GET",
+                "/redirect",
+                fields={"target": "/redirect?compressed=true", "compressed": "true"},
+                preload_content=False,
+            )
+        assert response.status == 200
+        gzip_decompress.assert_not_called()
+
     def test_303_redirect_makes_request_lose_body(self) -> None:
         with HTTPConnectionPool(self.host, self.port) as pool:
             response = pool.request(
diff --git uv.lock uv.lock
index c414705882..8f0c88065e 100644
--- uv.lock
+++ uv.lock
@@ -2,7 +2,8 @@ version = 1
 revision = 3
 requires-python = ">=3.9"
 resolution-markers = [
-    "python_full_version >= '3.11'",
+    "python_full_version >= '3.13'",
+    "python_full_version >= '3.11' and python_full_version < '3.13'",
     "python_full_version == '3.10.*'",
     "python_full_version < '3.10'",
 ]
@@ -45,7 +46,8 @@ name = "alabaster"
 version = "1.0.0"
 source = { registry = "https://pypi.org/simple" }
 resolution-markers = [
-    "python_full_version >= '3.11'",
+    "python_full_version >= '3.13'",
+    "python_full_version >= '3.11' and python_full_version < '3.13'",
     "python_full_version == '3.10.*'",
 ]
 sdist = { url = "https://files.pythonhosted.org/packages/a6/f8/d9c74d0daf3f742840fd818d69cfae176fa332022fd44e3469487d5a9420/alabaster-1.0.0.tar.gz", hash = "sha256:c00dca57bca26fa62a6d7d0a9fcce65f3e026e9bfe33e9c538fd3fbb2144fd9e", size = 24210, upload-time = "2024-07-26T18:15:03.762Z" }
@@ -573,7 +575,8 @@ name = "click"
 version = "8.3.0"
 source = { registry = "https://pypi.org/simple" }
 resolution-markers = [
-    "python_full_version >= '3.11'",
+    "python_full_version >= '3.13'",
+    "python_full_version >= '3.11' and python_full_version < '3.13'",
     "python_full_version == '3.10.*'",
 ]
 dependencies = [
@@ -724,7 +727,8 @@ name = "coverage"
 version = "7.11.0"
 source = { registry = "https://pypi.org/simple" }
 resolution-markers = [
-    "python_full_version >= '3.11'",
+    "python_full_version >= '3.13'",
+    "python_full_version >= '3.11' and python_full_version < '3.13'",
     "python_full_version == '3.10.*'",
 ]
 sdist = { url = "https://files.pythonhosted.org/packages/1c/38/ee22495420457259d2f3390309505ea98f98a5eed40901cf62196abad006/coverage-7.11.0.tar.gz", hash = "sha256:167bd504ac1ca2af7ff3b81d245dfea0292c5032ebef9d66cc08a7d28c1b8050", size = 811905, upload-time = "2025-10-15T15:15:08.542Z" }
@@ -944,15 +948,16 @@ wheels = [
 
 [[package]]
 name = "filelock"
-version = "3.20.0"
+version = "3.20.2"
 source = { registry = "https://pypi.org/simple" }
 resolution-markers = [
-    "python_full_version >= '3.11'",
+    "python_full_version >= '3.13'",
+    "python_full_version >= '3.11' and python_full_version < '3.13'",
     "python_full_version == '3.10.*'",
 ]
-sdist = { url = "https://files.pythonhosted.org/packages/58/46/0028a82567109b5ef6e4d2a1f04a583fb513e6cf9527fcdd09afd817deeb/filelock-3.20.0.tar.gz", hash = "sha256:711e943b4ec6be42e1d4e6690b48dc175c822967466bb31c0c293f34334c13f4", size = 18922, upload-time = "2025-10-08T18:03:50.056Z" }
+sdist = { url = "https://files.pythonhosted.org/packages/c1/e0/a75dbe4bca1e7d41307323dad5ea2efdd95408f74ab2de8bd7dba9b51a1a/filelock-3.20.2.tar.gz", hash = "sha256:a2241ff4ddde2a7cebddf78e39832509cb045d18ec1a09d7248d6bfc6bfbbe64", size = 19510, upload-time = "2026-01-02T15:33:32.582Z" }
 wheels = [
-    { url = "https://files.pythonhosted.org/packages/76/91/7216b27286936c16f5b4d0c530087e4a54eead683e6b0b73dd0c64844af6/filelock-3.20.0-py3-none-any.whl", hash = "sha256:339b4732ffda5cd79b13f4e2711a31b0365ce445d95d243bb996273d072546a2", size = 16054, upload-time = "2025-10-08T18:03:48.35Z" },
+    { url = "https://files.pythonhosted.org/packages/9a/30/ab407e2ec752aa541704ed8f93c11e2a5d92c168b8a755d818b74a3c5c2d/filelock-3.20.2-py3-none-any.whl", hash = "sha256:fbba7237d6ea277175a32c54bb71ef814a8546d8601269e1bfc388de333974e8", size = 16697, upload-time = "2026-01-02T15:33:31.133Z" },
 ]
 
 [[package]]
@@ -1006,6 +1011,8 @@ wheels = [
     { url = "https://files.pythonhosted.org/packages/7f/91/ae2eb6b7979e2f9b035a9f612cf70f1bf54aad4e1d125129bef1eae96f19/greenlet-3.2.4-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c2ca18a03a8cfb5b25bc1cbe20f3d9a4c80d8c3b13ba3df49ac3961af0b1018d", size = 584358, upload-time = "2025-08-07T13:18:23.708Z" },
     { url = "https://files.pythonhosted.org/packages/f7/85/433de0c9c0252b22b16d413c9407e6cb3b41df7389afc366ca204dbc1393/greenlet-3.2.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:9fe0a28a7b952a21e2c062cd5756d34354117796c6d9215a87f55e38d15402c5", size = 1113550, upload-time = "2025-08-07T13:42:37.467Z" },
     { url = "https://files.pythonhosted.org/packages/a1/8d/88f3ebd2bc96bf7747093696f4335a0a8a4c5acfcf1b757717c0d2474ba3/greenlet-3.2.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:8854167e06950ca75b898b104b63cc646573aa5fef1353d4508ecdd1ee76254f", size = 1137126, upload-time = "2025-08-07T13:18:20.239Z" },
+    { url = "https://files.pythonhosted.org/packages/f1/29/74242b7d72385e29bcc5563fba67dad94943d7cd03552bac320d597f29b2/greenlet-3.2.4-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:f47617f698838ba98f4ff4189aef02e7343952df3a615f847bb575c3feb177a7", size = 1544904, upload-time = "2025-11-04T12:42:04.763Z" },
+    { url = "https://files.pythonhosted.org/packages/c8/e2/1572b8eeab0f77df5f6729d6ab6b141e4a84ee8eb9bc8c1e7918f94eda6d/greenlet-3.2.4-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:af41be48a4f60429d5cad9d22175217805098a9ef7c40bfef44f7669fb9d74d8", size = 1611228, upload-time = "2025-11-04T12:42:08.423Z" },
     { url = "https://files.pythonhosted.org/packages/d6/6f/b60b0291d9623c496638c582297ead61f43c4b72eef5e9c926ef4565ec13/greenlet-3.2.4-cp310-cp310-win_amd64.whl", hash = "sha256:73f49b5368b5359d04e18d15828eecc1806033db5233397748f4ca813ff1056c", size = 298654, upload-time = "2025-08-07T13:50:00.469Z" },
     { url = "https://files.pythonhosted.org/packages/a4/de/f28ced0a67749cac23fecb02b694f6473f47686dff6afaa211d186e2ef9c/greenlet-3.2.4-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:96378df1de302bc38e99c3a9aa311967b7dc80ced1dcc6f171e99842987882a2", size = 272305, upload-time = "2025-08-07T13:15:41.288Z" },
     { url = "https://files.pythonhosted.org/packages/09/16/2c3792cba130000bf2a31c5272999113f4764fd9d874fb257ff588ac779a/greenlet-3.2.4-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:1ee8fae0519a337f2329cb78bd7a8e128ec0f881073d43f023c7b8d4831d5246", size = 632472, upload-time = "2025-08-07T13:42:55.044Z" },
@@ -1015,6 +1022,8 @@ wheels = [
     { url = "https://files.pythonhosted.org/packages/1f/8e/abdd3f14d735b2929290a018ecf133c901be4874b858dd1c604b9319f064/greenlet-3.2.4-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2523e5246274f54fdadbce8494458a2ebdcdbc7b802318466ac5606d3cded1f8", size = 587684, upload-time = "2025-08-07T13:18:25.164Z" },
     { url = "https://files.pythonhosted.org/packages/5d/65/deb2a69c3e5996439b0176f6651e0052542bb6c8f8ec2e3fba97c9768805/greenlet-3.2.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:1987de92fec508535687fb807a5cea1560f6196285a4cde35c100b8cd632cc52", size = 1116647, upload-time = "2025-08-07T13:42:38.655Z" },
     { url = "https://files.pythonhosted.org/packages/3f/cc/b07000438a29ac5cfb2194bfc128151d52f333cee74dd7dfe3fb733fc16c/greenlet-3.2.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:55e9c5affaa6775e2c6b67659f3a71684de4c549b3dd9afca3bc773533d284fa", size = 1142073, upload-time = "2025-08-07T13:18:21.737Z" },
+    { url = "https://files.pythonhosted.org/packages/67/24/28a5b2fa42d12b3d7e5614145f0bd89714c34c08be6aabe39c14dd52db34/greenlet-3.2.4-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c9c6de1940a7d828635fbd254d69db79e54619f165ee7ce32fda763a9cb6a58c", size = 1548385, upload-time = "2025-11-04T12:42:11.067Z" },
+    { url = "https://files.pythonhosted.org/packages/6a/05/03f2f0bdd0b0ff9a4f7b99333d57b53a7709c27723ec8123056b084e69cd/greenlet-3.2.4-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:03c5136e7be905045160b1b9fdca93dd6727b180feeafda6818e6496434ed8c5", size = 1613329, upload-time = "2025-11-04T12:42:12.928Z" },
     { url = "https://files.pythonhosted.org/packages/d8/0f/30aef242fcab550b0b3520b8e3561156857c94288f0332a79928c31a52cf/greenlet-3.2.4-cp311-cp311-win_amd64.whl", hash = "sha256:9c40adce87eaa9ddb593ccb0fa6a07caf34015a29bf8d344811665b573138db9", size = 299100, upload-time = "2025-08-07T13:44:12.287Z" },
     { url = "https://files.pythonhosted.org/packages/44/69/9b804adb5fd0671f367781560eb5eb586c4d495277c93bde4307b9e28068/greenlet-3.2.4-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:3b67ca49f54cede0186854a008109d6ee71f66bd57bb36abd6d0a0267b540cdd", size = 274079, upload-time = "2025-08-07T13:15:45.033Z" },
     { url = "https://files.pythonhosted.org/packages/46/e9/d2a80c99f19a153eff70bc451ab78615583b8dac0754cfb942223d2c1a0d/greenlet-3.2.4-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:ddf9164e7a5b08e9d22511526865780a576f19ddd00d62f8a665949327fde8bb", size = 640997, upload-time = "2025-08-07T13:42:56.234Z" },
@@ -1024,6 +1033,8 @@ wheels = [
     { url = "https://files.pythonhosted.org/packages/19/0d/6660d55f7373b2ff8152401a83e02084956da23ae58cddbfb0b330978fe9/greenlet-3.2.4-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3b3812d8d0c9579967815af437d96623f45c0f2ae5f04e366de62a12d83a8fb0", size = 607586, upload-time = "2025-08-07T13:18:28.544Z" },
     { url = "https://files.pythonhosted.org/packages/8e/1a/c953fdedd22d81ee4629afbb38d2f9d71e37d23caace44775a3a969147d4/greenlet-3.2.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:abbf57b5a870d30c4675928c37278493044d7c14378350b3aa5d484fa65575f0", size = 1123281, upload-time = "2025-08-07T13:42:39.858Z" },
     { url = "https://files.pythonhosted.org/packages/3f/c7/12381b18e21aef2c6bd3a636da1088b888b97b7a0362fac2e4de92405f97/greenlet-3.2.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:20fb936b4652b6e307b8f347665e2c615540d4b42b3b4c8a321d8286da7e520f", size = 1151142, upload-time = "2025-08-07T13:18:22.981Z" },
+    { url = "https://files.pythonhosted.org/packages/27/45/80935968b53cfd3f33cf99ea5f08227f2646e044568c9b1555b58ffd61c2/greenlet-3.2.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:ee7a6ec486883397d70eec05059353b8e83eca9168b9f3f9a361971e77e0bcd0", size = 1564846, upload-time = "2025-11-04T12:42:15.191Z" },
+    { url = "https://files.pythonhosted.org/packages/69/02/b7c30e5e04752cb4db6202a3858b149c0710e5453b71a3b2aec5d78a1aab/greenlet-3.2.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:326d234cbf337c9c3def0676412eb7040a35a768efc92504b947b3e9cfc7543d", size = 1633814, upload-time = "2025-11-04T12:42:17.175Z" },
     { url = "https://files.pythonhosted.org/packages/e9/08/b0814846b79399e585f974bbeebf5580fbe59e258ea7be64d9dfb253c84f/greenlet-3.2.4-cp312-cp312-win_amd64.whl", hash = "sha256:a7d4e128405eea3814a12cc2605e0e6aedb4035bf32697f72deca74de4105e02", size = 299899, upload-time = "2025-08-07T13:38:53.448Z" },
     { url = "https://files.pythonhosted.org/packages/49/e8/58c7f85958bda41dafea50497cbd59738c5c43dbbea5ee83d651234398f4/greenlet-3.2.4-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:1a921e542453fe531144e91e1feedf12e07351b1cf6c9e8a3325ea600a715a31", size = 272814, upload-time = "2025-08-07T13:15:50.011Z" },
     { url = "https://files.pythonhosted.org/packages/62/dd/b9f59862e9e257a16e4e610480cfffd29e3fae018a68c2332090b53aac3d/greenlet-3.2.4-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:cd3c8e693bff0fff6ba55f140bf390fa92c994083f838fece0f63be121334945", size = 641073, upload-time = "2025-08-07T13:42:57.23Z" },
@@ -1033,6 +1044,8 @@ wheels = [
     { url = "https://files.pythonhosted.org/packages/ee/43/3cecdc0349359e1a527cbf2e3e28e5f8f06d3343aaf82ca13437a9aa290f/greenlet-3.2.4-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:23768528f2911bcd7e475210822ffb5254ed10d71f4028387e5a99b4c6699671", size = 610497, upload-time = "2025-08-07T13:18:31.636Z" },
     { url = "https://files.pythonhosted.org/packages/b8/19/06b6cf5d604e2c382a6f31cafafd6f33d5dea706f4db7bdab184bad2b21d/greenlet-3.2.4-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:00fadb3fedccc447f517ee0d3fd8fe49eae949e1cd0f6a611818f4f6fb7dc83b", size = 1121662, upload-time = "2025-08-07T13:42:41.117Z" },
     { url = "https://files.pythonhosted.org/packages/a2/15/0d5e4e1a66fab130d98168fe984c509249c833c1a3c16806b90f253ce7b9/greenlet-3.2.4-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:d25c5091190f2dc0eaa3f950252122edbbadbb682aa7b1ef2f8af0f8c0afefae", size = 1149210, upload-time = "2025-08-07T13:18:24.072Z" },
+    { url = "https://files.pythonhosted.org/packages/1c/53/f9c440463b3057485b8594d7a638bed53ba531165ef0ca0e6c364b5cc807/greenlet-3.2.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:6e343822feb58ac4d0a1211bd9399de2b3a04963ddeec21530fc426cc121f19b", size = 1564759, upload-time = "2025-11-04T12:42:19.395Z" },
+    { url = "https://files.pythonhosted.org/packages/47/e4/3bb4240abdd0a8d23f4f88adec746a3099f0d86bfedb623f063b2e3b4df0/greenlet-3.2.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ca7f6f1f2649b89ce02f6f229d7c19f680a6238af656f61e0115b24857917929", size = 1634288, upload-time = "2025-11-04T12:42:21.174Z" },
     { url = "https://files.pythonhosted.org/packages/0b/55/2321e43595e6801e105fcfdee02b34c0f996eb71e6ddffca6b10b7e1d771/greenlet-3.2.4-cp313-cp313-win_amd64.whl", hash = "sha256:554b03b6e73aaabec3745364d6239e9e012d64c68ccd0b8430c64ccc14939a8b", size = 299685, upload-time = "2025-08-07T13:24:38.824Z" },
     { url = "https://files.pythonhosted.org/packages/22/5c/85273fd7cc388285632b0498dbbab97596e04b154933dfe0f3e68156c68c/greenlet-3.2.4-cp314-cp314-macosx_11_0_universal2.whl", hash = "sha256:49a30d5fda2507ae77be16479bdb62a660fa51b1eb4928b524975b3bde77b3c0", size = 273586, upload-time = "2025-08-07T13:16:08.004Z" },
     { url = "https://files.pythonhosted.org/packages/d1/75/10aeeaa3da9332c2e761e4c50d4c3556c21113ee3f0afa2cf5769946f7a3/greenlet-3.2.4-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:299fd615cd8fc86267b47597123e3f43ad79c9d8a22bebdce535e53550763e2f", size = 686346, upload-time = "2025-08-07T13:42:59.944Z" },
@@ -1040,6 +1053,8 @@ wheels = [
     { url = "https://files.pythonhosted.org/packages/dc/8b/29aae55436521f1d6f8ff4e12fb676f3400de7fcf27fccd1d4d17fd8fecd/greenlet-3.2.4-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:b4a1870c51720687af7fa3e7cda6d08d801dae660f75a76f3845b642b4da6ee1", size = 694659, upload-time = "2025-08-07T13:53:17.759Z" },
     { url = "https://files.pythonhosted.org/packages/92/2e/ea25914b1ebfde93b6fc4ff46d6864564fba59024e928bdc7de475affc25/greenlet-3.2.4-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:061dc4cf2c34852b052a8620d40f36324554bc192be474b9e9770e8c042fd735", size = 695355, upload-time = "2025-08-07T13:18:34.517Z" },
     { url = "https://files.pythonhosted.org/packages/72/60/fc56c62046ec17f6b0d3060564562c64c862948c9d4bc8aa807cf5bd74f4/greenlet-3.2.4-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:44358b9bf66c8576a9f57a590d5f5d6e72fa4228b763d0e43fee6d3b06d3a337", size = 657512, upload-time = "2025-08-07T13:18:33.969Z" },
+    { url = "https://files.pythonhosted.org/packages/23/6e/74407aed965a4ab6ddd93a7ded3180b730d281c77b765788419484cdfeef/greenlet-3.2.4-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:2917bdf657f5859fbf3386b12d68ede4cf1f04c90c3a6bc1f013dd68a22e2269", size = 1612508, upload-time = "2025-11-04T12:42:23.427Z" },
+    { url = "https://files.pythonhosted.org/packages/0d/da/343cd760ab2f92bac1845ca07ee3faea9fe52bee65f7bcb19f16ad7de08b/greenlet-3.2.4-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:015d48959d4add5d6c9f6c5210ee3803a830dce46356e3bc326d6776bde54681", size = 1680760, upload-time = "2025-11-04T12:42:25.341Z" },
     { url = "https://files.pythonhosted.org/packages/e3/a5/6ddab2b4c112be95601c13428db1d8b6608a8b6039816f2ba09c346c08fc/greenlet-3.2.4-cp314-cp314-win_amd64.whl", hash = "sha256:e37ab26028f12dbb0ff65f29a8d3d44a765c61e729647bf2ddfbbed621726f01", size = 303425, upload-time = "2025-08-07T13:32:27.59Z" },
     { url = "https://files.pythonhosted.org/packages/f7/c0/93885c4106d2626bf51fdec377d6aef740dfa5c4877461889a7cf8e565cc/greenlet-3.2.4-cp39-cp39-macosx_11_0_universal2.whl", hash = "sha256:b6a7c19cf0d2742d0809a4c05975db036fdff50cd294a93632d6a310bf9ac02c", size = 269859, upload-time = "2025-08-07T13:16:16.003Z" },
     { url = "https://files.pythonhosted.org/packages/4d/f5/33f05dc3ba10a02dedb1485870cf81c109227d3d3aa280f0e48486cac248/greenlet-3.2.4-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:27890167f55d2387576d1f41d9487ef171849ea0359ce1510ca6e06c8bece11d", size = 627610, upload-time = "2025-08-07T13:43:01.345Z" },
@@ -1049,6 +1064,8 @@ wheels = [
     { url = "https://files.pythonhosted.org/packages/6b/4c/f3de2a8de0e840ecb0253ad0dc7e2bb3747348e798ec7e397d783a3cb380/greenlet-3.2.4-cp39-cp39-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c9913f1a30e4526f432991f89ae263459b1c64d1608c0d22a5c79c287b3c70df", size = 582817, upload-time = "2025-08-07T13:18:35.48Z" },
     { url = "https://files.pythonhosted.org/packages/89/80/7332915adc766035c8980b161c2e5d50b2f941f453af232c164cff5e0aeb/greenlet-3.2.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:b90654e092f928f110e0007f572007c9727b5265f7632c2fa7415b4689351594", size = 1111985, upload-time = "2025-08-07T13:42:42.425Z" },
     { url = "https://files.pythonhosted.org/packages/66/71/1928e2c80197353bcb9b50aa19c4d8e26ee6d7a900c564907665cf4b9a41/greenlet-3.2.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:81701fd84f26330f0d5f4944d4e92e61afe6319dcd9775e39396e39d7c3e5f98", size = 1136137, upload-time = "2025-08-07T13:18:26.168Z" },
+    { url = "https://files.pythonhosted.org/packages/4b/bf/7bd33643e48ed45dcc0e22572f650767832bd4e1287f97434943cc402148/greenlet-3.2.4-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:28a3c6b7cd72a96f61b0e4b2a36f681025b60ae4779cc73c1535eb5f29560b10", size = 1542941, upload-time = "2025-11-04T12:42:27.427Z" },
+    { url = "https://files.pythonhosted.org/packages/9b/74/4bc433f91d0d09a1c22954a371f9df928cb85e72640870158853a83415e5/greenlet-3.2.4-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:52206cd642670b0b320a1fd1cbfd95bca0e043179c1d8a045f2c6109dfe973be", size = 1609685, upload-time = "2025-11-04T12:42:29.242Z" },
     { url = "https://files.pythonhosted.org/packages/89/48/a5dc74dde38aeb2b15d418cec76ed50e1dd3d620ccda84d8199703248968/greenlet-3.2.4-cp39-cp39-win32.whl", hash = "sha256:65458b409c1ed459ea899e939f0e1cdb14f58dbc803f2f93c5eab5694d32671b", size = 281400, upload-time = "2025-08-07T14:02:20.263Z" },
     { url = "https://files.pythonhosted.org/packages/e5/44/342c4591db50db1076b8bda86ed0ad59240e3e1da17806a4cf10a6d0e447/greenlet-3.2.4-cp39-cp39-win_amd64.whl", hash = "sha256:d2e685ade4dafd447ede19c31277a224a239a0a1a4eca4e6390efedf20260cfb", size = 298533, upload-time = "2025-08-07T13:56:34.168Z" },
 ]
@@ -1064,15 +1081,15 @@ wheels = [
 
 [[package]]
 name = "h2"
-version = "4.1.0"
+version = "4.3.0"
 source = { registry = "https://pypi.org/simple" }
 dependencies = [
     { name = "hpack" },
     { name = "hyperframe" },
 ]
-sdist = { url = "https://files.pythonhosted.org/packages/2a/32/fec683ddd10629ea4ea46d206752a95a2d8a48c22521edd70b142488efe1/h2-4.1.0.tar.gz", hash = "sha256:a83aca08fbe7aacb79fec788c9c0bac936343560ed9ec18b82a13a12c28d2abb", size = 2145593, upload-time = "2021-10-05T18:27:47.18Z" }
+sdist = { url = "https://files.pythonhosted.org/packages/1d/17/afa56379f94ad0fe8defd37d6eb3f89a25404ffc71d4d848893d270325fc/h2-4.3.0.tar.gz", hash = "sha256:6c59efe4323fa18b47a632221a1888bd7fde6249819beda254aeca909f221bf1", size = 2152026, upload-time = "2025-08-23T18:12:19.778Z" }
 wheels = [
-    { url = "https://files.pythonhosted.org/packages/2a/e5/db6d438da759efbb488c4f3fbdab7764492ff3c3f953132efa6b9f0e9e53/h2-4.1.0-py3-none-any.whl", hash = "sha256:03a46bcf682256c95b5fd9e9a99c1323584c3eec6440d379b9903d709476bc6d", size = 57488, upload-time = "2021-10-05T18:27:39.977Z" },
+    { url = "https://files.pythonhosted.org/packages/69/b2/119f6e6dcbd96f9069ce9a2665e0146588dc9f88f29549711853645e736a/h2-4.3.0-py3-none-any.whl", hash = "sha256:c438f029a25f7945c69e0ccf0fb951dc3f73a5f6412981daee861431b70e2bdd", size = 61779, upload-time = "2025-08-23T18:12:17.779Z" },
 ]
 
 [[package]]
@@ -1129,7 +1146,8 @@ name = "humanize"
 version = "4.14.0"
 source = { registry = "https://pypi.org/simple" }
 resolution-markers = [
-    "python_full_version >= '3.11'",
+    "python_full_version >= '3.13'",
+    "python_full_version >= '3.11' and python_full_version < '3.13'",
     "python_full_version == '3.10.*'",
 ]
 sdist = { url = "https://files.pythonhosted.org/packages/b6/43/50033d25ad96a7f3845f40999b4778f753c3901a11808a584fed7c00d9f5/humanize-4.14.0.tar.gz", hash = "sha256:2fa092705ea640d605c435b1ca82b2866a1b601cdf96f076d70b79a855eba90d", size = 82939, upload-time = "2025-10-15T13:04:51.214Z" }
@@ -1202,7 +1220,7 @@ name = "importlib-metadata"
 version = "8.7.0"
 source = { registry = "https://pypi.org/simple" }
 dependencies = [
-    { name = "zipp" },
+    { name = "zipp", marker = "python_full_version < '3.11'" },
 ]
 sdist = { url = "https://files.pythonhosted.org/packages/76/66/650a33bd90f786193e4de4b3ad86ea60b53c89b669a5c7be931fac31cdb0/importlib_metadata-8.7.0.tar.gz", hash = "sha256:d13b81ad223b890aa16c5471f2ac3056cf76c5f10f82d6f9292f0b415f389000", size = 56641, upload-time = "2025-04-27T15:29:01.736Z" }
 wheels = [
@@ -1291,7 +1309,8 @@ name = "markdown-it-py"
 version = "4.0.0"
 source = { registry = "https://pypi.org/simple" }
 resolution-markers = [
-    "python_full_version >= '3.11'",
+    "python_full_version >= '3.13'",
+    "python_full_version >= '3.11' and python_full_version < '3.13'",
     "python_full_version == '3.10.*'",
 ]
 dependencies = [
@@ -1426,7 +1445,8 @@ name = "mdit-py-plugins"
 version = "0.5.0"
 source = { registry = "https://pypi.org/simple" }
 resolution-markers = [
-    "python_full_version >= '3.11'",
+    "python_full_version >= '3.13'",
+    "python_full_version >= '3.11' and python_full_version < '3.13'",
     "python_full_version == '3.10.*'",
 ]
 dependencies = [
@@ -1640,7 +1660,8 @@ name = "platformdirs"
 version = "4.5.0"
 source = { registry = "https://pypi.org/simple" }
 resolution-markers = [
-    "python_full_version >= '3.11'",
+    "python_full_version >= '3.13'",
+    "python_full_version >= '3.11' and python_full_version < '3.13'",
     "python_full_version == '3.10.*'",
 ]
 sdist = { url = "https://files.pythonhosted.org/packages/61/33/9611380c2bdb1225fdef633e2a9610622310fed35ab11dac9620972ee088/platformdirs-4.5.0.tar.gz", hash = "sha256:70ddccdd7c99fc5942e9fc25636a8b34d04c24b335100223152c2803e4063312", size = 21632, upload-time = "2025-10-08T17:44:48.791Z" }
@@ -2039,7 +2060,8 @@ name = "sphinx"
 version = "8.2.3"
 source = { registry = "https://pypi.org/simple" }
 resolution-markers = [
-    "python_full_version >= '3.11'",
+    "python_full_version >= '3.13'",
+    "python_full_version >= '3.11' and python_full_version < '3.13'",
 ]
 dependencies = [
     { name = "alabaster", version = "1.0.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" },
@@ -2166,8 +2188,8 @@ name = "taskgroup"
 version = "0.2.2"
 source = { registry = "https://pypi.org/simple" }
 dependencies = [
-    { name = "exceptiongroup" },
-    { name = "typing-extensions" },
+    { name = "exceptiongroup", marker = "python_full_version < '3.11'" },
+    { name = "typing-extensions", marker = "python_full_version < '3.11'" },
 ]
 sdist = { url = "https://files.pythonhosted.org/packages/f0/8d/e218e0160cc1b692e6e0e5ba34e8865dbb171efeb5fc9a704544b3020605/taskgroup-0.2.2.tar.gz", hash = "sha256:078483ac3e78f2e3f973e2edbf6941374fbea81b9c5d0a96f51d297717f4752d", size = 11504, upload-time = "2025-01-03T09:24:13.761Z" }
 wheels = [
@@ -2484,7 +2506,7 @@ source = { registry = "https://pypi.org/simple" }
 dependencies = [
     { name = "distlib" },
     { name = "filelock", version = "3.19.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" },
-    { name = "filelock", version = "3.20.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" },
+    { name = "filelock", version = "3.20.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" },
     { name = "platformdirs", version = "4.4.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" },
     { name = "platformdirs", version = "4.5.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" },
     { name = "typing-extensions", marker = "python_full_version < '3.11'" },
@@ -2505,14 +2527,14 @@ wheels = [
 
 [[package]]
 name = "werkzeug"
-version = "3.1.3"
+version = "3.1.4"
 source = { registry = "https://pypi.org/simple" }
 dependencies = [
     { name = "markupsafe" },
 ]
-sdist = { url = "https://files.pythonhosted.org/packages/9f/69/83029f1f6300c5fb2471d621ab06f6ec6b3324685a2ce0f9777fd4a8b71e/werkzeug-3.1.3.tar.gz", hash = "sha256:60723ce945c19328679790e3282cc758aa4a6040e4bb330f53d30fa546d44746", size = 806925, upload-time = "2024-11-08T15:52:18.093Z" }
+sdist = { url = "https://files.pythonhosted.org/packages/45/ea/b0f8eeb287f8df9066e56e831c7824ac6bab645dd6c7a8f4b2d767944f9b/werkzeug-3.1.4.tar.gz", hash = "sha256:cd3cd98b1b92dc3b7b3995038826c68097dcb16f9baa63abe35f20eafeb9fe5e", size = 864687, upload-time = "2025-11-29T02:15:22.841Z" }
 wheels = [
-    { url = "https://files.pythonhosted.org/packages/52/24/ab44c871b0f07f491e5d2ad12c9bd7358e527510618cb1b803a88e986db1/werkzeug-3.1.3-py3-none-any.whl", hash = "sha256:54b78bf3716d19a65be4fceccc0d1d7b89e608834989dfae50ea87564639213e", size = 224498, upload-time = "2024-11-08T15:52:16.132Z" },
+    { url = "https://files.pythonhosted.org/packages/2f/f9/9e082990c2585c744734f85bec79b5dae5df9c974ffee58fe421652c8e91/werkzeug-3.1.4-py3-none-any.whl", hash = "sha256:2ad50fb9ed09cc3af22c54698351027ace879a0b60a3b5edf5730b2f7d876905", size = 224960, upload-time = "2025-11-29T02:15:21.13Z" },
 ]
 
 [[package]]

Description

This pull request introduces several important changes to the urllib3 project. The primary changes are:

  1. Security and Other Documentation Updates:

    • Addition of guidelines on handling vulnerabilities and issues related to experimental features (emscripten and http2).
    • Updates to the changelog to include fixes for security vulnerabilities and other notable changes in the 2.6.3 release.
  2. Workflow/CICD Enhancements:

    • Upgrades to multiple GitHub Action versions across ci.yml and publish.yml workflows.
  3. Code Changes and Bug Fixes:

    • Adjustments to HTTP redirection logic.
    • Ensuring Emscripten compatibility for VerifiedHTTPSConnection.
    • Additional parameters for retry logic and response handling.
  4. Getting Started Documentation Improvements:

    • Enhanced README with updated sections and minor rephrasing for clarity.
    • Reorganization in the documentation content for better accessibility.
  5. Dependency Management:

    • Chrome a few dependencies and upgrade them to their latest versions in uv.lock.

Possible Issues

  1. Compatibility:

    • Upgrading dependencies and GitHub Actions may lead to unforeseen issues if any backward-incompatible changes were introduced in the newer versions.
  2. Documentation:

    • The movement of sections in the documentation could potentially confuse users who are used to the old structure.

Security Hotspots

  1. Security.md Additions:

    • The new section about handling http2 and emscripten should ideally outline specific version details and possible security risks to ensure comprehensive understanding.
  2. gzip Compressed Redirection:

    • The handling of gzip compression in redirection logic must be thoroughly tested to ensure no security vulnerabilities like decompression bombs or malformed input breaches the service.

Privacy Hotspots

There are no noteworthy privacy risks introduced by this PR.

Changes

Changes

Documentation Changes

  1. .github/SECURITY.md

    +## Out of Scope Experimental Features
    +urllib3 contains two experimental modules: `emscripten` and `http2`.
    +...
  2. README.md

    -urllib3 is a powerful, *user-friendly* HTTP client for Python. Much of the
    -Python ecosystem already uses urllib3 and you should too.
    +urllib3 is a powerful, *user-friendly* HTTP client for Python.
    ...
  3. CHANGES.rst

    +2.6.3 (2026-01-07)
    +==================
    +...

Workflow Changes

  1. .github/workflows/ci.yml

    -        uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0
    +        uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0
    ...
  2. .github/workflows/publish.yml

    -        uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0
    +        uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
    ...

Code Changes

  1. dummyserver/app.py

    +    compressed = values.get("compressed") == "true"
    ...
    +    if compressed:
    +        headers.append(("Content-Encoding", "gzip"))
    +        data = gzip.compress(b"foo")
  2. src/urllib3/contrib/emscripten/connection.py

    +    response_class: type[BaseHTTPResponse] = EmscriptenHttpResponseWrapper
  3. src/urllib3/response.py

    -            self.read()
    +            self.read(
    +                decode_content=self._has_decoded_content,
    +            )
  4. src/urllib3/util/retry.py

    +    :param int retry_after_max: Number of seconds to allow as the maximum for
    +        Retry-After headers. Defaults to :attr:`Retry.DEFAULT_RETRY_AFTER_MAX`.
    ...
    +            retry_after_max=self.retry_after_max,
  5. uv.lock

    -version = "3.1.3"
    +version = "3.1.4"
    ...
sequenceDiagram
    participant User
    participant GitHubActions
    participant TestServer
    User->>GitHubActions: Create PR with changes
    GitHubActions-->>TestServer: Run updated CI
    Note right of GitHubActions: Upgrades to actions versions
    GitHubActions-->>User: Provide feedback on test results
    User->>TestServer: Implement and verify code changes
    TestServer-->>User: Confirm implementation
Loading

@onyb onyb merged commit fa73824 into master Jan 8, 2026
5 checks passed
@onyb onyb deleted the dependabot/pip/urllib3-2.6.3 branch January 8, 2026 08:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

dependencies Pull requests that update a dependency file P2 puLL-Merge python Pull requests that update python code security

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants