m/cup
1
0
mirror of https://github.com/sergi0g/cup.git synced 2025-11-15 08:33:49 -05:00
Files
cup/src/http.rs
2025-05-10 20:48:22 +03:00

137 lines
4.6 KiB
Rust

use std::fmt::Display;
use reqwest::Response;
use reqwest_middleware::{ClientBuilder, ClientWithMiddleware};
use reqwest_retry::{policies::ExponentialBackoff, RetryTransientMiddleware};
use crate::{error, Context};
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,
ctx: Context,
}
impl Client {
pub fn new(ctx: &Context) -> Self {
Self {
inner: ClientBuilder::new(reqwest::Client::new())
.with(RetryTransientMiddleware::new_with_policy(
ExponentialBackoff::builder().build_with_max_retries(3),
))
.build(),
ctx: ctx.clone(),
}
}
async fn request(
&self,
url: &str,
method: RequestMethod,
headers: &[(&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);
self.ctx.logger.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);
self.ctx.logger.warn(&message);
Err(message)
}
} else if status == 502 {
let message = format!("{} {}: The registry is currently unavailabile (returned status code 502).", method, url);
self.ctx.logger.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);
self.ctx.logger.warn(&message);
Err(message)
} else if error.is_timeout() {
let message = format!("{} {}: Connection timed out!", method, url);
self.ctx.logger.warn(&message);
Err(message)
} else if error.is_middleware() {
let message = format!("{} {}: Connection failed after 3 retries!", method, url);
self.ctx.logger.warn(&message);
Err(message)
} else {
error!(
"{} {}: Unexpected error: {}",
method,
url,
error.to_string()
)
}
}
}
}
pub async fn get(
&self,
url: &str,
headers: &[(&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: &[(&str, Option<&str>)],
) -> Result<Response, String> {
self.request(url, RequestMethod::HEAD, headers, false).await
}
}