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

@@ -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()),
})
}