blackbull.client.http1¶
blackbull.client.http1
¶
HTTP/1.1 request framing and response reading (RFC 9110, RFC 9112).
Fate
¶
Bases: Enum
What a finished response leaves its connection fit for.
HTTP1Client
¶
Bases: _TransportOwner
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 when HTTP
version, Connection options, and body framing permit it. A successful
CONNECT or 101 can be transferred with handoff; after that, the
returned HTTP1UpgradeSession owns transport closure. 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 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
¶
Run connected-socket scenario steps and collect outcomes in ScenarioResult.
Peer and transport failures land on the result; a step outside the
vocabulary raises TypeError and an exited client
ConnectionError, both before any byte. Abort stops further
steps. The vocabulary is docs/guide/fault_injection.md.
handoff()
¶
Transfer a completed CONNECT/101 transport to a raw session.
The returned session preserves bytes already buffered by the HTTP reader and exposes both read and write sides. This operation is available exactly once. If the client context exits before handoff, the client closes the transport; after handoff, the session owns it.
read_response(*, request_method=None, offered_protocols=None, timeout=None)
async
¶
Read one HTTP/1.1 response from the connection.
Optional timeout bounds the entire read (status line + headers
+ body). Raises asyncio.TimeoutError if the deadline is
hit; the caller decides whether to treat that as a transport-
fail or a normal protocol outcome. Supply the protocols offered with
Connection: upgrade as offered_protocols to validate a 101
for handoff; without them, a 101 is returned but its
transport is closed.
reset_wire_buffer()
¶
Discard previously captured wire bytes.
send_body_bytes(data, *, byte_interval=0.0)
async
¶
Send body bytes with the same semantics as send_raw.
send_chunk(data)
async
¶
Send one Transfer-Encoding: chunked chunk.
Caller must have already emitted Transfer-Encoding: chunked
via send_header_line and called end_headers.
Finish the chunked stream with 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.
With byte_interval > 0 the bytes go out one at a time, that many
seconds apart — the primitive a slowloris-style stall is built from.
Each write is drained, so the pacing is on the wire and not 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.
Owns the request's framing: a byte body is sent under one
Content-Length, an AsyncIterable body under that length when the
caller declared one and under Transfer-Encoding: chunked when it did
not. The Host header MUST be present (RFC 7230 §5.4) — the helper
raises ProtocolError if it is not.
prepare(method, path, headers, body=b'')
classmethod
¶
Validate and render everything that can fail before wire I/O.
The high-level client claims response ownership only after this method succeeds. A caller error such as a mismatched Content-Length or an unencodable request target therefore cannot retire an untouched keep-alive connection.
Framing belongs to the sender, so the whole field pair is decided
here: a caller's Content-Length is checked against the body and
then written once, canonically. A caller's Transfer-Encoding is
honoured only where rewriting it cannot change what the body means —
the value chunked, the one framing this client writes — and
refused otherwise.
A body whose length is known goes out as that many raw octets, a
stream included, with the total checked as it goes; one whose length
is not knowable up front is chunk-framed instead.
The return value is internal: pass it to send_prepared unchanged.
HTTP1ResponseHeadReader
¶
Read bounded response heads without body or connection-reuse policy.
receive_head(reader, *, skip_interim=True)
async
¶
Read through bounded interim heads and return the final head.
RFC 9110 requires clients to parse one or more 1xx responses before a final response. 101 is returned immediately because the following bytes belong to the switched protocol. The count cap owns aggregate work while the existing head limits continue to own each individual head.
HTTP1ResponseRecipient
¶
Bases: HTTP1ResponseHeadReader
Reads an HTTP/1.1 response from an AbstractReader.
Decodes Content-Length-bound, Transfer-Encoding: chunked, and
connection-close-delimited bodies. Returns a ClientResponse;
stream() returns an async iterator of body chunks instead, so large
responses don't have to fit in memory — true of all body framings.
receive(reader, *, method=None, skip_interim=True)
async
¶
Read one final response, optionally exposing an interim response.
Production callers keep skip_interim=True and therefore pass over
100/102/103 responses until the final response. 101 is final by
protocol-switch semantics. The low-level fault-injection API passes
skip_interim=False so each peer message remains observable.
method= overrides the constructor's, for a caller that knows it
only at the read.
HTTP1UpgradeSession
¶
Bases: _TransportClose
Bidirectional transport after CONNECT or an HTTP 101 switch.
Obtain one with HTTP1Client.handoff. The handoff is one-shot:
once returned, this session owns transport closure and the originating
HTTP client can no longer read or write the connection.
__getattr__(name)
¶
Deprecated SendBytes alias for SendRawBytes (BBD005).