Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
70 commits
Select commit Hold shift + click to select a range
13e0cfd
update test_https to use local http server
APonce911 Feb 9, 2026
e879020
add test_https_with_client
APonce911 Feb 9, 2026
182e5fb
WIP: add ClientBuilder for configuring Client instances
APonce911 Feb 11, 2026
92ca975
WIP: pass ClientConfig struct to tls layer
APonce911 Feb 12, 2026
5af7539
WIP: include feature on ClientConfig import
APonce911 Feb 12, 2026
b6650ba
WIP append custom cert
APonce911 Feb 12, 2026
bf1735d
WIP: update tests
APonce911 Feb 12, 2026
da22cf9
rename TlsConfig cert attribute
APonce911 Feb 12, 2026
a01979a
remove comment
APonce911 Feb 12, 2026
d89f559
style adjustment
APonce911 Feb 12, 2026
04a1c3a
add example
APonce911 Feb 12, 2026
7011045
Code review adjustment: Use AsyncConnection::new instead of new_with_…
APonce911 Feb 13, 2026
deb5397
WIP: include certificates on TlsConfig struct
APonce911 Feb 13, 2026
5a97e80
style adjustment
APonce911 Feb 13, 2026
a61b1ec
make rustls_stream mod public temporarily
APonce911 Feb 13, 2026
8000c6c
WIP: create Certificates wrapper on rustls_stream mod
APonce911 Feb 13, 2026
9b4a839
WIP use custom error when appending a certificate
APonce911 Feb 13, 2026
8d7ff6c
WIP remove moved code
APonce911 Feb 13, 2026
0538906
WIP remove unused field from TlsConfig
APonce911 Feb 13, 2026
5fc031b
add Certificates module
APonce911 Feb 13, 2026
9c11211
adjust privacy on structs
APonce911 Feb 13, 2026
22c2b2f
add new docs
APonce911 Feb 13, 2026
97b5d56
update doc and example
APonce911 Feb 13, 2026
4a08c7d
remove comment
APonce911 Feb 13, 2026
2cf40aa
Adjust custom_cert example feature
APonce911 Feb 16, 2026
937e3ba
fix: correct feature flag for CustomClientConfig import
APonce911 Feb 16, 2026
e682f92
List adjustments
APonce911 Feb 16, 2026
fd47ea1
fix Cargo fmt adjustments
APonce911 Feb 16, 2026
728ceb4
Rename `certificate` to `cert_der`
APonce911 Feb 16, 2026
c9d941d
take ownership of cert_der on append_certificate
APonce911 Feb 16, 2026
0f67697
rename parameter cert_der on Doc for with_root_certificate
APonce911 Feb 16, 2026
65e7c41
Reuse existing TLSConfig if possible - allows for multiple certificat…
APonce911 Feb 16, 2026
ecf4d12
Update TlsConfig::new and ClientBuilder::with_root_certificate to ret…
APonce911 Feb 17, 2026
c11c920
Update custom_cert example with new return from with_root_certificate…
APonce911 Feb 18, 2026
89153aa
Native-tls https adjustments
APonce911 Feb 18, 2026
86e01c1
add new tests
APonce911 Feb 18, 2026
b5c2108
WIP: native-tls client builder setup
APonce911 Feb 18, 2026
cc84c5c
Fix test flags
APonce911 Feb 19, 2026
abf89cf
warnings fix
APonce911 Feb 19, 2026
cccf43a
Merge with branch bitreq-client-builder
APonce911 Feb 19, 2026
8936da6
style adjustment
APonce911 Feb 19, 2026
315a3c8
warnings fix
APonce911 Feb 19, 2026
e8f47b6
fix: unresolved import - Refactor client mod to allow cleaner conditi…
APonce911 Feb 19, 2026
97124a9
Gate tls modules declarations
APonce911 Feb 19, 2026
8382cc1
Fix doctest
APonce911 Feb 19, 2026
2726d13
Merge branch 'bitreq-client-builder' into bitreq-client-builder-nativ…
APonce911 Feb 19, 2026
5bb6df5
Merge branch bitreq-client-builder
APonce911 Feb 19, 2026
d3470a9
Improve error handling
APonce911 Feb 21, 2026
05f8e5d
remove connector caching with client_config - always create new conne…
APonce911 Feb 21, 2026
2ef687e
Improve code organization: Move Client and ClientImpl declarations be…
APonce911 Feb 22, 2026
bfe2763
wrap ClientConfig with Arc smart pointer to reduce memory usage
APonce911 Feb 22, 2026
b2fe156
Merge branch 'master' into bitreq-client-builder
APonce911 Feb 22, 2026
8c5c9f4
Merge branch ´bitreq-client-builder´
APonce911 Feb 22, 2026
1a6fcc3
remove connector caching with client_config - create new connector
APonce911 Feb 22, 2026
f3851fb
use Arc clone instead of option clone
APonce911 Feb 22, 2026
53391ef
Merge branch 'bitreq-client-builder' into bitreq-client-builder-nativ…
APonce911 Feb 22, 2026
6ac03d7
Wrap Certificates with arc and inject from Client - load root_certs o…
APonce911 Feb 23, 2026
66769f4
Merge branch 'bitreq-client-builder' into bitreq-client-builder-nativ…
APonce911 Feb 23, 2026
0ccf01a
Adjust gates on Client
APonce911 Feb 23, 2026
c6d6ccd
format adjustment
APonce911 Feb 23, 2026
bf3a952
bump default Client default connection pool(capacity) to 10
APonce911 Feb 23, 2026
5d0d0c1
Merge branch 'bitreq-client-builder' into bitreq-client-builder-nativ…
APonce911 Feb 23, 2026
694adbf
native-tls support for multiple certs - make client builder method fa…
APonce911 Feb 24, 2026
005e00a
update example
APonce911 Feb 24, 2026
908a8cf
fix example feature gates
APonce911 Feb 24, 2026
67328f7
code review adjustment: remove example
APonce911 Mar 3, 2026
6194faa
code review adjustment: remove tls:enabled and tls:disabled modules
APonce911 Mar 3, 2026
27eac43
code review adjustment: reduce docs
APonce911 Mar 3, 2026
503702b
Implement disable_default_certificates function for client builder
APonce911 Mar 5, 2026
ee83cc2
code review adjustment: streamline code and reduce complexity to avoi…
APonce911 Mar 6, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
187 changes: 185 additions & 2 deletions bitreq/src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,182 @@
use std::collections::{hash_map, HashMap, VecDeque};
use std::sync::{Arc, Mutex};

