IMMREX7
# -*- coding: utf-8 -*-
from typing import Callable, Optional
import ssl
import cherrypy
from primordial.config import Config
def start_cherry(config: Config, api: Callable, path: str) -> None:
"""Start CherryPy running an API.
:param config: The configuration to use for CherryPy
:param api: The API callable to be started
:param path: The path under which the API should respond to requests
"""
cherrypy.tree.graft(api, path)
run_cherrypy(config)
def start_cherry_https(config: Config, api: Callable, path: str) -> None:
"""Start CherryPy running an API on HTTPS only.
This is a backport of how ServiceGroup & DNS
verticals start.
:param config: The configuration to use for CherryPy
:param api: The API callable to be started
:param path: The path under which the API should respond to requests
"""
cherrypy.tree.graft(api, path)
cert_file = config.get("server_cert")
pkey_file = config.get("server_key")
ca_certs_file = config.get("server_ca")
run_cherrypy(
config,
cert_path=cert_file,
key_path=pkey_file,
ca_certs_path=ca_certs_file)
def run_cherrypy(
config: Config,
cert_path: Optional[str] = None,
key_path: Optional[str] = None,
ca_certs_path: Optional[str] = None) -> None:
"""Run CherryPy. Called by `start_cherry`.
:param config: The configuration to use for running CherryPy
"""
restart_trigger = config.get('restart_trigger')
cherrypy.engine.autoreload.files.add(restart_trigger)
# Only reload when the restart trigger changes (not other files)
# TODO non-local only
cherrypy.engine.autoreload.match = restart_trigger
server_host = config.get('server.host')
https_port = config.get_int('server.https.port')
if https_port and cert_path is not None:
cherrypy.config.update({
'server.socket_host': server_host,
'server.socket_port': https_port,
'server.ssl_module': 'builtin',
'server.ssl_certificate': cert_path,
'server.ssl_private_key': key_path,
'server.ssl_context': ssl_context(cert_path, key_path, ca_certs_path)})
else:
server_port = config.get_int('server.port')
cherrypy.config.update({'server.socket_host': server_host,
'server.socket_port': server_port})
cherrypy.engine.start()
cherrypy.engine.block()
def ssl_context(certificate, private_key, ca_certs):
"""Create a server SSL context requiring client certificate authentication.
:param certificate: Path to server certificate.
:param private_key: Path to server private key.
:param ca_certs: Path to certificate authority chain against which client
certificates are validated.
"""
context = ssl.create_default_context(
purpose=ssl.Purpose.CLIENT_AUTH,
cafile=ca_certs
)
context.load_cert_chain(certificate, private_key)
context.verify_mode = ssl.CERT_REQUIRED
return context
Copyright © 2021 -