icap-rs
icap-rs is a Rust library for ICAP/1.0 clients and services, guided by
RFC 3507. It provides protocol
types, parsers, serializers, a Tokio-based client, and a Tokio-based server
API.
The crate focuses on explicit protocol behavior. Strict parsing is the default, wire framing follows RFC 3507, and compatibility behavior is opt-in where it exists.
Feature Flags
| Feature | Default | Purpose |
|---|---|---|
tls-rustls |
No | Enables direct ICAPS (icaps://) client connections and TLS/mTLS server listeners through Rustls. Bundles the ring crypto provider as the default backend. |
tls-rustls-aws-lc-rs |
No | Additive opt-in: also compiles the aws-lc-rs crypto provider and prefers it at runtime. Implies tls-rustls, so code gated on feature = "tls-rustls" keeps working. |
Without tls-rustls, plaintext icap:// clients and servers are available.
Quick Start: Client
use ;
async
To inspect the exact request bytes without sending them:
use ;
Quick Start: Server
use ;
const ISTAG: &str = "example-1.0";
async
OPTIONS responses are generated automatically per service. The router injects
the advertised Methods value from registered routes and can inherit
Max-Connections from the server limit.
Preview Flow
Preview is represented explicitly on both the client and server paths:
outbound [Request] values configure the wire behavior, [Client::send] and
[Client::send_streaming_reader] drive the client-side handshake, and server
routes receive [IncomingRequest] values that may expose [Body::Preview] to
preview-aware handlers.
Client requests can set Preview: N with [Request::preview] and optionally
send ieof for Preview: 0 with [Request::preview_ieof]:
use ;
use ;
Server handlers normally receive the request after the full body is available.
If a [ServerBuilder::route_reqmod], [ServerBuilder::route_respmod], or
[ServerBuilder::route] handler returns [IcapResult<PreviewDecision>], it is
preview-aware: the server can call it after preview bytes arrive and before
sending ICAP/1.0 100 Continue.
use ;
const ISTAG: &str = "preview-1.0";
async
Returning [PreviewDecision::Continue] resumes the RFC preview flow: the
server sends 100 Continue, reads the remainder, and dispatches the full
request. Returning [PreviewDecision::Respond] sends the supplied final
[Response] immediately. Services advertise the preview window in generated
OPTIONS responses with [ServiceOptions::with_preview].
Modifying an HTTP Request
For REQMOD, return 200 OK with an embedded HTTP request when the service
changes the request. Return 204 No Content only when no modification is
needed and the client allows that response.
This example adds an HTTP header to the encapsulated request and preserves the original body:
use Request as HttpRequest;
use ;
const ISTAG: &str = "reqmod-edit-1.0";
async
Streaming Bodies
The client can stream request bodies from an AsyncRead using
[Client::send_streaming_reader] and ICAP chunked framing. The final ICAP
response is parsed into [Response].
use ;
use ;
async
Embedded HTTP
REQMOD can encapsulate an HTTP request, and RESPMOD can encapsulate an HTTP
response. The ICAP serializer computes Encapsulated offsets and writes the
embedded HTTP head unchunked. Only the encapsulated entity body is encoded with
ICAP chunked framing.
use ;
use ;
ICAP Header Values
ICAP headers use http::HeaderValue validation. ASCII comma-separated values
such as X-TEST: test1, test2, test3 are accepted and preserved as one header
value. The crate only applies list semantics for headers with explicit protocol
logic, such as Allow. Custom header list parsing belongs to caller code
because comma handling is header-specific.
use ;
RFC 3507 Behavior
Hostis required on incoming ICAP requests.Encapsulatedis required by the strict parser and validated for duplicate, non-monotonic, and method-incompatible forms.OPTIONS,REQMOD, andRESPMODare supported.PreviewsupportsPreview: 0,Preview: N,ieof, and100 Continue.- Successful ICAP responses require a valid
ISTag. Outgoing responses always serialize it as the RFC 3507 quoted-string form, soResponse::no_content_with_istag("QUJD+/8=")writesISTag: "QUJD+/8=". Incoming response parsing is intentionally more permissive and accepts unquoted token/base64-like values for compatibility with existing ICAP servers. - Incoming client response parsing accepts legacy
204 No Contentresponses withoutEncapsulatedas equivalent toEncapsulated: null-body=0, matching c-icap behavior. - Server
ServiceOptionsnever invents a defaultISTag. Every route must configure one explicitly withwith_static_istag(...)orwith_istag_provider(...), because the tag is service policy metadata and should not be silently chosen by the framework. 204 No Contentis serialized asEncapsulated: null-body=0and must not carry body bytes.- Server handlers that return
204are guarded: if the request has neitherAllow: 204nor Preview, the server returns200 OKand echoes the embedded HTTP message instead. Allow: 206no-modification responses use theuse-original-bodymarker.- Keep-alive is supported without request pipelining.
- Client-side OPTIONS caching,
Transfer-*policy (§4.10.2), andProxy-Authorizationretry on407(§7.1) are opt-in onClientBuilderviawith_options_cacheandproxy_auth; without them the client never fetchesOPTIONSautomatically. - ICAP request and response header block limits default to 64 KiB and are
configurable via
ServerBuilder::with_request_header_limitandClientBuilder::with_response_header_limit. Oversized headers are rejected as protocol-level failures (400 Bad Requeston the server path). - Embedded HTTP object-size limits are configured per service with
ServiceOptions::with_max_object_size. The value is advertised inOPTIONSasMax-Object-Size; request handling counts the actual decoded ICAP chunked body bytes for that service and does not trust embedded HTTPContent-Lengthfor enforcement.
For the detailed support matrix and known gaps, see
docs/rfc3507.md.
Public API Map
Most applications can import the main API directly from the crate root:
use ;
| Type | Use it for |
|---|---|
[Client], [ClientBuilder], [ConnectionPolicy] |
Connecting to an ICAP service, configuring host/port or icap:// / icaps:// URI, keep-alive, timeouts, response header limits, default headers, and streaming sends. |
[Request], [Method] |
Building outbound OPTIONS, REQMOD, and RESPMOD requests for client send/build APIs. |
[IncomingRequest] |
Inspecting server-side ICAP requests in route handlers. ICAP metadata is read-only; services may mutate or consume only the embedded HTTP message. |
[Response], [StatusCode] |
Building ICAP responses, parsing raw responses, validating ISTag, serializing RFC-compatible wire bytes, and attaching embedded HTTP messages. |
[Server], [ServerBuilder] |
Running a Tokio ICAP service with per-service routes, aliases, default service routing, request header limits, connection limits, TLS/mTLS, and automatic OPTIONS. |
[ServiceOptions], [TransferBehavior] |
Describing per-service OPTIONS capabilities: Methods, Service, explicit ISTag, Allow, Preview, Max-Object-Size, Transfer-*, Options-TTL, and optional opt-body. |
[Body], [EmbeddedHttp] |
Inspecting embedded HTTP request/response heads and bodies in server handlers. Regular handlers receive [Body::Full]; preview-aware handlers may receive [Body::Preview]. |
[PreviewDecision] |
Returning an early final response from a preview-aware route, or continuing the RFC preview flow. |
[Error], [IcapResult] |
Handling protocol, parsing, serialization, network, service, and handler errors without converting them into generic I/O errors. |
[OptionsCacheConfig], [ProxyAuth] |
Opt-in client-side OPTIONS caching (ClientBuilder::with_options_cache, RFC 3507 §4.10 / §5 plus Transfer-* policy §4.10.2) and Proxy-Authorization retry on 407 (ClientBuilder::proxy_auth, §7.1). |
[ClientTimeouts], [ServerTimeouts] |
Tuning client- and server-side network deadlines (connect, write, idle keep-alive, Preview continue, body/header read, shutdown drain). |
[ShutdownEvent], [HandlerError], [HandlerResult] |
Observing graceful-shutdown drain progress (ServerBuilder::on_shutdown_event) and returning structured errors from route handlers. |
[IsTagHandle] |
Rotating a dynamic ISTag from a background task while handlers read the current value. |
Submodules are still public for discoverability and namespacing:
icap_rs::client, icap_rs::request, icap_rs::response,
icap_rs::server, and icap_rs::error. Prefer the crate-root imports above
for normal application code.
Server route handlers receive [IncomingRequest], not the outbound [Request]
builder. This intentionally prevents services from rewriting the ICAP request
line, ICAP headers, preview state, or Allow flags after parsing. If a service
needs to adapt traffic, it should return a [Response] with an embedded HTTP
request/response, or use [IncomingRequest::embedded_mut] /
[IncomingRequest::into_embedded] to work with the encapsulated HTTP data.
Common API Examples
Send an OPTIONS request with [Client::send] and [Request::options]:
use ;
async
Configure client-side network deadlines through [ClientBuilder]:
use Duration;
use Client;
let client = builder
.with_uri?
.timeout
.connect_timeout
.write_timeout
.continue_timeout
.with_response_header_limit
.try_build?;
# Ok::
Build a buffered REQMOD request with [Request::reqmod] and
[Request::with_http_request]:
use Request as HttpRequest;
use ;
Build a buffered RESPMOD request with [Request::respmod] and
[Request::with_http_response]:
use ;
use ;
Use [Request::try_new] when the ICAP method comes from dynamic input:
use Request as HttpRequest;
use ;
Stream a large body by pairing [Request::with_http_request_head] with
[Client::send_streaming_reader]:
use Request as HttpRequest;
use ;
async
Return no modification with [Response::no_content_with_istag], or return an
adapted HTTP message with [Response::ok_with_istag] and
[Response::with_http_response]:
use ;
use ;
Run a service with [Server::builder], [ServerBuilder::route_reqmod], and
[ServiceOptions]:
use ;
const ISTAG: &str = "policy-1";
async
TLS and ICAPS
Enable Rustls support with:
= { = "0.3.0", = ["tls-rustls"] }
Then use icaps:// URIs for clients, and ServerBuilder::with_tls plus a
ServerTlsConfig on the server side (use
with_client_auth_pem for in-memory PEM data or with_client_auth_pem_file
for file paths). On the client, customise TLS via
ClientTlsConfig and ClientBuilder::with_tls.
See the icap_rs::tls module documentation for the full guide.
Examples