spin_contrib_http/
request.rs

1use spin_sdk::http::{HeaderValue, Method, Request};
2
3const HEADER_SPIN_PATH_INFO: &str = "spin-path-info";
4
5/// Extensions for spin_sdk::http::Request
6pub trait Contrib {
7    /// returns route segments of the HTTP request.
8    ///
9    /// If the request was invoked using the root URL, an empty vector is returned
10    ///
11    /// # Example
12    /// ```rust
13    /// use spin_sdk::http::RequestBuilder;
14    /// use spin_contrib_http::request::Contrib;
15    ///
16    /// let fake_req = RequestBuilder::new(spin_sdk::http::Method::Get, "http://foo/bar")
17    ///    .header("spin-path-info", "/foo/bar/baz")
18    ///    .body(()).build();
19    ///
20    /// let segments = fake_req.get_route_segments();
21    ///
22    /// assert_eq!(segments.is_some(), true);
23    /// let segments = segments.unwrap();
24    /// assert_eq!(segments.len(), 3);
25    /// assert_eq!(segments[0], "foo");
26    /// assert_eq!(segments[1], "bar");
27    /// assert_eq!(segments[2], "baz");
28    /// ```
29    fn get_route_segments(&self) -> Option<Vec<&str>>;
30
31    /// Determines if the request is a preflight request
32    fn is_preflight_request(&self) -> bool;
33
34    /// Returns a header value as String. If header is not present or value is empty, an empty string is returned
35    fn get_header_value_as_string(&self, header_name: &str) -> String;
36}
37
38impl Contrib for Request {
39    fn get_route_segments(&self) -> Option<Vec<&str>> {
40        let spin_path_header_value = self.header(HEADER_SPIN_PATH_INFO)?;
41        let header_value = spin_path_header_value.as_str()?;
42        if header_value.trim().is_empty() || header_value == "/" {
43            return None;
44        };
45
46        let mut segments = header_value.split('/').collect::<Vec<&str>>();
47        segments.remove(0);
48        Some(segments)
49    }
50
51    fn is_preflight_request(&self) -> bool {
52        self.method() == &Method::Options && self.header(http::header::ORIGIN.as_str()).is_some()
53    }
54
55    fn get_header_value_as_string(&self, header_name: &str) -> String {
56        self.header(header_name)
57            .unwrap_or(&HeaderValue::string(String::default()))
58            .as_str()
59            .unwrap()
60            .to_string()
61    }
62}
63
64#[cfg(test)]
65mod tests {
66    use spin_sdk::http::{Method, RequestBuilder};
67
68    use super::*;
69
70    #[test]
71    fn get_route_segments_should_return_provided_segments() {
72        let req = RequestBuilder::new(Method::Get, "http://foo.bar")
73            .header(HEADER_SPIN_PATH_INFO, "/foo/bar/baz")
74            .body(())
75            .build();
76        let segments = req.get_route_segments();
77        assert_eq!(segments.is_some(), true);
78        let segments = segments.unwrap();
79        assert_eq!(segments.len(), 3);
80        assert_eq!(segments[0], "foo");
81        assert_eq!(segments[1], "bar");
82        assert_eq!(segments[2], "baz");
83    }
84
85    #[test]
86    fn get_route_segments_should_empty_vector_for_root_url() {
87        let test_data = vec!["/", "", " "];
88        for data in test_data {
89            let req = RequestBuilder::new(spin_sdk::http::Method::Get, "http://foo.bar")
90                .header(HEADER_SPIN_PATH_INFO, data)
91                .body(())
92                .build();
93
94            assert_eq!(req.get_route_segments(), None);
95        }
96    }
97
98    #[test]
99    fn get_header_value_as_string_should_return_correct_values() {
100        let test_data = vec![
101            ("my-header", "foo", "my-header", "foo"),
102            ("my-header", "", "my-header", ""),
103            ("my-header", "foo", "my-other-header", ""),
104            ("my-header", "", "my-other-header", ""),
105        ];
106        for data in test_data {
107            let req = RequestBuilder::new(spin_sdk::http::Method::Get, "http://foo.bar")
108                .header(data.0, data.1)
109                .body(())
110                .build();
111
112            assert_eq!(req.get_header_value_as_string(data.2), data.3);
113        }
114    }
115}