Skip to content

Commit f5817c6

Browse files
authored
chore(client): defer initialization of internal client (#811)
1 parent adc8b58 commit f5817c6

3 files changed

Lines changed: 46 additions & 146 deletions

File tree

src/client/http/mod.rs

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -54,9 +54,7 @@ use crate::{
5454
BoxedConnectorLayer, BoxedConnectorService, Conn, Connector, HttpConnector, Unnameable,
5555
},
5656
core::{
57-
client::{
58-
Builder, Client as NativeClient, connect::TcpConnectOptions, options::TransportOptions,
59-
},
57+
client::{HttpClient, connect::TcpConnectOptions, options::TransportOptions},
6058
ext::RequestConfig,
6159
rt::{TokioExecutor, tokio::TokioTimer},
6260
},
@@ -147,7 +145,6 @@ struct Config {
147145
http2_max_retry: usize,
148146
request_layers: Option<Vec<BoxedClientServiceLayer>>,
149147
connector_layers: Option<Vec<BoxedConnectorLayer>>,
150-
builder: Builder,
151148
tls_keylog_policy: Option<KeyLogPolicy>,
152149
tls_info: bool,
153150
tls_sni: bool,
@@ -211,7 +208,6 @@ impl ClientBuilder {
211208
dns_overrides: HashMap::new(),
212209
dns_resolver: None,
213210
http_version_pref: HttpVersionPref::All,
214-
builder: NativeClient::builder(TokioExecutor::new()),
215211
https_only: false,
216212
http2_max_retry: 2,
217213
request_layers: None,
@@ -238,7 +234,7 @@ impl ClientBuilder {
238234
/// This method fails if a TLS backend cannot be initialized, or the resolver
239235
/// cannot load the system configuration.
240236
pub fn build(self) -> crate::Result<Client> {
241-
let mut config = self.config;
237+
let config = self.config;
242238

243239
if let Some(err) = config.error {
244240
return Err(err);
@@ -256,17 +252,6 @@ impl ClientBuilder {
256252

257253
let (tls_opts, http1_opts, http2_opts) = config.transport_options.into_parts();
258254

259-
config
260-
.builder
261-
.http1_options(http1_opts)
262-
.http2_options(http2_opts)
263-
.http2_only(matches!(config.http_version_pref, HttpVersionPref::Http2))
264-
.http2_timer(TokioTimer::new())
265-
.pool_timer(TokioTimer::new())
266-
.pool_idle_timeout(config.pool_idle_timeout)
267-
.pool_max_idle_per_host(config.pool_max_idle_per_host)
268-
.pool_max_size(config.pool_max_size);
269-
270255
let connector = {
271256
let resolver = {
272257
let mut resolver: Arc<dyn Resolve> = match config.dns_resolver {
@@ -326,9 +311,24 @@ impl ClientBuilder {
326311
.build(tls_opts.unwrap_or_default(), config.connector_layers)?
327312
};
328313

314+
let client = {
315+
let http2_only = matches!(config.http_version_pref, HttpVersionPref::Http2);
316+
let mut builder = HttpClient::builder(TokioExecutor::new());
317+
builder
318+
.http1_options(http1_opts)
319+
.http2_options(http2_opts)
320+
.http2_only(http2_only)
321+
.http2_timer(TokioTimer::new())
322+
.pool_timer(TokioTimer::new())
323+
.pool_idle_timeout(config.pool_idle_timeout)
324+
.pool_max_idle_per_host(config.pool_max_idle_per_host)
325+
.pool_max_size(config.pool_max_size);
326+
builder.build(connector)
327+
};
328+
329329
let service = {
330330
let service = ClientService {
331-
client: config.builder.build(connector),
331+
client,
332332
config: Arc::new(ClientConfig {
333333
default_headers: config.headers,
334334
original_headers: RequestConfig::new(config.original_headers),

src/client/http/service.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ use crate::{
1212
connect::Connector,
1313
core::{
1414
body::Incoming,
15-
client::Client,
15+
client::HttpClient,
1616
ext::{RequestConfig, RequestOriginalHeaders},
1717
},
1818
error::{BoxError, Error},
@@ -22,7 +22,7 @@ use crate::{
2222

2323
#[derive(Clone)]
2424
pub struct ClientService {
25-
pub(super) client: Client<Connector, Body>,
25+
pub(super) client: HttpClient<Connector, Body>,
2626
pub(super) config: Arc<ClientConfig>,
2727
}
2828

src/core/client/mod.rs

Lines changed: 26 additions & 126 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,14 @@
22
//!
33
//! Provides HTTP over a single connection. See the [`conn`] module.
44
5+
mod pool;
6+
57
pub mod conn;
8+
pub mod connect;
69
pub(super) mod dispatch;
710
pub mod options;
811
pub mod proxy;
912

10-
pub mod connect;
11-
// Publicly available, but just for legacy purposes. A better pool will be
12-
// designed.
13-
mod pool;
14-
1513
use std::{
1614
error::Error as StdError,
1715
fmt,
@@ -151,11 +149,11 @@ impl ConnRequest {
151149
}
152150
}
153151

154-
/// A Client to make outgoing HTTP requests.
152+
/// A HttpClient to make outgoing HTTP requests.
155153
///
156-
/// `Client` is cheap to clone and cloning is the recommended way to share a `Client`. The
154+
/// `HttpClient` is cheap to clone and cloning is the recommended way to share a `HttpClient`. The
157155
/// underlying connection pool will be reused.
158-
pub struct Client<C, B> {
156+
pub struct HttpClient<C, B> {
159157
config: Config,
160158
connector: C,
161159
exec: Exec,
@@ -171,7 +169,6 @@ struct Config {
171169
ver: Ver,
172170
}
173171

174-
/// Client errors
175172
pub struct Error {
176173
kind: ErrorKind,
177174
source: Option<BoxError>,
@@ -232,38 +229,15 @@ type ResponseWrapper =
232229
SyncWrapper<Pin<Box<dyn Future<Output = Result<Response<Incoming>, Error>> + Send>>>;
233230

234231
/// A `Future` that will resolve to an HTTP Response.
235-
///
236-
/// This is returned by `Client::request` (and `Client::get`).
237232
#[must_use = "futures do nothing unless polled"]
238233
pub struct ResponseFuture {
239234
inner: ResponseWrapper,
240235
}
241236

242-
// ===== impl Client =====
237+
// ===== impl HttpClient =====
243238

244-
impl Client<(), ()> {
245-
/// Create a builder to configure a new `Client`.
246-
///
247-
/// # Example
248-
///
249-
/// ```
250-
/// #
251-
/// # fn run () {
252-
/// use crate::{
253-
/// core::rt::TokioExecutor,
254-
/// util::client::Client,
255-
/// };
256-
/// use std::time::Duration;
257-
///
258-
/// let client = Client::builder(TokioExecutor::new())
259-
/// .pool_idle_timeout(Duration::from_secs(30))
260-
/// .http2_only(true)
261-
/// .build_http();
262-
/// # let infer: Client<_, http_body_util::Full<bytes::Bytes>> = client;
263-
/// # drop(infer);
264-
/// # }
265-
/// # fn main() {}
266-
/// ```
239+
impl HttpClient<(), ()> {
240+
/// Create a builder to configure a new `HttpClient`.
267241
pub fn builder<E>(executor: E) -> Builder
268242
where
269243
E: Executor<BoxSendFuture> + Send + Sync + Clone + 'static,
@@ -272,7 +246,7 @@ impl Client<(), ()> {
272246
}
273247
}
274248

275-
impl<C, B> Client<C, B>
249+
impl<C, B> HttpClient<C, B>
276250
where
277251
C: tower::Service<ConnRequest> + Clone + Send + Sync + 'static,
278252
C::Response: Read + Write + Connection + Unpin + Send + 'static,
@@ -282,36 +256,7 @@ where
282256
B::Data: Send,
283257
B::Error: Into<BoxError>,
284258
{
285-
/// Send a constructed `Request` using this `Client`.
286-
///
287-
/// # Example
288-
///
289-
/// ```
290-
/// #
291-
/// # fn run () {
292-
/// use crate::{
293-
/// core::{
294-
/// Method,
295-
/// Request,
296-
/// rt::TokioExecutor,
297-
/// },
298-
/// util::client::Client,
299-
/// };
300-
/// use bytes::Bytes;
301-
/// use http_body_util::Full;
302-
///
303-
/// let client: Client<_, Full<Bytes>> = Client::builder(TokioExecutor::new()).build_http();
304-
///
305-
/// let req: Request<Full<Bytes>> = Request::builder()
306-
/// .method(Method::POST)
307-
/// .uri("http://httpbin.org/post")
308-
/// .body(Full::from("Hallo!"))
309-
/// .expect("request builder");
310-
///
311-
/// let future = client.request(req);
312-
/// # }
313-
/// # fn main() {}
314-
/// ```
259+
/// Send a constructed `Request` using this `HttpClient`.
315260
pub fn request(&self, mut req: Request<B>) -> ResponseFuture {
316261
let is_http_connect = req.method() == Method::CONNECT;
317262
// Validate HTTP version early
@@ -785,7 +730,7 @@ where
785730
}
786731
}
787732

788-
impl<C, B> tower::Service<Request<B>> for Client<C, B>
733+
impl<C, B> tower::Service<Request<B>> for HttpClient<C, B>
789734
where
790735
C: tower::Service<ConnRequest> + Clone + Send + Sync + 'static,
791736
C::Response: Read + Write + Connection + Unpin + Send + 'static,
@@ -808,7 +753,7 @@ where
808753
}
809754
}
810755

811-
impl<C, B> tower::Service<Request<B>> for &'_ Client<C, B>
756+
impl<C, B> tower::Service<Request<B>> for &'_ HttpClient<C, B>
812757
where
813758
C: tower::Service<ConnRequest> + Clone + Send + Sync + 'static,
814759
C::Response: Read + Write + Connection + Unpin + Send + 'static,
@@ -831,9 +776,9 @@ where
831776
}
832777
}
833778

834-
impl<C: Clone, B> Clone for Client<C, B> {
835-
fn clone(&self) -> Client<C, B> {
836-
Client {
779+
impl<C: Clone, B> Clone for HttpClient<C, B> {
780+
fn clone(&self) -> HttpClient<C, B> {
781+
HttpClient {
837782
config: self.config,
838783
exec: self.exec.clone(),
839784

@@ -845,9 +790,9 @@ impl<C: Clone, B> Clone for Client<C, B> {
845790
}
846791
}
847792

848-
impl<C, B> fmt::Debug for Client<C, B> {
793+
impl<C, B> fmt::Debug for HttpClient<C, B> {
849794
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
850-
f.debug_struct("Client").finish()
795+
f.debug_struct("HttpClient").finish()
851796
}
852797
}
853798

@@ -883,19 +828,19 @@ impl Future for ResponseFuture {
883828
}
884829
}
885830

886-
// ===== impl PoolClient =====
887-
831+
/// A pooled HTTP connection that can send requests
888832
struct PoolClient<B> {
889833
conn_info: Connected,
890834
tx: PoolTx<B>,
891835
}
892836

893837
enum PoolTx<B> {
894838
Http1(conn::http1::SendRequest<B>),
895-
896839
Http2(conn::http2::SendRequest<B>),
897840
}
898841

842+
// ===== impl PoolClient =====
843+
899844
impl<B> PoolClient<B> {
900845
fn poll_ready(&mut self, cx: &mut task::Context<'_>) -> Poll<Result<(), Error>> {
901846
match self.tx {
@@ -1088,28 +1033,7 @@ fn is_schema_secure(uri: &Uri) -> bool {
10881033
.unwrap_or_default()
10891034
}
10901035

1091-
/// A builder to configure a new [`Client`].
1092-
///
1093-
/// # Example
1094-
///
1095-
/// ```
1096-
/// #
1097-
/// # fn run () {
1098-
/// use crate::{
1099-
/// core::rt::TokioExecutor,
1100-
/// util::client::Client,
1101-
/// };
1102-
/// use std::time::Duration;
1103-
///
1104-
/// let client = Client::builder(TokioExecutor::new())
1105-
/// .pool_idle_timeout(Duration::from_secs(30))
1106-
/// .http2_only(true)
1107-
/// .build_http();
1108-
/// # let infer: Client<_, http_body_util::Full<bytes::Bytes>> = client;
1109-
/// # drop(infer);
1110-
/// # }
1111-
/// # fn main() {}
1112-
/// ```
1036+
/// A builder to configure a new [`HttpClient`].
11131037
#[derive(Clone)]
11141038
pub struct Builder {
11151039
client_config: Config,
@@ -1152,30 +1076,6 @@ impl Builder {
11521076
/// Pass `None` to disable timeout.
11531077
///
11541078
/// Default is 90 seconds.
1155-
///
1156-
/// # Example
1157-
///
1158-
/// ```
1159-
/// #
1160-
/// # fn run () {
1161-
/// use crate::{
1162-
/// core::rt::{
1163-
/// TokioExecutor,
1164-
/// TokioTimer,
1165-
/// },
1166-
/// util::client::Client,
1167-
/// };
1168-
/// use std::time::Duration;
1169-
///
1170-
/// let client = Client::builder(TokioExecutor::new())
1171-
/// .pool_idle_timeout(Duration::from_secs(30))
1172-
/// .pool_timer(TokioTimer::new())
1173-
/// .build_http();
1174-
///
1175-
/// # let infer: Client<_, http_body_util::Full<bytes::Bytes>> = client;
1176-
/// # }
1177-
/// # fn main() {}
1178-
/// ```
11791079
pub fn pool_idle_timeout<D>(&mut self, val: D) -> &mut Self
11801080
where
11811081
D: Into<Option<Duration>>,
@@ -1204,7 +1104,7 @@ impl Builder {
12041104
///
12051105
/// The destination must either allow HTTP2 Prior Knowledge, or the
12061106
/// `Connect` should be configured to do use ALPN to upgrade to `h2`
1207-
/// as part of the connection process. This will not make the `Client`
1107+
/// as part of the connection process. This will not make the `HttpClient`
12081108
/// utilize ALPN by itself.
12091109
///
12101110
/// Note that setting this to true prevents HTTP/1 from being allowed.
@@ -1279,8 +1179,8 @@ impl Builder {
12791179
self
12801180
}
12811181

1282-
/// Combine the configuration of this builder with a connector to create a `Client`.
1283-
pub fn build<C, B>(&self, connector: C) -> Client<C, B>
1182+
/// Combine the configuration of this builder with a connector to create a `HttpClient`.
1183+
pub fn build<C, B>(&self, connector: C) -> HttpClient<C, B>
12841184
where
12851185
C: tower::Service<ConnRequest> + Clone + Send + Sync + 'static,
12861186
C::Response: Read + Write + Connection + Unpin + Send + 'static,
@@ -1291,7 +1191,7 @@ impl Builder {
12911191
{
12921192
let exec = self.exec.clone();
12931193
let timer = self.pool_timer.clone();
1294-
Client {
1194+
HttpClient {
12951195
config: self.client_config,
12961196
exec: exec.clone(),
12971197

0 commit comments

Comments
 (0)