br_web/
request.rs

1use std::{fs, thread};
2use std::fs::OpenOptions;
3use std::io::Write;
4use std::str::Lines;
5use std::time::{Duration, UNIX_EPOCH};
6use chrono::{DateTime, Local};
7use json::{array, JsonValue, object};
8use log::{debug, error, info};
9use crate::config::Config;
10use crate::content_type::ContentType;
11
12/// 请求处理
13#[derive(Clone, Debug)]
14pub struct Request {
15    pub config: Config,
16    /// 协议版本
17    pub protocol: Protocol,
18
19    /// 当前请求类型
20    pub method: Method,
21    /// 请求地址
22    pub uri: String,
23    /// 应用名称
24    pub app: String,
25
26    /// 请求时间戳
27    pub timestamp: f64,
28    /// 当前时间
29    pub datetime: String,
30
31    /// 服务器IP
32    pub server_ip: String,
33    /// 客户端IP
34    pub client_ip: String,
35    /// 代理IP
36    pub agent_ip: String,
37
38    /// 请求源
39    pub origin: String,
40    /// 当前访问域名或者IP 主机和端口号 头域
41    pub host: String,
42    /// 浏览器对响应的处理方式
43    sec_fetch_dest: SecFetchDest,
44    /// 浏览器请求模式
45    sec_fetch_mode: SecFetchMode,
46    /// 浏览器请求的上下文环境
47    sec_fetch_site: SecFetchSite,
48    /// 连接方式
49    pub connection: Connection,
50    /// 客户端类型
51    pub user_agent: String,
52    /// 客户端接受类型
53    pub accept: String,
54    /// 客户端接受加密
55    pub accept_encoding: String,
56    /// 客户端接受语言
57    pub accept_language: String,
58    /// 缓存机制
59    pub cache_control: String,
60    /// 请求内容的长度
61    pub content_length: usize,
62    /// 请求消息体长度
63    pub content_body_length: usize,
64    /// 消息体
65    pub body: Vec<u8>,
66    /// 来源于
67    pub referer: String,
68    /// 请求类型
69    pub content_type: RequestContentType,
70    /// 分界线
71    pub boundary: String,
72    /// 自定义参数
73    pub custom: JsonValue,
74    /// 路径
75    pub path: String,
76    /// 查询条件
77    pub query: String,
78    pub post: JsonValue,
79    pub text: String,
80    pub html: String,
81    pub get: JsonValue,
82
83    pub files: JsonValue,
84    /// 认证模式
85    pub authorization_mode: String,
86    pub authorization_user: String,
87    pub authorization_pass: String,
88    pub authorization_token: String,
89    pub authorization_data: String,
90    /// 是否有源
91    pub is_origin: bool,
92
93    /// websocket
94    pub upgrade: String,
95    /// websocket 密钥
96    pub sec_web_socket_key: String,
97    /// websocket 版本
98    pub sec_web_socket_version: String,
99}
100
101impl Request {
102    pub fn new(config: Config, data: Vec<u8>) -> Result<Self, String> {
103        let (header, body) = if let Some(index) = data.windows(4).position(|window| window == [13, 10, 13, 10]) {
104            (data[..index].to_vec(), data[index + 4..].to_vec())
105        } else {
106            return Err("未找到消息头".to_string());
107        };
108        let header = unsafe { String::from_utf8_unchecked(header) };
109        if config.debug {
110            debug!("{:#}",header);
111        }
112        let mut data = header.lines();
113
114        let header_line = data.next().unwrap_or("");
115        let parts = header_line.split_whitespace().collect::<Vec<&str>>();
116        if parts.len() != 3 {
117            return Err(format!("非法请求: {}", header_line));
118        }
119
120
121        let method = Method::from(parts[0]);
122        let mut uri = br_crypto::encoding::urlencoding_decode(parts[1]);
123        if !uri.starts_with("/") {
124            uri = format!("/{}", uri.clone());
125        }
126        let protocol = Protocol::from(parts[2]);
127
128        let timestamp = Local::now().timestamp_millis() as f64 / 1000.0;
129        let d = UNIX_EPOCH + Duration::from_secs(timestamp as u64);
130        let datetime = DateTime::<Local>::from(d);
131        let datetime = datetime.format("%Y-%m-%d %H:%M:%S").to_string();
132
133        let mut request = Self {
134            config: config.clone(),
135            timestamp,
136            datetime,
137            protocol,
138            method,
139            uri,
140            server_ip: "".to_string(),
141            client_ip: "".to_string(),
142            agent_ip: "".to_string(),
143            origin: "".to_string(),
144            host: "".to_string(),
145            sec_fetch_dest: SecFetchDest::None,
146            sec_fetch_mode: SecFetchMode::None,
147            sec_fetch_site: SecFetchSite::None,
148            connection: Connection::None,
149            user_agent: "".to_string(),
150            accept: "".to_string(),
151            accept_encoding: "".to_string(),
152            accept_language: "".to_string(),
153            cache_control: "".to_string(),
154            content_length: 0,
155            content_body_length: body.len(),
156            body,
157            referer: "".to_string(),
158            content_type: RequestContentType::None,
159            boundary: "".to_string(),
160            custom: object! {},
161            path: "".to_string(),
162            query: "".to_string(),
163            post: object! {},
164            text: "".to_string(),
165            html: "".to_string(),
166            get: object! {},
167            files: object! {},
168            authorization_mode: "".to_string(),
169            authorization_user: "".to_string(),
170            authorization_pass: "".to_string(),
171            authorization_token: "".to_string(),
172            is_origin: false,
173            app: "".to_string(),
174            authorization_data: "".to_string(),
175            sec_web_socket_key: "".to_string(),
176            upgrade: "".to_string(),
177            sec_web_socket_version: "".to_string(),
178        };
179        request.header(data);
180        Ok(request)
181    }
182    pub fn json(&mut self) -> JsonValue {
183        let mut res = object! {};
184        res["protocol"] = self.protocol.str().into();
185        res["method"] = self.method.clone().str().into();
186        res["timestamp"] = self.timestamp.into();
187        res["datetime"] = self.datetime.clone().into();
188
189        res["schema"] = self.config.schema.clone().into();
190        // 插件库之后替换为url
191        res["domain"] = self.config.url.clone().into();
192        res["url"] = self.config.url.clone().into();
193
194        res["uri"] = self.uri.clone().into();
195        res["app"] = self.app.clone().into();
196
197        res["origin"] = self.origin.clone().into();
198        res["host"] = self.host.clone().into();
199        res["user_agent"] = self.user_agent.clone().into();
200        res["content_type"] = self.content_type.clone().str().into();
201        res["content_length"] = self.content_length.into();
202
203        res["server_ip"] = self.server_ip.clone().into();
204        res["client_ip"] = self.client_ip.clone().into();
205        res["agent_ip"] = self.agent_ip.clone().into();
206
207        res["path"] = self.path.clone().into();
208        res["query"] = self.query.clone().into();
209
210        res["custom"] = self.custom.clone();
211        res["post"] = self.post.clone();
212        res["get"] = self.get.clone();
213        res["text"] = self.text.clone().into();
214        res["html"] = self.html.clone().into();
215        res["files"] = self.files.clone();
216
217        res["authorization_mode"] = self.authorization_mode.clone().into();
218        res["authorization_user"] = self.authorization_user.clone().into();
219        res["authorization_pass"] = self.authorization_pass.clone().into();
220        res["authorization_token"] = self.authorization_token.clone().into();
221        res["authorization_data"] = self.authorization_data.clone().into();
222
223        res["public"] = self.config.public.clone().into();
224        res["runtime"] = self.config.runtime.clone().into();
225
226        if self.config.debug {
227            res["sec-fetch-dest"] = self.sec_fetch_dest.str().into();
228            res["sec-fetch-mode"] = self.sec_fetch_mode.str().into();
229            res["sec-fetch-site"] = self.sec_fetch_site.str().into();
230            res["accept-language"] = self.accept_language.clone().into();
231            res["connection"] = self.connection.str().into();
232        }
233        res
234    }
235    pub fn header(&mut self, data: Lines) -> &mut Self {
236        for text in data {
237            let (key, value) = match text.trim().find(":") {
238                None => {
239                    continue;
240                }
241                Some(e) => {
242                    let key = text[..e].trim().to_lowercase().clone();
243                    let value = text[e + 1..].trim().to_string();
244                    (key, value)
245                }
246            };
247            match key.as_str() {
248                "host" => self.host = value,
249                "sec-fetch-site" => self.sec_fetch_site = SecFetchSite::from(&value),
250                "sec-fetch-mode" => self.sec_fetch_mode = SecFetchMode::from(&value),
251                "sec-fetch-dest" => self.sec_fetch_dest = SecFetchDest::from(&value),
252                "connection" => self.connection = Connection::from(&value),
253                "user-agent" => self.user_agent = value,
254
255                "accept" => self.accept = value,
256                "accept-language" => {
257                    let language = value.split(",").collect::<Vec<&str>>();
258                    if language.len() > 1 {
259                        self.accept_language = language[0].to_string();
260                    } else {
261                        self.accept_language = value;
262                    }
263                }
264                "accept-encoding" => self.accept_encoding = value.split(",").collect::<Vec<&str>>().join(";"),
265
266                "cache-control" => self.cache_control = value,
267                "content-type" => {
268                    match value {
269                        _ if value.contains("multipart/form-data") => {
270                            let boundary: Vec<&str> = value.split("boundary=").collect();
271                            self.boundary = boundary[1..].join("").to_string();
272                            self.content_type = RequestContentType::from("multipart/form-data");
273                        }
274                        _ => {
275                            self.content_type = RequestContentType::from(value.as_str());
276                        }
277                    }
278                }
279                "content-length" => self.content_length = value.parse::<usize>().unwrap_or(0),
280                "origin" => self.origin = value,
281                "referer" => self.referer = value,
282                "authorization" => {
283                    let authorization = value.split_whitespace().collect::<Vec<&str>>();
284                    self.authorization_mode = authorization[0].to_lowercase();
285                    match self.authorization_mode.as_str() {
286                        "basic" => {
287                            let text = br_crypto::base64::decode(authorization[1]);
288                            let text: Vec<&str> = text.split(":").collect();
289                            self.authorization_user = text[0].to_string();
290                            self.authorization_pass = text[1].to_string();
291                        }
292                        "bearer" => {
293                            self.authorization_token = authorization[1].to_string();
294                        }
295                        _ => {
296                            self.authorization_data = authorization[1..].concat();
297                        }
298                    }
299                }
300                "sec-websocket-key" => {
301                    self.sec_web_socket_key = value;
302                }
303                "sec_websocket_version" => {
304                    self.sec_web_socket_version = value;
305                }
306                "upgrade" => {
307                    self.upgrade = value;
308                }
309                _ => {
310                    if !key.starts_with("x-") {
311                        continue;
312                    }
313                    match &key[2..] {
314                        "real-ip" => {
315                            self.client_ip = value;
316                        }
317                        // 代理IP
318                        "forwarded-for" => {
319                            self.agent_ip = value;
320                        }
321                        _ => {
322                            self.custom[&key[2..]] = value.into();
323                        }
324                    }
325                }
326            }
327        }
328        self.query();
329        self
330    }
331    fn query(&mut self) {
332        let mut uri = self.uri.clone();
333        match uri.find("?") {
334            None => {}
335            Some(e) => {
336                let query = uri[e + 1..].to_string();
337                uri = uri[..e].to_string();
338                self.query = query.clone();
339                let params = if query.contains("&") {
340                    query.split("&").collect::<Vec<&str>>()
341                } else {
342                    vec![query.as_str()]
343                };
344                for param in params {
345                    let res = param.split("=").collect::<Vec<&str>>();
346                    let key = res[0];
347                    let value = res[1..].concat();
348                    let key = br_crypto::encoding::urlencoding_decode(key);
349                    let value = br_crypto::encoding::urlencoding_decode(value.as_str());
350                    self.get[key] = value.into();
351                }
352            }
353        }
354        self.path = uri.clone();
355        let apps = self.path.split("/").collect::<Vec<&str>>();
356        self.app = if !apps.is_empty() {
357            apps[1].to_string()
358        } else {
359            "api".to_string()
360        };
361    }
362    /// 请求日志记录
363    pub(crate) fn record_log(&mut self) {
364        let text = format!("[{}] client-ip: {} agent_ip: {} content-type: {} content-length: {} {} {} {}\r\n",
365                           self.datetime.clone(),
366                           self.client_ip.clone(),
367                           self.agent_ip.clone(),
368                           self.content_type.clone().str(),
369                           self.content_length.clone(),
370                           self.method.clone().str(), self.protocol.str(), self.uri
371        );
372        let dir_path = format!("{}/log/{}", self.config.runtime, self.datetime[0..10].replace("-", "/"));
373        let path = format!("{}/{}.log", dir_path.clone(), &self.datetime[11..13]);
374        let debug = self.config.debug;
375        thread::spawn(move || {
376            let path_new = path.clone();
377            match fs::create_dir_all(dir_path.clone()) {
378                Ok(_) => {}
379                Err(e) => {
380                    if debug {
381                        return error!("创建日志目录失败: {} {}",dir_path.clone(),e.to_string());
382                    }
383                    return;
384                }
385            };
386            match OpenOptions::new()
387                .read(true)
388
389                .create(true)
390                .append(true)
391                .open(path_new.clone()) {
392                Ok(mut file) => {
393                    let _ = file.write_all(text.as_bytes());
394                }
395                Err(_) => {
396                    if debug {
397                        error!("日志写入失败: {}",path_new.clone())
398                    }
399                }
400            }
401        });
402    }
403    pub fn body(&mut self) -> Result<(), String> {
404        if self.content_length > self.content_body_length {
405            return Err(format!("消息长度错误: {}/{}", self.content_body_length, self.content_length));
406        }
407        match self.content_type {
408            RequestContentType::FormData => {
409                let request = unsafe { String::from_utf8_unchecked(self.body.clone()) };
410                let mut request = request.trim_end_matches(format!("--{}--\r\n", self.boundary).as_str()).split(format!("--{}\r\n", self.boundary).as_str()).collect::<Vec<&str>>();
411                request.remove(0);
412                for item in request.iter_mut() {
413                    if item.contains("filename=") {
414                        let text: Vec<&str> = item.split("\r\n\r\n").collect();
415                        let temp = if text.len() > 2 {
416                            text[1..].join("\r\n\r\n")
417                        } else {
418                            text[1].to_string()
419                        };
420                        let body = match temp.strip_suffix("\r\n") {
421                            None => temp.as_str(),
422                            Some(v) => v
423                        };
424                        let text: Vec<&str> = text[0].split("\r\n").collect();
425                        let name: Vec<&str> = text[0].split("\"").collect();
426                        let types: Vec<&str> = text[1].split(": ").collect();
427                        let accept = types[1];
428                        let field = name[1];
429                        let filename = name[3];
430
431                        let mut file_data = object! {};
432                        file_data["accept"] = accept.into();
433                        file_data["name"] = filename.into();
434                        file_data["md5"] = br_crypto::hash::str_to_md5(body).into();
435                        file_data["suffix"] = ContentType::from(accept).suffix.into();
436                        file_data["types"] = ContentType::from_suffix(file_data["suffix"].as_str().unwrap()).suffix_type_name().into();
437                        file_data["size"] = body.len().into();
438                        file_data["data"] = br_crypto::base64::encode_file(body.as_bytes().into()).into();
439                        if self.files[field].is_empty() {
440                            self.files[field] = array![file_data.clone()];
441                        } else {
442                            self.files[field].push(file_data.clone()).unwrap();
443                        }
444                    } else {
445                        let re = item.split("\r\n\r\n").collect::<Vec<&str>>();
446                        let name = re[0].trim_end_matches("\"").split("name=\"").collect::<Vec<&str>>();
447                        self.post[name[1]] = re[1].trim_end_matches("\r\n").into();
448                    }
449                }
450            }
451            RequestContentType::FOrmUrlencoded => {
452                let request = unsafe { String::from_utf8_unchecked(self.body.clone()) };
453                let params = if request.contains("&") {
454                    request.split("&").collect::<Vec<&str>>()
455                } else {
456                    vec![request.as_str()]
457                };
458                for param in params {
459                    let res = param.split("=").collect::<Vec<&str>>();
460                    let key = res[0];
461                    let value = res[1..].concat();
462                    let key = br_crypto::encoding::urlencoding_decode(key);
463                    let value = br_crypto::encoding::urlencoding_decode(value.as_str());
464                    self.post[key] = value.into();
465                }
466            }
467            RequestContentType::Javascript => {
468                let request = unsafe { String::from_utf8_unchecked(self.body.clone()) };
469                if self.config.debug {
470                    info!("Javascript: {}", request);
471                }
472            }
473            RequestContentType::Json => {
474                let request = unsafe { String::from_utf8_unchecked(self.body.clone()) };
475                match json::parse(request.as_str()) {
476                    Ok(e) => {
477                        self.post = e;
478                    }
479                    Err(_) => {
480                        error!("解析json失败,请检查格式:{}", request);
481                        self.post = object! {}
482                    }
483                };
484            }
485            RequestContentType::Xml => {
486                let request = unsafe { String::from_utf8_unchecked(self.body.clone()) };
487                let res = br_xml::Xml::xml_json(request);
488                self.post = res;
489            }
490            RequestContentType::Text => {
491                let request = unsafe { String::from_utf8_unchecked(self.body.clone()) };
492                self.text = request;
493            }
494            RequestContentType::Html => {
495                let request = unsafe { String::from_utf8_unchecked(self.body.clone()) };
496                self.html = request;
497            }
498            RequestContentType::None => {
499                let request = unsafe { String::from_utf8_unchecked(self.body.clone()) };
500                if self.config.debug {
501                    info!("RequestContentType 未知: {}", request);
502                }
503            }
504        }
505        Ok(())
506    }
507
508    /// 源判断
509    pub(crate) fn allow_origin(&mut self) {
510        self.is_origin = false;
511        if !self.config.cors.allow_origin.is_empty() {
512            if self.origin.is_empty() {
513                for origin in &self.config.cors.allow_origin {
514                    if self.client_ip.contains(origin) {
515                        self.is_origin = true;
516                    }
517                }
518            } else {
519                for origin in &self.config.cors.allow_origin {
520                    if self.origin.contains(origin) {
521                        self.is_origin = true;
522                    }
523                }
524            }
525        } else {
526            self.is_origin = true;
527        }
528    }
529}
530
531/// 浏览器的请求模式
532#[derive(Clone, Debug)]
533enum SecFetchSite {
534    /// 一个跨域请求,即请求的目标与当前页面不具有相同的来源
535    CrossSite,
536    /// 请求的目标与当前页面具有相同的来源,即同源请求
537    SameOrigin,
538    /// 请求不是从一个受信任的网站或者内嵌资源中发起的
539    None,
540}
541
542impl SecFetchSite {
543    pub fn from(name: &str) -> Self {
544        match name.to_lowercase().as_str() {
545            "none" => Self::None,
546            "cross-site" => Self::CrossSite,
547            "same-origin" => Self::SameOrigin,
548            _ => Self::None,
549        }
550    }
551    pub fn str(&mut self) -> &'static str {
552        match self {
553            SecFetchSite::CrossSite => "cross-site",
554            SecFetchSite::SameOrigin => "same-origin",
555            SecFetchSite::None => "none"
556        }
557    }
558}
559
560/// 浏览器的请求模式
561#[derive(Clone, Debug)]
562enum SecFetchMode {
563    /// 导航请求
564    Navigate,
565    /// 同源请求
566    SameOrigin,
567    /// 跨域请求,不需要浏览器执行跨域检查
568    NoCors,
569    /// 跨域请求,需要浏览器执行跨域检查
570    Cors,
571    /// WebSocket 连接请求
572    Websocket,
573    /// 未知
574    None,
575}
576
577impl SecFetchMode {
578    pub fn from(name: &str) -> Self {
579        match name.to_lowercase().as_str() {
580            "navigate" => Self::Navigate,
581            "same-origin" => Self::SameOrigin,
582            "no-cors" => Self::NoCors,
583            "cors" => Self::Cors,
584            "websocket" => Self::Websocket,
585            _ => Self::None,
586        }
587    }
588    pub fn str(&mut self) -> &'static str {
589        match self {
590            Self::Navigate => "navigate",
591            Self::SameOrigin => "same-origin",
592            Self::NoCors => "no-cors",
593            Self::Cors => "cors",
594            Self::Websocket => "websocket",
595            Self::None => ""
596        }
597    }
598}
599
600/// 浏览器对响应的处理方式
601#[derive(Clone, Debug)]
602enum SecFetchDest {
603    /// 浏览器希望接收的响应是一个文档(HTML、XML 等),通常用于页面导航或者获取页面内容的请求。
604    Document,
605    /// 浏览器希望接收的响应是一个图像
606    Image,
607    /// 浏览器希望接收的响应是媒体文件
608    Media,
609    /// 浏览器希望接收的响应是字体文件
610    Font,
611    /// 浏览器希望接收的响应是 JavaScript 脚本文件
612    Script,
613    /// 浏览器希望接收的响应是 CSS 样式表文件
614    Css,
615    /// 浏览器希望接收的响应是 PWA(渐进式 Web 应用)的清单文件
616    Manifest,
617    /// 浏览器希望接收的响应是 Service Worker 文件,用于支持离线缓存和推送通知等功能。
618    Serviceworker,
619    /// 未知
620    None,
621}
622
623impl SecFetchDest {
624    pub fn from(name: &str) -> Self {
625        match name.to_lowercase().as_str() {
626            "document" => Self::Document,
627            "image" => Self::Image,
628            "media" => Self::Media,
629            "font" => Self::Font,
630            "script" => Self::Script,
631            "css" => Self::Css,
632            "manifest" => Self::Manifest,
633            "serviceworker" => Self::Serviceworker,
634            _ => Self::None,
635        }
636    }
637    pub fn str(&mut self) -> &'static str {
638        match self {
639            Self::Document => "document",
640            Self::Image => "image",
641            Self::Media => "media",
642            Self::Font => "font",
643            Self::Script => "script",
644            Self::Css => "css",
645            Self::Manifest => "manifest",
646            Self::Serviceworker => "serviceworker",
647            Self::None => ""
648        }
649    }
650}
651
652/// 协议
653#[derive(Clone, Debug)]
654pub enum Protocol {
655    HTTP1_1,
656    HTTP2,
657    None,
658}
659
660impl Protocol {
661    pub fn from(name: &str) -> Self {
662        match name.to_lowercase().as_str() {
663            "http/1.1" => Self::HTTP1_1,
664            "http/2" => Self::HTTP2,
665            _ => Self::None,
666        }
667    }
668    pub fn str(&mut self) -> &'static str {
669        match self {
670            Protocol::HTTP1_1 => "HTTP/1.1",
671            Protocol::HTTP2 => "HTTP/2",
672            Protocol::None => ""
673        }
674    }
675}
676
677/// 客户端和服务器之间的连接状态
678#[derive(Clone, Debug)]
679pub enum Connection {
680    /// 客户端希望与服务器保持持久连接,以便在未来的请求中复用同一连接,减少连接的建立和断开开销,提高性能。
681    KeepAlive,
682    /// 客户端希望在完成当前请求之后立即关闭与服务器的连接。这种情况通常发生在客户端只需要单个请求-响应周期的情况下。
683    Close,
684    Upgrade,
685    None,
686}
687
688impl Connection {
689    pub fn from(name: &str) -> Self {
690        match name.to_lowercase().as_str() {
691            "keep-alive" => Self::KeepAlive,
692            "close" => Self::Close,
693            "upgrade" => Self::Upgrade,
694            _ => Self::None,
695        }
696    }
697    pub fn str(&mut self) -> &'static str {
698        match self {
699            Self::KeepAlive => "keep-alive",
700            Self::Close => "close",
701            Self::Upgrade => "upgrade",
702            Self::None => ""
703        }
704    }
705}
706
707/// 请求方法
708#[derive(Clone, Debug)]
709pub enum Method {
710    POST,
711    GET,
712    HEAD,
713    PUT,
714    DELETE,
715    OPTIONS,
716    PATCH,
717    TRACE,
718    None,
719}
720
721impl Method {
722    pub fn from(name: &str) -> Self {
723        match name.to_lowercase().as_str() {
724            "post" => Self::POST,
725            "get" => Self::GET,
726            "head" => Self::HEAD,
727            "put" => Self::PUT,
728            "delete" => Self::DELETE,
729            "options" => Self::OPTIONS,
730            "patch" => Self::PATCH,
731            "trace" => Self::TRACE,
732            _ => Self::None,
733        }
734    }
735    pub fn str(self) -> &'static str {
736        match self {
737            Method::POST => "post",
738            Method::GET => "get",
739            Method::HEAD => "head",
740            Method::PUT => "put",
741            Method::DELETE => "delete",
742            Method::OPTIONS => "options",
743            Method::PATCH => "patch",
744            Method::TRACE => "trace",
745            Method::None => ""
746        }
747    }
748}
749
750#[derive(Clone, Debug)]
751pub enum RequestContentType {
752    FormData,
753    FOrmUrlencoded,
754    Json,
755    Xml,
756    Javascript,
757    Text,
758    Html,
759    None,
760}
761
762impl RequestContentType {
763    pub fn from(name: &str) -> Self {
764        match name {
765            "multipart/form-data" => Self::FormData,
766            "application/x-www-form-urlencoded" => Self::FOrmUrlencoded,
767            "application/json" => Self::Json,
768            "application/xml" | "text/xml" => Self::Xml,
769            "application/javascript" => Self::Javascript,
770            "text/html" => Self::Html,
771            "text/plain" => Self::Text,
772            _ => {
773                info!("未知请求类型: {}", name);
774                Self::None
775            }
776        }
777    }
778    pub fn str(self) -> &'static str {
779        match self {
780            RequestContentType::FormData => "multipart/form-data",
781            RequestContentType::FOrmUrlencoded => "application/x-www-form-urlencoded",
782            RequestContentType::Json => "application/json",
783            RequestContentType::Xml => "application/xml",
784            RequestContentType::Javascript => "application/javascript",
785            RequestContentType::Text => "text/plain",
786            RequestContentType::Html => "text/html",
787            RequestContentType::None => ""
788        }
789    }
790}