Skip to content

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.