sark-core 0.11.0

the L7 layer for dope
Documentation
use http::StatusCode;
use o3::buffer::{Pooled, Shared};

use crate::http::compress::Gzip;

use super::{
    Chunked, EncodedBody, EncodedResponse, FixedResponse, MonoResponseInner, NeverStream, Serve,
    StaticResponseInner, Stream,
};

pub enum Egress<S> {
    Inline { written: usize },
    Static { head: usize, body: &'static [u8] },
    Shared { head: usize, body: Shared },
    Pooled { head: usize, body: Pooled },
    Stream { head: usize, stream: S },
    Failed,
}

pub enum CacheTemplate {
    Inline {
        bytes: Vec<u8>,
        date_offset: Option<usize>,
    },
    Static {
        head: Vec<u8>,
        date_offset: Option<usize>,
        body: &'static [u8],
    },
}

impl CacheTemplate {
    pub fn configure_head(&mut self, emit_date: bool, emit_server: bool) {
        let (template, date_offset) = match self {
            Self::Inline { bytes, date_offset } => (bytes, date_offset),
            Self::Static {
                head, date_offset, ..
            } => (head, date_offset),
        };
        if let Some(offset) = *date_offset {
            if emit_date && emit_server {
                return;
            }
            let term_start =
                offset - super::wire_emit::DATE_PREFIX.len() - super::wire_emit::SERVER_LINE.len();
            let term_end = term_start + super::wire_emit::SERVER_DATE_TERMINATOR_LEN;
            let mut tail = Vec::with_capacity(super::wire_emit::SERVER_DATE_TERMINATOR_LEN);
            if emit_server {
                tail.extend_from_slice(super::wire_emit::SERVER_LINE);
            }
            *date_offset = if emit_date {
                tail.extend_from_slice(super::wire_emit::DATE_PREFIX);
                let offset = term_start + tail.len();
                tail.extend_from_slice(&[0u8; super::wire_emit::DATE_LEN]);
                tail.extend_from_slice(super::wire_emit::CRLF);
                Some(offset)
            } else {
                None
            };
            tail.extend_from_slice(super::wire_emit::CRLF);
            template.splice(term_start..term_end, tail);
        }
    }
}

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Preparation {
    Plain,
    Compress,
    Cache,
}

pub enum Prepared<S> {
    Egress(Egress<S>),
    Cache(CacheTemplate),
}

pub struct ResponseView {
    pub status: StatusCode,
    pub headers: Shared,
    pub body: Shared,
}

pub trait Shape<'req>: Sized {
    type StreamInner: 'static;

    fn prepare(
        self,
        mode: Preparation,
        gzip: Option<&mut Gzip>,
        out: &mut [u8],
        date: &[u8; 29],
    ) -> Prepared<Self::StreamInner>;

    fn response_view(&self) -> Option<ResponseView> {
        None
    }
}

impl<'req, const N: usize> Shape<'req> for FixedResponse<'req, N> {
    type StreamInner = NeverStream;

    fn prepare(
        self,
        mode: Preparation,
        gzip: Option<&mut Gzip>,
        out: &mut [u8],
        date: &[u8; 29],
    ) -> Prepared<Self::StreamInner> {
        if mode == Preparation::Cache {
            let (bytes, date_offset) = self.preserialize();
            return Prepared::Cache(CacheTemplate::Inline {
                bytes,
                date_offset: Some(date_offset),
            });
        }
        if mode == Preparation::Compress
            && !self.has_content_encoding()
            && !self.body_ref().is_empty()
            && let Some(body) = gzip.and_then(|gzip| gzip.encode(self.body_ref()))
        {
            let body_len = body.len();
            let egress = match self.write_gzip_head(out, date, body_len) {
                Some(head) => Egress::Pooled { head, body },
                None => Egress::Failed,
            };
            return Prepared::Egress(egress);
        }
        Prepared::Egress(fixed_egress(self, out, date))
    }

    fn response_view(&self) -> Option<ResponseView> {
        Some(ResponseView {
            status: self.status(),
            headers: self.wire_headers(),
            body: self.body.clone(),
        })
    }
}

impl<'req, B, const N: usize> Shape<'req> for EncodedResponse<'req, B, N>
where
    B: EncodedBody,
{
    type StreamInner = NeverStream;

    fn prepare(
        self,
        mode: Preparation,
        _gzip: Option<&mut Gzip>,
        out: &mut [u8],
        date: &[u8; 29],
    ) -> Prepared<Self::StreamInner> {
        if mode == Preparation::Cache {
            let (bytes, date_offset) = self.preserialize();
            return Prepared::Cache(CacheTemplate::Inline {
                bytes,
                date_offset: Some(date_offset),
            });
        }
        let egress = if let Some(written) = self.write_into_slice(out, date) {
            Egress::Inline { written }
        } else {
            match self.write_head_split(out, date) {
                Some((head, body)) => Egress::Shared { head, body },
                None => Egress::Failed,
            }
        };
        Prepared::Egress(egress)
    }

    fn response_view(&self) -> Option<ResponseView> {
        Some(ResponseView {
            status: self.status(),
            headers: self.wire_headers(),
            body: self.encoded_body(),
        })
    }
}

