use std::sync::Arc;
use async_native_tls::{Identity, TlsConnector};
use async_trait::async_trait;
use bytes::Bytes;
use http::version::Version;
use http_body_util::Full;
use hyper::{body::Incoming, client::conn::http2::handshake, Request, Response};
use smol::net::TcpStream;
use smol_hyper::rt::FuturesIo;
use url::Url;
use crate::{
cert::ClientCert,
client::conn::http::{BaseHttpConnection, DeboaHttpConnection, Http2Request},
errors::{ConnectionError, DeboaError},
rt::smol::{executor::SmolExecutor, stream::SmolStream},
Result,
};
#[async_trait]
impl DeboaHttpConnection for BaseHttpConnection<Http2Request> {
type Sender = Http2Request;
#[inline]
fn url(&self) -> &Url {
&self.url
}
#[inline]
fn protocol(&self) -> Version {
Version::HTTP_2
}
async fn connect(
url: Arc<Url>,
client_cert: &Option<ClientCert>,
) -> Result<BaseHttpConnection<Self::Sender>> {
let host = url
.host()
.expect("uri has no host");
let io = {
match url.scheme() {
"http" => {
let stream = {
let port = url
.port()
.unwrap_or(80);
TcpStream::connect((host.to_string(), port)).await
};
if let Err(e) = stream {
return Err(DeboaError::Connection(ConnectionError::Tcp {
host: host.to_string(),
message: e.to_string(),
}));
}
let stream = stream.unwrap();
SmolStream::Plain(stream)
}
"https" => {
let stream = {
let port = url
.port()
.unwrap_or(443);
TcpStream::connect((host.to_string(), port)).await
};
if let Err(e) = stream {
return Err(DeboaError::Connection(ConnectionError::Tcp {
host: host.to_string(),
message: e.to_string(),
}));
}
let stream = stream.unwrap();
let connector = if let Some(client_cert) = client_cert {
let file = std::fs::read(client_cert.cert());
if let Err(e) = file {
return Err(DeboaError::ClientCert { message: e.to_string() });
}
let identity = Identity::from_pkcs12(&file.unwrap(), client_cert.pw());
if let Err(e) = identity {
return Err(DeboaError::ClientCert { message: e.to_string() });
}
TlsConnector::new().identity(identity.unwrap())
} else {
TlsConnector::new()
};
let stream = connector
.connect(&host.to_string(), stream)
.await;
if let Err(e) = stream {
return Err(DeboaError::Connection(ConnectionError::Tls {
host: host.to_string(),
message: e.to_string(),
}));
}
let stream = stream.unwrap();
SmolStream::Tls(stream)
}
scheme => {
return Err(DeboaError::Connection(ConnectionError::UnsupportedScheme {
message: format!("unsupported scheme: {scheme:?}"),
}));
}
}
};
let result = handshake(SmolExecutor::new(), FuturesIo::new(io)).await;
let (sender, conn) = result.unwrap();
smol::spawn(async move {
match conn.await {
Ok(_) => (),
Err(_err) => {}
};
})
.detach();
Ok(BaseHttpConnection::<Self::Sender> { url, sender })
}
async fn send_request(&mut self, request: Request<Full<Bytes>>) -> Result<Response<Incoming>> {
let method = request
.method()
.to_string();
let result = self
.sender
.send_request(request)
.await;
self.process_response(&self.url, &method, result)
.await
}
}
impl crate::client::conn::http::private::Sealed for BaseHttpConnection<Http2Request> {}