net-wire 0.1.0

Allocation-free no_std wire views and codecs for network protocols.
Documentation
use net_wire::http3::{
    Http3ControlFrame, Http3ControlStreamError, Http3ControlStreamState, Http3EndpointRole,
    Http3ErrorCode, Http3Frame, Http3FramePayloadField, Http3FramePayloadParseError,
    Http3FrameType, Http3GoawayBuilder, Http3PeerSettings, Http3PushId, Http3SettingId,
    Http3SettingsSemanticError,
};
use net_wire::quic::QuicVarIntParseError;

#[test]
fn control_stream_empty_role_specific_state_is_exposed() {
    let client = Http3ControlStreamState::new(Http3EndpointRole::Client);
    let server = Http3ControlStreamState::new(Http3EndpointRole::Server);

    for (state, role) in [
        (client, Http3EndpointRole::Client),
        (server, Http3EndpointRole::Server),
    ] {
        assert_eq!(state.role(), role);
        assert_eq!(state.settings(), None);
        assert_eq!(state.last_goaway_identifier(), None);
        assert_eq!(state.maximum_push_id(), None);
    }
}

#[test]
fn control_stream_requires_and_commits_one_valid_settings_frame_atomically() {
    let mut state = Http3ControlStreamState::new(Http3EndpointRole::Client);
    let before = state;
    let malformed_data = Http3Frame::parse(&[0x00, 0x01, 0x40], 1).unwrap();
    let error = state.receive(malformed_data).unwrap_err();
    assert_eq!(
        error,
        Http3ControlStreamError::MissingSettings {
            actual: Http3FrameType::DATA,
        }
    );
    assert_eq!(error.error_code(), Http3ErrorCode::MISSING_SETTINGS);
    assert_eq!(state, before);

    let unknown = Http3Frame::parse(&[0x21, 0x00], 0).unwrap();
    let error = state.receive(unknown).unwrap_err();
    assert_eq!(
        error,
        Http3ControlStreamError::MissingSettings {
            actual: Http3FrameType::new(0x21),
        }
    );
    assert_eq!(error.error_code(), Http3ErrorCode::MISSING_SETTINGS);
    assert_eq!(state, before);

    let settings = Http3Frame::parse(
        &[
            0x04, 0x0a, 0x01, 0x40, 0x40, 0x06, 0x00, 0x07, 0x80, 0x00, 0x40, 0x00,
        ],
        10,
    )
    .unwrap();
    match state.receive(settings).unwrap() {
        Http3ControlFrame::Settings {
            frame,
            peer_settings,
        } => {
            assert_eq!(frame.frame().frame_type(), Http3FrameType::SETTINGS);
            assert_eq!(
                frame.as_bytes(),
                &[
                    0x04, 0x0a, 0x01, 0x40, 0x40, 0x06, 0x00, 0x07, 0x80, 0x00, 0x40, 0x00
                ]
            );
            assert_eq!(peer_settings.qpack_max_table_capacity(), 64);
            assert_eq!(peer_settings.maximum_field_section_size(), Some(0));
            assert_eq!(peer_settings.qpack_blocked_streams(), 16_384);
            assert_ne!(peer_settings, Http3PeerSettings::default());
        }
        event => panic!("expected SETTINGS, got {event:?}"),
    }
    assert_eq!(state.settings().unwrap().qpack_max_table_capacity(), 64);
    assert_eq!(
        state.settings().unwrap().maximum_field_section_size(),
        Some(0)
    );
    assert_eq!(state.settings().unwrap().qpack_blocked_streams(), 16_384);

    let before = state;
    let second_malformed_settings = Http3Frame::parse(&[0x04, 0x01, 0x40], 1).unwrap();
    let error = state.receive(second_malformed_settings).unwrap_err();
    assert_eq!(
        error,
        Http3ControlStreamError::UnexpectedFrame {
            frame_type: Http3FrameType::SETTINGS,
        }
    );
    assert_eq!(error.error_code(), Http3ErrorCode::FRAME_UNEXPECTED);
    assert_eq!(state, before);
}

