blackbull.fault_injection.h2_server¶
blackbull.fault_injection.h2_server
¶
Programmable HTTP/2 server that emits deliberate misbehaviour.
:class:H2FaultServer listens on 127.0.0.1:<random>, accepts one
HTTP/2 (h2c plaintext) connection at a time, and walks a
:class:~blackbull.fault_injection.scenario_h2.ScenarioH2 against it.
It is designed for client-library / proxy / security-research test
suites that need to exercise a client against a server that
deliberately does the wrong thing — half-closed streams, exhausted
flow-control windows, illegal SETTINGS, weird frame sequences.
The server is an opt-in testing instrument. It refuses to start when
BB_PRODUCTION is set in the environment so a deliberate-misbehaviour
code path cannot accidentally fire on a production deployment. This
is the isolated, hard-opt-in design called out in the project's
out-of-scope list as the only acceptable form for fault-injection
machinery inside a correctness-focused framework.
Tutorial: docs/guide/fault_injection.md.
Quick start¶
.. code-block:: python
import pytest
from blackbull.fault_injection import H2FaultServer
from blackbull.fault_injection.catalogue import half_closed_stream_no_data
@pytest.fixture
async def fault_server():
async with H2FaultServer(scenario=half_closed_stream_no_data()) as srv:
yield srv
async def test_client_times_out_on_stalled_stream(fault_server):
client = MyH2Client(fault_server.url)
with pytest.raises(TimeoutError):
await client.get('/', timeout=1.0)
H2FaultServer
¶
Programmable HTTP/2 server emitting a :class:ScenarioH2.
Async context manager: enter to bind + start accepting, exit to
shut down. self.url is set after enter and gives the URL a
real h2c client can dial.
Parameters¶
scenario:
The :class:ScenarioH2 the executor walks for each accepted
connection.
host:
Bind host. Defaults to '127.0.0.1' — binding to a
non-localhost interface is rejected unless allow_remote
is set (the misbehaviour mode is for local tests only).
port:
Bind port. Defaults to 0 (kernel-assigned random port).
allow_remote:
Bypass the localhost-only safety check. Off by default.
ssl_context:
Optional :class:ssl.SSLContext. When provided, the server
terminates TLS and self.url is https://...; ALPN must
offer h2 so clients negotiating H/2 over TLS (httpx, curl
--http2, …) connect cleanly. When None (default), the
server speaks plaintext h2c — usable with prior-knowledge
clients only.
Attributes¶
url:
http://<host>:<port>/ after start, or https://... when
a TLS context is provided.
last_result:
:class:ScenarioH2Result from the most recently completed
connection. None before any client has connected.
wait_for_connection_done(timeout=5.0)
async
¶
Block until a client has connected and the scenario finished.
H2FaultServerError
¶
Bases: RuntimeError
Raised when H2FaultServer cannot start.
The most common cause is BB_PRODUCTION being set in the
environment: deliberate-misbehaviour machinery refuses to run in
a production context regardless of how it was reached.
serialize_frame(frame)
¶
Convert a :class:FrameBase instance to wire bytes.
Restricted to the frame types this server emits: SETTINGS,
WINDOW_UPDATE, RST_STREAM, GOAWAY, PING, DATA. Anything else
must go through :class:SendRawBytes instead.