Satisfy futures::AsyncRead for a reqwest response body?

I have a reqwest::Response whose body I want to read into a buffer asynchronously inside a futures::executor::block_on, however the compiler complains about the following:

error[E0599]: the method `read` exists for struct `reqwest::Response`, but its trait bounds were not satisfied
   --> src/get.rs:194:51
    |
194 |                 let bytes_read = async { response.read(&mut buffer).await.map_err(Error::Read) };
    |                                                   ^^^^ method cannot be called on `reqwest::Response` due to unsatisfied trait bounds
    |
   ::: /home/user/.cargo/registry/src/github.com-1ecc6299db9ec823/reqwest-0.11.10/src/async_impl/response.rs:26:1
    |
26  | pub struct Response {
    | -------------------
    | |
    | doesn't satisfy `reqwest::Response: futures::AsyncReadExt`
    | doesn't satisfy `reqwest::Response: futures::AsyncRead`
    |
    = note: the following trait bounds were not satisfied:
            `reqwest::Response: futures::AsyncRead`
            which is required by `reqwest::Response: futures::AsyncReadExt`

For more information about this error, try `rustc --explain E0599`.
error: could not compile `async-fetcher` due to previous error

Neither converting it to a string or bytes seem to help in this case.

Original code here, although I am trying to use reqwest for the network operations instead of isahc.

This can be done with a combination of Response::bytes_stream() and TryStreamExt::into_async_read():

use futures::{AsyncReadExt, TryStreamExt};
use std::{error, str};

#[tokio::main]
async fn main() -> Result<(), Box<dyn error::Error>> {
    let response = reqwest::get("https://www.example.com/").await?;
    let mut buffer = Vec::new();
    let bytes_read = async {
        response
            .bytes_stream()
            .map_err(|e| futures::io::Error::new(futures::io::ErrorKind::Other, e))
            .into_async_read()
            .read_to_end(&mut buffer)
            .await
    };
    println!(
        "{} bytes: {:?}",
        bytes_read.await?,
        str::from_utf8(&buffer)?
    );
    Ok(())
}

Alternatively, one can read directly from the Stream:

use futures::StreamExt;
use std::{error, str};

#[tokio::main]
async fn main() -> Result<(), Box<dyn error::Error>> {
    let response = reqwest::get("https://www.example.com/").await?;
    let mut buffer = Vec::new();
    let bytes_read = async {
        let mut stream = response.bytes_stream();
        let mut bytes_read = 0;
        while let Some(item) = stream.next().await {
            let item = item?;
            bytes_read += item.len();
            buffer.extend(item);
        }
        Ok::<_, reqwest::Error>(bytes_read)
    };
    println!(
        "{} bytes: {:?}",
        bytes_read.await?,
        str::from_utf8(&buffer)?
    );
    Ok(())
}
2 Likes

You probably shouldn't be using the futures::AsyncRead trait in the first place. You probably also shouldn't be using futures::executor::block_on, as reqwest requires use of a Tokio runtime.

Did you try using the reqwest::blocking module instead of the async api? That is a lot easier than trying to use reqwest via block_on.

3 Likes

I was mostly leaving the code as-is since the original plan was to have reqwest as an optional feature, though I think I will ditch that idea since the fork would be mostly for personal use.