"""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()