feat: add fetch-rs
This commit is contained in:
@@ -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
1201
__network/fetch-rs/Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load Diff
10
__network/fetch-rs/Cargo.toml
Normal file
10
__network/fetch-rs/Cargo.toml
Normal 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"
|
||||||
18
__network/fetch-rs/README.md
Normal file
18
__network/fetch-rs/README.md
Normal 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
|
||||||
|
});
|
||||||
|
```
|
||||||
143
__network/fetch-rs/src/main.rs
Normal file
143
__network/fetch-rs/src/main.rs
Normal 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()),
|
||||||
|
})
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user