Co-Authored-By: Claude Sonnet 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_01KqJyCidz3ZgRyHABv2GVJh
47 lines
2.1 KiB
Python
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()
|