impl<'req, const N: usize> Shape<'req> for MonoResponseInner<'req, N> {
    type StreamInner = NeverStream;

    fn prepare(
        self,
        mode: Preparation,
        _gzip: Option<&mut Gzip>,
        out: &mut [u8],
        date: &[u8; 29],
    ) -> Prepared<Self::StreamInner> {
        if mode == Preparation::Cache {
            return Prepared::Egress(Egress::Failed);
        }
        let egress = if let Some(written) = self.write_into_slice(out, date) {
            Egress::Inline { written }
        } else {
            match self.write_head_split(out, date) {
                Some((head, body)) => Egress::Shared { head, body },
                None => Egress::Failed,
            }
        };
        Prepared::Egress(egress)
    }

    fn response_view(&self) -> Option<ResponseView> {
        Some(ResponseView {
            status: self.status(),
            headers: self.wire_headers(),
            body: self.body.clone().into_shared(),
        })
    }
}

impl<'req, const N: usize> Shape<'req> for StaticResponseInner<'req, N> {
    type StreamInner = NeverStream;

    fn prepare(
        self,
        mode: Preparation,
        _gzip: Option<&mut Gzip>,
        out: &mut [u8],
        date: &[u8; 29],
    ) -> Prepared<Self::StreamInner> {
        if mode == Preparation::Cache {
            let (head, date_offset, body) = self.preserialize_static();
            return Prepared::Cache(CacheTemplate::Static {
                head,
                date_offset: Some(date_offset),
                body,
            });
        }
        Prepared::Egress(match self.write_head_only(out, date) {
            Some((head, body)) => Egress::Static { head, body },
            None => Egress::Failed,
        })
    }

    fn response_view(&self) -> Option<ResponseView> {
        Some(ResponseView {
            status: self.status(),
            headers: self.wire_headers(),
            body: Shared::from_static(self.body_ref()),
        })
    }
}

impl<'req> Shape<'req> for Chunked {
    type StreamInner = NeverStream;

    fn prepare(
        self,
        mode: Preparation,
        _gzip: Option<&mut Gzip>,
        out: &mut [u8],
        date: &[u8; 29],
    ) -> Prepared<Self::StreamInner> {
        if mode == Preparation::Cache {
            return Prepared::Egress(Egress::Failed);
        }
        let egress = if let Some(written) = self.write_into_slice(out, date) {
            Egress::Inline { written }
        } else {
            match self.write_head_split(out, date) {
                Some((head, body)) => Egress::Shared { head, body },
                None => Egress::Failed,
            }
        };
        Prepared::Egress(egress)
    }
}

impl<'req, S> Shape<'req> for Stream<S>
where
    S: 'static,
{
    type StreamInner = S;

    fn prepare(
        self,
        mode: Preparation,
        _gzip: Option<&mut Gzip>,
        out: &mut [u8],
        date: &[u8; 29],
    ) -> Prepared<Self::StreamInner> {
        if mode == Preparation::Cache {
            return Prepared::Egress(Egress::Failed);
        }
        Prepared::Egress(match self.write_head_stream(out, date) {
            Some((head, stream)) => Egress::Stream { head, stream },
            None => Egress::Failed,
        })
    }
}

impl<'req, const N: usize> Shape<'req> for Serve<'req, N> {
    type StreamInner = NeverStream;

    fn prepare(
        self,
        mode: Preparation,
        gzip: Option<&mut Gzip>,
        out: &mut [u8],
        date: &[u8; 29],
    ) -> Prepared<Self::StreamInner> {
        match self {
            Self::Fixed(response) => response.prepare(mode, gzip, out, date),
            Self::Mono(response) => response.prepare(mode, gzip, out, date),
            Self::Chunked(response) => response.prepare(mode, gzip, out, date),
        }
    }

    fn response_view(&self) -> Option<ResponseView> {
        match self {
            Self::Fixed(response) => response.response_view(),
            Self::Mono(response) => response.response_view(),
            Self::Chunked(_) => None,
        }
    }
}

fn fixed_egress<'req, const N: usize>(
    response: FixedResponse<'req, N>,
    out: &mut [u8],
    date: &[u8; 29],
) -> Egress<NeverStream> {
    if let Some(written) = response.write_into_slice(out, date) {
        return Egress::Inline { written };
    }
    match response.write_head_split(out, date) {
        Some((head, body)) => Egress::Shared { head, body },
        None => Egress::Failed,
    }
}