Pure-function detector over a parsed :class:HeaderMap. The full
RFC 7540 §3.2 upgrade dance has three pieces; this module scopes
exactly to the detection step:
Client sends an HTTP/1.1 request with::
Connection: Upgrade, HTTP2-Settings
Upgrade: h2c
HTTP2-Settings: <base64url(SETTINGS payload)>
Server returns 101 Switching Protocols and starts speaking
HTTP/2 on the same TCP connection (the decoded SETTINGS payload
from step 1 is the peer's initial SETTINGS frame).
Subsequent bytes on the connection are the HTTP/2 client
preface (PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n) followed
by frames.
This module owns step 1 — the detection — exclusively. The 101
write, the SETTINGS payload decode, and the HTTP/2 driver hand-off
all live in the reactor and HTTP/2 server modules. Placing the
detector in flare.http.proto lets the H1 parser, the H2
acceptor, and the unified reactor each call into a single, neutral
helper instead of duplicating the predicate or chaining a
reactor-bound import.
Why a sans-I/O module
Before this extraction the canonical helper lived in
flare.http2.server.detect_h2c_upgrade. The reactor's
:class:flare.http._reactor.conn_handle.ConnHandle needed the same
predicate but couldn't import :mod:flare.http2.server (that
module pulls in the H2 reactor driver, which would create a
conn_handle -> http2.server -> flare.http import
cycle). So a byte-for-byte _detect_h2c_upgrade_inline copy
shipped with a TODO note pointing at this future extraction.
Promoting the detector into flare.http.proto.h2c_upgrade
breaks the cycle: every consumer (the H2 server, the unified
reactor, downstream code that wants to detect h2c on its own H1
parser output) imports from the neutral sans-I/O sublayer that
nothing else depends on. The two prior call sites
(:func:flare.http2.server.detect_h2c_upgrade and
conn_handle._detect_h2c_upgrade_inline) both delegate to
:func:detect_h2c_upgrade here, so behaviour is preserved exactly.
Strict acceptance contract
The RFC requires both Upgrade: h2c and a non-empty
HTTP2-Settings header. We accept the upgrade only when both
are present:
Upgrade header value must be exactly "h2c" (case-
insensitive comparison via :class:HeaderMap lookup).
HTTP2-Settings header must be present with a non-empty
value. The base64url decode + payload validation happens later
in the reactor when the connection migrates; rejecting the
upgrade here on a missing header is sufficient to skip the
reactor migration entirely for malformed clients.
Sans-I/O contract
This module imports from :mod:flare.http.headers only. No
sockets, no reactor, no TLS, no filesystem. HeaderMap is a
pure value type. The detector is a one-line predicate over header
lookups; it raises nothing and allocates nothing.