use std::fmt;
use std::str::FromStr;
use std::sync::Arc;
use std::task::{Context, Poll};
use chateau::client::pool::{Key, KeyError};
use tower::ServiceExt;
use tower::util::Oneshot;
use self::conn::protocol::auto;
use crate::BoxError;
pub use chateau::client::ConnectionPoolLayer;
pub use chateau::client::ConnectionPoolService;
use chateau::client::conn::transport::tcp::TcpTransport;
pub use chateau::client::pool;
use chateau::services::SharedService;
mod builder;
pub mod conn;
mod error;
pub use self::error::Error;
pub use builder::Builder;
pub use chateau::client::PoolConfig;
#[cfg(feature = "tls")]
pub fn default_tls_config() -> rustls::ClientConfig {
let mut roots = rustls::RootCertStore::empty();
for cert in rustls_native_certs::load_native_certs().expect("could not load platform certs") {
roots.add(cert).unwrap();
}
let mut cfg = rustls::ClientConfig::builder()
.with_root_certificates(roots)
.with_no_client_auth();
cfg.alpn_protocols.push(b"h2".to_vec());
cfg.alpn_protocols.push(b"http/1.1".to_vec());
cfg
}
pub type SharedClientService<BIn, BOut> =
SharedService<http::Request<BIn>, http::Response<BOut>, Error>;
#[derive(Clone)]
struct ClientRef {
service: SharedClientService<crate::Body, crate::Body>,
}
impl ClientRef {
fn new(service: impl Into<SharedClientService<crate::Body, crate::Body>>) -> Self {
Self {
service: service.into(),
}
}
fn request(
&mut self,
request: http::Request<crate::Body>,
) -> Oneshot<SharedClientService<crate::Body, crate::Body>, http::Request<crate::Body>> {
let service = self.service.clone();
std::mem::replace(&mut self.service, service).oneshot(request)
}
}
#[derive(Clone)]
pub struct Client {
inner: Arc<ClientRef>,
}
impl fmt::Debug for Client {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Client").finish()
}
}
impl Default for Client {
fn default() -> Self {
Builder::default().build()
}
}
impl Client {
pub fn new_from_service<S>(service: S) -> Self
where
S: Into<SharedClientService<crate::Body, crate::Body>>,
{
Client {
inner: Arc::new(ClientRef::new(service)),
}
}
pub fn builder() -> self::builder::Builder<(), ()> {
Builder::new()
}
pub fn build_tcp_http() -> self::builder::Builder<
TcpTransport<conn::dns::GaiResolver>,
auto::AlpnHttpConnectionBuilder<crate::Body>,
> {
Builder::default()
}
pub fn new_tcp_http() -> Self {
Builder::default().build()
}
pub fn into_inner(self) -> SharedClientService<crate::Body, crate::Body> {
match Arc::try_unwrap(self.inner) {
Ok(client) => client.service,
Err(client) => client.service.clone(),
}
}
}
impl Client {
pub fn request(
&mut self,
request: http::Request<crate::Body>,
) -> Oneshot<SharedClientService<crate::Body, crate::Body>, http::Request<crate::Body>> {
Arc::make_mut(&mut self.inner).request(request)
}
pub async fn get(&mut self, uri: http::Uri) -> Result<http::Response<crate::Body>, BoxError> {
let request = http::Request::get(uri.clone())
.body(crate::body::Body::empty())
.unwrap();
let response = self.request(request).await?;
Ok(response)
}
}
impl tower::Service<http::Request<crate::Body>> for Client {
type Response = http::Response<crate::Body>;
type Error = Error;
type Future =
Oneshot<SharedClientService<crate::Body, crate::Body>, http::Request<crate::Body>>;
fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
Arc::make_mut(&mut self.inner).service.poll_ready(cx)
}
fn call(&mut self, request: http::Request<crate::Body>) -> Self::Future {
Arc::make_mut(&mut self.inner).request(request)
}
}
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
pub struct UriKey(http::uri::Scheme, Option<http::uri::Authority>);
impl fmt::Display for UriKey {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"{}://{}",
self.0,
self.1.as_ref().map_or("", |a| a.as_str())
)
}
}
impl From<(http::uri::Scheme, http::uri::Authority)> for UriKey {
fn from(value: (http::uri::Scheme, http::uri::Authority)) -> Self {
Self(value.0, Some(value.1))
}
}
impl TryFrom<http::Uri> for UriKey {
type Error = KeyError;
fn try_from(uri: http::Uri) -> Result<Self, Self::Error> {
let parts = uri.into_parts();
let authority = parts.authority.clone();
let scheme = parts.scheme.clone().ok_or_else(|| {
KeyError::new(format!(
"Missing scheme in URI: {}",
http::Uri::from_parts(parts).unwrap()
))
})?;
Ok::<_, KeyError>(Self(scheme, authority))
}
}
impl FromStr for UriKey {
type Err = KeyError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let uri = http::Uri::from_str(s).map_err(KeyError::new)?;
uri.try_into()
}
}
impl<B> Key<http::Request<B>> for UriKey {
fn build_key(request: &http::Request<B>) -> Result<Self, KeyError>
where
Self: Sized,
{
let uri = request.uri().clone();
let parts = uri.into_parts();
let authority = parts.authority;
let scheme = parts.scheme.unwrap_or_else(|| match request.version() {
http::Version::HTTP_2 | http::Version::HTTP_3 => http::uri::Scheme::HTTPS,
_ => http::uri::Scheme::HTTP,
});
Ok::<_, KeyError>(Self(scheme, authority))
}
}
#[cfg(test)]
pub(crate) mod test_key {
use super::*;
#[test]
fn key_from_uri() {
let uri = http::Uri::from_static("http://localhost:8080");
let key: UriKey = uri.try_into().unwrap();
assert_eq!(key.0, http::uri::Scheme::HTTP);
assert_eq!(
key.1,
Some(http::uri::Authority::from_static("localhost:8080"))
);
}
#[test]
fn key_display() {
let key = UriKey(
http::uri::Scheme::HTTP,
Some(http::uri::Authority::from_static("localhost:8080")),
);
assert_eq!(key.to_string(), "http://localhost:8080");
}
#[test]
fn key_from_tuple() {
let key: UriKey = (
http::uri::Scheme::HTTP,
http::uri::Authority::from_static("localhost:8080"),
)
.into();
assert_eq!(key.0, http::uri::Scheme::HTTP);
assert_eq!(
key.1,
Some(http::uri::Authority::from_static("localhost:8080"))
);
}
#[test]
fn key_debug() {
let key = UriKey(
http::uri::Scheme::HTTP,
Some(http::uri::Authority::from_static("localhost:8080")),
);
assert_eq!(format!("{key:?}"), "UriKey(\"http\", Some(localhost:8080))");
}
}
#[cfg(test)]
mod tests {
use static_assertions::assert_impl_all;
use crate::Client;
assert_impl_all!(Client: Send, Sync);
}