net-wire 0.1.0

Allocation-free no_std wire views and codecs for network protocols.
Documentation
use net_wire::http3::{
    Http3ErrorCode, Http3Frame, Http3FrameBuildError, Http3FrameBuilder, Http3FrameMut,
    Http3FrameParseError, Http3FrameType, Http3PushId, Http3SettingId, Http3StreamId,
    Http3StreamType,
};
use net_wire::quic::{QuicVarIntBuildError, QuicVarIntParseError};

#[test]
fn raw_scalars_preserve_unknown_values() {
    let unknown = u64::MAX;

    assert_eq!(Http3FrameType::new(unknown).value(), unknown);
    assert_eq!(Http3SettingId::new(unknown).value(), unknown);
    assert_eq!(Http3StreamType::new(unknown).value(), unknown);
    assert_eq!(Http3StreamId::new(unknown).value(), unknown);
    assert_eq!(Http3PushId::new(unknown).value(), unknown);
    assert_eq!(Http3ErrorCode::new(unknown).value(), unknown);

    assert_eq!(Http3FrameType::SETTINGS.value(), 0x04);
    assert_eq!(Http3SettingId::MAX_FIELD_SECTION_SIZE.value(), 0x06);
    assert_eq!(Http3StreamType::QPACK_ENCODER.value(), 0x02);
    assert_eq!(Http3ErrorCode::QPACK_DECODER_STREAM_ERROR.value(), 0x202);
}

#[test]
fn raw_frame_parses_exact_noncanonical_varints_and_excludes_suffix() {
    let wire = [0x40, 0x21, 0x80, 0x00, 0x00, 0x02, 0xde, 0xad, 0xfa];
    let frame = Http3Frame::parse(&wire, 2).unwrap();

    assert_eq!(frame.frame_type(), Http3FrameType::new(0x21));
    assert_eq!(frame.frame_type_varint().as_bytes(), &[0x40, 0x21]);
    assert_eq!(
        frame.payload_length_varint().as_bytes(),
        &[0x80, 0x00, 0x00, 0x02]
    );
    assert_eq!(frame.payload(), &[0xde, 0xad]);
    assert_eq!(frame.as_bytes(), &wire[..8]);
    assert_eq!(&wire[frame.as_bytes().len()..], &[0xfa]);
}

#[test]
fn raw_frame_reports_exact_structural_errors() {
    assert_eq!(
        Http3Frame::parse(&[], 0),
        Err(Http3FrameParseError::TypeVarInt(
            QuicVarIntParseError::Incomplete {
                required: 1,
                available: 0,
            }
        ))
    );
    assert_eq!(
        Http3Frame::parse(&[0x80, 0x00, 0x00], 0),
        Err(Http3FrameParseError::TypeVarInt(
            QuicVarIntParseError::Incomplete {
                required: 4,
                available: 3,
            }
        ))
    );
    assert_eq!(
        Http3Frame::parse(&[0x01], 0),
        Err(Http3FrameParseError::LengthVarInt(
            QuicVarIntParseError::Incomplete {
                required: 1,
                available: 0,
            }
        ))
    );
    assert_eq!(
        Http3Frame::parse(&[0x01, 0x40], 0),
        Err(Http3FrameParseError::LengthVarInt(
            QuicVarIntParseError::Incomplete {
                required: 2,
                available: 1,
            }
        ))
    );
    assert_eq!(
        Http3Frame::parse(&[0x01, 0x03], 2),
        Err(Http3FrameParseError::PayloadTooLarge {
            maximum: 2,
            actual: 3,
        })
    );
    assert_eq!(
        Http3Frame::parse(&[0x01, 0x03, 0xaa], 3),
        Err(Http3FrameParseError::IncompletePayload {
            required: 5,
            available: 3,
        })
    );
}

#[test]
fn mutable_raw_frame_bounds_one_prefix_and_preserves_its_suffix() {
    let mut wire = [0x40, 0x21, 0x80, 0x00, 0x00, 0x02, 0xde, 0xad, 0xfa];
    {
        let mut frame = Http3FrameMut::parse(&mut wire, 2).unwrap();

        assert_eq!(frame.frame_type(), Http3FrameType::new(0x21));
        assert_eq!(frame.payload_length(), 2);
        assert_eq!(
            frame.as_bytes(),
            &[0x40, 0x21, 0x80, 0x00, 0x00, 0x02, 0xde, 0xad]
        );
        frame.payload_mut()[1] = 0xbe;
        assert_eq!(frame.payload(), &[0xde, 0xbe]);
    }

    assert_eq!(wire, [0x40, 0x21, 0x80, 0x00, 0x00, 0x02, 0xde, 0xbe, 0xfa]);
}

#[test]
fn raw_builder_canonically_writes_a_destination_backed_frame() {
    let mut destination = [0xaa; 8];
    {
        let frame: Http3Frame<'_> =
            Http3FrameBuilder::new(&mut destination, Http3FrameType::new(0x40), &[0xde, 0xad])
                .build()
                .unwrap();

        assert!(frame.frame_type_varint().is_canonical());
        assert!(frame.payload_length_varint().is_canonical());
        assert_eq!(frame.frame_type_varint().as_bytes(), &[0x40, 0x40]);
        assert_eq!(frame.payload_length_varint().as_bytes(), &[0x02]);
        assert_eq!(frame.frame_type(), Http3FrameType::new(0x40));
        assert_eq!(frame.payload(), &[0xde, 0xad]);
        assert_eq!(frame.as_bytes(), &[0x40, 0x40, 0x02, 0xde, 0xad]);
    }
    assert_eq!(&destination[5..], &[0xaa, 0xaa, 0xaa]);
}

#[test]
fn raw_builder_failures_are_atomic() {
    let mut too_large_destination = [0xaa; 8];
    assert_eq!(
        Http3FrameBuilder::new(
            &mut too_large_destination,
            Http3FrameType::new(u64::MAX),
            &[],
        )
        .build(),
        Err(Http3FrameBuildError::TypeVarInt(
            QuicVarIntBuildError::ValueTooLarge { value: u64::MAX }
        ))
    );
    assert_eq!(too_large_destination, [0xaa; 8]);

    let mut short_destination = [0xaa; 4];
    assert_eq!(
        Http3FrameBuilder::new(
            &mut short_destination,
            Http3FrameType::new(0x40),
            &[0xde, 0xad],
        )
        .build(),
        Err(Http3FrameBuildError::BufferTooShort {
            required: 5,
            available: 4,
        })
    );
    assert_eq!(short_destination, [0xaa; 4]);
}