Skip to content

Commit 95f4567

Browse files
authored
Wrap urllib3's SSLError as requests' SSLError (#6057)
1 parent 7ae3887 commit 95f4567

File tree

3 files changed

+55
-5
lines changed

3 files changed

+55
-5
lines changed

HISTORY.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,11 @@ dev
66

77
- \[Short description of non-trivial change.\]
88

9+
**Bugfixes**
10+
11+
- Fixed urllib3 exception leak, wrapping `urllib3.exceptions.SSLError` with
12+
`requests.exceptions.SSLError` for `content` and `iter_content`.
13+
914
2.27.1 (2022-01-05)
1015
-------------------
1116

requests/models.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,12 @@
1919
from urllib3.filepost import encode_multipart_formdata
2020
from urllib3.util import parse_url
2121
from urllib3.exceptions import (
22-
DecodeError, ReadTimeoutError, ProtocolError, LocationParseError)
22+
DecodeError,
23+
LocationParseError,
24+
ProtocolError,
25+
ReadTimeoutError,
26+
SSLError,
27+
)
2328

2429
from io import UnsupportedOperation
2530
from .hooks import default_hooks
@@ -32,6 +37,7 @@
3237
ContentDecodingError, ConnectionError, StreamConsumedError,
3338
InvalidJSONError)
3439
from .exceptions import JSONDecodeError as RequestsJSONDecodeError
40+
from .exceptions import SSLError as RequestsSSLError
3541
from ._internal_utils import to_native_string, unicode_is_ascii
3642
from .utils import (
3743
guess_filename, get_auth_from_url, requote_uri,
@@ -765,6 +771,8 @@ def generate():
765771
raise ContentDecodingError(e)
766772
except ReadTimeoutError as e:
767773
raise ConnectionError(e)
774+
except SSLError as e:
775+
raise RequestsSSLError(e)
768776
else:
769777
# Standard file-like object.
770778
while True:

tests/test_requests.py

Lines changed: 41 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
import io
1515
import requests
1616
import pytest
17+
import urllib3
1718
from requests.adapters import HTTPAdapter
1819
from requests.auth import HTTPDigestAuth, _basic_auth_str
1920
from requests.compat import (
@@ -22,9 +23,25 @@
2223
from requests.cookies import (
2324
cookiejar_from_dict, morsel_to_cookie)
2425
from requests.exceptions import (
25-
ConnectionError, ConnectTimeout, InvalidSchema, InvalidURL,
26-
MissingSchema, ReadTimeout, Timeout, RetryError, RequestException, TooManyRedirects,
27-
ProxyError, InvalidHeader, UnrewindableBodyError, SSLError, InvalidProxyURL, InvalidJSONError)
26+
ChunkedEncodingError,
27+
ConnectionError,
28+
ConnectTimeout,
29+
ContentDecodingError,
30+
InvalidHeader,
31+
InvalidJSONError,
32+
InvalidProxyURL,
33+
InvalidSchema,
34+
InvalidURL,
35+
MissingSchema,
36+
ProxyError,
37+
ReadTimeout,
38+
RequestException,
39+
RetryError,
40+
Timeout,
41+
TooManyRedirects,
42+
UnrewindableBodyError,
43+
)
44+
from requests.exceptions import SSLError as RequestsSSLError
2845
from requests.models import PreparedRequest
2946
from requests.structures import CaseInsensitiveDict
3047
from requests.sessions import SessionRedirectMixin
@@ -910,7 +927,7 @@ def test_certificate_failure(self, httpbin_secure):
910927
"""
911928
When underlying SSL problems occur, an SSLError is raised.
912929
"""
913-
with pytest.raises(SSLError):
930+
with pytest.raises(RequestsSSLError):
914931
# Our local httpbin does not have a trusted CA, so this call will
915932
# fail if we use our default trust bundle.
916933
requests.get(httpbin_secure('status', '200'))
@@ -1320,6 +1337,26 @@ def test_response_chunk_size_type(self):
13201337
with pytest.raises(TypeError):
13211338
chunks = r.iter_content("1024")
13221339

1340+
@pytest.mark.parametrize(
1341+
'exception, args, expected', (
1342+
(urllib3.exceptions.ProtocolError, tuple(), ChunkedEncodingError),
1343+
(urllib3.exceptions.DecodeError, tuple(), ContentDecodingError),
1344+
(urllib3.exceptions.ReadTimeoutError, (None, '', ''), ConnectionError),
1345+
(urllib3.exceptions.SSLError, tuple(), RequestsSSLError),
1346+
)
1347+
)
1348+
def test_iter_content_wraps_exceptions(
1349+
self, httpbin, mocker, exception, args, expected
1350+
):
1351+
r = requests.Response()
1352+
r.raw = mocker.Mock()
1353+
# ReadTimeoutError can't be initialized by mock
1354+
# so we'll manually create the instance with args
1355+
r.raw.stream.side_effect = exception(*args)
1356+
1357+
with pytest.raises(expected):
1358+
next(r.iter_content(1024))
1359+
13231360
def test_request_and_response_are_pickleable(self, httpbin):
13241361
r = requests.get(httpbin('get'))
13251362

0 commit comments

Comments
 (0)