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

HTTP/3 Support #2378

Merged
merged 74 commits into from Jun 27, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
74 commits
Select commit Hold shift + click to select a range
f1c2d0e
WIP to begin http3
ahopkins Dec 12, 2021
027c8e0
Merge conflicts
ahopkins Dec 26, 2021
34fe437
Merge branch 'main' of github.com:sanic-org/sanic into http3
ahopkins Dec 26, 2021
a937977
WIP
ahopkins Dec 27, 2021
6a65222
WIP
ahopkins Dec 27, 2021
fa69294
Merge main conflicts into HTTP/3
ahopkins Jan 16, 2022
a43ae38
Merge pull request #2379 from sanic-org/http3-conflict
ahopkins Jan 16, 2022
c852406
Add aioquic and HTTP auto
ahopkins Jan 16, 2022
70a3a6f
Merge branch 'http3' of github.com:sanic-org/sanic into http3-startup
ahopkins Jan 17, 2022
65459fd
WIP
ahopkins Jan 17, 2022
cab7453
Add multi-serve for http3 and http1
ahopkins Jan 19, 2022
1bb80ba
Add spinner on startup delay
ahopkins Jan 20, 2022
96a746e
Merge branch 'main' of github.com:sanic-org/sanic into http3-startup
ahopkins Jan 20, 2022
6e3e5f1
Better exception
ahopkins Feb 17, 2022
64d4419
Add alt-svc header touchup
ahopkins Feb 17, 2022
97158d8
Add altsvs
ahopkins Feb 17, 2022
13ee4c4
Allow for TLS certs to be created on HTTP/1.1 dev servers
ahopkins Feb 21, 2022
80cc6a2
Add deprecation notice
ahopkins Feb 21, 2022
bb1bf66
Cleanup TODOs
ahopkins Feb 21, 2022
e193714
Merge pull request #2380 from sanic-org/http3-startup
ahopkins Feb 22, 2022
e447751
Cleanup merge conflicts
ahopkins Feb 22, 2022
65c02e2
Cleanup merge conflicts
ahopkins Feb 22, 2022
964e0b0
Cleanup merge conflicts
ahopkins Feb 22, 2022
067316a
Merge branch 'main' of github.com:sanic-org/sanic into http3
ahopkins Feb 22, 2022
d26d79c
Add TLS password to config
ahopkins Feb 23, 2022
06035c8
Move HTTP streaming events to receiver
ahopkins Feb 23, 2022
2262c69
Streaming send
ahopkins Feb 24, 2022
517d7d5
Setup response headers
ahopkins Feb 24, 2022
8b090f2
Merge branch 'main' of github.com:sanic-org/sanic into http3-flow
ahopkins Feb 27, 2022
b66b460
Merge conflicts
ahopkins May 11, 2022
35bbdfe
Logging
ahopkins May 11, 2022
199d6ea
Move verbosity filtering to logger
ahopkins May 11, 2022
7eb64bb
Merge branch 'main' of github.com:sanic-org/sanic into verbosity
ahopkins May 11, 2022
e98a631
Fix verbosity test on ASGI
ahopkins May 11, 2022
4d2afed
Add Sanic color
ahopkins May 11, 2022
cccf536
Merge conflicts
ahopkins May 11, 2022
425182c
WIP
ahopkins May 12, 2022
46af59b
merge conflict
ahopkins May 23, 2022
9045719
Finish flow
ahopkins May 24, 2022
92d2f41
Merge pull request #2403 from sanic-org/http3-flow
ahopkins May 24, 2022
0e85218
Merge branch 'main' of github.com:sanic-org/sanic into http3
ahopkins Jun 15, 2022
171dabb
Merge branch 'main' of github.com:sanic-org/sanic into http3
ahopkins Jun 16, 2022
dd96f1a
Get regular tests running again
ahopkins Jun 16, 2022
15d654c
Add trustme certs (#2468)
ahopkins Jun 16, 2022
c944291
Merge conflicts
ahopkins Jun 19, 2022
191d5c5
Add some unit tests
ahopkins Jun 19, 2022
872b58f
Add some unit tests
ahopkins Jun 19, 2022
1222d11
Change relative import of test client
ahopkins Jun 19, 2022
d1ddbca
TLS creators tests
ahopkins Jun 19, 2022
c2792eb
Update tests
ahopkins Jun 19, 2022
9572091
clenup
ahopkins Jun 19, 2022
1c9a037
Add TLS creator tests
ahopkins Jun 20, 2022
000a166
Add TLS creator selection test
ahopkins Jun 20, 2022
ec33f8a
Remove unneeded files
ahopkins Jun 20, 2022
f1cfc34
Cleanup config tests
ahopkins Jun 20, 2022
53bf127
Add some typeing
ahopkins Jun 20, 2022
d228ae3
Remove unnecessary coverage
ahopkins Jun 20, 2022
a8c65fc
Reduce duplicate code in server runners
ahopkins Jun 20, 2022
84fd7e0
Remove unnecessary inits
ahopkins Jun 20, 2022
b64c5b6
Remove unnecessary inits
ahopkins Jun 20, 2022
bb68e28
Fix some typing and linting issues
ahopkins Jun 20, 2022
ed840a2
Expand test coverage
ahopkins Jun 20, 2022
3d37a92
Cleanup existing tests
ahopkins Jun 20, 2022
9111f93
Increase testing coverage
ahopkins Jun 20, 2022
689c830
Add connection info and transport details
ahopkins Jun 21, 2022
ceee998
Fix 3.7 and 3.8 tests
ahopkins Jun 21, 2022
44b6b72
Fix 3.7
ahopkins Jun 21, 2022
d8c1424
Merge branch 'main' of github.com:sanic-org/sanic into http3
ahopkins Jun 26, 2022
089a8d2
Better typing and error message
ahopkins Jun 26, 2022
2f244c1
Remove unnecessary __version__
ahopkins Jun 26, 2022
ea00532
Only allow stream_id on HTTP/3
ahopkins Jun 26, 2022
93ca605
Cleanup sanic-routing imports
ahopkins Jun 26, 2022
96042fc
Setup mock test for HTTP/3 send headers
ahopkins Jun 26, 2022
653343c
Merge branch 'main' into http3
ahopkins Jun 27, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions .coveragerc
Expand Up @@ -20,6 +20,7 @@ exclude_lines =
noqa
NOQA
pragma: no cover
TYPE_CHECKING
omit =
site-packages
sanic/__main__.py
Expand Down
7 changes: 7 additions & 0 deletions pyproject.toml
Expand Up @@ -16,3 +16,10 @@ lines_after_imports = 2
lines_between_types = 1
multi_line_output = 3
profile = "black"

[[tool.mypy.overrides]]
module = [
"trustme.*",
"sanic_routing.*",
]
ignore_missing_imports = true
15 changes: 8 additions & 7 deletions sanic/app.py
Expand Up @@ -43,11 +43,8 @@
from urllib.parse import urlencode, urlunparse
from warnings import filterwarnings

from sanic_routing.exceptions import ( # type: ignore
FinalizationError,
NotFound,
)
from sanic_routing.route import Route # type: ignore
from sanic_routing.exceptions import FinalizationError, NotFound
from sanic_routing.route import Route

from sanic.application.ext import setup_ext
from sanic.application.state import ApplicationState, Mode, ServerStage
Expand All @@ -64,6 +61,7 @@
URLBuildError,
)
from sanic.handlers import ErrorHandler
from sanic.helpers import _default
from sanic.http import Stage
from sanic.log import (
LOGGING_CONFIG_DEFAULTS,
Expand Down Expand Up @@ -92,7 +90,7 @@
from sanic.touchup import TouchUp, TouchUpMeta


if TYPE_CHECKING: # no cov
if TYPE_CHECKING:
try:
from sanic_ext import Extend # type: ignore
from sanic_ext.extensions.base import Extension # type: ignore
Expand Down Expand Up @@ -949,6 +947,7 @@ async def handle_request(self, request: Request): # no cov
"response": response,
},
)
...
await response.send(end_stream=True)
elif isinstance(response, ResponseStream):
resp = await response(request)
Expand Down Expand Up @@ -1532,8 +1531,10 @@ async def _startup(self):
if hasattr(self, "_ext"):
self.ext._display()

