Skip to content

Commit 5c14552

Browse files
authored
Merge pull request #2373 from alexcrichton/probe-handler-type-once
Lift http handler type discovery up a layer
2 parents 04f0ddd + e565b78 commit 5c14552

File tree

2 files changed

+83
-58
lines changed

2 files changed

+83
-58
lines changed

crates/trigger-http/src/handler.rs

Lines changed: 71 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
use std::{net::SocketAddr, str, str::FromStr};
22

33
use crate::{Body, ChainedRequestHandler, HttpExecutor, HttpInstance, HttpTrigger, Store};
4-
use anyhow::bail;
54
use anyhow::{anyhow, Context, Result};
65
use futures::TryFutureExt;
76
use http::{HeaderName, HeaderValue};
@@ -11,7 +10,7 @@ use outbound_http::OutboundHttpComponent;
1110
use spin_core::async_trait;
1211
use spin_core::wasi_2023_10_18::exports::wasi::http::incoming_handler::Guest as IncomingHandler2023_10_18;
1312
use spin_core::wasi_2023_11_10::exports::wasi::http::incoming_handler::Guest as IncomingHandler2023_11_10;
14-
use spin_core::Instance;
13+
use spin_core::{Component, Engine, Instance};
1514
use spin_http::body;
1615
use spin_http::routes::RouteMatch;
1716
use spin_trigger::TriggerAppEngine;
@@ -43,26 +42,21 @@ impl HttpExecutor for HttpHandlerExecutor {
4342
);
4443

