From 0772633a71d59cd001966de363ae8f41a172a86a Mon Sep 17 00:00:00 2001 From: Ryan Levick Date: Wed, 25 Sep 2024 10:48:22 +0200 Subject: [PATCH 1/3] Move HandlerType to spin-http Signed-off-by: Ryan Levick --- Cargo.lock | 1 + crates/http/Cargo.toml | 1 + crates/http/src/trigger.rs | 63 +++++++++++++++++++++++++++++++ crates/trigger-http/src/server.rs | 60 +---------------------------- crates/trigger-http/src/wasi.rs | 7 +--- 5 files changed, 68 insertions(+), 64 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index caaf4ad993..9fab68d38d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7397,6 +7397,7 @@ dependencies = [ "spin-locked-app", "toml 0.8.19", "tracing", + "wasmtime", "wasmtime-wasi-http", ] diff --git a/crates/http/Cargo.toml b/crates/http/Cargo.toml index 78fe2d4189..a781e53c3b 100644 --- a/crates/http/Cargo.toml +++ b/crates/http/Cargo.toml @@ -16,6 +16,7 @@ serde = { workspace = true } spin-app = { path = "../app", optional = true } spin-locked-app = { path = "../locked-app" } tracing = { workspace = true } +wasmtime = { workspace = true } wasmtime-wasi-http = { workspace = true, optional = true } [dev-dependencies] diff --git a/crates/http/src/trigger.rs b/crates/http/src/trigger.rs index 37a030ed0d..6f21eb1330 100644 --- a/crates/http/src/trigger.rs +++ b/crates/http/src/trigger.rs @@ -1,4 +1,5 @@ use serde::{Deserialize, Serialize}; +use wasmtime::component::Component; #[derive(Clone, Debug, Default, Deserialize, Serialize)] #[serde(deny_unknown_fields)] @@ -11,3 +12,65 @@ pub struct Metadata { pub fn default_base() -> String { "/".into() } + +/// The type of http handler export used by a component. +#[derive(Clone, Copy)] +pub enum HandlerType { + Spin, + Wagi, + Wasi0_2, + Wasi2023_11_10, + Wasi2023_10_18, +} + +/// The `incoming-handler` export for `wasi:http` version rc-2023-10-18 +pub 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 +pub 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 0.2.0 +pub const WASI_HTTP_EXPORT_0_2_0: &str = "wasi:http/incoming-handler@0.2.0"; +/// The `incoming-handler` export for `wasi:http` version 0.2.1 +pub const WASI_HTTP_EXPORT_0_2_1: &str = "wasi:http/incoming-handler@0.2.1"; + +impl HandlerType { + /// Determine the handler type from the exports of a component + pub fn from_component( + engine: impl AsRef, + component: &Component, + ) -> anyhow::Result { + let mut handler_ty = None; + + let mut set = |ty: HandlerType| { + if handler_ty.is_none() { + handler_ty = Some(ty); + Ok(()) + } else { + Err(anyhow::anyhow!( + "component exports multiple different handlers but \ + it's expected to export only one" + )) + } + }; + let ty = component.component_type(); + for (name, _) in ty.exports(engine.as_ref()) { + match name { + WASI_HTTP_EXPORT_2023_10_18 => set(HandlerType::Wasi2023_10_18)?, + WASI_HTTP_EXPORT_2023_11_10 => set(HandlerType::Wasi2023_11_10)?, + WASI_HTTP_EXPORT_0_2_0 | WASI_HTTP_EXPORT_0_2_1 => set(HandlerType::Wasi0_2)?, + "fermyon:spin/inbound-http" => set(HandlerType::Spin)?, + _ => {} + } + } + + handler_ty.ok_or_else(|| { + anyhow::anyhow!( + "Expected component to export one of \ + `{WASI_HTTP_EXPORT_2023_10_18}`, \ + `{WASI_HTTP_EXPORT_2023_11_10}`, \ + `{WASI_HTTP_EXPORT_0_2_0}`, \ + `{WASI_HTTP_EXPORT_0_2_1}`, \ + or `fermyon:spin/inbound-http` but it exported none of those" + ) + }) + } +} diff --git a/crates/trigger-http/src/server.rs b/crates/trigger-http/src/server.rs index 46b2836efd..4defbe188c 100644 --- a/crates/trigger-http/src/server.rs +++ b/crates/trigger-http/src/server.rs @@ -20,6 +20,7 @@ use spin_http::{ body, config::{HttpExecutorType, HttpTriggerConfig}, routes::{RouteMatch, Router}, + trigger::HandlerType, }; use tokio::{ io::{AsyncRead, AsyncWrite}, @@ -27,7 +28,6 @@ use tokio::{ task, }; use tracing::Instrument; -use wasmtime::component::Component; use wasmtime_wasi_http::body::HyperOutgoingBody; use crate::{ @@ -464,61 +464,3 @@ pub(crate) trait HttpExecutor: Clone + Send + Sync + 'static { client_addr: SocketAddr, ) -> impl Future>>; } - -/// Whether this handler uses the custom Spin http handler interface for wasi-http -#[derive(Clone, Copy)] -pub enum HandlerType { - Spin, - Wagi, - Wasi0_2, - Wasi2023_11_10, - Wasi2023_10_18, -} - -pub const WASI_HTTP_EXPORT_2023_10_18: &str = "wasi:http/incoming-handler@0.2.0-rc-2023-10-18"; -pub const WASI_HTTP_EXPORT_2023_11_10: &str = "wasi:http/incoming-handler@0.2.0-rc-2023-11-10"; -pub const WASI_HTTP_EXPORT_0_2_0: &str = "wasi:http/incoming-handler@0.2.0"; -pub const WASI_HTTP_EXPORT_0_2_1: &str = "wasi:http/incoming-handler@0.2.1"; - -impl HandlerType { - /// Determine the handler type from the exports of a component - pub fn from_component( - engine: impl AsRef, - component: &Component, - ) -> anyhow::Result { - let mut handler_ty = None; - - let mut set = |ty: HandlerType| { - if handler_ty.is_none() { - handler_ty = Some(ty); - Ok(()) - } else { - Err(anyhow::anyhow!( - "component exports multiple different handlers but \ - it's expected to export only one" - )) - } - }; - let ty = component.component_type(); - for (name, _) in ty.exports(engine.as_ref()) { - match name { - WASI_HTTP_EXPORT_2023_10_18 => set(HandlerType::Wasi2023_10_18)?, - WASI_HTTP_EXPORT_2023_11_10 => set(HandlerType::Wasi2023_11_10)?, - WASI_HTTP_EXPORT_0_2_0 | WASI_HTTP_EXPORT_0_2_1 => set(HandlerType::Wasi0_2)?, - "fermyon:spin/inbound-http" => set(HandlerType::Spin)?, - _ => {} - } - } - - handler_ty.ok_or_else(|| { - anyhow::anyhow!( - "Expected component to export one of \ - `{WASI_HTTP_EXPORT_2023_10_18}`, \ - `{WASI_HTTP_EXPORT_2023_11_10}`, \ - `{WASI_HTTP_EXPORT_0_2_0}`, \ - `{WASI_HTTP_EXPORT_0_2_1}`, \ - or `fermyon:spin/inbound-http` but it exported none of those" - ) - }) - } -} diff --git a/crates/trigger-http/src/wasi.rs b/crates/trigger-http/src/wasi.rs index 2724f21da8..0e431a488b 100644 --- a/crates/trigger-http/src/wasi.rs +++ b/crates/trigger-http/src/wasi.rs @@ -8,16 +8,13 @@ use spin_factor_outbound_http::wasi_2023_10_18::exports::wasi::http::incoming_ha use spin_factor_outbound_http::wasi_2023_11_10::exports::wasi::http::incoming_handler as incoming_handler2023_11_10; use spin_factors::RuntimeFactors; use spin_http::routes::RouteMatch; +use spin_http::trigger::HandlerType; use tokio::{sync::oneshot, task}; use tracing::{instrument, Instrument, Level}; use wasmtime_wasi_http::bindings::http::types::Scheme; use wasmtime_wasi_http::{bindings::Proxy, body::HyperIncomingBody as Body, WasiHttpView}; -use crate::{ - headers::prepare_request_headers, - server::{HandlerType, HttpExecutor}, - TriggerInstanceBuilder, -}; +use crate::{headers::prepare_request_headers, server::HttpExecutor, TriggerInstanceBuilder}; /// An [`HttpExecutor`] that uses the `wasi:http/incoming-handler` interface. #[derive(Clone)] From 286d5a7e308b340fd1be8b735adbfe09964f6df8 Mon Sep 17 00:00:00 2001 From: Ryan Levick Date: Wed, 25 Sep 2024 12:42:16 +0200 Subject: [PATCH 2/3] Expose engine and component from executor app Signed-off-by: Ryan Levick --- Cargo.lock | 2 +- crates/factors-executor/src/lib.rs | 10 ++++++++++ crates/http/Cargo.toml | 2 +- crates/http/src/trigger.rs | 12 +++++++----- crates/trigger-http/src/server.rs | 2 +- 5 files changed, 20 insertions(+), 8 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9fab68d38d..c0524dca10 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7389,7 +7389,7 @@ dependencies = [ "http 1.1.0", "http-body-util", "hyper 1.4.1", - "indexmap 1.9.3", + "indexmap 2.5.0", "percent-encoding", "routefinder", "serde 1.0.210", diff --git a/crates/factors-executor/src/lib.rs b/crates/factors-executor/src/lib.rs index eb0b8fd6d8..7d1f1706ef 100644 --- a/crates/factors-executor/src/lib.rs +++ b/crates/factors-executor/src/lib.rs @@ -211,6 +211,16 @@ impl<'a, T: RuntimeFactors, U> FactorsInstanceBuilder<'a, T, U> { pub fn factor_builder(&mut self) -> Option<&mut F::InstanceBuilder> { self.factor_builders().for_factor::() } + + /// Returns the underlying wasmtime engine for the instance. + pub fn engine(&self) -> &spin_core::wasmtime::Engine { + self.instance_pre.engine() + } + + /// Returns the compiled component for the instance. + pub fn component(&self) -> &Component { + self.instance_pre.component() + } } impl<'a, T: RuntimeFactors, U: Send> FactorsInstanceBuilder<'a, T, U> { diff --git a/crates/http/Cargo.toml b/crates/http/Cargo.toml index a781e53c3b..1365afa9f0 100644 --- a/crates/http/Cargo.toml +++ b/crates/http/Cargo.toml @@ -9,7 +9,7 @@ anyhow = { workspace = true } http = { workspace = true } http-body-util = { workspace = true } hyper = { workspace = true } -indexmap = "1" +indexmap = "2" percent-encoding = "2" routefinder = "0.5.4" serde = { workspace = true } diff --git a/crates/http/src/trigger.rs b/crates/http/src/trigger.rs index 6f21eb1330..3bfef9de63 100644 --- a/crates/http/src/trigger.rs +++ b/crates/http/src/trigger.rs @@ -31,11 +31,13 @@ pub const WASI_HTTP_EXPORT_2023_11_10: &str = "wasi:http/incoming-handler@0.2.0- pub const WASI_HTTP_EXPORT_0_2_0: &str = "wasi:http/incoming-handler@0.2.0"; /// The `incoming-handler` export for `wasi:http` version 0.2.1 pub const WASI_HTTP_EXPORT_0_2_1: &str = "wasi:http/incoming-handler@0.2.1"; +/// The `inbound-http` export for `fermyon:spin` +pub const SPIN_HTTP_EXPORT: &str = "fermyon:spin/inbound-http"; impl HandlerType { - /// Determine the handler type from the exports of a component + /// Determine the handler type from the exports of a component. pub fn from_component( - engine: impl AsRef, + engine: &wasmtime::Engine, component: &Component, ) -> anyhow::Result { let mut handler_ty = None; @@ -52,12 +54,12 @@ impl HandlerType { } }; let ty = component.component_type(); - for (name, _) in ty.exports(engine.as_ref()) { + for (name, _) in ty.exports(engine) { match name { WASI_HTTP_EXPORT_2023_10_18 => set(HandlerType::Wasi2023_10_18)?, WASI_HTTP_EXPORT_2023_11_10 => set(HandlerType::Wasi2023_11_10)?, WASI_HTTP_EXPORT_0_2_0 | WASI_HTTP_EXPORT_0_2_1 => set(HandlerType::Wasi0_2)?, - "fermyon:spin/inbound-http" => set(HandlerType::Spin)?, + SPIN_HTTP_EXPORT => set(HandlerType::Spin)?, _ => {} } } @@ -69,7 +71,7 @@ impl HandlerType { `{WASI_HTTP_EXPORT_2023_11_10}`, \ `{WASI_HTTP_EXPORT_0_2_0}`, \ `{WASI_HTTP_EXPORT_0_2_1}`, \ - or `fermyon:spin/inbound-http` but it exported none of those" + or `{SPIN_HTTP_EXPORT}` but it exported none of those" ) }) } diff --git a/crates/trigger-http/src/server.rs b/crates/trigger-http/src/server.rs index 4defbe188c..0d0747c52b 100644 --- a/crates/trigger-http/src/server.rs +++ b/crates/trigger-http/src/server.rs @@ -104,7 +104,7 @@ impl HttpServer { let handler_type = match &trigger_config.executor { None | Some(HttpExecutorType::Http) => { let component = trigger_app.get_component(component_id)?; - HandlerType::from_component(trigger_app.engine(), component)? + HandlerType::from_component(trigger_app.engine().as_ref(), component)? } Some(HttpExecutorType::Wagi(wagi_config)) => { anyhow::ensure!( From e2693b60552758cbc558cf6e0c7b7113c3a6d2d4 Mon Sep 17 00:00:00 2001 From: Ryan Levick Date: Wed, 25 Sep 2024 15:48:47 +0200 Subject: [PATCH 3/3] Differentiate between spin_core::Engine and wasmtime::Engine Signed-off-by: Ryan Levick --- crates/core/src/lib.rs | 1 + crates/core/src/store.rs | 6 +++--- crates/factors-executor/src/lib.rs | 2 +- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/crates/core/src/lib.rs b/crates/core/src/lib.rs index cb7fb53419..167fe23c7a 100644 --- a/crates/core/src/lib.rs +++ b/crates/core/src/lib.rs @@ -18,6 +18,7 @@ use tracing::instrument; use wasmtime::{InstanceAllocationStrategy, PoolingAllocationConfig}; pub use async_trait::async_trait; +pub use wasmtime::Engine as WasmtimeEngine; pub use wasmtime::{ self, component::{Component, Instance, InstancePre, Linker}, diff --git a/crates/core/src/store.rs b/crates/core/src/store.rs index 9afbd3cfd3..8e4a2a89c3 100644 --- a/crates/core/src/store.rs +++ b/crates/core/src/store.rs @@ -1,7 +1,7 @@ use anyhow::Result; use std::time::{Duration, Instant}; -use crate::{limits::StoreLimitsAsync, State}; +use crate::{limits::StoreLimitsAsync, State, WasmtimeEngine}; #[cfg(doc)] use crate::EngineBuilder; @@ -80,14 +80,14 @@ impl wasmtime::AsContextMut for Store { /// /// A new [`StoreBuilder`] can be obtained with [`crate::Engine::store_builder`]. pub struct StoreBuilder { - engine: wasmtime::Engine, + engine: WasmtimeEngine, epoch_tick_interval: Duration, store_limits: StoreLimitsAsync, } impl StoreBuilder { // Called by Engine::store_builder. - pub(crate) fn new(engine: wasmtime::Engine, epoch_tick_interval: Duration) -> Self { + pub(crate) fn new(engine: WasmtimeEngine, epoch_tick_interval: Duration) -> Self { Self { engine, epoch_tick_interval, diff --git a/crates/factors-executor/src/lib.rs b/crates/factors-executor/src/lib.rs index 7d1f1706ef..8d2adee770 100644 --- a/crates/factors-executor/src/lib.rs +++ b/crates/factors-executor/src/lib.rs @@ -213,7 +213,7 @@ impl<'a, T: RuntimeFactors, U> FactorsInstanceBuilder<'a, T, U> { } /// Returns the underlying wasmtime engine for the instance. - pub fn engine(&self) -> &spin_core::wasmtime::Engine { + pub fn wasmtime_engine(&self) -> &spin_core::WasmtimeEngine { self.instance_pre.engine() }