pub struct ChunksWritten(());
pub struct ChunkWriter<W: crate::io::Write> {
writer: W,
}
impl<W: crate::io::Write> ChunkWriter<W> {
pub async fn write_chunk(&mut self, chunk: &[u8]) -> Result<(), W::Error> {
use crate::io::WriteExt;
if chunk.is_empty() {
return Ok(());
}
write!(&mut self.writer, "{:x}\r\n", chunk.len()).await?;
self.writer.write_all(chunk).await?;
self.writer.write_all(b"\r\n").await?;
Ok(())
}
pub async fn finalize(mut self) -> Result<ChunksWritten, W::Error> {
self.writer.write_all(b"0\r\n\r\n").await?;
self.writer.flush().await?;
Ok(ChunksWritten(()))
}
pub async fn write_fmt(&mut self, args: core::fmt::Arguments<'_>) -> Result<(), W::Error> {
use crate::io::WriteExt;
use core::fmt::Write;
let mut chunk_size = 0;
if super::MeasureFormatSize(&mut chunk_size)
.write_fmt(args)
.is_err()
{
log_warn!("Skipping writing chunk due to Format Error");
return Ok(());
}
if chunk_size == 0 {
return Ok(());
}
write!(&mut self.writer, "{chunk_size:x}\r\n{args}\r\n",).await?;
Ok(())
}
pub async fn flush(&mut self) -> Result<(), W::Error> {
self.writer.flush().await
}
}
pub trait Chunks {
fn content_type(&self) -> &'static str;
async fn write_chunks<W: crate::io::Write>(
self,
chunk_writer: ChunkWriter<W>,
) -> Result<ChunksWritten, W::Error>;
}
pub struct ChunkedResponse<C: Chunks> {
chunks: C,
}
impl<C: Chunks> ChunkedResponse<C> {
pub fn new(chunks: C) -> Self {
Self { chunks }
}
pub fn into_response(self) -> super::Response<impl super::HeadersIter, impl super::Body> {
struct Body<C: Chunks>(C);
impl<C: Chunks> super::Body for Body<C> {
async fn write_response_body<
R: crate::io::Read,
W: crate::io::Write<Error = R::Error>,
>(
self,
_connection: super::Connection<'_, R>,
writer: W,
) -> Result<(), W::Error> {
self.0
.write_chunks(ChunkWriter { writer })
.await
.map(|ChunksWritten(())| ())
}
}
let content_type = self.chunks.content_type();
super::Response {
status_code: super::StatusCode::OK,
headers: [
("Content-Type", content_type),
("Transfer-Encoding", "chunked"),
],
body: Body(self.chunks),
}
}
}
impl<C: Chunks> super::IntoResponse for ChunkedResponse<C> {
async fn write_to<R: crate::io::Read, W: super::ResponseWriter<Error = R::Error>>(
self,
connection: super::Connection<'_, R>,
response_writer: W,
) -> Result<crate::ResponseSent, W::Error> {
response_writer
.write_response(connection, self.into_response())
.await
}
}