tokio-rs / axum

Ergonomic and modular web framework built with Tokio, Tower, and Hyper

Repository from Github https://github.comtokio-rs/axumRepository from Github https://github.comtokio-rs/axum

Writer based ResponseBody

mineichen opened this issue · comments

  • I have looked for existing issues (including closed) about this

Feature Request

Motivation

I'm using async_zip to send data directly to the client. async_zip::core::ZipFileWriter::new accepts a param, which implements AsyncWrite. Afterwards, i can asynchronously add files to the ZipFileWriter, and the encoded/compressed data is written to the underlying writer. Unfortunately, I couldn't get a hand on a writer for the HttpBody.

Proposal

I came up with a new IntoResponse type, which accepts a closure that returns a future. As long as the future is not finished, it's able to write data to the writer. You can find a naive implementation here. The usage of that type looks like this:

IoStreamBody::with_writer(move |mut w| {
    async move {
       w.write_all(b"Hello").await?;
       Ok(())
    }
}),

This will allow easy integration with async_zip and it will potentially be easier to imlement allocation-free data-streams, as we never need to represent the data to be returned as a owned byte-slice.

Alternatives

Just leave this to the user, as it can be implemented today.
I don't know, how important backward compatibility is... Maybe It could be implemented with AsyncFn instead?
Such a IntoResponse-Type could also life in the axum-extras project. The Error type should most likely not be anyhow::Error (as in the poc implementation).

You don't need a new type, you can just use ReaderStream to convert your Reader into a Stream and then Body::from_stream.

Does that solve your issue?

It could be part of the implementation, but there is more to it.
I don't just have a AsyncRead, but I have to create one, e.g. with piper. But now I have drive the future which fills the pipe somewhere. In my implementation I do this as part of the Stream( the easiest way is, to just spawn a tokio task, but in a complete implementation I have to handle Errors/Panics of this task so it can be related to the Request and is not just a "global error").

I aggree, that this doesn't have to be directly related to axum, but outside axum, we need to go through a indirection (e.g. Piper), but axum has the possibility to expose the writer with access to the RequestSocket, if it's implemented by axum itself and not in axum-extra.

It would be another paradigm, and other Languages/Frameworks (e.g. Dotnet) allow you to write to the http-body instead of returning a value from the handler which is then written by the framework. It would be nice to have something like that, because it wasn't obvious to me how I can use AsyncFileWriter without a intermediate Buffer. A junior dev might even think, that this is not possible with axum.