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