br_reqwest/
lib.rs

1use json::{array, object, JsonValue};
2use log::{debug, error, info};
3use std::collections::HashMap;
4use std::fmt::Debug;
5use std::io::Write;
6use std::path::Path;
7use std::process::Command;
8use std::str::Lines;
9use std::{env, fs, str};
10use xmltree::Element;
11
12pub struct Client {
13    pub url: String,
14    debug: bool,
15    pub method: Method,
16    pub header: HashMap<String, String>,
17    body_data: JsonValue,
18    pub content_type: ContentType,
19    proxy: ProxyInfo,
20    cert_p12: bool,
21    cert_p12_filename: String,
22    cert_p12_password: String,
23    args: Vec<String>,
24    /// 请求超时
25    request_timeout: usize,
26    /// 响应超时
27    response_timeout: usize,
28    /// 重试次数
29    retry: usize,
30    command: Vec<String>,
31}
32
33impl Default for Client {
34    fn default() -> Self {
35        Self::new()
36    }
37}
38
39impl Client {
40    pub fn new() -> Self {
41        Self {
42            url: "".to_string(),
43            debug: false,
44            method: Method::NONE,
45            header: Default::default(),
46            body_data: object! {},
47            content_type: ContentType::Text,
48            proxy: ProxyInfo::None,
49            cert_p12: false,
50            cert_p12_filename: "".to_string(),
51            cert_p12_password: "".to_string(),
52            args: vec![],
53            request_timeout: 0,
54            response_timeout: 0,
55            retry: 0,
56            command: vec![],
57        }
58    }
59    pub fn debug(&mut self) -> &mut Self {
60        self.debug = true;
61        self
62    }
63    pub fn post(&mut self, url: &str) -> &mut Self {
64        self.url = url.to_string();
65        self.method = Method::POST;
66        self
67    }
68    pub fn get(&mut self, url: &str) -> &mut Self {
69        self.url = url.to_string();
70        self.method = Method::GET;
71        self
72    }
73    pub fn put(&mut self, url: &str) -> &mut Self {
74        self.url = url.to_string();
75        self.method = Method::PUT;
76        self
77    }
78    pub fn patch(&mut self, url: &str) -> &mut Self {
79        self.url = url.to_string();
80        self.method = Method::PATCH;
81        self
82    }
83    pub fn delete(&mut self, url: &str) -> &mut Self {
84        self.url = url.to_string();
85        self.method = Method::DELETE;
86        self
87    }
88    pub fn head(&mut self, url: &str) -> &mut Self {
89        self.url = url.to_string();
90        self.method = Method::HEAD;
91        self
92    }
93    pub fn options(&mut self, url: &str) -> &mut Self {
94        self.url = url.to_string();
95        self.method = Method::OPTIONS;
96        self
97    }
98    pub fn trace(&mut self, url: &str) -> &mut Self {
99        self.url = url.to_string();
100        self.method = Method::TRACE;
101        self
102    }
103    pub fn header(&mut self, key: &str, value: &str) -> &mut Self {
104        self.header.insert(key.to_string(), value.to_string());
105        self
106    }
107
108    pub fn query(&mut self, params: JsonValue) -> &mut Self {
109        let mut txt = vec![];
110        for (key, value) in params.entries() {
111            txt.push(format!("{key}={value}"));
112        }
113        if self.url.contains('?') {
114            if !txt.is_empty() {
115                self.url = format!("{}&{}", self.url, txt.join("&"));
116            }
117        } else if !txt.is_empty() {
118            self.url = format!("{}?{}", self.url, txt.join("&"));
119        }
120        self
121    }
122    /// 设置连接超时
123    pub fn set_request_timeout(&mut self, s: usize) -> &mut Self {
124        self.request_timeout = s;
125        self
126    }
127
128    pub fn set_response_timeout(&mut self, s: usize) -> &mut Self {
129        self.response_timeout = s;
130        self
131    }
132
133    pub fn set_retry(&mut self, count: usize) -> &mut Self {
134        self.retry = count;
135        self
136    }
137
138    pub fn set_command(&mut self, cmd: Vec<String>) -> &mut Self {
139        self.command = cmd;
140        self
141    }
142
143    /// JSON请求
144    pub fn raw_json(&mut self, data: JsonValue) -> &mut Self {
145        self.header("Content-Type", ContentType::Json.str().as_str());
146        self.content_type = ContentType::Json;
147        self.body_data = data;
148        self
149    }
150    pub fn raw_xml(&mut self, data: JsonValue) -> &mut Self {
151        self.header("Content-Type", ContentType::Xml.str().as_str());
152        self.content_type = ContentType::Xml;
153        self.body_data = data;
154        self
155    }
156    pub fn raw_stream(&mut self, filename: &str) -> &mut Self {
157        self.content_type = ContentType::Stream;
158        self.body_data = filename.into();
159        self
160    }
161    pub fn raw_binary_str(&mut self, text: &str) -> &mut Self {
162        self.content_type = ContentType::BinaryStr;
163        self.body_data = text.into();
164        self
165    }
166    pub fn raw_stream_urlencode(&mut self, text: &str) -> &mut Self {
167        self.content_type = ContentType::StreamUrlencode;
168        self.body_data = text.into();
169        self
170    }
171    /// FormData请求
172    pub fn form_data(&mut self, data: JsonValue) -> &mut Self {
173        self.header("Content-Type", ContentType::FormData.str().as_str());
174        self.content_type = ContentType::FormData;
175        self.body_data = data;
176        self
177    }
178    /// FormData请求
179    pub fn form_urlencoded(&mut self, data: JsonValue) -> &mut Self {
180        self.header("Content-Type", ContentType::FormUrlencoded.str().as_str());
181        self.content_type = ContentType::FormUrlencoded;
182        self.body_data = data;
183        self
184    }
185    /// 开启旧的ssl
186    pub fn set_cert_p12(&mut self, filename: &str, password: &str) -> &mut Self {
187        self.cert_p12_filename = filename.to_string();
188        self.cert_p12_password = password.to_string();
189        self.cert_p12 = true;
190        self
191    }
192    /// 指定代理
193    // curl -x socks5://proxy.example.com:1080 http://example.com
194    pub fn add_proxy(&mut self, proxy_addr: String) {
195        self.proxy = ProxyInfo::Addr(proxy_addr);
196    }
197
198    /// 指定代理, 该代理需要用户名和密码认证
199    // curl -x http://proxy-server:port -U username:password http://example.com
200    pub fn add_proxy_with_account(&mut self, proxy_addr: String, user: String, password: String) {
201        self.proxy = ProxyInfo::AddrWithAccount(proxy_addr, user, password);
202    }
203
204    pub fn send(&mut self) -> Result<Response, String> {
205        let mut output = Command::new("curl");
206
207        if !self.command.is_empty() {
208            self.args.extend(self.command.clone());
209        }
210        if self.cert_p12 {
211            self.args.extend([
212                "--cert-type".to_string(),
213                "P12".to_string(),
214                "--cert".to_string(),
215                self.cert_p12_filename.to_string(),
216                "--pass".to_string(),
217                self.cert_p12_password.to_string(),
218            ]);
219        }
220
221        if self.debug {
222            self.args.extend(["--trace-ascii".to_string(), "br-reqwest.log".to_string()]);
223        }
224
225        if self.request_timeout > 0 {
226            self.args.extend([
227                "--connect-timeout".to_string(),
228                self.request_timeout.to_string(),
229            ]);
230        }
231        if self.response_timeout > 0 {
232            self.args.extend(["--max-time".to_string(), self.response_timeout.to_string()]);
233        }
234
235        if self.retry > 0 {
236            self.args.extend(["--retry".to_string(), self.retry.to_string()]);
237        }
238
239        match &self.proxy {
240            ProxyInfo::None => {
241                self.args.extend(["--noproxy".to_string(), "*".to_string()]);
242            }
243            ProxyInfo::Addr(proxy) => {
244                self.args.extend(["-x".to_string(), proxy.to_string()]);
245            }
246            ProxyInfo::AddrWithAccount(proxy, user, pwd) => {
247                self.args.extend([
248                    "-x".to_string(),
249                    proxy.to_string(),
250                    "-U".to_string(),
251                    format!("{user}:{pwd}"),
252                ]);
253            }
254        }
255
256        match self.method {
257            Method::HEAD => {
258                self.args.push("-I".to_string());
259            }
260            _ => {
261                self.args.extend([
262                    "-i".to_string(),
263                    "-X".to_string(),
264                    self.method.to_str().to_uppercase(),
265                ]);
266            }
267        }
268        self.args.push(self.url.to_string());
269
270        if !self.header.is_empty() {
271            for (key, value) in self.header.iter() {
272                self.args.extend(["-H".to_string(), format!("{key}: {value}")]);
273            }
274        }
275
276        match self.content_type {
277            ContentType::FormData => {
278                for (_, value) in self.body_data.entries() {
279                    self.args.push("-F".to_string());
280
281                    if value[2].is_empty() {
282                        self.args.push(format!("{}={}", value[0], value[1]));
283                    } else {
284                        self.args.push(format!("{}={};{}", value[0], value[1], value[2]));
285                    }
286                }
287            }
288            ContentType::FormUrlencoded => {
289                self.args.push("-d".to_string());
290                let mut d = vec![];
291                for (key, value) in self.body_data.entries() {
292                    d.push(format!("{key}={value}"))
293                }
294                self.args.push(d.join("&").to_string());
295            }
296            ContentType::Json | ContentType::Xml => {
297                self.args.extend(["-d".to_string(), format!("{}", self.body_data)]);
298            }
299            ContentType::Javascript => {}
300            ContentType::Text => {}
301            ContentType::Html => {}
302            ContentType::Other(_) => {}
303            ContentType::Stream => {
304                // 文件路径
305                self.args.push("--data-binary".to_string());
306                self.args.push(format!("@{}", self.body_data));
307            }
308            ContentType::BinaryStr => {
309                self.args.push("--data-binary".to_string());
310                self.args.push(format!("{}", self.body_data));
311            }
312            ContentType::StreamUrlencode => {
313                self.args.push("--data-urlencode".to_string());
314                self.args.push(format!("{}", self.body_data));
315            }
316        }
317        output.args(self.args.as_slice());
318        if self.debug {
319            info!("curl {}", self.args.join(" "));
320        }
321        let req = match output.output() {
322            Ok(e) => e,
323            Err(e) => {
324                return Err(e.to_string());
325            }
326        };
327
328        if req.status.success() {
329            let body = req.stdout.clone();
330            if self.debug {
331                let text = String::from_utf8_lossy(&req.stdout);
332                info!("响应内容:\n{text}");
333            }
334            Ok(Response::new(self.debug, body)?)
335        } else {
336            let err = String::from_utf8_lossy(&req.stderr).to_string();
337            let txt = match err.find("ms:") {
338                None => err,
339                Some(e) => err[e + 3..].trim().to_string(),
340            };
341            Err(txt)
342        }
343    }
344}
345
346enum ProxyInfo {
347    None,
348    Addr(String),
349    AddrWithAccount(String, String, String),
350}
351
352#[derive(Debug)]
353pub struct Response {
354    debug: bool,
355    version: String,
356    status: i32,
357    status_text: String,
358    headers: JsonValue,
359    cookies: JsonValue,
360    /// 请求体
361    body: Body,
362}
363impl Response {
364    const CRLF2: [u8; 4] = [13, 10, 13, 10];
365
366    fn new(debug: bool, body: Vec<u8>) -> Result<Self, String> {
367        let mut that = Self {
368            debug,
369            version: "".to_string(),
370            status: 0,
371            status_text: "".to_string(),
372            headers: object! {},
373            cookies: object! {},
374            body: Default::default(),
375        };
376
377        let (header, body) = match body.windows(Self::CRLF2.len()).position(|window| window == Self::CRLF2) {
378            None => (vec![], vec![]),
379            Some(index) => {
380                let header = body[..index].to_vec();
381                let body = Vec::from(&body[index + Self::CRLF2.len()..]);
382                (header, body)
383            }
384        };
385
386        let text = String::from_utf8_lossy(header.as_slice());
387        let lines = text.lines();
388
389        that.get_request_line(lines.clone().next().expect("get next line in response err"))?;
390        // Header处理
391        that.get_header(lines.clone())?;
392        that.body.stream = body.clone();
393        that.body.set_content(body);
394        Ok(that)
395    }
396
397    /// 获取请求行信息
398    fn get_request_line(&mut self, line: &str) -> Result<(), String> {
399        let lines = line.split_whitespace().collect::<Vec<&str>>();
400        if lines.len() < 2 {
401            return Err("请求行错误".to_string());
402        }
403        self.version = lines[0].to_string();
404        self.status = lines[1].parse::<i32>().unwrap();
405        if !lines[2].is_empty() {
406            self.status_text = lines[2].trim().to_string();
407        }
408        Ok(())
409    }
410
411    /// Header处理
412    fn get_header(&mut self, data: Lines) -> Result<(), String> {
413        let mut header = object! {};
414        let mut cookie = object! {};
415        let mut body = Body::default();
416
417        for text in data {
418            let (key, value) = match text.trim().find(":") {
419                None => continue,
420                Some(e) => {
421                    let key = text[..e].trim().to_lowercase().clone();
422                    let value = text[e + 1..].trim().to_string();
423                    (key, value)
424                }
425            };
426            match key.as_str() {
427                "content-type" => match value {
428                    _ if value.contains("multipart/form-data") => {
429                        let boundarys = value.split("boundary=").collect::<Vec<&str>>();
430                        body.boundary = boundarys[1..].join("");
431                        body.content_type = ContentType::from("multipart/form-data");
432                        let _ = header.insert(key.as_str(), "multipart/form-data");
433                    }
434                    _ => {
435                        let value = match value.find(";") {
436                            None => value,
437                            Some(e) => value[..e].trim().to_string(),
438                        };
439                        body.content_type = ContentType::from(value.as_str());
440                        let _ = header.insert(key.as_str(), body.content_type.str());
441                    }
442                },
443                "content-length" => {
444                    body.content_length = value.to_string().parse::<usize>().unwrap_or(0);
445                }
446                "cookie" => {
447                    let _ = value.split(";").collect::<Vec<&str>>().iter().map(|&x| {
448                        match x.find("=") {
449                            None => {}
450                            Some(index) => {
451                                let key = x[..index].trim().to_string();
452                                let val = x[index + 1..].trim().to_string();
453                                let _ = cookie.insert(key.as_str(), val);
454                            }
455                        };
456                        ""
457                    }).collect::<Vec<&str>>();
458                }
459                _ => {
460                    if self.debug {
461                        debug!("header: {key} = {value}");
462                    }
463                    let _ = header.insert(key.as_str(), value);
464                }
465            };
466        }
467        self.headers = header.clone();
468        self.cookies = cookie.clone();
469        self.body = body.clone();
470        Ok(())
471    }
472    pub fn status(&self) -> i32 {
473        self.status
474    }
475    pub fn status_text(&self) -> String {
476        self.status_text.clone()
477    }
478    pub fn version(&self) -> String {
479        self.version.clone()
480    }
481    pub fn headers(&self) -> JsonValue {
482        self.headers.clone()
483    }
484    pub fn cookies(&self) -> JsonValue {
485        self.cookies.clone()
486    }
487    pub fn content_type(&self) -> String {
488        self.body.content_type.str().clone()
489    }
490    pub fn json(&self) -> Result<JsonValue, String> {
491        if self.body.content.is_empty() {
492            Ok(object! {})
493        } else {
494            match json::parse(self.body.content.to_string().as_str()) {
495                Ok(e) => Ok(e),
496                Err(e) => Err(e.to_string()),
497            }
498        }
499    }
500    pub fn xml(&self) -> Result<JsonValue, String> {
501        if self.body.content.is_empty() {
502            Ok(object! {})
503        } else {
504            let json = match Element::parse(self.body.content.to_string().as_bytes()) {
505                Ok(e) => xml_element_to_json(&e),
506                Err(e) => {
507                    if self.debug {
508                        error!("{:?} {}", e.to_string(), self.body.content.clone());
509                    }
510                    self.body.content.clone()
511                }
512            };
513            Ok(json)
514        }
515    }
516    pub fn body(&self) -> JsonValue {
517        self.body.content.clone()
518    }
519    pub fn stream(&self) -> Vec<u8> {
520        self.body.stream.clone()
521    }
522}
523fn xml_element_to_json(elem: &Element) -> JsonValue {
524    let mut obj = object! {};
525
526    for child in &elem.children {
527        if let xmltree::XMLNode::Element(e) = child {
528            obj[e.name.clone()] = xml_element_to_json(e);
529        }
530    }
531
532    match elem.get_text() {
533        None => obj,
534        Some(text) => JsonValue::from(text.to_string()),
535    }
536}
537
538#[derive(Clone, Debug)]
539pub enum Method {
540    GET,
541    POST,
542    OPTIONS,
543    PATCH,
544    HEAD,
545    DELETE,
546    TRACE,
547    PUT,
548    NONE,
549}
550
551impl Method {
552    pub fn to_str(&self) -> String {
553        match self {
554            Method::GET => "GET",
555            Method::POST => "POST",
556            Method::OPTIONS => "OPTIONS",
557            Method::PATCH => "PATCH",
558            Method::HEAD => "HEAD",
559            Method::DELETE => "DELETE",
560            Method::TRACE => "TRACE",
561            Method::PUT => "PUT",
562            Method::NONE => "NONE",
563        }.to_string()
564    }
565    pub fn from(name: &str) -> Self {
566        match name.to_lowercase().as_str() {
567            "post" => Self::POST,
568            "get" => Self::GET,
569            "head" => Self::HEAD,
570            "put" => Self::PUT,
571            "delete" => Self::DELETE,
572            "options" => Self::OPTIONS,
573            "patch" => Self::PATCH,
574            "trace" => Self::TRACE,
575            _ => Self::NONE,
576        }
577    }
578}
579#[derive(Clone, Debug)]
580pub enum Version {
581    Http09,
582    Http10,
583    Http11,
584    H2,
585    H3,
586    None,
587}
588
589impl Version {
590    pub fn str(&mut self) -> String {
591        match self {
592            Version::Http09 => "HTTP/0.9",
593            Version::Http10 => "HTTP/1.0",
594            Version::Http11 => "HTTP/1.1",
595            Version::H2 => "HTTP/2.0",
596            Version::H3 => "HTTP/3.0",
597            Version::None => "",
598        }.to_string()
599    }
600    pub fn from(name: &str) -> Version {
601        match name {
602            "HTTP/0.9" => Self::Http09,
603            "HTTP/1.0" => Self::Http10,
604            "HTTP/1.1" => Self::Http11,
605            "HTTP/2.0" => Self::H2,
606            "HTTP/3.0" => Self::H3,
607            _ => Self::None,
608        }
609    }
610    pub fn set_version(name: &str) -> Version {
611        match name {
612            "0.9" => Self::Http09,
613            "1.0" => Self::Http10,
614            "1.1" => Self::Http11,
615            "2.0" => Self::H2,
616            "3.0" => Self::H3,
617            _ => Self::None,
618        }
619    }
620}
621
622#[derive(Clone, Debug)]
623pub enum FormData {
624    /// KEY
625    /// value
626    /// 文本类型
627    Text(String, JsonValue, String),
628    /// 文件名
629    /// 文件路径
630    /// 文件类型
631    File(String, String, String),
632    None,
633}
634
635#[derive(Debug, Clone)]
636pub struct Body {
637    pub content_type: ContentType,
638    pub boundary: String,
639    pub content_length: usize,
640    pub content: JsonValue,
641    pub stream: Vec<u8>,
642}
643impl Body {
644    /// 解码
645    pub fn decode(input: &str) -> Result<String, String> {
646        let mut decoded = String::new();
647        let bytes = input.as_bytes();
648        let mut i = 0;
649
650        while i < bytes.len() {
651            if bytes[i] == b'%' {
652                if i + 2 >= bytes.len() {
653                    return Err("Incomplete percent-encoding".into());
654                }
655                let hex = &input[i + 1..i + 3];
656                match u8::from_str_radix(hex, 16) {
657                    Ok(byte) => decoded.push(byte as char),
658                    Err(_) => return Err(format!("Invalid percent-encoding: %{hex}")),
659                }
660                i += 3;
661            } else if bytes[i] == b'+' {
662                decoded.push(' ');
663                i += 1;
664            } else {
665                decoded.push(bytes[i] as char);
666                i += 1;
667            }
668        }
669
670        Ok(decoded)
671    }
672    pub fn set_content(&mut self, data: Vec<u8>) {
673        match self.content_type.clone() {
674            ContentType::FormData | ContentType::Stream => {
675                let mut fields = object! {};
676                let boundary_marker = format!("--{}", self.boundary);
677                let text = unsafe { String::from_utf8_unchecked(data) };
678                let parts = text.split(&boundary_marker).collect::<Vec<&str>>();
679                for part in parts {
680                    let part = part.trim();
681                    if part.is_empty() || part == "--" {
682                        continue; // 跳过无效部分
683                    }
684
685                    let mut headers_and_body = part.splitn(2, "\r\n\r\n");
686                    if let (Some(headers), Some(body)) = (headers_and_body.next(), headers_and_body.next())
687                    {
688                        // 解析头部,查找 Content-Disposition
689                        let headers = headers.split("\r\n");
690
691                        let mut field_name = "";
692                        let mut filename = "";
693                        let mut content_type = ContentType::Text;
694
695                        for header in headers {
696                            if header.to_lowercase().starts_with("content-disposition:") {
697                                match header.find("filename=\"") {
698                                    None => {}
699                                    Some(filename_start) => {
700                                        let filename_len = filename_start + 10;
701                                        let filename_end = header[filename_len..].find('"').unwrap() + filename_len;
702                                        filename = &header[filename_len..filename_end];
703                                    }
704                                }
705
706                                match header.find("name=\"") {
707                                    None => {}
708                                    Some(name_start) => {
709                                        let name_start = name_start + 6;
710                                        let name_end = header[name_start..].find('"').unwrap() + name_start;
711                                        field_name = &header[name_start..name_end];
712                                    }
713                                }
714                            }
715                            if header.to_lowercase().starts_with("content-type:") {
716                                content_type = ContentType::from(
717                                    header.to_lowercase().trim_start_matches("content-type:").trim(),
718                                );
719                            }
720                        }
721
722                        if filename.is_empty() {
723                            fields[field_name.to_string()] = JsonValue::from(body);
724                        } else {
725                            // 获取系统临时目录
726                            let mut temp_dir = env::temp_dir();
727                            // 构造临时文件的完整路径
728                            temp_dir.push(filename);
729                            // 打开(创建)临时文件
730                            let mut temp_file = match fs::File::create(&temp_dir) {
731                                Ok(e) => e,
732                                Err(_) => continue,
733                            };
734                            if temp_file.write(body.as_bytes()).is_ok() {
735                                if fields[field_name.to_string()].is_empty() {
736                                    fields[field_name.to_string()] = array![]
737                                }
738
739                                let extension = Path::new(filename).extension() // 可能返回 None
740                                                                   .and_then(|ext| ext.to_str()); // 转换为 &str
741
742                                let suffix = extension.unwrap_or("txt");
743
744                                fields[field_name.to_string()].push(object! {
745                                        //id:sha_256(body.as_bytes().to_vec()),
746                                        name:filename,
747                                        suffix:suffix,
748                                        size:body.len(),
749                                        "type":content_type.str(),
750                                        file:temp_dir.to_str()
751                                    }).unwrap();
752                            };
753                        }
754                    }
755                }
756                self.content = fields;
757            }
758            ContentType::FormUrlencoded => {
759                let text = unsafe { String::from_utf8_unchecked(data) };
760                let params = text.split("&").collect::<Vec<&str>>();
761                let mut list = object! {};
762                for param in params.iter() {
763                    let t = param.split("=").collect::<Vec<&str>>().iter().map(|&x| Body::decode(x).unwrap_or(x.to_string())).collect::<Vec<String>>();
764                    list[t[0].to_string()] = t[1].clone().into();
765                }
766                self.content = list;
767            }
768            ContentType::Json => {
769                let text = unsafe { String::from_utf8_unchecked(data) };
770                self.content = json::parse(text.as_str()).unwrap_or(object! {});
771            }
772            ContentType::Xml => {
773                let text = unsafe { String::from_utf8_unchecked(data) };
774                self.content = text.into();
775            }
776            ContentType::Html | ContentType::Text | ContentType::Javascript => {
777                let text = unsafe { String::from_utf8_unchecked(data) };
778                self.content = text.into();
779            }
780            ContentType::Other(_) => self.content = data.into(),
781            ContentType::BinaryStr => todo!(),
782            ContentType::StreamUrlencode => todo!(),
783        }
784    }
785}
786
787impl Default for Body {
788    fn default() -> Self {
789        Self {
790            content_type: ContentType::Other("text/plain".to_string()),
791            boundary: "".to_string(),
792            content_length: 0,
793            content: object! {},
794            stream: vec![],
795        }
796    }
797}
798
799/// 内容类型
800#[derive(Debug, Clone)]
801pub enum ContentType {
802    FormData,
803    FormUrlencoded,
804    Json,
805    Xml,
806    Javascript,
807    Text,
808    Html,
809    Stream,
810    BinaryStr,
811    StreamUrlencode,
812    Other(String),
813}
814impl ContentType {
815    pub fn from(name: &str) -> Self {
816        match name {
817            "multipart/form-data" => Self::FormData,
818            "application/x-www-form-urlencoded" => Self::FormUrlencoded,
819            "application/json" => Self::Json,
820            "application/xml" | "text/xml" => Self::Xml,
821            "application/javascript" => Self::Javascript,
822            "text/html" => Self::Html,
823            "text/plain" => Self::Text,
824            "application/octet-stream" => Self::Stream,
825            _ => Self::Other(name.to_string()),
826        }
827    }
828    pub fn str(&self) -> String {
829        match self {
830            Self::FormData => "multipart/form-data",
831            Self::FormUrlencoded => "application/x-www-form-urlencoded",
832            Self::Json => "application/json",
833            Self::Xml => "application/xml",
834            Self::Javascript => "application/javascript",
835            Self::Text => "text/plain",
836            Self::Html => "text/html",
837            Self::Other(name) => name,
838            Self::Stream => "application/octet-stream",
839            Self::BinaryStr => "application/octet-stream",
840            ContentType::StreamUrlencode => "application/x-www-form-urlencoded",
841        }.to_string()
842    }
843}