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        if self.body.content.is_empty() { 
336            Ok(object! {})
337        } else {
338            match json::parse(self.body.content.to_string().as_str()) {
339                Ok(e) => Ok(e),
340                Err(e) => Err(e.to_string())
341            }
342        }
343    }
344    pub fn body(&self) -> JsonValue {
345        self.body.content.clone()
346    }
347}
348
349#[derive(Clone, Debug)]
350pub enum Method {
351    GET,
352    POST,
353    OPTIONS,
354    PATCH,
355    HEAD,
356    DELETE,
357    TRACE,
358    PUT,
359    NONE,
360}
361
362impl Method {
363    pub fn to_str(&self) -> String {
364        match self {
365            Method::GET => "GET",
366            Method::POST => "POST",
367            Method::OPTIONS => "OPTIONS",
368            Method::PATCH => "PATCH",
369            Method::HEAD => "HEAD",
370            Method::DELETE => "DELETE",
371            Method::TRACE => "TRACE",
372            Method::PUT => "PUT",
373            Method::NONE => "NONE",
374        }.to_string()
375    }
376    pub fn from(name: &str) -> Self {
377        match name.to_lowercase().as_str() {
378            "post" => Self::POST,
379            "get" => Self::GET,
380            "head" => Self::HEAD,
381            "put" => Self::PUT,
382            "delete" => Self::DELETE,
383            "options" => Self::OPTIONS,
384            "patch" => Self::PATCH,
385            "trace" => Self::TRACE,
386            _ => Self::NONE,
387        }
388    }
389}
390#[derive(Clone, Debug)]
391pub enum Version {
392    Http09,
393    Http10,
394    Http11,
395    H2,
396    H3,
397    None,
398}
399
400impl Version {
401    pub fn str(&mut self) -> String {
402        match self {
403            Version::Http09 => "HTTP/0.9",
404            Version::Http10 => "HTTP/1.0",
405            Version::Http11 => "HTTP/1.1",
406            Version::H2 => "HTTP/2.0",
407            Version::H3 => "HTTP/3.0",
408            Version::None => "",
409        }.to_string()
410    }
411    pub fn from(name: &str) -> Version {
412        match name {
413            "HTTP/0.9" => Self::Http09,
414            "HTTP/1.0" => Self::Http10,
415            "HTTP/1.1" => Self::Http11,
416            "HTTP/2.0" => Self::H2,
417            "HTTP/3.0" => Self::H3,
418            _ => Self::None,
419        }
420    }
421    pub fn set_version(name: &str) -> Version {
422        match name {
423            "0.9" => Self::Http09,
424            "1.0" => Self::Http10,
425            "1.1" => Self::Http11,
426            "2.0" => Self::H2,
427            "3.0" => Self::H3,
428            _ => Self::None,
429        }
430    }
431}
432
433
434#[derive(Clone, Debug)]
435pub enum FormData {
436    /// KEY
437    /// value
438    /// 文本类型
439    Text(String, JsonValue, String),
440    /// 文件名
441    /// 文件路径
442    /// 文件类型
443    File(String, String, String),
444    None,
445}
446
447
448#[derive(Debug, Clone)]
449pub struct Body {
450    pub content_type: ContentType,
451    pub boundary: String,
452    pub content_length: usize,
453    pub content: JsonValue,
454}
455impl Body {
456    /// 解码
457    pub fn decode(input: &str) -> Result<String, String> {
458        let mut decoded = String::new();
459        let bytes = input.as_bytes();
460        let mut i = 0;
461
462        while i < bytes.len() {
463            if bytes[i] == b'%' {
464                if i + 2 >= bytes.len() {
465                    return Err("Incomplete percent-encoding".into());
466                }
467                let hex = &input[i + 1..i + 3];
468                match u8::from_str_radix(hex, 16) {
469                    Ok(byte) => decoded.push(byte as char),
470                    Err(_) => return Err(format!("Invalid percent-encoding: %{}", hex)),
471                }
472                i += 3;
473            } else if bytes[i] == b'+' {
474                decoded.push(' ');
475                i += 1;
476            } else {
477                decoded.push(bytes[i] as char);
478                i += 1;
479            }
480        }
481
482        Ok(decoded)
483    }
484    pub fn set_content(&mut self, data: Vec<u8>) {
485        match self.content_type.clone() {
486            ContentType::FormData => {
487                let mut fields = object! {};
488                let boundary_marker = format!("--{}", self.boundary);
489                let text = unsafe { String::from_utf8_unchecked(data) };
490                let parts = text.split(&boundary_marker).collect::<Vec<&str>>();
491                for part in parts {
492                    let part = part.trim();
493                    if part.is_empty() || part == "--" {
494                        continue; // 跳过无效部分
495                    }
496
497                    let mut headers_and_body = part.splitn(2, "\r\n\r\n");
498                    if let (Some(headers), Some(body)) = (headers_and_body.next(), headers_and_body.next())
499                    {
500                        // 解析头部,查找 Content-Disposition
501                        let headers = headers.split("\r\n");
502
503                        let mut field_name = "";
504                        let mut filename = "";
505                        let mut content_type = ContentType::Text;
506
507                        for header in headers {
508                            if header.to_lowercase().starts_with("content-disposition:") {
509                                match header.find("filename=\"") {
510                                    None => {}
511                                    Some(filename_start) => {
512                                        let filename_len = filename_start + 10;
513                                        let filename_end = header[filename_len..].find('"').unwrap() + filename_len;
514                                        filename = &header[filename_len..filename_end];
515                                    }
516                                }
517
518                                match header.find("name=\"") {
519                                    None => {}
520                                    Some(name_start) => {
521                                        let name_start = name_start + 6;
522                                        let name_end = header[name_start..].find('"').unwrap() + name_start;
523                                        field_name = &header[name_start..name_end];
524                                    }
525                                }
526                            }
527                            if header.to_lowercase().starts_with("content-type:") {
528                                content_type = ContentType::from(
529                                    header.to_lowercase().trim_start_matches("content-type:").trim(),
530                                );
531                            }
532                        }
533
534                        if filename.is_empty() {
535                            fields[field_name.to_string()] = JsonValue::from(body);
536                        } else {
537                            // 获取系统临时目录
538                            let mut temp_dir = env::temp_dir();
539                            // 构造临时文件的完整路径
540                            temp_dir.push(filename);
541                            // 打开(创建)临时文件
542                            let mut temp_file = match fs::File::create(&temp_dir) {
543                                Ok(e) => e,
544                                Err(_) => continue,
545                            };
546                            if temp_file.write(body.as_bytes()).is_ok() {
547                                if fields[field_name.to_string()].is_empty() {
548                                    fields[field_name.to_string()] = array![]
549                                }
550
551                                let extension = Path::new(filename).extension() // 可能返回 None
552                                                                   .and_then(|ext| ext.to_str()); // 转换为 &str
553
554                                let suffix = extension.unwrap_or("txt");
555
556                                fields[field_name.to_string()].push(object! {
557                                        //id:sha_256(body.as_bytes().to_vec()),
558                                        name:filename,
559                                        suffix:suffix,
560                                        size:body.len(),
561                                        "type":content_type.str(),
562                                        file:temp_dir.to_str()
563                                    }).unwrap();
564                            };
565                        }
566                    }
567                }
568                self.content = fields;
569            }
570            ContentType::FormUrlencoded => {
571                let text = unsafe { String::from_utf8_unchecked(data) };
572                let params = text.split("&").collect::<Vec<&str>>();
573                let mut list = object! {};
574                for param in params.iter() {
575                    let t = param.split("=").collect::<Vec<&str>>().iter().map(|&x| Body::decode(x).unwrap_or(x.to_string())).collect::<Vec<String>>();
576                    list[t[0].to_string()] = t[1].clone().into();
577                }
578                self.content = list;
579            }
580            ContentType::Json => {
581                let text = unsafe { String::from_utf8_unchecked(data) };
582                self.content = json::parse(text.as_str()).unwrap_or(object! {});
583            }
584            ContentType::Xml => {
585                let text = unsafe { String::from_utf8_unchecked(data) };
586                self.content = text.into();
587            }
588            ContentType::Html | ContentType::Text | ContentType::Javascript => {
589                let text = unsafe { String::from_utf8_unchecked(data) };
590                self.content = text.into();
591            }
592            ContentType::Other(name) => match name.as_str() {
593                "application/pdf" => {
594                    let text = unsafe { String::from_utf8_unchecked(data) };
595                    self.content = text.into();
596                }
597                _ => {
598                    let text = unsafe { String::from_utf8_unchecked(data) };
599                    self.content = text.into();
600                }
601            },
602            ContentType::Stream => {}
603        }
604    }
605}
606
607impl Default for Body {
608    fn default() -> Self {
609        Self {
610            content_type: ContentType::Other("text/plain".to_string()),
611            boundary: "".to_string(),
612            content_length: 0,
613            content: object! {},
614        }
615    }
616}
617
618/// 内容类型
619#[derive(Debug, Clone)]
620pub enum ContentType {
621    FormData,
622    FormUrlencoded,
623    Json,
624    Xml,
625    Javascript,
626    Text,
627    Html,
628    Stream,
629    Other(String),
630}
631impl ContentType {
632    pub fn from(name: &str) -> Self {
633        match name {
634            "multipart/form-data" => Self::FormData,
635            "application/x-www-form-urlencoded" => Self::FormUrlencoded,
636            "application/json" => Self::Json,
637            "application/xml" | "text/xml" => Self::Xml,
638            "application/javascript" => Self::Javascript,
639            "text/html" => Self::Html,
640            "text/plain" => Self::Text,
641            "application/octet-stream" => Self::Stream,
642            _ => Self::Other(name.to_string()),
643        }
644    }
645    pub fn str(&self) -> String {
646        match self {
647            Self::FormData => "multipart/form-data",
648            Self::FormUrlencoded => "application/x-www-form-urlencoded",
649            Self::Json => "application/json",
650            Self::Xml => "application/xml",
651            Self::Javascript => "application/javascript",
652            Self::Text => "text/plain",
653            Self::Html => "text/html",
654            Self::Other(name) => name,
655            Self::Stream => "application/octet-stream",
656        }.to_string()
657    }
658}