spin_http/
config.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
use serde::{Deserialize, Serialize};

/// Configuration for the HTTP trigger
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
#[serde(deny_unknown_fields)]
pub struct HttpTriggerConfig {
    /// Component ID to invoke
    pub component: String,
    /// HTTP route the component will be invoked for
    pub route: HttpTriggerRouteConfig,
    /// The HTTP executor the component requires
    #[serde(default)]
    pub executor: Option<HttpExecutorType>,
}

/// An HTTP trigger route
#[derive(Clone, Debug, Deserialize, Serialize)]
#[serde(untagged)]
pub enum HttpTriggerRouteConfig {
    Route(String),
    Private(HttpPrivateEndpoint),
}

/// Indicates that a trigger is a private endpoint (not routable).
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
#[serde(deny_unknown_fields)]
pub struct HttpPrivateEndpoint {
    /// Whether the private endpoint is private. This must be true.
    pub private: bool,
}

impl Default for HttpTriggerRouteConfig {
    fn default() -> Self {
        Self::Route(Default::default())
    }
}

impl<T: Into<String>> From<T> for HttpTriggerRouteConfig {
    fn from(value: T) -> Self {
        Self::Route(value.into())
    }
}

/// The executor for the HTTP component.
/// The component can either implement the Spin HTTP interface,
/// the `wasi-http` interface, or the Wagi CGI interface.
///
/// If an executor is not specified, the inferred default is `HttpExecutor::Spin`.
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
#[serde(deny_unknown_fields, rename_all = "lowercase", tag = "type")]
pub enum HttpExecutorType {
    /// The component implements an HTTP based interface.
    ///
    /// This can be either `fermyon:spin/inbound-http` or `wasi:http/incoming-handler`
    #[default]
    #[serde(alias = "spin")]
    Http,
    /// The component implements the Wagi CGI interface.
    Wagi(WagiTriggerConfig),
}

/// Wagi specific configuration for the http executor.
#[derive(Clone, Debug, Deserialize, Serialize)]
#[serde(default, deny_unknown_fields)]
pub struct WagiTriggerConfig {
    /// The name of the entrypoint. (DEPRECATED)
    #[serde(skip_serializing)]
    pub entrypoint: String,

    /// A string representation of the argv array.
    ///
    /// This should be a space-separate list of strings. The value
    /// ${SCRIPT_NAME} will be replaced with the Wagi SCRIPT_NAME,
    /// and the value ${ARGS} will be replaced with the query parameter
    /// name/value pairs presented as args. For example,
    /// `param1=val1&param2=val2` will become `param1=val1 param2=val2`,
    /// which will then be presented to the program as two arguments
    /// in argv.
    pub argv: String,
}

impl Default for WagiTriggerConfig {
    fn default() -> Self {
        /// This is the default Wagi entrypoint.
        const WAGI_DEFAULT_ENTRYPOINT: &str = "_start";
        const WAGI_DEFAULT_ARGV: &str = "${SCRIPT_NAME} ${ARGS}";

        Self {
            entrypoint: WAGI_DEFAULT_ENTRYPOINT.to_owned(),
            argv: WAGI_DEFAULT_ARGV.to_owned(),
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn wagi_config_smoke_test() {
        let HttpExecutorType::Wagi(config) = toml::toml! { type = "wagi" }.try_into().unwrap()
        else {
            panic!("wrong type");
        };
        assert_eq!(config.entrypoint, "_start");
        assert_eq!(config.argv, "${SCRIPT_NAME} ${ARGS}");
    }
}