giong-http
A small, strict HTTP/1.1 server for the giong runtime.
use Duration;
use TcpListener;
use ;
async
What it does
- HTTP/1.1 and HTTP/1.0, keep-alive, pipelining. Responses to pipelined requests leave in a single write; if a handler has to wait, the responses before it are sent first.
- Bodies by
Content-Lengthor chunked, received in full before the handler is called.Expect: 100-continueis supported. - A
Requestshares the connection's receive buffer instead of copying it, so building a request allocates nothing. A handler may keep the request for as long as it likes. - Limits on the size of the head (64 KiB) and the body (1 MiB); timeouts for idle connections (60 s), slow requests (30 s, against slowloris) and clients that read slowly (30 s). All of them can be changed on
Server. - Graceful shutdown: a request in progress is answered with
Connection: close, an idle connection is closed at once. - HTTPS through the
tlsfeature (see below). - Serving over a Unix socket with
Server::serve_unix, the usual setup behind a reverse proxy on the same machine. DateandContent-Lengthare added for you. A header value with control characters is refused (against response splitting).
Strict parsing
Any request that a proxy in front could read differently is answered with 400, because that difference is exactly how request smuggling works:
- two different
Content-Lengthheaders, orContent-Lengthtogether withTransfer-Encoding - whitespace before the colon, folded header lines, line endings that are a bare LF
- control characters in a header name or value, a missing or repeated
Hostin HTTP/1.1
Other status codes: 408 the request was too slow, 413 the body is too large, 417 an unknown Expect, 431 the head is too large, 501 a transfer-encoding other than chunked, 505 not HTTP/1.x.
HTTPS
Turn on the tls feature to get Server::serve_tls, which uses the giong-tls crate (rustls, TLS 1.2 and 1.3):
= { = "0.2", = ["tls"] }
= "0.2"
let acceptor = from_pem?;
// ... in each worker:
let listener = bind.expect;
new.serve_tls.await;
Handlers, limits, timeouts and graceful shutdown are the same as for plain HTTP. Each handshake runs in its connection's own task, so a slow client does not hold up accepting new connections; a client that does not speak TLS (a port scan, plain HTTP sent to the wrong port) is closed when the handshake timeout expires. When it closes a connection the server sends close_notify, so the client can tell a complete response from a connection that was cut.
To serve over another kind of connection (a different TLS layer, a fake connection in a test), implement the Transport trait and call Server::serve_connection.
Not there yet
HTTP/2, streaming bodies (a body over the limit is refused, not passed on piece by piece), compression, routing.
Tests
cargo test -p giong-http --all-features: 15 unit tests (including 500,000 random inputs for the parser and the chunked decoder), 17 end-to-end tests on real sockets (one of them over a Unix socket), 9 end-to-end HTTPS tests with a rustls client (TLS 1.2 and 1.3, pipelining, a 3 MB body, 400/413 errors, idle connections, clients that do not speak TLS, graceful shutdown, 16 client threads at once), 5 documentation examples.