monoio_netreq/http/
client.rs1use std::rc::Rc;
2use std::time::Duration;
3
4use http::{HeaderMap, Request, Uri};
5use monoio::net::TcpStream;
6use monoio_http::common::body::HttpBody;
7use monoio_transports::connectors::TlsConnector;
8use monoio_transports::connectors::{Connector, TcpConnector, TlsStream};
9use monoio_transports::http::HttpConnector;
10
11use crate::{
12 error::{Error, Result, TransportError},
13 key::PoolKey,
14 Protocol,
15 request::HttpRequest,
16 response::Response,
17 apply_parameter_from_config,
18};
19
20enum HttpConnectorType {
21 HTTP(HttpConnector<TcpConnector, PoolKey, TcpStream>),
22 HTTPS(HttpConnector<TlsConnector<TcpConnector>, PoolKey, TlsStream<TcpStream>>),
23}
24
25#[derive(Default, Clone, Debug)]
26struct ClientConfig {
27 default_headers: Rc<HeaderMap>,
28}
29
30struct ClientInner {
31 config: ClientConfig,
32 http_connector: HttpConnectorType,
33}
34
35pub struct MonoioClient {
36 inner: Rc<ClientInner>,
37}
38
39impl MonoioClient {
40 pub fn builder() -> ClientBuilder {
41 ClientBuilder::default()
42 }
43}
44
45impl Clone for MonoioClient {
46 fn clone(&self) -> Self {
47 Self {
48 inner: self.inner.clone(),
49 }
50 }
51}
52
53#[derive(Default, Clone)]
54struct ClientBuilderConfig {
55 protocol: Protocol,
56 enable_https: bool,
57 pool_disabled: bool,
58 max_idle_connections: Option<usize>,
59 idle_timeout_duration: Option<Duration>,
60 read_timeout: Option<Duration>,
61 initial_max_streams: Option<usize>,
62 max_concurrent_streams: Option<u32>,
63 default_headers: HeaderMap,
64}
65
66#[derive(Default)]
67pub struct ClientBuilder {
68 build_config: ClientBuilderConfig,
69}
70
71impl ClientBuilder {
72 pub fn default_headers(mut self, val: HeaderMap) -> Self {
75 self.build_config.default_headers = val;
76 self
77 }
78
79 pub fn disable_connection_pool(mut self) -> Self {
82 self.build_config.pool_disabled = true;
83 self
84 }
85
86 pub fn max_idle_connections(mut self, val: usize) -> Self {
90 self.build_config.max_idle_connections = Some(val);
91 self
92 }
93
94 pub fn idle_connection_timeout(mut self, val: u64) -> Self {
98 self.build_config.idle_timeout_duration = Some(Duration::from_secs(val));
99 self
100 }
101
102 pub fn set_read_timeout(mut self, val: u64) -> Self {
106 self.build_config.read_timeout = Some(Duration::from_secs(val));
107 self
108 }
109
110 pub fn initial_max_streams(mut self, val: usize) -> Self {
113 self.build_config.initial_max_streams = Some(val);
114 self
115 }
116
117 pub fn max_concurrent_streams(mut self, val: u32) -> Self {
121 self.build_config.max_concurrent_streams = Some(val);
122 self
123 }
124
125 pub fn http1_only(mut self) -> Self {
128 self.build_config.protocol = Protocol::Http1;
129 self
130 }
131
132 pub fn http2_prior_knowledge(mut self) -> Self {
135 self.build_config.protocol = Protocol::Http2;
136 self
137 }
138
139 pub fn enable_https(mut self) -> Self {
142 self.build_config.enable_https = true;
143 self
144 }
145}
146
147impl ClientBuilder {
148 pub fn build(self) -> MonoioClient {
149 let build_config = self.build_config.clone();
150 let config = ClientConfig::default();
151 let tcp_connector = TcpConnector::default();
152
153 let mut http_connector = if build_config.enable_https {
154 let alpn = match build_config.protocol {
157 Protocol::Http1 => vec!["http/1.1"],
158 Protocol::Http2 => vec!["h2"],
159 Protocol::Auto => vec!["http/1.1", "h2"],
160 };
161
162 let tls_connector = TlsConnector::new_with_tls_default(tcp_connector, Some(alpn));
163
164 #[cfg(feature = "pool")]
165 let https_connector = HttpConnectorType::HTTPS(HttpConnector::new_with_pool_options(
166 tls_connector,
167 build_config.max_idle_connections,
168 build_config.idle_timeout_duration,
169 ));
170 #[cfg(not(feature = "pool"))]
171 let https_connector = HttpConnectorType::HTTPS(HttpConnector::new(tls_connector));
172
173 https_connector
174 } else {
175 #[cfg(not(feature = "pool"))]
177 let mut connector = HttpConnector::new(tcp_connector);
178 #[cfg(feature = "pool")]
179 let mut connector = HttpConnector::new_with_pool_options(
180 tcp_connector,
181 build_config.max_idle_connections,
182 build_config.idle_timeout_duration,
183 );
184
185 if build_config.protocol.is_protocol_h1() {
186 connector.set_http1_only();
187 }
188
189 if build_config.protocol.is_protocol_h2() {
191 connector.set_http2_only();
192 }
193
194 HttpConnectorType::HTTP(connector)
195 };
196
197 if let Some(val) = build_config.initial_max_streams {
198 apply_parameter_from_config!(
199 http_connector,
200 h2_builder().initial_max_send_streams(val)
201 );
202 }
203
204 if let Some(val) = build_config.max_concurrent_streams {
205 apply_parameter_from_config!(http_connector, h2_builder().max_concurrent_streams(val));
206 }
207
208 apply_parameter_from_config!(http_connector, set_read_timeout(build_config.read_timeout));
209
210 let inner = Rc::new(ClientInner {
211 config,
212 http_connector,
213 });
214
215 MonoioClient { inner }
216 }
217}
218
219impl MonoioClient {
220 pub fn make_request(&self) -> HttpRequest<MonoioClient> {
222 let mut request = HttpRequest::new(self.clone());
223 for (key, val) in self.inner.config.default_headers.iter() {
224 request = request.set_header(key, val)
225 }
226
227 request
228 }
229
230 pub(crate) async fn send_request(
231 &self,
232 req: Request<HttpBody>,
233 uri: Uri,
234 ) -> Result<Response<HttpBody>> {
235 let key = uri.try_into().map_err(|e| Error::UriKeyError(e))?;
236
237 let (response, _) = match self.inner.http_connector
238 {
239 HttpConnectorType::HTTP(ref connector) => {
240 let mut conn = connector
241 .connect(key)
242 .await
243 .map_err(|e| TransportError::HttpConnectorError(e))?;
244 conn.send_request(req).await
245 }
246
247 HttpConnectorType::HTTPS(ref connector) => {
248 let mut conn = connector
249 .connect(key)
250 .await
251 .map_err(|e| TransportError::HttpConnectorError(e))?;
252 conn.send_request(req).await
253 }
254 };
255
256 response.map_err(|e| Error::HttpResponseError(e))
257 }
258}