if self.state.is_debug:
if self.state.is_debug and self.config.TOUCHUP is not True:
self.config.TOUCHUP = False
elif self.config.TOUCHUP is _default:
self.config.TOUCHUP = True

# Setup routers
self.signalize(self.config.TOUCHUP)
Expand Down
23 changes: 23 additions & 0 deletions sanic/application/constants.py
@@ -0,0 +1,23 @@
from enum import Enum, IntEnum, auto


class StrEnum(str, Enum):
def _generate_next_value_(name: str, *args) -> str: # type: ignore
return name.lower()


class Server(StrEnum):
SANIC = auto()
ASGI = auto()
GUNICORN = auto()


class Mode(StrEnum):
PRODUCTION = auto()
DEBUG = auto()


class ServerStage(IntEnum):
STOPPED = auto()
PARTIAL = auto()
SERVING = auto()
2 changes: 1 addition & 1 deletion sanic/application/ext.py
Expand Up @@ -5,7 +5,7 @@
from typing import TYPE_CHECKING


if TYPE_CHECKING: # no cov
if TYPE_CHECKING:
from sanic import Sanic

try:
Expand Down
86 changes: 86 additions & 0 deletions sanic/application/spinner.py
@@ -0,0 +1,86 @@
import os
import sys
import time

from contextlib import contextmanager
from queue import Queue
from threading import Thread


