blackbull.fault_injection.scenario_h2¶
blackbull.fault_injection.scenario_h2
¶
Programmable HTTP/2 wire-level scenario model.
A :class:ScenarioH2 is a sequence of typed steps that the
:class:blackbull.fault_injection.h2_server.H2FaultServer executor
walks in order against a connected HTTP/2 client. This is the
server-side half of the :mod:blackbull.fault_injection toolkit:
a programmable server that emits deliberate misbehaviour toward a
client — half-closed streams, exhausted flow-control windows,
illegal SETTINGS, weird frame sequences — expressed as data, not
procedural test code.
The symmetric client-side half (programmable HTTP/1.1 client
driving deliberate misbehaviour toward a server) lives in
:mod:blackbull.fault_injection.scenario_h1.
Use cases:
- HTTP/2 client-library authors testing their client's resilience against a misbehaving server.
- Proxy / load-balancer authors testing what their transit code does when an upstream emits illegal frame sequences.
- Security researchers reproducing CVE-class patterns (CONTINUATION-flood, RST-flood) from a deterministic harness.
Steps¶
- :class:
SendFrame— emit one parsed :class:FrameBaseinstance. The executor handles serialisation through the existing :class:~blackbull.protocol.frame.FrameFactory. - :class:
SendRawBytes— escape hatch for bytes the framework'sFrameFactorycannot construct (e.g. illegal frame types, oversized frames, malformed length fields). - :class:
WaitForClientFrame— pause until an inbound frame from the client matches the declarative match dict. Fields supported:
=============== =========================================
type 'HEADERS', 'SETTINGS', etc.
stream_id Integer; None = any.
flags_set List of flag names (uppercase) that must be set.
flags_unset List of flag names that must be unset.
=============== =========================================
- :class:
Sleep— idle without sending or reading. - :class:
Abort— hard-close the underlying transport (RST on Linux). - :class:
CloseGracefully— send a GOAWAY frame, then close cleanly.
Serialisation¶
:meth:ScenarioH2.to_json / :meth:ScenarioH2.from_json round-trip
through JSON Lines. Because :class:SendFrame carries a frame
object, round-tripping requires the frame to be reconstructable from
its serialised form — currently SETTINGS, WINDOW_UPDATE, RST_STREAM,
GOAWAY, PING, and a typed-payload form of HEADERS / DATA. For ad-hoc
frames the caller passes through :class:SendRawBytes, which is
always round-trippable.
Abort
dataclass
¶
Hard-close the connection (transport.abort → RST on Linux).
CloseGracefully
dataclass
¶
Send a GOAWAY then close cleanly.
Subsequent scenario steps short-circuit (this is a terminator
just like :class:Abort). error_code is one of the
:class:~blackbull.protocol.frame_types.ErrorCodes values;
last_stream_id advertises the last stream the server is
willing to process — pass 0 to refuse all client streams,
or the highest accepted stream ID otherwise.
ScenarioH2
dataclass
¶
Sequence of steps a programmable H2 server walks per connection.
Two control knobs sit outside the step list because they apply to the whole connection, not to one step:
send_preface: whether the server sends the standardSERVER_PREFACE_BYTES+ initial SETTINGS at handshake time. Most catalogue scenarios want this (real H2 clients require it before proceeding); setFalseto exercise client behaviour against a server that skips the handshake.initial_settings: tuple of(setting_id, value)pairs the server advertises in its initial SETTINGS frame. Used by the "exhausted window" and "illegal SETTINGS" catalogue entries to inject a hostile starting state before the first step runs.
ScenarioH2Result
dataclass
¶
Outcome of one :class:ScenarioH2 run.
Mirrors :class:~blackbull.fault_injection.scenario_h1.ScenarioResult's
shape so callers can write uniform pytest assertions across
protocols.
SendFrame
dataclass
¶
Emit one parsed frame onto the connection.
Routed through :class:~blackbull.protocol.frame.FrameFactory so
the on-wire serialisation matches the framework's normal output.
Use :class:SendRawBytes for frames the factory cannot construct.
SendRawBytes
dataclass
¶
Push arbitrary bytes onto the connection.
Escape hatch for malformed frames (illegal type byte, length
exceeding SETTINGS_MAX_FRAME_SIZE, etc.) that the typed
:class:SendFrame path will not produce.
byte_interval > 0 transmits one byte at a time with that
delay — useful for stalled-handshake patterns where the client
is expected to enforce a preface-completion timeout.
Sleep
dataclass
¶
Idle for duration seconds without reading or writing.
StepOpH2
¶
Bases: str, Enum
Tag used by the JSON serialiser.
WaitForClientFrame
dataclass
¶
Block until an inbound frame matches match.
Declarative grammar — see module docstring for the supported keys. Frames the client sends that do not match are still consumed (the executor remains responsive to the wire) but do not advance this step.
On timeout expiry the executor records the miss on
:class:ScenarioH2Result and proceeds to the next step.
frame_matches(frame, match)
¶
Return True iff frame satisfies every key in match.
Recognised keys: type, stream_id, flags_set,
flags_unset. Unknown keys fail closed — an unrecognised
match key is almost certainly a typo in a catalogue entry, and
silently matching on a missing key would hide the bug.
scenario_from_json(src)
¶
Parse JSON Lines back to a :class:ScenarioH2.
scenario_to_json(scenario)
¶
Serialise scenario to JSON Lines (one step per line).
Header lines (send_preface flag, initial_settings) sit on
the first line under the op HEADER so the file is one
line-oriented stream with no out-of-band metadata.