1616
1717"""Helpers for client setup and configuration."""
1818
19+ import os
1920from typing import Callable , Optional , Tuple
21+ from urllib .parse import urlparse , urlunparse
2022
23+ import google .auth .transport .mtls # type: ignore
2124from google .auth .exceptions import MutualTLSChannelError # type: ignore
2225
2326
@@ -38,28 +41,62 @@ def get_default_mtls_endpoint(api_endpoint: Optional[str]) -> Optional[str]:
3841 if not api_endpoint or ".mtls." in api_endpoint .lower ():
3942 return api_endpoint
4043
41- # Handle optional port suffix (e.g. ":443")
42- parts = api_endpoint .split (":" )
43- host = parts [0 ]
44- port = ":" + parts [1 ] if len (parts ) > 1 else ""
44+ has_scheme = "://" in api_endpoint
45+ if not has_scheme :
46+ parsed = urlparse ("//" + api_endpoint )
47+ else :
48+ parsed = urlparse (api_endpoint )
49+
50+ host = parsed .hostname
51+ if not host :
52+ return api_endpoint
53+
54+ port = f":{ parsed .port } " if parsed .port else ""
4555
4656 lowered_host = host .lower ()
4757 if lowered_host .endswith (".sandbox.googleapis.com" ):
48- # len(".sandbox.googleapis.com") == 23
49- return host [:- 23 ] + ".mtls.sandbox.googleapis.com" + port
58+ new_host = host [:- 23 ] + ".mtls.sandbox.googleapis.com"
59+ elif lowered_host .endswith (".googleapis.com" ):
60+ new_host = host [:- 15 ] + ".mtls.googleapis.com"
61+ else :
62+ return api_endpoint
5063
51- if lowered_host .endswith (".googleapis.com" ):
52- # len(".googleapis.com") == 15
53- return host [:- 15 ] + ".mtls.googleapis.com" + port
64+ netloc = new_host + port
65+ new_parsed = parsed ._replace (netloc = netloc )
5466
55- return api_endpoint
67+ if not has_scheme :
68+ return urlunparse (new_parsed )[2 :]
69+ else :
70+ return urlunparse (new_parsed )
71+
72+
73+ def should_use_mtls_endpoint (client_cert_available : bool ) -> bool :
74+ """Helper to determine whether to use mTLS endpoint.
75+
76+ Uses google.auth.transport.mtls.should_use_mtls_endpoint if available,
77+ otherwise falls back to evaluation of GOOGLE_API_USE_MTLS_ENDPOINT.
78+ """
79+ if hasattr (google .auth .transport .mtls , "should_use_mtls_endpoint" ):
80+ return google .auth .transport .mtls .should_use_mtls_endpoint (client_cert_available )
81+
82+ # Fallback logic for older google-auth versions
83+ use_mtls_endpoint = os .getenv ("GOOGLE_API_USE_MTLS_ENDPOINT" , "auto" ).strip ().lower ()
84+ if use_mtls_endpoint == "always" :
85+ return True
86+ elif use_mtls_endpoint == "never" :
87+ return False
88+ elif use_mtls_endpoint == "auto" :
89+ return client_cert_available
90+ else :
91+ raise MutualTLSChannelError (
92+ f"Environment variable `GOOGLE_API_USE_MTLS_ENDPOINT` must be `never`, `auto` or `always`"
93+ )
5694
5795
5896def get_api_endpoint (
5997 api_override : Optional [str ],
6098 client_cert_source : Optional [Callable [[], Tuple [bytes , bytes ]]],
6199 universe_domain : str ,
62- use_mtls_endpoint : str ,
63100 default_universe : str ,
64101 default_mtls_endpoint : Optional [str ],
65102 default_endpoint_template : str ,
@@ -72,8 +109,6 @@ def get_api_endpoint(
72109 client_cert_source (Optional[Callable[[], Tuple[bytes, bytes]]]): The client
73110 certificate source used by the client.
74111 universe_domain (str): The universe domain used by the client.
75- use_mtls_endpoint (str): How to use the mTLS endpoint. Possible values
76- are "always", "auto", or "never".
77112 default_universe (str): The default universe domain.
78113 default_mtls_endpoint (Optional[str]): The default mTLS endpoint.
79114 default_endpoint_template (str): The default endpoint template containing
@@ -89,9 +124,9 @@ def get_api_endpoint(
89124 """
90125 if api_override is not None :
91126 return api_override
92- elif use_mtls_endpoint == "always" or (
93- use_mtls_endpoint == "auto" and client_cert_source
94- ):
127+
128+ client_cert_available = client_cert_source is not None
129+ if should_use_mtls_endpoint ( client_cert_available ):
95130 if universe_domain .lower () != default_universe .lower ():
96131 raise MutualTLSChannelError (
97132 f"mTLS is not supported in any universe other than { default_universe } ."
0 commit comments