spin_http/
config.rs

1use serde::{Deserialize, Serialize};
2
3/// Configuration for the HTTP trigger
4#[derive(Clone, Debug, Default, Deserialize, Serialize)]
5#[serde(deny_unknown_fields)]
6pub struct HttpTriggerConfig {
7    /// Component ID to invoke
8    pub component: String,
9    /// HTTP route the component will be invoked for
10    pub route: HttpTriggerRouteConfig,
11    /// The HTTP executor the component requires
12    #[serde(default)]
13    pub executor: Option<HttpExecutorType>,
14}
15
16/// An HTTP trigger route
17#[derive(Clone, Debug, Deserialize, Serialize)]
18#[serde(untagged)]
19pub enum HttpTriggerRouteConfig {
20    Route(String),
21    Private(HttpPrivateEndpoint),
22}
23
24/// Indicates that a trigger is a private endpoint (not routable).
25#[derive(Clone, Debug, Default, Deserialize, Serialize)]
26#[serde(deny_unknown_fields)]
27pub struct HttpPrivateEndpoint {
28    /// Whether the private endpoint is private. This must be true.
29    pub private: bool,
30}
31
32impl Default for HttpTriggerRouteConfig {
33    fn default() -> Self {
34        Self::Route(Default::default())
35    }
36}
37
38impl<T: Into<String>> From<T> for HttpTriggerRouteConfig {
39    fn from(value: T) -> Self {
40        Self::Route(value.into())
41    }
42}
43
44/// The executor for the HTTP component.
45/// The component can either implement the Spin HTTP interface,
46/// the `wasi-http` interface, or the Wagi CGI interface.
47///
48/// If an executor is not specified, the inferred default is `HttpExecutor::Spin`.
49#[derive(Clone, Debug, Default, Deserialize, Serialize)]
50#[serde(deny_unknown_fields, rename_all = "lowercase", tag = "type")]
51pub enum HttpExecutorType {
52    /// The component implements an HTTP based interface.
53    ///
54    /// This can be either `fermyon:spin/inbound-http` or `wasi:http/incoming-handler`
55    #[default]
56    #[serde(alias = "spin")]
57    Http,
58    /// The component implements the Wagi CGI interface.
59    Wagi(WagiTriggerConfig),
60}
61
62/// Wagi specific configuration for the http executor.
63#[derive(Clone, Debug, Deserialize, Serialize)]
64#[serde(default, deny_unknown_fields)]
65pub struct WagiTriggerConfig {
66    /// The name of the entrypoint. (DEPRECATED)
67    #[serde(skip_serializing)]
68    pub entrypoint: String,
69
70    /// A string representation of the argv array.
71    ///
72    /// This should be a space-separate list of strings. The value
73    /// ${SCRIPT_NAME} will be replaced with the Wagi SCRIPT_NAME,
74    /// and the value ${ARGS} will be replaced with the query parameter
75    /// name/value pairs presented as args. For example,
76    /// `param1=val1&param2=val2` will become `param1=val1 param2=val2`,
77    /// which will then be presented to the program as two arguments
78    /// in argv.
79    pub argv: String,
80}
81
82impl Default for WagiTriggerConfig {
83    fn default() -> Self {
84        /// This is the default Wagi entrypoint.
85        const WAGI_DEFAULT_ENTRYPOINT: &str = "_start";
86        const WAGI_DEFAULT_ARGV: &str = "${SCRIPT_NAME} ${ARGS}";
87
88        Self {
89            entrypoint: WAGI_DEFAULT_ENTRYPOINT.to_owned(),
90            argv: WAGI_DEFAULT_ARGV.to_owned(),
91        }
92    }
93}
94
95#[cfg(test)]
96mod tests {
97    use super::*;
98
99    #[test]
100    fn wagi_config_smoke_test() {
101        let HttpExecutorType::Wagi(config) = toml::toml! { type = "wagi" }.try_into().unwrap()
102        else {
103            panic!("wrong type");
104        };
105        assert_eq!(config.entrypoint, "_start");
106        assert_eq!(config.argv, "${SCRIPT_NAME} ${ARGS}");
107    }
108}