1use serde::{Deserialize, Serialize};
2use spin_factor_outbound_http::wasi_2023_10_18::ProxyIndices as ProxyIndices2023_10_18;
3use spin_factor_outbound_http::wasi_2023_11_10::ProxyIndices as ProxyIndices2023_11_10;
4use wasmtime::component::InstancePre;
5use wasmtime_wasi::p2::bindings::CommandIndices;
6use wasmtime_wasi_http::bindings::ProxyIndices;
7use wasmtime_wasi_http::p3::bindings::ProxyIndices as P3ProxyIndices;
8
9use crate::config::HttpExecutorType;
10
11#[derive(Clone, Debug, Default, Deserialize, Serialize)]
12#[serde(deny_unknown_fields)]
13pub struct Metadata {
14 #[serde(default = "default_base")]
16 pub base: String,
17}
18
19pub fn default_base() -> String {
20 "/".into()
21}
22
23pub enum HandlerType {
25 Spin,
26 Wagi(CommandIndices),
27 Wasi0_2(ProxyIndices),
28 Wasi0_3(P3ProxyIndices),
29 Wasi2023_11_10(ProxyIndices2023_11_10),
30 Wasi2023_10_18(ProxyIndices2023_10_18),
31}
32
33const WASI_HTTP_EXPORT_2023_10_18: &str = "wasi:http/incoming-handler@0.2.0-rc-2023-10-18";
35const WASI_HTTP_EXPORT_2023_11_10: &str = "wasi:http/incoming-handler@0.2.0-rc-2023-11-10";
37const WASI_HTTP_EXPORT_0_2_PREFIX: &str = "wasi:http/incoming-handler@0.2";
39const WASI_HTTP_EXPORT_0_3_0_RC_2025_09_16: &str = "wasi:http/handler@0.3.0-rc-2025-09-16";
41const SPIN_HTTP_EXPORT: &str = "fermyon:spin/inbound-http";
43
44impl HandlerType {
45 pub fn from_instance_pre<T>(pre: &InstancePre<T>) -> anyhow::Result<HandlerType> {
47 let mut candidates = Vec::new();
48 if let Ok(indices) = ProxyIndices::new(pre) {
49 candidates.push(HandlerType::Wasi0_2(indices));
50 }
51 if let Ok(indices) = P3ProxyIndices::new(pre) {
52 candidates.push(HandlerType::Wasi0_3(indices));
53 }
54 if let Ok(indices) = ProxyIndices2023_10_18::new(pre) {
55 candidates.push(HandlerType::Wasi2023_10_18(indices));
56 }
57 if let Ok(indices) = ProxyIndices2023_11_10::new(pre) {
58 candidates.push(HandlerType::Wasi2023_11_10(indices));
59 }
60 if pre
61 .component()
62 .get_export_index(None, SPIN_HTTP_EXPORT)
63 .is_some()
64 {
65 candidates.push(HandlerType::Spin);
66 }
67
68 match candidates.len() {
69 0 => {
70 anyhow::bail!(
71 "Expected component to export one of \
72 `{WASI_HTTP_EXPORT_2023_10_18}`, \
73 `{WASI_HTTP_EXPORT_2023_11_10}`, \
74 `{WASI_HTTP_EXPORT_0_2_PREFIX}.*`, \
75 `{WASI_HTTP_EXPORT_0_3_0_RC_2025_09_16}`, \
76 or `{SPIN_HTTP_EXPORT}` but it exported none of those. \
77 This may mean the component handles a different trigger, or that its `wasi:http` export is newer then those supported by Spin. \
78 If you're sure this is an HTTP module, check if a Spin upgrade is available: this may handle the newer version."
79 )
80 }
81 1 => Ok(candidates.pop().unwrap()),
82 _ => anyhow::bail!(
83 "component exports multiple different handlers but \
84 it's expected to export only one"
85 ),
86 }
87 }
88
89 pub fn validate_executor(&self, executor: &Option<HttpExecutorType>) -> anyhow::Result<()> {
91 match (self, executor) {
92 (HandlerType::Wasi0_3(_), Some(HttpExecutorType::Wasip3Unstable)) => {
93 terminal::warn!(
94 "You’re using wasip3-unstable, an experimental executor for the \
95 WASI Preview 3 RC interfaces, which will be removed in a future Spin release."
96 );
97 Ok(())
98 }
99 (HandlerType::Wasi0_3(_), Some(_) | None) => {
100 anyhow::bail!(
101 "`{WASI_HTTP_EXPORT_0_3_0_RC_2025_09_16}` is currently unstable and will be \
102 removed in a future Spin release. You can opt-in to this unstable interface \
103 by adding `executor = {{ type = \"wasip3-unstable\" }}` to the appropriate \
104 `[[trigger.http]]` section of your spin.toml file."
105 );
106 }
107 (handler_type, Some(HttpExecutorType::Wasip3Unstable)) => {
108 anyhow::bail!(
109 "The wasip3-unstable trigger executor expected a component that \
110 exports `{WASI_HTTP_EXPORT_0_3_0_RC_2025_09_16}` but found a \
111 component with type {name}",
112 name = handler_type.name(),
113 )
114 }
115 (_, _) => Ok(()),
116 }
117 }
118
119 fn name(&self) -> &str {
120 match self {
121 HandlerType::Spin => SPIN_HTTP_EXPORT,
122 HandlerType::Wasi0_2(_) => WASI_HTTP_EXPORT_0_2_PREFIX,
123 HandlerType::Wasi0_3(_) => WASI_HTTP_EXPORT_0_3_0_RC_2025_09_16,
124 HandlerType::Wasi2023_11_10(_) => WASI_HTTP_EXPORT_2023_11_10,
125 HandlerType::Wasi2023_10_18(_) => WASI_HTTP_EXPORT_2023_10_18,
126 _ => unreachable!(), }
128 }
129}