Module std.net.http.codec.request
Parse buffered HTTP/1.1 requests and build server responses over std.net.
Call conn.recv() directly; pending reads can suspend the caller.
Use fork for concurrent work and await for the resulting task.
The codec buffers bodies and uses one connection per request; HTTPS and
connection pooling are not provided here.
Example
import std.net;
import std.net.http.codec;
import std.encoding.utf8;
actor Server {
let addr: string; // "127.0.0.1:8080"
receive fn serve(unused: i64) {
let listener = match net.listen(addr) { .Ok(value) => value, .Err(error) => panic("network operation failed"), };
let conn = listener.accept().expect("accept"); // suspends on readiness
var raw = "";
var reads = 0;
var done = false;
while !done {
let chunk = match conn.recv() {
.Some(data) => utf8.decode(data) handle error {
println(f"invalid UTF-8: {error}");
return;
},
.None => "",
}; // suspends the worker
reads = reads + 1;
if (chunk).is_empty() {
done = true;
} else {
raw = raw + chunk;
if codec.over_limit(raw) { // F2: fail closed
done = true;
} else if codec.request_complete(raw) {
done = true;
}
}
if reads > codec.MAX_READS { // F2: read ceiling
done = true;
}
}
let reply = match codec.parse_request(raw) {
.Ok(.Some(req)) => match req.path() {
"/hello" => codec.response_text(200, "hi from hew"),
_ => codec.response_text(404, "nope"),
}
.Ok(.None) => codec.response_text(400, "incomplete request"),
.Err(error) => codec.response_text(error.status(), "rejected"),
}.expect("valid response fields");
conn.send(reply.to_bytes()) handle error {
println(f"response send failed: {error}");
return;
};
let _ = conn.close();
let _ = listener.close();
}
}
Contents
- fn request_complete
- fn over_limit
- fn parse_request
- fn response_text
- fn response_json
- fn build_response
- struct Request
- enum RequestError
- const MAX_HEADER_BYTES
- const MAX_BODY_BYTES
- const MAX_REQUEST_BYTES
- const MAX_READS
- inherent methods
Functions
Function request_complete
pub fn request_complete(raw: string) -> bool
Whether raw holds a complete HTTP/1.1 request: the header terminator
(CRLF-CRLF) is present AND, when a Content-Length header is declared, the
buffered body is at least that long. Drives the acceptor's read loop so it
stops reading as soon as the request is fully buffered (rather than waiting
for the peer to close).
Function over_limit
pub fn over_limit(raw: string) -> bool
Whether raw has exceeded a size bound and the read loop must STOP and fail
closed (F2). True when the buffered bytes exceed the total ceiling, the head
exceeds the header ceiling (even before a terminator is seen), or the declared
Content-Length exceeds the body ceiling. The acceptor's read loop checks
this before request_complete so a malicious peer
cannot exhaust the worker's buffer by
streaming an unbounded head/body or declaring an enormous Content-Length.
Function parse_request
pub fn parse_request(raw: string) -> Option<Request> fails RequestError
Parse a buffered request. None means more header or declared body bytes
are needed. Malformed framing and size limits return a checked error;
RequestError.status() supplies the rejection status for the response.
Function response_text
pub fn response_text(status: i64, body: string) -> string fails BuildError
Build a plain-text HTTP/1.1 response (Content-Type: text/plain).
Function response_json
pub fn response_json(status: i64, json: string) -> string fails BuildError
Build a JSON HTTP/1.1 response (Content-Type: application/json).
Function build_response
pub fn build_response(status: i64, content_type: string, body: string) -> string fails BuildError
Build a full HTTP/1.1 response: the status line, Content-Type,
Content-Length (derived from body), Connection: close, the blank line,
then the body. The status text is a small well-known mapping (anything
unknown is labelled Status).
FAILS CLOSED on header smuggling (F4): a content_type carrying a \r/\n
or other control character would inject extra headers into the response, so
such a value returns a checked error before response bytes are emitted.
Types
Struct Request
A fully-buffered HTTP/1.1 request parsed from the bytes read off an accepted
connection. verb is the method (GET/POST/…), target the request path,
header_section the raw header block (CRLF-separated lines after the request
line), and body_text the request body. Parsing returns only validated requests.
Fields
Methods
fn method(self) -> string
The HTTP method (e.g. "GET", "POST").
fn path(self) -> string
The request path (e.g. "/api/users").
fn header(self, name: string) -> Option<string>
Look up a request header by name (case-insensitive), or None if absent.
fn body(self) -> string
The request body as a string.
Enum RequestError
A malformed or oversized buffered HTTP request.
Variants
Malformed(string)TooLarge
Inherent methods
impl RequestError
pub fn status(self) -> i64
The HTTP rejection status appropriate for this parse failure.
Constants
Constant MAX_HEADER_BYTES
pub const MAX_HEADER_BYTES: i64 = 65536
Maximum bytes accepted in the request head (request line + headers). A head
larger than this fails closed with 413 rather than buffering unbounded.
Constant MAX_BODY_BYTES
pub const MAX_BODY_BYTES: i64 = 8388608
Maximum bytes accepted in the request body — also the Content-Length
ceiling. A declared or buffered body larger than this fails closed with 413.
Constant MAX_REQUEST_BYTES
pub const MAX_REQUEST_BYTES: i64 = 8454144
Maximum total request bytes buffered before the read loop fails closed
(MAX_HEADER_BYTES + MAX_BODY_BYTES). The single overall cap that bounds the
accumulator regardless of framing.
Constant MAX_READS
pub const MAX_READS: i64 = 4096
Maximum number of read iterations a request loop will accept before failing closed. Bounds an endless-trickle peer that never satisfies a framing rule.