edjx/
request.rs

1//! Read-only HTTP request input for HTTP-trigger serverless functions.
2//!
3//! For executing HTTP Requests, use [`crate::HttpFetch`].
4
5use super::error::{Error, HTTPBodyTooLargeError, HeaderError, HttpError, UriError};
6use super::stream::{BaseStream, ReadStream};
7use http::Uri;
8use http::{header::HeaderName, HeaderMap, Version};
9use num_derive::FromPrimitive;
10use num_traits::FromPrimitive;
11use std::str;
12use std::str::FromStr;
13use std::{collections::HashMap, convert::TryFrom};
14use url::form_urlencoded;
15
16#[link(wasm_import_module = "request")]
17extern "C" {
18    fn http_cur_req_get_method() -> i32;
19    fn http_cur_req_get_uri(err_code: *mut i32) -> i32;
20    fn http_cur_req_get_body(error_code: *mut i32) -> i32;
21    fn http_cur_req_get_read_stream(read_sd: *mut u32) -> i32;
22    fn http_cur_req_get_all_header_keys(err_code: *mut i32) -> i32;
23    fn http_cur_req_get_header_value(
24        header_ptr: *const u8,
25        header_len: i32,
26        err_code: *mut i32,
27    ) -> i32;
28}
29
30/// Request which may include version, body, headers, method, and URL.
31///
32#[derive(Debug)]
33pub struct HttpRequest {
34    version: Version,
35    method: HttpMethod,
36    uri: Uri,
37    headers: HeaderMap,
38    body: Option<Vec<u8>>,
39    read_stream: Option<ReadStream>,
40}
41
42/// Enum containing a number of common HTTP methods.
43#[derive(Debug, FromPrimitive, PartialEq)]
44pub enum HttpMethod {
45    NONE = 0,
46    GET = 1,
47    HEAD = 2,
48    POST = 3,
49    PUT = 4,
50    DELETE = 5,
51    CONNECT = 6,
52    OPTIONS = 7,
53    TRACE = 8,
54    PATCH = 9,
55}
56
57impl FromStr for HttpMethod {
58    type Err = ();
59
60    fn from_str(input: &str) -> Result<HttpMethod, Self::Err> {
61        match input.to_uppercase().as_str() {
62            "GET" => Ok(HttpMethod::GET),
63            "HEAD" => Ok(HttpMethod::HEAD),
64            "POST" => Ok(HttpMethod::POST),
65            "PUT" => Ok(HttpMethod::PUT),
66            "DELETE" => Ok(HttpMethod::DELETE),
67            "CONNECT" => Ok(HttpMethod::CONNECT),
68            "OPTIONS" => Ok(HttpMethod::OPTIONS),
69            "TRACE" => Ok(HttpMethod::TRACE),
70            "PATCH" => Ok(HttpMethod::PATCH),
71            _ => Ok(HttpMethod::NONE),
72        }
73    }
74}
75
76impl HttpRequest {
77    /// Returns the client request being handled by the serverless function.
78    ///
79    /// # Panics
80    ///
81    /// This method panics if the client request has already been retrieved by this method once and the body
82    /// has been fetched.
83    ///
84    /// # Limits Exceeded Errors
85    ///
86    /// If the request exceeds the limits specified, this method sends an error response
87    /// with a corresponding error code for the exceeding limit, so that the same can be
88    /// handled explicitly by returning a customized error page.
89    ///  
90    ///             
91    ///  
92    /// Normally, this method does not need to be used explicity. EDJX sample
93    /// [`Rust Serverless Application`](https://github.com/edjx/edjfunction-example-rust)
94    /// code file (lib.rs) uses this method to fetch HTTP Request.
95    ///
96    /// `lib.rs`:
97    ///
98    /// ```
99    /// #[no_mangle]
100    /// pub fn init() {
101    ///   let req = HttpRequest::from_client();
102    ///   crate::serverless_function::serverless(req.unwrap());
103    /// }
104    /// ```
105    ///
106    /// `init()` function in `lib.rs` needs to be changed only if a
107    /// mutable reference to the request is required.
108    ///
109    pub fn from_client() -> Result<HttpRequest, HttpError> {
110        let mut headers = HeaderMap::new();
111        let header_keys = get_all_header_keys_from_host()?;
112        for key in header_keys {
113            headers.insert(
114                HeaderName::from_str(key.as_str()).unwrap(),
115                get_header_value_from_host(key.as_str())?.parse().unwrap(),
116            );
117        }
118
119        Ok(HttpRequest {
120            version: Version::HTTP_11,
121            method: get_method_from_host(),
122            uri: get_uri_from_host()?,
123            headers: headers,
124            body: None,
125            read_stream: None,
126        })
127    }
128
129    /// Returns the enum value for HttpMethod.
130    ///
131    /// # Example
132    ///
133    /// ```no_run
134    /// # use edjx::{HttpMethod, HttpRequest};
135    /// use std::str::FromStr;
136    ///
137    /// fn serverless(req: &HttpRequest) {
138    ///     let method = req.method();
139    ///     assert_eq!(method, &HttpMethod::from_str("get").unwrap());
140    /// }
141    /// ```
142    pub fn method(&self) -> &HttpMethod {
143        return &self.method;
144    }
145
146    /// Returns the request URI value as [`http::Uri`] object. Use different functions available in
147    /// [`http::Uri`] for the corresponding
148    /// object, as needed.
149    ///
150    /// # Example
151    ///
152    /// ```no_run
153    /// use edjx::{HttpRequest};
154    ///
155    /// fn serverless(req: &HttpRequest) {
156    ///     let uri = req.uri();
157    ///     assert_eq!(uri.path(), "/foo/bar");
158    /// }
159    /// ```
160    pub fn uri(&self) -> &Uri {
161        return &self.uri;
162    }
163
164    /// Returns the query string of request as an `Option` of [`&str`].
165    pub fn query(&self) -> Option<&str> {
166        return self.uri.query();
167    }
168
169    /// Returns the optional value for a provided key in query paramters.
170    pub fn query_param_by_name(&self, name: &str) -> Option<String> {
171        let query_params: HashMap<String, String> = self
172            .uri
173            .query()
174            .map(|v| form_urlencoded::parse(v.as_bytes()).into_owned().collect())
175            .unwrap_or_else(HashMap::new);
176
177        match query_params.get(name) {
178            Some(param_value) => Some(param_value.to_string()),
179            None => None,
180        }
181    }
182
183    /// Returns the path part of the request URI.
184    pub fn path(&self) -> &str {
185        return &self.uri.path();
186    }
187
188    /// Returns the map of headers in the request as an [`http::HeaderMap`] object.
189    /// Use different functions available in [`http::HeaderMap`] for the corresponding
190    /// object, as needed.
191    ///
192    /// # Example
193    ///
194    /// ```no_run
195    /// use edjx::{HttpRequest};
196    ///
197    /// fn serverless(req: &HttpRequest) {
198    ///     let headers = req.headers();
199    ///     assert!(headers.get("host").is_some());
200    ///     assert_eq!(headers.get("host").unwrap(), &"example.com");
201    /// }
202    /// ```
203    pub fn headers(&self) -> &HeaderMap {
204        return &self.headers;
205    }
206
207    /// Returns the body associated with the request as a `Result` response of
208    /// [`Vec<u8>`].
209    ///
210    /// # Example
211    ///
212    /// ```no_run
213    /// use edjx::{error, HttpRequest, HttpResponse};
214    ///
215    /// fn serverless(req: &HttpRequest) {
216    ///     let body = match req.body() {
217    ///        Ok(body_vec_option) => body_vec_option,
218    ///        Err(e) => {
219    ///            error!("{}", &e.to_string());
220    ///            HttpResponse::new()
221    ///                .set_status(StatusCode::BAD_REQUEST)
222    ///                .send()
223    ///                .unwrap();
224    ///            return;
225    ///        }
226    ///     }
227    /// }
228    /// ```
229    pub fn body(&mut self) -> Result<&Option<Vec<u8>>, HttpError> {
230        if self.body.is_none() {
231            self.body = Some(get_body_from_host()?);
232        }
233        return Ok(&self.body);
234    }
235
236    /// Opens a read stream to read the request body.
237    ///
238    /// [`HttpRequest::body`] and [`HttpRequest::get_read_stream`] cannot be used at the same time.
239    pub fn get_read_stream(&mut self) -> Result<&mut ReadStream, HttpError> {
240        if self.read_stream.is_none() {
241            match get_read_stream_from_host() {
242                Ok(rs) => {
243                    self.read_stream = Some(rs);
244                }
245                Err(err) => {
246                    return Err(err);
247                }
248            }
249        }
250        Ok(self.read_stream.as_mut().unwrap())
251    }
252}
253
254fn get_method_from_host() -> HttpMethod {
255    let response = unsafe { http_cur_req_get_method() };
256
257    let method_enum_value: Option<HttpMethod> = FromPrimitive::from_i32(response);
258    match method_enum_value {
259        Some(v) => v,
260        None => HttpMethod::NONE,
261    }
262}
263
264fn get_uri_from_host() -> Result<Uri, UriError> {
265    let mut err = 0;
266    let response = unsafe { http_cur_req_get_uri(&mut err) };
267
268    if response > 0 {
269        let value = String::from_utf8(super::result::get_result_bytes(response).unwrap()).unwrap();
270        value.parse().map_err(|_| UriError::InvalidUri)
271    } else {
272        Err(UriError::try_from(err).unwrap())
273    }
274}
275
276fn get_body_from_host() -> Result<Vec<u8>, HTTPBodyTooLargeError> {
277    let mut err = 0;
278    let response = unsafe { http_cur_req_get_body(&mut err) };
279
280    if response > 0 {
281        let result = super::result::get_result_bytes(response);
282        Ok(result.unwrap())
283    } else if response == 0 {
284        Ok(Vec::with_capacity(0))
285    } else {
286        assert!(err == Error::HTTPTooLargeBody as i32);
287        return Err(HTTPBodyTooLargeError());
288    }
289}
290
291fn get_read_stream_from_host() -> Result<ReadStream, HttpError> {
292    let mut read_sd = 0;
293    let response = unsafe { http_cur_req_get_read_stream(&mut read_sd) };
294    if response < 0 {
295        return Err(HttpError::from(Error::from_i32(-1).unwrap()));
296    }
297
298    Ok(BaseStream::new(read_sd))
299}
300
301fn get_all_header_keys_from_host() -> Result<Vec<String>, HeaderError> {
302    let mut err = 0;
303    let response = unsafe { http_cur_req_get_all_header_keys(&mut err) };
304
305    if response > 0 {
306        let data = super::result::get_result_bytes(response).unwrap();
307
308        let keys_delimited_string = str::from_utf8(&data).unwrap();
309        let mut v = Vec::new();
310        for header_key in keys_delimited_string.split(",") {
311            if header_key.len() > 0 {
312                v.push(String::from(header_key))
313            }
314        }
315        Ok(v)
316    } else if response == 0 {
317        Ok(Vec::with_capacity(0))
318    } else {
319        assert!(err == Error::HTTPTooLargeHeaderName as i32);
320        Err(HeaderError::TooLargeName)
321    }
322}
323
324fn get_header_value_from_host(header_key: &str) -> Result<String, HeaderError> {
325    let mut key = String::from(header_key);
326    let key = key.as_mut_str();
327    let dest_length = key.len() as i32;
328    let dest_pointer = key.as_mut_ptr() as *const u8;
329    let mut err = 0;
330
331    let result_size = unsafe { http_cur_req_get_header_value(dest_pointer, dest_length, &mut err) };
332    if result_size > 0 {
333        Ok(String::from_utf8(super::result::get_result_bytes(result_size).unwrap()).unwrap())
334    } else if result_size == 0 {
335        Ok(String::with_capacity(0))
336    } else {
337        Err(HeaderError::from_i32(err).unwrap())
338    }
339}