Skip to content

Commit 2c57c75

Browse files
authored
share WASI outbound HTTP connection pool across instances (#3240)
Now each `InstanceState` gets a clone of the `AppState` HTTP clients containing the pools, meaning connections may persist across instances (and thus across inbound requests). Signed-off-by: Joel Dice <[email protected]>
1 parent 74d17e5 commit 2c57c75

File tree

2 files changed

+36
-27
lines changed

2 files changed

+36
-27
lines changed

crates/factor-outbound-http/src/lib.rs

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -50,11 +50,13 @@ impl Factor for OutboundHttpFactor {
5050
&self,
5151
mut ctx: ConfigureAppContext<T, Self>,
5252
) -> anyhow::Result<Self::AppState> {
53+
let connection_pooling = ctx
54+
.take_runtime_config()
55+
.unwrap_or_default()
56+
.connection_pooling;
5357
Ok(AppState {
54-
connection_pooling: ctx
55-
.take_runtime_config()
56-
.unwrap_or_default()
57-
.connection_pooling,
58+
wasi_http_clients: wasi::HttpClients::new(connection_pooling),
59+
connection_pooling,
5860
})
5961
}
6062

@@ -74,7 +76,7 @@ impl Factor for OutboundHttpFactor {
7476
self_request_origin: None,
7577
request_interceptor: None,
7678
spin_http_client: None,
77-
wasi_http_clients: None,
79+
wasi_http_clients: ctx.app_state().wasi_http_clients.clone(),
7880
connection_pooling: ctx.app_state().connection_pooling,
7981
})
8082
}
@@ -88,9 +90,16 @@ pub struct InstanceState {
8890
self_request_origin: Option<SelfRequestOrigin>,
8991
request_interceptor: Option<Arc<dyn OutboundHttpInterceptor>>,
9092
// Connection-pooling client for 'fermyon:spin/http' interface
93+
//
94+
// TODO: We could move this to `AppState` to like the
95+
// `wasi:http/outgoing-handler` pool for consistency, although it's probably
96+
// not a high priority given that `fermyon:spin/http` is deprecated anyway.
9197
spin_http_client: Option<reqwest::Client>,
92-
// Connection pooling client for `wasi:http/outgoing-handler` interface
93-
wasi_http_clients: Option<wasi::HttpClients>,
98+
// Connection pooling clients for `wasi:http/outgoing-handler` interface
99+
//
100+
// This is a clone of `AppState::wasi_http_clients`, meaning it is shared
101+
// among all instances of the app.
102+
wasi_http_clients: wasi::HttpClients,
94103
connection_pooling: bool,
95104
}
96105

@@ -171,5 +180,7 @@ impl std::fmt::Display for SelfRequestOrigin {
171180
}
172181

173182
pub struct AppState {
183+
// Connection pooling clients for `wasi:http/outgoing-handler` interface
184+
wasi_http_clients: wasi::HttpClients,
174185
connection_pooling: bool,
175186
}

crates/factor-outbound-http/src/wasi.rs

Lines changed: 18 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,23 @@ pub(super) struct HttpClients {
101101
https: HttpsClient,
102102
}
103103

104+
impl HttpClients {
105+
pub(super) fn new(enable_pooling: bool) -> Self {
106+
let builder = move || {
107+
let mut builder = Client::builder(TokioExecutor::new());
108+
if !enable_pooling {
109+
builder.pool_max_idle_per_host(0);
110+
}
111+
builder
112+
};
113+
Self {
114+
http1: builder().build(HttpConnector),
115+
http2: builder().http2_only(true).build(HttpConnector),
116+
https: builder().build(HttpsConnector),
117+
}
118+
}
119+
}
120+
104121
pub(crate) struct WasiHttpImplInner<'a> {
105122
state: &'a mut InstanceState,
106123
table: &'a mut ResourceTable,
@@ -135,25 +152,6 @@ impl WasiHttpView for WasiHttpImplInner<'_> {
135152
request: Request<wasmtime_wasi_http::body::HyperOutgoingBody>,
136153
config: wasmtime_wasi_http::types::OutgoingRequestConfig,
137154
) -> wasmtime_wasi_http::HttpResult<wasmtime_wasi_http::types::HostFutureIncomingResponse> {
138-
let connection_pooling = self.state.connection_pooling;
139-
let builder = move || {
140-
let mut builder = Client::builder(TokioExecutor::new());
141-
if !connection_pooling {
142-
builder.pool_max_idle_per_host(0);
143-
}
144-
builder
145-
};
146-
147-
let http_clients = self
148-
.state
149-
.wasi_http_clients
150-
.get_or_insert_with(|| HttpClients {
151-
http1: builder().build(HttpConnector),
152-
http2: builder().http2_only(true).build(HttpConnector),
153-
https: builder().build(HttpsConnector),
154-
})
155-
.clone();
156-
157155
Ok(HostFutureIncomingResponse::Pending(
158156
wasmtime_wasi::runtime::spawn(
159157
send_request_impl(
@@ -164,7 +162,7 @@ impl WasiHttpView for WasiHttpImplInner<'_> {
164162
self.state.request_interceptor.clone(),
165163
self.state.self_request_origin.clone(),
166164
self.state.blocked_networks.clone(),
167-
http_clients,
165+
self.state.wasi_http_clients.clone(),
168166
)
169167
.in_current_span(),
170168
),

0 commit comments

Comments
 (0)