148 lines
4.2 KiB
Rust
148 lines
4.2 KiB
Rust
//! response handles the incoming request parsed `HTTPRequest`
|
|
//! it will check if the `HTTPRequest` is valid and 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 crate::http::request::{HTTPRequest, HTTPVersion};
|
|
use async_trait::async_trait;
|
|
use json;
|
|
// add the Store trait to be used by `FileStore`
|
|
use crate::stores::FileStore;
|
|
use crate::stores::Store;
|
|
|
|
#[derive(Debug, PartialEq)]
|
|
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 {
|
|
fn set_status_code(&mut self, code: HTTPStatusCode) {
|
|
self.status_code = code;
|
|
}
|
|
|
|
pub fn get_status_code(&self) -> &HTTPStatusCode {
|
|
&self.status_code
|
|
}
|
|
}
|
|
|
|
/// 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 {
|
|
/// creates a response from the incoming `Request`
|
|
/// `From<T>` could be used instead of forcing it like this
|
|
/// it fails using `async_trait` attributes (only custom traits work ?)
|
|
pub async fn from(request: HTTPRequest) -> Self {
|
|
let mut response = HTTPResponse::default();
|
|
if !request.is_valid() {
|
|
return response;
|
|
}
|
|
|
|
// empty body -> invalid request (credentials needed)
|
|
if let None = request.body {
|
|
return Self::as_403();
|
|
}
|
|
|
|
// TODO: path to `store.txt` must not be hardcoded, should be in a config file and load at
|
|
// runtime
|
|
let mut store = FileStore::new("tests/data/store.txt".to_string());
|
|
let body = request.body.unwrap();
|
|
let is_auth = store.is_auth(&body.get_data()).await;
|
|
|
|
if !is_auth {
|
|
return Self::as_403();
|
|
}
|
|
|
|
// TODO: must be a valid JWT (to implement)
|
|
let body = json::parse(
|
|
r#"{"token": "header.payload.signature", "refresh": "header.payload.signature"}"#,
|
|
)
|
|
.unwrap();
|
|
|
|
response.status_line.version = request.start_line.version;
|
|
response.status_line.status_code = HTTPStatusCode::Http200;
|
|
response.body = body;
|
|
|
|
response
|
|
}
|
|
|
|
/// generates a 403 response with a correct error message
|
|
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
|
|
}
|
|
}
|