1use std::fs;
2use std::path::Path;
3use chrono::{Utc};
4use json::JsonValue;
5use log::{error};
6use crate::config::Config;
7use crate::request::Protocol;
8
9#[derive(Clone, Debug)]
11pub struct Response {
12 config: Config,
13 protocol: Protocol,
15 code: usize,
16 content_type: String,
18 content_charset: String,
20 response: Vec<String>,
21}
22
23impl Response {
24 pub fn new(config: Config, protocol: Protocol) -> Response {
25 Self {
26 config,
27 protocol,
28 code: 0,
29 content_type: "text/plain".to_string(),
30 content_charset: "UTF-8".to_string(),
31 response: vec![],
32 }
33 }
34 pub fn set_code(&mut self, code: usize) -> &mut Self {
35 self.response = vec![];
36 self.code = code;
37 let msg = match code {
38 100 => "Continue",
39 101 => "Switching Protocols",
40 102 => "Processing",
41 200 => "OK",
42 201 => "Created",
43 301 => "Permanently Moved",302 => "Move temporarily",
45 403 => "Forbidden",
46 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",_ => {
54 self.code = 505;
55 "HTTP Version Not Supported"
56 }
57 };
58 self.response.push(format!("{} {} {}", self.protocol.str(), self.code, msg));
59 self.response.push(format!("Date: {}", Utc::now().format("%a, %d %b %Y %H:%M:%S GMT")));
60 self.response.push("Server: Blue Rain Rust".to_string());
61 self
62 }
63 pub fn set_access_control_allow_origin(&mut self) -> &mut Self {
64 self.response.push(format!("Access-Control-Allow-Origin: {}", "*"));
65 self
66 }
67 pub fn set_cache_control(&mut self, max_age: i32) -> &mut Self {
68 self.response.push(format!("Cache-Control: no-cache,max-age={}", max_age));
69 self
70 }
71 fn set_cors_allow_origin(&mut self) {
72 if self.config.cors.allow_origin.is_empty() {
73 self.response.push(format!("Access-Control-Allow-Origin: {}", "*"));
74 } else {
75 for origin in self.config.cors.allow_origin.iter() {
76 self.response.push(format!("Access-Control-Allow-Origin: {}", origin));
77 }
78 }
79 }
80 fn set_cors_allow_credentials(&mut self) {
81 if self.config.cors.allow_credentials {
82 self.response.push(format!("Access-Control-Allow-Credentials: {}", self.config.cors.allow_credentials));
83 }
84 }
85 pub fn options(&mut self) -> String {
87 self.set_code(200);
88 self.set_cors_allow_origin();
89 self.set_cors_allow_credentials();
90 self.response.push(format!("Access-Control-Allow-Methods: {}", self.config.cors.allow_methods));
91 self.response.push(format!("Access-Control-Allow-Headers: {}", self.config.cors.allow_headers));
92 self.response.push(format!("Access-Control-Expose-Headers: {}", self.config.cors.expose_headers));
93 self.response.push(format!("Access-Control-Max-Age: {}\r\n\r\n", self.config.cors.max_age));
94 self.response.join("\r\n")
95 }
96 pub fn file(&mut self, filename: String) -> String {
98 let sufxx: Vec<&str> = filename.split(".").collect();
99 let sufxx = sufxx[sufxx.len() - 1];
100 let file = fs::read(filename.as_str()).unwrap();
101 let contents = unsafe { String::from_utf8_unchecked(file.clone()) };
102 let content_type = match sufxx.to_lowercase().as_str() {
103 "jpg" => "image/jpg",
104 "png" => "image/png",
105 "bmp" => "image/bmp",
106 "jpeg" => "image/jpeg",
107 "svg" => "image/svg+xml",
108 "webp" => "image/webp",
109 "ico" => "image/ico",
110 "gif" => "image/gif",
111 "avif" => "image/avif",
112 "pdf" => "application/pdf",
113 "xlsx" => "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
114 "xls" => "application/vnd.ms-excel",
115 "xml" => "text/xml",
116 "json" => "application/json",
117 "html" => "text/html;charset=utf-8",
118 _ => "text/plain;charset=utf-8"
119 };
120 self.set_code(200);
121 self.response.push("Connection: keep-alive".to_string());
122 self.response.push(format!("Content-Type: {}", content_type));
123 self.response.push("Cache-Control: private,max-age=0".to_string());
124 self.response.push("Strict-Transport-Security: max-age=0; includeSubDomains;".to_string());
125 self.response.push(format!("ETag: {}", br_crypto::hash::str_to_md5(&contents.to_string())));
126 self.response.push(format!("Content-Length: {}\r\n", contents.len()));
127 self.response.push(contents.to_string());
128 self.response.join("\r\n")
129 }
130 pub fn receipt(&mut self, content_type: &str, content: JsonValue) -> String {
131 self.content_type = content_type.to_string();
132 self.set_code(200);
133 self.set_cors_allow_origin();
134 match self.content_type.as_str() {
138 "json" => {
139 self.response.push("Connection: keep-alive".to_string());
140 self.response.push("Cache-Control: no-cache,max-age=0".to_string());
141 self.response.push(format!("Content-type: application/json;charset={}", self.content_charset));
142 self.response.push(format!("Content-Length: {}\r\n", content.to_string().len()));
143 self.response.push(content.to_string());
144 }
145 "text" => {
146 self.response.push("Connection: keep-alive".to_string());
147 self.response.push(format!("Access-Control-Max-Age: {}", self.config.cors.max_age));
148 self.response.push("Content-type: text/plain;charset=utf-8".to_string());
149 self.response.push(format!("Content-Length: {}\r\n", content.to_string().len()));
150 self.response.push(content.to_string());
151 }
152 "html" => {
153 self.response.push("Connection: keep-alive".to_string());
154 self.response.push(format!("Access-Control-Max-Age: {}", self.config.cors.max_age));
155 self.response.push("Content-type: text/html;charset=utf-8".to_string());
156 self.response.push(format!("Content-Length: {}\r\n", content.to_string().len()));
157 self.response.push(content.to_string());
158 }
159 "url" => {
160 self.set_code(301);
161 self.set_cors_allow_origin();
162 self.response.push("Connection: Close".to_string());
163 self.response.push("Cache-control: no-cache".to_string());
164 self.response.push(format!("Location: {}", content));
165 self.response.push(format!("\r\n{}", ""));
166 }
167 "download" => {
168 self.response.push("Connection: keep-alive".to_string());
169 self.response.push("Cache-Control: no-cache,max-age=0".to_string());
170 self.response.push("Content-type: application/octet-stream".to_string());
171 self.response.push(format!("Access-Control-Expose-Headers: {}", self.config.cors.expose_headers));
172
173 let path = Path::new(content.as_str().unwrap());
174 match fs::read(path.to_str().unwrap()) {
175 Ok(file) => {
176 let filename = path.file_name().unwrap();
177 let filename = br_crypto::encoding::urlencoding_encode(filename.to_str().unwrap());
178 self.response.push(format!("Content-Disposition: attachment; filename={}", filename));
179 let content = unsafe { String::from_utf8_unchecked(file.clone()) };
180 self.response.push(format!("Content-Length: {}\r\n", content.len()));
181 self.response.push(content);
182 }
183 Err(_) => {
184 self.response.push(format!("\r\n{}", ""));
185 }
186 }
187 }
188 "download_delete_dir" => {
189 self.response.push("Connection: keep-alive".to_string());
190 self.response.push("Cache-Control: no-cache,max-age=0".to_string());
191 self.response.push("Content-type: application/octet-stream".to_string());
192 self.response.push(format!("Access-Control-Expose-Headers: {}", self.config.cors.expose_headers));
193
194 let path = Path::new(content.as_str().unwrap());
195 match fs::read(path.to_str().unwrap()) {
196 Ok(file) => {
197 let filename = path.file_name().unwrap();
198 let filename = br_crypto::encoding::urlencoding_encode(filename.to_str().unwrap());
199 self.response.push(format!("Content-Disposition: attachment; filename={}", filename));
200 let content = unsafe { String::from_utf8_unchecked(file.clone()) };
201
202 match fs::remove_dir_all(path.parent().unwrap()) {
203 Ok(_) => {
204 self.response.push(format!("Content-Length: {}\r\n", content.len()));
205 self.response.push(content);
206 }
207 Err(e) => {
208 error!("{}", format!("文件下载并删除文件夹失败: {}", e));
209 self.response.push(format!("\r\n{}", ""));
210 }
211 }
212 }
213 Err(_) => {
214 self.response.push(format!("\r\n{}", ""));
215 }
216 }
217 }
218 _ => {
219 self.response.push("Connection: keep-alive".to_string());
220 self.response.push("Cache-Control: no-cache,max-age=0".to_string());
221 self.response.push("Content-type: text/html;charset=utf-8".to_string());
222 }
223 }
224
225 self.response.join("\r\n")
226 }
227
228 pub fn websocket(&mut self, key: String) -> String {
229 self.response = vec![];
230 self.response.push("HTTP/1.1 101 Switching Protocols".to_string());
231 self.response.push("Upgrade: websocket".to_string());
232 self.response.push("Connection: Upgrade".to_string());
233
234 let sha_code = br_crypto::hmac::sha_1(format!("{}258EAFA5-E914-47DA-95CA-C5AB0DC85B11", key).as_str());
235 let sec_websocket_accept = br_crypto::base64::encode_file(sha_code);
236
237 self.response.push(format!("Sec-WebSocket-Accept: {}", sec_websocket_accept));
238 self.response.push("\r\n".to_string());
239 self.response.join("\r\n")
240 }
241}