Skip to content
CryoCryo home
Stdlibnet::http

server

import std::net::http::server; · source

Types

HttpServer

type struct HttpServer {
    addr: SocketAddr;

    handler: Option<(Request) -> Response>;

    router: Router*;

    on_ready: Option<() -> void>;

    read_timeout_secs: i64;

    static new(addr: SocketAddr, handler: (Request) -> Response) -> HttpServer;
    static with_router(addr: SocketAddr, router: mut &Router) -> HttpServer;
    with_ready(mut &this, cb: () -> void) -> &HttpServer;
    with_read_timeout(mut &this, secs: i64) -> &HttpServer;
    async run(mut &this) -> Result<(), IoError>;
    async run_on(mut &this, listener: TcpListener) -> Result<(), IoError>;
    dispatch(&this, req: Request) -> Response;
    async serve_connection(mut &this, conn: BufStream<TcpStream>) -> void;
}
mut server: HttpServer = HttpServer::with_router(addr, &router);
mut ex: Executor = Executor::new();
ex.block_on(server.run());

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. A read that hits the cap is abandoned, and abandoning an in-flight read closes the connection — it is a hard close, not a recoverable error. Both return the server for chaining. run_on(listener) runs against a listener you already have, and serve_connection drives one already-accepted connection through keep-alive to close.

run() never completes normally, returning only on a fatal listener-level error such as EMFILE. Per-connection failures — parse errors, client hangups, write errors — are swallowed.

Properties

PropertyTypeNotes
addrSocketAddr
routerRouter*Non-null when built with with_router. Takes priority over handler. Owned by the caller; the Router must outlive this server.
read_timeout_secsi64Deadline applied to each request read (whole seconds). Zero disables; defaults to DEFAULT_READ_TIMEOUT_SECS.

Constructors

static new(addr: SocketAddr, handler: (Request) -> Response) -> HttpServer;

Build a server with a raw handler function. The handler receives every request and must produce a Response; all routing logic lives inside the handler.

static with_router(addr: SocketAddr, router: mut &Router) -> HttpServer;

Build a server backed by a Router. Pass &router; the router must outlive the server (declare both in the same scope).

Methods

handler: Option<(Request) -> Response>;

Set when built with new. None when using a Router.

on_ready: Option<() -> void>;
with_ready(mut &this, cb: () -> void) -> &HttpServer;

Attach a ready callback and return this for chaining. cb is called once after the socket binds successfully, before the first accept. Use it to log the listening address, write a pid-file, or signal an external health-check.

with_read_timeout(mut &this, secs: i64) -> &HttpServer;

Override the per-connection read timeout. secs == 0 disables; the default is DEFAULT_READ_TIMEOUT_SECS.

async run(mut &this) -> Result<(), IoError>;

Bind to this.addr and serve requests forever. Returns Err only on a fatal listener error (e.g. EMFILE, ENFILE).

async run_on(mut &this, listener: TcpListener) -> Result<(), IoError>;

Serve on a listener the caller already bound, which is the form to reach for when the port matters and this.addr cannot name it: bind to port 0, read the assigned port off local_addr, then hand the listener over. Binding here and reporting the port afterwards would mean racing another process for the port in between.

dispatch(&this, req: Request) -> Response;

Route req to the router (if set) or the raw handler. Consumes the request regardless of which path is taken.

async serve_connection(mut &this, conn: BufStream<TcpStream>) -> void;

Serve requests on conn until the client (or this server) signals close, a read exceeds the cap, or MAX_REQUESTS_PER_CONNECTION is reached. A parse failure on the FIRST request writes a canned 400; failures on follow-up requests are taken as "peer gave up" and close silently. Write failures (peer gone) end the connection. The accept loop always continues.

Keep-alive

Within a connection the server honours HTTP/1.1 persistence: after answering a request it reads the next one on the same socket until the client sends Connection: close, the read timeout fires, or the peer hangs up. Each request within the connection is still served sequentially — there is no pipelining.

Constants

const MAX_REQUESTS_PER_CONNECTION: u64 = 100;
const DEFAULT_READ_TIMEOUT_SECS:   i64 = 30;