hopr_chain_rpc/
transport.rs1use alloy::transports::{http::Http, utils::guess_local_url};
2use async_trait::async_trait;
3pub use reqwest::Client as ReqwestClient;
4use tracing::{debug, trace};
5use url::Url;
6
7use crate::errors::HttpRequestError;
8
9#[async_trait]
11pub trait HttpRequestor: std::fmt::Debug + Send + Sync {
12 async fn http_get(&self, url: &str) -> std::result::Result<Box<[u8]>, HttpRequestError>;
14}
15
16#[derive(Clone, Debug)]
18pub struct HttpWrapper<T> {
19 client: T,
20 url: Url,
21}
22
23impl<T> HttpWrapper<T> {
24 pub const fn with_client(client: T, url: Url) -> Self {
26 Self { client, url }
27 }
28
29 pub fn set_url(&mut self, url: Url) {
31 self.url = url;
32 }
33
34 pub fn set_client(&mut self, client: T) {
36 self.client = client;
37 }
38
39 pub fn guess_local(&self) -> bool {
45 guess_local_url(&self.url)
46 }
47
48 pub const fn client(&self) -> &T {
50 &self.client
51 }
52
53 pub fn url(&self) -> &str {
55 self.url.as_ref()
56 }
57}
58
59impl<T: Clone> From<Http<T>> for HttpWrapper<T> {
60 fn from(value: Http<T>) -> Self {
61 Self {
62 client: value.client().clone(),
63 url: Url::parse(value.url()).unwrap(),
64 }
65 }
66}
67
68impl<T: Clone> From<HttpWrapper<T>> for Http<T> {
69 fn from(value: HttpWrapper<T>) -> Self {
70 Self::with_client(value.client().clone(), Url::parse(value.url()).unwrap())
71 }
72}
73
74#[async_trait]
75impl HttpRequestor for ReqwestClient {
76 #[inline]
77 async fn http_get(&self, url: &str) -> std::result::Result<Box<[u8]>, HttpRequestError> {
78 let res = self
79 .get(url)
80 .send()
81 .await
82 .map_err(|e| HttpRequestError::TransportError(e.to_string()))?;
83
84 let status = res.status();
85
86 debug!(%status, "received response from server");
87
88 let body = res
89 .bytes()
90 .await
91 .map_err(|e| HttpRequestError::UnknownError(e.to_string()))?;
92
93 debug!(bytes = body.len(), "retrieved response body. Use `trace` for full body");
94 trace!(body = %String::from_utf8_lossy(&body), "response body");
95
96 if !status.is_success() {
97 return Err(HttpRequestError::HttpError(
98 http::StatusCode::try_from(status.as_u16()).unwrap_or(http::StatusCode::INTERNAL_SERVER_ERROR),
99 ));
100 }
101
102 Ok(body.to_vec().into_boxed_slice())
103 }
104}