sark-http 0.27.0

HTTP wire types, semantics, codecs, and ownership invariants for Sark
Documentation
pub mod common;

use std::ops;

use common::wire::base64::{VECTORS, encode, try_decode};
use o3::buffer::write::{Sink as _, Staged};
use sark_http::field::{
    Encode as _,
    base64::{Decoder, Encoded, Encoder, Error},
};

#[test]
fn accepts_padding_forms() {
    for &(raw, plain, padded) in VECTORS {
        assert_eq!(encode(raw, false), plain);
        assert_eq!(encode(raw, true), padded);
        for (value, expected) in [(Encoder::new(raw), plain), (Encoder::padded(raw), padded)] {
            let len = value.encoded_len();
            let mut out = Vec::with_capacity(len);
            let mut writer = Staged::try_new(&mut out, len).expect("admitted capacity");
            value
                .try_write(|bytes| writer.write_slice(bytes))
                .expect("field emission");
            writer.try_complete().expect("exact field length").commit();
            assert_eq!(out, expected);
        }
        assert!(try_decode(plain).as_deref() == Ok(raw));
        assert!(try_decode(padded).as_deref() == Ok(raw));
    }
}

#[test]
fn every_octet_round_trips() {
    let raw = (0..=u8::MAX).collect::<Vec<_>>();
    for padded in [false, true] {
        let encoded = encode(&raw, padded);
        assert!(Encoded::new(&encoded).decoded_len() == Ok(raw.len()));
        assert!(try_decode(&encoded) == Ok(raw.clone()));
    }
}

#[test]
fn decodes_overlaps_safely() {
    let encoded = b"AQIDBAUG";
    for dst in [0, 8, 10] {
        let mut owner = [0xa5; 24];
        owner[8..16].copy_from_slice(encoded);
        assert!(Decoder::new(&mut owner).decode(8..16, dst) == Ok(6));
        assert_eq!(&owner[dst..dst + 6], &[1, 2, 3, 4, 5, 6]);
    }

    let raw = (0..96).collect::<Vec<_>>();
    let encoded = encode(&raw, false);
    let mut owner = vec![0xa5; encoded.len() + 32];
    owner[32..].copy_from_slice(&encoded);
    let end = owner.len();
    assert!(Decoder::new(&mut owner).decode(32..end, 0) == Ok(raw.len()));
    assert_eq!(&owner[..raw.len()], raw);
}

#[test]
fn matches_all_overlaps() {
    for raw_len in 0..=96 {
        let raw = (0..raw_len)
            .map(|index| (index * 37 % 256) as u8)
            .collect::<Vec<_>>();
        for padded in [false, true] {
            let encoded = encode(&raw, padded);
            let source = 17;
            let dests = [
                0,
                source - 1,
                source,
                source + 1,
                source + encoded.len() / 2,
                source + encoded.len() + 1,
            ];
            for dst in dests {
                let src_end = source + encoded.len();
                let dst_end = dst + raw.len();
                let mut owner = vec![0xa5; src_end.max(dst_end) + 17];
                owner[source..src_end].copy_from_slice(&encoded);
                assert!(Decoder::new(&mut owner).decode(source..src_end, dst) == Ok(raw.len()));
                assert_eq!(&owner[dst..dst + raw.len()], raw);
            }
        }
    }
}

#[test]
fn validates_octets_and_offsets() {
    for byte in 0..=u8::MAX {
        for index in 0..64 {
            let mut encoded = [b'A'; 68];
            encoded[index] = byte;
            let expected = if byte.is_ascii_alphanumeric() || matches!(byte, b'+' | b'/') {
                Ok(51)
            } else {
                Err(Error::Byte { index, byte })
            };
            assert!(Encoded::new(&encoded).validate() == expected);
        }
    }
}

#[test]
fn rejects_malformed_text() {
    assert!(try_decode(b"A") == Err(Error::Length { actual: 1 }));
    assert!(
        try_decode(b"A===")
            == Err(Error::Padding {
                index: 1,
                byte: b'=',
            })
    );
    assert!(
        try_decode(b"AA=A")
            == Err(Error::Byte {
                index: 2,
                byte: b'=',
            })
    );
    assert!(
        try_decode(b"AB")
            == Err(Error::Padding {
                index: 1,
                byte: b'B',
            })
    );
    assert!(
        try_decode(b"AA?A")
            == Err(Error::Byte {
                index: 2,
                byte: b'?',
            })
    );
    assert!(
        Encoded::new(b"dGhlIHNhbXBsZSBub25jZR==").validate()
            == Err(Error::Padding {
                index: 21,
                byte: b'R',
            })
    );
    assert!(
        Encoded::new(b"dGhlIHNhbXBsZSBub25jZQ=A").validate()
            == Err(Error::Byte {
                index: 22,
                byte: b'=',
            })
    );

    let mut lane = *b"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
    lane[17] = b'?';
    assert!(
        try_decode(&lane)
            == Err(Error::Byte {
                index: 17,
                byte: b'?'
            })
    );
}

#[test]
fn reports_storage_bounds() {
    let mut owner = *b"AAAA";
    assert!(
        Decoder::new(&mut owner).decode(ops::Range { start: 3, end: 2 }, 0)
            == Err(Error::Source {
                start: 3,
                end: 2,
                capacity: 4,
            })
    );
    assert!(
        Decoder::new(&mut owner).decode(0..4, 2)
            == Err(Error::Output {
                start: 2,
                len: 3,
                capacity: 4,
            })
    );
    assert!(
        Decoder::new(&mut owner).decode(0..4, usize::MAX)
            == Err(Error::Output {
                start: usize::MAX,
                len: 3,
                capacity: 4,
            })
    );
}