reqwest-rest 0.1.0

An opinionated utility to help with creating (and configuring) one-off idiomatic REST API clients.
Documentation
use crate::LoggingRetryableStrategy;
use reqwest::ClientBuilder;
use reqwest_middleware::ClientBuilder as ClientWithMiddlewareBuilder;
use reqwest_retry::{RetryTransientMiddleware, policies::ExponentialBackoff};
use std::time::Duration;

/// Configuration of timeouts and retries for basic http client, with sane defaults
#[derive(Clone, Debug)]
#[non_exhaustive]
pub struct CommonRestConfig {
    /// Fail if we can't connect in this amount of time
    pub connect_timeout: Duration,
    /// Fail if we don't get a response in this amount of time
    pub request_timeout: Duration,
    /// Setting for tcp keepalive interval
    pub tcp_keepalive: Duration,
    /// Max retries for transient or 5xx type errors
    pub max_retries: u32,
    /// Bounds for the exponential backoff retry policy
    pub retry_backoff_bounds: (Duration, Duration),
}

impl Default for CommonRestConfig {
    fn default() -> Self {
        Self {
            connect_timeout: Duration::from_secs(3),
            request_timeout: Duration::from_secs(3),
            tcp_keepalive: Duration::from_secs(60),
            max_retries: 3,
            retry_backoff_bounds: (Duration::from_millis(50), Duration::from_millis(500)),
        }
    }
}

impl CommonRestConfig {
    /// Apply settings from CommonRestConfig to a reqwest::ClientBuilder (which may already have other options set).
    /// Then build a ClientWithMiddleware builder, attach configured RetryTransientMiddleware, and return the builder.
    /// Further middleware may then be attached, and build may be called.
    pub fn apply_to_builder(
        &self,
        builder: ClientBuilder,
    ) -> Result<ClientWithMiddlewareBuilder, reqwest::Error> {
        let client = builder
            .connect_timeout(self.connect_timeout)
            .timeout(self.request_timeout)
            .tcp_keepalive(self.tcp_keepalive)
            .build()?;

        // Attach middleware
        let retry_policy = ExponentialBackoff::builder()
            .retry_bounds(self.retry_backoff_bounds.0, self.retry_backoff_bounds.1)
            .build_with_max_retries(self.max_retries);
        let retry_middleware = RetryTransientMiddleware::new_with_policy_and_strategy(
            retry_policy,
            LoggingRetryableStrategy::default(),
        );

        Ok(ClientWithMiddlewareBuilder::new(client).with(retry_middleware))
    }
}