Is there a good example of streaming response from http server using Hyper?
In my particular case, upon the client's request, the server would read a big file, decrypt it chunk by chunk and return the plain data back to the client. What is the best practice or pattern of doing that in Hyper?
I've posted this with code details but wanted to also ask here if it's okay.
It is possible via Body::channel() method and Bytes struct, but you have to create Bytes for every chunk to be written to channel and it requires a lot of memory allocation/deallocation, unfortunately, there is no way to send a byte slice &[u8] and reuse a buffer instead of allocation/deallocation.
@seanmonstar, Any other way to write a large file in a stream way instead of loading the whole file in a buffer, I am also stuck in this situation?
Now I understand more about Stream and have implemented the stream for this case. It is basically like this:
pub struct DecryptFileStream {
f: File,
<other_fields>,
}
impl Stream for DecryptFileStream {
type Item = io::Result<Vec<u8>>;
fn poll_next(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Option<io::Result<Vec<u8>>>> {
<business_logic>
if decrypted.len() > 0 {
Poll::Ready(Some(Ok(decrypted)))
} else {
Poll::Ready(None)
}
}
}
//. then use the above stream:
let stream = DecryptFileStream::new(...);
Response::new(Body::wrap_stream(stream))
Check it out, it might help you:
https://github.com/rousan/stream-body
@keepsimple1, @rousan, bytes crate was initially developed to minimize memory allocations. The only magical method is BytesMut::reserve which reuses the same buffer if all other handles have been dropped.
tokio framework already have helpers that do conversion AsyncRead -> Stream in tokio-util crate. See an example here.
Here is my attempt to test that buffer is reused: link to playground.
thanks @nickkuk . I'm not ready to use AsyncRead yet, so I was not able to use Stream from tokio directly. And it turns out that implementing Stream trait for my own struct is not that bad.
send_file example was updated with suggested approach.
@nickkuk,
send_file example was updated with suggested approach.
in this example, does the FramedRead re-uses a Bytes instance on every data chunk? or it creates one Bytes instance for each data chunk, if so, isn't it inefficient in terms of memory?
The FramedRead re-uses a BytesMut, which will use the same buffer if possible.
Most helpful comment
send_file example was updated with suggested approach.