Ring adapter backed by a zero-dependency Java core: blocking I/O on virtual threads, one virtual thread per connection.
Ring adapter backed by a zero-dependency Java core: blocking I/O on virtual threads, one virtual thread per connection.
(flush! w)Emits any pending bytes as a chunk and forces them onto the wire.
Emits any pending bytes as a chunk and forces them onto the wire.
(port server)Actual listening port of server.
Actual listening port of `server`.
(run-server handler)(run-server handler {:keys [error-handler] :as opts})Starts an HTTP server calling handler with Ring request maps.
Returns the server, stop it with stop.
Network options:
:port - listen port, 0 picks an ephemeral port (default 8080):host - bind address (default "0.0.0.0"):backlog - accept queue length (default 1024)Timeouts:
:idle-timeout - per-read socket timeout in ms, 0 disables (default 30000):request-timeout - wall-clock deadline for reading a full request in ms,
0 disables (default 30000). Slowloris protection.:shutdown-timeout - graceful shutdown wait for in-flight requests in ms
(default 10000). Idle keep-alive connections close immediately.TLS:
:ssl-context - javax.net.ssl.SSLContext. When set, listens as TLS with
the context's keystore/truststore/protocols. Falls back to a user-space
file transfer for File response bodies (no zero-copy on TLS).:ssl-need-client-auth - require a valid client certificate (default false):ssl-want-client-auth - request but not require a client cert (default false):alpn-protocols - seq of ALPN protocol IDs to advertise. Defaults to
["h2" "http/1.1"] when :http2 is enabled, otherwise JVM default.:enabled-cipher-suites - seq of cipher suite names to enable (JVM default when nil):enabled-tls-protocols - seq of TLS protocol versions to enable (JVM default when nil):ssl-session-cache-size - SSL session cache size in entries. 0 = JVM
default (10000 on OpenJDK). Larger caches help session-resumption hit
rate under many short-lived TLS clients.HTTP/1.1 keep-alive:
:max-keep-alive-requests - cap on requests per connection, 0 = unlimited (default 1000):keep-alive-timeout - between-request idle timeout in ms, 0 = fall back to
:idle-timeout (default 0). Distinct from mid-request idle.TCP socket options (applied to acceptor + accepted sockets):
:so-nodelay - TCP_NODELAY (default true):so-reuse-addr - SO_REUSEADDR (default true):so-linger - SO_LINGER seconds, -1 disables (default -1):so-rcv-buf / :so-snd-buf - socket buffer sizes, 0 = OS defaultHTTP/2 hardening:
:http2-stream-reset-limit - RST_STREAM cap per connection, CVE-2023-44487
mitigation (default 400, matches Nginx):http2-continuation-limit - CONTINUATION frames per HEADERS (default 64)HTTP/3 (opt-in; requires PEM cert + key on disk since quiche loads them
itself rather than from :ssl-context):
:http3 (false) — enables HTTP/3 listener. Alt-Svc auto-advertised on
h1/h2 responses when both enabled.:http3-cert-path, :http3-key-path — PEM cert chain + private key.
Required when :http3 is true.:http3-port — UDP port. Defaults to :port (co-exists on the same
port number over UDP + TCP).:http3-max-idle-timeout (30000) — quiche idle timeout in ms.:http3-initial-max-data (1 GiB) — connection flow control window.:http3-initial-max-streams-bidi (100) — concurrent request streams.:http3-max-udp-payload-size (1350) — MTU-safe default.:http3-stateless-retry (false) — force clients to prove reachability
before we allocate conn state (RFC 9000 §8.1.2). Enable under DDoS.:advertise-alt-svc — override auto behavior (auto = true iff :http3).:alt-svc-max-age (86400) — ma= field on the emitted Alt-Svc.HTTP/3 advanced (QPACK, transport):
:http3-initial-max-streams-uni - peer's unidirectional stream credit (default 8; min 3):http3-max-field-section-size - SETTINGS_MAX_FIELD_SECTION_SIZE, our
inbound cap advertised to the peer (default 64 KiB, 0 = no limit):http3-qpack-max-table-capacity - SETTINGS_QPACK_MAX_TABLE_CAPACITY (default 0 = static-table only):http3-qpack-blocked-streams - SETTINGS_QPACK_BLOCKED_STREAMS (default 0):http3-initial-max-stream-data-bidi-local:http3-initial-max-stream-data-bidi-remote:http3-initial-max-stream-data-uni - per-stream flow control windows.
Default -1 means derive from :http3-initial-max-data / stream count.:http3-ack-delay-exponent - RFC 9000 ack_delay_exponent, [0, 20]. -1 = quiche default:http3-max-ack-delay - RFC 9000 max_ack_delay ms, [0, 16383]. -1 = quiche default:http3-active-connection-id-limit - RFC 9000 active_connection_id_limit, >= 2. -1 = quiche defaultServer-wide:
:server-header - value emitted as the Server: response header. Nil/empty
omits the header (default nil). Handler-supplied Server header wins.:worker-executor - java.util.concurrent.Executor for request-handler
tasks. Nil = built-in virtual-thread-per-task (default). The server does not
shutdown() a user-supplied executor.Error handling:
:error-handler - (fn [request throwable]) returning a Ring response map.
Invoked when the main handler throws or returns nil. If the error handler
itself throws or returns nil, a fallback 500 text response is sent.Buffers / limits (tune only if you know why):
:request-buffer-size - initial request parse buffer size (default 16384).
Grown up to :max-header-bytes as headers arrive.:max-header-bytes - hard cap for request headers, 431 above (default 65536).
Also caps how large the parse buffer may grow.:max-inline-body - response bodies at or below this size are inlined into
the header write buffer for one-syscall dispatch (default 16384). Larger
values reduce syscalls for big responses at the cost of more short-term
heap during pipelined bursts.:coalesce-high-water - pending response bytes at which a pipelined batch
is force-flushed (default 32768). Should be a small multiple of
:max-inline-body — batches beyond this cost more in memory than they
save in syscalls.:chunk-buffer-size - read chunk size when streaming response bodies with
Transfer-Encoding: chunked (default 8192). Reused across pipelined
responses on the same connection.:max-drain-bytes - largest ignored request body size drained before the
connection is closed instead of reused (default 65536). If the handler
ignores a POST body larger than this, keep-alive is dropped.:max-request-body-bytes - cap for the incoming request body in bytes,
0 disables (default 10 MiB). Content-Length above the cap → 413 upfront;
chunked bodies get 413 mid-stream once the cap is exceeded. Also caps
WebSocket frame payload size (uses same limit).Interaction notes:
:request-buffer-size and :max-header-bytes should typically be equal
or request-buffer-size <= max-header-bytes. Otherwise the initial buffer
caps at max-header-bytes and pipelined batches larger than that get
fragmented reads.:max-inline-body <= :coalesce-high-water — otherwise a single large
response triggers a flush before the next pipelined request can be
coalesced, defeating the batching.:request-timeout interacts with :idle-timeout: the per-read timeout is
min(idle, remaining-request-budget). Set both to sensible values.Errors are routed through java.util.logging under the loggers
com.s_exp.enso.http1.HttpConnection and com.s_exp.enso.EnsoServer. Wire a
handler / SLF4J bridge in your application to redirect them.
Starts an HTTP server calling `handler` with Ring request maps. Returns the server, stop it with [[stop]]. Network options: - `:port` - listen port, 0 picks an ephemeral port (default 8080) - `:host` - bind address (default "0.0.0.0") - `:backlog` - accept queue length (default 1024) Timeouts: - `:idle-timeout` - per-read socket timeout in ms, 0 disables (default 30000) - `:request-timeout` - wall-clock deadline for reading a full request in ms, 0 disables (default 30000). Slowloris protection. - `:shutdown-timeout` - graceful shutdown wait for in-flight requests in ms (default 10000). Idle keep-alive connections close immediately. TLS: - `:ssl-context` - `javax.net.ssl.SSLContext`. When set, listens as TLS with the context's keystore/truststore/protocols. Falls back to a user-space file transfer for File response bodies (no zero-copy on TLS). - `:ssl-need-client-auth` - require a valid client certificate (default false) - `:ssl-want-client-auth` - request but not require a client cert (default false) - `:alpn-protocols` - seq of ALPN protocol IDs to advertise. Defaults to `["h2" "http/1.1"]` when `:http2` is enabled, otherwise JVM default. - `:enabled-cipher-suites` - seq of cipher suite names to enable (JVM default when nil) - `:enabled-tls-protocols` - seq of TLS protocol versions to enable (JVM default when nil) - `:ssl-session-cache-size` - SSL session cache size in entries. 0 = JVM default (10000 on OpenJDK). Larger caches help session-resumption hit rate under many short-lived TLS clients. HTTP/1.1 keep-alive: - `:max-keep-alive-requests` - cap on requests per connection, 0 = unlimited (default 1000) - `:keep-alive-timeout` - between-request idle timeout in ms, 0 = fall back to `:idle-timeout` (default 0). Distinct from mid-request idle. TCP socket options (applied to acceptor + accepted sockets): - `:so-nodelay` - TCP_NODELAY (default true) - `:so-reuse-addr` - SO_REUSEADDR (default true) - `:so-linger` - SO_LINGER seconds, -1 disables (default -1) - `:so-rcv-buf` / `:so-snd-buf` - socket buffer sizes, 0 = OS default HTTP/2 hardening: - `:http2-stream-reset-limit` - RST_STREAM cap per connection, CVE-2023-44487 mitigation (default 400, matches Nginx) - `:http2-continuation-limit` - CONTINUATION frames per HEADERS (default 64) HTTP/3 (opt-in; requires PEM cert + key on disk since quiche loads them itself rather than from `:ssl-context`): - `:http3` (false) — enables HTTP/3 listener. `Alt-Svc` auto-advertised on h1/h2 responses when both enabled. - `:http3-cert-path`, `:http3-key-path` — PEM cert chain + private key. Required when `:http3` is true. - `:http3-port` — UDP port. Defaults to `:port` (co-exists on the same port number over UDP + TCP). - `:http3-max-idle-timeout` (30000) — quiche idle timeout in ms. - `:http3-initial-max-data` (1 GiB) — connection flow control window. - `:http3-initial-max-streams-bidi` (100) — concurrent request streams. - `:http3-max-udp-payload-size` (1350) — MTU-safe default. - `:http3-stateless-retry` (false) — force clients to prove reachability before we allocate conn state (RFC 9000 §8.1.2). Enable under DDoS. - `:advertise-alt-svc` — override auto behavior (auto = true iff `:http3`). - `:alt-svc-max-age` (86400) — `ma=` field on the emitted `Alt-Svc`. HTTP/3 advanced (QPACK, transport): - `:http3-initial-max-streams-uni` - peer's unidirectional stream credit (default 8; min 3) - `:http3-max-field-section-size` - SETTINGS_MAX_FIELD_SECTION_SIZE, our inbound cap advertised to the peer (default 64 KiB, 0 = no limit) - `:http3-qpack-max-table-capacity` - SETTINGS_QPACK_MAX_TABLE_CAPACITY (default 0 = static-table only) - `:http3-qpack-blocked-streams` - SETTINGS_QPACK_BLOCKED_STREAMS (default 0) - `:http3-initial-max-stream-data-bidi-local` - `:http3-initial-max-stream-data-bidi-remote` - `:http3-initial-max-stream-data-uni` - per-stream flow control windows. Default -1 means derive from `:http3-initial-max-data` / stream count. - `:http3-ack-delay-exponent` - RFC 9000 ack_delay_exponent, [0, 20]. -1 = quiche default - `:http3-max-ack-delay` - RFC 9000 max_ack_delay ms, [0, 16383]. -1 = quiche default - `:http3-active-connection-id-limit` - RFC 9000 active_connection_id_limit, >= 2. -1 = quiche default Server-wide: - `:server-header` - value emitted as the `Server:` response header. Nil/empty omits the header (default nil). Handler-supplied `Server` header wins. - `:worker-executor` - `java.util.concurrent.Executor` for request-handler tasks. Nil = built-in virtual-thread-per-task (default). The server does not shutdown() a user-supplied executor. Error handling: - `:error-handler` - `(fn [request throwable])` returning a Ring response map. Invoked when the main handler throws or returns nil. If the error handler itself throws or returns nil, a fallback 500 text response is sent. Buffers / limits (tune only if you know why): - `:request-buffer-size` - initial request parse buffer size (default 16384). Grown up to `:max-header-bytes` as headers arrive. - `:max-header-bytes` - hard cap for request headers, 431 above (default 65536). Also caps how large the parse buffer may grow. - `:max-inline-body` - response bodies at or below this size are inlined into the header write buffer for one-syscall dispatch (default 16384). Larger values reduce syscalls for big responses at the cost of more short-term heap during pipelined bursts. - `:coalesce-high-water` - pending response bytes at which a pipelined batch is force-flushed (default 32768). Should be a small multiple of `:max-inline-body` — batches beyond this cost more in memory than they save in syscalls. - `:chunk-buffer-size` - read chunk size when streaming response bodies with Transfer-Encoding: chunked (default 8192). Reused across pipelined responses on the same connection. - `:max-drain-bytes` - largest ignored request body size drained before the connection is closed instead of reused (default 65536). If the handler ignores a POST body larger than this, keep-alive is dropped. - `:max-request-body-bytes` - cap for the incoming request body in bytes, 0 disables (default 10 MiB). Content-Length above the cap → 413 upfront; chunked bodies get 413 mid-stream once the cap is exceeded. Also caps WebSocket frame payload size (uses same limit). Interaction notes: - `:request-buffer-size` and `:max-header-bytes` should typically be equal or `request-buffer-size <= max-header-bytes`. Otherwise the initial buffer caps at `max-header-bytes` and pipelined batches larger than that get fragmented reads. - `:max-inline-body <= :coalesce-high-water` — otherwise a single large response triggers a flush before the next pipelined request can be coalesced, defeating the batching. - `:request-timeout` interacts with `:idle-timeout`: the per-read timeout is `min(idle, remaining-request-budget)`. Set both to sensible values. Errors are routed through `java.util.logging` under the loggers `com.s_exp.enso.http1.HttpConnection` and `com.s_exp.enso.EnsoServer`. Wire a handler / SLF4J bridge in your application to redirect them.
(stop server)Stops server. In-flight requests complete, open connections close.
Stops `server`. In-flight requests complete, open connections close.
(write! w data)Buffers bytes into the pending chunk. String is UTF-8 encoded.
Buffers bytes into the pending chunk. String is UTF-8 encoded.
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 |