Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add GenericCreator for loading SSL certs in processes #2578

Merged
merged 10 commits into from
Oct 31, 2022
23 changes: 17 additions & 6 deletions sanic/worker/loader.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
cast,
)

from sanic.http.tls.context import process_to_context
from sanic.http.tls.creators import CertCreator, MkcertCreator, TrustmeCreator


Expand Down Expand Up @@ -109,18 +110,28 @@ class CertLoader:
_creator_class: Type[CertCreator]

def __init__(self, ssl_data: Dict[str, Union[str, os.PathLike]]):
self._ssl_data = ssl_data

creator_name = ssl_data.get("creator")
if creator_name not in ("mkcert", "trustme"):
raise RuntimeError(f"Unknown certificate creator: {creator_name}")
elif creator_name == "mkcert":
self._creator_class = MkcertCreator
elif creator_name == "trustme":
self._creator_class = TrustmeCreator
if not creator_name:
return
else:
ahopkins marked this conversation as resolved.
Show resolved Hide resolved
if creator_name not in ("mkcert", "trustme"):
raise RuntimeError(
f"Unknown certificate creator: {creator_name}"
)
elif creator_name == "mkcert":
self._creator_class = MkcertCreator
elif creator_name == "trustme":
self._creator_class = TrustmeCreator
ahopkins marked this conversation as resolved.
Show resolved Hide resolved

self._key = ssl_data["key"]
self._cert = ssl_data["cert"]
self._localhost = cast(str, ssl_data["localhost"])

def load(self, app: SanicApp):
if not hasattr(self, "_creator_class"):
ahopkins marked this conversation as resolved.
Show resolved Hide resolved
return process_to_context(self._ssl_data)
ahopkins marked this conversation as resolved.
Show resolved Hide resolved

creator = self._creator_class(app, self._key, self._cert)
return creator.generate_cert(self._localhost)
27 changes: 27 additions & 0 deletions tests/test_tls.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import subprocess

from contextlib import contextmanager
from multiprocessing import Event
from pathlib import Path
from unittest.mock import Mock, patch
from urllib.parse import urlparse
Expand Down Expand Up @@ -636,3 +637,29 @@ def test_sanic_ssl_context_create():

assert sanic_context is context
assert isinstance(sanic_context, SanicSSLContext)


def test_ssl_in_multiprocess_mode(app: Sanic, caplog):

ssl_dict = {"cert": localhost_cert, "key": localhost_key}
event = Event()

@app.main_process_start
async def main_start(app: Sanic):
app.shared_ctx.event = event

@app.after_server_start
async def shutdown(app):
app.shared_ctx.event.set()
app.stop()

assert not event.is_set()
with caplog.at_level(logging.INFO):
app.run(ssl=ssl_dict)
assert event.is_set()

assert (
"sanic.root",
logging.INFO,
"Goin' Fast @ https://127.0.0.1:8000",
) in caplog.record_tuples