Standard Library
class HttpServer
A web server that listens on a port and answers each request with a handler function.
since 0.1.0-alpha.1linux
Overview
Create it with a port number, check ok(), and call handle with a function that receives an HttpRequest and returns an HttpResponse. Connections are kept open for further requests unless either side sends Connection: close. If the handler throws, the client receives a 500 Internal Server Error and the connection is closed, while the server keeps serving other clients. A response made with HttpResponse::ofStream is sent in chunks as your code writes it.
Give a certificate and a private key to the second form of the constructor to serve HTTPS; the handler code stays the same. The server runs on the event loop: the program keeps running while the server is listening, until you call stop.
Description
HttpServer(port) listens on a port; handle(handler) installs the function that answers
requests and starts serving. The handler takes an HttpRequest and returns an HttpResponse:
server.handle((req) => HttpResponse(200, "hello"));
ok() tells you whether the port could be opened, and stop() stops listening.
The server takes care of the protocol around your handler.
- Framing. A request is read incrementally: the head, then the body as declared by
Content-Length. Your handler is called once the whole request has arrived. - Keep-alive. When the request did not ask for
Connection: close, the connection is kept for the next request, up to 100 requests per connection. - Errors. If the handler throws, the client gets a
500response withConnection: closeand the server keeps serving other clients. - Streaming. A handler may return
HttpResponse::ofStream(status, headers, writer). The server sends the head, then callswriterwith aChunkedSinkthat can keep writing from a timer or a subscription until it callsend(). A finite stream keeps the connection alive only after its last chunk has been sent. - HTTPS.
HttpServer(port, certPath, keyPath)serves over TLS with exactly the same handler surface. A client that fails the handshake is dropped and the server carries on. Seelang.tls.
A handler is an ordinary function, so you can test it without a network by building a request from text and calling it:
Testing a handler without a server
HttpResponse handle(HttpRequest req) {
if (req.path == "/hello") {
return HttpResponse(200, "hello, " + req.method);
}
return HttpResponse(404, "no such page");
}
HttpRequest home = HttpRequest();
home.parse("GET /hello HTTP/1.1\r\nHost: test\r\n\r\n");
HttpResponse a = handle(home);
console.writeln("${a.status} ${a.reason()} ${a.body}");
HttpRequest other = HttpRequest();
other.parse("GET /missing HTTP/1.1\r\nHost: test\r\n\r\n");
HttpResponse b = handle(other);
console.writeln("${b.status} ${b.reason()} ${b.body}");
200 OK hello, GET
404 Not Found no such page
Rules
- A server needs the event loop: it keeps the program alive until
stop()is called. - The handler must return a response. Return
HttpResponse(404, ...)for pages that do not exist; there is no automatic routing. - There is no pipelining. If a client sends more bytes while a response is in progress, the connection is closed.
- A response to a
HEADrequest, or with a1xx,204or304status, has no body. For a streaming response, only the head is sent and the writer is not called. - A streaming response to a request that is not
HTTP/1.1is replaced by505and the connection is closed. - The server computes
Content-Length,ConnectionandTransfer-Encoding; headers with these names that your handler sets are ignored. - Bodies are text.
Examples
A server with a plain route and a streaming route that sends three events:
HttpServer server = HttpServer(8080);
if (!server.ok()) {
console.writeln("port 8080 is not available");
} else {
server.handle((req) => {
if (req.path == "/events") {
HeaderMap h = HeaderMap();
h.set("Content-Type", "text/event-stream");
return HttpResponse::ofStream(200, h, (sink) => {
Timer ticker = std::every(1000);
ticker.subscribe((n) => {
sink.write("data: tick ${n}\n\n");
if (n == 3) { ticker.cancel(); sink.end(); }
});
sink.onClose(() => ticker.cancel());
});
}
return HttpResponse(200, "hello").withHeader("Content-Type", "text/plain");
});
}
Notes
Not implemented: request pipelining.
Examples
A small server
HttpServer server = HttpServer(8080);
if (server.ok()) {
server.handle((req) => {
if (req.path == "/hello") {
return HttpResponse(200, "Hello from ${req.method}");
}
return HttpResponse(404, "not found");
});
}
A streaming response
HttpServer server = HttpServer(8080);
server.handle((req) => HttpResponse::ofStream(200, HeaderMap(), (sink) => {
sink.write("first part\n");
sink.write("second part\n");
sink.end();
}));
Constructors
new
new(int port)Starts listening for plain HTTP connections on a port.
The port is bound immediately, so call ok() to find out whether that worked. No requests are answered until handle is called.
Parameters
- port
- The port number to listen on.
new(int port, string cert, string key)Starts listening for HTTPS connections on a port.
It behaves like HttpServer(port), but every connection first completes a secure handshake using the certificate and key. A client whose handshake fails is dropped and the server carries on.
Parameters
- port
- The port number to listen on.
- cert
- The path of the certificate file.
- key
- The path of the private key file.
Methods
handle
handle((HttpRequest) => HttpResponse h) -> voidStarts answering requests with a handler function.
The handler is called once for each complete request and must return the response to send. Calling handle again replaces the handler for connections accepted afterwards.
Parameters
- h
- The function that turns a request into a response.
See also: stop
ok
ok() -> boolTells whether the server is listening.
Returns
true when the port was bound, false when it could not be (for example because it is already in use).
stop
stop() -> voidStops listening for new connections.
Calling it more than once is harmless. Once the server has stopped, a program with nothing else to wait for can finish.
See also: handle
See also
- HttpRequest — An HTTP request as seen by a server: the request line, the headers and the body.
- HttpResponse — An HTTP response: a status code, headers and a body.
- HttpClient — A simple HTTP client that sends one request per connection and delivers the response to a callback.
- TcpListener — A listening socket that delivers each incoming connection as a
TcpStream. - ChunkedSink — The writer for a streaming HTTP response body.