#[cfg(any(
all(feature = "native-tls", feature = "tokio-native-tls"),
all(feature = "rustls", feature = "tokio-rustls")
))]
use crate::connection::certificates::{Certificates, CertificatesBuilder};
use crate::connection::AsyncConnection;
use crate::request::{OwnedConnectionParams as ConnectionKey, ParsedRequest};
use crate::{Error, Request, Response};

#[derive(Clone)]
pub(crate) struct ClientConfig {
#[cfg(any(
all(feature = "native-tls", feature = "tokio-native-tls"),
all(feature = "rustls", feature = "tokio-rustls")
))]
pub(crate) tls: Option<TlsConfig>,
}

#[cfg(any(
all(feature = "native-tls", feature = "tokio-native-tls"),
all(feature = "rustls", feature = "tokio-rustls")
))]
#[derive(Clone)]
pub(crate) struct TlsConfig {
pub(crate) certificates: Certificates,
}

#[cfg(any(
all(feature = "native-tls", feature = "tokio-native-tls"),
all(feature = "rustls", feature = "tokio-rustls")
))]
impl TlsConfig {
fn new(certificates: Certificates) -> Self { Self { certificates } }
}

pub struct ClientBuilder {
capacity: usize,
#[cfg(any(
all(feature = "native-tls", feature = "tokio-native-tls"),
all(feature = "rustls", feature = "tokio-rustls")
))]
certificates: Option<CertificatesBuilder>,
}

/// Builder for configuring a `Client` with custom settings.
///
/// # Example
///
/// ```no_run
/// # async fn example() -> Result<(), bitreq::Error> {
/// use bitreq::{Client, RequestExt};
///
/// let client = Client::builder().with_capacity(20).build()?;
///
/// let response = bitreq::get("https://example.com")
/// .send_async_with_client(&client)
/// .await?;
/// # Ok(())
/// # }
/// ```
impl ClientBuilder {
/// Creates a new `ClientBuilder` with a default pool capacity of 10.
#[cfg(any(
all(feature = "native-tls", feature = "tokio-native-tls"),
all(feature = "rustls", feature = "tokio-rustls")
))]
pub fn new() -> Self { Self { capacity: 10, certificates: None } }

/// Creates a new `ClientBuilder` with a default pool capacity of 10.
#[cfg(not(any(
all(feature = "native-tls", feature = "tokio-native-tls"),
all(feature = "rustls", feature = "tokio-rustls")
)))]
pub fn new() -> Self { Self { capacity: 10 } }

/// Sets the maximum number of connections to keep in the pool.
pub fn with_capacity(mut self, capacity: usize) -> Self {
self.capacity = capacity;
self
}

