Skip to content

blackbull.fault_injection.h1_server

blackbull.fault_injection.h1_server

A deliberately misbehaving HTTP/1.1 server.

The HTTP/1.1 half of the fault-injection grid's server direction — the cell the toolkit did not have. Point your own HTTP/1.1 client at it and assert that the client survives what a real server can do wrong::

scenario = ScenarioH1Server(steps=[
    WaitForRequest(),
    SendRawBytes(b'HTTP/1.1 200 OK\r\nContent-Length: 100\r\n\r\nshort'),
    CloseGracefully(),
])
async with H1FaultServer(scenario) as srv:
    with pytest.raises(...):
        await my_client.get(f'http://{srv.host}:{srv.port}/')

It assembles every byte itself. Nothing here imports blackbull.server.sender or blackbull.server.response, and that is load-bearing rather than incidental: a fault server built on the production send path cannot emit a fault the production send path has, so the one bug class it is least able to find is the one in the code it shares. The HTTP/2 half made the same choice — it carries its own frame encoder rather than calling FrameBase.save(). There is a test for it (tests/unit/test_fault_injection_h1_server.py::TestTheBreakerIsIndependent) because the property is invisible until the day it matters.

Two safety locks, the same pair :class:H2FaultServer carries and both in scope for security reports per SECURITY.md: it refuses to start in a production context, and it refuses a non-loopback bind without an explicit allow_remote=True.

H1FaultServer

Serves one :class:ScenarioH1Server to each connecting client.

stop() async

Stop accepting and cancel anything still mid-scenario.

The cancellation is the part that matters: a scenario whose last step is a long Sleep is the normal shape here — that is how "the server never answers" is expressed — so a teardown that waited for handlers would wait out the sleep on every such case.

wait_for_connection_done(timeout=5.0) async

Block until a client has connected and the scenario finished.

H1FaultServerError

Bases: RuntimeError

Raised when the fault server refuses to run.