Skip to content

Commit 6f6107d

Browse files
committed
tidied up #1815
1 parent a2219eb commit 6f6107d

File tree

5 files changed

+153
-2
lines changed

5 files changed

+153
-2
lines changed

tests/server.crt

+33
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
-----BEGIN CERTIFICATE-----
2+
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
32+
IeY7UK1ylg==
33+
-----END CERTIFICATE-----

tests/server.key

+52
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
-----BEGIN PRIVATE KEY-----
2+
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
51+
n+nYHLy1vGIssY5ODapJLixk/nt/Jg==
52+
-----END PRIVATE KEY-----

tests/test_config.py

+30
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import logging
55
import os
66
import socket
7+
import ssl
78
import sys
89
import typing
910
from pathlib import Path
@@ -279,6 +280,35 @@ def test_ssl_config(
279280
assert config.is_ssl is True
280281

281282

283+
def ssl_context():
284+
context = ssl.SSLContext(int(ssl.PROTOCOL_TLS_SERVER))
285+
allowed_ciphers = "DEFAULT:!aNULL:!eNULL:!MD5:!3DES:!DES:!RC4:!IDEA:!SEED:!aDSS:!SRP:!PSK"
286+
context.set_ciphers(allowed_ciphers)
287+
list_options = [ssl.OP_NO_RENEGOTIATION]
288+
for each_option in list_options:
289+
context.options |= each_option
290+
return context
291+
292+
293+
def test_ssl_context() -> None:
294+
config = Config(app=asgi_app, ssl_context=ssl_context)
295+
config.load()
296+
if config.ssl_context is not None:
297+
assert ssl.PROTOCOL_TLS_SERVER is config.ssl_version
298+
assert "TLSv1" in config.ssl_ciphers
299+
if config.ssl is not None:
300+
assert ssl.OP_NO_RENEGOTIATION in config.ssl.options
301+
302+
303+
def test_ssl_context_load_cert() -> None:
304+
config = Config(
305+
app=asgi_app, ssl_context=ssl_context, ssl_certfile="tests/server.crt", ssl_keyfile="tests/server.key"
306+
)
307+
config.load()
308+
assert config.ssl_context is not None
309+
ctx = ssl_context()
310+
311+
282312
def test_ssl_config_combined(tls_certificate_key_and_chain_path: str) -> None:
283313
config = Config(
284314
app=asgi_app,

uvicorn/config.py

+36-2
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,27 @@
9898
logger = logging.getLogger("uvicorn.error")
9999

100100

101+
def update_ssl_context(
102+
ctx: ssl.SSLContext,
103+
certfile: str | os.PathLike[str] | None,
104+
keyfile: str | os.PathLike[str] | None,
105+
password: str | None,
106+
cert_reqs: int,
107+
ca_certs: str | os.PathLike[str] | None,
108+
ciphers: str | None,
109+
) -> ssl.SSLContext:
110+
get_password = (lambda: password) if password else None
111+
if certfile and keyfile:
112+
ctx.load_cert_chain(certfile, keyfile, get_password)
113+
if cert_reqs:
114+
ctx.verify_mode = ssl.VerifyMode(cert_reqs)
115+
if ca_certs:
116+
ctx.load_verify_locations(ca_certs)
117+
if ciphers:
118+
ctx.set_ciphers(ciphers)
119+
return ctx
120+
121+
101122
def create_ssl_context(
102123
certfile: str | os.PathLike[str],
103124
keyfile: str | os.PathLike[str] | None,
@@ -219,6 +240,7 @@ def __init__(
219240
ssl_cert_reqs: int = ssl.CERT_NONE,
220241
ssl_ca_certs: str | None = None,
221242
ssl_ciphers: str = "TLSv1",
243+
ssl_context: Callable[[], Any] | None = None,
222244
headers: list[tuple[str, str]] | None = None,
223245
factory: bool = False,
224246
h11_max_incomplete_event_size: int | None = None,
@@ -263,6 +285,7 @@ def __init__(
263285
self.ssl_cert_reqs = ssl_cert_reqs
264286
self.ssl_ca_certs = ssl_ca_certs
265287
self.ssl_ciphers = ssl_ciphers
288+
self.ssl_context = ssl_context
266289
self.headers: list[tuple[str, str]] = headers or []
267290
self.encoded_headers: list[tuple[bytes, bytes]] = []
268291
self.factory = factory
@@ -394,9 +417,19 @@ def configure_logging(self) -> None:
394417
def load(self) -> None:
395418
assert not self.loaded
396419

397-
if self.is_ssl:
420+
if self.ssl_context:
421+
self.ssl: ssl.SSLContext | None = update_ssl_context(
422+
self.ssl_context(),
423+
keyfile=self.ssl_keyfile,
424+
certfile=self.ssl_certfile,
425+
password=self.ssl_keyfile_password,
426+
cert_reqs=self.ssl_cert_reqs,
427+
ca_certs=self.ssl_ca_certs,
428+
ciphers=self.ssl_ciphers,
429+
)
430+
elif self.is_ssl and not self.ssl_context:
398431
assert self.ssl_certfile
399-
self.ssl: ssl.SSLContext | None = create_ssl_context(
432+
self.ssl = create_ssl_context(
400433
keyfile=self.ssl_keyfile,
401434
certfile=self.ssl_certfile,
402435
password=self.ssl_keyfile_password,
@@ -405,6 +438,7 @@ def load(self) -> None:
405438
ca_certs=self.ssl_ca_certs,
406439
ciphers=self.ssl_ciphers,
407440
)
441+
408442
else:
409443
self.ssl = None
410444

uvicorn/main.py

+2
Original file line numberDiff line numberDiff line change
@@ -501,6 +501,7 @@ def run(
501501
ssl_cert_reqs: int = ssl.CERT_NONE,
502502
ssl_ca_certs: str | None = None,
503503
ssl_ciphers: str = "TLSv1",
504+
ssl_context: Callable[[], Any] | None = None,
504505
headers: list[tuple[str, str]] | None = None,
505506
use_colors: bool | None = None,
506507
app_dir: str | None = None,
@@ -553,6 +554,7 @@ def run(
553554
ssl_cert_reqs=ssl_cert_reqs,
554555
ssl_ca_certs=ssl_ca_certs,
555556
ssl_ciphers=ssl_ciphers,
557+
ssl_context=ssl_context,
556558
headers=headers,
557559
use_colors=use_colors,
558560
factory=factory,

0 commit comments

Comments
 (0)