feather_runtime/http/
request.rs

1use std::io;
2/// Simple alias for error results in this module.
3/// We use a boxed std error to avoid depending on the removed crate error type.
4pub type Error = Box<dyn std::error::Error>;
5use bytes::Bytes;
6use http::{Extensions, HeaderMap, Method, Uri, Version};
7use std::str::FromStr;
8use std::{borrow::Cow, collections::HashMap, fmt};
9use urlencoding::decode;
10
11/// Contains a incoming Http Request
12#[derive(Debug)]
13pub struct Request {
14    /// The HTTP method of the request.<br>
15    /// For example, GET, POST, PUT, DELETE, etc.
16    pub method: Method,
17    /// The URI of the request.
18    pub uri: Uri,
19    /// The HTTP version of the request.
20    pub version: Version,
21    /// The headers of the request.
22    pub headers: HeaderMap,
23    /// The body of the request.
24    pub body: Bytes,
25    /// The extensions of the request.
26    pub extensions: Extensions,
27
28    /// The route parameters of the request.
29    params: HashMap<String, String>,
30}
31
32impl Request {
33    /// Parses a Request from raw bytes if parsing fails returns a error
34    pub fn parse(raw: &[u8]) -> Result<Request, Error> {
35        let mut headers = [httparse::EMPTY_HEADER; 64];
36        let mut request = httparse::Request::new(&mut headers);
37
38        request.parse(raw).map_err(|e| -> Error { Box::new(io::Error::new(io::ErrorKind::InvalidData, format!("Failed to parse request: {}", e))) })?;
39
40        // Get the method string, ensuring it exists
41        let method_str = request.method.ok_or_else(|| -> Error { Box::new(io::Error::new(io::ErrorKind::InvalidData, "Missing HTTP method")) })?;
42
43        // Validate method against known HTTP methods
44        let method = Method::from_str(method_str).map_err(|_| -> Error { Box::new(io::Error::new(io::ErrorKind::InvalidData, format!("Invalid HTTP method: {}", method_str))) })?;
45        let path = request.path.ok_or_else(|| -> Error { Box::new(io::Error::new(io::ErrorKind::InvalidData, "Failed to parse URI")) })?;
46        let uri: Uri = path.parse().map_err(|e| -> Error { Box::new(io::Error::new(io::ErrorKind::InvalidData, format!("Failed to parse URI: {}", e))) })?;
47
48        let version = match request.version {
49            Some(0) => Version::HTTP_10,
50            Some(1) => Version::HTTP_11,
51            _ => Version::HTTP_11,
52        };
53        let mut header_map = HeaderMap::new();
54        for header in request.headers.iter() {
55            let name = http::header::HeaderName::from_bytes(header.name.as_bytes()).map_err(|e| -> Error { Box::new(io::Error::new(io::ErrorKind::InvalidData, format!("Failed to parse header name: {}", e))) })?;
56            let value = http::header::HeaderValue::from_bytes(header.value).map_err(|e| -> Error { Box::new(io::Error::new(io::ErrorKind::InvalidData, format!("Failed to parse header value: {}", e))) })?;
57
58            header_map.insert(name, value);
59        }
60        let body_start = raw.windows(4).position(|w| w == b"\r\n\r\n").map(|pos| pos + 4).unwrap_or(raw.len());
61        let body = Bytes::copy_from_slice(&raw[body_start..]);
62        let extensions = Extensions::new();
63        Ok(Request {
64            method,
65            uri,
66            version,
67            headers: header_map,
68            body,
69            extensions,
70            params: HashMap::new(),
71        })
72    }
73
74    /// Parses the body of the request as Serde JSON Value. Returns an error if the body is not valid JSON.  
75    /// This method is useful for parsing JSON payloads in requests.  
76    #[cfg(feature = "json")]
77    pub fn json(&self) -> Result<serde_json::Value, Error> {
78        serde_json::from_slice(&self.body).map_err(|e| -> Error { Box::new(io::Error::new(io::ErrorKind::InvalidData, format!("Failed to parse JSON body: {}", e))) })
79    }
80    /// Returns a Hashmap of the query parameters of the Request.  
81    /// Returns a Error if parsing fails
82    pub fn query(&self) -> Result<HashMap<String, String>, Error> {
83        if let Some(query) = self.uri.query() {
84            serde_urlencoded::from_str(query).map_err(|e| -> Error { Box::new(io::Error::new(io::ErrorKind::InvalidData, format!("Failed to Parse Query parameters {}", e))) })
85        } else {
86            Ok(HashMap::new())
87        }
88    }
89
90    pub fn set_params(&mut self, params: HashMap<String, String>) {
91        self.params = params;
92    }
93
94    pub fn param(&self, key: &str) -> Option<&str> {
95        self.params.get(key).map(|v| &**v)
96    }
97
98    /// Returns the path of the Request
99    pub fn path(&self) -> Cow<'_, str> {
100        decode(self.uri.path()).unwrap()
101    }
102}
103
104impl fmt::Display for Request {
105    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
106        write!(f, "{} {}", self.method, self.uri.path())
107    }
108}