monoio_netreq/http/
client.rsuse std::rc::Rc;
use std::time::Duration;
use http::{HeaderMap, Request, Uri};
use monoio::net::TcpStream;
use monoio_http::common::body::HttpBody;
use monoio_transports::connectors::TlsConnector;
use monoio_transports::connectors::{Connector, TcpConnector, TlsStream};
use monoio_transports::http::HttpConnector;
use crate::{
error::{Error, Result, TransportError},
key::PoolKey,
Protocol,
request::HttpRequest,
response::Response,
apply_parameter_from_config,
};
enum HttpConnectorType {
HTTP(HttpConnector<TcpConnector, PoolKey, TcpStream>),
HTTPS(HttpConnector<TlsConnector<TcpConnector>, PoolKey, TlsStream<TcpStream>>),
}
#[derive(Default, Clone, Debug)]
struct ClientConfig {
default_headers: Rc<HeaderMap>,
}
struct ClientInner {
config: ClientConfig,
http_connector: HttpConnectorType,
}
pub struct MonoioClient {
inner: Rc<ClientInner>,
}
impl MonoioClient {
pub fn builder() -> ClientBuilder {
ClientBuilder::default()
}
}
impl Clone for MonoioClient {
fn clone(&self) -> Self {
Self {
inner: self.inner.clone(),
}
}
}
#[derive(Default, Clone)]
struct ClientBuilderConfig {
protocol: Protocol,
enable_https: bool,
pool_disabled: bool,
max_idle_connections: Option<usize>,
idle_timeout_duration: Option<Duration>,
read_timeout: Option<Duration>,
initial_max_streams: Option<usize>,
max_concurrent_streams: Option<u32>,
default_headers: HeaderMap,
}
#[derive(Default)]
pub struct ClientBuilder {
build_config: ClientBuilderConfig,
}
impl ClientBuilder {
pub fn default_headers(mut self, val: HeaderMap) -> Self {
self.build_config.default_headers = val;
self
}
pub fn disable_connection_pool(mut self) -> Self {
self.build_config.pool_disabled = true;
self
}
pub fn max_idle_connections(mut self, val: usize) -> Self {
self.build_config.max_idle_connections = Some(val);
self
}
pub fn idle_connection_timeout(mut self, val: u64) -> Self {
self.build_config.idle_timeout_duration = Some(Duration::from_secs(val));
self
}
pub fn set_read_timeout(mut self, val: u64) -> Self {
self.build_config.read_timeout = Some(Duration::from_secs(val));
self
}
pub fn initial_max_streams(mut self, val: usize) -> Self {
self.build_config.initial_max_streams = Some(val);
self
}
pub fn max_concurrent_streams(mut self, val: u32) -> Self {
self.build_config.max_concurrent_streams = Some(val);
self
}
pub fn http1_only(mut self) -> Self {
self.build_config.protocol = Protocol::Http1;
self
}
pub fn http2_prior_knowledge(mut self) -> Self {
self.build_config.protocol = Protocol::Http2;
self
}
pub fn enable_https(mut self) -> Self {
self.build_config.enable_https = true;
self
}
}
impl ClientBuilder {
pub fn build(self) -> MonoioClient {
let build_config = self.build_config.clone();
let config = ClientConfig::default();
let tcp_connector = TcpConnector::default();
let mut http_connector = if build_config.enable_https {
let alpn = match build_config.protocol {
Protocol::Http1 => vec!["http/1.1"],
Protocol::Http2 => vec!["h2"],
Protocol::Auto => vec!["http/1.1", "h2"],
};
let tls_connector = TlsConnector::new_with_tls_default(tcp_connector, Some(alpn));
#[cfg(feature = "pool")]
let https_connector = HttpConnectorType::HTTPS(HttpConnector::new_with_pool_options(
tls_connector,
build_config.max_idle_connections,
build_config.idle_timeout_duration,
));
#[cfg(not(feature = "pool"))]
let https_connector = HttpConnectorType::HTTPS(HttpConnector::new(tls_connector));
https_connector
} else {
#[cfg(not(feature = "pool"))]
let mut connector = HttpConnector::new(tcp_connector);
#[cfg(feature = "pool")]
let mut connector = HttpConnector::new_with_pool_options(
tcp_connector,
build_config.max_idle_connections,
build_config.idle_timeout_duration,
);
if build_config.protocol.is_protocol_h1() {
connector.set_http1_only();
}
if build_config.protocol.is_protocol_h2() {
connector.set_http2_only();
}
HttpConnectorType::HTTP(connector)
};
if let Some(val) = build_config.initial_max_streams {
apply_parameter_from_config!(
http_connector,
h2_builder().initial_max_send_streams(val)
);
}
if let Some(val) = build_config.max_concurrent_streams {
apply_parameter_from_config!(http_connector, h2_builder().max_concurrent_streams(val));
}
apply_parameter_from_config!(http_connector, set_read_timeout(build_config.read_timeout));
let inner = Rc::new(ClientInner {
config,
http_connector,
});
MonoioClient { inner }
}
}
impl MonoioClient {
pub fn make_request(&self) -> HttpRequest<MonoioClient> {
let mut request = HttpRequest::new(self.clone());
for (key, val) in self.inner.config.default_headers.iter() {
request = request.set_header(key, val)
}
request
}
pub(crate) async fn send_request(
&self,
req: Request<HttpBody>,
uri: Uri,
) -> Result<Response<HttpBody>> {
let key = uri.try_into().map_err(|e| Error::UriKeyError(e))?;
let (response, _) = match self.inner.http_connector
{
HttpConnectorType::HTTP(ref connector) => {
let mut conn = connector
.connect(key)
.await
.map_err(|e| TransportError::HttpConnectorError(e))?;
conn.send_request(req).await
}
HttpConnectorType::HTTPS(ref connector) => {
let mut conn = connector
.connect(key)
.await
.map_err(|e| TransportError::HttpConnectorError(e))?;
conn.send_request(req).await
}
};
response.map_err(|e| Error::HttpResponseError(e))
}
}