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 async] :as opts})Starts an HTTP server calling handler with Ring request maps.
Returns the server, stop it with stop. Unknown options and invalid
values throw ex-info naming the option.
handler is (fn [request] response), or with :async true a Ring
asynchronous handler (fn [request respond raise]). It runs on a
virtual thread per request (per connection on HTTP/1.1).
Response :body: nil, String, byte array, InputStream, File, a seq
(each element str'd), (fn [ChunkedWriter]) streaming with
write! / flush! (an enso extension), or anything satisfying
ring.core.protocols/StreamableResponseBody when on the classpath.
:status defaults to 200 when absent.
WebSocket (HTTP/1.1): answer an upgrade request with
{:ring.websocket/listener l}, l a map of :on-open :on-message
:on-ping :on-pong :on-error :on-close fns or a
ring.websocket.protocols/Listener. See doc/handlers.md.
Durations are milliseconds, 0 disables a timeout; sizes are bytes.
Network:
:port (8080) - listen port, 0 picks an ephemeral port
:host ("0.0.0.0") - bind address
:backlog (1024) - accept queue length
:max-connections (10000) - open connections server-wide, TCP and QUIC
together, 0 = unlimited. TCP connections over the limit are closed at
accept, QUIC Initials dropped.
:max-connections-per-ip (0) - open connections per client address, 0 =
unlimited. Leave off behind a proxy or NAT, where clients share an
address.
Timeouts:
:handshake-timeout (10000) - ms to complete the TLS handshake (plus the
HTTP/2 preface and first SETTINGS, or the QUIC handshake), wall clock. It
and :idle-timeout must not both be 0, which would leave a half-open
connection with no bound.
:header-timeout (10000) - ms to receive a complete request head from its
first byte, wall clock (slowloris protection). Answered with 408.
:read-timeout (30000) - longest ms without progress reading a request
body; the read fails (408 unless the handler catches it). Also bounds head
reads when :header-timeout is 0, and is the wall-clock time a WebSocket
message may take from its first frame to its last (CLOSE 1008), less time
the server held it back for :max-buffered-bytes.
:min-data-rate-bytes (240) - least request body bytes per second while a
handler waits for them, once :min-data-rate-grace of waiting is spent
(Kestrel's default), 0 = off. Below it the read fails (408, protocol error
"min-data-rate"): a body trickled just inside :read-timeout can't hold a
request forever. Time the handler isn't reading doesn't count.
:min-data-rate-grace (5000) - ms of waiting for request body bytes allowed
before :min-data-rate-bytes applies.
:write-timeout (30000) - longest ms without write progress (a peer that
stopped reading); the connection is force-closed.
:idle-timeout (75000) - ms a connection may sit with no request in
progress: HTTP/1.1 between requests (and before the first), HTTP/2 and
HTTP/3 with no open stream, a WebSocket with no frame from the client
(closing handshake with 1001).
:handler-timeout (0) - ms a handler may run, 0 = off. On expiry the client
gets 503 (nothing was sent yet) and the handler thread is interrupted.
:shutdown-timeout (10000) - ms stop waits for in-flight requests across
all connections before force-closing them.
Limits:
:max-header-bytes (65536) - request head size cap on every protocol, 431
above: the HTTP/1.1 head (and chunked trailers), the decoded HTTP/2 header
list (advertised as SETTINGS_MAX_HEADER_LIST_SIZE), the decoded HTTP/3
field section (SETTINGS_MAX_FIELD_SECTION_SIZE). Over 4x this (at least 64
KiB) an HTTP/2 connection ends with GOAWAY(ENHANCE_YOUR_CALM), an HTTP/3
stream is reset.
:max-header-fields (100) - request header fields per head on every
protocol, 431 above (Apache's LimitRequestFields and Tomcat's
maxHeaderCount default).
:max-request-body-bytes (10485760) - request body cap on every protocol, 0
= none. A larger Content-Length gets 413 upfront; a body without one fails
mid-stream. WebSocket messages have their own cap,
:ws-max-message-bytes.
:max-keep-alive-requests (1000) - requests per HTTP/1.1 connection, 0 =
unlimited. The last response carries Connection: close.
:max-buffered-bytes (0) - bytes the server buffers or promises
(flow-control credit) for peers, server-wide: unread request bodies
(HTTP/2, HTTP/3) and the HTTP/2 credit granted for them, streamed HTTP/2
response bytes, queued WebSocket sends and incoming WebSocket messages
past 64 KiB. 0 = a quarter of the maximum heap. Credit is paid for before
it is granted. From three quarters of it connections over their fair share
are throttled, at the limit all are: HTTP/2 grants connection credit only
up to the initial 65535 octets, HTTP/3 stops reading request streams until
bodies are read and a WebSocket stops reading a large message until there
is room (backpressure), one WebSocket at a time reading on past the limit
so whole messages still complete; WebSocket asynchronous sends fail at
once.
TCP:
:so-nodelay (true) - TCP_NODELAY on accepted sockets.
:so-reuse-addr (true) - SO_REUSEADDR on the listening socket.
:so-linger (-1) - SO_LINGER on accepted sockets, in seconds as the socket
option, -1 = off.
:so-rcv-buf-bytes (0) - SO_RCVBUF, set on the listening socket before bind
so accepted sockets inherit it; 0 = OS default.
:so-snd-buf-bytes (0) - SO_SNDBUF on accepted sockets, 0 = OS default.
TLS:
:ssl-context (none) - javax.net.ssl.SSLContext; when set the TCP
listener speaks TLS. File bodies then go through user space (no
zero-copy).
:ssl-context-provider (none) - fn (or java.util.function.Supplier)
returning an SSLContext, called at startup and per accepted connection
so rotated certificates apply to new connections; return a cached context
(one built per call costs that per connection and loses session
resumption). Exclusive with :ssl-context.
:ssl-need-client-auth (false) - require a valid client certificate.
:ssl-want-client-auth (false) - request a client certificate without
requiring one.
:ssl-alpn-protocols (none) - ALPN protocol ids offered; default ["h2"
"http/1.1"] with :http2, else ["http/1.1"].
:ssl-cipher-suites (none) - enabled cipher suites, nil = JVM default.
:ssl-protocols (none) - enabled TLS protocol versions, nil = JVM default.
:ssl-session-cache-size (0) - server TLS session cache entries, set on
every context the server uses; 0 = JVM default.
HTTP/2:
:http2 (false) - serve "h2" over TLS + ALPN (needs :ssl-context;
cleartext HTTP/2 is :http2c).
:http2c (false) - serve cleartext HTTP/2 with prior knowledge (RFC 9113
§3.3) on a plain listener next to HTTP/1.1: a connection that opens with
the HTTP/2 preface is HTTP/2. No Upgrade: h2c (deprecated by RFC 9113).
Not with :ssl-context (use :http2).
:http2-max-concurrent-streams (100) - SETTINGS_MAX_CONCURRENT_STREAMS.
:http2-initial-window-bytes (262144) - per-stream receive window a stream
starts with, [65535, 2^31-1]; it doubles, up to :http2-max-window-bytes,
while the handler reads half of it within two round trips. The connection
window grows to 4x this (1 MiB by default) once request body bytes arrive,
and doubles by the same rule up to twice :http2-max-window-bytes, every
growth as far as :max-buffered-bytes pays for it. Credit returns as
handlers read.
:http2-max-window-bytes (8388608) - most a stream's receive window grows
to, [:http2-initial-window-bytes, 2^31-1]; the connection window grows
to twice this. Equal to :http2-initial-window-bytes: fixed windows.
:http2-max-frame-bytes (16384) - SETTINGS_MAX_FRAME_SIZE, [16384,
16777215].
:http2-stream-reset-limit (400) - client RST_STREAMs allowed per
connection, refilling at that many per 30 s; above it ENHANCE_YOUR_CALM
(CVE-2023-44487). 0 = off.
:http2-continuation-limit (64) - CONTINUATION frames per header block.
HTTP/3 (QUIC via quiche):
:http3 (false) - serve HTTP/3 over UDP; needs :http3-cert-path and
:http3-key-path.
:http3-port (0) - UDP port, 0 = the :port number. With :port 0 set
this (or :advertise-alt-svc false): Alt-Svc must name a fixed port.
:http3-cert-path (none) - PEM certificate chain (quiche reads it from
disk).
:http3-key-path (none) - PEM private key.
:http3-initial-max-data-bytes (524288) - initial connection flow-control
window (it autotunes up to twice :http3-max-window-bytes on release
shims, stays fixed on a stock libquiche); charged to :max-buffered-bytes
while the connection reads a request body. Also bounds request-body bytes
buffered per connection.
:http3-initial-max-streams-bidi (100) - concurrent request streams per
connection; twice this many live handlers make new requests
H3_REQUEST_REJECTED.
:http3-initial-max-streams-uni (8) - peer unidirectional stream credit, at
least 3 (control + QPACK encoder/decoder).
:http3-max-udp-payload-bytes (1350) - max UDP payload, [1200, 65527]; the
default is MTU-safe.
:http3-stateless-retry (false) - make clients prove their address before
any state is allocated (RFC 9000 §8.1.2); for floods.
:http3-cert-reload-interval (10000) - milliseconds between checks of
:http3-cert-path / :http3-key-path for changes; a changed pair is loaded
for new connections, existing ones keep theirs; 0 = off.
:http3-retry-threshold (256) - handshaking connections from which a client
Initial without a valid token gets a stateless Retry.
:http3-max-half-open (1024) - handshaking connections above which new
Initials are dropped; 0 = unlimited.
:http3-stream-reset-limit (400) - peer stream resets allowed per
connection, refilling at that many per 30 s (rapid reset); 0 = off.
:http3-event-loops (0) - event loop threads owning QUIC connections, [0,
64]; 0 = one per core on Linux, 1 elsewhere.
:http3-so-rcv-buf-bytes (4194304) - UDP socket receive buffer requested
(best effort); 0 = OS default.
:http3-so-snd-buf-bytes (4194304) - UDP socket send buffer requested (best
effort); 0 = OS default.
:http3-initial-max-stream-data-bidi-local-bytes (-1) - per-stream window,
-1 = half of :http3-initial-max-data-bytes (256 KiB by default), so one
unread stream can't stall the connection.
:http3-initial-max-stream-data-bidi-remote-bytes (-1) - same, for
peer-initiated streams.
:http3-initial-max-stream-data-uni-bytes (-1) - same, for unidirectional
streams.
:http3-max-window-bytes (8388608) - most a stream's receive window
autotunes to, at least the per-stream initial windows; a connection's
window grows to twice this as far as :max-buffered-bytes pays. Needs the
patched libquiche of the release builds; ignored on a stock libquiche
(fixed windows).
:http3-ack-delay-exponent (-1) - RFC 9000 ack_delay_exponent, [0, 20]; -1
= quiche default.
:http3-max-ack-delay (-1) - RFC 9000 max_ack_delay in ms as the transport
parameter, [0, 16383]; -1 = quiche default.
:http3-active-connection-id-limit (-1) - RFC 9000
active_connection_id_limit, >= 2; -1 = quiche default.
:advertise-alt-svc (none) - send Alt-Svc on HTTP/1.1 and HTTP/2
responses; nil = whenever :http3 is on. True needs :http3.
:alt-svc-max-age (86400) - ma of the Alt-Svc header, in seconds as the
header parameter.
WebSocket:
:ws-max-message-bytes (1048576) - largest message once reassembled and
decompressed; a larger one closes the connection with 1009. Messages are
buffered whole.
:ws-max-queued-bytes (1048576) - payload bytes asynchronous sends may
queue per connection (plus 64 per frame), 0 = unlimited. A send over it
fails at once (its fail callback runs); one message larger than the limit
is accepted when nothing is queued.
:ws-close-timeout (5000) - ms a closing handshake the server started
(close, failure, idle timeout, shutdown) waits for the client's CLOSE
before dropping TCP; at least 1.
:ws-compression (false) - negotiate permessage-deflate (RFC 7692) when the
client offers it. A compressing connection holds a Deflater and an
Inflater (about 300 KiB of native memory); without the server's context
takeover its Deflater is borrowed from a pool per message.
:ws-ping-interval (0) - ms without a frame from the client after which the
server sends a ping (and again at that interval), 0 = never. The client's
pongs count as frames, so a live but silent client outlasts
:idle-timeout; less than :idle-timeout.
:ws-allowed-origins (none) - origins allowed to open a WebSocket
(cross-site WebSocket hijacking protection): a collection of origins such
as "https://app.example.com" ("*" for any) or a fn of the Origin value.
nil = same origin: the Origin scheme and host must match the connection
and the Host header. A configured allowlist is a hard gate: a handshake
without Origin (not from a browser) is refused. With no allowlist a
handshake without Origin is allowed. A refused handshake gets 403.
Handler and observability:
:async (false) - handler is a Ring asynchronous handler, (fn [request respond raise]). It runs on the request's virtual thread, which waits
until respond or raise is called (from any thread), without bound
unless :handler-timeout is set; raise goes to :error-handler. Only
the first call counts: the handler throwing before either is a raise,
after one it is logged. A StreamableResponseBody may write after returning
and ends when it closes its stream.
:error-handler (none) - (fn [request throwable]) returning a Ring
response, when the handler throws or returns nil, on every protocol. If it
throws or returns nil too, a plain 500 is sent.
:server-header (none) - Server response header value; nil or empty omits
it. A handler-supplied Server wins.
:server-events (none) - map of fns {:connection-opened (fn [protocol remote-addr]) :connection-closed (fn [protocol remote-addr nanos]) :request-completed (fn [protocol method status request-bytes response-bytes nanos]) :protocol-error (fn [protocol kind])}, or a
com.s_exp.enso.api.ServerEvents. remote-addr is formatted as
:remote-addr. A request head refused before it was parsed is reported
only as a protocol error. Protocols: "http/1.1", "h2", "h2c", "h3";
"websocket" for an upgraded connection's open / close; "tcp" (connection
refused at accept) and "tls" (handshake failures) for protocol errors.
Delivered in order from one dedicated thread, never from a server thread;
past 8192 queued events (a listener that fell behind) new ones are dropped
and counted (EnsoServer.droppedEvents, a log warning, a JFR event).
Exceptions are logged and ignored. No cost when absent.
Errors are logged through java.util.logging under com.s_exp.enso.*:
client-caused failures at FINE, server-side faults at WARNING (rate-limited).
Starts an HTTP server calling `handler` with Ring request maps.
Returns the server, stop it with [[stop]]. Unknown options and invalid
values throw ex-info naming the option.
`handler` is `(fn [request] response)`, or with `:async` true a Ring
asynchronous handler `(fn [request respond raise])`. It runs on a
virtual thread per request (per connection on HTTP/1.1).
Response `:body`: nil, String, byte array, InputStream, File, a seq
(each element `str`'d), `(fn [ChunkedWriter])` streaming with
[[write!]] / [[flush!]] (an enso extension), or anything satisfying
`ring.core.protocols/StreamableResponseBody` when on the classpath.
`:status` defaults to 200 when absent.
WebSocket (HTTP/1.1): answer an upgrade request with
`{:ring.websocket/listener l}`, `l` a map of `:on-open` `:on-message`
`:on-ping` `:on-pong` `:on-error` `:on-close` fns or a
`ring.websocket.protocols/Listener`. See doc/handlers.md.
Durations are milliseconds, 0 disables a timeout; sizes are bytes.
Network:
- `:port` (8080) - listen port, 0 picks an ephemeral port
- `:host` ("0.0.0.0") - bind address
- `:backlog` (1024) - accept queue length
- `:max-connections` (10000) - open connections server-wide, TCP and QUIC
together, 0 = unlimited. TCP connections over the limit are closed at
accept, QUIC Initials dropped.
- `:max-connections-per-ip` (0) - open connections per client address, 0 =
unlimited. Leave off behind a proxy or NAT, where clients share an
address.
Timeouts:
- `:handshake-timeout` (10000) - ms to complete the TLS handshake (plus the
HTTP/2 preface and first SETTINGS, or the QUIC handshake), wall clock. It
and `:idle-timeout` must not both be 0, which would leave a half-open
connection with no bound.
- `:header-timeout` (10000) - ms to receive a complete request head from its
first byte, wall clock (slowloris protection). Answered with 408.
- `:read-timeout` (30000) - longest ms without progress reading a request
body; the read fails (408 unless the handler catches it). Also bounds head
reads when `:header-timeout` is 0, and is the wall-clock time a WebSocket
message may take from its first frame to its last (CLOSE 1008), less time
the server held it back for `:max-buffered-bytes`.
- `:min-data-rate-bytes` (240) - least request body bytes per second while a
handler waits for them, once `:min-data-rate-grace` of waiting is spent
(Kestrel's default), 0 = off. Below it the read fails (408, protocol error
"min-data-rate"): a body trickled just inside `:read-timeout` can't hold a
request forever. Time the handler isn't reading doesn't count.
- `:min-data-rate-grace` (5000) - ms of waiting for request body bytes allowed
before `:min-data-rate-bytes` applies.
- `:write-timeout` (30000) - longest ms without write progress (a peer that
stopped reading); the connection is force-closed.
- `:idle-timeout` (75000) - ms a connection may sit with no request in
progress: HTTP/1.1 between requests (and before the first), HTTP/2 and
HTTP/3 with no open stream, a WebSocket with no frame from the client
(closing handshake with 1001).
- `:handler-timeout` (0) - ms a handler may run, 0 = off. On expiry the client
gets 503 (nothing was sent yet) and the handler thread is interrupted.
- `:shutdown-timeout` (10000) - ms `stop` waits for in-flight requests across
all connections before force-closing them.
Limits:
- `:max-header-bytes` (65536) - request head size cap on every protocol, 431
above: the HTTP/1.1 head (and chunked trailers), the decoded HTTP/2 header
list (advertised as SETTINGS_MAX_HEADER_LIST_SIZE), the decoded HTTP/3
field section (SETTINGS_MAX_FIELD_SECTION_SIZE). Over 4x this (at least 64
KiB) an HTTP/2 connection ends with GOAWAY(ENHANCE_YOUR_CALM), an HTTP/3
stream is reset.
- `:max-header-fields` (100) - request header fields per head on every
protocol, 431 above (Apache's LimitRequestFields and Tomcat's
maxHeaderCount default).
- `:max-request-body-bytes` (10485760) - request body cap on every protocol, 0
= none. A larger Content-Length gets 413 upfront; a body without one fails
mid-stream. WebSocket messages have their own cap,
`:ws-max-message-bytes`.
- `:max-keep-alive-requests` (1000) - requests per HTTP/1.1 connection, 0 =
unlimited. The last response carries Connection: close.
- `:max-buffered-bytes` (0) - bytes the server buffers or promises
(flow-control credit) for peers, server-wide: unread request bodies
(HTTP/2, HTTP/3) and the HTTP/2 credit granted for them, streamed HTTP/2
response bytes, queued WebSocket sends and incoming WebSocket messages
past 64 KiB. 0 = a quarter of the maximum heap. Credit is paid for before
it is granted. From three quarters of it connections over their fair share
are throttled, at the limit all are: HTTP/2 grants connection credit only
up to the initial 65535 octets, HTTP/3 stops reading request streams until
bodies are read and a WebSocket stops reading a large message until there
is room (backpressure), one WebSocket at a time reading on past the limit
so whole messages still complete; WebSocket asynchronous sends fail at
once.
TCP:
- `:so-nodelay` (true) - TCP_NODELAY on accepted sockets.
- `:so-reuse-addr` (true) - SO_REUSEADDR on the listening socket.
- `:so-linger` (-1) - SO_LINGER on accepted sockets, in seconds as the socket
option, -1 = off.
- `:so-rcv-buf-bytes` (0) - SO_RCVBUF, set on the listening socket before bind
so accepted sockets inherit it; 0 = OS default.
- `:so-snd-buf-bytes` (0) - SO_SNDBUF on accepted sockets, 0 = OS default.
TLS:
- `:ssl-context` (none) - `javax.net.ssl.SSLContext`; when set the TCP
listener speaks TLS. File bodies then go through user space (no
zero-copy).
- `:ssl-context-provider` (none) - fn (or `java.util.function.Supplier`)
returning an `SSLContext`, called at startup and per accepted connection
so rotated certificates apply to new connections; return a cached context
(one built per call costs that per connection and loses session
resumption). Exclusive with `:ssl-context`.
- `:ssl-need-client-auth` (false) - require a valid client certificate.
- `:ssl-want-client-auth` (false) - request a client certificate without
requiring one.
- `:ssl-alpn-protocols` (none) - ALPN protocol ids offered; default ["h2"
"http/1.1"] with `:http2`, else ["http/1.1"].
- `:ssl-cipher-suites` (none) - enabled cipher suites, nil = JVM default.
- `:ssl-protocols` (none) - enabled TLS protocol versions, nil = JVM default.
- `:ssl-session-cache-size` (0) - server TLS session cache entries, set on
every context the server uses; 0 = JVM default.
HTTP/2:
- `:http2` (false) - serve "h2" over TLS + ALPN (needs `:ssl-context`;
cleartext HTTP/2 is `:http2c`).
- `:http2c` (false) - serve cleartext HTTP/2 with prior knowledge (RFC 9113
§3.3) on a plain listener next to HTTP/1.1: a connection that opens with
the HTTP/2 preface is HTTP/2. No `Upgrade: h2c` (deprecated by RFC 9113).
Not with `:ssl-context` (use `:http2`).
- `:http2-max-concurrent-streams` (100) - SETTINGS_MAX_CONCURRENT_STREAMS.
- `:http2-initial-window-bytes` (262144) - per-stream receive window a stream
starts with, [65535, 2^31-1]; it doubles, up to `:http2-max-window-bytes`,
while the handler reads half of it within two round trips. The connection
window grows to 4x this (1 MiB by default) once request body bytes arrive,
and doubles by the same rule up to twice `:http2-max-window-bytes`, every
growth as far as `:max-buffered-bytes` pays for it. Credit returns as
handlers read.
- `:http2-max-window-bytes` (8388608) - most a stream's receive window grows
to, [`:http2-initial-window-bytes`, 2^31-1]; the connection window grows
to twice this. Equal to `:http2-initial-window-bytes`: fixed windows.
- `:http2-max-frame-bytes` (16384) - SETTINGS_MAX_FRAME_SIZE, [16384,
16777215].
- `:http2-stream-reset-limit` (400) - client RST_STREAMs allowed per
connection, refilling at that many per 30 s; above it ENHANCE_YOUR_CALM
(CVE-2023-44487). 0 = off.
- `:http2-continuation-limit` (64) - CONTINUATION frames per header block.
HTTP/3 (QUIC via quiche):
- `:http3` (false) - serve HTTP/3 over UDP; needs `:http3-cert-path` and
`:http3-key-path`.
- `:http3-port` (0) - UDP port, 0 = the `:port` number. With `:port` 0 set
this (or `:advertise-alt-svc` false): Alt-Svc must name a fixed port.
- `:http3-cert-path` (none) - PEM certificate chain (quiche reads it from
disk).
- `:http3-key-path` (none) - PEM private key.
- `:http3-initial-max-data-bytes` (524288) - initial connection flow-control
window (it autotunes up to twice `:http3-max-window-bytes` on release
shims, stays fixed on a stock libquiche); charged to `:max-buffered-bytes`
while the connection reads a request body. Also bounds request-body bytes
buffered per connection.
- `:http3-initial-max-streams-bidi` (100) - concurrent request streams per
connection; twice this many live handlers make new requests
H3_REQUEST_REJECTED.
- `:http3-initial-max-streams-uni` (8) - peer unidirectional stream credit, at
least 3 (control + QPACK encoder/decoder).
- `:http3-max-udp-payload-bytes` (1350) - max UDP payload, [1200, 65527]; the
default is MTU-safe.
- `:http3-stateless-retry` (false) - make clients prove their address before
any state is allocated (RFC 9000 §8.1.2); for floods.
- `:http3-cert-reload-interval` (10000) - milliseconds between checks of
:http3-cert-path / :http3-key-path for changes; a changed pair is loaded
for new connections, existing ones keep theirs; 0 = off.
- `:http3-retry-threshold` (256) - handshaking connections from which a client
Initial without a valid token gets a stateless Retry.
- `:http3-max-half-open` (1024) - handshaking connections above which new
Initials are dropped; 0 = unlimited.
- `:http3-stream-reset-limit` (400) - peer stream resets allowed per
connection, refilling at that many per 30 s (rapid reset); 0 = off.
- `:http3-event-loops` (0) - event loop threads owning QUIC connections, [0,
64]; 0 = one per core on Linux, 1 elsewhere.
- `:http3-so-rcv-buf-bytes` (4194304) - UDP socket receive buffer requested
(best effort); 0 = OS default.
- `:http3-so-snd-buf-bytes` (4194304) - UDP socket send buffer requested (best
effort); 0 = OS default.
- `:http3-initial-max-stream-data-bidi-local-bytes` (-1) - per-stream window,
-1 = half of `:http3-initial-max-data-bytes` (256 KiB by default), so one
unread stream can't stall the connection.
- `:http3-initial-max-stream-data-bidi-remote-bytes` (-1) - same, for
peer-initiated streams.
- `:http3-initial-max-stream-data-uni-bytes` (-1) - same, for unidirectional
streams.
- `:http3-max-window-bytes` (8388608) - most a stream's receive window
autotunes to, at least the per-stream initial windows; a connection's
window grows to twice this as far as `:max-buffered-bytes` pays. Needs the
patched libquiche of the release builds; ignored on a stock libquiche
(fixed windows).
- `:http3-ack-delay-exponent` (-1) - RFC 9000 ack_delay_exponent, [0, 20]; -1
= quiche default.
- `:http3-max-ack-delay` (-1) - RFC 9000 max_ack_delay in ms as the transport
parameter, [0, 16383]; -1 = quiche default.
- `:http3-active-connection-id-limit` (-1) - RFC 9000
active_connection_id_limit, >= 2; -1 = quiche default.
- `:advertise-alt-svc` (none) - send `Alt-Svc` on HTTP/1.1 and HTTP/2
responses; nil = whenever `:http3` is on. True needs `:http3`.
- `:alt-svc-max-age` (86400) - `ma` of the `Alt-Svc` header, in seconds as the
header parameter.
WebSocket:
- `:ws-max-message-bytes` (1048576) - largest message once reassembled and
decompressed; a larger one closes the connection with 1009. Messages are
buffered whole.
- `:ws-max-queued-bytes` (1048576) - payload bytes asynchronous sends may
queue per connection (plus 64 per frame), 0 = unlimited. A send over it
fails at once (its fail callback runs); one message larger than the limit
is accepted when nothing is queued.
- `:ws-close-timeout` (5000) - ms a closing handshake the server started
(close, failure, idle timeout, shutdown) waits for the client's CLOSE
before dropping TCP; at least 1.
- `:ws-compression` (false) - negotiate permessage-deflate (RFC 7692) when the
client offers it. A compressing connection holds a Deflater and an
Inflater (about 300 KiB of native memory); without the server's context
takeover its Deflater is borrowed from a pool per message.
- `:ws-ping-interval` (0) - ms without a frame from the client after which the
server sends a ping (and again at that interval), 0 = never. The client's
pongs count as frames, so a live but silent client outlasts
`:idle-timeout`; less than `:idle-timeout`.
- `:ws-allowed-origins` (none) - origins allowed to open a WebSocket
(cross-site WebSocket hijacking protection): a collection of origins such
as "https://app.example.com" ("*" for any) or a fn of the `Origin` value.
nil = same origin: the `Origin` scheme and host must match the connection
and the `Host` header. A configured allowlist is a hard gate: a handshake
without `Origin` (not from a browser) is refused. With no allowlist a
handshake without `Origin` is allowed. A refused handshake gets 403.
Handler and observability:
- `:async` (false) - `handler` is a Ring asynchronous handler, `(fn [request
respond raise])`. It runs on the request's virtual thread, which waits
until `respond` or `raise` is called (from any thread), without bound
unless `:handler-timeout` is set; `raise` goes to `:error-handler`. Only
the first call counts: the handler throwing before either is a `raise`,
after one it is logged. A StreamableResponseBody may write after returning
and ends when it closes its stream.
- `:error-handler` (none) - `(fn [request throwable])` returning a Ring
response, when the handler throws or returns nil, on every protocol. If it
throws or returns nil too, a plain 500 is sent.
- `:server-header` (none) - `Server` response header value; nil or empty omits
it. A handler-supplied `Server` wins.
- `:server-events` (none) - map of fns `{:connection-opened (fn [protocol
remote-addr]) :connection-closed (fn [protocol remote-addr nanos])
:request-completed (fn [protocol method status request-bytes
response-bytes nanos]) :protocol-error (fn [protocol kind])}`, or a
`com.s_exp.enso.api.ServerEvents`. `remote-addr` is formatted as
`:remote-addr`. A request head refused before it was parsed is reported
only as a protocol error. Protocols: "http/1.1", "h2", "h2c", "h3";
"websocket" for an upgraded connection's open / close; "tcp" (connection
refused at accept) and "tls" (handshake failures) for protocol errors.
Delivered in order from one dedicated thread, never from a server thread;
past 8192 queued events (a listener that fell behind) new ones are dropped
and counted (`EnsoServer.droppedEvents`, a log warning, a JFR event).
Exceptions are logged and ignored. No cost when absent.
Errors are logged through `java.util.logging` under `com.s_exp.enso.*`:
client-caused failures at FINE, server-side faults at WARNING (rate-limited).(stop server)Stops server. In-flight requests complete, open connections close.
Stops `server`. In-flight requests complete, open connections close.
(write! w data)Buffers data (a String or byte array) into the pending chunk of a
streamed (fn) body. Strings are encoded with the response's charset (the
charset of its Content-Type), UTF-8 when it has none.
Buffers `data` (a String or byte array) into the pending chunk of a streamed (fn) body. Strings are encoded with the response's charset (the `charset` of its Content-Type), UTF-8 when it has none.
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 |