Server
Once you have a router, you serve it with a
RestServerRef. The server owns a
Chronos HTTP server, dispatches incoming requests through the router, and turns
the RestApiResponse your handlers
return into real HTTP responses.
Creating a server
RestServerRef.new returns a Result,
so unwrap it with get() (or handle the error). At minimum it needs a router
and an address to bind to.
let server = RestServerRef.new(router, address).get()
Common options
new accepts many keyword arguments; the most useful ones are:
| Argument | Default | Description |
|---|---|---|
serverIdent | PrestoIdent | value of the Server response header |
serverFlags | {NotifyDisconnect} | Chronos HttpServerFlags |
socketFlags | {ReuseAddr} | listening socket flags |
maxConnections | -1 (unlimited) | connection cap |
bufferSize | 4096 | per-connection buffer |
httpHeadersTimeout | 10.seconds | header read timeout |
maxHeadersSize | 8192 | max request header bytes |
maxRequestBodySize | 1_048_576 | max request body bytes |
requestErrorHandler | nil | custom error handler (see below) |
errorType | cstring | error type of the returned Result |
The errorType parameter lets you choose whether construction failures are
reported as cstring (the default) or string:
let res = RestServerRef.new(router, address, errorType = string)
if res.isErr():
echo "failed to start: ", res.error()
Lifecycle
server.start() # begin accepting connections
echo server.state # Running | Stopped | Closed
echo server.localAddress() # actual bound address (useful with port 0)
await server.stop() # stop accepting new connections
await server.drop() # drop pending connections
await server.closeWait() # stop and release all resources
The relevant procedures are
start,
stop,
drop,
closeWait,
join,
state and
localAddress.
A typical test or short-lived program brackets its work with start and
closeWait:
server.start()
try:
# … issue requests …
discard
finally:
await server.closeWait()
Handling errors
Some failures happen before or around your handler: a malformed request, an
unknown route, or an undecodable body. By default Presto answers these with a
bare status code (400, 404, …). Supply a requestErrorHandler to customize
them.
The handler receives a
RestRequestError describing
what went wrong and the HttpRequestRef, and returns an HttpResponseRef.
proc onError(kind: RestRequestError,
request: HttpRequestRef): Future[HttpResponseRef] {.
async: (raises: [CancelledError]).} =
try:
case kind
of RestRequestError.Invalid:
await request.respond(Http400, "invalid request")
of RestRequestError.NotFound:
await request.respond(Http404, "no such endpoint")
of RestRequestError.InvalidContentBody:
await request.respond(Http400, "bad body")
of RestRequestError.InvalidContentType:
await request.respond(Http400, "bad content-type")
of RestRequestError.Unexpected:
defaultResponse()
except HttpError:
defaultResponse()
let server = RestServerRef.new(
router, initTAddress("127.0.0.1:8080"), requestErrorHandler = onError).get()
The RestRequestError cases are:
Invalid— the request line or path could not be parsed.NotFound— no route matched.InvalidContentBody— the body could not be read.InvalidContentType— a body was sent without a usableContent-Type.Unexpected— a catch-all.
defaultResponse() returns Presto's built-in response and is handy as a
fallback.
The error handler covers framework errors. Errors your own handler wants to
report should be returned as RestApiResponse.error(...) instead (see
Building responses).
Serving over HTTPS
For TLS, use SecureRestServerRef
from presto/secureserver. It behaves exactly like RestServerRef—same
routing, same responses—but additionally takes a TLS private key and
certificate.
let
key = TLSPrivateKey.init(myKeyPem)
cert = TLSCertificate.init(myCertPem)
server = SecureRestServerRef.new(
router, initTAddress("127.0.0.1:8443"), key, cert).get()
server.start()
SecureRestServerRef supports the
same lifecycle procedures (start, stop, drop, closeWait, join,
state, localAddress) and the same errorType option as the plain server,
plus a secureFlags: set[TLSFlags] argument for TLS-specific behavior.