Skip to content

Replacing reqwest crate with async_minreq crate #128

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 14 commits into
base: master
Choose a base branch
from

Conversation

psg-19
Copy link

@psg-19 psg-19 commented Jun 8, 2025

Addresses #121

Overview

This PR replaces our reqwest-based async client with a lightweight async-minreq fork. All existing unit tests pass, but because async-minreq pulls in Tokio 1.44.0 and Tokio-rustls 0.26.2, the project’s MSRV must be bumped to 1.71.0 in CI.

Changes

  • Dependency swap

  • Async client rewrite

    • Updated async.rs to use the async-minreq API for GET/POST, timeouts, retries, and HTTPS.
  • Tests & MSRV

    • Verified unit tests pass on Rust 1.71.0.
    • CI workflows now declare rust-version = "1.71.0" to match Tokio’s MSRV.

Issues with my Implementation

  • The async-minreq crate currently fails to compile (on rust version 1.71) without my recursion fix.
  • The async_minreq crate lacks some async features.

CI tests can be viewed here, on 1.71.0 MSRV
Looking forward to feedback on these changes and any suggestions for improvement!

Checklists

All Submissions:

  • I’ve signed all my commits
  • I followed the contribution guidelines
  • I ran cargo fmt and cargo clippy before committing

New Features:

  • I’ve added tests for the new feature
  • I’ve added docs for the new feature

Copy link
Contributor

@tnull tnull left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a pretty substantial change that also breaks compatibility between rust-esplora-client versions. That said, I really like the idea of eventually getting rid of the huge dependency tree of reqwest.

Given that this PR seems to be based on a draft fork-of-a-fork branch that was edited without any code review, I think it would be very important to move the async-minreq crate to the bitcoindevkit org, have it undergo proper code review, and introduce co-maintainers before we'd want to take on this new dependency.

@notmandatory notmandatory added the summer-of-bitcoin Summer of Bitcoin Project Proposal label Jun 12, 2025
Copy link
Member

@notmandatory notmandatory left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good start and glad to see tests are passing. Per our call here are comments so far.

Cargo.toml Outdated
@@ -10,7 +10,7 @@ documentation = "https://docs.rs/esplora-client/"
description = "Bitcoin Esplora API client library. Supports plaintext, TLS and Onion servers. Blocking or async"
keywords = ["bitcoin", "esplora"]
readme = "README.md"
rust-version = "1.63.0"
rust-version = "1.70.0"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
rust-version = "1.70.0"
rust-version = "1.71.0"

Cargo.toml Outdated
@@ -22,7 +22,8 @@ bitcoin = { version = "0.32", features = ["serde", "std"], default-features = fa
hex = { version = "0.2", package = "hex-conservative" }
log = "^0.4"
minreq = { version = "2.11.0", features = ["json-using-serde"], optional = true }
reqwest = { version = "0.12", features = ["json"], default-features = false, optional = true }
async_minreq = { git = "https://github.com/psg-19/async-minreq.git", default-features = false, optional = true }
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Best to put this PR in draft mode until dependency changes like this are upstreamed and released.

Cargo.toml Outdated
@@ -22,7 +22,8 @@ bitcoin = { version = "0.32", features = ["serde", "std"], default-features = fa
hex = { version = "0.2", package = "hex-conservative" }
log = "^0.4"
minreq = { version = "2.11.0", features = ["json-using-serde"], optional = true }
reqwest = { version = "0.12", features = ["json"], default-features = false, optional = true }
async_minreq = { git = "https://github.com/psg-19/async-minreq.git", default-features = false, optional = true }
serde_json = "1.0.100"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be provided when the json-using-serde feature is enabled and not added here.

Cargo.toml Outdated
Comment on lines 48 to 49
async-https-native = ["async"]
async-https-rustls = ["async"]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are these features needed if they don't activate new features in async_minreq ?
Once you're able to confirm they work they should be tested in the CI matrix also.

Cargo.toml Outdated
async-https = ["async"]
async-https-native = ["async"]
async-https-rustls = ["async"]
async-https-rustls-manual-roots = ["async"]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If this feature isn't supported by async-minreq, two questions:

  1. can it be added as a new feature there? is it supported by minreq ?
  2. if not what projects will be impacted if it is removed?
  3. see if feature similar to blocking minreq make sense for async_minreq

src/async.rs Outdated
@@ -106,14 +77,17 @@ impl<S: Sleeper> AsyncClient<S> {
let url = format!("{}{}", self.url, path);
let response = self.get_with_retry(&url).await?;

if !response.status().is_success() {
if response.status_code > 299 {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ideally the http status code values would be consts in the async_minreq crate, but if not you should add your own const to improve readability and as a place to document what it means.

src/async.rs Outdated
@@ -284,7 +280,7 @@ impl<S: Sleeper> AsyncClient<S> {
pub async fn get_tx_no_opt(&self, txid: &Txid) -> Result<Transaction, Error> {
match self.get_tx(txid).await {
Ok(Some(tx)) => Ok(tx),
Ok(None) => Err(Error::TransactionNotFound(*txid)),
Ok(None) => Err(Error::TransactionNotFound(*txid)), //look into
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit, can remove comment.

src/async.rs Outdated
/// Sends a GET request to the given `url`, retrying failed attempts
/// for retryable error codes until max retries hit.
async fn get_with_retry(&self, url: &str) -> Result<Response, Error> {
async fn get_with_retry(&self, url: &str) -> Result<async_minreq::Response, Error> {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If possible should just be Response and have a use async_minreq::Response above.

@@ -478,8 +474,8 @@ impl<S: Sleeper> AsyncClient<S> {
}
}

fn is_status_retryable(status: reqwest::StatusCode) -> bool {
RETRYABLE_ERROR_CODES.contains(&status.as_u16())
fn is_status_retryable(status: i32) -> bool {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If async_minreq project is open to it would be nice to have a similar StatusCode enum in that project to use here and also instead of your 299 value above.

@notmandatory
Copy link
Member

notmandatory commented Jun 12, 2025

@tnull thanks for taking a look, @psg-19 is my summer of bitcoin mentee. I agree this is a big change but as you pointed out the goal is to reduce the dependency tree. I and @psg-19 will work with @BEULAHEVANJALIN on async_minreq to upstream changes we need and do any other work to get that crate ready for production use.

@psg-19 psg-19 marked this pull request as draft June 12, 2025 16:56
@coveralls
Copy link

Pull Request Test Coverage Report for Build 15519708808

Details

  • 33 of 54 (61.11%) changed or added relevant lines in 1 file are covered.
  • No unchanged relevant lines lost coverage.
  • Overall coverage decreased (-0.6%) to 87.583%

Changes Missing Coverage Covered Lines Changed/Added Lines %
src/async.rs 33 54 61.11%
Totals Coverage Status
Change from base Build 14597440477: -0.6%
Covered Lines: 1058
Relevant Lines: 1208

💛 - Coveralls

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
summer-of-bitcoin Summer of Bitcoin Project Proposal
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants