#[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.
Trait Implementations§
Source§impl Clone for ClientConfig
impl Clone for ClientConfig
Source§fn clone(&self) -> ClientConfig
fn clone(&self) -> ClientConfig
1.0.0 (const: unstable) · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
source. Read moreSource§impl Debug for ClientConfig
impl Debug for ClientConfig
Source§impl Default for ClientConfig
impl Default for ClientConfig
impl Eq for ClientConfig
Source§impl PartialEq for ClientConfig
impl PartialEq for ClientConfig
Source§fn eq(&self, other: &ClientConfig) -> bool
fn eq(&self, other: &ClientConfig) -> bool
self and other values to be equal, and is used by ==.