blackbull.client.http1¶
blackbull.client.http1
¶
HTTP/1.1 client (RFC 7230).
Provides HTTP1Client plus the lower-level HTTP1RequestSender /
HTTP1ResponseRecipient helpers that frame and unframe HTTP/1.1 messages
on the wire.
Symmetric with the server-side HTTP1Sender / HTTP1Recipient in
:mod:blackbull.server.sender and :mod:blackbull.server.recipient,
but reversed: the client writes request lines + request headers + request
body, and reads status lines + response headers + response body.
HTTP1Client
¶
Async HTTP/1.1 client.
Use as an async context manager::
async with HTTP1Client('localhost', 8000) as c:
res = await c.request(HTTPMethod.GET, '/path')
The connection persists across multiple request() calls (HTTP/1.1
persistent connections, RFC 7230 §6.3) until __aexit__ closes it.
Pass ssl= to use TLS.
The Host header is injected automatically when the caller omits it.
wire_buffer
property
¶
Bytes sent so far by the low-level primitives in this session.
Empty unless the client was constructed with
record_wire_bytes=True. Reset with :meth:reset_wire_buffer.
end_chunked()
async
¶
Emit the size-0 terminator chunk that closes a chunked body.
end_headers()
async
¶
Emit the bare CRLF that terminates the header block.
execute_scenario(scenario)
async
¶
Walk scenario.steps against the connected socket.
Never raises. Every outcome (response, timeout, transport
failure, hard-abort) is folded into the returned
:class:ScenarioResult so callers can categorise without
try/except boilerplate per scenario.
Step dispatch
- :class:
SendBytes→ :meth:send_raw - :class:
Sleep→ :func:asyncio.sleep - :class:
ReadResponse→ :meth:read_response - :class:
Abort→transport.abort()(RST on Linux); walks no further steps.
read_response(*, timeout=None)
async
¶
Read one HTTP/1.1 response from the connection.
Optional timeout bounds the entire read (status line + headers
+ body). Raises :class:asyncio.TimeoutError if the deadline is
hit; the caller decides whether to treat that as a transport-
fail or a normal protocol outcome.
reset_wire_buffer()
¶
Discard previously captured wire bytes.
send_body_bytes(data, *, byte_interval=0.0)
async
¶
Send body octets to the peer.
Same semantics as :meth:send_raw, kept separate for readability
at call sites that frame headers separately from the body.
send_chunk(data)
async
¶
Send one Transfer-Encoding: chunked chunk.
Caller must have already emitted Transfer-Encoding: chunked
via :meth:send_header_line and called :meth:end_headers.
Finish the chunked stream with :meth:end_chunked.
send_header_line(name, value)
async
¶
Emit one Name: Value\r\n header line with no dedup or
validation. Callers wanting a duplicate Content-Length or a
header value containing arbitrary bytes use this primitive
directly.
send_raw(data, *, byte_interval=0.0)
async
¶
Push arbitrary bytes onto the underlying socket.
When byte_interval > 0 the bytes are transmitted one at a time
with byte_interval seconds between writes — the primitive
slowloris-style stall the differential tests rely on. Each per-
byte write is followed by drain() (inherited from
:class:AsyncioWriter), so the bytes actually leave the socket
on schedule rather than accumulating in the asyncio send buffer.
send_request_line(method, target, *, version=b'HTTP/1.1')
async
¶
Emit METHOD<SP>TARGET<SP>HTTP/1.1\r\n with no validation.
Accepts arbitrary bytes for method/target/version so a
test can deliberately send b"BREW", lowercase versions, or
garbage tokens. No automatic Host or Content-Length injection —
the caller drives the wire bit by bit.
stream(method, path, *, headers=(), body=b'')
async
¶
Send a request and yield body chunks lazily.
Unlike request() this does not buffer the response body, so
gigabyte-sized responses do not need to fit in memory. Status and
headers are not exposed by this method; use request() if you
need them.
HTTP1RequestSender
¶
Writes an HTTP/1.1 request — request line, headers, body — to an AbstractWriter.
Adds Content-Length automatically for fixed-size byte bodies; switches
to Transfer-Encoding: chunked for AsyncIterable bodies. The
Host header MUST be present (RFC 7230 §5.4) — the helper raises
ProtocolError if it is not.
HTTP1ResponseRecipient
¶
Reads an HTTP/1.1 response from an AbstractReader.
Decodes both Content-Length-bound and Transfer-Encoding: chunked
bodies. Returns a ClientResponse; stream() returns an async
iterator of body chunks instead, so large responses don't have to fit
in memory.