quic/s2n-quic-transport/src/dc/manager/tests.rs (228 lines of code) (raw):

// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 use super::*; use crate::{ contexts::testing::MockWriteContext, endpoint::testing::{Client, Server}, }; use insta::{assert_debug_snapshot, assert_snapshot}; use s2n_quic_core::{ crypto::tls::testing::Session, dc::testing::MockDcPath, event::testing::Publisher, frame::Frame, packet::number::{PacketNumberRange, PacketNumberSpace}, stateless_reset::token::testing::{TEST_TOKEN_1, TEST_TOKEN_2, TEST_TOKEN_3}, time::clock::testing::now, transmission::{interest::Provider as _, writer::testing::OutgoingFrameBuffer, Provider as _}, varint::VarInt, }; #[test] fn new() { let mut publisher = Publisher::snapshot(); let manager: Manager<Server> = Manager::new(Some(MockDcPath::default()), 1, &mut publisher); assert!(matches!(manager.state, State::InitServer)); assert_eq!(Some(1), manager.version); let manager: Manager<Client> = Manager::new(Some(MockDcPath::default()), 1, &mut publisher); assert!(matches!(manager.state, State::InitClient)); assert_eq!(Some(1), manager.version); } #[test] fn disabled() { let mut publisher = Publisher::snapshot(); let manager: Manager<Server> = Manager::disabled(); assert_eq!(None, manager.version()); assert!(manager.state.is_complete()); assert!(!manager.has_transmission_interest()); let mut manager: Manager<Server> = Manager::new(None, 1, &mut publisher); let ack_set = &PacketNumberRange::new(pn(1), pn(2)); let mut frame_buffer = OutgoingFrameBuffer::new(); let mut context = MockWriteContext::new( now(), &mut frame_buffer, transmission::Constraint::None, transmission::Mode::Normal, endpoint::Type::Client, ); assert_eq!(None, manager.version()); assert!(manager.state.is_complete()); assert!(!manager.has_transmission_interest()); // verify calling all the methods doesn't panic manager.on_peer_dc_stateless_reset_tokens([TEST_TOKEN_1].iter(), &mut publisher); assert!(manager .on_path_secrets_ready(&Session, &mut publisher) .is_ok()); manager.on_packet_ack(ack_set, &mut publisher); manager.on_packet_loss(ack_set); manager.on_transmit(&mut context); // state is still complete assert!(manager.state.is_complete()); } #[test] fn on_path_secrets_ready() { let mut publisher = Publisher::snapshot(); let path = MockDcPath::default(); let mut manager: Manager<Server> = Manager::new(Some(path), 1, &mut publisher); assert!(manager .on_path_secrets_ready(&Session, &mut publisher) .is_ok()); assert_eq!(1, manager.path().on_path_secrets_ready_count); assert!(manager.state.is_path_secrets_ready()); // Server doesn't transmit until it receives tokens from the client assert!(!manager.has_transmission_interest()); let path = MockDcPath::default(); let mut manager: Manager<Client> = Manager::new(Some(path), 1, &mut publisher); assert!(manager .on_path_secrets_ready(&Session, &mut publisher) .is_ok()); assert_eq!(1, manager.path().on_path_secrets_ready_count); assert!(manager.state.is_path_secrets_ready()); // Client starts transmitting as soon as path secrets are ready assert!(manager.has_transmission_interest()); // Calling on_path_secrets_ready again results in an internal error assert_eq!( manager.on_path_secrets_ready(&Session, &mut publisher), Err(transport::Error::INTERNAL_ERROR) ); } #[test] fn on_peer_dc_stateless_reset_tokens_server() { let mut publisher = Publisher::snapshot(); let path = MockDcPath::default(); let mut manager: Manager<Server> = Manager::new(Some(path), 1, &mut publisher); on_peer_dc_stateless_reset_tokens(&mut manager, &mut publisher); } #[test] fn on_peer_dc_stateless_reset_tokens_client() { let mut publisher = Publisher::snapshot(); let path = MockDcPath::default(); let mut manager: Manager<Client> = Manager::new(Some(path), 1, &mut publisher); on_peer_dc_stateless_reset_tokens(&mut manager, &mut publisher); } fn on_peer_dc_stateless_reset_tokens<Config, Endpoint>( manager: &mut Manager<Config>, publisher: &mut Publisher, ) where Config: endpoint::Config<DcEndpoint = Endpoint>, Endpoint: dc::Endpoint<Path = MockDcPath>, { let tokens = [TEST_TOKEN_1, TEST_TOKEN_2, TEST_TOKEN_3]; manager.on_peer_dc_stateless_reset_tokens(tokens.iter(), publisher); // peer tokens were delivered too early assert_eq!(0, manager.path().on_peer_stateless_reset_tokens_count); assert!(manager.path().peer_stateless_reset_tokens.is_empty()); // Now path secrets are ready, so the peer tokens are received assert!(manager.on_path_secrets_ready(&Session, publisher).is_ok()); manager.on_peer_dc_stateless_reset_tokens(tokens.iter(), publisher); assert_eq!(1, manager.path().on_peer_stateless_reset_tokens_count); assert_eq!( tokens.as_slice(), manager.path().peer_stateless_reset_tokens.as_slice() ); if Config::ENDPOINT_TYPE.is_server() { assert!(manager.state.is_server_tokens_sent()); assert_eq!(0, manager.path().on_dc_handshake_complete); } else { assert_eq!(1, manager.path().on_dc_handshake_complete); assert!(manager.state.is_complete()); } // Receiving the peer tokens again doesn't call the provider again manager.on_peer_dc_stateless_reset_tokens(tokens.iter(), publisher); assert_eq!(1, manager.path().on_peer_stateless_reset_tokens_count); } #[test] fn on_packet_ack_client() { let mut publisher = Publisher::snapshot(); let mut path = MockDcPath::default(); let tokens = [TEST_TOKEN_1, TEST_TOKEN_2]; path.stateless_reset_tokens.extend(tokens); let mut manager: Manager<Client> = Manager::new(Some(path), 1, &mut publisher); on_packet_ack(&mut manager, tokens.as_slice(), &mut publisher); // Client completes when it has received stateless reset tokens from the peer assert!(!manager.state.is_complete()); assert_eq!(0, manager.path().on_dc_handshake_complete); } #[test] fn on_packet_ack_server() { let mut publisher = Publisher::snapshot(); let mut path = MockDcPath::default(); let tokens = [TEST_TOKEN_1, TEST_TOKEN_2]; path.stateless_reset_tokens.extend(tokens); let mut manager: Manager<Server> = Manager::new(Some(path), 1, &mut publisher); on_packet_ack(&mut manager, tokens.as_slice(), &mut publisher); // Server completes when its stateless reset tokens are acked assert!(manager.state.is_complete()); assert_eq!(1, manager.path().on_dc_handshake_complete); } fn on_packet_ack<Config, Endpoint>( manager: &mut Manager<Config>, tokens: &[stateless_reset::Token], publisher: &mut Publisher, ) where Config: endpoint::Config<DcEndpoint = Endpoint>, Endpoint: dc::Endpoint<Path = MockDcPath>, { let expected_frame = Frame::DcStatelessResetTokens(DcStatelessResetTokens::new(tokens).unwrap()); let mut frame_buffer = OutgoingFrameBuffer::new(); let mut context = MockWriteContext::new( now(), &mut frame_buffer, transmission::Constraint::None, transmission::Mode::Normal, endpoint::Type::Client, ); let pn = context.packet_number(); assert!(manager.on_path_secrets_ready(&Session, publisher).is_ok()); if Config::ENDPOINT_TYPE.is_server() { // Receive tokens on the server to trigger sending manager.on_peer_dc_stateless_reset_tokens([TEST_TOKEN_3].iter(), publisher); } assert!(manager.has_transmission_interest()); manager.on_transmit(&mut context); // We no longer have transmission interest, but DC_STATELESS_RESET_TOKENS will still // be transmitted passively until one is acked assert!(!manager.has_transmission_interest()); assert_eq!( expected_frame, context.frame_buffer.pop_front().unwrap().as_frame() ); manager.on_transmit(&mut context); // Same DC_STATELESS_RESET_TOKENS frame is written assert_eq!( expected_frame, context.frame_buffer.pop_front().unwrap().as_frame() ); // Ack the first one manager.on_packet_ack(&PacketNumberRange::new(pn, pn), publisher); assert!(!manager.has_transmission_interest()); } #[test] fn on_packet_loss() { let mut publisher = Publisher::snapshot(); let mut path = MockDcPath::default(); let tokens = [TEST_TOKEN_1, TEST_TOKEN_2]; path.stateless_reset_tokens.extend(tokens); let mut manager: Manager<Client> = Manager::new(Some(path), 1, &mut publisher); let mut frame_buffer = OutgoingFrameBuffer::new(); let mut context = MockWriteContext::new( now(), &mut frame_buffer, transmission::Constraint::None, transmission::Mode::Normal, endpoint::Type::Client, ); let pn = context.packet_number(); assert!(manager .on_path_secrets_ready(&Session, &mut publisher) .is_ok()); assert!(manager.has_transmission_interest()); manager.on_transmit(&mut context); assert!(!manager.has_transmission_interest()); // DC_STATELESS_RESET_TOKENS frame was lost manager.on_packet_loss(&PacketNumberRange::new(pn, pn)); // so now we have transmission interest again assert!(manager.has_transmission_interest()); } #[test] fn on_mtu_updated() { let mut publisher = Publisher::snapshot(); let path = MockDcPath::default(); let mut manager: Manager<Server> = Manager::new(Some(path), 1, &mut publisher); manager.on_mtu_updated(1500); assert_eq!(1500, manager.path().mtu); } #[test] #[cfg_attr(miri, ignore)] fn snapshots() { assert_debug_snapshot!(State::test_transitions()); } #[test] #[cfg_attr(miri, ignore)] fn dot_test() { assert_snapshot!(State::dot()); } /// Creates an application space packet number with the given value fn pn(nr: usize) -> PacketNumber { PacketNumberSpace::ApplicationData.new_packet_number(VarInt::new(nr as u64).unwrap()) }