br_reqwest/
lib.rs

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