import logging import sys from asyncio import Event from pathlib import Path import pytest from sanic import Sanic from sanic.compat import UVLOOP_INSTALLED from sanic.http.constants import HTTP parent_dir = Path(__file__).parent.parent localhost_dir = parent_dir / "certs/localhost" @pytest.mark.parametrize("version", (3, HTTP.VERSION_3)) @pytest.mark.skipif( sys.version_info < (3, 8) and not UVLOOP_INSTALLED, reason="In 3.7 w/o uvloop the port is not always released", ) def test_server_starts_http3(app: Sanic, version, caplog): ev = Event() @app.after_server_start def shutdown(*_): ev.set() app.stop() with caplog.at_level(logging.INFO): app.run( version=version, ssl={ "cert": localhost_dir / "fullchain.pem", "key": localhost_dir / "privkey.pem", }, single_process=True, ) assert ev.is_set() assert ( "sanic.root", logging.INFO, "server: sanic, HTTP/3", ) in caplog.record_tuples @pytest.mark.skipif( sys.version_info < (3, 8) and not UVLOOP_INSTALLED, reason="In 3.7 w/o uvloop the port is not always released", ) def test_server_starts_http1_and_http3(app: Sanic, caplog): @app.after_server_start def shutdown(*_): app.stop() app.prepare( version=3, ssl={ "cert": localhost_dir / "fullchain.pem", "key": localhost_dir / "privkey.pem", }, ) app.prepare( version=1, ssl={ "cert": localhost_dir / "fullchain.pem", "key": localhost_dir / "privkey.pem", }, ) with caplog.at_level(logging.INFO): Sanic.serve_single() assert ( "sanic.root", logging.INFO, "server: sanic, HTTP/1.1", ) in caplog.record_tuples assert ( "sanic.root", logging.INFO, "server: sanic, HTTP/3", ) in caplog.record_tuples @pytest.mark.skipif( sys.version_info < (3, 8) and not UVLOOP_INSTALLED, reason="In 3.7 w/o uvloop the port is not always released", ) def test_server_starts_http1_and_http3_bad_order(app: Sanic, caplog): @app.after_server_start def shutdown(*_): app.stop() app.prepare( version=1, ssl={ "cert": localhost_dir / "fullchain.pem", "key": localhost_dir / "privkey.pem", }, ) message = ( "Serving HTTP/3 instances as a secondary server is not supported. " "There can only be a single HTTP/3 worker and it must be the first " "instance prepared." ) with pytest.raises(RuntimeError, match=message): app.prepare( version=3, ssl={ "cert": localhost_dir / "fullchain.pem", "key": localhost_dir / "privkey.pem", }, )