Expand description
Token-bucket rate limiter as a ServerInterceptor.
Provides RateLimitInterceptor, a ready-made interceptor that limits
request throughput per caller. The caller key is derived from
CallContext::caller_identity; for unauthenticated callers behind a
trusted reverse proxy, the client IP can be taken from x-forwarded-for
(see RateLimitConfig::trusted_proxy_hops).
§Example
use std::sync::Arc;
use a2a_protocol_server::rate_limit::{RateLimitInterceptor, RateLimitConfig};
let limiter = Arc::new(
RateLimitInterceptor::new(RateLimitConfig {
requests_per_window: 100,
window_secs: 60,
..RateLimitConfig::default()
})
.expect("valid rate limit config"),
);Then add it to the handler builder:
let handler = RequestHandlerBuilder::new(executor)
.with_interceptor(limiter)
.build()?;§Caller identity
The per-caller key is derived in this order:
CallContext::caller_identity— set by an authentication interceptor. This is the recommended source: it cannot be forged by the client.- The client IP from
x-forwarded-for, only whenRateLimitConfig::trusted_proxy_hopsis non-zero. The header is client-controlled, so by default (trusted_proxy_hops == 0) it is ignored entirely — otherwise a caller could evade the limit by forging a fresh address on every request. - A shared
"anonymous"key. All remaining callers share one budget, which keeps the limit enforceable (fail-closed) at the cost of granularity.
§Design
Uses a fixed-window counter per caller key. Windows are aligned to wall
clock seconds. When a request exceeds the per-window limit, the before
hook returns an error. A2A / JSON-RPC define no dedicated throttling code,
so this surfaces as an internal error (-32603) whose message names the
rate limit; the request is rejected. (If you need a distinct client-visible
signal for backoff, wrap this in a transport adapter that maps the message
to your preferred status — e.g. HTTP 429.)
The bucket map is bounded by RateLimitConfig::max_buckets. When the
map is full and stale buckets cannot be evicted, requests from new
callers are rejected until capacity frees up (fail-closed).
For production deployments requiring sliding windows, distributed counters,
or more sophisticated algorithms, implement a custom ServerInterceptor
or use a reverse proxy (nginx, Envoy).
Structs§
- Rate
Limit Config - Configuration for
RateLimitInterceptor. - Rate
Limit Interceptor - A fixed-window rate limiting
ServerInterceptor.
Constants§
- DEFAULT_
MAX_ BUCKETS - Default cap on the number of tracked caller buckets.