From 87337b5dd762fd156abfa3967add98d883de5c46 Mon Sep 17 00:00:00 2001 From: Lann Martin Date: Thu, 25 Jun 2026 09:35:42 -0400 Subject: [PATCH 1/4] trigger-http: Remove useless server::HttpExecutor trait --- crates/trigger-http/src/server.rs | 13 ------------- crates/trigger-http/src/spin.rs | 5 ++--- crates/trigger-http/src/wagi.rs | 6 +++--- crates/trigger-http/src/wasi.rs | 6 +++--- 4 files changed, 8 insertions(+), 22 deletions(-) diff --git a/crates/trigger-http/src/server.rs b/crates/trigger-http/src/server.rs index 69120b40de..8783890ccf 100644 --- a/crates/trigger-http/src/server.rs +++ b/crates/trigger-http/src/server.rs @@ -1,6 +1,5 @@ use std::{ collections::HashMap, - future::Future, io::{ErrorKind, IsTerminal}, net::SocketAddr, sync::{Arc, OnceLock}, @@ -46,7 +45,6 @@ use wasmtime_wasi_http::p2::body::HyperOutgoingBody; use crate::{ Body, InstanceReuseConfig, NotFoundRouteKind, OutputFormat, TlsConfig, TriggerApp, - TriggerInstanceBuilder, headers::strip_forbidden_headers, instrument::{MatchedRoute, finalize_http_span, http_span, instrument_error}, outbound_http::OutboundHttpInterceptor, @@ -688,17 +686,6 @@ fn set_req_uri(req: &mut Request, scheme: Scheme) -> anyhow::Result<()> { Ok(()) } -/// An HTTP executor. -pub(crate) trait HttpExecutor { - fn execute( - &self, - instance_builder: TriggerInstanceBuilder, - route_match: &RouteMatch<'_, '_>, - req: Request, - client_addr: SocketAddr, - ) -> impl Future>>; -} - pub(crate) struct HttpHandlerState { trigger_app: Arc>, component_id: String, diff --git a/crates/trigger-http/src/spin.rs b/crates/trigger-http/src/spin.rs index a2a8d02925..c2c01b4c9a 100644 --- a/crates/trigger-http/src/spin.rs +++ b/crates/trigger-http/src/spin.rs @@ -12,16 +12,15 @@ use tracing::{Level, instrument}; use crate::{ Body, TriggerInstanceBuilder, headers::{append_headers, prepare_request_headers}, - server::HttpExecutor, }; /// An [`HttpExecutor`] that uses the `fermyon:spin/inbound-http` interface. #[derive(Clone)] pub struct SpinHttpExecutor; -impl HttpExecutor for SpinHttpExecutor { +impl SpinHttpExecutor { #[instrument(name = "spin_trigger_http.execute_wasm", skip_all, err(level = Level::INFO), fields(otel.name = format!("execute_wasm_component {}", route_match.lookup_key().to_string())))] - async fn execute( + pub async fn execute( &self, instance_builder: TriggerInstanceBuilder<'_, F>, route_match: &RouteMatch<'_, '_>, diff --git a/crates/trigger-http/src/wagi.rs b/crates/trigger-http/src/wagi.rs index ee6dba5bc2..b118ff83a0 100644 --- a/crates/trigger-http/src/wagi.rs +++ b/crates/trigger-http/src/wagi.rs @@ -11,16 +11,16 @@ use wasmtime_wasi::p2::bindings::CommandIndices; use wasmtime_wasi::p2::pipe::MemoryOutputPipe; use wasmtime_wasi_http::p2::body::HyperIncomingBody as Body; -use crate::{TriggerInstanceBuilder, headers::compute_default_headers, server::HttpExecutor}; +use crate::{TriggerInstanceBuilder, headers::compute_default_headers}; pub struct WagiHttpExecutor<'a> { pub wagi_config: &'a WagiTriggerConfig, pub indices: &'a CommandIndices, } -impl HttpExecutor for WagiHttpExecutor<'_> { +impl WagiHttpExecutor<'_> { #[instrument(name = "spin_trigger_http.execute_wagi", skip_all, err(level = Level::INFO), fields(otel.name = format!("execute_wagi_component {}", route_match.lookup_key().to_string())))] - async fn execute( + pub async fn execute( &self, mut instance_builder: TriggerInstanceBuilder<'_, F>, route_match: &RouteMatch<'_, '_>, diff --git a/crates/trigger-http/src/wasi.rs b/crates/trigger-http/src/wasi.rs index ae8721983d..4cf74191be 100644 --- a/crates/trigger-http/src/wasi.rs +++ b/crates/trigger-http/src/wasi.rs @@ -16,7 +16,7 @@ use wasmtime_wasi_http::handler::HandlerState; use wasmtime_wasi_http::p2::bindings::http::types::Scheme; use wasmtime_wasi_http::p2::{bindings::Proxy, body::HyperIncomingBody as Body}; -use crate::{TriggerInstanceBuilder, headers::prepare_request_headers, server::HttpExecutor}; +use crate::{TriggerInstanceBuilder, headers::prepare_request_headers}; pub(super) fn prepare_request( route_match: &RouteMatch<'_, '_>, @@ -51,9 +51,9 @@ pub struct WasiHttpExecutor<'a, S: HandlerState> { pub handler_type: &'a HandlerType, } -impl HttpExecutor for WasiHttpExecutor<'_, S> { +impl WasiHttpExecutor<'_, S> { #[instrument(name = "spin_trigger_http.execute_wasm", skip_all, err(level = Level::INFO), fields(otel.name = format!("execute_wasm_component {}", route_match.lookup_key().to_string())))] - async fn execute( + pub async fn execute( &self, instance_builder: TriggerInstanceBuilder<'_, F>, route_match: &RouteMatch<'_, '_>, From 1ed6d2f6b3c7a3647914dbb205d968cf9ef83524 Mon Sep 17 00:00:00 2001 From: Lann Martin Date: Thu, 25 Jun 2026 16:02:07 -0400 Subject: [PATCH 2/4] trigger-http: Fix service chaining for wasip3 handlers --- crates/trigger-http/src/server.rs | 76 +++++++++++++++++++------------ crates/trigger-http/src/spin.rs | 17 ++++--- crates/trigger-http/src/wagi.rs | 21 ++++----- crates/trigger-http/src/wasi.rs | 12 +++-- crates/trigger-http/src/wasip3.rs | 6 ++- 5 files changed, 77 insertions(+), 55 deletions(-) diff --git a/crates/trigger-http/src/server.rs b/crates/trigger-http/src/server.rs index 8783890ccf..24d106a7ca 100644 --- a/crates/trigger-http/src/server.rs +++ b/crates/trigger-http/src/server.rs @@ -45,6 +45,7 @@ use wasmtime_wasi_http::p2::body::HyperOutgoingBody; use crate::{ Body, InstanceReuseConfig, NotFoundRouteKind, OutputFormat, TlsConfig, TriggerApp, + TriggerInstanceBuilder, headers::strip_forbidden_headers, instrument::{MatchedRoute, finalize_http_span, http_span, instrument_error}, outbound_http::OutboundHttpInterceptor, @@ -178,9 +179,10 @@ impl HttpServer { None | Some(HttpExecutorType::Http) => HandlerType::from_instance_pre( pre, HttpHandlerState { - trigger_app: trigger_app.clone(), component_id: component_id.into(), reuse_config, + server: Default::default(), + self_scheme: Default::default(), }, )?, Some(HttpExecutorType::Wagi(wagi_config)) => { @@ -327,7 +329,7 @@ impl HttpServer { server_scheme: Scheme, client_addr: SocketAddr, ) -> anyhow::Result> { - set_req_uri(&mut req, server_scheme.clone())?; + set_req_uri(&mut req, server_scheme)?; let app_id = self .trigger_app .app() @@ -353,7 +355,6 @@ impl HttpServer { self.respond_wasm_component( req, route_match, - server_scheme, client_addr, component, &trigger_config.executor, @@ -381,28 +382,10 @@ impl HttpServer { self: &Arc, req: Request, route_match: RouteMatch<'_, '_>, - server_scheme: Scheme, client_addr: SocketAddr, component_id: &str, executor: &Option, ) -> anyhow::Result> { - let mut instance_builder = self.trigger_app.prepare(component_id)?; - - // Set up outbound HTTP request origin and service chaining - // The outbound HTTP factor is required since both inbound and outbound wasi HTTP - // implementations assume they use the same underlying wasmtime resource storage. - // Eventually, we may be able to factor this out to a separate factor. - let outbound_http = instance_builder - .factor_builder::() - .context( - "The wasi HTTP trigger was configured without the required wasi outbound http support", - )?; - - let self_addr = self.get_local_addr(); - let origin = SelfRequestOrigin::create(server_scheme, &self_addr.to_string())?; - outbound_http.set_self_request_origin(origin); - outbound_http.set_request_interceptor(OutboundHttpInterceptor::new(self.clone()))?; - // Prepare HTTP executor let handler_type = self .component_handler_types @@ -414,19 +397,19 @@ impl HttpServer { HttpExecutorType::Http => match handler_type { HandlerType::Spin => { SpinHttpExecutor - .execute(instance_builder, &route_match, req, client_addr) + .execute(self, &route_match, req, client_addr, component_id) .await } HandlerType::Wasi0_3(_, handler) => { Wasip3HttpExecutor(handler) - .execute(&route_match, req, client_addr) + .execute(self, &route_match, req, client_addr) .await } HandlerType::Wasi0_2(_) | HandlerType::Wasi2023_11_10(_) | HandlerType::Wasi2023_10_18(_) => { WasiHttpExecutor { handler_type } - .execute(instance_builder, &route_match, req, client_addr) + .execute(self, &route_match, req, client_addr, component_id) .await } HandlerType::Wagi(_) => unreachable!(), @@ -441,7 +424,7 @@ impl HttpServer { indices, }; executor - .execute(instance_builder, &route_match, req, client_addr) + .execute(self, &route_match, req, client_addr, component_id) .await } }; @@ -458,6 +441,31 @@ impl HttpServer { } } + pub(crate) fn trigger_instance_builder( + self: &'_ Arc, + component_id: &str, + self_scheme: Option<&Scheme>, + ) -> anyhow::Result> { + let mut instance_builder = self.trigger_app.prepare(component_id)?; + + // Set up outbound HTTP request origin and service chaining + // The outbound HTTP factor is required since both inbound and outbound wasi HTTP + // implementations assume they use the same underlying wasmtime resource storage. + // Eventually, we may be able to factor this out to a separate factor. + let outbound_http = instance_builder + .factor_builder::() + .context( + "The wasi HTTP trigger was configured without the required wasi outbound http support", + )?; + + let self_scheme = self_scheme.cloned().unwrap_or(Scheme::HTTPS); + let self_addr = self.get_local_addr(); + let origin = SelfRequestOrigin::create(self_scheme, &self_addr.to_string())?; + outbound_http.set_self_request_origin(origin); + outbound_http.set_request_interceptor(OutboundHttpInterceptor::new(self.clone()))?; + Ok(instance_builder) + } + fn respond_static_response( sr: &spin_http::config::StaticResponse, ) -> anyhow::Result> { @@ -687,9 +695,19 @@ fn set_req_uri(req: &mut Request, scheme: Scheme) -> anyhow::Result<()> { } pub(crate) struct HttpHandlerState { - trigger_app: Arc>, component_id: String, reuse_config: InstanceReuseConfig, + server: OnceLock>>, + self_scheme: OnceLock, +} + +impl HttpHandlerState { + pub(crate) fn init_once(&self, server: &Arc>, first_uri: &Uri) { + self.server.get_or_init(|| server.clone()); + if let Some(scheme) = first_uri.scheme() { + self.self_scheme.get_or_init(|| scheme.clone()); + } + } } impl HandlerState for HttpHandlerState { @@ -698,8 +716,10 @@ impl HandlerState for HttpHandlerState { fn new_store(&self, _req_id: Option) -> wasmtime::Result> { Ok(StoreBundle { store: self - .trigger_app - .prepare(&self.component_id) + .server + .get() + .expect("server should have been set") + .trigger_instance_builder(&self.component_id, self.self_scheme.get()) .to_wasmtime_result()? .instantiate_store(()) .to_wasmtime_result()? diff --git a/crates/trigger-http/src/spin.rs b/crates/trigger-http/src/spin.rs index c2c01b4c9a..87d694f8ef 100644 --- a/crates/trigger-http/src/spin.rs +++ b/crates/trigger-http/src/spin.rs @@ -1,4 +1,4 @@ -use std::net::SocketAddr; +use std::{net::SocketAddr, sync::Arc}; use anyhow::Result; use http_body_util::BodyExt; @@ -10,7 +10,7 @@ use spin_world::v1::http_types; use tracing::{Level, instrument}; use crate::{ - Body, TriggerInstanceBuilder, + Body, HttpServer, headers::{append_headers, prepare_request_headers}, }; @@ -22,19 +22,18 @@ impl SpinHttpExecutor { #[instrument(name = "spin_trigger_http.execute_wasm", skip_all, err(level = Level::INFO), fields(otel.name = format!("execute_wasm_component {}", route_match.lookup_key().to_string())))] pub async fn execute( &self, - instance_builder: TriggerInstanceBuilder<'_, F>, + server: &Arc>, route_match: &RouteMatch<'_, '_>, req: Request, client_addr: SocketAddr, + component_id: &str, ) -> Result> { - let spin_http::routes::TriggerLookupKey::Component(component_id) = route_match.lookup_key() - else { - unreachable!() - }; - tracing::trace!("Executing request using the Spin executor for component {component_id}"); - let (instance, mut store) = instance_builder.instantiate(()).await?; + let (instance, mut store) = server + .trigger_instance_builder(component_id, req.uri().scheme())? + .instantiate(()) + .await?; let headers = prepare_request_headers(&req, route_match, client_addr)?; // Expects here are safe since we have already checked that this diff --git a/crates/trigger-http/src/wagi.rs b/crates/trigger-http/src/wagi.rs index b118ff83a0..8900017a98 100644 --- a/crates/trigger-http/src/wagi.rs +++ b/crates/trigger-http/src/wagi.rs @@ -1,4 +1,4 @@ -use std::{io::Cursor, net::SocketAddr}; +use std::{io::Cursor, net::SocketAddr, sync::Arc}; use anyhow::{Context, Result, ensure}; use http_body_util::BodyExt; @@ -11,7 +11,7 @@ use wasmtime_wasi::p2::bindings::CommandIndices; use wasmtime_wasi::p2::pipe::MemoryOutputPipe; use wasmtime_wasi_http::p2::body::HyperIncomingBody as Body; -use crate::{TriggerInstanceBuilder, headers::compute_default_headers}; +use crate::{HttpServer, headers::compute_default_headers}; pub struct WagiHttpExecutor<'a> { pub wagi_config: &'a WagiTriggerConfig, @@ -22,20 +22,13 @@ impl WagiHttpExecutor<'_> { #[instrument(name = "spin_trigger_http.execute_wagi", skip_all, err(level = Level::INFO), fields(otel.name = format!("execute_wagi_component {}", route_match.lookup_key().to_string())))] pub async fn execute( &self, - mut instance_builder: TriggerInstanceBuilder<'_, F>, + server: &Arc>, route_match: &RouteMatch<'_, '_>, req: Request, client_addr: SocketAddr, + component_id: &str, ) -> Result> { - let spin_http::routes::TriggerLookupKey::Component(component) = route_match.lookup_key() - else { - unreachable!() - }; - - tracing::trace!( - "Executing request using the Wagi executor for component {}", - component - ); + tracing::trace!("Executing request using the Wagi executor for component {component_id}"); let uri_path = req.uri().path(); @@ -79,6 +72,8 @@ impl WagiHttpExecutor<'_> { let stdout = MemoryOutputPipe::new(usize::MAX); + let mut instance_builder = + server.trigger_instance_builder(component_id, parts.uri.scheme())?; let wasi_builder = instance_builder .factor_builder::() .context("The wagi HTTP trigger was configured without the required wasi support")?; @@ -110,7 +105,7 @@ impl WagiHttpExecutor<'_> { let stdout = stdout.try_into_inner().unwrap(); ensure!( !stdout.is_empty(), - "The {component:?} component is configured to use the WAGI executor \ + "The {component_id:?} component is configured to use the WAGI executor \ but did not write to stdout. Check the `executor` in spin.toml." ); diff --git a/crates/trigger-http/src/wasi.rs b/crates/trigger-http/src/wasi.rs index 4cf74191be..c035e6d8c7 100644 --- a/crates/trigger-http/src/wasi.rs +++ b/crates/trigger-http/src/wasi.rs @@ -1,5 +1,6 @@ use std::io::IsTerminal; use std::net::SocketAddr; +use std::sync::Arc; use anyhow::{Context, Result, anyhow}; use futures::TryFutureExt; @@ -16,7 +17,8 @@ use wasmtime_wasi_http::handler::HandlerState; use wasmtime_wasi_http::p2::bindings::http::types::Scheme; use wasmtime_wasi_http::p2::{bindings::Proxy, body::HyperIncomingBody as Body}; -use crate::{TriggerInstanceBuilder, headers::prepare_request_headers}; +use crate::HttpServer; +use crate::headers::prepare_request_headers; pub(super) fn prepare_request( route_match: &RouteMatch<'_, '_>, @@ -55,14 +57,18 @@ impl WasiHttpExecutor<'_, S> { #[instrument(name = "spin_trigger_http.execute_wasm", skip_all, err(level = Level::INFO), fields(otel.name = format!("execute_wasm_component {}", route_match.lookup_key().to_string())))] pub async fn execute( &self, - instance_builder: TriggerInstanceBuilder<'_, F>, + server: &Arc>, route_match: &RouteMatch<'_, '_>, mut req: Request, client_addr: SocketAddr, + component_id: &str, ) -> Result> { prepare_request(route_match, &mut req, client_addr)?; - let (instance, mut store) = instance_builder.instantiate(()).await?; + let (instance, mut store) = server + .trigger_instance_builder(component_id, req.uri().scheme())? + .instantiate(()) + .await?; let mut wasi_http = spin_factor_outbound_http::OutboundHttpFactor::get_wasi_http_impl( store.data_mut().factors_instance_state_mut(), diff --git a/crates/trigger-http/src/wasip3.rs b/crates/trigger-http/src/wasip3.rs index a65e075902..e5793af684 100644 --- a/crates/trigger-http/src/wasip3.rs +++ b/crates/trigger-http/src/wasip3.rs @@ -1,4 +1,4 @@ -use crate::server::HttpHandlerState; +use crate::{HttpServer, server::HttpHandlerState}; use anyhow::{Context as _, Result}; use futures::{FutureExt, channel::oneshot}; use http_body_util::BodyExt; @@ -6,7 +6,7 @@ use spin_factor_outbound_http::{NotifyOnDropBody, p3_to_p2_error_code}; use spin_factors::RuntimeFactors; use spin_factors_executor::InstanceState; use spin_http::routes::RouteMatch; -use std::net::SocketAddr; +use std::{net::SocketAddr, sync::Arc}; use tracing::{Instrument, Level, instrument}; use wasmtime::component::Accessor; use wasmtime_wasi_http::{ @@ -24,10 +24,12 @@ impl Wasip3HttpExecutor<'_, F> { #[instrument(name = "spin_trigger_http.execute_wasm", skip_all, err(level = Level::INFO), fields(otel.name = format!("execute_wasm_component {}", route_match.lookup_key().to_string())))] pub async fn execute( &self, + server: &Arc>, route_match: &RouteMatch<'_, '_>, mut req: http::Request, client_addr: SocketAddr, ) -> Result> { + self.0.state().init_once(server, req.uri()); super::wasi::prepare_request(route_match, &mut req, client_addr)?; let getter = (|data| wasi_http::(data).unwrap()) From 1298907bce83c7ac52c7636934a37dbfb1df390c Mon Sep 17 00:00:00 2001 From: Lann Martin Date: Thu, 25 Jun 2026 09:41:22 -0400 Subject: [PATCH 3/4] trigger-http: Reorder Handlertypes Make the order consistently ordered by descending newness. This is technically a change in behavior, but only in the rare (or non-existant) case of a component exporting multiple handler interfaces. In any case the new logic of preferring the most recent interface is almost certainly what someone would expect to happen if they intentionally implement multiple exports. --- crates/http/src/trigger.rs | 30 ++++++++++++++++-------------- crates/trigger-http/src/server.rs | 10 +++++----- crates/trigger-http/src/wasi.rs | 11 ++++++----- 3 files changed, 27 insertions(+), 24 deletions(-) diff --git a/crates/http/src/trigger.rs b/crates/http/src/trigger.rs index 6737ed9d11..eaa7fc3c0e 100644 --- a/crates/http/src/trigger.rs +++ b/crates/http/src/trigger.rs @@ -20,23 +20,25 @@ pub fn default_base() -> String { } /// The type of http handler export used by a component. +// Note: These are ordered by descending newness, reflecting the selection order +// in from_instance_pre below. pub enum HandlerType { - Spin, - Wagi(CommandIndices), - Wasi0_2(ProxyIndices), Wasi0_3(ServiceIndices, ProxyHandler), + Wasi0_2(ProxyIndices), Wasi2023_11_10(ProxyIndices2023_11_10), Wasi2023_10_18(ProxyIndices2023_10_18), + Spin, + Wagi(CommandIndices), } -/// The `incoming-handler` export for `wasi:http` version rc-2023-10-18 -const WASI_HTTP_EXPORT_2023_10_18: &str = "wasi:http/incoming-handler@0.2.0-rc-2023-10-18"; -/// The `incoming-handler` export for `wasi:http` version rc-2023-11-10 -const WASI_HTTP_EXPORT_2023_11_10: &str = "wasi:http/incoming-handler@0.2.0-rc-2023-11-10"; -/// The `incoming-handler` export prefix for all `wasi:http` 0.2 versions -const WASI_HTTP_EXPORT_0_2_PREFIX: &str = "wasi:http/incoming-handler@0.2"; /// The `handler` export `wasi:http` version 0.3.0-rc-2025-08-15 const WASI_HTTP_EXPORT_0_3_0_RC_03_15: &str = "wasi:http/handler@0.3.0-rc-2026-03-15"; +/// The `incoming-handler` export prefix for all `wasi:http` 0.2 versions +const WASI_HTTP_EXPORT_0_2_PREFIX: &str = "wasi:http/incoming-handler@0.2"; +/// The `incoming-handler` export for `wasi:http` version rc-2023-11-10 +const WASI_HTTP_EXPORT_2023_11_10: &str = "wasi:http/incoming-handler@0.2.0-rc-2023-11-10"; +/// The `incoming-handler` export for `wasi:http` version rc-2023-10-18 +const WASI_HTTP_EXPORT_2023_10_18: &str = "wasi:http/incoming-handler@0.2.0-rc-2023-10-18"; /// The `inbound-http` export for `fermyon:spin` const SPIN_HTTP_EXPORT: &str = "fermyon:spin/inbound-http"; @@ -44,9 +46,6 @@ impl> HandlerType { /// Determine the handler type from the exports of a component. pub fn from_instance_pre(pre: &InstancePre, handler_state: S) -> anyhow::Result { let mut candidates = Vec::new(); - if let Ok(indices) = ProxyIndices::new(pre) { - candidates.push(HandlerType::Wasi0_2(indices)); - } if let Ok(pre) = ServicePre::new(pre.clone()) { candidates.push(HandlerType::Wasi0_3( // We `.unwrap()` here because the `Ok(_)` result from @@ -56,12 +55,15 @@ impl> HandlerType { ProxyHandler::new(handler_state, ProxyPre::P3(pre)), )); } - if let Ok(indices) = ProxyIndices2023_10_18::new(pre) { - candidates.push(HandlerType::Wasi2023_10_18(indices)); + if let Ok(indices) = ProxyIndices::new(pre) { + candidates.push(HandlerType::Wasi0_2(indices)); } if let Ok(indices) = ProxyIndices2023_11_10::new(pre) { candidates.push(HandlerType::Wasi2023_11_10(indices)); } + if let Ok(indices) = ProxyIndices2023_10_18::new(pre) { + candidates.push(HandlerType::Wasi2023_10_18(indices)); + } if pre .component() .get_export_index(None, SPIN_HTTP_EXPORT) diff --git a/crates/trigger-http/src/server.rs b/crates/trigger-http/src/server.rs index 24d106a7ca..fdd2eb9695 100644 --- a/crates/trigger-http/src/server.rs +++ b/crates/trigger-http/src/server.rs @@ -395,11 +395,6 @@ impl HttpServer { let res = match executor { HttpExecutorType::Http => match handler_type { - HandlerType::Spin => { - SpinHttpExecutor - .execute(self, &route_match, req, client_addr, component_id) - .await - } HandlerType::Wasi0_3(_, handler) => { Wasip3HttpExecutor(handler) .execute(self, &route_match, req, client_addr) @@ -412,6 +407,11 @@ impl HttpServer { .execute(self, &route_match, req, client_addr, component_id) .await } + HandlerType::Spin => { + SpinHttpExecutor + .execute(self, &route_match, req, client_addr, component_id) + .await + } HandlerType::Wagi(_) => unreachable!(), }, HttpExecutorType::Wagi(wagi_config) => { diff --git a/crates/trigger-http/src/wasi.rs b/crates/trigger-http/src/wasi.rs index c035e6d8c7..e6f3cb79b2 100644 --- a/crates/trigger-http/src/wasi.rs +++ b/crates/trigger-http/src/wasi.rs @@ -87,15 +87,16 @@ impl WasiHttpExecutor<'_, S> { } let handler = match self.handler_type { - HandlerType::Wasi2023_10_18(indices) => { - let guest = indices.load(&mut store, &instance)?; - Handler::Handler2023_10_18(guest) - } + HandlerType::Wasi0_2(indices) => Handler::Latest(indices.load(&mut store, &instance)?), HandlerType::Wasi2023_11_10(indices) => { let guest = indices.load(&mut store, &instance)?; Handler::Handler2023_11_10(guest) } - HandlerType::Wasi0_2(indices) => Handler::Latest(indices.load(&mut store, &instance)?), + HandlerType::Wasi2023_10_18(indices) => { + let guest = indices.load(&mut store, &instance)?; + Handler::Handler2023_10_18(guest) + } + HandlerType::Wasi0_3(_, _) => unreachable!("should have used Wasip3HttpExecutor"), HandlerType::Spin => unreachable!("should have used SpinHttpExecutor"), HandlerType::Wagi(_) => unreachable!("should have used WagiExecutor instead"), From db64555e7395f6ac39e8081eb2d78436250958d1 Mon Sep 17 00:00:00 2001 From: Lann Martin Date: Thu, 25 Jun 2026 10:16:01 -0400 Subject: [PATCH 4/4] Minor refactors --- crates/factors-executor/src/lib.rs | 51 +++++++++++++++++++----------- crates/http/src/trigger.rs | 7 ++-- 2 files changed, 36 insertions(+), 22 deletions(-) diff --git a/crates/factors-executor/src/lib.rs b/crates/factors-executor/src/lib.rs index dce2b32594..3e9aa844dc 100644 --- a/crates/factors-executor/src/lib.rs +++ b/crates/factors-executor/src/lib.rs @@ -203,12 +203,12 @@ impl FactorsExecutorApp { /// /// It is generic over the executor's [`RuntimeFactors`] and any ad-hoc additional /// per-instance state needed by the caller. -pub struct FactorsInstanceBuilder<'a, F: RuntimeFactors, U: 'static> { +pub struct FactorsInstanceBuilder<'a, T: RuntimeFactors, U: 'static> { app_component: AppComponent<'a>, store_builder: spin_core::StoreBuilder, - factor_builders: F::InstanceBuilders, - instance_pre: &'a InstancePre, - factors: &'a F, + factor_builders: T::InstanceBuilders, + instance_pre: &'a InstancePre, + factors: &'a T, } impl FactorsInstanceBuilder<'_, T, U> { @@ -244,7 +244,7 @@ impl FactorsInstanceBuilder<'_, T, U> { } impl FactorsInstanceBuilder<'_, T, U> { - /// Instantiates the instance with the given executor instance state + /// Instantiates the instance with the given executor instance state. pub async fn instantiate( self, executor_instance_state: U, @@ -252,16 +252,13 @@ impl FactorsInstanceBuilder<'_, T, U> { spin_core::Instance, spin_core::Store>, )> { - let instance_state = InstanceState { - core: Default::default(), - factors: self.factors.build_instance_state(self.factor_builders)?, - executor: executor_instance_state, - cpu_time_elapsed: Duration::from_millis(0), - cpu_time_last_entry: None, - memory_used_on_init: 0, - component_id: self.app_component.id().into(), - }; - let mut store = self.store_builder.build(instance_state)?; + let mut store = Self::build_store( + self.store_builder, + self.factor_builders, + &self.app_component, + self.factors, + executor_instance_state, + )?; #[cfg(feature = "cpu-time-metrics")] store.as_mut().call_hook(|mut store, hook| { @@ -277,20 +274,38 @@ impl FactorsInstanceBuilder<'_, T, U> { Ok((instance, store)) } + /// Builds a store with the given executor instance state. pub fn instantiate_store( self, executor_instance_state: U, ) -> anyhow::Result>> { + Self::build_store( + self.store_builder, + self.factor_builders, + &self.app_component, + self.factors, + executor_instance_state, + ) + } + + fn build_store( + store_builder: spin_core::StoreBuilder, + factor_builders: T::InstanceBuilders, + app_component: &AppComponent, + factors: &T, + executor_instance_state: U, + ) -> anyhow::Result>> { + let factors = factors.build_instance_state(factor_builders)?; let instance_state = InstanceState { core: Default::default(), - factors: self.factors.build_instance_state(self.factor_builders)?, + factors, executor: executor_instance_state, cpu_time_elapsed: Duration::from_millis(0), cpu_time_last_entry: None, memory_used_on_init: 0, - component_id: self.app_component.id().into(), + component_id: app_component.id().into(), }; - self.store_builder.build(instance_state) + store_builder.build(instance_state) } } diff --git a/crates/http/src/trigger.rs b/crates/http/src/trigger.rs index eaa7fc3c0e..31e68b9fc8 100644 --- a/crates/http/src/trigger.rs +++ b/crates/http/src/trigger.rs @@ -73,6 +73,7 @@ impl> HandlerType { } match candidates.len() { + 1 => Ok(candidates.pop().unwrap()), 0 => { anyhow::bail!( "Expected component to export one of \ @@ -85,10 +86,8 @@ impl> HandlerType { If you're sure this is an HTTP module, check if a Spin upgrade is available: this may handle the newer version." ) } - 1 => Ok(candidates.pop().unwrap()), - _ => anyhow::bail!( - "component exports multiple different handlers but \ - it's expected to export only one" + too_many => anyhow::bail!( + "component exports {too_many} different handlers but it's expected to export only one" ), } }