Module http/http
HTTP core types — HttpMethod, StatusCode, HeaderMap, HttpRequest,
HttpResponse, and the two parsers that turn wire bytes into them.
Pure values and pure text: nothing in this file does I/O or blocks, so it
is equally usable in a client, in a server, and in a test. The parsers
return Result(_, HttpParseError) rather than throwing (D13), which is
what lets HttpServer.serve answer a malformed request with a 400 and
keep serving instead of unwinding out of its accept loop.
Two shapes are worth knowing before reading further, because both differ
from the obvious data structure: a header field name compares
case-insensitively but is STORED as written (that is what goes on the
wire), and a field may legitimately REPEAT with the order significant
(Set-Cookie always does) — so headers are an insertion-ordered
multimap, not a HashMap(String, String). And a status is a u16
newtype with 22 named constructors rather than an enum, because the
registered set is OPEN: a peer may send a code this list does not carry,
and an enum would have to reject or box it.
Stability
unstable — the type surface was reworked on 2026-09-09 (StatusCode
replacing a bare status_code : i32, HeaderMap replacing
ArrayList(HttpHeader)) and gained HttpResponse.version on 2026-09-10
for connection reuse, so it is inside its one-release window. One
decided-convention violation is still standing: HttpRequest/
HttpResponse carry get_header/set_header beside the HeaderMap's
own Rust-shaped get/insert/append, which is two spellings for one
operation and the get_* prefix D2 rules out — the plan lists
get_header among its D2 violations. Removing the pair is breaking, so
it waits for a window; the multimap and StatusCode themselves are
expected to stay.
Types
HTTP client error variants.
Variants
| Variant | Fields | Description |
|---|---|---|
ConnectionFailed | msg: String | Failed to connect to the remote host. |
InvalidUrl | msg: String | The URL could not be parsed. |
Timeout | The whole request (every connect, write, read and redirect hop) did not
finish within | |
TooManyRedirects | The server kept redirecting past | |
UnsupportedScheme | scheme: String | The URL scheme is neither |
ResponseTooLarge | The raw response (status line, headers and body) grew past
| |
MalformedChunkedBody | msg: String | A |
MalformedContentLength | msg: String | A |
Other | msg: String | An unclassified HTTP error. |
Trait Implementations
impl(generic(T : Type), where(T <: ToString), T : (ToString))
impl(generic(T : Type), where(T <: ToString), T : (ToString), Format)
format : fn(self : Self, spec : str) -> StringRender self under spec. An unrecognised spec degrades to the plain
to_string() rendering rather than failing.
Parameters
| Name | Type | Notes |
|---|---|---|
self | Self | |
spec | str |
Returns: String
Methods
to_string : (HttpError) fn(self : HttpError) -> Stringsource : (HttpError) fn(self : HttpError) -> Option(dyn( + ToString))The error that caused this one, or .None at the root of the chain.
Rust's Error::source. Defaulted to .None, so an error with nothing
underneath it implements the trait by saying only what it is; a wrapper
overrides it to hand back what it wrapped. Walking the chain to the root
cause is not expressible yet — the returned Dyn loses the Error
trait on an erased receiver, so a caller can print one link but cannot
follow it (#521,
issues/self-trait-in-a-return-type-loses-the-trait-on-an-erased-receiver.md).
Parameters
| Name | Type | Notes |
|---|---|---|
self | HttpError |
Standard HTTP request methods.
Variants
| Variant | Fields | Description |
|---|---|---|
GET | ||
POST | ||
PUT | ||
DELETE | ||
HEAD | ||
PATCH | ||
OPTIONS |
Trait Implementations
impl(generic(T : Type), where(T <: ToString), T : (ToString))
impl(generic(T : Type), where(T <: ToString), T : (ToString), Format)
format : fn(self : Self, spec : str) -> StringRender self under spec. An unrecognised spec degrades to the plain
to_string() rendering rather than failing.
Parameters
| Name | Type | Notes |
|---|---|---|
self | Self | |
spec | str |
Returns: String
impl(HttpMethod, ToString(...))
to_string : (
self ->
match(
self,
.GET => `GET`,
.POST => `POST`,
.PUT => `PUT`,
.DELETE => `DELETE`,
.HEAD => `HEAD`,
.PATCH => `PATCH`,
.OPTIONS => `OPTIONS`
)
)impl(HttpMethod, ...)
from_string : (HttpMethod) fn(s : String) -> Option(HttpMethod)Parse a request-line method token (case-sensitive, as RFC 9110 §9.1
requires). .None for anything not in the enum.
Parameters
| Name | Type | Notes |
|---|---|---|
s | String |
Returns: Option(HttpMethod)
Methods
to_string : (HttpMethod) fn(self : HttpMethod) -> StringSerialize to wire format, on the status line's own version (so a
parsed response round-trips). A Content-Length header is added
when none is present (the byte length of body), so the peer can
delimit the message without waiting for close.
Parameters
| Name | Type | Notes |
|---|---|---|
self | HttpMethod |
Returns: String
An HTTP header as a name-value pair.
Fields
| Name | Type | Description |
|---|---|---|
name | String | |
value | String |
impl(HttpHeader, ...)
new : (HttpHeader) fn(name : String, value : String) -> HttpHeaderCreate a response with the given status and reason phrase.
Parameters
| Name | Type | Notes |
|---|---|---|
name | String | |
value | String |
Returns: HttpHeader
A header section — Rust's http::HeaderMap.
Two properties HTTP requires and a HashMap(String, String) cannot give:
- field names are case-insensitive (RFC 9110 §5.1), so
Content-Typeandcontent-typeare the same field. Comparison folds ASCII case; the name is STORED as written, because that is what goes on the wire. - a field may appear more than once (
Set-Cookiealways does), and the order of the repeated values is significant. So this is a multimap that preserves insertion order, backed by a list rather than a hash table.
insert replaces every existing value for a name, append adds one — the
same split Rust draws, and the reason set_header on a request used to
silently accumulate duplicates.
Fields
| Name | Type | Description |
|---|---|---|
_entries | ArrayList(HttpHeader) |
Trait Implementations
impl(generic(T : Type), where(T <: ToString), T : (ToString))
impl(generic(T : Type), where(T <: ToString), T : (ToString), Format)
format : fn(self : Self, spec : str) -> StringRender self under spec. An unrecognised spec degrades to the plain
to_string() rendering rather than failing.
Parameters
| Name | Type | Notes |
|---|---|---|
self | Self | |
spec | str |
Returns: String
impl(HeaderMap, ...)
new : (HeaderMap) fn() -> HeaderMapCreate a response with the given status and reason phrase.
Returns: HeaderMap
len : (HeaderMap) fn(self : HeaderMap) -> usizeNumber of field LINES, counting each repeat of a name separately.
Parameters
| Name | Type | Notes |
|---|---|---|
self | HeaderMap |
Returns: usize
is_empty : (HeaderMap) fn(self : HeaderMap) -> boolget : (HeaderMap) fn(self : HeaderMap, name : String) -> Option(String)get_all : (HeaderMap) fn(self : HeaderMap, name : String) -> ArrayList(String)contains_key : (HeaderMap) fn(self : HeaderMap, name : String) -> boolappend : (HeaderMap) fn(self : HeaderMap, name : String, value : String) -> unitinsert : (HeaderMap) fn(self : HeaderMap, name : String, value : String) -> Option(String)remove : (HeaderMap) fn(self : HeaderMap, name : String) -> usizeentries : (HeaderMap) fn(self : HeaderMap) -> ArrayList(HttpHeader)The field lines in order — for serialization and iteration. The list is a snapshot: pushing to it does not change the map.
Parameters
| Name | Type | Notes |
|---|---|---|
self | HeaderMap |
Returns: ArrayList(HttpHeader)
impl(HeaderMap, ToString(...))
to_string : (HeaderMap) fn(self : HeaderMap) -> StringAn HTTP status code — Rust's http::StatusCode.
A u16 newtype rather than a bare integer, so the CLASS predicates
(is_success, is_client_error, ...) live with the value instead of being
re-derived at every call site, and so a status cannot be confused with a
port, a length or a byte count. u16 because RFC 9110 SS15 fixes the range
at three digits: a status is never negative, which an i32 allowed.
Fields
| Name | Type | Description |
|---|---|---|
code | u16 |
Trait Implementations
impl(generic(T : Type), where(T <: ToString), T : (ToString))
impl(generic(T : Type), where(T <: ToString), T : (ToString), Format)
format : fn(self : Self, spec : str) -> StringRender self under spec. An unrecognised spec degrades to the plain
to_string() rendering rather than failing.
Parameters
| Name | Type | Notes |
|---|---|---|
self | Self | |
spec | str |
Returns: String
impl(StatusCode, ...)
from_u16 : (StatusCode) fn(code : u16) -> Option(StatusCode)Wrap a raw code, or .None when it is outside the 100-599 range RFC 9110
SS15 defines. An unrecognized code INSIDE the range is accepted — a client
must treat an unknown 4xx as 400, so refusing it would break the very
forward-compatibility the classes exist for.
Parameters
| Name | Type | Notes |
|---|---|---|
code | u16 |
Returns: Option(StatusCode)
as_u16 : (StatusCode) fn(self : StatusCode) -> u16is_informational : (StatusCode) fn(self : StatusCode) -> bool1xx — the request was received and the process continues.
Parameters
| Name | Type | Notes |
|---|---|---|
self | StatusCode |
Returns: bool
is_success : (StatusCode) fn(self : StatusCode) -> boolis_redirection : (StatusCode) fn(self : StatusCode) -> bool3xx — further action is needed to complete the request.
Parameters
| Name | Type | Notes |
|---|---|---|
self | StatusCode |
Returns: bool
is_client_error : (StatusCode) fn(self : StatusCode) -> bool4xx — the request was malformed or cannot be fulfilled.
Parameters
| Name | Type | Notes |
|---|---|---|
self | StatusCode |
Returns: bool
is_server_error : (StatusCode) fn(self : StatusCode) -> bool5xx — the server failed to fulfil an apparently valid request.
Parameters
| Name | Type | Notes |
|---|---|---|
self | StatusCode |
Returns: bool
is_error : (StatusCode) fn(self : StatusCode) -> boolreason : (StatusCode) fn(self : StatusCode) -> StringThe reason phrase RFC 9110 registers for this code, or Unknown.
The phrase is ADVISORY: a server may send any text and a client must not
act on it — which is why this is a lookup here rather than a field, and
why HttpResponse keeps whatever phrase the peer actually sent.
Parameters
| Name | Type | Notes |
|---|---|---|
self | StatusCode |
Returns: String
impl(StatusCode, ToString(...))
to_string : (StatusCode) fn(self : StatusCode) -> StringSerialize to wire format, on the status line's own version (so a
parsed response round-trips). A Content-Length header is added
when none is present (the byte length of body), so the peer can
delimit the message without waiting for close.
Parameters
| Name | Type | Notes |
|---|---|---|
self | StatusCode |
Returns: String
impl(StatusCode, Eq(StatusCode)(...))
impl(StatusCode, Ord(StatusCode)(...))
impl(StatusCode, ...)
OK : (StatusCode) fn() -> StatusCodeReturns: StatusCode
CREATED : (StatusCode) fn() -> StatusCodeReturns: StatusCode
ACCEPTED : (StatusCode) fn() -> StatusCodeReturns: StatusCode
NO_CONTENT : (StatusCode) fn() -> StatusCodeReturns: StatusCode
MOVED_PERMANENTLY : (StatusCode) fn() -> StatusCodeReturns: StatusCode
FOUND : (StatusCode) fn() -> StatusCodeReturns: StatusCode
NOT_MODIFIED : (StatusCode) fn() -> StatusCodeReturns: StatusCode
TEMPORARY_REDIRECT : (StatusCode) fn() -> StatusCodeReturns: StatusCode
PERMANENT_REDIRECT : (StatusCode) fn() -> StatusCodeReturns: StatusCode
BAD_REQUEST : (StatusCode) fn() -> StatusCodeReturns: StatusCode
UNAUTHORIZED : (StatusCode) fn() -> StatusCodeReturns: StatusCode
FORBIDDEN : (StatusCode) fn() -> StatusCodeReturns: StatusCode
NOT_FOUND : (StatusCode) fn() -> StatusCodeReturns: StatusCode
METHOD_NOT_ALLOWED : (StatusCode) fn() -> StatusCodeReturns: StatusCode
CONFLICT : (StatusCode) fn() -> StatusCodeReturns: StatusCode
CONTENT_TOO_LARGE : (StatusCode) fn() -> StatusCodeReturns: StatusCode
TOO_MANY_REQUESTS : (StatusCode) fn() -> StatusCodeReturns: StatusCode
INTERNAL_SERVER_ERROR : (StatusCode) fn() -> StatusCodeReturns: StatusCode
NOT_IMPLEMENTED : (StatusCode) fn() -> StatusCodeReturns: StatusCode
BAD_GATEWAY : (StatusCode) fn() -> StatusCodeReturns: StatusCode
SERVICE_UNAVAILABLE : (StatusCode) fn() -> StatusCodeReturns: StatusCode
GATEWAY_TIMEOUT : (StatusCode) fn() -> StatusCodeReturns: StatusCode
Methods
== : (StatusCode) fn(lhs : StatusCode, rhs : StatusCode) -> bool!= : (StatusCode) fn(lhs : StatusCode, rhs : StatusCode) -> bool< : (StatusCode) fn(lhs : StatusCode, rhs : StatusCode) -> bool<= : (StatusCode) fn(lhs : StatusCode, rhs : StatusCode) -> bool> : (StatusCode) fn(lhs : StatusCode, rhs : StatusCode) -> bool>= : (StatusCode) fn(lhs : StatusCode, rhs : StatusCode) -> boolcmp : (StatusCode) fn(lhs : StatusCode, rhs : StatusCode) -> OrderingAn HTTP request with method, path, headers, and optional body.
Fields
| Name | Type | Description |
|---|---|---|
method | HttpMethod | |
path | String | |
headers | HeaderMap | The response's header fields, in the order they arrived. A multimap:
use |
body | String | The body as received, byte-transparent — it is sliced out of the raw
message on the CRLFCRLF boundary and wrapped without UTF-8 validation,
so a binary body survives (a |
Trait Implementations
impl(generic(T : Type), where(T <: ToString), T : (ToString))
impl(generic(T : Type), where(T <: ToString), T : (ToString), Format)
format : fn(self : Self, spec : str) -> StringRender self under spec. An unrecognised spec degrades to the plain
to_string() rendering rather than failing.
Parameters
| Name | Type | Notes |
|---|---|---|
self | Self | |
spec | str |
Returns: String
impl(HttpRequest, ...)
new : (HttpRequest) fn(method : HttpMethod, path : String) -> HttpRequestCreate a response with the given status and reason phrase.
Parameters
| Name | Type | Notes |
|---|---|---|
method | HttpMethod | |
path | String |
Returns: HttpRequest
header : (HttpRequest) fn(self : HttpRequest, name : String, value : String) -> HttpRequestAdd a header and return the response (builder pattern). APPENDS, so a repeatable field can be built up by chaining.
Parameters
| Name | Type | Notes |
|---|---|---|
self | HttpRequest | |
name | String | |
value | String |
Returns: HttpRequest
with_body : (HttpRequest) fn(self : HttpRequest, body : String) -> HttpRequestSet the body and return the response (builder pattern).
Parameters
| Name | Type | Notes | Description |
|---|---|---|---|
self | HttpRequest | ||
body | String | The body as received, byte-transparent — it is sliced out of the raw
message on the CRLFCRLF boundary and wrapped without UTF-8 validation,
so a binary body survives (a |
Returns: HttpRequest
set_host : (HttpRequest) fn(self : HttpRequest, host : String) -> unitSet the Host header, REPLACING any existing one — there is exactly one Host per request (RFC 9112 SS3.2), and appending a second used to put both on the wire.
Parameters
| Name | Type | Notes |
|---|---|---|
self | HttpRequest | |
host | String |
Returns: unit
set_header : (HttpRequest) fn(self : HttpRequest, name : String, value : String) -> unitSet a header, REPLACING every existing value for that name.
Parameters
| Name | Type | Notes |
|---|---|---|
self | HttpRequest | |
name | String | |
value | String |
Returns: unit
set_body : (HttpRequest) fn(self : HttpRequest, body : String) -> unitSet the request body.
Parameters
| Name | Type | Notes | Description |
|---|---|---|---|
self | HttpRequest | ||
body | String | The body as received, byte-transparent — it is sliced out of the raw
message on the CRLFCRLF boundary and wrapped without UTF-8 validation,
so a binary body survives (a |
Returns: unit
get_header : (HttpRequest) fn(self : HttpRequest, name : String) -> Option(String)Look up a header value by name (case-insensitive). The first value, when
the field repeats — self.headers.get_all(name) for all of them, which
is what Set-Cookie needs.
Parameters
| Name | Type | Notes |
|---|---|---|
self | HttpRequest | |
name | String |
impl(HttpRequest, ToString(...))
to_string : (HttpRequest) fn(self : HttpRequest) -> StringSerialize to wire format, on the status line's own version (so a
parsed response round-trips). A Content-Length header is added
when none is present (the byte length of body), so the peer can
delimit the message without waiting for close.
Parameters
| Name | Type | Notes |
|---|---|---|
self | HttpRequest |
Returns: String
An HTTP response with status, headers, and body.
Fields
| Name | Type | Description |
|---|---|---|
status | StatusCode | The status. A |
status_text | String | The reason phrase as the peer sent it, which may be anything (RFC 9110
§15 makes it advisory). |
version | String | The version on the status line, as received — |
headers | HeaderMap | The response's header fields, in the order they arrived. A multimap:
use |
body | String | The body as received, byte-transparent — it is sliced out of the raw
message on the CRLFCRLF boundary and wrapped without UTF-8 validation,
so a binary body survives (a |
Trait Implementations
impl(generic(T : Type), where(T <: ToString), T : (ToString))
impl(generic(T : Type), where(T <: ToString), T : (ToString), Format)
format : fn(self : Self, spec : str) -> StringRender self under spec. An unrecognised spec degrades to the plain
to_string() rendering rather than failing.
Parameters
| Name | Type | Notes |
|---|---|---|
self | Self | |
spec | str |
Returns: String
impl(HttpResponse, ...)
new : (HttpResponse) fn(status : StatusCode, status_text : String) -> HttpResponseCreate a response with the given status and reason phrase.
Parameters
| Name | Type | Notes | Description |
|---|---|---|---|
status | StatusCode | The status. A | |
status_text | String | The reason phrase as the peer sent it, which may be anything (RFC 9110
§15 makes it advisory). |
Returns: HttpResponse
get_header : (HttpResponse) fn(self : HttpResponse, name : String) -> Option(String)Look up a header value by name (case-insensitive). The first value, when
the field repeats — self.headers.get_all(name) for all of them, which
is what Set-Cookie needs.
Parameters
| Name | Type | Notes |
|---|---|---|
self | HttpResponse | |
name | String |
set_header : (HttpResponse) fn(self : HttpResponse, name : String, value : String) -> unitSet a header, REPLACING every existing value for that name.
Parameters
| Name | Type | Notes |
|---|---|---|
self | HttpResponse | |
name | String | |
value | String |
Returns: unit
is_ok : (HttpResponse) fn(self : HttpResponse) -> boolis_redirect : (HttpResponse) fn(self : HttpResponse) -> boolis_error : (HttpResponse) fn(self : HttpResponse) -> boolwith_status : (HttpResponse) fn(status : StatusCode) -> HttpResponseA response with status and its registered reason phrase.
Parameters
| Name | Type | Notes | Description |
|---|---|---|---|
status | StatusCode | The status. A |
Returns: HttpResponse
header : (HttpResponse) fn(self : HttpResponse, name : String, value : String) -> HttpResponseAdd a header and return the response (builder pattern). APPENDS, so a repeatable field can be built up by chaining.
Parameters
| Name | Type | Notes |
|---|---|---|
self | HttpResponse | |
name | String | |
value | String |
Returns: HttpResponse
with_body : (HttpResponse) fn(self : HttpResponse, body : String) -> HttpResponseSet the body and return the response (builder pattern).
Parameters
| Name | Type | Notes | Description |
|---|---|---|---|
self | HttpResponse | ||
body | String | The body as received, byte-transparent — it is sliced out of the raw
message on the CRLFCRLF boundary and wrapped without UTF-8 validation,
so a binary body survives (a |
Returns: HttpResponse
impl(HttpResponse, ToString(...))
to_string : (HttpResponse) fn(self : HttpResponse) -> StringSerialize to wire format, on the status line's own version (so a
parsed response round-trips). A Content-Length header is added
when none is present (the byte length of body), so the peer can
delimit the message without waiting for close.
Parameters
| Name | Type | Notes |
|---|---|---|
self | HttpResponse |
Returns: String
Why a raw HTTP message did not parse — the error of parse_request and
parse_response, which are pure decoders and so return Result (D13).
Each variant carries the offending text; to_string renders the line a
server puts in its 400 body. Until 2026-09-06 the two functions returned
Result(_, String), the one std shape a caller could neither match on nor
distinguish from any other string
(issues/fixed/http-parse-errors-were-bare-strings.md).
Variants
| Variant | Fields | Description |
|---|---|---|
Empty | No start line at all. | |
InvalidStatusLine | line: String | A response whose first line is not |
InvalidStatusCode | text: String | A status code that is not an integer. |
InvalidRequestLine | line: String | A request line that is not exactly |
UnknownMethod | name: String | A request method outside |
UnsupportedVersion | version: String | A request version other than |
InvalidHeaderLine | line: String | A header line without a |
Trait Implementations
impl(generic(T : Type), where(T <: ToString), T : (ToString))
impl(generic(T : Type), where(T <: ToString), T : (ToString), Format)
format : fn(self : Self, spec : str) -> StringRender self under spec. An unrecognised spec degrades to the plain
to_string() rendering rather than failing.
Parameters
| Name | Type | Notes |
|---|---|---|
self | Self | |
spec | str |
Returns: String
Methods
to_string : (HttpParseError) fn(self : HttpParseError) -> StringSerialize to wire format, on the status line's own version (so a
parsed response round-trips). A Content-Length header is added
when none is present (the byte length of body), so the peer can
delimit the message without waiting for close.
Parameters
| Name | Type | Notes |
|---|---|---|
self | HttpParseError |
Returns: String
source : (HttpParseError) fn(self : HttpParseError) -> Option(dyn( + ToString))The error that caused this one, or .None at the root of the chain.
Rust's Error::source. Defaulted to .None, so an error with nothing
underneath it implements the trait by saying only what it is; a wrapper
overrides it to hand back what it wrapped. Walking the chain to the root
cause is not expressible yet — the returned Dyn loses the Error
trait on an erased receiver, so a caller can print one link but cannot
follow it (#521,
issues/self-trait-in-a-return-type-loses-the-trait-on-an-erased-receiver.md).
Parameters
| Name | Type | Notes |
|---|---|---|
self | HttpParseError |
Functions
Parse a raw HTTP response into an HttpResponse.
The head is split from the body on the \r\n\r\n boundary and the body is
sliced as BYTES — it may be binary, and it used to be reassembled by
splitting the WHOLE message on \r\n and re-joining the tail lines one
concatenation at a time: quadratic in the body size, and a substring over
a UTF-8 continuation byte panics.
Parameters
| Name | Type | Notes |
|---|---|---|
raw | String |
Returns: Result(HttpResponse, HttpParseError)
Parse a raw HTTP/1.1 request (request line, headers, body) into an
HttpRequest. The body is taken verbatim — read_http_message has
already decoded chunked framing when it read the message. Errors name the
defect (Invalid request line, unknown method, unsupported HTTP version).
Parameters
| Name | Type | Notes |
|---|---|---|
raw | String |
Returns: Result(HttpRequest, HttpParseError)