#[cfg(any(
all(feature = "native-tls", feature = "tokio-native-tls"),
all(feature = "rustls", feature = "tokio-rustls")
))]
/// Builds the `Client` with the configured settings.
pub fn build(self) -> Result<Client, Error> {
let build_config = if let Some(builder) = self.certificates {
let certificates = builder.build()?;
let tls_config = TlsConfig::new(certificates);
Some(ClientConfig { tls: Some(tls_config) })
} else {
None
};
let client_config = build_config.map(Arc::new);

Ok(Client {
r#async: Arc::new(Mutex::new(ClientImpl {
connections: HashMap::new(),
lru_order: VecDeque::new(),
capacity: self.capacity,
client_config,
})),
})
}

/// Builds the `Client` with the configured settings.
#[cfg(not(any(
all(feature = "native-tls", feature = "tokio-native-tls"),
all(feature = "rustls", feature = "tokio-rustls")
)))]
pub fn build(self) -> Result<Client, Error> {
Ok(Client {
r#async: Arc::new(Mutex::new(ClientImpl {
connections: HashMap::new(),
lru_order: VecDeque::new(),
capacity: self.capacity,
client_config: None,
})),
})
}

/// Adds a custom DER-encoded root certificate for TLS verification.
/// The certificate must be provided in DER format. This method accepts any type
/// that can be converted into a `Vec<u8>`.
/// The certificate is appended to the default trust store rather than replacing it.
/// The trust store used depends on the TLS backend: system certificates for native-tls,
/// Mozilla's root certificates(rustls-webpki) and/or system certificates(rustls-native-certs) for rustls.
///
/// # Example
///
/// ```no_run
/// # use bitreq::Client;
/// # async fn example() -> Result<(), bitreq::Error> {
/// let client = Client::builder()
/// .with_root_certificate(include_bytes!("../tests/test_cert.der"))?
/// .build()?;
/// # Ok(())
/// # }
/// ```
#[cfg(any(
all(feature = "native-tls", feature = "tokio-native-tls"),
all(feature = "rustls", feature = "tokio-rustls")
))]
pub fn with_root_certificate<T: Into<Vec<u8>>>(mut self, cert_der: T) -> Result<Self, Error> {
let cert_der = cert_der.into();
if let Some(ref mut certificates) = self.certificates {
certificates.append_certificate(cert_der)?;

return Ok(self);
}

self.certificates = Some(CertificatesBuilder::new(Some(cert_der))?);
Ok(self)
}

/// Disables default root certificates for TLS connections.
/// Returns [`Error::InvalidTlsConfig`] if TLS has not been configured.
#[cfg(any(
all(feature = "native-tls", feature = "tokio-native-tls"),
all(feature = "rustls", feature = "tokio-rustls")
))]
pub fn disable_default_certificates(mut self) -> Result<Self, Error> {
match self.certificates {
Some(ref mut certificates) => certificates.disable_default()?,
None => return Err(Error::InvalidTlsConfig),
};

Ok(self)
}
}

impl Default for ClientBuilder {
fn default() -> Self { Self::new() }
}

/// A client that caches connections for reuse.
///
/// The client maintains a pool of up to `capacity` connections, evicting
Expand All @@ -39,10 +211,11 @@ struct ClientImpl<T> {
connections: HashMap<ConnectionKey, Arc<T>>,
lru_order: VecDeque<ConnectionKey>,
capacity: usize,
client_config: Option<Arc<ClientConfig>>,
}

impl Client {
/// Creates a new `Client` with the specified connection cache capacity.
/// Creates a new `Client` with the specified connection pool capacity.
///
/// # Arguments
///
Expand All @@ -54,10 +227,14 @@ impl Client {
connections: HashMap::new(),
lru_order: VecDeque::new(),
capacity,
client_config: None,
})),
}
}

/// Create a builder for a client
pub fn builder() -> ClientBuilder { ClientBuilder::new() }

/// Sends a request asynchronously using a cached connection if available.
pub async fn send_async(&self, request: Request) -> Result<Response, Error> {
let parsed_request = ParsedRequest::new(request)?;
Expand All @@ -77,7 +254,13 @@ impl Client {
let conn = if let Some(conn) = conn_opt {
conn
} else {
let connection = AsyncConnection::new(key, parsed_request.timeout_at).await?;
let client_config = {
let state = self.r#async.lock().unwrap();
state.client_config.as_ref().map(Arc::clone)
};

let connection =
AsyncConnection::new(key, parsed_request.timeout_at, client_config).await?;
let connection = Arc::new(connection);

let mut state = self.r#async.lock().unwrap();
Expand Down
Loading