Skip to content

fix(google-auth): keep PoolManager settings in AuthorizedHttp.configure_mtls_channel - #18427

Open
Shubham-Padkonde wants to merge 1 commit into
googleapis:mainfrom
Shubham-Padkonde:fix-urllib3-mtls-pool-settings
Open

Shubham-Padkonde wants to merge 1 commit into
googleapis:mainfrom
Shubham-Padkonde:fix-urllib3-mtls-pool-settings

Conversation

@Shubham-Padkonde

Copy link
Copy Markdown

Fixes #18366 🦕

AuthorizedHttp.configure_mtls_channel() replaced self.http with a PoolManager built from scratch, so a caller's pool configuration was dropped. It now carries the non-TLS settings of the current PoolManager over to the new one:

  • retries, timeout, maxsize and block (from connection_pool_kw);
  • headers;
  • num_pools.

This matches how AuthorizedSession.configure_mtls_channel() keeps the adapter's retry and pool settings.

How it works:

  • _pool_manager_settings(http) returns only the settings that were set on a real urllib3.PoolManager, and {} for anything else (mocks, custom HTTP objects).
  • _make_mutual_tls_http(cert, key, **pool_kwargs) and _make_default_http(**pool_kwargs) pass them to PoolManager. With no extra settings, both behave exactly as before.
  • The settings are carried over on both the mTLS path and the fallback path (GOOGLE_API_USE_CLIENT_CERTIFICATE=true but no cert found), because both replace the caller's PoolManager.
  • Cert rotation on a 401 goes through configure_mtls_channel(), so the settings also survive rotation.
  • TLS settings (ssl_context, cert_reqs, ca_certs, …) and proxy settings are not carried over.

A related behaviour I noticed but didn't change here: AuthorizedHttp.__init__ calls RequestMethods.__init__(), which resets self.headers. Because headers is proxied to self.http, headers passed to a user-provided PoolManager's constructor are already cleared when AuthorizedHttp is created. The tests therefore set headers on AuthorizedHttp after construction, which is the case this PR fixes.

Testing

I ran these locally in packages/google-auth with Python 3.12:

  • There are 3 new tests in tests/transport/test_urllib3.py:
    • _make_mutual_tls_http with pool kwargs;
    • configure_mtls_channel preserving the settings on the mTLS path;
    • the same on the non-mTLS path.
  • All 3 new tests fail on main and pass with this change.
  • pytest tests/transport: 452 passed with urllib3 2.8.0. tests/transport/test_urllib3.py also passes (41 tests) with urllib3 1.26.20.
  • ruff check --select I and ruff format --check (ruff 0.14.14, same flags as nox -s lint) and flake8 are clean on the changed files.

This change was written with help from an AI coding assistant (Claude Code). I reviewed and tested it as described above.

🤖 Generated with Claude Code

…re_mtls_channel

configure_mtls_channel replaced the underlying urllib3.PoolManager with a
new one built from scratch, discarding the caller's retries, timeout,
maxsize, block, headers and num_pools. Carry those non-TLS settings over
to the new PoolManager on both the mTLS and the non-mTLS path, matching
how AuthorizedSession.configure_mtls_channel keeps the adapter's retry
and pool settings.

Fixes googleapis#18366
@Shubham-Padkonde
Shubham-Padkonde requested review from a team as code owners September 19, 2026 11:00

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Code Review

This pull request updates the urllib3 transport in google-auth to ensure that connection pool settings (such as retries, timeout, maxsize, block, headers, and num_pools) are preserved when configure_mtls_channel replaces the urllib3.PoolManager. However, the implementation incorrectly accesses the private _maxsize attribute of RecentlyUsedContainer to retrieve the pool limit, which will evaluate to None at runtime and cause the num_pools setting to be silently ignored. Both the source code and the corresponding test assertions should be updated to use the public maxsize attribute instead.

Comment on lines +188 to +190
num_pools = getattr(getattr(http, "pools", None), "_maxsize", None)
if isinstance(num_pools, int) and num_pools != _DEFAULT_NUM_POOLS:
settings["num_pools"] = num_pools

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

high

In urllib3, the RecentlyUsedContainer class (which is the type of http.pools) stores its maximum size in the public attribute maxsize, not _maxsize. Using _maxsize will return None at runtime, causing the num_pools setting to be silently ignored and not carried over to the new PoolManager instance.

Suggested change
num_pools = getattr(getattr(http, "pools", None), "_maxsize", None)
if isinstance(num_pools, int) and num_pools != _DEFAULT_NUM_POOLS:
settings["num_pools"] = num_pools
num_pools = getattr(getattr(http, "pools", None), "maxsize", None)
if isinstance(num_pools, int) and num_pools != _DEFAULT_NUM_POOLS:
settings["num_pools"] = num_pools

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

I checked this. RecentlyUsedContainer has no public maxsize attribute in either supported urllib3 major version; _maxsize is where it stores the size:

1.26.20 RecentlyUsedContainer has _maxsize: True 3 | has maxsize: False
2.8.0   RecentlyUsedContainer has _maxsize: True 3 | has maxsize: False

(from urllib3.PoolManager(num_pools=3).pools). Switching to maxsize would make num_pools silently stop carrying over, so I kept _maxsize, read through getattr with an int check. The tests pass on urllib3 1.26.20 and 2.8.0.

for name in ("retries", "timeout", "maxsize", "block"):
assert new_http.connection_pool_kw[name] is old_http.connection_pool_kw[name]
assert new_http.headers == {"x-custom": "value"}
assert new_http.pools._maxsize == 3

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

Since RecentlyUsedContainer uses maxsize instead of _maxsize to store the maximum number of pools, this assertion should check maxsize to correctly verify the behavior.

Suggested change
assert new_http.pools._maxsize == 3
assert new_http.pools.maxsize == 3

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

I checked this. RecentlyUsedContainer has no public maxsize attribute in either supported urllib3 major version; _maxsize is where it stores the size:

1.26.20 RecentlyUsedContainer has _maxsize: True 3 | has maxsize: False
2.8.0   RecentlyUsedContainer has _maxsize: True 3 | has maxsize: False

(from urllib3.PoolManager(num_pools=3).pools). Switching to maxsize would make num_pools silently stop carrying over, so I kept _maxsize, read through getattr with an int check. The tests pass on urllib3 1.26.20 and 2.8.0.

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.

auth: AuthorizedHttp.configure_mtls_channel() drops custom PoolManager settings

1 participant