Skip to main content

ServerBuilder

Struct ServerBuilder 

Source
pub struct ServerBuilder { /* private fields */ }
Expand description

Builder for Server.

Construct with Server::builder, register routes, then call ServerBuilder::build.

Duplicate registration of the same (service, method) panics with a clear error, like axum.

Implementations§

Source§

impl ServerBuilder

Source

pub fn bind(self, addr: &str) -> Self

Set the bind address, e.g. "127.0.0.1:1344".

Source

pub fn with_max_connections(self, n: usize) -> Self

Limit the number of concurrent connections accepted by the server.

The value is also advertised in OPTIONS as Max-Connections if not explicitly configured on the per-service options.

Source

pub const fn with_timeouts(self, timeouts: ServerTimeouts) -> Self

Install a full ServerTimeouts configuration in one shot.

See ServerTimeouts for the meaning of each field. Defaults are None (no timeout); fields not set on the supplied value disable the corresponding deadline.

Source

pub const fn with_request_header_limit(self, bytes: usize) -> Self

Set the maximum ICAP request header block size, in bytes.

The limit includes the request line, all ICAP header lines, and the terminating CRLFCRLF. The default is 64 KiB. Oversized request headers receive 400 Bad Request and the connection is closed.

Source

pub fn on_shutdown_event<F>(self, handler: F) -> Self
where F: Fn(ShutdownEvent) + Send + Sync + 'static,

Register a callback that is called with ShutdownEvent during graceful shutdown.

The handler runs synchronously inside the accept loop task — keep it fast. Use it for custom logging, metrics, or alerting. When not set, the server logs via tracing::warn by default.

§Example
use icap_rs::{IcapResult, Server, ShutdownEvent};

#[tokio::main]
async fn main() -> IcapResult<()> {
    let server = Server::builder()
        .bind("127.0.0.1:1344")
        .on_shutdown_event(|event| match event {
            ShutdownEvent::Draining { active_connections, drain_timeout } => {
                eprintln!("[shutdown] {active_connections} connection(s) still active");
                if let Some(d) = drain_timeout {
                    eprintln!("[shutdown] force-close in {d:.1?}");
                }
            }
            ShutdownEvent::DrainTimedOut { remaining_connections } => {
                eprintln!("[shutdown] timed out, cancelling {remaining_connections}");
            }
            _ => {}
        })
        .build()
        .await?;

    server.run_until(async { tokio::signal::ctrl_c().await.ok(); }).await
}
Source

pub const fn with_compatibility_request_parser(self) -> Self

Enable legacy compatibility request parsing.

Strict RFC parsing is the default and requires every ICAP request, including OPTIONS, to carry an Encapsulated header. This opt-in mode accepts legacy OPTIONS requests without Encapsulated.

Source

pub fn route<MIt, MItem, F, Fut>( self, service: &str, methods: MIt, handler: F, options: Option<ServiceOptions>, ) -> Self
where MIt: IntoIterator<Item = MItem>, MItem: Into<Method>, F: Fn(IncomingRequest) -> Fut + Send + Sync + 'static, Fut: Future + Send + 'static, Fut::Output: RouteOutput,

Register a service route for one or more ICAP methods.

  • Each service must have a ServiceOptions value with an explicit ISTag; routes without options are rejected by build.
  • Multiple calls to .route(..) for the same service are allowed as long as methods do not overlap.
  • Registering the same method for the same service twice will panic! with a clear message.
  • The same handler can be reused for multiple methods in a single call.
  • Return IcapResult<PreviewDecision> from the handler to make the route preview-aware. Such handlers are called with Body::Preview after preview bytes arrive and before the server sends 100 Continue. Returning PreviewDecision::Continue resumes the RFC preview flow; the same handler is called again with Body::Full after the remainder is read.
§Handler invocation and the Allow: 204 header

The handler is always called for every REQMOD/RESPMOD request.

RFC 3507 §4.6 prohibits the server from returning 204 No Content unless the client explicitly advertised Allow: 204. When the handler returns 204 but the client did not send Allow: 204, the server automatically converts the response: it echoes the original embedded HTTP message in a 200 OK (or 206 Partial Content if Allow: 206 was sent).

This means handlers can always return Response::no_content() to signal “no modification needed” — the server takes care of the RFC-compliant wrapping regardless of what the client advertised.

Source

pub fn route_reqmod<F, Fut>( self, service: &str, handler: F, options: Option<ServiceOptions>, ) -> Self
where F: Fn(IncomingRequest) -> Fut + Send + Sync + 'static, Fut: Future + Send + 'static, Fut::Output: RouteOutput,

Register a route for REQMOD only.

Convenience wrapper around route with methods = [Method::ReqMod]. See route for full semantics, including panic conditions on duplicate (service, method) registration and ServiceOptions requirements.

§Panics

Panics if a REQMOD handler for the same service was already registered, or if options is provided more than once for the same service.

Source

pub fn route_respmod<F, Fut>( self, service: &str, handler: F, options: Option<ServiceOptions>, ) -> Self
where F: Fn(IncomingRequest) -> Fut + Send + Sync + 'static, Fut: Future + Send + 'static, Fut::Output: RouteOutput,

Register a route for RESPMOD only.

Convenience wrapper around route with methods = [Method::RespMod]. See route for full semantics, including panic conditions on duplicate (service, method) registration and ServiceOptions requirements.

§Panics

Panics if a RESPMOD handler for the same service was already registered, or if options is provided more than once for the same service.

Source

pub fn alias(self, from: &str, to: &str) -> Self

Add an alias for a service path: fromto.

Both ends are normalized to canonical request paths, so "scan" and "/scan" refer to the same route. Useful to make the root path behave like an existing service:

let builder = Server::builder()
    .alias("/", "scan"); // "icap://host:1344/" is routed to "/scan"

Notes:

  • Aliases are applied after default_service is considered for the root (“/”) path.
  • Up to 4 alias rewrites are applied to avoid cycles.
Source

pub fn default_service(self, svc: &str) -> Self

Set a default service for the root (“/”) path (e.g. "scan").

The value is normalized to a canonical request path, so "scan" and "/scan" are equivalent.

Example:

let builder = Server::builder()
    .default_service("scan");

If a client sends icap://host:1344/ or an empty service, requests are internally routed to the specified service path.

Source

pub fn with_task_tracker(self, tracker: TaskTracker) -> Self

Register a TaskTracker for user-owned background tasks.

After all active connections drain following a shutdown signal, the server calls TaskTracker::close on the tracker and waits for all tracked tasks to finish before returning from Server::run_until.

If a drain timeout is configured via ServerTimeouts::with_shutdown_drain, the remaining budget is shared: once the drain deadline fires the tracker wait is skipped and the server returns immediately.

§Example
use icap_rs::{IcapResult, Server};
use tokio_util::task::TaskTracker;

#[tokio::main]
async fn main() -> IcapResult<()> {
    let tracker = TaskTracker::new();

    // Spawn a background task and track it so the server waits for it on shutdown.
    tracker.spawn(async {
        // background work ...
    });

    let server = Server::builder()
        .bind("127.0.0.1:1344")
        .with_task_tracker(tracker)
        .build()
        .await?;

    server.run_until(async { tokio::signal::ctrl_c().await.ok(); }).await
}
Source

pub async fn build(self) -> IcapResult<Server>

Finalize the builder and create a Server.

Trait Implementations§

Source§

impl Default for ServerBuilder

Source§

fn default() -> Self

Returns the “default value” for a type. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more