4544
let (instance, mut store) = engine.prepare_instance(component_id).await?;
46-
let HttpInstance::Component(instance) = instance else {
45+
let HttpInstance::Component(instance, ty) = instance else {
4746
unreachable!()
4847
};
4948

5049
set_http_origin_from_request(&mut store, engine.clone(), self, &req);
5150

52-
let resp = match HandlerType::from_exports(instance.exports(&mut store)) {
53-
Some(HandlerType::Wasi) => {
54-
Self::execute_wasi(store, instance, base, route_match, req, client_addr).await?
55-
}
56-
Some(HandlerType::Spin) => {
51+
let resp = match ty {
52+
HandlerType::Spin => {
5753
Self::execute_spin(store, instance, base, route_match, req, client_addr)
5854
.await
5955
.map_err(contextualise_err)?
6056
}
61-
None => bail!(
62-
"Expected component to either export `{WASI_HTTP_EXPORT_2023_10_18}`, \
63-
`{WASI_HTTP_EXPORT_2023_11_10}`, `{WASI_HTTP_EXPORT_0_2_0}`, \
64-
or `fermyon:spin/inbound-http` but it exported none of those"
65-
),
57+
_ => {
58+
Self::execute_wasi(store, instance, ty, base, route_match, req, client_addr).await?
59+
}
6660
};
6761

6862
tracing::info!(
@@ -157,6 +151,7 @@ impl HttpHandlerExecutor {
157151
async fn execute_wasi(
158152
mut store: Store,
159153
instance: Instance,
154+
ty: HandlerType,
160155
base: &str,
161156
route_match: &RouteMatch,
162157
mut req: Request<Body>,
@@ -188,31 +183,33 @@ impl HttpHandlerExecutor {
188183
Handler2023_10_18(IncomingHandler2023_10_18),
189184
}
190185

191-
let handler = match instance
192-
.exports(&mut store)
193-
.instance("wasi:http/[email protected]")
194-
{
195-
Some(mut instance) => Some(Handler::Handler2023_10_18(IncomingHandler2023_10_18::new(
196-
&mut instance,
197-
)?)),
198-
None => None,
199-
};
200-
let handler = match handler {
201-
Some(handler) => Some(handler),
202-
None => match instance
203-
.exports(&mut store)
204-
.instance("wasi:http/[email protected]")
186+
let handler =
205187
{
206-
Some(mut instance) => Some(Handler::Handler2023_11_10(
207-
IncomingHandler2023_11_10::new(&mut instance)?,
208-
)),
209-
None => None,
210-
},
211-
};
212-
let handler = match handler {
213-
Some(handler) => handler,
214-
None => Handler::Latest(Proxy::new(&mut store, &instance)?),
215-
};
188+
let mut exports = instance.exports(&mut store);
189+
match ty {
190+
HandlerType::Wasi2023_10_18 => {
191+
let mut instance = exports
192+
.instance(WASI_HTTP_EXPORT_2023_10_18)
193+
.ok_or_else(|| {
194+
anyhow!("export of `{WASI_HTTP_EXPORT_2023_10_18}` not an instance")
195+
})?;
196+
Handler::Handler2023_10_18(IncomingHandler2023_10_18::new(&mut instance)?)
197+
}
198+
HandlerType::Wasi2023_11_10 => {
199+
let mut instance = exports
200+
.instance(WASI_HTTP_EXPORT_2023_11_10)
201+
.ok_or_else(|| {
202+
anyhow!("export of `{WASI_HTTP_EXPORT_2023_11_10}` not an instance")
203+
})?;
204+
Handler::Handler2023_11_10(IncomingHandler2023_11_10::new(&mut instance)?)
205+
}
206+
HandlerType::Wasi0_2 => {
207+
drop(exports);
208+
Handler::Latest(Proxy::new(&mut store, &instance)?)
209+
}
210+
HandlerType::Spin => panic!("should have used execute_spin instead"),
211+
}
212+
};
216213

217214
let span = tracing::debug_span!("execute_wasi");
218215
let handle = task::spawn(
@@ -336,28 +333,52 @@ impl HttpHandlerExecutor {
336333
}
337334

338335
/// Whether this handler uses the custom Spin http handler interface for wasi-http
339-
enum HandlerType {
336+
#[derive(Copy, Clone)]
337+
pub enum HandlerType {
340338
Spin,
341-
Wasi,
339+
Wasi0_2,
340+
Wasi2023_11_10,
341+
Wasi2023_10_18,
342342
}
343343

344344
const WASI_HTTP_EXPORT_2023_10_18: &str = "wasi:http/[email protected]";
345345
const WASI_HTTP_EXPORT_2023_11_10: &str = "wasi:http/[email protected]";
346346
const WASI_HTTP_EXPORT_0_2_0: &str = "wasi:http/[email protected]";
347347

348348
impl HandlerType {
349-
/// Determine the handler type from the exports
350-
fn from_exports(mut exports: wasmtime::component::Exports<'_>) -> Option<HandlerType> {
351-
if exports.instance(WASI_HTTP_EXPORT_2023_10_18).is_some()
352-
|| exports.instance(WASI_HTTP_EXPORT_2023_11_10).is_some()
353-
|| exports.instance(WASI_HTTP_EXPORT_0_2_0).is_some()
354-
{
355-
return Some(HandlerType::Wasi);
356-
}
357-
if exports.instance("fermyon:spin/inbound-http").is_some() {
358-
return Some(HandlerType::Spin);
349+
/// Determine the handler type from the exports of a component
350+
pub fn from_component<T>(engine: &Engine<T>, component: &Component) -> Result<HandlerType> {
351+
let mut handler_ty = None;
352+
353+
let mut set = |ty: HandlerType| {
354+
if handler_ty.is_none() {
355+
handler_ty = Some(ty);
356+
Ok(())
357+
} else {
358+
Err(anyhow!(
359+
"component exports multiple different handlers but \
360+
it's expected to export only one"
361+
))
362+
}
363+
};
364+
let ty = component.component_type();
365+
for (name, _) in ty.exports(engine.as_ref()) {
366+
match name {
367+
WASI_HTTP_EXPORT_2023_10_18 => set(HandlerType::Wasi2023_10_18)?,
368+
WASI_HTTP_EXPORT_2023_11_10 => set(HandlerType::Wasi2023_11_10)?,
369+
WASI_HTTP_EXPORT_0_2_0 => set(HandlerType::Wasi0_2)?,
370+
"fermyon:spin/inbound-http" => set(HandlerType::Spin)?,
371+
_ => {}
372+
}
359373
}
360-
None
374+
375+
handler_ty.ok_or_else(|| {
376+
anyhow!(
377+
"Expected component to either export `{WASI_HTTP_EXPORT_2023_10_18}`, \
378+
`{WASI_HTTP_EXPORT_2023_11_10}`, `{WASI_HTTP_EXPORT_0_2_0}`, \
379+
or `fermyon:spin/inbound-http` but it exported none of those"
380+
)
381+
})
361382
}
362383
}
363384

crates/trigger-http/src/lib.rs

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ use wasmtime_wasi_http::{
5353
};
5454

5555
use crate::{
56-
handler::HttpHandlerExecutor,
56+
handler::{HandlerType, HttpHandlerExecutor},
5757
instrument::{instrument_error, MatchedRoute},
5858
wagi::WagiHttpExecutor,
5959
};
@@ -102,12 +102,12 @@ impl CliArgs {
102102
}
103103

104104
pub enum HttpInstancePre {
105-
Component(spin_core::InstancePre<RuntimeData>),
105+
Component(spin_core::InstancePre<RuntimeData>, HandlerType),
106106
Module(spin_core::ModuleInstancePre<RuntimeData>),
107107
}
108108

109109
pub enum HttpInstance {
110-
Component(spin_core::Instance),
110+
Component(spin_core::Instance, HandlerType),
111111
Module(spin_core::ModuleInstance),
112112
}
113113

@@ -205,16 +205,20 @@ impl TriggerInstancePre<RuntimeData, HttpTriggerConfig> for HttpInstancePre {
205205
))
206206
} else {
207207
let comp = component.load_component(engine).await?;
208-
Ok(HttpInstancePre::Component(engine.instantiate_pre(&comp)?))
208+
let handler_ty = HandlerType::from_component(engine, &comp)?;
209+
Ok(HttpInstancePre::Component(
210+
engine.instantiate_pre(&comp)?,
211+
handler_ty,
212+
))
209213
}
210214
}
211215

212216
async fn instantiate(&self, store: &mut Store) -> Result<HttpInstance> {
213217
match self {
214-
HttpInstancePre::Component(pre) => pre
215-
.instantiate_async(store)
216-
.await
217-
.map(HttpInstance::Component),
218+
HttpInstancePre::Component(pre, ty) => Ok(HttpInstance::Component(
219+
pre.instantiate_async(store).await?,
220+
*ty,
221+
)),
218222
HttpInstancePre::Module(pre) => {
219223
pre.instantiate_async(store).await.map(HttpInstance::Module)
220224
}

0 commit comments

Comments
 (0)