Skip to main content

Module http

Module http 

Source
Expand description

A minimal blocking HTTP/1.1 client over any Read + Write.

This is the single highest-leverage minimalism decision: one ~250-line module replaces the ureq/url→IDNA→ICU dependency tax. It carries the intelligence wire over TCP, TLS, unix sockets, and vsock alike — the transport is just the stream.

Two request paths: send buffers the whole response (the LLM/intelligence path), and send_streaming returns the status + headers plus a live reader so the caller can either buffer it (application/json) or pump it as an SSE stream (SseReader) — the MCP Streamable HTTP transport, where a response may be a single JSON body or a text/event-stream, and a long-lived GET carries server→client notifications. connect_tcp is intentionally unguarded, and dials by name: it exists for the operator-configured endpoints (the intelligence dial, the auth/token endpoints), which the model cannot influence. A model/agent/peer-supplied URL must NOT be dialled through it — not even with a guard wrapped around it, since the guard’s lookup and this function’s lookup are two resolutions and a hostile nameserver can answer them differently. Those surfaces use crate::ssrf::connect_vetted, which resolves once and dials the address it vetted.

Structs§

Response
A parsed HTTP response.
SseEvent
One parsed text/event-stream event. For MCP, data is a JSON-RPC message; event/id are the optional SSE field lines.
SseReader
A blocking, line-based SSE reader. next_event accumulates field: value lines and emits one SseEvent per blank-line separator (multiple data: lines join with \n), returning Ok(None) at end of stream. Bounded per event by MAX_RESPONSE so a hostile stream cannot exhaust memory.
StreamingResponse
A streamed response: status + headers, plus the reader positioned at the body. The caller decides how to drain it — [into_body] to buffer (application/json) or [sse] to pump it as an SSE event stream (text/event-stream). Owns the underlying stream, matching the MCP client’s per-request connection model.
Url
A parsed absolute URL (the subset we need: scheme/host/port/path).

Constants§

MAX_RESPONSE
Response body cap. LLM responses can be large; 8 MiB is generous without being an unbounded allocation from a hostile peer.

Traits§

Stream
Any bidirectional byte stream the HTTP client can run over. Box<dyn Stream> is itself Read + Write (via std’s impl<R: Read + ?Sized> Read for Box<R>), so an OWNED boxed stream can be handed to send_streaming by value — used by the long-lived MCP notification SSE reader.

Functions§

connect_tcp
Connect a plain TCP stream with connect + read/write timeouts. Intentionally unguarded, and resolves the name itself — for operator-configured endpoints only. A model/agent/peer-supplied URL belongs on crate::ssrf::connect_vetted: composing ssrf::guard_host around this function resolves twice and is the DNS-rebinding hole, not a fix for it.
is_loopback_host
Whether host names the local loopback — the dev/test carve-out for plaintext http:// (production transports are TLS-only). Accepts the IPv4 loopback block (127.0.0.0/8), the IPv6 loopback (::1, bare or bracketed), and the literal name localhost. A resolvable-but-unresolved name is NOT loopback — this classifies the written form, without DNS.
send
Issue one request over stream and read the full response. Adds Host, Connection: close, and Content-Length; the caller supplies any other headers (e.g. Authorization, Content-Type).
send_streaming
Issue one request over an OWNED stream and return the status + headers + body reader WITHOUT draining the body (unlike send). Adds Host, Connection: close, and Content-Length; the caller supplies the rest (Accept, Authorization, Content-Type, Mcp-Session-Id, …).