if os.name == "nt": # noqa
import ctypes # noqa

class _CursorInfo(ctypes.Structure):
_fields_ = [("size", ctypes.c_int), ("visible", ctypes.c_byte)]


class Spinner: # noqa
def __init__(self, message: str) -> None:
self.message = message
self.queue: Queue[int] = Queue()
self.spinner = self.cursor()
self.thread = Thread(target=self.run)

def start(self):
self.queue.put(1)
self.thread.start()
self.hide()

def run(self):
while self.queue.get():
output = f"\r{self.message} [{next(self.spinner)}]"
sys.stdout.write(output)
sys.stdout.flush()
time.sleep(0.1)
self.queue.put(1)

def stop(self):
self.queue.put(0)
self.thread.join()
self.show()

@staticmethod
def cursor():
while True:
for cursor in "|/-\\":
yield cursor

@staticmethod
def hide():
if os.name == "nt":
ci = _CursorInfo()
handle = ctypes.windll.kernel32.GetStdHandle(-11)
ctypes.windll.kernel32.GetConsoleCursorInfo(
handle, ctypes.byref(ci)
)
ci.visible = False
ctypes.windll.kernel32.SetConsoleCursorInfo(
handle, ctypes.byref(ci)
)
elif os.name == "posix":
sys.stdout.write("\033[?25l")
sys.stdout.flush()

@staticmethod
def show():
if os.name == "nt":
ci = _CursorInfo()
handle = ctypes.windll.kernel32.GetStdHandle(-11)
ctypes.windll.kernel32.GetConsoleCursorInfo(
handle, ctypes.byref(ci)
)
ci.visible = True
ctypes.windll.kernel32.SetConsoleCursorInfo(
handle, ctypes.byref(ci)
)
elif os.name == "posix":
sys.stdout.write("\033[?25h")
sys.stdout.flush()


@contextmanager
def loading(message: str = "Loading"): # noqa
spinner = Spinner(message)
spinner.start()
yield
spinner.stop()
26 changes: 2 additions & 24 deletions sanic/application/state.py
Expand Up @@ -3,42 +3,20 @@
import logging

