net::http
Enough HTTP to stand up a handler-based server and make client requests with no external dependencies. HTTP/1.1 with keep-alive lives in net::http; HTTP/2 and WebSocket are the sibling modules net::http2 and net::ws.
Everything here builds on the transports in net, so the same code runs over plaintext TCP or TLS.
| Item | Import |
|---|---|
Method | import std::net::http::method; |
StatusCode | import std::net::http::status; |
Headers | import std::net::http::headers; |
Request | import std::net::http::request; |
Response | import std::net::http::response; |
Router, RouteKind | import std::net::http::router; |
HttpServer | import std::net::http::server; |
Client, send | import std::net::http::client; |
Http2Client, Http2Server | import std::net::http2; |
WebSocket, Message | import std::net::ws; |
A server
import std::net::http;
import std::future;
function hello(req: Request) -> Response {
return Response::text(StatusCode::ok(), Str::new("hi"));
}
function main() -> i32 {
mut router: Router = Router::new();
router.get(Str::new("/"), hello);
router.get(Str::new("/users/:id"), show_user);
mut server: HttpServer = HttpServer::with_router(addr, &router);
mut ex: Executor = Executor::new();
ex.block_on(server.run());
return 0;
}
run() is an async method and needs an Executor — its reactor is what the accept and the per-connection reads register with. Connections are still served one at a time: this layer is on the async transport, not yet concurrent across connections.
Two entry points. HttpServer::new(addr, handler) takes a raw function-pointer handler, with all routing and method checking inside it. HttpServer::with_router(addr, &router) delegates to a Router.
The router must outlive the server — declare both in the same scope. The server holds a pointer, not ownership.
with_ready(cb) attaches a callback invoked once after the socket binds and before the first accept: log the listening address, write a pid-file, signal a health check. with_read_timeout(secs) sets the per-request read deadline; zero disables it. Both return the server for chaining. run_on(listener) runs against a listener you already have.
Router
Register routes with the per-method helpers, then hand a pointer to the server. Routes are evaluated in registration order; the first route where both method and path match wins.
/ exact root
/users exact match
/users/:id `:id` captures any non-empty segment
/files/:dir/:name two named captures
get, post, put, del, patch, head, and options each take a pattern and a (Request) -> Response handler; route(method, pattern, handler) is the generic form. Each also has an overload taking a RouteKind, which is how you register a prefix route — one that matches as long as the pattern is a prefix of the request path, accepting trailing segments.
on_not_found(handler) and on_method_not_allowed(handler) override the fallbacks. dispatch(req) is what the server calls.
Captured segments come back through Request::param:
function show_user(req: Request) -> Response {
match (req.param(Str::new("id"))) {
Option::Some(id) => { return Response::text(StatusCode::ok(), id.as_str()); }
Option::None => { return Response::new(StatusCode::bad_request()); }
}
}
Method
An enum over the standard verbs, with as_str(), static parse(source: Str) -> Result<Method, ConversionError>, and parse_opt.
StatusCode
A wrapper over the numeric code with named constructors across the ranges — ok, created, accepted, no_content, moved_permanently, found, not_modified, temporary_redirect, permanent_redirect, bad_request, unauthorized, forbidden, not_found, method_not_allowed, conflict, length_required, payload_too_large, unsupported_media_type, too_many_requests, internal_server_error, not_implemented, bad_gateway, service_unavailable, gateway_timeout, plus continue_ and switching_protocols.
The class predicates are is_informational, is_success, is_redirection, is_client_error, and is_server_error. reason_phrase() gives the standard text.
Headers
A case-insensitive map. new(), length(), is_empty(), insert(name, value), get(name) -> Option<String>, contains(name), remove(name) -> boolean, and encode_into(out) to serialize every header as name: value\r\n.
content_length() returns Result<Option<u64>, IoError> — absent is Ok(None), malformed is an error.
Request and Response
Request::new(method, path) builds one; with_header(name, value) and with_body(body) chain, and set_body(body) mutates in place. param(name) reads a router capture. encode_into(out) writes the complete HTTP/1.1 message.
Response::new(status) is the bare form; Response::text(status, body: Str) and Response::json(status, body: Array<u8>) set the body and the matching content type. set_body and encode_into mirror the request side.
Both parse incoming messages through from_request_line / from_status_line plus the header and body reads the server and client drive.
MAX_HEADER_LINE and MAX_BODY bound what will be accepted.
Client
import std::net::http::client;
mut c: Client = Client::new();
const resp = ex.block_on(c.get(addr, Str::new("/health")));
Client::new(), then the async methods get(addr, path) and post(addr, path, body, content_type). The free async function send(addr: SocketAddr, req: Request) -> Result<Response, IoError> sends a request you built yourself.
The Host header is filled in from the address automatically.
For HTTPS, net::https wraps the same machinery with TLS underneath.
HTTP/2
net::http2 is a binary framing layer multiplexing many request/response streams over one connection, with HPACK header compression (RFC 7541) over the RFC 7540 framing.
Http2Connection<S> runs over any Read + Write transport, so the same code speaks h2c — prior-knowledge cleartext over TcpStream — and h2 over a TlsStream once ALPN has negotiated h2.
Http2Client::new() with get(addr, authority, path) and post(addr, authority, path, body, content_type) drives requests. Http2Server::bind(addr) with serve_one(handler) or run(handler) accepts a connection and dispatches each stream to a (Request) -> Response handler.
WebSocket
net::ws speaks RFC 6455 over any Read + Write transport, so the same code handles ws:// over TCP and wss:// over TLS. It is non-blocking throughout, over one buffered connection: a WebSocket<S> owns a BufStream<S>.
import std::net::ws;
mut ws: WebSocket<TcpStream> = WebSocket<TcpStream>::of(conn, true);
await ws.client_handshake(host, path);
await ws.send_text(Str::new("hello"));
match (await ws.recv()) {
Result::Ok(msg) => { /* Message::Text / Binary / Ping / Pong / Close */ }
Result::Err(e) => { }
}
static of(conn, is_client), then client_handshake(host, path) or server_handshake(). The handshake is an HTTP/1.1 Upgrade.
The message surface is send_text(text: Str), send_binary(data: Slice<u8>), ping(data), close(code: u16, reason: Str), and recv() -> Result<Message, IoError> — all async. Framing covers text and binary messages, fragmentation reassembly, and the Ping/Pong/Close control frames. is_closed() and connection() round it out.
SHA-1 and base64 for the accept key are pure Cryo from encoding, so plain ws:// pulls in no crypto dependency at all.