Files
javademos/http3/server/h3server.py
T

47 lines
2.1 KiB
Python

"""Minimal HTTP/3 server on aioquic (the library Hypercorn uses for QUIC). It answers the same paths as app.py.
Hypercorn's own QUIC listener proved unreliable under packet loss (its UDPServer task dies on a KeyError and the
port then goes silent), so HTTP/3 is served from here and Hypercorn serves only TCP."""
import asyncio, ssl
from aioquic.asyncio import QuicConnectionProtocol, serve as quic_serve
from aioquic.h3.connection import H3_ALPN, H3Connection
from aioquic.h3.events import HeadersReceived, DataReceived
from aioquic.quic.configuration import QuicConfiguration
from aioquic.quic.events import ProtocolNegotiated
from app import respond # (path, http_version, host, port) -> (status, headers, body)
class H3Protocol(QuicConnectionProtocol):
def __init__(self, *a, **kw):
super().__init__(*a, **kw)
self._http = None
self._pending = {}
def quic_event_received(self, event):
if isinstance(event, ProtocolNegotiated) and event.alpn_protocol in H3_ALPN:
self._http = H3Connection(self._quic)
if self._http is None:
return
for ev in self._http.handle_event(event):
if isinstance(ev, HeadersReceived):
self._pending[ev.stream_id] = dict(ev.headers)
if ev.stream_ended: self._reply(ev.stream_id)
elif isinstance(ev, DataReceived) and ev.stream_ended:
self._reply(ev.stream_id)
def _reply(self, stream_id):
h = self._pending.pop(stream_id, None)
if h is None: return
status, headers, body = respond(h[b":path"].decode(), "3", "127.0.0.1", self._port)
self._http.send_headers(stream_id, [(b":status", str(status).encode())] + headers)
self._http.send_data(stream_id, body, end_stream=True)
self.transmit()
_port = 4433
async def start(port, certfile, keyfile):
H3Protocol._port = port
cfg = QuicConfiguration(is_client=False, alpn_protocols=H3_ALPN)
cfg.load_cert_chain(certfile, keyfile)
await quic_serve("127.0.0.1", port, configuration=cfg, create_protocol=H3Protocol)
await asyncio.Event().wait()