br_web_server/
response.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
use std::collections::{BTreeMap};
use std::{fs, io};
use json::{JsonValue};
use crate::request::Protocol;
use std::path::{PathBuf};
use chrono::{DateTime, Utc};
use log::{debug, warn};
use crate::base64::{encode_file, sha_1};
use crate::client::Scheme;
use crate::config::Config;
use crate::encoding::{compress_to_gzip};

/// 响应
#[derive(Clone, Debug)]
pub struct Response {
    pub config: Config,
    pub status: Status,
    pub headers: BTreeMap<String, String>,
    pub cookies: BTreeMap<String, String>,
    /// 消息体
    pub body: Vec<u8>,
    pub body_text: JsonValue,
    pub content_encoding: String,
}

impl Response {
    pub fn new(config: Config) -> Self {
        Self {
            config,
            status: Status::default(),
            headers: Default::default(),
            cookies: Default::default(),
            body: vec![],
            body_text: JsonValue::Null,
            content_encoding: "".to_string(),
        }
    }
    pub fn status(&mut self, code: i32) -> &mut Self {
        self.status.set_code(code);
        self
    }
    /// 跳转
    pub fn location(&mut self, uri: &str) -> &mut Self {
        self.header("Location", uri);
        self
    }
    /// 设置 HOST
    pub fn set_host(&mut self, host: &str) -> &mut Self {
        self.header("Host", host);
        self
    }
    /// 设置压缩
    pub fn set_compress(&mut self, value: Vec<String>) -> &mut Self {
        let content_encoding = value.join(",");
        self.content_encoding = content_encoding;
        self.header("Content-Encoding", self.content_encoding.clone().as_str());
        self
    }
    pub fn header(&mut self, key: &str, value: &str) -> &mut Self {
        self.headers.insert(key.to_string(), value.to_string());
        self
    }
    pub fn cookie(&mut self, key: &str, value: &str) -> &mut Self {
        self.cookies.insert(key.to_string(), value.to_string());
        self
    }
    pub fn set_server(&mut self, name: &str) {
        self.header("Server", name);
    }
    pub fn update_body(&mut self) {
        self.body = self.body_text.to_string().into_bytes();
    }
    pub fn get_date(&mut self) -> String {
        let utc: DateTime<Utc> = Utc::now();
        utc.format("%a, %d %b %Y %H:%M:%S GMT").to_string()
    }
    /// HTML 返回
    pub fn html(&mut self, value: &str) -> &mut Self {
        self.header("Content-Type", format!("{}; charset={}", Extension::form("html").as_str(), self.config.charset).as_str());
        self.body = value.to_string().into_bytes();
        self.body_text = value.into();
        self
    }
    /// TXT 返回
    pub fn txt(&mut self, value: &str) -> &mut Self {
        self.header("Content-Type", format!("{}; charset={}", Extension::form("txt").as_str(), self.config.charset).as_str());
        self.body = value.to_string().into_bytes();
        self.body_text = value.into();
        self
    }
    /// JSON 返回
    pub fn json(&mut self, value: JsonValue) -> &mut Self {
        self.header("Content-Type", format!("{}; charset={}", Extension::form("json").as_str(), self.config.charset).as_str());
        self.body = value.to_string().into_bytes();
        self.body_text = value;
        self
    }
    /// 下载 返回
    pub fn download(&mut self, filename: PathBuf) -> &mut Self {
        let file = match fs::read(filename.clone()) {
            Ok(e) => e,
            Err(_) => {
                self.status(404);
                return self;
            }
        };
        let extension = filename.extension().unwrap().to_str().unwrap().to_lowercase();
        self.header("Content-Type", format!("{}; charset={}", Extension::form(extension.as_str()).as_str(), self.config.charset).as_str());
        self.header("Content-Disposition", format!(r#"attachment; filename="{}""#, filename.file_name().unwrap().to_str().unwrap()).as_str());
        let digest = md5::compute(file.clone());
        self.header("Cache-Control", "no-cache");
        self.header("ETag", format!("{:x}", digest).as_str());
        self.body = file;
        self
    }
    /// 预览文件 返回
    pub fn file(&mut self, filename: PathBuf) -> &mut Self {
        let file = match fs::read(filename.clone()) {
            Ok(e) => e,
            Err(_) => {
                self.status(404);
                return self;
            }
        };
        let extension = filename.extension().unwrap().to_str().unwrap().to_lowercase();
        self.header("Content-Type", format!("{}; charset={}", Extension::form(extension.as_str()).as_str(), self.config.charset).as_str());
        self.header("Content-Disposition", format!(r#"inline; filename="{}""#, filename.file_name().unwrap().to_str().unwrap()).as_str());
        let digest = md5::compute(file.clone());
        self.header("Cache-Control", "no-cache");
        self.header("ETag", format!("{:x}", digest).as_str());
        self.body = file;
        self
    }
    pub fn websocket(&mut self, key: String) -> &mut Self {
        self.header("Upgrade", "websocket");
        self.header("Connection", "Upgrade");
        let sha_code = sha_1(format!("{}258EAFA5-E914-47DA-95CA-C5AB0DC85B11", key).as_str());
        let sec_websocket_accept = encode_file(sha_code);
        self.header("Sec-WebSocket-Accept", sec_websocket_accept.leak());
        self
    }
    pub fn send(&mut self, scheme: Scheme) -> io::Result<()> {
        let mut header = vec![];
        header.push(self.status.text());

        header.push(format!("Date: {}", self.get_date()));

        for (key, value) in self.headers.iter() {
            header.push(format!("{}: {}", key, value))
        }

        for (key, value) in self.cookies.iter() {
            header.push(format!("Set-Cookie: {}={}", key, value));
        }

        if !self.body.is_empty() {
            header.push(format!("Content-Length: {}", self.body.len()));
        }

        let body = if !self.content_encoding.is_empty() {
            match self.content_encoding.as_str() {
                x if x.contains("gzip") => {
                    let t = compress_to_gzip(self.body.clone())?;
                    unsafe { String::from_utf8_unchecked(t) }
                }
                _ => {
                    unsafe { String::from_utf8_unchecked(self.body.clone()) }
                }
            }
        } else {
            unsafe { String::from_utf8_unchecked(self.body.clone()) }
        };

        let text = format!("{}\r\n\r\n{}", header.join("\r\n"), body);
        if self.config.is_debug {
            debug!("\r\n=================响应信息=================\r\n{}\r\n===========================================", text);
        }
        scheme.write(text.as_bytes())?;
        Ok(())
    }
}
impl Default for Response {
    fn default() -> Self {
        Self {
            config: Default::default(),
            status: Default::default(),
            headers: Default::default(),
            cookies: Default::default(),
            body: vec![],
            body_text: JsonValue::Null,
            content_encoding: "".to_string(),
        }
    }
}
#[derive(Clone, Debug)]
pub struct Status {
    pub code: i32,
    protocol: Protocol,
    reason: String,
}
impl Status {
    pub fn set_code(&mut self, code: i32) {
        self.code = code;
        self.reason = match code {
            100 => "Continue",
            101 => "Switching Protocols",
            102 => "Processing",
            200 => "OK",
            201 => "Created",
            204 => "No Content",
            301 => "Permanently Moved", // 重定向
            302 => "Move temporarily",
            403 => "Forbidden",
            404 => "Not Found", //服务器无法根据客户端的请求找到资源(网页)。通过此代码,网站设计人员可设置"您所请求的资源无法找到"的个性页面
            500 => "Internal Server Error", //服务器内部错误,无法完成请求
            501 => "Not Implemented", //服务器不支持请求的功能,无法完成请求
            502 => "Bad Gateway", //作为网关或者代理工作的服务器尝试执行请求时,从远程服务器接收到了一个无效的响应
            503 => "Service Unavailable", //由于超载或系统维护,服务器暂时的无法处理客户端的请求。延时的长度可包含在服务器的Retry-After头信息中
            504 => "Gateway Time-out", //充当网关或代理的服务器,未及时从远端服务器获取请求
            505 => "HTTP Version Not Supported", //服务器不支持请求的HTTP协议的版本,无法完成处理
            _ => ""
        }.to_string()
    }
    pub fn text(&mut self) -> String {
        format!("{} {} {}", self.protocol.str(), self.code, self.reason)
    }
}
impl Default for Status {
    fn default() -> Self {
        Self {
            code: 200,
            protocol: Protocol::HTTP1_1,
            reason: "OK".to_string(),
        }
    }
}

/// 文件类型
enum Extension {}

impl Extension {
    pub fn form(extension: &str) -> String {
        match extension {
            "html" => "text/html",
            "css" => "text/css",
            "js" => "application/javascript",
            "json" => "application/json",
            "png" => "image/png",

            "jpg" | "jpeg" => "image/jpeg",
            "gif" => "image/gif",
            "txt" => "text/plain",
            "pdf" => "application/pdf",
            "svg" => "image/svg+xml",
            "woff" => "application/font-woff",
            "woff2" => "application/font-woff2",
            _ => {
                warn!("未知 content_type: {}",extension);
                "application/octet-stream"
            }
        }.to_string()
    }
}