pub struct ServerConfig { /* private fields */ }Expand description
The settings one Streamable HTTP server is built from.
Implementations§
Source§impl ServerConfig
impl ServerConfig
Sourcepub fn new() -> Result<Self, EndpointError>
pub fn new() -> Result<Self, EndpointError>
A configuration with every default: the specification’s endpoint, the
domain’s default frame ceiling, a replay window of
RECOMMENDED_CAPACITY, inline
JSON answers, and the strict security headers.
§Errors
EndpointError, because the default endpoint is parsed rather than
trusted — a const that could be wrong is a bug the caller finds out
about at construction instead of at mount time.
Sourcepub const fn max_concurrency(&self) -> NonZeroU32
pub const fn max_concurrency(&self) -> NonZeroU32
How many requests may be in flight at once.
A ceiling rather than a queue: past this the connection is shed with
503, because a request that waits for a slot has already spent the
caller’s timeout doing nothing. OWASP API Security Top 10:2023,
API4:2023 (Unrestricted Resource Consumption).
Sourcepub const fn with_max_concurrency(self, limit: NonZeroU32) -> Self
pub const fn with_max_concurrency(self, limit: NonZeroU32) -> Self
Sets the in-flight ceiling. A limit of zero is rejected by
NonZeroU32, because a server that accepts nothing is a server that
is up and useless.
Sourcepub const fn request_timeout(&self) -> Duration
pub const fn request_timeout(&self) -> Duration
How long one request may take end to end.
Distinct from stream_timeout, which bounds
the idle gap between events on an already-open stream. This one
bounds the whole request, so a handler that never answers is dropped
rather than holding a connection open forever.
Sourcepub const fn with_request_timeout(self, timeout: Duration) -> Self
pub const fn with_request_timeout(self, timeout: Duration) -> Self
Sets the per-request deadline.
Sourcepub const fn rate_limit(&self) -> &RateLimit
pub const fn rate_limit(&self) -> &RateLimit
The per-subject request allowance.
Sourcepub const fn with_rate_limit(self, limit: RateLimit) -> Self
pub const fn with_rate_limit(self, limit: RateLimit) -> Self
Sets the per-subject allowance.
Sourcepub const fn max_body(&self) -> MaxFrameSize
pub const fn max_body(&self) -> MaxFrameSize
The largest POST body accepted, in bytes.
Enforced before the body is read into memory where the framework allows it, and again on the assembled buffer, because the two limits protect different things: the first the process, the second the framing layer.
Sourcepub const fn replay_capacity(&self) -> NonZeroUsize
pub const fn replay_capacity(&self) -> NonZeroUsize
How many events a session retains for replay.
Sourcepub const fn with_streaming(self, streaming: Streaming) -> Self
pub const fn with_streaming(self, streaming: Streaming) -> Self
Sets the streaming policy.
Sourcepub const fn stream_timeout(&self) -> Duration
pub const fn stream_timeout(&self) -> Duration
How long a server-initiated stream stays open.
Sourcepub const fn with_stream_timeout(self, timeout: Duration) -> Self
pub const fn with_stream_timeout(self, timeout: Duration) -> Self
Sets the stream timeout. A zero timeout closes the stream as soon as its replay window is drained, which is a legitimate way to run a “deliver the backlog, then hang up” endpoint.
Sourcepub const fn keep_alive(&self) -> Duration
pub const fn keep_alive(&self) -> Duration
How long a stream waits before emitting a keep-alive comment.
Sourcepub const fn with_keep_alive(self, keep_alive: Duration) -> Self
pub const fn with_keep_alive(self, keep_alive: Duration) -> Self
Sets the keep-alive interval.
Sourcepub const fn security(&self) -> &SecurityHeaders
pub const fn security(&self) -> &SecurityHeaders
The three security headers.
Sourcepub fn with_security(self, security: SecurityHeaders) -> Self
pub fn with_security(self, security: SecurityHeaders) -> Self
Replaces the security headers.
Sourcepub const fn required_scopes(&self) -> ScopeSet
pub const fn required_scopes(&self) -> ScopeSet
The scopes a stateful request must hold.
Empty by default, and empty is not the same as unauthenticated. An empty requirement is a statement that this endpoint has nothing to protect; a caller that disagrees names the scope it needs and every request that arrives without it is refused. The alternative — a default that demands a scope — would make the transport refuse every request from every deployment that has not yet wired T16’s OAuth, which is a worse failure than serving a read-only endpoint to the public.
What the empty default does guarantee is fail-secure structure rather
than fail-secure policy: an anonymous caller is
AuthContext::anonymous,
which holds no scopes, so the moment a scope is named the anonymous
caller is refused. There is no path by which an unauthenticated request
reaches a scope-bearing route.
Sourcepub const fn with_required_scopes(self, scopes: ScopeSet) -> Self
pub const fn with_required_scopes(self, scopes: ScopeSet) -> Self
Names the scopes a stateful request must hold.
Sourcepub fn resource_metadata(&self) -> Option<&ResourceMetadata>
pub fn resource_metadata(&self) -> Option<&ResourceMetadata>
The RFC 9728 metadata this server publishes, if it publishes one.
None by default, because a server that names no authorization servers
has nothing to publish and an empty document is not the same as no
document.
Setting this does both halves of RFC 9728 at once: the router serves the
document at ResourceUri::metadata_path,
and a 401 carries the WWW-Authenticate challenge
naming that same URL. One value, so the two cannot name different
places.
Sourcepub fn with_resource_metadata(self, metadata: ResourceMetadata) -> Self
pub fn with_resource_metadata(self, metadata: ResourceMetadata) -> Self
Publishes metadata, enabling both the WWW-Authenticate challenge and
the well-known route that serves it.
McpRouter::into_router
mounts the document at the path its resource identifier implies. A
consumer does not merge anything.
§Examples
use protocol_droid::adapters::primary::transport::http::ServerConfig;
use protocol_droid::domain::mcp::{ResourceMetadata, ResourceUri};
let metadata = ResourceMetadata::new(ResourceUri::new("https://example.com/mcp")?)
.with_authorization_server("https://login.example.com");
// The route is mounted at /.well-known/oauth-protected-resource/mcp.
let config = ServerConfig::new()?.with_resource_metadata(metadata);
assert!(config.challenge().is_some());Sourcepub fn challenge(&self) -> Option<String>
pub fn challenge(&self) -> Option<String>
The WWW-Authenticate challenge for a 401, if this server publishes
metadata.
RFC 9728 §5.1: a 401 on a protected resource carries a
WWW-Authenticate naming where the client can discover the
authorization server, and that challenge is the only pointer a client
gets. Without it a client holding an expired token has no URL to fetch
authorization-server metadata from, which is the situation the challenge
exists to solve.
Returns None rather than a bare Bearer when nothing is published:
a challenge naming no resource metadata tells the client the scheme and
nothing else, and a deployment that has not configured a document is
better served by an unadorned 401 that it can recognise as
misconfigured.