br_reqwest/
lib.rs

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