Skip to content

Commit a816ee5

Browse files
author
Pierre-Louis Peeters
committed
Add specific exceptions for authentication issues
1 parent 36ddc68 commit a816ee5

6 files changed

+62
-2
lines changed

aiohttp/__init__.py

+6
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,10 @@
2727
ConnectionTimeoutError,
2828
ContentTypeError,
2929
Fingerprint,
30+
InvalidAuthClientError,
31+
InvalidRedirectUrlAuthClientError,
3032
InvalidURL,
33+
InvalidUrlAuthClientError,
3134
InvalidUrlClientError,
3235
InvalidUrlRedirectClientError,
3336
NamedPipeConnector,
@@ -137,7 +140,10 @@
137140
"ConnectionTimeoutError",
138141
"ContentTypeError",
139142
"Fingerprint",
143+
"InvalidAuthClientError",
144+
"InvalidRedirectUrlAuthClientError",
140145
"InvalidURL",
146+
"InvalidUrlAuthClientError",
141147
"InvalidUrlClientError",
142148
"InvalidUrlRedirectClientError",
143149
"NonHttpUrlClientError",

aiohttp/client.py

+19-1
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,10 @@
5757
ClientSSLError,
5858
ConnectionTimeoutError,
5959
ContentTypeError,
60+
InvalidAuthClientError,
61+
InvalidRedirectUrlAuthClientError,
6062
InvalidURL,
63+
InvalidUrlAuthClientError,
6164
InvalidUrlClientError,
6265
InvalidUrlRedirectClientError,
6366
NonHttpUrlClientError,
@@ -124,7 +127,10 @@
124127
"ClientSSLError",
125128
"ConnectionTimeoutError",
126129
"ContentTypeError",
130+
"InvalidAuthClientError",
131+
"InvalidRedirectUrlAuthClientError",
127132
"InvalidURL",
133+
"InvalidUrlAuthClientError",
128134
"InvalidUrlClientError",
129135
"RedirectClientError",
130136
"NonHttpUrlClientError",
@@ -576,7 +582,19 @@ async def _request(
576582

577583
# Override the auth with the one from the URL only if we
578584
# have no auth, or if we got an auth from a redirect URL
579-
if auth is None or (history and auth_from_url is not None):
585+
if (auth is None or history) and auth_from_url is not None:
586+
# Pre-check the credentials can be encoded to
587+
# avoid crashing down the line
588+
try:
589+
auth_from_url.encode()
590+
except UnicodeEncodeError as e:
591+
auth_err_exc_cls = (
592+
InvalidRedirectUrlAuthClientError
593+
if redirects
594+
else InvalidUrlAuthClientError
595+
)
596+
raise auth_err_exc_cls(url, str(e))
597+
580598
auth = auth_from_url
581599

582600
if (

aiohttp/client_exceptions.py

+15
Original file line numberDiff line numberDiff line change
@@ -48,9 +48,12 @@
4848
"ContentTypeError",
4949
"ClientPayloadError",
5050
"InvalidURL",
51+
"InvalidAuthClientError",
52+
"InvalidUrlAuthClientError",
5153
"InvalidUrlClientError",
5254
"RedirectClientError",
5355
"NonHttpUrlClientError",
56+
"InvalidRedirectUrlAuthClientError",
5457
"InvalidUrlRedirectClientError",
5558
"NonHttpUrlRedirectClientError",
5659
"WSMessageTypeError",
@@ -306,6 +309,14 @@ class InvalidUrlClientError(InvalidURL):
306309
"""Invalid URL client error."""
307310

308311

312+
class InvalidAuthClientError(ClientError):
313+
"""Invalid auth client error."""
314+
315+
316+
class InvalidUrlAuthClientError(InvalidURL):
317+
"""Invalid URL auth client error."""
318+
319+
309320
class RedirectClientError(ClientError):
310321
"""Client redirect error."""
311322

@@ -318,6 +329,10 @@ class InvalidUrlRedirectClientError(InvalidUrlClientError, RedirectClientError):
318329
"""Invalid URL redirect client error."""
319330

320331

332+
class InvalidRedirectUrlAuthClientError(InvalidUrlRedirectClientError):
333+
"""Invalid redirect URL auth client error."""
334+
335+
321336
class NonHttpUrlRedirectClientError(NonHttpUrlClientError, RedirectClientError):
322337
"""Non http URL redirect client error."""
323338

aiohttp/client_reqrep.py

+13-1
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,9 @@
3535
ClientOSError,
3636
ClientResponseError,
3737
ContentTypeError,
38+
InvalidAuthClientError,
3839
InvalidURL,
40+
InvalidUrlAuthClientError,
3941
ServerFingerprintMismatch,
4042
)
4143
from .compression_utils import HAS_BROTLI
@@ -497,7 +499,11 @@ def update_transfer_encoding(self) -> None:
497499
def update_auth(self, auth: Optional[BasicAuth], trust_env: bool = False) -> None:
498500
"""Set basic auth."""
499501
if auth is None:
502+
auth_from_url = True
500503
auth = self.auth
504+
else:
505+
auth_from_url = False
506+
501507
if auth is None and trust_env and self.url.host is not None:
502508
netrc_obj = netrc_from_env()
503509
with contextlib.suppress(LookupError):
@@ -508,7 +514,13 @@ def update_auth(self, auth: Optional[BasicAuth], trust_env: bool = False) -> Non
508514
if not isinstance(auth, helpers.BasicAuth):
509515
raise TypeError("BasicAuth() tuple is required instead")
510516

511-
self.headers[hdrs.AUTHORIZATION] = auth.encode()
517+
try:
518+
self.headers[hdrs.AUTHORIZATION] = auth.encode()
519+
except UnicodeEncodeError as e:
520+
auth_err_exc_cls = (
521+
InvalidUrlAuthClientError if auth_from_url else InvalidAuthClientError
522+
)
523+
raise auth_err_exc_cls(self.url, str(e))
512524

513525
def update_body_from_data(self, body: Any) -> None:
514526
if body is None:

tests/test_client_functional.py

+4
Original file line numberDiff line numberDiff line change
@@ -2683,6 +2683,10 @@ async def handler_redirect(request: web.Request) -> web.Response:
26832683
("http:/", "http:///"),
26842684
("http:/example.com", "http:///example.com"),
26852685
("http:///example.com", "http:///example.com"),
2686+
(
2687+
"http://badchar username@example.com",
2688+
"http://example.com - 'latin-1' codec can't encode",
2689+
),
26862690
)
26872691

26882692
NON_HTTP_URL_WITH_ERROR_MESSAGE = (

tests/test_client_request.py

+5
Original file line numberDiff line numberDiff line change
@@ -378,6 +378,11 @@ def test_invalid_url(make_request: _RequestMaker) -> None:
378378
make_request("get", "hiwpefhipowhefopw")
379379

380380

381+
def test_invalid_auth(make_request: _RequestMaker) -> None:
382+
with pytest.raises(aiohttp.InvalidUrlAuthClientError):
383+
make_request("get", "http://badchar username@example.com")
384+
385+
381386
def test_no_path(make_request: _RequestMaker) -> None:
382387
req = make_request("get", "http://python.org")
383388
assert "/" == req.url.path

0 commit comments

Comments
 (0)