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