#[test]
fn control_stream_settings_failures_map_errors_and_leave_state_unchanged() {
    let mut state = Http3ControlStreamState::new(Http3EndpointRole::Server);
    let before = state;
    let malformed = Http3Frame::parse(&[0x04, 0x01, 0x40], 1).unwrap();
    let error = state.receive(malformed).unwrap_err();
    assert_eq!(
        error,
        Http3ControlStreamError::FramePayload {
            frame_type: Http3FrameType::SETTINGS,
            error: Http3FramePayloadParseError::PayloadVarInt {
                field: Http3FramePayloadField::SettingIdentifier,
                offset: 0,
                error: QuicVarIntParseError::Incomplete {
                    required: 2,
                    available: 1,
                },
            },
        }
    );
    assert_eq!(error.error_code(), Http3ErrorCode::FRAME_ERROR);
    assert_eq!(state, before);

    let duplicate = Http3Frame::parse(&[0x04, 0x04, 0x21, 0x00, 0x21, 0x00], 4).unwrap();
    let error = state.receive(duplicate).unwrap_err();
    assert_eq!(
        error,
        Http3ControlStreamError::Settings(Http3SettingsSemanticError::DuplicateSetting {
            id: Http3SettingId::new(0x21),
            offset: 2,
        })
    );
    assert_eq!(error.error_code(), Http3ErrorCode::SETTINGS_ERROR);
    assert_eq!(state, before);

    let prohibited = Http3Frame::parse(&[0x04, 0x02, 0x00, 0x00], 2).unwrap();
    let error = state.receive(prohibited).unwrap_err();
    assert_eq!(
        error,
        Http3ControlStreamError::Settings(Http3SettingsSemanticError::ProhibitedSetting {
            id: Http3SettingId::new(0),
            offset: 0,
        })
    );
    assert_eq!(error.error_code(), Http3ErrorCode::SETTINGS_ERROR);
    assert_eq!(state, before);
}

#[test]
fn control_stream_rejects_forbidden_frames_and_preserves_unknown_wire_bytes() {
    let mut state = Http3ControlStreamState::new(Http3EndpointRole::Client);
    state
        .receive(Http3Frame::parse(&[0x04, 0x00], 0).unwrap())
        .unwrap();

    for (wire, frame_type) in [
        (&[0x00, 0x00][..], Http3FrameType::DATA),
        (&[0x01, 0x00][..], Http3FrameType::HEADERS),
        (&[0x05, 0x00][..], Http3FrameType::PUSH_PROMISE),
        (&[0x02, 0x00][..], Http3FrameType::new(0x02)),
        (&[0x06, 0x00][..], Http3FrameType::new(0x06)),
        (&[0x08, 0x00][..], Http3FrameType::new(0x08)),
        (&[0x09, 0x00][..], Http3FrameType::new(0x09)),
    ] {
        let before = state;
        let error = state
            .receive(Http3Frame::parse(wire, 0).unwrap())
            .unwrap_err();
        assert_eq!(
            error,
            Http3ControlStreamError::UnexpectedFrame { frame_type }
        );
        assert_eq!(error.error_code(), Http3ErrorCode::FRAME_UNEXPECTED);
        assert_eq!(state, before);
    }

    for wire in [
        &[0x40, 0x40, 0x80, 0x00, 0x00, 0x02, 0xde, 0xad][..],
        &[0x40, 0x21, 0x80, 0x00, 0x00, 0x02, 0xbe, 0xef][..],
    ] {
        let before = state;
        match state.receive(Http3Frame::parse(wire, 2).unwrap()).unwrap() {
            Http3ControlFrame::Unknown(frame) => {
                assert_eq!(frame.as_bytes(), wire);
                assert_eq!(frame.frame_type_varint().as_bytes(), &wire[..2]);
                assert_eq!(frame.payload_length_varint().as_bytes(), &wire[2..6]);
            }
            event => panic!("expected unknown control frame, got {event:?}"),
        }
        assert_eq!(state, before);
    }
}

