use core::time::Duration;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum RedirectAuthHeaders {
Never,
SameHost,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum RedirectPolicy {
Follow,
FollowReturnLast,
NoFollow,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum HttpStatusHandling {
AsError,
AsResponse,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ProtocolRestriction {
HttpsOnly,
Any,
}
#[derive(Debug, Clone)]
pub struct Config {
pub timeout: Option<Duration>,
pub user_agent: Option<alloc::string::String>,
pub redirect_policy: RedirectPolicy,
pub max_redirects: u32,
pub http_status_handling: HttpStatusHandling,
pub redirect_auth_headers: RedirectAuthHeaders,
pub max_response_header_size: usize,
pub timeout_connect: Option<Duration>,
pub timeout_read: Option<Duration>,
pub accept: Option<alloc::string::String>,
pub protocol_restriction: ProtocolRestriction,
pub connection_pooling: bool,
pub max_idle_per_host: usize,
pub idle_timeout: Option<Duration>,
pub max_uri_length: Option<usize>,
}
impl Default for Config {
fn default() -> Self {
Self {
timeout: None,
user_agent: Some(alloc::string::String::from("barehttp/1.0")),
redirect_policy: RedirectPolicy::Follow,
max_redirects: 10,
http_status_handling: HttpStatusHandling::AsError,
redirect_auth_headers: RedirectAuthHeaders::Never,
max_response_header_size: 64 * 1024,
timeout_connect: None,
timeout_read: None,
accept: Some(alloc::string::String::from("*/*")),
protocol_restriction: ProtocolRestriction::Any,
connection_pooling: true,
max_idle_per_host: 5,
idle_timeout: Some(Duration::from_secs(90)),
max_uri_length: Some(8192), }
}
}
pub struct ConfigBuilder {
config: Config,
}
impl ConfigBuilder {
#[must_use]
pub fn new() -> Self {
Self {
config: Config::default(),
}
}
#[must_use]
pub const fn timeout(
mut self,
duration: Duration,
) -> Self {
self.config.timeout = Some(duration);
self
}
#[must_use]
pub fn user_agent(
mut self,
agent: impl Into<alloc::string::String>,
) -> Self {
self.config.user_agent = Some(agent.into());
self
}
#[must_use]
pub const fn redirect_policy(
mut self,
policy: RedirectPolicy,
) -> Self {
self.config.redirect_policy = policy;
self
}
#[must_use]
pub const fn max_redirects(
mut self,
max: u32,
) -> Self {
self.config.max_redirects = max;
self
}
#[must_use]
pub const fn http_status_handling(
mut self,
handling: HttpStatusHandling,
) -> Self {
self.config.http_status_handling = handling;
self
}
#[must_use]
pub const fn redirect_auth_headers(
mut self,
policy: RedirectAuthHeaders,
) -> Self {
self.config.redirect_auth_headers = policy;
self
}
#[must_use]
pub const fn max_response_header_size(
mut self,
size: usize,
) -> Self {
self.config.max_response_header_size = size;
self
}
#[must_use]
pub const fn timeout_connect(
mut self,
duration: Duration,
) -> Self {
self.config.timeout_connect = Some(duration);
self
}
#[must_use]
pub const fn timeout_read(
mut self,
duration: Duration,
) -> Self {
self.config.timeout_read = Some(duration);
self
}
#[must_use]
pub fn accept(
mut self,
value: impl Into<alloc::string::String>,
) -> Self {
self.config.accept = Some(value.into());
self
}
#[must_use]
pub const fn protocol_restriction(
mut self,
restriction: ProtocolRestriction,
) -> Self {
self.config.protocol_restriction = restriction;
self
}
#[must_use]
pub const fn connection_pooling(
mut self,
enabled: bool,
) -> Self {
self.config.connection_pooling = enabled;
self
}
#[must_use]
pub const fn max_idle_per_host(
mut self,
max: usize,
) -> Self {
self.config.max_idle_per_host = max;
self
}
#[must_use]
pub const fn idle_timeout(
mut self,
duration: Duration,
) -> Self {
self.config.idle_timeout = Some(duration);
self
}
#[must_use]
pub fn build(self) -> Config {
self.config
}
}
impl Default for ConfigBuilder {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn config_default_values() {
let config = Config::default();
assert!(config.timeout.is_none());
assert_eq!(config.user_agent, Some(alloc::string::String::from("barehttp/1.0")));
assert_eq!(config.redirect_policy, RedirectPolicy::Follow);
assert_eq!(config.max_redirects, 10);
assert_eq!(config.http_status_handling, HttpStatusHandling::AsError);
assert_eq!(config.redirect_auth_headers, RedirectAuthHeaders::Never);
assert_eq!(config.max_response_header_size, 64 * 1024);
assert!(config.timeout_connect.is_none());
assert!(config.timeout_read.is_none());
assert_eq!(config.accept, Some(alloc::string::String::from("*/*")));
assert_eq!(config.protocol_restriction, ProtocolRestriction::Any);
}
#[test]
fn config_builder_timeout() {
let config = ConfigBuilder::new()
.timeout(Duration::from_secs(30))
.build();
assert_eq!(config.timeout, Some(Duration::from_secs(30)));
}
#[test]
fn config_builder_user_agent() {
let config = ConfigBuilder::new().user_agent("MyClient/1.0").build();
assert_eq!(config.user_agent, Some(alloc::string::String::from("MyClient/1.0")));
}
#[test]
fn config_builder_redirect_policy() {
let config = ConfigBuilder::new()
.redirect_policy(RedirectPolicy::NoFollow)
.build();
assert_eq!(config.redirect_policy, RedirectPolicy::NoFollow);
}
#[test]
fn config_builder_max_redirects() {
let config = ConfigBuilder::new().max_redirects(5).build();
assert_eq!(config.max_redirects, 5);
}
#[test]
fn config_builder_http_status_handling() {
let config = ConfigBuilder::new()
.http_status_handling(HttpStatusHandling::AsResponse)
.build();
assert_eq!(config.http_status_handling, HttpStatusHandling::AsResponse);
}
#[test]
fn config_builder_chaining() {
let config = ConfigBuilder::new()
.timeout(Duration::from_secs(10))
.user_agent("Test/1.0")
.max_redirects(3)
.http_status_handling(HttpStatusHandling::AsResponse)
.build();
assert_eq!(config.timeout, Some(Duration::from_secs(10)));
assert_eq!(config.max_redirects, 3);
assert_eq!(config.http_status_handling, HttpStatusHandling::AsResponse);
}
#[test]
fn config_builder_protocol_restriction() {
let config = ConfigBuilder::new()
.protocol_restriction(ProtocolRestriction::HttpsOnly)
.build();
assert_eq!(config.protocol_restriction, ProtocolRestriction::HttpsOnly);
}
#[test]
fn config_builder_timeouts() {
let config = ConfigBuilder::new()
.timeout_connect(Duration::from_secs(5))
.timeout_read(Duration::from_secs(30))
.build();
assert_eq!(config.timeout_connect, Some(Duration::from_secs(5)));
assert_eq!(config.timeout_read, Some(Duration::from_secs(30)));
}
#[test]
fn config_builder_accept_header() {
let config = ConfigBuilder::new().accept("application/json").build();
assert_eq!(config.accept, Some(alloc::string::String::from("application/json")));
}
}