std/http/server
std/http/src/server.trb
The HTTP/1.1 server: a listener, a handler that is a function from a request to a task of a response, an accept loop per worker on the one listening socket, and one task per connection that serves its requests one after another (docs/design/NETWORK.md sections 4 and 8).
alias Handler
type Handler = (request: Request) => Task<Result<Response, HttpError>>
What serves a request: a function from the request to a task of the response. A web framework is one of these.
type ServerLimits
type ServerLimits
What a server allows a client, each against an attack of its own. Times are milliseconds, because a field's default
is a constant and a Duration is none.
field headBytes
headBytes: Int = 65536
The request line and every field together: nginx allows 8 KiB a line and 32 KiB in all; a browser with many cookies needs more.
field headFields
headFields: Int = 100
Fields of one head: the count Go and Node refuse above.
field headMilliseconds
headMilliseconds: Int = 10000
The time from the first byte of a request to the end of its head: a client that sends it a byte a minute (slowloris) is cut off.
field idleMilliseconds
idleMilliseconds: Int = 60000
How long a kept-alive connection may wait for its next request.
field drainBytes
drainBytes: Int = 65536
What of a request's body the handler did not read is read and dropped up to this; more closes the connection.
type Server
shared type Server with Close
An HTTP/1.1 server. It listens as soon as it is made; Server.serve accepts connections until the server is shut
down or closed - with an accept loop on every worker, all of them on the one listening socket - and every connection
is a task of its own, on the worker whose loop accepted it.
fn hello(request: Request): Task<Result<Response, HttpError>> {
Ok Response.text("hello from {request.path()}")
}
var server = Server.listen(SocketAddress(IpAddress.loopback, 8080), hello)?
var serving = server.serve()
// ... and when the program is to stop:
server.shutdown().await()
A handler that fails is answered with the status its failure names where it names one - the body it read was cut
short, not UTF-8 or not the JSON asked for - and 500 Internal Server Error otherwise; the failure itself is not sent
to the client. A request the parser refuses is answered with the status its failure names
(HttpError.answerStatus), and the connection is closed. The server writes no Server field: a product name on the
wire tells an attacker what to try first, and a handler that wants one sets it.
fn listen
static fn listen(address: SocketAddress, handler: Handler, limits: ServerLimits = ServerLimits(), tls: ServerIdentity? = None): Result<Server, HttpError>
A server listening on address; port 0 asks the system for a free one, which Server.localAddress then says. With
tls, every connection is HTTPS: the TLS handshake comes first, within the head time of the limits.
fn localAddress
fn localAddress(): SocketAddress
Where it listens.
fn serve
var fn serve(): Task<Result<Void, HttpError>>
Accepts connections and serves each in a task of its own, until the server is shut down or closed - then it
answers Ok - or the listening socket fails.
It starts one accept loop per worker (Workers.count()), each accepting on the same listening socket, so the
connections spread over every core: a loop holds nothing but the socket's handle, the handler and the limits, so an
idle worker takes it before its first run, and a connection stays on the worker whose loop accepted it. A loop stays
on this task's worker where the handler holds an object that cannot move, and a server that speaks TLS runs one
loop, because its identity is such an object - correct either way, and parallel where it can be.
fn serveOne
var fn serveOne(): Task<Result<Void, HttpError>>
Accepts one connection and serves it to its end: what a test wants.
fn shutdown
var fn shutdown(grace: Duration = 10.seconds()): Task<Void>
Stops gracefully: no connection is accepted any more, a connection that waits for its next request is closed at
once, a request in progress is finished and answered with Connection: close, and what still runs once grace
has passed is cancelled. Finishes when every connection has ended.
fn close
var fn close()
Stops at once: the listener closes and every accept loop is cancelled, and with it every connection it serves. What the release of a server runs.