Settings to review before putting enso in front of real traffic, and what the server already protects against.
:handler-timeout. It is off by default because how long a handler
may take is application policy. Without it, a handler stuck on a slow
dependency holds its connection (or HTTP/2 or HTTP/3 stream slot) and
its virtual thread until it returns. A few times your slowest legitimate
handler is a reasonable value.:max-connections-per-ip when clients connect directly. Without it,
one address can take every connection slot. Leave it off behind a load
balancer or NAT, where clients share addresses, and limit per client
there instead.Origin scheme and host must match the connection and Host. Behind a
proxy that rewrites Host, or for cross-origin clients, list the allowed
origins in :ws-allowed-origins, which then refuses a handshake with no
Origin. Authenticate in the handler before returning the listener.The defaults bound what a single client can make the server hold.
| Limit | Default | Option |
|---|---|---|
| request head | 64 KiB | :max-header-bytes |
| header fields per request | 100 | :max-header-fields |
| request body | 10 MiB | :max-request-body-bytes |
| WebSocket message | 1 MiB | :ws-max-message-bytes |
| queued WebSocket sends | 1 MiB per connection | :ws-max-queued-bytes |
| open connections | 10000 | :max-connections |
| buffered peer data and flow-control credit | a quarter of the heap, shared fairly under pressure | :max-buffered-bytes |
| slowest request body | 240 bytes/s after 5 s of waiting | :min-data-rate-bytes |
Timeouts are listed in options.md.
Request parsing:
Content-Length (even with equal values, on every protocol), Transfer-Encoding with Content-Length, obs-fold, bare LF line endings, invalid chunk sizes;:scheme, :authority and Host, connection-specific fields, TE, Content-Length). A well-formed CONNECT gets 501, as on HTTP/1.1.Responses:
Slow and stalled clients are cut off by the same timeouts on every protocol: handshake, header (slowloris), read and minimum data rate (slow uploads), write (peers that stop reading), idle and handler.
Floods and resource exhaustion:
:http2-stream-reset-limit;:http2-continuation-limit and the header size ceiling;:http3-stream-reset-limit;:http3-retry-threshold handshaking connections, and a cap on half-open connections;:max-connections-per-ip off; every client shares the proxy's address.Host, list the public origins in :ws-allowed-origins.:idle-timeout of 75 s is longer than the 60 s idle timeout of common load balancers (AWS ALB), so the balancer, not enso, closes idle connections.:ssl-cert-path and :ssl-key-path at PEM files for TLS from
disk: the certificate (leaf first, then any intermediates) and an
unencrypted RSA or EC private key in PKCS8 (PRIVATE KEY), PKCS1 (RSA PRIVATE KEY) or SEC1 (EC PRIVATE KEY) encoding — the common openssl
outputs, including the EC PARAMETERS block it prepends to a SEC1 key.
EC keys must name a curve. Encrypted keys are rejected; convert one with
openssl pkcs8 -topk8 -nocrypt. This path reloads on change, so
certificate rotation needs no restart. :ssl-context and
:ssl-context-provider stay available for a prebuilt context.:ssl-cert-reload-interval sets how often the files are checked (10 s by
default, 0 to turn it off). A handshake reads only the current
certificate, so reloading costs nothing per connection.:ssl-ca-path to the CA bundle that signs client
certificates and turn on :ssl-need-client-auth (or
:ssl-want-client-auth). This is TCP only; combining it with :http3 is
rejected, since HTTP/3 client authentication is not yet supported.:ssl-cert-path and :ssl-key-path too when
:http3-cert-path is not set, so both transports rotate from one pair of
files under one watcher. Set :http3-cert-path and :http3-key-path for
a separate HTTP/3 certificate.net.core.rmem_max and net.core.wmem_max on busy servers. The
4 MiB UDP buffers enso asks for are clamped to them, and a smaller grant
is logged at INFO.--enable-native-access=ALL-UNNAMED to the JVM
options (details).noexec, point
-Denso.quiche.tmpdir (or ENSO_QUICHE_TMPDIR) at one that isn't. The
shim is extracted there at startup.EnsoServer.isHealthy() is true while the server runs with its
acceptor, timer and every HTTP/3 event loop alive. Each of these threads
restarts itself after a failure, so false is a fault worth alerting on.java.util.logging (options.md).
Client-caused failures log at FINE; WARNING means a server fault.:server-events and JDK Flight Recorder events cover connections,
requests and protocol errors
(options.md).Every request runs on its own virtual thread. While
jdk.trackAllThreads is on (the JDK default), each virtual thread is also
recorded in a thread container for thread dumps, which costs a small
allocation per request. -Djdk.trackAllThreads=false saves it, but those
threads then don't appear in jcmd Thread.dump_to_file.
Can you improve this documentation?Edit on GitHub
cljdoc builds & hosts documentation for Clojure/Script libraries
| Ctrl+k | Jump to recent docs |
| ← | Move to previous article |
| → | Move to next article |
| Ctrl+/ | Jump to the search field |