use http::header;
use crate::{h1::body::BodyWriteMode, Body, BodyChunk, Headers, HeadersExt, Response};
use fluke_buffet::Piece;
pub trait ResponseState {}
pub struct ExpectResponseHeaders;
impl ResponseState for ExpectResponseHeaders {}
pub struct ExpectResponseBody {
mode: BodyWriteMode,
}
impl ResponseState for ExpectResponseBody {}
pub struct ResponseDone;
impl ResponseState for ResponseDone {}
pub struct Responder<E, S>
where
E: Encoder,
S: ResponseState,
{
pub(crate) encoder: E,
pub(crate) state: S,
}
impl<E> Responder<E, ExpectResponseHeaders>
where
E: Encoder,
{
pub async fn write_interim_response(&mut self, res: Response) -> eyre::Result<()> {
if !res.status.is_informational() {
return Err(eyre::eyre!("interim response must have status code 1xx"));
}
self.encoder.write_response(res).await?;
Ok(())
}
pub async fn write_final_response(
mut self,
mut res: Response,
) -> eyre::Result<Responder<E, ExpectResponseBody>> {
if res.status.is_informational() {
return Err(eyre::eyre!("final response must have status code >= 200"));
}
let mode = if res.means_empty_body() {
BodyWriteMode::Empty
} else {
match res.headers.content_length() {
Some(0) => BodyWriteMode::Empty,
Some(len) => {
res.headers
.insert(header::CONTENT_LENGTH, format!("{len}").into_bytes().into());
BodyWriteMode::ContentLength
}
None => {
res.headers
.insert(header::TRANSFER_ENCODING, "chunked".into());
BodyWriteMode::Chunked
}
}
};
self.encoder.write_response(res).await?;
Ok(Responder {
state: ExpectResponseBody { mode },
encoder: self.encoder,
})
}
pub async fn write_final_response_with_body(
self,
mut res: Response,
body: &mut impl Body,
) -> eyre::Result<Responder<E, ResponseDone>> {
if let Some(clen) = body.content_len() {
res.headers
.entry(header::CONTENT_LENGTH)
.or_insert_with(|| {
format!("{clen}").into_bytes().into()
});
}
let mut this = self.write_final_response(res).await?;
loop {
match body.next_chunk().await? {
BodyChunk::Chunk(chunk) => {
this.write_chunk(chunk).await?;
}
BodyChunk::Done { trailers } => {
return this.finish_body(trailers).await;
}
}
}
}
}
impl<E> Responder<E, ExpectResponseBody>
where
E: Encoder,
{
pub async fn write_chunk(&mut self, chunk: Piece) -> eyre::Result<()> {
self.encoder.write_body_chunk(chunk, self.state.mode).await
}
pub async fn finish_body(
mut self,
trailers: Option<Box<Headers>>,
) -> eyre::Result<Responder<E, ResponseDone>> {
self.encoder.write_body_end(self.state.mode).await?;
if let Some(trailers) = trailers {
self.encoder.write_trailers(trailers).await?;
}
Ok(Responder {
state: ResponseDone,
encoder: self.encoder,
})
}
}
impl<E> Responder<E, ResponseDone>
where
E: Encoder,
{
pub fn into_inner(self) -> E {
self.encoder
}
}
pub trait Encoder {
async fn write_response(&mut self, res: Response) -> eyre::Result<()>;
async fn write_body_chunk(&mut self, chunk: Piece, mode: BodyWriteMode) -> eyre::Result<()>;
async fn write_body_end(&mut self, mode: BodyWriteMode) -> eyre::Result<()>;
async fn write_trailers(&mut self, trailers: Box<Headers>) -> eyre::Result<()>;
}