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