spin_http/
trigger.rs

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
9#[derive(Clone, Debug, Default, Deserialize, Serialize)]
10#[serde(deny_unknown_fields)]
11pub struct Metadata {
12    // The based url
13    #[serde(default = "default_base")]
14    pub base: String,
15}
16
17pub fn default_base() -> String {
18    "/".into()
19}
20
21/// The type of http handler export used by a component.
22pub enum HandlerType {
23    Spin,
24    Wagi(CommandIndices),
25    Wasi0_2(ProxyIndices),
26    Wasi0_3(P3ProxyIndices),
27    Wasi2023_11_10(ProxyIndices2023_11_10),
28    Wasi2023_10_18(ProxyIndices2023_10_18),
29}
30
31/// The `incoming-handler` export for `wasi:http` version rc-2023-10-18
32const WASI_HTTP_EXPORT_2023_10_18: &str = "wasi:http/incoming-handler@0.2.0-rc-2023-10-18";
33/// The `incoming-handler` export for `wasi:http` version rc-2023-11-10
34const WASI_HTTP_EXPORT_2023_11_10: &str = "wasi:http/incoming-handler@0.2.0-rc-2023-11-10";
35/// The `incoming-handler` export prefix for all `wasi:http` 0.2 versions
36const WASI_HTTP_EXPORT_0_2_PREFIX: &str = "wasi:http/incoming-handler@0.2";
37/// The `handler` export `wasi:http` version 0.3.0-rc-2025-08-15
38const WASI_HTTP_EXPORT_0_3_0_RC_2025_09_16: &str = "wasi:http/handler@0.3.0-rc-2025-09-16";
39/// The `inbound-http` export for `fermyon:spin`
40const SPIN_HTTP_EXPORT: &str = "fermyon:spin/inbound-http";
41
42impl HandlerType {
43    /// Determine the handler type from the exports of a component.
44    pub fn from_instance_pre<T>(pre: &InstancePre<T>) -> anyhow::Result<HandlerType> {
45        let mut candidates = Vec::new();
46        if let Ok(indices) = ProxyIndices::new(pre) {
47            candidates.push(HandlerType::Wasi0_2(indices));
48        }
49        if let Ok(indices) = P3ProxyIndices::new(pre) {
50            candidates.push(HandlerType::Wasi0_3(indices));
51        }
52        if let Ok(indices) = ProxyIndices2023_10_18::new(pre) {
53            candidates.push(HandlerType::Wasi2023_10_18(indices));
54        }
55        if let Ok(indices) = ProxyIndices2023_11_10::new(pre) {
56            candidates.push(HandlerType::Wasi2023_11_10(indices));
57        }
58        if pre
59            .component()
60            .get_export_index(None, SPIN_HTTP_EXPORT)
61            .is_some()
62        {
63            candidates.push(HandlerType::Spin);
64        }
65
66        match candidates.len() {
67            0 => {
68                anyhow::bail!(
69                    "Expected component to export one of \
70                    `{WASI_HTTP_EXPORT_2023_10_18}`, \
71                    `{WASI_HTTP_EXPORT_2023_11_10}`, \
72                    `{WASI_HTTP_EXPORT_0_2_PREFIX}.*`, \
73                    `{WASI_HTTP_EXPORT_0_3_0_RC_2025_09_16}`, \
74                     or `{SPIN_HTTP_EXPORT}` but it exported none of those. \
75                     This may mean the component handles a different trigger, or that its `wasi:http` export is newer then those supported by Spin. \
76                     If you're sure this is an HTTP module, check if a Spin upgrade is available: this may handle the newer version."
77                )
78            }
79            1 => Ok(candidates.pop().unwrap()),
80            _ => anyhow::bail!(
81                "component exports multiple different handlers but \
82                     it's expected to export only one"
83            ),
84        }
85    }
86}