m/cup
1
0
mirror of https://github.com/sergi0g/cup.git synced 2025-11-17 09:33:38 -05:00
Files
cup/src/http.rs

132 lines
4.1 KiB
Rust

use std::fmt::Display;
use reqwest::Response;
use reqwest_middleware::{ClientBuilder, ClientWithMiddleware};
use reqwest_retry::{policies::ExponentialBackoff, RetryTransientMiddleware};
use crate::{error, warn};
pub enum RequestMethod {
GET,
HEAD,
}
impl Display for RequestMethod {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(match self {
RequestMethod::GET => "GET",
RequestMethod::HEAD => "HEAD",
})
}
}
/// A struct for handling HTTP requests. Takes care of the repetitive work of checking for errors, etc and exposes a simple interface
pub struct Client {
inner: ClientWithMiddleware,
}
impl Client {
pub fn new() -> Self {
Self {
inner: ClientBuilder::new(reqwest::Client::new())
.with(RetryTransientMiddleware::new_with_policy(
ExponentialBackoff::builder().build_with_max_retries(3),
))
.build(),
}
}
async fn request(
&self,
url: &str,
method: RequestMethod,
headers: Vec<(&str, Option<&str>)>,
ignore_401: bool,
) -> Result<Response, String> {
let mut request = match method {
RequestMethod::GET => self.inner.get(url),
RequestMethod::HEAD => self.inner.head(url),
};
for (name, value) in headers {
if let Some(v) = value {
request = request.header(name, v)
}
}
match request.send().await {
Ok(response) => {
let status = response.status();
if status == 404 {
let message = format!("{} {}: Not found!", method, url);
warn!("{}", message);
Err(message)
} else if status == 401 {
if ignore_401 {
Ok(response)
} else {
let message = format!("{} {}: Unauthorized! Please configure authentication for this registry or if you have already done so, please make sure it is correct.", method, url);
warn!("{}", message);
Err(message)
}
} else if status.as_u16() <= 400 {
Ok(response)
} else {
match method {
RequestMethod::GET => error!(
"{} {}: Unexpected error: {}",
method,
url,
response.text().await.unwrap()
),
RequestMethod::HEAD => error!(
"{} {}: Unexpected error: Recieved status code {}",
method, url, status
),
}
}
}
Err(error) => {
if error.is_connect() {
let message = format!("{} {}: Connection failed!", method, url);
warn!("{}", message);
Err(message)
} else if error.is_timeout() {
let message = format!("{} {}: Connection timed out!", method, url);
warn!("{}", message);
Err(message)
} else {
error!(
"{} {}: Unexpected error: {}",
method,
url,
error.to_string()
)
}
}
}
}
pub async fn get(
&self,
url: &str,
headers: Vec<(&str, Option<&str>)>,
ignore_401: bool,
) -> Result<Response, String> {
self.request(url, RequestMethod::GET, headers, ignore_401)
.await
}
pub async fn head(
&self,
url: &str,
headers: Vec<(&str, Option<&str>)>,
) -> Result<Response, String> {
self.request(url, RequestMethod::HEAD, headers, false).await
}
}
impl Default for Client {
fn default() -> Self {
Self::new()
}
}