Skip to content
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

Allow hyper::Body to be created from Read or AsyncRead #1328

Closed
cetra3 opened this issue Sep 20, 2017 · 10 comments
Closed

Allow hyper::Body to be created from Read or AsyncRead #1328

cetra3 opened this issue Sep 20, 2017 · 10 comments

Comments

@cetra3
Copy link

cetra3 commented Sep 20, 2017

I'd like to be able to construct a response from a static File or a Read interface. Not sure if this is already possible.

I.e,

    let f = File::open("example.txt").unwrap();
    let body = hyper::Body::from(f);
@srijs
Copy link
Contributor

srijs commented Sep 23, 2017

Hi @cetra3! When building a response, you are not forced to use hyper::Body. In fact, you can use any other type that implements futures::Stream.

To get from a std::io::File to a Stream you can use the futures-fs crate, for instance.

As an example:

// initialise fs pool somewhere in your server
let pool = futures_fs::FsPool::default();

// later, in a request handler
let file_read_stream = pool.read("example.txt");
let response = hyper::Response::new().with_body(file_read_stream);

@srijs
Copy link
Contributor

srijs commented Sep 23, 2017

@seanmonstar should we add this to the guide?

@seanmonstar
Copy link
Member

Sorry, didn't write here as the user also asked on Reddit, and got several answers.

The guide does try to touch on the stream being able to be changed, but it might not be that obvious. Probably the guide should be more properly designed, instead of the sort of mishmash I've put together so far...

@xinghun92
Copy link

I use a Stream to construct a Request like this.

let mut request = ::hyper::Request::new(::hyper::Method::Post, ::hyper::Uri::from_str("http://www.google.com").unwrap());
let pool = FsPool::default();
let file_read_stream = pool.read("test.txt");
request.set_body(file_read_stream);

And I can not compile it, the error is

the trait `std::convert::From<futures_fs::FsReadStream>` is not implemented for `hyper::Body`

How to fix it? thank you!

@srijs
Copy link
Contributor

srijs commented Sep 26, 2017

@xinghun92 Try explicitly creating a Request<FsReadStream>:

let mut request: ::hyper::Request<FsReadStream> = ::hyper::Request::new(::hyper::Method::Post, ::hyper::Uri::from_str("http://www.google.com").unwrap());
let pool = FsPool::default();
let file_read_stream = pool.read("test.txt");
request.set_body(file_read_stream);

@xinghun92
Copy link

It fails when send this request using client.

let mut core = tokio_core::reactor::Core::new().unwrap();
let handle = core.handle();
let client = Client::new(&handle);
let work = client.request(request).and_then(|res| {
    println!("Response: {}", res.status());
    println!("Headers: \n{}", res.headers());

    res.body().for_each(|chunk| {
        io::stdout().write_all(&chunk).map_err(From::from)
    })
}).map(|_| {
    println!("\n\nDone.");
});

The error is

let work = client.request(request).and_then(|res| {
    |                               ^^^^^^^ expected struct `hyper::Body`, found struct `futures_fs::FsReadStream`

@srijs
Copy link
Contributor

srijs commented Sep 26, 2017

Hmm, seems like this is starting to become more tricky. Your current problem would be solved by using Client::configure().body::<FsReadStream>().build(), but then the next problem will be that FsReadStream has error type std::io::Error, where Config::build() expects the body to have error type hyper::Error

@seanmonstar Can you think of anything that might make this easier? For instance, could we change Config::build and Client::request so that they can automatically cast errors?

I'm thinking something like

impl<B> Config<UseDefaultConnector, B>
where B: Stream,
      B::Item: AsRef<[u8]>,
      B::Error: Into<Error>,
{
    ...
}

impl<C, B> Client<C, B> 
where
    C: Connect,
    B: Stream + 'static,
    B::Item: AsRef<[u8]>,
    B::Error: Into<Error>,
{
    ...
}

@seanmonstar
Copy link
Member

Having Into there would probably be nice. This is actually a case of why #1129 exists. I would wish that users would not have to give a hyper::Error back to hyper to signify that the stream is busted...

@cetra3
Copy link
Author

cetra3 commented Sep 27, 2017

@srijs I'm using the shio-rs framework that requires a hyper::Body to be sent in the response so the proposed methods here don't work.

I've implemented a workaround using the hyper::Body::pair() function, but it's very complex to do so, and has its own issues with futures.

So it is possible to still use hyper::Body with a file stream which is good, but the ergonomics around it is pretty bad.

@mehcode
Copy link

mehcode commented Sep 27, 2017

@cetra3 Could you open an issue on Shio? We should generalize and support this.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants