173 lines
		
	
	
		
			4.4 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
			
		
		
	
	
			173 lines
		
	
	
		
			4.4 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
| //! response handles the incoming request parsed `HTTPRequest`
 | |
| //! it will build an HTTPResponse corresponding to the HTTP message specs. see: https://developer.mozilla.org/en-US/docs/Web/HTTP/Messages
 | |
| //! NOTE: only few parts of the specification has been implemented
 | |
| 
 | |
| use super::{HTTPMessage, HTTPVersion};
 | |
| use json;
 | |
| 
 | |
| #[derive(Debug, PartialEq, Clone)]
 | |
| pub enum HTTPStatusCode {
 | |
|     Http200,
 | |
|     Http400,
 | |
|     Http403,
 | |
|     Http404,
 | |
|     Http500,
 | |
| }
 | |
| 
 | |
| impl Into<String> for HTTPStatusCode {
 | |
|     fn into(self) -> String {
 | |
|         match self {
 | |
|             Self::Http200 => "200".to_string(),
 | |
|             Self::Http400 => "400".to_string(),
 | |
|             Self::Http404 => "404".to_string(),
 | |
|             Self::Http403 => "403".to_string(),
 | |
|             Self::Http500 => "500".to_string(),
 | |
|         }
 | |
|     }
 | |
| }
 | |
| 
 | |
| pub struct HTTPStatusLine {
 | |
|     version: HTTPVersion,
 | |
|     status_code: HTTPStatusCode,
 | |
| }
 | |
| 
 | |
| impl Default for HTTPStatusLine {
 | |
|     fn default() -> HTTPStatusLine {
 | |
|         HTTPStatusLine {
 | |
|             version: HTTPVersion::Http1_1,
 | |
|             status_code: HTTPStatusCode::Http400,
 | |
|         }
 | |
|     }
 | |
| }
 | |
| 
 | |
| impl Into<String> for HTTPStatusLine {
 | |
|     fn into(self) -> String {
 | |
|         let version: String = self.version.into();
 | |
|         let status_code: String = self.status_code.into();
 | |
|         format! {"{} {}", version, status_code}
 | |
|     }
 | |
| }
 | |
| 
 | |
| impl HTTPStatusLine {
 | |
|     pub fn set_status_code(&mut self, code: HTTPStatusCode) {
 | |
|         self.status_code = code;
 | |
|     }
 | |
| 
 | |
|     #[allow(dead_code)]
 | |
|     pub fn get_status_code(&self) -> HTTPStatusCode {
 | |
|         self.status_code.clone()
 | |
|     }
 | |
| }
 | |
| 
 | |
| /// represents an HTTP response (headers are not parsed)
 | |
| /// NOTE: for simplicity, only JSON body are accepted
 | |
| pub struct HTTPResponse {
 | |
|     pub status_line: HTTPStatusLine,
 | |
|     body: json::JsonValue,
 | |
| }
 | |
| 
 | |
| impl Default for HTTPResponse {
 | |
|     fn default() -> Self {
 | |
|         HTTPResponse {
 | |
|             status_line: HTTPStatusLine::default(),
 | |
|             body: json::parse(r#"{"error": "the incoming request is not valid"}"#).unwrap(),
 | |
|         }
 | |
|     }
 | |
| }
 | |
| 
 | |
| impl Into<String> for HTTPResponse {
 | |
|     fn into(self) -> String {
 | |
|         // move `self.body` into a new var
 | |
|         let b = self.body;
 | |
|         let body: String = json::stringify(b);
 | |
| 
 | |
|         let status_line: String = self.status_line.into();
 | |
|         format!(
 | |
|             "{}\r\nContent-Type: application/json\r\nContent-Length: {}\r\n\r\n{}",
 | |
|             status_line,
 | |
|             body.len(),
 | |
|             body
 | |
|         )
 | |
|     }
 | |
| }
 | |
| 
 | |
| impl HTTPResponse {
 | |
|     pub fn as_500(message: Option<json::JsonValue>) -> Self {
 | |
|         let mut response = Self::default();
 | |
| 
 | |
|         response
 | |
|             .status_line
 | |
|             .set_status_code(HTTPStatusCode::Http500);
 | |
| 
 | |
|         response.body = {
 | |
|             match message {
 | |
|                 Some(m) => m,
 | |
|                 None => json::parse(r#"{"error": "unexpected error occurred"}"#).unwrap(),
 | |
|             }
 | |
|         };
 | |
| 
 | |
|         response
 | |
|     }
 | |
| 
 | |
|     pub fn as_404() -> Self {
 | |
|         let mut response = Self::default();
 | |
| 
 | |
|         response
 | |
|             .status_line
 | |
|             .set_status_code(HTTPStatusCode::Http404);
 | |
|         response.body = json::parse(r#"{"error": "the url requested does not exist"}"#).unwrap();
 | |
| 
 | |
|         response
 | |
|     }
 | |
| 
 | |
|     pub fn as_403() -> Self {
 | |
|         let mut response = HTTPResponse {
 | |
|             status_line: HTTPStatusLine::default(),
 | |
|             body: json::parse(r#"{"error": "invalid credentials"}"#).unwrap(),
 | |
|         };
 | |
| 
 | |
|         response
 | |
|             .status_line
 | |
|             .set_status_code(HTTPStatusCode::Http403);
 | |
| 
 | |
|         response
 | |
|     }
 | |
| 
 | |
|     /// wrap the `Self::default()` associated func (not really clear)
 | |
|     pub fn as_400() -> Self {
 | |
|         Self::default()
 | |
|     }
 | |
| 
 | |
|     pub fn as_200(message: Option<json::JsonValue>) -> Self {
 | |
|         let mut response = Self::default();
 | |
| 
 | |
|         response
 | |
|             .status_line
 | |
|             .set_status_code(HTTPStatusCode::Http200);
 | |
| 
 | |
|         response.body = {
 | |
|             match message {
 | |
|                 Some(m) => m,
 | |
|                 None => json::parse(r#"{"status": "ok"}"#).unwrap(),
 | |
|             }
 | |
|         };
 | |
| 
 | |
|         response
 | |
|     }
 | |
| 
 | |
|     /// build and HTTP 200 response with the generated JWT
 | |
|     pub fn send_token(token: &str) -> Self {
 | |
|         let mut http_message = HTTPMessage::default();
 | |
|         http_message.put("token", token);
 | |
| 
 | |
|         let message = {
 | |
|             match http_message.try_into() {
 | |
|                 Ok(m) => m,
 | |
|                 Err(_e) => json::parse(r#"{"token": "error.generation.token"}"#).unwrap(),
 | |
|             }
 | |
|         };
 | |
| 
 | |
|         HTTPResponse::as_200(Some(message))
 | |
|     }
 | |
| }
 |