use std::fs;
use std::path::Path;
use chrono::{Utc};
use json::JsonValue;
use log::{error};
use crate::config::Config;
use crate::request::Protocol;
#[derive(Clone, Debug)]
pub struct Response {
config: Config,
protocol: Protocol,
code: usize,
content_type: String,
content_charset: String,
response: Vec<String>,
}
impl Response {
pub fn new(config: Config, protocol: Protocol) -> Response {
Self {
config,
protocol,
code: 0,
content_type: "text/plain".to_string(),
content_charset: "UTF-8".to_string(),
response: vec![],
}
}
pub fn set_code(&mut self, code: usize) -> &mut Self {
self.response = vec![];
self.code = code;
let msg = match code {
100 => "Continue",
101 => "Switching Protocols",
102 => "Processing",
200 => "OK",
201 => "Created",
301 => "Permanently Moved", 302 => "Move temporarily",
403 => "Forbidden",
404 => "Not Found", 500 => "Internal Server Error", 501 => "Not Implemented", 502 => "Bad Gateway", 503 => "Service Unavailable", 504 => "Gateway Time-out", 505 => "HTTP Version Not Supported", _ => {
self.code = 505;
"HTTP Version Not Supported"
}
};
self.response.push(format!("{} {} {}", self.protocol.str(), self.code, msg));
self.response.push(format!("Date: {}", Utc::now().format("%a, %d %b %Y %H:%M:%S GMT")));
self.response.push("Server: Blue Rain Rust".to_string());
self
}
pub fn set_access_control_allow_origin(&mut self) -> &mut Self {
self.response.push(format!("Access-Control-Allow-Origin: {}", "*"));
self
}
pub fn set_cache_control(&mut self, max_age: i32) -> &mut Self {
self.response.push(format!("Cache-Control: no-cache,max-age={}", max_age));
self
}
fn set_cors_allow_origin(&mut self) {
if self.config.cors.allow_origin.is_empty() {
self.response.push(format!("Access-Control-Allow-Origin: {}", "*"));
} else {
for origin in self.config.cors.allow_origin.iter() {
self.response.push(format!("Access-Control-Allow-Origin: {}", origin));
}
}
}
fn set_cors_allow_credentials(&mut self) {
if self.config.cors.allow_credentials {
self.response.push(format!("Access-Control-Allow-Credentials: {}", self.config.cors.allow_credentials));
}
}
pub fn options(&mut self) -> String {
self.set_code(200);
self.set_cors_allow_origin();
self.set_cors_allow_credentials();
self.response.push(format!("Access-Control-Allow-Methods: {}", self.config.cors.allow_methods));
self.response.push(format!("Access-Control-Allow-Headers: {}", self.config.cors.allow_headers));
self.response.push(format!("Access-Control-Expose-Headers: {}", self.config.cors.expose_headers));
self.response.push(format!("Access-Control-Max-Age: {}\r\n\r\n", self.config.cors.max_age));
self.response.join("\r\n")
}
pub fn file(&mut self, filename: String) -> String {
let sufxx: Vec<&str> = filename.split(".").collect();
let sufxx = sufxx[sufxx.len() - 1];
let file = fs::read(filename.as_str()).unwrap();
let contents = unsafe { String::from_utf8_unchecked(file.clone()) };
let content_type = match sufxx.to_lowercase().as_str() {
"jpg" => "image/jpg",
"png" => "image/png",
"bmp" => "image/bmp",
"jpeg" => "image/jpeg",
"svg" => "image/svg+xml",
"webp" => "image/webp",
"ico" => "image/ico",
"gif" => "image/gif",
"avif" => "image/avif",
"pdf" => "application/pdf",
"xlsx" => "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
"xls" => "application/vnd.ms-excel",
"xml" => "text/xml",
"json" => "application/json",
"html" => "text/html;charset=utf-8",
_ => "text/plain;charset=utf-8"
};
self.set_code(200);
self.response.push("Connection: keep-alive".to_string());
self.response.push(format!("Content-Type: {}", content_type));
self.response.push("Cache-Control: private,max-age=0".to_string());
self.response.push("Strict-Transport-Security: max-age=0; includeSubDomains;".to_string());
self.response.push(format!("ETag: {}", br_crypto::hash::str_to_md5(&contents.to_string())));
self.response.push(format!("Content-Length: {}\r\n", contents.len()));
self.response.push(contents.to_string());
self.response.join("\r\n")
}
pub fn receipt(&mut self, content_type: &str, content: JsonValue) -> String {
self.content_type = content_type.to_string();
self.set_code(200);
self.set_cors_allow_origin();
match self.content_type.as_str() {
"json" => {
self.response.push("Connection: keep-alive".to_string());
self.response.push("Cache-Control: no-cache,max-age=0".to_string());
self.response.push(format!("Content-type: application/json;charset={}", self.content_charset));
self.response.push(format!("Content-Length: {}\r\n", content.to_string().len()));
self.response.push(content.to_string());
}
"text" => {
self.response.push("Connection: keep-alive".to_string());
self.response.push(format!("Access-Control-Max-Age: {}", self.config.cors.max_age));
self.response.push("Content-type: text/plain;charset=utf-8".to_string());
self.response.push(format!("Content-Length: {}\r\n", content.to_string().len()));
self.response.push(content.to_string());
}
"html" => {
self.response.push("Connection: keep-alive".to_string());
self.response.push(format!("Access-Control-Max-Age: {}", self.config.cors.max_age));
self.response.push("Content-type: text/html;charset=utf-8".to_string());
self.response.push(format!("Content-Length: {}\r\n", content.to_string().len()));
self.response.push(content.to_string());
}
"url" => {
self.set_code(301);
self.set_cors_allow_origin();
self.response.push("Connection: Close".to_string());
self.response.push("Cache-control: no-cache".to_string());
self.response.push(format!("Location: {}", content));
self.response.push(format!("\r\n{}", ""));
}
"download" => {
self.response.push("Connection: keep-alive".to_string());
self.response.push("Cache-Control: no-cache,max-age=0".to_string());
self.response.push("Content-type: application/octet-stream".to_string());
self.response.push(format!("Access-Control-Expose-Headers: {}", self.config.cors.expose_headers));
let path = Path::new(content.as_str().unwrap());
match fs::read(path.to_str().unwrap()) {
Ok(file) => {
let filename = path.file_name().unwrap();
let filename = br_crypto::encoding::urlencoding_encode(filename.to_str().unwrap());
self.response.push(format!("Content-Disposition: attachment; filename={}", filename));
let content = unsafe { String::from_utf8_unchecked(file.clone()) };
self.response.push(format!("Content-Length: {}\r\n", content.len()));
self.response.push(content);
}
Err(_) => {
self.response.push(format!("\r\n{}", ""));
}
}
}
"download_delete_dir" => {
self.response.push("Connection: keep-alive".to_string());
self.response.push("Cache-Control: no-cache,max-age=0".to_string());
self.response.push("Content-type: application/octet-stream".to_string());
self.response.push(format!("Access-Control-Expose-Headers: {}", self.config.cors.expose_headers));
let path = Path::new(content.as_str().unwrap());
match fs::read(path.to_str().unwrap()) {
Ok(file) => {
let filename = path.file_name().unwrap();
let filename = br_crypto::encoding::urlencoding_encode(filename.to_str().unwrap());
self.response.push(format!("Content-Disposition: attachment; filename={}", filename));
let content = unsafe { String::from_utf8_unchecked(file.clone()) };
match fs::remove_dir_all(path.parent().unwrap()) {
Ok(_) => {
self.response.push(format!("Content-Length: {}\r\n", content.len()));
self.response.push(content);
}
Err(e) => {
error!("{}", format!("文件下载并删除文件夹失败: {}", e));
self.response.push(format!("\r\n{}", ""));
}
}
}
Err(_) => {
self.response.push(format!("\r\n{}", ""));
}
}
}
_ => {
self.response.push("Connection: keep-alive".to_string());
self.response.push("Cache-Control: no-cache,max-age=0".to_string());
self.response.push("Content-type: text/html;charset=utf-8".to_string());
}
}
self.response.join("\r\n")
}
pub fn websocket(&mut self, key: String) -> String {
self.response = vec![];
self.response.push("HTTP/1.1 101 Switching Protocols".to_string());
self.response.push("Upgrade: websocket".to_string());
self.response.push("Connection: Upgrade".to_string());
let sha_code = br_crypto::hmac::sha_1(format!("{}258EAFA5-E914-47DA-95CA-C5AB0DC85B11", key).as_str());
let sec_websocket_accept = br_crypto::base64::encode_file(sha_code);
self.response.push(format!("Sec-WebSocket-Accept: {}", sec_websocket_accept));
self.response.push("\r\n".to_string());
self.response.join("\r\n")
}
}