use actix_web::{web, HttpRequest, HttpResponse}; use crate::controllers::routes::RequestResult; use crate::data::http_error::HttpError; use std::error::Error; use serde::Serialize; use crate::data::error::{ResultBoxError, ExecError}; use std::collections::HashMap; use crate::helpers::api_helper; use actix_web::http::{HeaderName, HeaderValue}; use std::str::FromStr; use crate::data::config::conf; use crate::data::api_client::APIClient; /// Http request handler /// /// @author Pierre Hubert /// Single request body value pub struct RequestValue { pub string: Option } impl RequestValue { /// Build a string value pub fn string(s: String) -> RequestValue { RequestValue { string: Some(s) } } } #[derive(Serialize)] struct SuccessMessage { success: String } pub struct HttpRequestHandler { request: web::HttpRequest, body: HashMap, response: Option, headers: HashMap, client: Option, } impl HttpRequestHandler { /// Construct a new request handler pub fn new(req: HttpRequest, body: HashMap) -> HttpRequestHandler { HttpRequestHandler { request: req, body, response: None, headers: HashMap::new(), client: None, } } /// Check if a response has been set for this request pub fn has_response(&self) -> bool { self.response.is_some() } /// Get the response status code, eg. 200 or 404 pub fn response_status_code(&self) -> u16 { self.response.as_ref().unwrap().status().as_u16() } /// Take the response from this struct pub fn response(self) -> ResultBoxError { let mut response = self.response.unwrap(); // Put additional headers if required for (k, v) in &self.headers { response.headers_mut().insert(HeaderName::from_str(k)?, HeaderValue::from_str(v)?, ); } Ok(response) } /// Success message pub fn success(&mut self, message: &str) -> RequestResult { self.response = Some(HttpResponse::Ok().json(SuccessMessage { success: message.to_string() })); Ok(()) } /// Internal error message pub fn internal_error(&mut self, error: Box) -> RequestResult { self.response = Some(HttpResponse::InternalServerError().json( HttpError::internal_error("Internal server error."))); Err(error) } /// Bad request pub fn bad_request(&mut self, message: String) -> RequestResult { self.response = Some(HttpResponse::BadRequest().json( HttpError::bad_request(&message))); Err(Box::new(ExecError::new(&message))) } /// If result is not OK, return a bad request pub fn ok_or_bad_request(&mut self, res: ResultBoxError, msg: &str) -> ResultBoxError { match res { Ok(e) => Ok(e), Err(err) => { println!("Error leading to bad request: {}", err); self.bad_request(msg.to_string())?; unreachable!() } } } /// Get the path of the request pub fn request_path(&self) -> String { self.request.path().to_string() } /// Check if a POST parameter was present in the request or not pub fn has_post_parameter(&self, name: &str) -> bool { self.body.contains_key(name) } /// Get a post parameter pub fn post_parameter(&mut self, name: &str) -> ResultBoxError<&RequestValue> { if !self.has_post_parameter(name) { self.bad_request(format!("POST parameter '{}' not found in request!", name))?; } Ok(self.body.get(name).unwrap()) } /// Get a post string pub fn post_string(&mut self, name: &str) -> ResultBoxError { self.post_string_opt(name, 1, true) } /// Get a post string, specifying minimum length pub fn post_string_opt(&mut self, name: &str, min_length: usize, required: bool) -> ResultBoxError { let param = self.post_parameter(name)?; match (¶m.string, required) { (None, true) => Err(self.bad_request(format!("'{}' is not a string!", name)).unwrap_err()), (None, false) => Ok(String::new()), (Some(s), _) => { if s.len() >= min_length { Ok(s.to_string()) } else { Err(self.bad_request(format!("'{}' is too short!", name)).unwrap_err()) } } } } /// Check login tokens pub fn check_client_token(&mut self) -> Result<(), Box> { let api_name = self.post_string("serviceName")?; let api_token = self.post_string("serviceToken")?; let client = self.ok_or_bad_request( api_helper::get_client(&api_name, &api_token), "Client not recognized!", )?; if let Some(domain) = &client.domain { let allowed_origin = match conf().force_https { true => format!("https://{}", domain), false => format!("http://{}", domain) }; match self.request.headers().get("Referer") { None => self.bad_request("Unknown origin!".to_string())?, Some(s) => { if !s.to_str()?.starts_with(&allowed_origin) { self.bad_request("Use of this client is prohibited from this domain!".to_string())?; } } } self.headers.insert("Access-Control-Allow-Origin".to_string(), allowed_origin); } self.client = Some(client); Ok(()) } /// Get an email included in the request pub fn post_email(&mut self, name: &str) -> ResultBoxError { let mail = self.post_string(name)?; if !mailchecker::is_valid(&mail) { self.bad_request("Invalid email address!".to_string())?; } Ok(mail) } }