feat: add fetch-rs

This commit is contained in:
2023-06-04 13:54:54 +08:00
parent 2b71b456a3
commit c259d6c102
5 changed files with 1374 additions and 1 deletions

View File

@@ -142,6 +142,7 @@ Project or files:
├── __network ├── __network
│   ├── async-speed-limit │   ├── async-speed-limit
│   ├── dingo │   ├── dingo
│   ├── fetch-rs
│   ├── ip │   ├── ip
│   ├── ipnet-demo │   ├── ipnet-demo
│   ├── iptables │   ├── iptables
@@ -274,6 +275,6 @@ Project or files:
├── vec.rs ├── vec.rs
└── while.rs └── while.rs
243 directories, 40 files 244 directories, 40 files
``` ```

1201
__network/fetch-rs/Cargo.lock generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,10 @@
[package]
name = "fetch-rs"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
reqwest = { version = "0.11.18", features = ["blocking"] }
rust_util = "0.6.41"

View File

@@ -0,0 +1,18 @@
```js
const response = fetch("http://example.com/movies.json")
const responseJson = response.json();
```
```js
const response = fetch(url, {
method: "POST", // *GET, POST, PUT, DELETE, etc.
headers: {
"Content-Type": "application/json",
// 'Content-Type': 'application/x-www-form-urlencoded',
},
redirect: "follow", // manual, *follow, error
body: JSON.stringify(data), // body data type must match "Content-Type" header
});
```

View File

@@ -0,0 +1,143 @@
use std::str::FromStr;
use std::time::Duration;
use reqwest::{Method, Url};
use reqwest::blocking::{Body, Client, Request};
use reqwest::header::{HeaderName, HeaderValue};
use rust_util::{simple_error, XResult};
fn main() -> XResult<()> {
let response = fetch(
"https://hatter.ink/util/print_request.action",
&FetchOptions {
method: FetchMethod::Post,
headers: Some(vec![
FetchHeader {
key: "content-type".to_string(),
value: "application/json".to_string(),
}
]),
body: Some("{}".as_bytes().to_vec()),
..Default::default()
})?;
println!("Status: {}", response.status);
response.headers.iter().for_each(|h| {
println!("- {}: {}", h.key, h.value);
});
println!("----- BEGIN RESPONSE BODY -----\n{}\n----- END RESPONSE BODY -----",
String::from_utf8(response.body().clone().unwrap().to_vec())?);
Ok(())
}
#[derive(Clone, Copy, Debug)]
pub enum FetchMethod {
Get,
Post,
Put,
}
impl FetchMethod {
pub fn require_body(&self) -> bool {
match self {
FetchMethod::Get => false,
FetchMethod::Post | FetchMethod::Put => true,
}
}
}
impl Default for FetchMethod {
fn default() -> Self {
Self::Get
}
}
#[derive(Clone, Debug)]
pub struct FetchHeader {
pub key: String,
pub value: String,
}
#[derive(Clone, Debug, Default)]
pub struct FetchOptions {
pub method: FetchMethod,
pub headers: Option<Vec<FetchHeader>>,
pub body: Option<Vec<u8>>,
// connect timeout or read timeout?
pub timeout: Option<Duration>,
// proxy supports
// hsts supports
}
impl FetchOptions {}
#[derive(Clone, Debug)]
pub struct FetchResponse {
status: u16,
headers: Vec<FetchHeader>,
body: Option<Vec<u8>>,
}
impl FetchResponse {
pub fn body(&self) -> &Option<Vec<u8>> {
&self.body
}
pub fn headers(&self) /* TODO */ {
todo!()
}
pub fn ok(&self) -> bool {
self.status == 200
}
pub fn status(&self) -> u16 {
self.status
}
}
pub fn fetch(url: &str, option: &FetchOptions) -> XResult<FetchResponse> {
let client = Client::builder()
// .proxy()
.build()?;
let method = match option.method {
FetchMethod::Get => Method::GET,
FetchMethod::Post => Method::POST,
FetchMethod::Put => Method::PUT,
};
let mut request = Request::new(method, Url::from_str(url)?);
if let Some(timeout) = &option.timeout {
let _ = request.timeout_mut().insert(timeout.to_owned());
}
if let Some(headers) = &option.headers {
let headers_mut = request.headers_mut();
for header in headers {
let header_name = HeaderName::from_str(&header.key)?;
let header_value = HeaderValue::from_str(&header.value)?;
headers_mut.insert(header_name, header_value);
}
}
if option.method.require_body() && option.body.is_none() {
return simple_error!("Require body.");
}
if let Some(body) = &option.body {
let _ = request.body_mut().insert(Body::from(body.to_vec()));
}
let response = client.execute(request)?;
let response_status = response.status();
let mut response_headers = vec![];
let response_header_map = response.headers();
for (response_header_name, response_header_vlaue) in response_header_map {
let key = response_header_name.to_string();
let value = response_header_vlaue.to_str()?.to_string();
response_headers.push(FetchHeader { key, value });
}
let bytes = response.bytes()?;
Ok(FetchResponse {
status: response_status.as_u16(),
headers: response_headers,
body: Some(bytes.to_vec()),
})
}