br_web/
response.rs

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/// 响应处理
10#[derive(Clone, Debug)]
11pub struct Response {
12    config: Config,
13    /// 协议
14    protocol: Protocol,
15    code: usize,
16    /// 响应正文类型
17    content_type: String,
18    /// 正文使用的编码
19    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",// 重定向
44            302 => "Move temporarily",
45            403 => "Forbidden",
46            404 => "Not Found",//服务器无法根据客户端的请求找到资源(网页)。通过此代码,网站设计人员可设置"您所请求的资源无法找到"的个性页面
47            500 => "Internal Server Error",//服务器内部错误,无法完成请求
48            501 => "Not Implemented",//服务器不支持请求的功能,无法完成请求
49            502 => "Bad Gateway",//作为网关或者代理工作的服务器尝试执行请求时,从远程服务器接收到了一个无效的响应
50            503 => "Service Unavailable",//由于超载或系统维护,服务器暂时的无法处理客户端的请求。延时的长度可包含在服务器的Retry-After头信息中
51            504 => "Gateway Time-out",//充当网关或代理的服务器,未及时从远端服务器获取请求
52            505 => "HTTP Version Not Supported",//服务器不支持请求的HTTP协议的版本,无法完成处理
53            _ => {
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    /// options 响应
86    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    /// 读取文件返回
97    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        // self.response.push(format!("Access-Control-Allow-Methods: {}", self.config.cors.allow_methods));
135        // self.response.push(format!("Access-Control-Allow-Headers: {}", self.config.cors.allow_headers));
136        // self.response.push(format!("Access-Control-Expose-Headers: {}", self.config.cors.expose_headers));
137        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}