use crate::message::http_value::{ContentDisposition, StatusCode};
use crate::security::safety::HttpSafety;
use crate::message::body::HttpBody;
use crate::util::cookie::Cookie;
use crate::message::http_value::HttpContentType;
use crate::message::meta::HttpMeta;
use crate::context::io;
use crate::message::start_line::{HttpStartLine, ResponseStartLine};
use std::collections::HashMap;
use hotaru_core::connection::{HotaruBufRead, HotaruWrite};
#[derive(Debug, Clone)]
pub struct HttpResponse {
pub meta: HttpMeta,
pub body: HttpBody,
}
impl HttpResponse {
pub fn new(meta: HttpMeta, body: HttpBody) -> Self {
Self { meta, body }
}
pub async fn parse_lazy<R: HotaruBufRead<Error = std::io::Error> + Unpin + Send>(
stream: &mut R,
config: &HttpSafety,
print_raw: bool,
) -> Self {
match io::parse_lazy(stream, config, false, print_raw).await {
Ok((meta, body)) => Self::new(meta, body),
Err(_) => Self::default(),
}
}
pub async fn parse_body(&mut self, safety_setting: &HttpSafety) {
let body = std::mem::take(&mut self.body);
self.body = body.parse_buffer(safety_setting);
}
pub async fn get_parsed_body(&mut self, safety: HttpSafety) -> HttpBody {
self.parse_body(&safety).await;
self.body.clone()
}
pub fn add_cookie<T: Into<String>>(mut self, key: T, cookie: Cookie) -> Self {
self.meta.add_cookie(key, cookie);
self
}
pub fn content_type(mut self, content_type: HttpContentType) -> Self {
self.meta.set_content_type(content_type);
self
}
pub fn add_header<T: Into<String>, U: Into<String>>(mut self, key: T, value: U) -> Self {
self.meta.set_attribute(key, value.into());
self
}
pub fn content_disposition(mut self, disposition: ContentDisposition) -> Self {
self.meta.set_content_disposition(disposition);
self
}
pub fn status<T: Into<StatusCode>>(mut self, status: T) -> Self {
self.meta.start_line.set_status_code(status);
self
}
pub async fn send<W: HotaruWrite<Error = std::io::Error> + Unpin + Send>(self, writer: &mut W) -> std::io::Result<()> {
io::send(self.meta, self.body, writer).await
}
}
impl Default for HttpResponse {
fn default() -> Self {
let meta = HttpMeta::new(
HttpStartLine::Response(ResponseStartLine::default()),
HashMap::new(),
);
let body = HttpBody::default(); HttpResponse::new(meta, body)
}
}
pub mod response_templates {
use std::collections::HashMap;
use std::path::Path;
use akari::TemplateManager;
use akari::Value;
use super::HttpResponse;
use crate::message::body::HttpBody;
use crate::message::http_value::{HttpContentType, HttpVersion, StatusCode};
use crate::message::meta::HttpMeta;
use crate::message::start_line::HttpStartLine;
pub fn text_response(body: impl Into<String>) -> HttpResponse {
let start_line = HttpStartLine::new_response(HttpVersion::Http11, StatusCode::OK);
let mut meta = HttpMeta::new(start_line, HashMap::new());
meta.set_content_type(HttpContentType::TextPlain());
HttpResponse::new(meta, HttpBody::Text(body.into()))
}
pub fn html_response(body: impl Into<Vec<u8>>) -> HttpResponse {
let start_line = HttpStartLine::new_response(HttpVersion::Http11, StatusCode::OK);
let mut meta = HttpMeta::new(start_line, HashMap::new());
meta.set_content_type(HttpContentType::TextHtml());
HttpResponse::new(meta, HttpBody::Binary(body.into()))
}
pub fn redirect_response(url: &str) -> HttpResponse {
let start_line = HttpStartLine::new_response(HttpVersion::Http11, StatusCode::FOUND);
let mut meta = HttpMeta::new(start_line, HashMap::new());
meta.set_location(Some(url.to_string()));
HttpResponse::new(meta, HttpBody::Empty)
}
pub fn plain_template_response(file: &str) -> HttpResponse {
let start_line = HttpStartLine::new_response(HttpVersion::Http11, StatusCode::OK);
let mut meta = HttpMeta::new(start_line, HashMap::new());
let file_path = Path::new("templates").join(file);
let body = match std::fs::read(file_path) {
Ok(content) => content,
Err(_) => return return_status(StatusCode::NOT_FOUND),
};
meta.set_content_type(HttpContentType::TextHtml());
HttpResponse::new(meta, HttpBody::Binary(body))
}
pub fn serve_static_file(file: &str) -> HttpResponse {
let start_line = HttpStartLine::new_response(HttpVersion::Http11, StatusCode::OK);
let mut meta = HttpMeta::new(start_line, HashMap::new());
let file_path = Path::new("templates").join(file);
meta.set_content_type(HttpContentType::from_file_name(
file_path
.file_name()
.unwrap_or_default()
.to_str()
.unwrap_or(""),
));
let body = match std::fs::read(file_path) {
Ok(content) => content,
Err(_) => return return_status(StatusCode::NOT_FOUND),
};
HttpResponse::new(meta, HttpBody::Binary(body))
}
pub fn normal_response<S: Into<StatusCode>, B: Into<Vec<u8>>>(
status_code: S,
body: B,
) -> HttpResponse {
let start_line = HttpStartLine::new_response(HttpVersion::Http11, status_code.into());
let meta = HttpMeta::new(start_line, HashMap::new());
HttpResponse::new(meta, HttpBody::Binary(body.into())).content_type(HttpContentType::Text {
subtype: "plain".to_string(),
charset: Some("utf-8".to_string()),
})
}
pub fn json_response(body: Value) -> HttpResponse {
let start_line = HttpStartLine::new_response(HttpVersion::Http11, StatusCode::OK);
let mut meta = HttpMeta::new(start_line, HashMap::new());
meta.set_content_type(HttpContentType::ApplicationJson());
HttpResponse::new(meta, HttpBody::Json(body))
}
pub fn template_response(file: &str, data: HashMap<String, Value>) -> HttpResponse {
let template_manager = TemplateManager::new("templates");
let result = match template_manager.render(file, &data) {
Ok(content) => content,
Err(err) => return text_response(err.to_string()),
};
let start_line = HttpStartLine::new_response(HttpVersion::Http11, StatusCode::OK);
let mut meta = HttpMeta::new(start_line, HashMap::new());
meta.set_content_type(HttpContentType::TextHtml());
let body = result.into_bytes();
HttpResponse::new(meta, HttpBody::Binary(body))
}
pub fn return_status(status_code: StatusCode) -> HttpResponse {
normal_response(status_code, Vec::<u8>::new())
}
}