Skip to content

blackbull.server.parser

blackbull.server.parser

parse_headers(frame)

Build a native :class:Connection (http or websocket) from a HEADERS frame, or None when the request is malformed.

Hot path on every request — kept as a module-level function so that callers avoid the dict-lookup + parser allocation that ParserFactory requires.

Yields a :class:Connection, not an ASGI scope dict — the H/2 analogue of :meth:HTTP1Actor._parse. The actor derives the compat scope via :meth:Connection.as_scope at the dispatch boundary; the websocket-only subprotocols scope key is attached there too, since it is not a :class:Connection field, the same way :meth:HTTP1Actor._handle_upgrade augments the derived scope.

The :class:Connection is built once, at the very end, from locals accumulated while walking the pseudo-headers — the same single-construction idiom :meth:HTTP1Actor._parse uses. Every early-out returns None rather than a half-built Connection, so the contract is uniformly result is None ⟺ frame.malformed: callers check frame.malformed before reading the result, and _request_headers_with_host marks it before returning None. The plain-HTTP branch constructs through _build_h2_connection rather than the dataclass constructor; see that function's docstring.

Also performs request-level pseudo-header presence checks (RFC 9113 §8.3.1). Field-level checks already happened in parse_payload. On any known-bad input — parse_payload having flagged frame.malformed, or a missing/empty required pseudo found here (which sets frame.malformed) — we return None rather than build a throwaway :class:Connection: the actor's frame.malformed check answers RST_STREAM before it would read the result, so no object is constructed on the error path.