#[test]
fn control_stream_enforces_push_role_and_maximum_monotonicity_atomically() {
    let mut client = Http3ControlStreamState::new(Http3EndpointRole::Client);
    client
        .receive(Http3Frame::parse(&[0x04, 0x00], 0).unwrap())
        .unwrap();
    for (wire, frame_type) in [
        (&[0x03, 0x01, 0x40][..], Http3FrameType::CANCEL_PUSH),
        (&[0x0d, 0x01, 0x40][..], Http3FrameType::MAX_PUSH_ID),
    ] {
        let before = client;
        let error = client
            .receive(Http3Frame::parse(wire, 1).unwrap())
            .unwrap_err();
        assert_eq!(
            error,
            Http3ControlStreamError::UnexpectedFrame { frame_type }
        );
        assert_eq!(error.error_code(), Http3ErrorCode::FRAME_UNEXPECTED);
        assert_eq!(client, before);
    }

    let mut server = Http3ControlStreamState::new(Http3EndpointRole::Server);
    server
        .receive(Http3Frame::parse(&[0x04, 0x00], 0).unwrap())
        .unwrap();
    let before_cancel = server;
    match server
        .receive(Http3Frame::parse(&[0x03, 0x01, 0x2a], 1).unwrap())
        .unwrap()
    {
        Http3ControlFrame::CancelPush(cancel) => assert_eq!(cancel.push_id(), Http3PushId::new(42)),
        event => panic!("expected CANCEL_PUSH, got {event:?}"),
    }
    assert_eq!(server, before_cancel);

    for wire in [&[0x0d, 0x02, 0x40, 0x40][..], &[0x0d, 0x02, 0x40, 0x40][..]] {
        match server.receive(Http3Frame::parse(wire, 2).unwrap()).unwrap() {
            Http3ControlFrame::MaxPushId(maximum) => {
                assert_eq!(maximum.push_id(), Http3PushId::new(64));
            }
            event => panic!("expected MAX_PUSH_ID, got {event:?}"),
        }
    }
    match server
        .receive(Http3Frame::parse(&[0x0d, 0x04, 0x80, 0x00, 0x40, 0x00], 4).unwrap())
        .unwrap()
    {
        Http3ControlFrame::MaxPushId(maximum) => {
            assert_eq!(maximum.push_id(), Http3PushId::new(16_384));
        }
        event => panic!("expected MAX_PUSH_ID, got {event:?}"),
    }
    assert_eq!(server.maximum_push_id(), Some(Http3PushId::new(16_384)));

    let before = server;
    let error = server
        .receive(Http3Frame::parse(&[0x0d, 0x02, 0x40, 0x40], 2).unwrap())
        .unwrap_err();
    assert_eq!(
        error,
        Http3ControlStreamError::ReducedMaximumPushId {
            previous: Http3PushId::new(16_384),
            current: Http3PushId::new(64),
        }
    );
    assert_eq!(error.error_code(), Http3ErrorCode::ID_ERROR);
    assert_eq!(server, before);
}

