HTTP/3 Support (#2378)

This commit is contained in:
Adam Hopkins
2022-06-27 11:19:26 +03:00
committed by GitHub
parent 70382f21ba
commit b59da498cc
72 changed files with 2567 additions and 437 deletions

View File

@@ -1,3 +1,4 @@
from .altsvc import AltSvcCheck # noqa
from .base import BaseScheme
from .ode import OptionalDispatchEvent # noqa

View File

@@ -0,0 +1,56 @@
from __future__ import annotations
from ast import Assign, Constant, NodeTransformer, Subscript
from typing import TYPE_CHECKING, Any, List
from sanic.http.constants import HTTP
from .base import BaseScheme
if TYPE_CHECKING:
from sanic import Sanic
class AltSvcCheck(BaseScheme):
ident = "ALTSVC"
def visitors(self) -> List[NodeTransformer]:
return [RemoveAltSvc(self.app, self.app.state.verbosity)]
class RemoveAltSvc(NodeTransformer):
def __init__(self, app: Sanic, verbosity: int = 0) -> None:
self._app = app
self._verbosity = verbosity
self._versions = {
info.settings["version"] for info in app.state.server_info
}
def visit_Assign(self, node: Assign) -> Any:
if any(self._matches(target) for target in node.targets):
if self._should_remove():
return None
assert isinstance(node.value, Constant)
node.value.value = self.value()
return node
def _should_remove(self) -> bool:
return len(self._versions) == 1
@staticmethod
def _matches(node) -> bool:
return (
isinstance(node, Subscript)
and isinstance(node.slice, Constant)
and node.slice.value == "alt-svc"
)
def value(self):
values = []
for info in self._app.state.server_info:
port = info.settings["port"]
version = info.settings["version"]
if version is HTTP.VERSION_3:
values.append(f'h3=":{port}"')
return ", ".join(values)

View File

@@ -1,5 +1,8 @@
from abc import ABC, abstractmethod
from typing import Set, Type
from ast import NodeTransformer, parse
from inspect import getsource
from textwrap import dedent
from typing import Any, Dict, List, Set, Type
class BaseScheme(ABC):
@@ -10,11 +13,26 @@ class BaseScheme(ABC):
self.app = app
@abstractmethod
def run(self, method, module_globals) -> None:
def visitors(self) -> List[NodeTransformer]:
...
def __init_subclass__(cls):
BaseScheme._registry.add(cls)
def __call__(self, method, module_globals):
return self.run(method, module_globals)
def __call__(self):
return self.visitors()
@classmethod
def build(cls, method, module_globals, app):
raw_source = getsource(method)
src = dedent(raw_source)
node = parse(src)
for scheme in cls._registry:
for visitor in scheme(app)():
node = visitor.visit(node)
compiled_src = compile(node, method.__name__, "exec")
exec_locals: Dict[str, Any] = {}
exec(compiled_src, module_globals, exec_locals) # nosec
return exec_locals[method.__name__]

View File

@@ -1,7 +1,5 @@
from ast import Attribute, Await, Dict, Expr, NodeTransformer, parse
from inspect import getsource
from textwrap import dedent
from typing import Any
from ast import Attribute, Await, Expr, NodeTransformer
from typing import Any, List
from sanic.log import logger
@@ -20,16 +18,8 @@ class OptionalDispatchEvent(BaseScheme):
signal.name for signal in app.signal_router.routes
]
def run(self, method, module_globals):
raw_source = getsource(method)
src = dedent(raw_source)
tree = parse(src)
node = RemoveDispatch(self._registered_events).visit(tree)
compiled_src = compile(node, method.__name__, "exec")
exec_locals: Dict[str, Any] = {}
exec(compiled_src, module_globals, exec_locals) # nosec
return exec_locals[method.__name__]
def visitors(self) -> List[NodeTransformer]:
return [RemoveDispatch(self._registered_events)]
def _sync_events(self):
all_events = set()

View File

@@ -21,10 +21,8 @@ class TouchUp:
module = getmodule(target)
module_globals = dict(getmembers(module))
for scheme in BaseScheme._registry:
modified = scheme(app)(method, module_globals)
setattr(target, method_name, modified)
modified = BaseScheme.build(method, module_globals, app)
setattr(target, method_name, modified)
target.__touched__ = True