use std::rc::Rc;
use eyre::Context;
use tracing::debug;
use crate::{
h1::body::{H1Body, H1BodyKind},
util::{read_and_parse, SemanticError},
ExpectResponseHeaders, HeadersExt, Responder, ServerDriver,
};
use fluke_buffet::{ReadOwned, RollMut, WriteOwned};
use super::encode::H1Encoder;
pub struct ServerConf {
pub max_http_header_len: usize,
pub max_header_record_len: usize,
pub max_header_records: usize,
}
impl Default for ServerConf {
fn default() -> Self {
Self {
max_http_header_len: 64 * 1024,
max_header_record_len: 4 * 1024,
max_header_records: 128,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ServeOutcome {
ClientRequestedConnectionClose,
ServerRequestedConnectionClose,
ClientClosedConnectionBetweenRequests,
ClientDidntSpeakHttp11,
}
pub async fn serve(
(mut transport_r, mut transport_w): (impl ReadOwned, impl WriteOwned),
conf: Rc<ServerConf>,
mut client_buf: RollMut,
driver: impl ServerDriver,
) -> eyre::Result<ServeOutcome> {
loop {
let req;
(client_buf, req) = match read_and_parse(
super::parse::request,
&mut transport_r,
client_buf,
conf.max_http_header_len,
)
.await
{
Ok(t) => match t {
Some(t) => t,
None => {
debug!("client went away before sending request headers");
return Ok(ServeOutcome::ClientClosedConnectionBetweenRequests);
}
},
Err(e) => {
if let Some(se) = e.downcast_ref::<SemanticError>() {
transport_w
.write_all_owned(se.as_http_response())
.await
.wrap_err("writing error response downstream")?;
}
debug!(?e, "error reading request header from downstream");
return Ok(ServeOutcome::ClientDidntSpeakHttp11);
}
};
debug!("got request {req:?}");
let chunked = req.headers.is_chunked_transfer_encoding();
let connection_close = req.headers.is_connection_close();
let content_len = req.headers.content_length().unwrap_or_default();
let mut req_body = H1Body::new(
transport_r,
client_buf,
if chunked {
H1BodyKind::Chunked
} else {
H1BodyKind::ContentLength(content_len)
},
);
let responder = Responder {
encoder: H1Encoder { transport_w },
state: ExpectResponseHeaders,
};
let resp = driver
.handle(req, &mut req_body, responder)
.await
.wrap_err("handling request")?;
transport_w = resp.into_inner().transport_w;
(client_buf, transport_r) = req_body
.into_inner()
.ok_or_else(|| eyre::eyre!("request body not drained, have to close connection"))?;
if connection_close {
debug!("client requested connection close");
return Ok(ServeOutcome::ClientRequestedConnectionClose);
}
}
}