#[test]
fn control_stream_goaway_role_semantics_are_monotonic_and_atomic() {
    let mut client = Http3ControlStreamState::new(Http3EndpointRole::Client);
    client
        .receive(Http3Frame::parse(&[0x04, 0x00], 0).unwrap())
        .unwrap();
    for identifier in [8, 8, 4] {
        let mut destination = [0; 10];
        let frame = Http3GoawayBuilder::new(&mut destination, identifier)
            .build()
            .unwrap();
        match client
            .receive(Http3Frame::parse(frame.as_bytes(), 8).unwrap())
            .unwrap()
        {
            Http3ControlFrame::Goaway(goaway) => assert_eq!(goaway.identifier(), identifier),
            event => panic!("expected GOAWAY, got {event:?}"),
        }
    }
    assert_eq!(client.last_goaway_identifier(), Some(4));
    let before = client;
    let error = client
        .receive(Http3Frame::parse(&[0x07, 0x01, 0x05], 1).unwrap())
        .unwrap_err();
    assert_eq!(
        error,
        Http3ControlStreamError::InvalidGoawayStreamId { identifier: 5 }
    );
    assert_eq!(error.error_code(), Http3ErrorCode::ID_ERROR);
    assert_eq!(client, before);
    let error = client
        .receive(Http3Frame::parse(&[0x07, 0x01, 0x08], 1).unwrap())
        .unwrap_err();
    assert_eq!(
        error,
        Http3ControlStreamError::IncreasedGoawayIdentifier {
            previous: 4,
            current: 8,
        }
    );
    assert_eq!(error.error_code(), Http3ErrorCode::ID_ERROR);
    assert_eq!(client, before);

    let mut server = Http3ControlStreamState::new(Http3EndpointRole::Server);
    server
        .receive(Http3Frame::parse(&[0x04, 0x00], 0).unwrap())
        .unwrap();
    let maximum = 0x3fff_ffff_ffff_ffff;
    match server
        .receive(
            Http3Frame::parse(
                &[0x07, 0x08, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff],
                8,
            )
            .unwrap(),
        )
        .unwrap()
    {
        Http3ControlFrame::Goaway(goaway) => assert_eq!(goaway.identifier(), maximum),
        event => panic!("expected GOAWAY, got {event:?}"),
    }
    match server
        .receive(Http3Frame::parse(&[0x07, 0x01, 0x2a], 1).unwrap())
        .unwrap()
    {
        Http3ControlFrame::Goaway(goaway) => assert_eq!(goaway.identifier(), 42),
        event => panic!("expected GOAWAY, got {event:?}"),
    }
    let before = server;
    let error = server
        .receive(Http3Frame::parse(&[0x07, 0x01, 0x2b], 1).unwrap())
        .unwrap_err();
    assert_eq!(
        error,
        Http3ControlStreamError::IncreasedGoawayIdentifier {
            previous: 42,
            current: 43,
        }
    );
    assert_eq!(error.error_code(), Http3ErrorCode::ID_ERROR);
    assert_eq!(server, before);
    let error = server
        .receive(Http3Frame::parse(&[0x07, 0x03, 0x40, 0x2a, 0xff], 3).unwrap())
        .unwrap_err();
    assert_eq!(
        error,
        Http3ControlStreamError::FramePayload {
            frame_type: Http3FrameType::GOAWAY,
            error: Http3FramePayloadParseError::TrailingPayload {
                consumed: 2,
                actual: 3,
            },
        }
    );
    assert_eq!(error.error_code(), Http3ErrorCode::FRAME_ERROR);
    assert_eq!(server, before);
}

#[test]
fn control_stream_errors_describe_the_failure_and_expose_only_nested_sources() {
    let mut state = Http3ControlStreamState::new(Http3EndpointRole::Server);
    let missing = state
        .receive(Http3Frame::parse(&[0x00, 0x00], 0).unwrap())
        .unwrap_err();
    assert!(missing.to_string().contains("starts with frame type 0"));
    assert!(core::error::Error::source(&missing).is_none());

    let payload = state
        .receive(Http3Frame::parse(&[0x04, 0x01, 0x40], 1).unwrap())
        .unwrap_err();
    assert!(payload.to_string().contains("SETTINGS identifier"));
    assert!(core::error::Error::source(&payload).is_some());

    let semantic = state
        .receive(Http3Frame::parse(&[0x04, 0x04, 0x21, 0x00, 0x21, 0x00], 4).unwrap())
        .unwrap_err();
    assert!(
        semantic
            .to_string()
            .contains("duplicates an earlier identifier")
    );
    assert!(core::error::Error::source(&semantic).is_some());

    let mut ready = Http3ControlStreamState::new(Http3EndpointRole::Client);
    ready
        .receive(Http3Frame::parse(&[0x04, 0x00], 0).unwrap())
        .unwrap();
    let unexpected = ready
        .receive(Http3Frame::parse(&[0x00, 0x00], 0).unwrap())
        .unwrap_err();
    assert!(
        unexpected
            .to_string()
            .contains("unexpected on the control stream")
    );
    assert!(core::error::Error::source(&unexpected).is_none());
}