from dataclasses import dataclass, field
from enum import Enum, IntEnum, auto
from pathlib import Path
from socket import socket
from ssl import SSLContext
from typing import TYPE_CHECKING, Any, Dict, List, Optional, Set, Union

from sanic.application.constants import Mode, Server, ServerStage
from sanic.log import VerbosityFilter, logger
from sanic.server.async_server import AsyncioServer


if TYPE_CHECKING: # no cov
if TYPE_CHECKING:
from sanic import Sanic


class StrEnum(str, Enum):
def _generate_next_value_(name: str, *args) -> str: # type: ignore
return name.lower()


class Server(StrEnum):
SANIC = auto()
ASGI = auto()
GUNICORN = auto()


class Mode(StrEnum):
PRODUCTION = auto()
DEBUG = auto()


class ServerStage(IntEnum):
STOPPED = auto()
PARTIAL = auto()
SERVING = auto()


@dataclass
class ApplicationServerInfo:
settings: Dict[str, Any]
Expand Down
2 changes: 1 addition & 1 deletion sanic/asgi.py
Expand Up @@ -17,7 +17,7 @@
from sanic.server.websockets.connection import WebSocketConnection


if TYPE_CHECKING: # no cov
if TYPE_CHECKING:
from sanic import Sanic


Expand Down
2 changes: 1 addition & 1 deletion sanic/blueprint_group.py
Expand Up @@ -5,7 +5,7 @@
from typing import TYPE_CHECKING, List, Optional, Union


if TYPE_CHECKING: # no cov
if TYPE_CHECKING:
from sanic.blueprints import Blueprint


Expand Down
6 changes: 3 additions & 3 deletions sanic/blueprints.py
Expand Up @@ -21,8 +21,8 @@
Union,
)

from sanic_routing.exceptions import NotFound # type: ignore
from sanic_routing.route import Route # type: ignore
from sanic_routing.exceptions import NotFound
from sanic_routing.route import Route

from sanic.base.root import BaseSanic
from sanic.blueprint_group import BlueprintGroup
Expand All @@ -36,7 +36,7 @@
)


if TYPE_CHECKING: # no cov
if TYPE_CHECKING:
from sanic import Sanic


Expand Down
16 changes: 14 additions & 2 deletions sanic/cli/app.py
Expand Up @@ -58,10 +58,13 @@ def __init__(self) -> None:
os.environ.get("SANIC_RELOADER_PROCESS", "") != "true"
)
self.args: List[Any] = []
self.groups: List[Group] = []

def attach(self):
for group in Group._registry:
group.create(self.parser).attach()
instance = group.create(self.parser)
instance.attach()
self.groups.append(instance)

def run(self):
# This is to provide backwards compat -v to display version
Expand All @@ -81,9 +84,13 @@ def run(self):
try:
app = self._get_app()
kwargs = self._build_run_kwargs()
app.run(**kwargs)
except ValueError:
error_logger.exception("Failed to run app")
else:
for http_version in self.args.http:
app.prepare(**kwargs, version=http_version)

Sanic.serve()

def _precheck(self):
# # Custom TLS mismatch handling for better diagnostics
Expand Down Expand Up @@ -163,11 +170,14 @@ def _get_app(self):
" Example File: project/sanic_server.py -> app\n"
" Example Module: project.sanic_server.app"
)
sys.exit(1)
else:
raise e
return app

def _build_run_kwargs(self):
for group in self.groups:
group.prepare(self.args)
ssl: Union[None, dict, str, list] = []
if self.args.tlshost:
ssl.append(None)
Expand All @@ -192,6 +202,7 @@ def _build_run_kwargs(self):
"unix": self.args.unix,
"verbosity": self.args.verbosity or 0,
"workers": self.args.workers,
"auto_tls": self.args.auto_tls,
}

for maybe_arg in ("auto_reload", "dev"):
Expand All @@ -201,4 +212,5 @@ def _build_run_kwargs(self):
if self.args.path:
kwargs["auto_reload"] = True
kwargs["reload_dir"] = self.args.path

return kwargs