use std::fmt::{self, Debug};
use http::{StatusCode, Uri, Version};
use tracing::debug;
use fluke_buffet::PieceCore;
mod headers;
pub use headers::*;
mod method;
pub use method::*;
#[derive(Clone)]
pub struct Request {
pub method: Method,
pub uri: Uri,
pub version: Version,
pub headers: Headers,
}
impl Default for Request {
fn default() -> Self {
Self {
method: Method::Get,
uri: "/".parse().unwrap(),
version: Version::HTTP_11,
headers: Default::default(),
}
}
}
impl fmt::Debug for Request {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Request")
.field("method", &self.method)
.field("uri", &self.uri)
.field("version", &self.version)
.finish()?;
for (name, value) in &self.headers {
debug!(%name, value = ?std::str::from_utf8(value), "header");
}
Ok(())
}
}
#[derive(Clone)]
pub struct Response {
pub version: Version,
pub status: StatusCode,
pub headers: Headers,
}
impl Default for Response {
fn default() -> Self {
Self {
version: Version::HTTP_11,
status: StatusCode::OK,
headers: Default::default(),
}
}
}
impl Response {
pub(crate) fn debug_print(&self) {
debug!(code = %self.status, version = ?self.version, "got response");
for (name, value) in &self.headers {
debug!(%name, value = ?std::str::from_utf8(value), "got header");
}
}
pub fn means_empty_body(&self) -> bool {
matches!(
self.status,
StatusCode::NO_CONTENT | StatusCode::NOT_MODIFIED
)
}
}
pub enum BodyChunk {
Chunk(PieceCore),
Done {
trailers: Option<Box<Headers>>,
},
}
#[derive(Debug, thiserror::Error)]
pub struct BodyError {
reason: BodyErrorReason,
context: Option<Box<dyn Debug + Send + Sync>>,
}
impl fmt::Display for BodyError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "body error: {:?}", self.reason)?;
if let Some(context) = &self.context {
write!(f, " ({context:?})")
} else {
Ok(())
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum BodyErrorReason {
CalledNextChunkAfterError,
ClosedWhileReadingChunkSize,
InvalidChunkSize,
ClosedWhileReadingChunkData,
ClosedWhileReadingContentLength,
ErrorWhileReadingChunkData,
ClosedWhileReadingChunkTerminator,
InvalidChunkTerminator,
CalledWriteBodyChunkWhenNoBodyWasExpected,
}
impl BodyErrorReason {
pub fn as_err(self) -> BodyError {
BodyError {
reason: self,
context: None,
}
}
pub fn with_cx(self, context: impl Debug + Send + Sync + 'static) -> BodyError {
BodyError {
reason: self,
context: Some(Box::new(context)),
}
}
}
#[allow(async_fn_in_trait)] pub trait Body: Debug
where
Self: Sized,
{
fn content_len(&self) -> Option<u64>;
fn eof(&self) -> bool;
async fn next_chunk(&mut self) -> eyre::Result<BodyChunk>;
}
impl Body for () {
fn content_len(&self) -> Option<u64> {
Some(0)
}
fn eof(&self) -> bool {
true
}
async fn next_chunk(&mut self) -> eyre::Result<BodyChunk> {
Ok(BodyChunk::Done { trailers: None })
}
}