sark-http 0.28.7

HTTP wire types, semantics, codecs, and ownership invariants for Sark
Documentation
use sark_http::{
    field::{
        head::{self, Flags, headers::Known},
        metadata::map::record::Entry,
        text::IntError,
    },
    h1::{
        field::expect,
        framing::{Admission, RequestBody},
        head::{Semantic, line::Version, request::Fields},
        target,
    },
    payload::response::Method,
};

pub const NO_AUTHORITY: &[u8] = b"GET / HTTP/1.0\r\n\r\n";
pub const EXPECT_VERSIONS: &[(&[u8], bool)] = &[
    (
        b"POST / HTTP/1.0\r\nHost: local\r\nContent-Length: 1\r\nExpect: 100-continue\r\n\r\n",
        false,
    ),
    (
        b"POST / HTTP/1.1\r\nHost: local\r\nContent-Length: 1\r\nExpect: 100-continue\r\n\r\n",
        true,
    ),
    (
        b"POST / HTTP/1.0\r\nHost: local\r\nContent-Length: 1\r\nExpect: 100-continue\r\nExpect: 100-continue\r\n\r\n",
        false,
    ),
    (
        b"POST / HTTP/1.1\r\nHost: local\r\nContent-Length: 1\r\nExpect: \r\nExpect: 100-continue\r\nExpect: 100-continue\r\n\r\n",
        true,
    ),
];
pub const ACCEPT_GZIP: &[u8] = b"gzip";
pub const ENCODING_QUALITY: [(&[u8], (u16, u16, u16)); 5] = [
    (b"", (0, 0, 1000)),
    (b"x-gzip;q=0,*;q=0.8", (800, 800, 1000)),
    (b"br;q=1,gzip;q=0.4,identity;q=0.5", (1000, 400, 500)),
    (b"br;q=0.6", (600, 400, 500)),
    (b"gzip;q=0,*;q=0,identity;q=0", (600, 0, 0)),
];
pub const INVALID_GZIP_LINE: &[u8] = b"gzip;q=0, br;q=2\r\n";
pub const INVALID_CONNECTION_LINE: &[u8] = b" \t, bad/name, \t\r\n";
pub const LENGTH_LINES: &[(&[u8], usize, usize, usize)] = &[
    (b"123\r\n", 0, 3, 3),
    (b" 123\r\n", 1, 4, 4),
    (b" 123 \t\r\n", 1, 4, 6),
    (b"\t 123 \t\r\n", 2, 5, 7),
];
pub const INVALID_LENGTH_LINES: &[(&[u8], IntError)] = &[
    (b"123x\r\n", IntError::InvalidByte),
    (b"123 \rx", IntError::InvalidByte),
    (b"\r\n", IntError::InvalidByte),
    (b"99999999999999999999999999999\r\n", IntError::Overflow),
];

#[derive(PartialEq, Eq, Debug)]
pub enum Failure {
    Field(head::Error),
    Semantic(Semantic),
}

fn try_admit(scan: Fields, method: &[u8], target: &[u8]) -> Result<Admission, Failure> {
    let target = target::Target::parse(method, target)
        .map_err(|error| Failure::Semantic(Semantic::Authority(error)))?;
    let plan = scan
        .admit_target(target, b"HTTP/1.1", target::Policy::Require)
        .map_err(Failure::Semantic)?;
    scan.transition(Method::from_bytes(method), Version::Eleven)
        .map_err(Failure::Semantic)?;
    Ok(plan)
}

pub const AUTHORITY: &[u8] = b"origin.example:443";
pub const AUTHORITY_TARGET: &[u8] = b"/items?q=1";

pub fn expectation(mut scan: Fields) -> Result<expect::Decision, Failure> {
    let mut flags = Flags::default();
    for (known, value) in [
        (Known::ContentLength, b"1".as_slice()),
        (Known::Host, b"test.invalid"),
    ] {
        known
            .apply(&mut scan, &mut flags, value, Version::Eleven)
            .map_err(Failure::Field)?;
    }
    try_admit(scan, b"POST", b"/").map(|admit| admit.expectation())
}

pub trait Request: Sized {
    fn try_headers(headers: &[(Known, &[u8])]) -> Result<RequestBody, Failure>;
    fn try_hosts(method: &[u8], target: &[u8], hosts: &[&[u8]]) -> Result<RequestBody, Failure>;
    fn try_upgrade(headers: &[(Known, &[u8])]) -> Result<RequestBody, Failure>;
    fn try_scan(headers: &[Entry<'_>]) -> Result<Self, head::Error>;
    fn try_body(self) -> Result<RequestBody, Semantic>;
}

impl Request for Fields {
    fn try_headers(headers: &[(Known, &[u8])]) -> Result<RequestBody, Failure> {
        let mut scan = Fields::default();
        let mut flags = Flags::default();
        for &(header, value) in headers {
            header
                .apply(&mut scan, &mut flags, value, Version::Eleven)
                .map_err(Failure::Field)?;
        }
        Known::Host
            .apply(&mut scan, &mut flags, b"test.invalid", Version::Eleven)
            .map_err(Failure::Field)?;
        let target = target::Target::parse(b"GET", b"/")
            .map_err(|error| Failure::Semantic(Semantic::Authority(error)))?;
        scan.admit_target(target, b"HTTP/1.1", target::Policy::AllowEmpty)
            .map(|admit| admit.body())
            .map_err(Failure::Semantic)
    }

    fn try_hosts(method: &[u8], target: &[u8], hosts: &[&[u8]]) -> Result<RequestBody, Failure> {
        let mut scan = Fields::default();
        let mut flags = Flags::default();
        for &host in hosts {
            Known::Host
                .apply(&mut scan, &mut flags, host, Version::Eleven)
                .map_err(Failure::Field)?;
        }
        try_admit(scan, method, target).map(|admit| admit.body())
    }

    fn try_upgrade(headers: &[(Known, &[u8])]) -> Result<RequestBody, Failure> {
        let mut scan = Fields::default();
        let mut flags = Flags::default();
        Known::Host
            .apply(&mut scan, &mut flags, b"origin.example", Version::Eleven)
            .map_err(Failure::Field)?;
        for &(header, value) in headers {
            header
                .apply(&mut scan, &mut flags, value, Version::Eleven)
                .map_err(Failure::Field)?;
        }
        try_admit(scan, b"GET", b"/").map(|admit| admit.body())
    }

    fn try_scan(headers: &[Entry<'_>]) -> Result<Self, head::Error> {
        let mut scan = Fields::default();
        let mut flags = Flags::default();
        for header in headers {
            if let Some(known) = Known::from_name(header.name) {
                known.apply(&mut scan, &mut flags, header.value, Version::Eleven)?;
            }
        }
        Known::Host.apply(&mut scan, &mut flags, b"test.invalid", Version::Eleven)?;
        Ok(scan)
    }

    fn try_body(self) -> Result<RequestBody, Semantic> {
        let target = target::Target::parse(b"GET", b"/").map_err(Semantic::Authority)?;
        self.admit_target(target, b"HTTP/1.1", target::Policy::Require)
            .map(|admit| admit.body())
    }
}