Skip to content

Commit 034e5e3

Browse files
patchback[bot]webknjazDreamsorcererbdraco
authored
[PR #8042/4b91b530 backport][3.9] Tightening the runtime type check for ssl (#7698) (#8043)
**This is a backport of PR #8042 as merged into 3.10 (4b91b53).** Currently, the valid types of ssl parameter are SSLContext, Literal[False], Fingerprint or None. If user sets ssl = False, we disable ssl certificate validation which makes total sense. But if user set ssl = True by mistake, instead of enabling ssl certificate validation or raising errors, we silently disable the validation too which is a little subtle but weird. In this PR, we added a check that if user sets ssl=True, we enable certificate validation by treating it as using Default SSL Context. --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Sviatoslav Sydorenko <[email protected]> Co-authored-by: Sam Bull <[email protected]> Co-authored-by: J. Nick Koston <[email protected]> Co-authored-by: Sam Bull <[email protected]> (cherry picked from commit 9e14ea1)
1 parent f000174 commit 034e5e3

10 files changed

+44
-42
lines changed

CHANGES/7698.feature

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Added support for passing `True` to `ssl` while deprecating `None`. -- by :user:`xiangyan99`

aiohttp/client.py

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@
2222
Generic,
2323
Iterable,
2424
List,
25-
Literal,
2625
Mapping,
2726
Optional,
2827
Set,
@@ -408,7 +407,7 @@ async def _request(
408407
verify_ssl: Optional[bool] = None,
409408
fingerprint: Optional[bytes] = None,
410409
ssl_context: Optional[SSLContext] = None,
411-
ssl: Optional[Union[SSLContext, Literal[False], Fingerprint]] = None,
410+
ssl: Union[SSLContext, bool, Fingerprint] = True,
412411
server_hostname: Optional[str] = None,
413412
proxy_headers: Optional[LooseHeaders] = None,
414413
trace_request_ctx: Optional[SimpleNamespace] = None,
@@ -562,7 +561,7 @@ async def _request(
562561
proxy_auth=proxy_auth,
563562
timer=timer,
564563
session=self,
565-
ssl=ssl,
564+
ssl=ssl if ssl is not None else True,
566565
server_hostname=server_hostname,
567566
proxy_headers=proxy_headers,
568567
traces=traces,
@@ -738,7 +737,7 @@ def ws_connect(
738737
headers: Optional[LooseHeaders] = None,
739738
proxy: Optional[StrOrURL] = None,
740739
proxy_auth: Optional[BasicAuth] = None,
741-
ssl: Union[SSLContext, Literal[False], None, Fingerprint] = None,
740+
ssl: Union[SSLContext, bool, None, Fingerprint] = True,
742741
verify_ssl: Optional[bool] = None,
743742
fingerprint: Optional[bytes] = None,
744743
ssl_context: Optional[SSLContext] = None,
@@ -790,7 +789,7 @@ async def _ws_connect(
790789
headers: Optional[LooseHeaders] = None,
791790
proxy: Optional[StrOrURL] = None,
792791
proxy_auth: Optional[BasicAuth] = None,
793-
ssl: Union[SSLContext, Literal[False], None, Fingerprint] = None,
792+
ssl: Optional[Union[SSLContext, bool, Fingerprint]] = True,
794793
verify_ssl: Optional[bool] = None,
795794
fingerprint: Optional[bytes] = None,
796795
ssl_context: Optional[SSLContext] = None,
@@ -824,6 +823,9 @@ async def _ws_connect(
824823
extstr = ws_ext_gen(compress=compress)
825824
real_headers[hdrs.SEC_WEBSOCKET_EXTENSIONS] = extstr
826825

826+
# For the sake of backward compatibility, if user passes in None, convert it to True
827+
if ssl is None:
828+
ssl = True
827829
ssl = _merge_ssl_params(ssl, verify_ssl, ssl_context, fingerprint)
828830

829831
# send request

aiohttp/client_exceptions.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -180,12 +180,12 @@ def port(self) -> Optional[int]:
180180
return self._conn_key.port
181181

182182
@property
183-
def ssl(self) -> Union[SSLContext, None, bool, "Fingerprint"]:
183+
def ssl(self) -> Union[SSLContext, bool, "Fingerprint"]:
184184
return self._conn_key.ssl
185185

186186
def __str__(self) -> str:
187187
return "Cannot connect to host {0.host}:{0.port} ssl:{1} [{2}]".format(
188-
self, self.ssl if self.ssl is not None else "default", self.strerror
188+
self, "default" if self.ssl is True else self.ssl, self.strerror
189189
)
190190

191191
# OSError.__reduce__ does too much black magick
@@ -219,7 +219,7 @@ def path(self) -> str:
219219

220220
def __str__(self) -> str:
221221
return "Cannot connect to unix socket {0.path} ssl:{1} [{2}]".format(
222-
self, self.ssl if self.ssl is not None else "default", self.strerror
222+
self, "default" if self.ssl is True else self.ssl, self.strerror
223223
)
224224

225225

aiohttp/client_reqrep.py

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@
1717
Dict,
1818
Iterable,
1919
List,
20-
Literal,
2120
Mapping,
2221
Optional,
2322
Tuple,
@@ -151,22 +150,22 @@ def check(self, transport: asyncio.Transport) -> None:
151150
if ssl is not None:
152151
SSL_ALLOWED_TYPES = (ssl.SSLContext, bool, Fingerprint, type(None))
153152
else: # pragma: no cover
154-
SSL_ALLOWED_TYPES = type(None)
153+
SSL_ALLOWED_TYPES = (bool, type(None))
155154

156155

157156
def _merge_ssl_params(
158-
ssl: Union["SSLContext", Literal[False], Fingerprint, None],
157+
ssl: Union["SSLContext", bool, Fingerprint],
159158
verify_ssl: Optional[bool],
160159
ssl_context: Optional["SSLContext"],
161160
fingerprint: Optional[bytes],
162-
) -> Union["SSLContext", Literal[False], Fingerprint, None]:
161+
) -> Union["SSLContext", bool, Fingerprint]:
163162
if verify_ssl is not None and not verify_ssl:
164163
warnings.warn(
165164
"verify_ssl is deprecated, use ssl=False instead",
166165
DeprecationWarning,
167166
stacklevel=3,
168167
)
169-
if ssl is not None:
168+
if ssl is not True:
170169
raise ValueError(
171170
"verify_ssl, ssl_context, fingerprint and ssl "
172171
"parameters are mutually exclusive"
@@ -179,7 +178,7 @@ def _merge_ssl_params(
179178
DeprecationWarning,
180179
stacklevel=3,
181180
)
182-
if ssl is not None:
181+
if ssl is not True:
183182
raise ValueError(
184183
"verify_ssl, ssl_context, fingerprint and ssl "
185184
"parameters are mutually exclusive"
@@ -192,7 +191,7 @@ def _merge_ssl_params(
192191
DeprecationWarning,
193192
stacklevel=3,
194193
)
195-
if ssl is not None:
194+
if ssl is not True:
196195
raise ValueError(
197196
"verify_ssl, ssl_context, fingerprint and ssl "
198197
"parameters are mutually exclusive"
@@ -214,7 +213,7 @@ class ConnectionKey:
214213
host: str
215214
port: Optional[int]
216215
is_ssl: bool
217-
ssl: Union[SSLContext, None, Literal[False], Fingerprint]
216+
ssl: Union[SSLContext, bool, Fingerprint]
218217
proxy: Optional[URL]
219218
proxy_auth: Optional[BasicAuth]
220219
proxy_headers_hash: Optional[int] # hash(CIMultiDict)
@@ -276,7 +275,7 @@ def __init__(
276275
proxy_auth: Optional[BasicAuth] = None,
277276
timer: Optional[BaseTimerContext] = None,
278277
session: Optional["ClientSession"] = None,
279-
ssl: Union[SSLContext, Literal[False], Fingerprint, None] = None,
278+
ssl: Union[SSLContext, bool, Fingerprint] = True,
280279
proxy_headers: Optional[LooseHeaders] = None,
281280
traces: Optional[List["Trace"]] = None,
282281
trust_env: bool = False,
@@ -315,7 +314,7 @@ def __init__(
315314
real_response_class = response_class
316315
self.response_class: Type[ClientResponse] = real_response_class
317316
self._timer = timer if timer is not None else TimerNoop()
318-
self._ssl = ssl
317+
self._ssl = ssl if ssl is not None else True
319318
self.server_hostname = server_hostname
320319

321320
if loop.get_debug():
@@ -357,7 +356,7 @@ def is_ssl(self) -> bool:
357356
return self.url.scheme in ("https", "wss")
358357

359358
@property
360-
def ssl(self) -> Union["SSLContext", None, Literal[False], Fingerprint]:
359+
def ssl(self) -> Union["SSLContext", bool, Fingerprint]:
361360
return self._ssl
362361

363362
@property

aiohttp/connector.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -762,7 +762,7 @@ def __init__(
762762
ttl_dns_cache: Optional[int] = 10,
763763
family: int = 0,
764764
ssl_context: Optional[SSLContext] = None,
765-
ssl: Union[None, Literal[False], Fingerprint, SSLContext] = None,
765+
ssl: Union[bool, Fingerprint, SSLContext] = True,
766766
local_addr: Optional[Tuple[str, int]] = None,
767767
resolver: Optional[AbstractResolver] = None,
768768
keepalive_timeout: Union[None, float, object] = sentinel,
@@ -955,13 +955,13 @@ def _get_ssl_context(self, req: ClientRequest) -> Optional[SSLContext]:
955955
sslcontext = req.ssl
956956
if isinstance(sslcontext, ssl.SSLContext):
957957
return sslcontext
958-
if sslcontext is not None:
958+
if sslcontext is not True:
959959
# not verified or fingerprinted
960960
return self._make_ssl_context(False)
961961
sslcontext = self._ssl
962962
if isinstance(sslcontext, ssl.SSLContext):
963963
return sslcontext
964-
if sslcontext is not None:
964+
if sslcontext is not True:
965965
# not verified or fingerprinted
966966
return self._make_ssl_context(False)
967967
return self._make_ssl_context(True)

tests/test_client_exceptions.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ class TestClientConnectorError:
119119
host="example.com",
120120
port=8080,
121121
is_ssl=False,
122-
ssl=None,
122+
ssl=True,
123123
proxy=None,
124124
proxy_auth=None,
125125
proxy_headers_hash=None,
@@ -136,7 +136,7 @@ def test_ctor(self) -> None:
136136
assert err.os_error.strerror == "No such file"
137137
assert err.host == "example.com"
138138
assert err.port == 8080
139-
assert err.ssl is None
139+
assert err.ssl is True
140140

141141
def test_pickle(self) -> None:
142142
err = client.ClientConnectorError(
@@ -153,7 +153,7 @@ def test_pickle(self) -> None:
153153
assert err2.os_error.strerror == "No such file"
154154
assert err2.host == "example.com"
155155
assert err2.port == 8080
156-
assert err2.ssl is None
156+
assert err2.ssl is True
157157
assert err2.foo == "bar"
158158

159159
def test_repr(self) -> None:
@@ -171,7 +171,7 @@ def test_str(self) -> None:
171171
os_error=OSError(errno.ENOENT, "No such file"),
172172
)
173173
assert str(err) == (
174-
"Cannot connect to host example.com:8080 ssl:" "default [No such file]"
174+
"Cannot connect to host example.com:8080 ssl:default [No such file]"
175175
)
176176

177177

@@ -180,7 +180,7 @@ class TestClientConnectorCertificateError:
180180
host="example.com",
181181
port=8080,
182182
is_ssl=False,
183-
ssl=None,
183+
ssl=True,
184184
proxy=None,
185185
proxy_auth=None,
186186
proxy_headers_hash=None,

tests/test_client_fingerprint.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ def test_fingerprint_check_no_ssl() -> None:
3737

3838
def test__merge_ssl_params_verify_ssl() -> None:
3939
with pytest.warns(DeprecationWarning):
40-
assert _merge_ssl_params(None, False, None, None) is False
40+
assert _merge_ssl_params(True, False, None, None) is False
4141

4242

4343
def test__merge_ssl_params_verify_ssl_conflict() -> None:
@@ -50,7 +50,7 @@ def test__merge_ssl_params_verify_ssl_conflict() -> None:
5050
def test__merge_ssl_params_ssl_context() -> None:
5151
ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
5252
with pytest.warns(DeprecationWarning):
53-
assert _merge_ssl_params(None, None, ctx, None) is ctx
53+
assert _merge_ssl_params(True, None, ctx, None) is ctx
5454

5555

5656
def test__merge_ssl_params_ssl_context_conflict() -> None:
@@ -64,7 +64,7 @@ def test__merge_ssl_params_ssl_context_conflict() -> None:
6464
def test__merge_ssl_params_fingerprint() -> None:
6565
digest = hashlib.sha256(b"123").digest()
6666
with pytest.warns(DeprecationWarning):
67-
ret = _merge_ssl_params(None, None, None, digest)
67+
ret = _merge_ssl_params(True, None, None, digest)
6868
assert ret.fingerprint == digest
6969

7070

tests/test_client_request.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,7 @@ def test_host_port_default_http(make_request) -> None:
166166
req = make_request("get", "http://python.org/")
167167
assert req.host == "python.org"
168168
assert req.port == 80
169-
assert not req.ssl
169+
assert not req.is_ssl()
170170

171171

172172
def test_host_port_default_https(make_request) -> None:
@@ -400,7 +400,7 @@ def test_ipv6_default_http_port(make_request) -> None:
400400
req = make_request("get", "http://[2001:db8::1]/")
401401
assert req.host == "2001:db8::1"
402402
assert req.port == 80
403-
assert not req.ssl
403+
assert not req.is_ssl()
404404

405405

406406
def test_ipv6_default_https_port(make_request) -> None:

tests/test_connector.py

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -28,19 +28,19 @@
2828
@pytest.fixture()
2929
def key():
3030
# Connection key
31-
return ConnectionKey("localhost", 80, False, None, None, None, None)
31+
return ConnectionKey("localhost", 80, False, True, None, None, None)
3232

3333

3434
@pytest.fixture
3535
def key2():
3636
# Connection key
37-
return ConnectionKey("localhost", 80, False, None, None, None, None)
37+
return ConnectionKey("localhost", 80, False, True, None, None, None)
3838

3939

4040
@pytest.fixture
4141
def ssl_key():
4242
# Connection key
43-
return ConnectionKey("localhost", 80, True, None, None, None, None)
43+
return ConnectionKey("localhost", 80, True, True, None, None, None)
4444

4545

4646
@pytest.fixture
@@ -1219,9 +1219,9 @@ async def test_cleanup_closed_disabled(loop, mocker) -> None:
12191219
assert not conn._cleanup_closed_transports
12201220

12211221

1222-
async def test_tcp_connector_ctor(loop) -> None:
1223-
conn = aiohttp.TCPConnector(loop=loop)
1224-
assert conn._ssl is None
1222+
async def test_tcp_connector_ctor() -> None:
1223+
conn = aiohttp.TCPConnector()
1224+
assert conn._ssl is True
12251225

12261226
assert conn.use_dns_cache
12271227
assert conn.family == 0
@@ -1307,7 +1307,7 @@ async def test___get_ssl_context3(loop) -> None:
13071307
conn = aiohttp.TCPConnector(loop=loop, ssl=ctx)
13081308
req = mock.Mock()
13091309
req.is_ssl.return_value = True
1310-
req.ssl = None
1310+
req.ssl = True
13111311
assert conn._get_ssl_context(req) is ctx
13121312

13131313

@@ -1333,7 +1333,7 @@ async def test___get_ssl_context6(loop) -> None:
13331333
conn = aiohttp.TCPConnector(loop=loop)
13341334
req = mock.Mock()
13351335
req.is_ssl.return_value = True
1336-
req.ssl = None
1336+
req.ssl = True
13371337
assert conn._get_ssl_context(req) is conn._make_ssl_context(True)
13381338

13391339

tests/test_proxy.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ async def make_conn():
7575
auth=None,
7676
headers={"Host": "www.python.org"},
7777
loop=self.loop,
78-
ssl=None,
78+
ssl=True,
7979
)
8080

8181
conn.close()
@@ -117,7 +117,7 @@ async def make_conn():
117117
auth=None,
118118
headers={"Host": "www.python.org", "Foo": "Bar"},
119119
loop=self.loop,
120-
ssl=None,
120+
ssl=True,
121121
)
122122

123123
conn.close()

0 commit comments

Comments
 (0)