#[non_exhaustive]pub struct ClientConfig {
pub request_timeout: Duration,
pub max_session_body: u64,
pub max_call_body: u64,
pub max_download_body: u64,
pub max_upload_response_body: u64,
pub max_sse_frame: usize,
pub max_ws_message: usize,
}Expand description
Per-client configuration for timeouts and body size limits.
Use ClientConfig::default() for production defaults (30s timeout, RFC-safe caps).
This type is #[non_exhaustive]: callers outside this crate must use
..ClientConfig::default() when constructing it, allowing new fields to
be added in minor versions without breaking callers.
§Field-type rationale (bd:JMAP-6r7c.21)
The fields mix u64 and usize integer types. The split is deliberate
and tracks the underlying transport’s expectations:
- HTTP body caps are
u64(max_session_body,max_call_body,max_download_body,max_upload_response_body).reqwestreportsContent-Lengthasu64, and an HTTP body can in principle exceedusize::MAXon a 32-bit target (4 GiB) without exceeding 64-bit limits; the cap comparison happens before accumulation. - In-memory frame/message caps are
usize(max_sse_frame,max_ws_message). These bound aVec<u8>orStringthat must fit in process memory;usizeis the address-space-native type and matches whattokio_tungstenite::tungstenite::protocol::WebSocketConfigexpects.
A future minor release MAY consolidate on u64 with internal
usize::try_from casts at the call sites that need it. The mixed-int
API is a small ergonomic cost (callers cannot pass the same untyped
integer literal to both kinds of field without a _u64 or _usize
suffix or as usize cast) traded for transparency to the underlying
transport’s contract.
§No cross-field invariants are enforced
validate() checks each field independently. There is intentionally
no constraint that max_sse_frame <= max_call_body or similar — they
govern different threats (per-frame buffer cap vs whole-body cap)
and a deployment may rationally configure them in either order.
Document the chosen values in your own deployment notes.
Fields (Non-exhaustive)§
This struct is marked as non-exhaustive
Struct { .. } syntax; cannot be matched against without a wildcard ..; and struct update syntax will not work.request_timeout: DurationTimeout for HTTP request/response cycles (fetch_session, call, upload_blob, download_blob).
Does NOT apply to SSE or WebSocket streams (which are indefinite by nature).
Must be > 0. Use Duration::from_secs(30) for a 30-second timeout.
Default: 30 seconds.
Duration::ZERO is forbidden because reqwest::RequestBuilder::timeout
treats Duration::ZERO as “no per-request timeout” (only the
client-level connect_timeout applies), not “instant fail”. A future
maintainer who thinks “zero means disable timeout, why not allow it?”
is reading reqwest’s semantics correctly but missing that this
crate intentionally forbids the no-timeout configuration: a
caller-visible 30-second-by-default timeout protects against
indefinite hangs on stalled servers and against a slowloris-style
resource leak in JMAP clients that hold one outstanding request per
account. validate() enforces the positive-timeout invariant
(bd:JMAP-6r7c.29). Do not relax this without re-deriving the
no-timeout DoS argument.
max_session_body: u64Maximum response body for fetch_session. Default: 1 MiB.
max_call_body: u64Maximum response body for call(). Default: 8 MiB.
max_download_body: u64Maximum response body for download_blob(). Default: 64 MiB.
max_upload_response_body: u64Maximum size in bytes of the JSON response body returned by the
server in reply to upload_blob(). Does NOT cap the size of the
blob being uploaded — the JMAP server enforces that via its
maxSizeUpload capability. This field caps only the small JSON
envelope the server returns describing the stored blob.
Default: 1 MiB.
max_sse_frame: usizeMaximum byte length of a single SSE frame; applied independently to the raw incoming bytes (pre-UTF-8 decode) and the decoded text. Protects against memory exhaustion from a hostile or misbehaving server that sends a single very large frame. Must be > 0. Default: 1 MiB.
Memory residency note (bd:JMAP-6lsm.7): because the raw byte
buffer and the decoded text buffer are tracked separately, the
in-flight footprint while parsing a single frame can momentarily
reach ~2 × max_sse_frame (raw bytes accumulated up to the limit,
plus decoded text not yet drained). If you tune this value for a
tight memory budget, plan for that 2× peak. The independent
tracking is correct for the streaming UTF-8 decoder (which needs
the raw buffer to be at least one full frame to handle split
multi-byte sequences across HTTP chunks); see decode_utf8_chunk
for the rationale.
max_ws_message: usizeMaximum byte length of a single WebSocket message (and frame). Mirrors
max_sse_frame for the WebSocket transport. Used by
JmapClient::connect_ws_session and threaded through to
tokio_tungstenite::tungstenite::protocol::WebSocketConfig’s
max_message_size and max_frame_size. Must be > 0.
Default: 1 MiB. (bd:JMAP-6lsm.5)
Implementations§
Source§impl ClientConfig
impl ClientConfig
Sourcepub fn validate(&self) -> Result<(), ClientError>
pub fn validate(&self) -> Result<(), ClientError>
Validate that all config fields satisfy their constraints.
Called automatically by JmapClient::new. Callers may also call
this directly to pre-validate a config before passing it to the
constructor.
§Errors
Returns ClientError::InvalidArgument when any field is zero or
out-of-range.