Skip to content

Commit a33a70a

Browse files
committed
Replacing reqwest with async-minreq
1 parent ecea653 commit a33a70a

File tree

3 files changed

+149
-83
lines changed

3 files changed

+149
-83
lines changed

Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ hex = { version = "0.2", package = "hex-conservative" }
2323
log = "^0.4"
2424
minreq = { version = "2.11.0", features = ["json-using-serde"], optional = true }
2525
reqwest = { version = "0.12", features = ["json"], default-features = false, optional = true }
26+
async_minreq = { git = "https://github.com/BEULAHEVANJALIN/async-minreq.git" }
27+
serde_json = "1.0.140"
2628

2729
# default async runtime
2830
tokio = { version = "1", features = ["time"], optional = true }

src/async.rs

Lines changed: 124 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414
use std::collections::HashMap;
1515
use std::marker::PhantomData;
1616
use std::str::FromStr;
17+
use std::time::Duration;//-----------------------added----------------
18+
use std::convert::TryInto;//-------------------added-----------------
1719

1820
use bitcoin::consensus::{deserialize, serialize, Decodable, Encodable};
1921
use bitcoin::hashes::{sha256, Hash};
@@ -26,71 +28,96 @@ use bitcoin::{
2628
#[allow(unused_imports)]
2729
use log::{debug, error, info, trace};
2830

29-
use reqwest::{header, Client, Response};
30-
31+
// use reqwest::{header, Client, Response};
32+
use async_minreq::{Method, Request}; //-----------------------added-------------------
3133
use crate::api::AddressStats;
3234
use crate::{
3335
BlockStatus, BlockSummary, Builder, Error, MerkleProof, OutputStatus, Tx, TxStatus,
3436
BASE_BACKOFF_MILLIS, RETRYABLE_ERROR_CODES,
3537
};
3638

39+
// #[derive(Debug, Clone)]
40+
// pub struct AsyncClient<S = DefaultSleeper> {
41+
// /// The URL of the Esplora Server.
42+
// url: String,
43+
// /// The inner [`reqwest::Client`] to make HTTP requests.
44+
// client: Client,
45+
// /// Number of times to retry a request
46+
// max_retries: usize,
47+
48+
// /// Marker for the type of sleeper used
49+
// marker: PhantomData<S>,
50+
// }
51+
3752
#[derive(Debug, Clone)]
3853
pub struct AsyncClient<S = DefaultSleeper> {
39-
/// The URL of the Esplora Server.
54+
/// The base URL of the Esplora server.
4055
url: String,
41-
/// The inner [`reqwest::Client`] to make HTTP requests.
42-
client: Client,
43-
/// Number of times to retry a request
56+
/// Number of times to retry a request.
4457
max_retries: usize,
45-
46-
/// Marker for the type of sleeper used
58+
/// Default headers (applied to every request).
59+
headers: HashMap<String, String>,
60+
/// Marker for the sleeper.
4761
marker: PhantomData<S>,
4862
}
4963

5064
impl<S: Sleeper> AsyncClient<S> {
5165
/// Build an async client from a builder
5266
pub fn from_builder(builder: Builder) -> Result<Self, Error> {
53-
let mut client_builder = Client::builder();
54-
55-
#[cfg(not(target_arch = "wasm32"))]
56-
if let Some(proxy) = &builder.proxy {
57-
client_builder = client_builder.proxy(reqwest::Proxy::all(proxy)?);
58-
}
5967

60-
#[cfg(not(target_arch = "wasm32"))]
61-
if let Some(timeout) = builder.timeout {
62-
client_builder = client_builder.timeout(core::time::Duration::from_secs(timeout));
63-
}
64-
65-
if !builder.headers.is_empty() {
66-
let mut headers = header::HeaderMap::new();
67-
for (k, v) in builder.headers {
68-
let header_name = header::HeaderName::from_lowercase(k.to_lowercase().as_bytes())
69-
.map_err(|_| Error::InvalidHttpHeaderName(k))?;
70-
let header_value = header::HeaderValue::from_str(&v)
71-
.map_err(|_| Error::InvalidHttpHeaderValue(v))?;
72-
headers.insert(header_name, header_value);
73-
}
74-
client_builder = client_builder.default_headers(headers);
75-
}
76-
77-
Ok(AsyncClient {
68+
//----------------------------------------not needed since no client struct in async minreq------------------------------------
69+
// let mut client_builder = Client::builder();
70+
71+
// #[cfg(not(target_arch = "wasm32"))]
72+
// if let Some(proxy) = &builder.proxy {
73+
// client_builder = client_builder.proxy(reqwest::Proxy::all(proxy)?);
74+
// }
75+
76+
// #[cfg(not(target_arch = "wasm32"))]
77+
// if let Some(timeout) = builder.timeout {
78+
// client_builder = client_builder.timeout(core::time::Duration::from_secs(timeout));
79+
// }
80+
81+
// if !builder.headers.is_empty() {
82+
// let mut headers = header::HeaderMap::new();
83+
// for (k, v) in builder.headers {
84+
// let header_name = header::HeaderName::from_lowercase(k.to_lowercase().as_bytes())
85+
// .map_err(|_| Error::InvalidHttpHeaderName(k))?;
86+
// let header_value = header::HeaderValue::from_str(&v)
87+
// .map_err(|_| Error::InvalidHttpHeaderValue(v))?;
88+
// headers.insert(header_name, header_value);
89+
// }
90+
// client_builder = client_builder.default_headers(headers);
91+
// }
92+
93+
// Ok(AsyncClient {
94+
// url: builder.base_url,
95+
// client: client_builder.build()?,
96+
// max_retries: builder.max_retries,
97+
// marker: PhantomData,
98+
// })
99+
100+
//--------------------------------------------------------------------------------------
101+
Ok(AsyncClient {
78102
url: builder.base_url,
79-
client: client_builder.build()?,
80103
max_retries: builder.max_retries,
104+
headers: builder.headers,
81105
marker: PhantomData,
82106
})
83-
}
84107

85-
pub fn from_client(url: String, client: Client) -> Self {
86-
AsyncClient {
87-
url,
88-
client,
89-
max_retries: crate::DEFAULT_MAX_RETRIES,
90-
marker: PhantomData,
91-
}
108+
92109
}
93110

111+
//----------------------------------------not needed since no client struct in async minreq------------------------------------
112+
// pub fn from_client(url: String, client: Client) -> Self {
113+
// AsyncClient {
114+
// url,
115+
// client,
116+
// max_retries: crate::DEFAULT_MAX_RETRIES,
117+
// marker: PhantomData,
118+
// }
119+
// }
120+
//-----------------------------------------------------------------------------------------------
94121
/// Make an HTTP GET request to given URL, deserializing to any `T` that
95122
/// implement [`bitcoin::consensus::Decodable`].
96123
///
@@ -106,14 +133,14 @@ impl<S: Sleeper> AsyncClient<S> {
106133
let url = format!("{}{}", self.url, path);
107134
let response = self.get_with_retry(&url).await?;
108135

109-
if !response.status().is_success() {
136+
if !(response.status_code==200) {
110137
return Err(Error::HttpResponse {
111-
status: response.status().as_u16(),
112-
message: response.text().await?,
138+
status: response.status_code as u16,
139+
message: response.as_str().unwrap().to_string(),
113140
});
114141
}
115142

116-
Ok(deserialize::<T>(&response.bytes().await?)?)
143+
Ok(deserialize::<T>(&response.as_bytes())?)
117144
}
118145

119146
/// Make an HTTP GET request to given URL, deserializing to `Option<T>`.
@@ -146,14 +173,14 @@ impl<S: Sleeper> AsyncClient<S> {
146173
let url = format!("{}{}", self.url, path);
147174
let response = self.get_with_retry(&url).await?;
148175

149-
if !response.status().is_success() {
176+
if !(response.status_code==200) {
150177
return Err(Error::HttpResponse {
151-
status: response.status().as_u16(),
152-
message: response.text().await?,
178+
status: response.status_code as u16,
179+
message: response.as_str().unwrap().to_string(),
153180
});
154181
}
155182

156-
response.json::<T>().await.map_err(Error::Reqwest)
183+
serde_json::from_str(&response.as_str().unwrap().to_string()).map_err(Error::Json)
157184
}
158185

159186
/// Make an HTTP GET request to given URL, deserializing to `Option<T>`.
@@ -188,14 +215,14 @@ impl<S: Sleeper> AsyncClient<S> {
188215
let url = format!("{}{}", self.url, path);
189216
let response = self.get_with_retry(&url).await?;
190217

191-
if !response.status().is_success() {
218+
if !(response.status_code==200) {
192219
return Err(Error::HttpResponse {
193-
status: response.status().as_u16(),
194-
message: response.text().await?,
220+
status: response.status_code as u16,
221+
message:response.as_str().unwrap().to_string(),
195222
});
196223
}
197224

198-
let hex_str = response.text().await?;
225+
let hex_str =response.as_str().unwrap().to_string();
199226
Ok(deserialize(&Vec::from_hex(&hex_str)?)?)
200227
}
201228

@@ -225,14 +252,14 @@ impl<S: Sleeper> AsyncClient<S> {
225252
let url = format!("{}{}", self.url, path);
226253
let response = self.get_with_retry(&url).await?;
227254

228-
if !response.status().is_success() {
255+
if !(response.status_code==200) {
229256
return Err(Error::HttpResponse {
230-
status: response.status().as_u16(),
231-
message: response.text().await?,
257+
status: response.status_code as u16,
258+
message:response.as_str().unwrap().to_string(),
232259
});
233260
}
234-
235-
Ok(response.text().await?)
261+
// let x=response.as_str().unwrap().to_string();
262+
Ok(response.as_str().unwrap().to_string())
236263
}
237264

238265
/// Make an HTTP GET request to given URL, deserializing to `Option<T>`.
@@ -263,15 +290,12 @@ impl<S: Sleeper> AsyncClient<S> {
263290
let url = format!("{}{}", self.url, path);
264291
let body = serialize::<T>(&body).to_lower_hex_string();
265292

266-
let response = self.client.post(url).body(body).send().await?;
267-
268-
if !response.status().is_success() {
269-
return Err(Error::HttpResponse {
270-
status: response.status().as_u16(),
271-
message: response.text().await?,
272-
});
293+
let mut request = Request::new(Method::Post, &url).with_body(body);
294+
for (key, value) in &self.headers {
295+
request = request.with_header(key, value);
273296
}
274-
297+
298+
let _ = request.send().await.map_err(Error::AsyncMinreq)?;
275299
Ok(())
276300
}
277301

@@ -284,7 +308,7 @@ impl<S: Sleeper> AsyncClient<S> {
284308
pub async fn get_tx_no_opt(&self, txid: &Txid) -> Result<Transaction, Error> {
285309
match self.get_tx(txid).await {
286310
Ok(Some(tx)) => Ok(tx),
287-
Ok(None) => Err(Error::TransactionNotFound(*txid)),
311+
Ok(None) => Err(Error::TransactionNotFound(*txid)), //look into
288312
Err(e) => Err(e),
289313
}
290314
}
@@ -455,32 +479,52 @@ impl<S: Sleeper> AsyncClient<S> {
455479
}
456480

457481
/// Get the underlying [`Client`].
458-
pub fn client(&self) -> &Client {
459-
&self.client
460-
}
482+
// pub fn client(&self) -> &Client {
483+
// &self.client
484+
// }
461485

462486
/// Sends a GET request to the given `url`, retrying failed attempts
463487
/// for retryable error codes until max retries hit.
464-
async fn get_with_retry(&self, url: &str) -> Result<Response, Error> {
488+
async fn get_with_retry(&self, url: &str) -> Result<async_minreq::Response, Error> {
465489
let mut delay = BASE_BACKOFF_MILLIS;
466490
let mut attempts = 0;
467491

468492
loop {
469-
match self.client.get(url).send().await? {
470-
resp if attempts < self.max_retries && is_status_retryable(resp.status()) => {
471-
S::sleep(delay).await;
472-
attempts += 1;
473-
delay *= 2;
493+
let mut request = Request::new(Method::Get, url);
494+
// Apply headers from the builder.
495+
for (key, value) in &self.headers {
496+
request = request.with_header(key, value);
497+
}
498+
// request.
499+
500+
let res = request.send().await.map_err(Error::AsyncMinreq);
501+
502+
match res {
503+
Ok(body) => {
504+
505+
506+
return Ok(body);
507+
508+
},
509+
Err(e) => {
510+
// Here you might inspect the error (if possible) to decide whether to retry.
511+
// For simplicity, we retry on any error until max_retries is reached.
512+
if attempts < self.max_retries {
513+
S::sleep(delay).await;
514+
attempts += 1;
515+
delay *= 2;
516+
continue;
517+
}
518+
return Err(e);
474519
}
475-
resp => return Ok(resp),
476520
}
477521
}
478522
}
479523
}
480524

481-
fn is_status_retryable(status: reqwest::StatusCode) -> bool {
482-
RETRYABLE_ERROR_CODES.contains(&status.as_u16())
483-
}
525+
// fn is_status_retryable(status: reqwest::StatusCode) -> bool {
526+
// RETRYABLE_ERROR_CODES.contains(&status.as_u16())
527+
// }
484528

485529
pub trait Sleeper: 'static {
486530
type Sleep: std::future::Future<Output = ()>;

src/lib.rs

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -201,8 +201,14 @@ pub enum Error {
201201
#[cfg(feature = "blocking")]
202202
Minreq(::minreq::Error),
203203
/// Error during reqwest HTTP request
204-
#[cfg(feature = "async")]
205-
Reqwest(::reqwest::Error),
204+
//-----------------------------------------------changed------------------------------
205+
// #[cfg(feature = "async")]
206+
// Reqwest(::reqwest::Error),
207+
#[cfg(feature = "async")]
208+
AsyncMinreq(async_minreq::Error),
209+
Json(serde_json::Error),
210+
211+
206212
/// HTTP response error
207213
HttpResponse { status: u16, message: String },
208214
/// Invalid number returned
@@ -251,8 +257,22 @@ macro_rules! impl_error {
251257
impl std::error::Error for Error {}
252258
#[cfg(feature = "blocking")]
253259
impl_error!(::minreq::Error, Minreq, Error);
260+
261+
//---------------------------------------------changed------------------------------
254262
#[cfg(feature = "async")]
255-
impl_error!(::reqwest::Error, Reqwest, Error);
263+
// impl_error!(::reqwest::Error, Reqwest, Error);
264+
impl std::convert::From<async_minreq::Error> for Error {
265+
fn from(err: async_minreq::Error) -> Self {
266+
Error::AsyncMinreq(err)
267+
}
268+
}
269+
270+
impl std::convert::From<serde_json::Error> for Error {
271+
fn from(err: serde_json::Error) -> Self {
272+
Error::Json(err)
273+
}
274+
}
275+
// impl_error!(::reqwest::Error, Reqwest, Error);
256276
impl_error!(std::num::ParseIntError, Parsing, Error);
257277
impl_error!(bitcoin::consensus::encode::Error, BitcoinEncoding, Error);
258278
impl_error!(bitcoin::hex::HexToArrayError, HexToArray, Error);

0 commit comments

Comments
 (0)