diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/build.rs | 45 | ||||
-rw-r--r-- | src/crypto.rs | 105 | ||||
-rw-r--r-- | src/dgram.rs | 4 | ||||
-rw-r--r-- | src/ffi.rs | 387 | ||||
-rw-r--r-- | src/frame.rs | 162 | ||||
-rw-r--r-- | src/h3/ffi.rs | 19 | ||||
-rw-r--r-- | src/h3/frame.rs | 202 | ||||
-rw-r--r-- | src/h3/mod.rs | 1633 | ||||
-rw-r--r-- | src/h3/qpack/decoder.rs | 11 | ||||
-rw-r--r-- | src/h3/qpack/encoder.rs | 16 | ||||
-rw-r--r-- | src/h3/qpack/mod.rs | 48 | ||||
-rw-r--r-- | src/h3/qpack/static_table.rs | 206 | ||||
-rw-r--r-- | src/h3/stream.rs | 111 | ||||
-rw-r--r-- | src/lib.rs | 4302 | ||||
-rw-r--r-- | src/octets.rs | 52 | ||||
-rw-r--r-- | src/packet.rs | 696 | ||||
-rw-r--r-- | src/recovery/cubic.rs | 503 | ||||
-rw-r--r-- | src/recovery/delivery_rate.rs | 5 | ||||
-rw-r--r-- | src/recovery/hystart.rs | 157 | ||||
-rw-r--r-- | src/recovery/mod.rs | 271 | ||||
-rw-r--r-- | src/recovery/reno.rs | 150 | ||||
-rw-r--r-- | src/stream.rs | 1423 | ||||
-rw-r--r-- | src/tls.rs | 231 |
23 files changed, 8652 insertions, 2087 deletions
diff --git a/src/build.rs b/src/build.rs index 98774aa..875f556 100644 --- a/src/build.rs +++ b/src/build.rs @@ -28,6 +28,7 @@ const CMAKE_PARAMS_ANDROID_NDK: &[(&str, &[(&str, &str)])] = &[ ("x86_64", &[("ANDROID_ABI", "x86_64")]), ]; +// iOS. const CMAKE_PARAMS_IOS: &[(&str, &[(&str, &str)])] = &[ ("aarch64", &[ ("CMAKE_OSX_ARCHITECTURES", "arm64"), @@ -39,6 +40,12 @@ const CMAKE_PARAMS_IOS: &[(&str, &[(&str, &str)])] = &[ ]), ]; +// ARM Linux. +const CMAKE_PARAMS_ARM_LINUX: &[(&str, &[(&str, &str)])] = &[ + ("aarch64", &[("CMAKE_SYSTEM_PROCESSOR", "aarch64")]), + ("arm", &[("CMAKE_SYSTEM_PROCESSOR", "arm")]), +]; + /// Returns the platform-specific output path for lib. /// /// MSVC generator on Windows place static libs in a target sub-folder, @@ -103,7 +110,6 @@ fn get_boringssl_cmake_config() -> cmake::Config { for (android_arch, params) in cmake_params_android { if *android_arch == arch { for (name, value) in *params { - eprintln!("android arch={} add {}={}", arch, name, value); boringssl_cmake.define(name, value); } } @@ -111,7 +117,6 @@ fn get_boringssl_cmake_config() -> cmake::Config { let toolchain_file = android_ndk_home.join("build/cmake/android.toolchain.cmake"); let toolchain_file = toolchain_file.to_str().unwrap(); - eprintln!("android toolchain={}", toolchain_file); boringssl_cmake.define("CMAKE_TOOLCHAIN_FILE", toolchain_file); // 21 is the minimum level tested. You can give higher value. @@ -125,7 +130,6 @@ fn get_boringssl_cmake_config() -> cmake::Config { for (ios_arch, params) in CMAKE_PARAMS_IOS { if *ios_arch == arch { for (name, value) in *params { - eprintln!("ios arch={} add {}={}", arch, name, value); boringssl_cmake.define(name, value); } } @@ -149,6 +153,34 @@ fn get_boringssl_cmake_config() -> cmake::Config { boringssl_cmake }, + "linux" => match arch.as_ref() { + "aarch64" | "arm" => { + for (arm_arch, params) in CMAKE_PARAMS_ARM_LINUX { + if *arm_arch == arch { + for (name, value) in *params { + boringssl_cmake.define(name, value); + } + } + } + boringssl_cmake.define("CMAKE_SYSTEM_NAME", "Linux"); + boringssl_cmake.define("CMAKE_SYSTEM_VERSION", "1"); + + boringssl_cmake + }, + + "x86" => { + boringssl_cmake.define( + "CMAKE_TOOLCHAIN_FILE", + pwd.join("deps/boringssl/src/util/32-bit-toolchain.cmake") + .as_os_str(), + ); + + boringssl_cmake + }, + + _ => boringssl_cmake, + }, + _ => { // Configure BoringSSL for building on 32-bit non-windows platforms. if arch == "x86" && os != "windows" { @@ -197,7 +229,7 @@ Cflags: -I${{includedir}} } fn main() { - if cfg!(feature = "boringssl-vendored") { + if cfg!(feature = "boringssl-vendored") && !cfg!(feature = "boring-sys") { let bssl_dir = std::env::var("QUICHE_BSSL_PATH").unwrap_or_else(|_| { let mut cfg = get_boringssl_cmake_config(); @@ -217,6 +249,11 @@ fn main() { println!("cargo:rustc-link-lib=static=ssl"); } + if cfg!(feature = "boring-sys") { + println!("cargo:rustc-link-lib=static=crypto"); + println!("cargo:rustc-link-lib=static=ssl"); + } + // MacOS: Allow cdylib to link with undefined symbols if cfg!(target_os = "macos") { println!("cargo:rustc-cdylib-link-arg=-Wl,-undefined,dynamic_lookup"); diff --git a/src/crypto.rs b/src/crypto.rs index b45f4a5..6e33957 100644 --- a/src/crypto.rs +++ b/src/crypto.rs @@ -297,7 +297,7 @@ pub fn derive_initial_key_material( let key_len = aead.key_len(); let nonce_len = aead.nonce_len(); - let initial_secret = derive_initial_secret(&cid, version)?; + let initial_secret = derive_initial_secret(&cid, version); // Client. let mut client_key = vec![0; key_len]; @@ -334,26 +334,33 @@ pub fn derive_initial_key_material( Ok((open, seal)) } -fn derive_initial_secret(secret: &[u8], version: u32) -> Result<hkdf::Prk> { +fn derive_initial_secret(secret: &[u8], version: u32) -> hkdf::Prk { const INITIAL_SALT: [u8; 20] = [ + 0x38, 0x76, 0x2c, 0xf7, 0xf5, 0x59, 0x34, 0xb3, 0x4d, 0x17, 0x9a, 0xe6, + 0xa4, 0xc8, 0x0c, 0xad, 0xcc, 0xbb, 0x7f, 0x0a, + ]; + + const INITIAL_SALT_DRAFT29: [u8; 20] = [ 0xaf, 0xbf, 0xec, 0x28, 0x99, 0x93, 0xd2, 0x4c, 0x9e, 0x97, 0x86, 0xf1, 0x9c, 0x61, 0x11, 0xe0, 0x43, 0x90, 0xa8, 0x99, ]; - const INITIAL_SALT_OLD: [u8; 20] = [ + const INITIAL_SALT_DRAFT27: [u8; 20] = [ 0xc3, 0xee, 0xf7, 0x12, 0xc7, 0x2e, 0xbb, 0x5a, 0x11, 0xa7, 0xd2, 0x43, 0x2b, 0xb4, 0x63, 0x65, 0xbe, 0xf9, 0xf5, 0x02, ]; let salt = match version { crate::PROTOCOL_VERSION_DRAFT27 | crate::PROTOCOL_VERSION_DRAFT28 => - &INITIAL_SALT_OLD, + &INITIAL_SALT_DRAFT27, + + crate::PROTOCOL_VERSION_DRAFT29 => &INITIAL_SALT_DRAFT29, _ => &INITIAL_SALT, }; let salt = hkdf::Salt::new(hkdf::HKDF_SHA256, salt); - Ok(salt.extract(secret)) + salt.extract(secret) } fn derive_client_initial_secret(prk: &hkdf::Prk, out: &mut [u8]) -> Result<()> { @@ -458,7 +465,86 @@ mod tests { use super::*; #[test] - fn derive_initial_secrets() { + fn derive_initial_secrets_v1() { + let dcid = [0x83, 0x94, 0xc8, 0xf0, 0x3e, 0x51, 0x57, 0x08]; + + let mut secret = [0; 32]; + let mut pkt_key = [0; 16]; + let mut pkt_iv = [0; 12]; + let mut hdr_key = [0; 16]; + + let aead = Algorithm::AES128_GCM; + + let initial_secret = + derive_initial_secret(&dcid, crate::PROTOCOL_VERSION_V1); + + // Client. + assert!( + derive_client_initial_secret(&initial_secret, &mut secret).is_ok() + ); + let expected_client_initial_secret = [ + 0xc0, 0x0c, 0xf1, 0x51, 0xca, 0x5b, 0xe0, 0x75, 0xed, 0x0e, 0xbf, + 0xb5, 0xc8, 0x03, 0x23, 0xc4, 0x2d, 0x6b, 0x7d, 0xb6, 0x78, 0x81, + 0x28, 0x9a, 0xf4, 0x00, 0x8f, 0x1f, 0x6c, 0x35, 0x7a, 0xea, + ]; + assert_eq!(&secret, &expected_client_initial_secret); + + assert!(derive_pkt_key(aead, &secret, &mut pkt_key).is_ok()); + let expected_client_pkt_key = [ + 0x1f, 0x36, 0x96, 0x13, 0xdd, 0x76, 0xd5, 0x46, 0x77, 0x30, 0xef, + 0xcb, 0xe3, 0xb1, 0xa2, 0x2d, + ]; + assert_eq!(&pkt_key, &expected_client_pkt_key); + + assert!(derive_pkt_iv(aead, &secret, &mut pkt_iv).is_ok()); + let expected_client_pkt_iv = [ + 0xfa, 0x04, 0x4b, 0x2f, 0x42, 0xa3, 0xfd, 0x3b, 0x46, 0xfb, 0x25, + 0x5c, + ]; + assert_eq!(&pkt_iv, &expected_client_pkt_iv); + + assert!(derive_hdr_key(aead, &secret, &mut hdr_key).is_ok()); + let expected_client_hdr_key = [ + 0x9f, 0x50, 0x44, 0x9e, 0x04, 0xa0, 0xe8, 0x10, 0x28, 0x3a, 0x1e, + 0x99, 0x33, 0xad, 0xed, 0xd2, + ]; + assert_eq!(&hdr_key, &expected_client_hdr_key); + + // Server. + assert!( + derive_server_initial_secret(&initial_secret, &mut secret).is_ok() + ); + let expected_server_initial_secret = [ + 0x3c, 0x19, 0x98, 0x28, 0xfd, 0x13, 0x9e, 0xfd, 0x21, 0x6c, 0x15, + 0x5a, 0xd8, 0x44, 0xcc, 0x81, 0xfb, 0x82, 0xfa, 0x8d, 0x74, 0x46, + 0xfa, 0x7d, 0x78, 0xbe, 0x80, 0x3a, 0xcd, 0xda, 0x95, 0x1b, + ]; + assert_eq!(&secret, &expected_server_initial_secret); + + assert!(derive_pkt_key(aead, &secret, &mut pkt_key).is_ok()); + let expected_server_pkt_key = [ + 0xcf, 0x3a, 0x53, 0x31, 0x65, 0x3c, 0x36, 0x4c, 0x88, 0xf0, 0xf3, + 0x79, 0xb6, 0x06, 0x7e, 0x37, + ]; + assert_eq!(&pkt_key, &expected_server_pkt_key); + + assert!(derive_pkt_iv(aead, &secret, &mut pkt_iv).is_ok()); + let expected_server_pkt_iv = [ + 0x0a, 0xc1, 0x49, 0x3c, 0xa1, 0x90, 0x58, 0x53, 0xb0, 0xbb, 0xa0, + 0x3e, + ]; + assert_eq!(&pkt_iv, &expected_server_pkt_iv); + + assert!(derive_hdr_key(aead, &secret, &mut hdr_key).is_ok()); + let expected_server_hdr_key = [ + 0xc2, 0x06, 0xb8, 0xd9, 0xb9, 0xf0, 0xf3, 0x76, 0x44, 0x43, 0x0b, + 0x49, 0x0e, 0xea, 0xa3, 0x14, + ]; + assert_eq!(&hdr_key, &expected_server_hdr_key); + } + + #[test] + fn derive_initial_secrets_draft29() { let dcid = [0x83, 0x94, 0xc8, 0xf0, 0x3e, 0x51, 0x57, 0x08]; let mut secret = [0; 32]; @@ -469,7 +555,7 @@ mod tests { let aead = Algorithm::AES128_GCM; let initial_secret = - derive_initial_secret(&dcid, crate::PROTOCOL_VERSION).unwrap(); + derive_initial_secret(&dcid, crate::PROTOCOL_VERSION_DRAFT29); // Client. assert!( @@ -537,7 +623,7 @@ mod tests { } #[test] - fn derive_initial_secrets_old() { + fn derive_initial_secrets_draft27() { let dcid = [0x83, 0x94, 0xc8, 0xf0, 0x3e, 0x51, 0x57, 0x08]; let mut secret = [0; 32]; @@ -548,8 +634,7 @@ mod tests { let aead = Algorithm::AES128_GCM; let initial_secret = - derive_initial_secret(&dcid, crate::PROTOCOL_VERSION_DRAFT28) - .unwrap(); + derive_initial_secret(&dcid, crate::PROTOCOL_VERSION_DRAFT27); // Client. assert!( diff --git a/src/dgram.rs b/src/dgram.rs index 755d95f..023df5f 100644 --- a/src/dgram.rs +++ b/src/dgram.rs @@ -99,6 +99,10 @@ impl DatagramQueue { self.queue.len() == self.queue_max_len } + pub fn len(&self) -> usize { + self.queue.len() + } + pub fn byte_size(&self) -> usize { self.queue_bytes_size } @@ -29,6 +29,8 @@ use std::ptr; use std::slice; use std::sync::atomic; +use std::net::SocketAddr; + #[cfg(unix)] use std::os::unix::io::FromRawFd; @@ -36,13 +38,45 @@ use libc::c_char; use libc::c_int; use libc::c_void; use libc::size_t; +use libc::sockaddr; use libc::ssize_t; +use libc::timespec; + +#[cfg(not(windows))] +use libc::sockaddr_in; +#[cfg(windows)] +use winapi::shared::ws2def::SOCKADDR_IN as sockaddr_in; + +#[cfg(not(windows))] +use libc::sockaddr_in6; +#[cfg(windows)] +use winapi::shared::ws2ipdef::SOCKADDR_IN6_LH as sockaddr_in6; + +#[cfg(not(windows))] +use libc::sockaddr_storage; +#[cfg(windows)] +use winapi::shared::ws2def::SOCKADDR_STORAGE_LH as sockaddr_storage; + +#[cfg(windows)] +use libc::c_int as socklen_t; +#[cfg(not(windows))] +use libc::socklen_t; + +#[cfg(not(windows))] +use libc::AF_INET; +#[cfg(windows)] +use winapi::shared::ws2def::AF_INET; + +#[cfg(not(windows))] +use libc::AF_INET6; +#[cfg(windows)] +use winapi::shared::ws2def::AF_INET6; use crate::*; #[no_mangle] pub extern fn quiche_version() -> *const u8 { - //static VERSION: &str = concat!(env!("CARGO_PKG_VERSION"), "\0"); + //static VERSION: &str = concat!("0.9.0", "\0"); // ANDROID's build system doesn't support environment variables // so we hardcode the package version here. static VERSION: &str = concat!("0.6.0", "\0"); @@ -119,6 +153,19 @@ pub extern fn quiche_config_load_priv_key_from_pem_file( } #[no_mangle] +pub extern fn quiche_config_load_verify_locations_from_file( + config: &mut Config, path: *const c_char, +) -> c_int { + let path = unsafe { ffi::CStr::from_ptr(path).to_str().unwrap() }; + + match config.load_verify_locations_from_file(path) { + Ok(_) => 0, + + Err(e) => e.to_c() as c_int, + } +} + +#[no_mangle] pub extern fn quiche_config_verify_peer(config: &mut Config, v: bool) { config.verify_peer(v); } @@ -157,10 +204,10 @@ pub extern fn quiche_config_set_max_idle_timeout(config: &mut Config, v: u64) { } #[no_mangle] -pub extern fn quiche_config_set_max_udp_payload_size( - config: &mut Config, v: u64, +pub extern fn quiche_config_set_max_recv_udp_payload_size( + config: &mut Config, v: size_t, ) { - config.set_max_udp_payload_size(v); + config.set_max_recv_udp_payload_size(v); } #[no_mangle] @@ -253,6 +300,13 @@ pub extern fn quiche_config_enable_dgram( } #[no_mangle] +pub extern fn quiche_config_set_max_send_udp_payload_size( + config: &mut Config, v: size_t, +) { + config.set_max_send_udp_payload_size(v); +} + +#[no_mangle] pub extern fn quiche_config_free(config: *mut Config) { unsafe { Box::from_raw(config) }; } @@ -325,17 +379,22 @@ pub extern fn quiche_header_info( #[no_mangle] pub extern fn quiche_accept( scid: *const u8, scid_len: size_t, odcid: *const u8, odcid_len: size_t, - config: &mut Config, + from: &sockaddr, from_len: socklen_t, config: &mut Config, ) -> *mut Connection { let scid = unsafe { slice::from_raw_parts(scid, scid_len) }; + let scid = ConnectionId::from_ref(scid); - let odcid = if !odcid.is_null() || odcid_len == 0 { - Some(unsafe { slice::from_raw_parts(odcid, odcid_len) }) + let odcid = if !odcid.is_null() && odcid_len > 0 { + Some(ConnectionId::from_ref(unsafe { + slice::from_raw_parts(odcid, odcid_len) + })) } else { None }; - match accept(scid, odcid, config) { + let from = std_addr_from_c(from, from_len); + + match accept(&scid, odcid.as_ref(), from, config) { Ok(c) => Box::into_raw(Pin::into_inner(c)), Err(_) => ptr::null_mut(), @@ -344,8 +403,8 @@ pub extern fn quiche_accept( #[no_mangle] pub extern fn quiche_connect( - server_name: *const c_char, scid: *const u8, scid_len: size_t, - config: &mut Config, + server_name: *const c_char, scid: *const u8, scid_len: size_t, to: &sockaddr, + to_len: socklen_t, config: &mut Config, ) -> *mut Connection { let server_name = if server_name.is_null() { None @@ -354,8 +413,11 @@ pub extern fn quiche_connect( }; let scid = unsafe { slice::from_raw_parts(scid, scid_len) }; + let scid = ConnectionId::from_ref(scid); + + let to = std_addr_from_c(to, to_len); - match connect(server_name, scid, config) { + match connect(server_name, &scid, to, config) { Ok(c) => Box::into_raw(Pin::into_inner(c)), Err(_) => ptr::null_mut(), @@ -368,10 +430,14 @@ pub extern fn quiche_negotiate_version( out: *mut u8, out_len: size_t, ) -> ssize_t { let scid = unsafe { slice::from_raw_parts(scid, scid_len) }; + let scid = ConnectionId::from_ref(scid); + let dcid = unsafe { slice::from_raw_parts(dcid, dcid_len) }; + let dcid = ConnectionId::from_ref(dcid); + let out = unsafe { slice::from_raw_parts_mut(out, out_len) }; - match negotiate_version(scid, dcid, out) { + match negotiate_version(&scid, &dcid, out) { Ok(v) => v as ssize_t, Err(e) => e.to_c(), @@ -390,12 +456,18 @@ pub extern fn quiche_retry( token_len: size_t, version: u32, out: *mut u8, out_len: size_t, ) -> ssize_t { let scid = unsafe { slice::from_raw_parts(scid, scid_len) }; + let scid = ConnectionId::from_ref(scid); + let dcid = unsafe { slice::from_raw_parts(dcid, dcid_len) }; + let dcid = ConnectionId::from_ref(dcid); + let new_scid = unsafe { slice::from_raw_parts(new_scid, new_scid_len) }; + let new_scid = ConnectionId::from_ref(new_scid); + let token = unsafe { slice::from_raw_parts(token, token_len) }; let out = unsafe { slice::from_raw_parts_mut(out, out_len) }; - match retry(scid, dcid, new_scid, token, version, out) { + match retry(&scid, &dcid, &new_scid, token, version, out) { Ok(v) => v as ssize_t, Err(e) => e.to_c(), @@ -405,19 +477,32 @@ pub extern fn quiche_retry( #[no_mangle] pub extern fn quiche_conn_new_with_tls( scid: *const u8, scid_len: size_t, odcid: *const u8, odcid_len: size_t, - config: &mut Config, ssl: *mut c_void, is_server: bool, + peer: &sockaddr, peer_len: socklen_t, config: &mut Config, ssl: *mut c_void, + is_server: bool, ) -> *mut Connection { let scid = unsafe { slice::from_raw_parts(scid, scid_len) }; + let scid = ConnectionId::from_ref(scid); - let odcid = if !odcid.is_null() || odcid_len == 0 { - Some(unsafe { slice::from_raw_parts(odcid, odcid_len) }) + let odcid = if !odcid.is_null() && odcid_len > 0 { + Some(ConnectionId::from_ref(unsafe { + slice::from_raw_parts(odcid, odcid_len) + })) } else { None }; + let peer = std_addr_from_c(peer, peer_len); + let tls = unsafe { tls::Handshake::from_ptr(ssl) }; - match Connection::with_tls(scid, odcid, config, tls, is_server) { + match Connection::with_tls( + &scid, + odcid.as_ref(), + peer, + config, + tls, + is_server, + ) { Ok(c) => Box::into_raw(Pin::into_inner(c)), Err(_) => ptr::null_mut(), @@ -506,8 +591,35 @@ pub extern fn quiche_conn_set_qlog_fd( } #[no_mangle] +pub extern fn quiche_conn_set_session( + conn: &mut Connection, buf: *const u8, buf_len: size_t, +) -> c_int { + let buf = unsafe { slice::from_raw_parts(buf, buf_len) }; + + match conn.set_session(buf) { + Ok(_) => 0, + + Err(e) => e.to_c() as c_int, + } +} + +#[repr(C)] +pub struct RecvInfo<'a> { + from: &'a sockaddr, + from_len: socklen_t, +} + +impl<'a> From<&RecvInfo<'a>> for crate::RecvInfo { + fn from(info: &RecvInfo) -> crate::RecvInfo { + crate::RecvInfo { + from: std_addr_from_c(info.from, info.from_len), + } + } +} + +#[no_mangle] pub extern fn quiche_conn_recv( - conn: &mut Connection, buf: *mut u8, buf_len: size_t, + conn: &mut Connection, buf: *mut u8, buf_len: size_t, info: &RecvInfo, ) -> ssize_t { if buf_len > <ssize_t>::max_value() as usize { panic!("The provided buffer is too large"); @@ -515,16 +627,24 @@ pub extern fn quiche_conn_recv( let buf = unsafe { slice::from_raw_parts_mut(buf, buf_len) }; - match conn.recv(buf) { + match conn.recv(buf, info.into()) { Ok(v) => v as ssize_t, Err(e) => e.to_c(), } } +#[repr(C)] +pub struct SendInfo { + to: sockaddr_storage, + to_len: socklen_t, + + at: timespec, +} + #[no_mangle] pub extern fn quiche_conn_send( - conn: &mut Connection, out: *mut u8, out_len: size_t, + conn: &mut Connection, out: *mut u8, out_len: size_t, out_info: &mut SendInfo, ) -> ssize_t { if out_len > <ssize_t>::max_value() as usize { panic!("The provided buffer is too large"); @@ -533,7 +653,13 @@ pub extern fn quiche_conn_send( let out = unsafe { slice::from_raw_parts_mut(out, out_len) }; match conn.send(out) { - Ok(v) => v as ssize_t, + Ok((v, info)) => { + out_info.to_len = std_addr_to_c(&info.to, &mut out_info.to); + + std_time_to_c(&info.at, &mut out_info.at); + + v as ssize_t + }, Err(e) => e.to_c(), } @@ -580,6 +706,17 @@ pub extern fn quiche_conn_stream_send( } #[no_mangle] +pub extern fn quiche_conn_stream_priority( + conn: &mut Connection, stream_id: u64, urgency: u8, incremental: bool, +) -> c_int { + match conn.stream_priority(stream_id, urgency, incremental) { + Ok(_) => 0, + + Err(e) => e.to_c() as c_int, + } +} + +#[no_mangle] pub extern fn quiche_conn_stream_shutdown( conn: &mut Connection, stream_id: u64, direction: Shutdown, err: u64, ) -> c_int { @@ -602,6 +739,13 @@ pub extern fn quiche_conn_stream_capacity( } #[no_mangle] +pub extern fn quiche_conn_stream_readable( + conn: &mut Connection, stream_id: u64, +) -> bool { + conn.stream_readable(stream_id) +} + +#[no_mangle] pub extern fn quiche_conn_stream_finished( conn: &mut Connection, stream_id: u64, ) -> bool { @@ -618,8 +762,19 @@ pub extern fn quiche_conn_writable(conn: &Connection) -> *mut StreamIter { Box::into_raw(Box::new(conn.writable())) } +#[no_mangle] +pub extern fn quiche_conn_max_send_udp_payload_size(conn: &Connection) -> usize { + conn.max_send_udp_payload_size() +} + +#[no_mangle] +pub extern fn quiche_conn_is_readable(conn: &Connection) -> bool { + conn.is_readable() +} + struct AppData(*mut c_void); unsafe impl Send for AppData {} +unsafe impl Sync for AppData {} #[no_mangle] pub extern fn quiche_conn_stream_init_application_data( @@ -681,6 +836,37 @@ pub extern fn quiche_conn_on_timeout(conn: &mut Connection) { } #[no_mangle] +pub extern fn quiche_conn_trace_id( + conn: &mut Connection, out: &mut *const u8, out_len: &mut size_t, +) { + let trace_id = conn.trace_id(); + + *out = trace_id.as_ptr(); + *out_len = trace_id.len(); +} + +#[no_mangle] +pub extern fn quiche_conn_source_id( + conn: &mut Connection, out: &mut *const u8, out_len: &mut size_t, +) { + let conn_id = conn.source_id(); + let id = conn_id.as_ref(); + *out = id.as_ptr(); + *out_len = id.len(); +} + +#[no_mangle] +pub extern fn quiche_conn_destination_id( + conn: &mut Connection, out: &mut *const u8, out_len: &mut size_t, +) { + let conn_id = conn.destination_id(); + let id = conn_id.as_ref(); + + *out = id.as_ptr(); + *out_len = id.len(); +} + +#[no_mangle] pub extern fn quiche_conn_application_proto( conn: &mut Connection, out: &mut *const u8, out_len: &mut size_t, ) { @@ -691,6 +877,20 @@ pub extern fn quiche_conn_application_proto( } #[no_mangle] +pub extern fn quiche_conn_session( + conn: &mut Connection, out: &mut *const u8, out_len: &mut size_t, +) { + match conn.session() { + Some(session) => { + *out = session.as_ptr(); + *out_len = session.len(); + }, + + None => *out_len = 0, + } +} + +#[no_mangle] pub extern fn quiche_conn_is_established(conn: &mut Connection) -> bool { conn.is_established() } @@ -701,11 +901,35 @@ pub extern fn quiche_conn_is_in_early_data(conn: &mut Connection) -> bool { } #[no_mangle] +pub extern fn quiche_conn_is_draining(conn: &mut Connection) -> bool { + conn.is_draining() +} + +#[no_mangle] pub extern fn quiche_conn_is_closed(conn: &mut Connection) -> bool { conn.is_closed() } #[no_mangle] +pub extern fn quiche_conn_peer_error( + conn: &mut Connection, is_app: *mut bool, error_code: *mut u64, + reason: &mut *const u8, reason_len: &mut size_t, +) -> bool { + match &conn.peer_error { + Some(conn_err) => unsafe { + *is_app = conn_err.is_app; + *error_code = conn_err.error_code; + *reason = conn_err.reason.as_ptr(); + *reason_len = conn_err.reason.len(); + + true + }, + + None => false, + } +} + +#[no_mangle] pub extern fn quiche_stream_iter_next( iter: &mut StreamIter, stream_id: *mut u64, ) -> bool { @@ -724,12 +948,12 @@ pub extern fn quiche_stream_iter_free(iter: *mut StreamIter) { #[repr(C)] pub struct Stats { - pub recv: usize, - pub sent: usize, - pub lost: usize, - pub rtt: u64, - pub cwnd: usize, - pub delivery_rate: u64, + recv: usize, + sent: usize, + lost: usize, + rtt: u64, + cwnd: usize, + delivery_rate: u64, } #[no_mangle] @@ -754,6 +978,39 @@ pub extern fn quiche_conn_dgram_max_writable_len(conn: &Connection) -> ssize_t { } #[no_mangle] +pub extern fn quiche_conn_dgram_recv_front_len(conn: &Connection) -> ssize_t { + match conn.dgram_recv_front_len() { + None => Error::Done.to_c(), + + Some(v) => v as ssize_t, + } +} + +#[no_mangle] +pub extern fn quiche_conn_dgram_recv_queue_len(conn: &Connection) -> ssize_t { + conn.dgram_recv_queue_len() as ssize_t +} + +#[no_mangle] +pub extern fn quiche_conn_dgram_recv_queue_byte_size( + conn: &Connection, +) -> ssize_t { + conn.dgram_recv_queue_byte_size() as ssize_t +} + +#[no_mangle] +pub extern fn quiche_conn_dgram_send_queue_len(conn: &Connection) -> ssize_t { + conn.dgram_send_queue_len() as ssize_t +} + +#[no_mangle] +pub extern fn quiche_conn_dgram_send_queue_byte_size( + conn: &Connection, +) -> ssize_t { + conn.dgram_send_queue_byte_size() as ssize_t +} + +#[no_mangle] pub extern fn quiche_conn_dgram_send( conn: &mut Connection, buf: *const u8, buf_len: size_t, ) -> ssize_t { @@ -805,3 +1062,79 @@ pub extern fn quiche_conn_dgram_purge_outgoing( pub extern fn quiche_conn_free(conn: *mut Connection) { unsafe { Box::from_raw(conn) }; } + +#[no_mangle] +pub extern fn quiche_conn_peer_streams_left_bidi(conn: &mut Connection) -> u64 { + conn.peer_streams_left_bidi() +} + +#[no_mangle] +pub extern fn quiche_conn_peer_streams_left_uni(conn: &mut Connection) -> u64 { + conn.peer_streams_left_uni() +} + +fn std_addr_from_c(addr: &sockaddr, addr_len: socklen_t) -> SocketAddr { + unsafe { + match addr.sa_family as i32 { + AF_INET => { + assert!(addr_len as usize == std::mem::size_of::<sockaddr_in>()); + + SocketAddr::V4( + *(addr as *const _ as *const sockaddr_in as *const _), + ) + }, + + AF_INET6 => { + assert!(addr_len as usize == std::mem::size_of::<sockaddr_in6>()); + + SocketAddr::V6( + *(addr as *const _ as *const sockaddr_in6 as *const _), + ) + }, + + _ => unimplemented!("unsupported address type"), + } + } +} + +fn std_addr_to_c(addr: &SocketAddr, out: &mut sockaddr_storage) -> socklen_t { + unsafe { + match addr { + SocketAddr::V4(addr) => { + let sa_len = std::mem::size_of::<sockaddr_in>(); + + let src = addr as *const _ as *const u8; + let dst = out as *mut _ as *mut u8; + + std::ptr::copy_nonoverlapping(src, dst, sa_len); + + sa_len as socklen_t + }, + + SocketAddr::V6(addr) => { + let sa_len = std::mem::size_of::<sockaddr_in6>(); + + let src = addr as *const _ as *const u8; + let dst = out as *mut _ as *mut u8; + + std::ptr::copy_nonoverlapping(src, dst, sa_len); + + sa_len as socklen_t + }, + } + } +} + +#[cfg(not(any(target_os = "macos", target_os = "ios", target_os = "windows")))] +fn std_time_to_c(time: &std::time::Instant, out: &mut timespec) { + unsafe { + ptr::copy_nonoverlapping(time as *const _ as *const timespec, out, 1) + } +} + +#[cfg(any(target_os = "macos", target_os = "ios", target_os = "windows"))] +fn std_time_to_c(_time: &std::time::Instant, out: &mut timespec) { + // TODO: implement Instant conversion for systems that don't use timespec. + out.tv_sec = 0; + out.tv_nsec = 0; +} diff --git a/src/frame.rs b/src/frame.rs index b191a42..c0cba7e 100644 --- a/src/frame.rs +++ b/src/frame.rs @@ -65,6 +65,11 @@ pub enum Frame { data: stream::RangeBuf, }, + CryptoHeader { + offset: u64, + length: usize, + }, + NewToken { token: Vec<u8>, }, @@ -74,6 +79,13 @@ pub enum Frame { data: stream::RangeBuf, }, + StreamHeader { + stream_id: u64, + offset: u64, + length: usize, + fin: bool, + }, + MaxData { max: u64, }, @@ -368,13 +380,13 @@ impl Frame { }, Frame::Crypto { data } => { - b.put_varint(0x06)?; + encode_crypto_header(data.off() as u64, data.len() as u64, b)?; - b.put_varint(data.off() as u64)?; - b.put_varint(data.len() as u64)?; b.put_bytes(&data)?; }, + Frame::CryptoHeader { .. } => (), + Frame::NewToken { token } => { b.put_varint(0x07)?; @@ -383,26 +395,19 @@ impl Frame { }, Frame::Stream { stream_id, data } => { - let mut ty: u8 = 0x08; - - // Always encode offset - ty |= 0x04; - - // Always encode length - ty |= 0x02; - - if data.fin() { - ty |= 0x01; - } - - b.put_varint(u64::from(ty))?; + encode_stream_header( + *stream_id, + data.off() as u64, + data.len() as u64, + data.fin(), + b, + )?; - b.put_varint(*stream_id)?; - b.put_varint(data.off() as u64)?; - b.put_varint(data.len() as u64)?; - b.put_bytes(data.as_ref())?; + b.put_bytes(&data)?; }, + Frame::StreamHeader { .. } => (), + Frame::MaxData { max } => { b.put_varint(0x10)?; @@ -583,10 +588,17 @@ impl Frame { Frame::Crypto { data } => { 1 + // frame type octets::varint_len(data.off() as u64) + // offset - octets::varint_len(data.len() as u64) + // length + 2 + // length, always encode as 2-byte varint data.len() // data }, + Frame::CryptoHeader { offset, length, .. } => { + 1 + // frame type + octets::varint_len(*offset) + // offset + 2 + // length, always encode as 2-byte varint + length // data + }, + Frame::NewToken { token } => { 1 + // frame type octets::varint_len(token.len() as u64) + // token length @@ -597,10 +609,23 @@ impl Frame { 1 + // frame type octets::varint_len(*stream_id) + // stream_id octets::varint_len(data.off() as u64) + // offset - octets::varint_len(data.len() as u64) + // length + 2 + // length, always encode as 2-byte varint data.len() // data }, + Frame::StreamHeader { + stream_id, + offset, + length, + .. + } => { + 1 + // frame type + octets::varint_len(*stream_id) + // stream_id + octets::varint_len(*offset) + // offset + 2 + // length, always encode as 2-byte varint + length // data + }, + Frame::MaxData { max } => { 1 + // frame type octets::varint_len(*max) // max @@ -706,10 +731,13 @@ impl Frame { pub fn ack_eliciting(&self) -> bool { // Any other frame is ack-eliciting (note the `!`). - !matches!(self, Frame::Padding { .. } | - Frame::ACK { .. } | - Frame::ApplicationClose { .. } | - Frame::ConnectionClose { .. }) + !matches!( + self, + Frame::Padding { .. } | + Frame::ACK { .. } | + Frame::ApplicationClose { .. } | + Frame::ConnectionClose { .. } + ) } pub fn shrink_for_retransmission(&mut self) { @@ -758,6 +786,9 @@ impl Frame { data.len().to_string(), ), + Frame::CryptoHeader { offset, length } => + qlog::QuicFrame::crypto(offset.to_string(), length.to_string()), + Frame::NewToken { token } => qlog::QuicFrame::new_token( token.len().to_string(), "TODO: https://github.com/quiclog/internet-drafts/issues/36" @@ -772,6 +803,19 @@ impl Frame { None, ), + Frame::StreamHeader { + stream_id, + offset, + length, + fin, + } => qlog::QuicFrame::stream( + stream_id.to_string(), + offset.to_string(), + length.to_string(), + *fin, + None, + ), + Frame::MaxData { max } => qlog::QuicFrame::max_data(max.to_string()), Frame::MaxStreamData { stream_id, max } => @@ -864,7 +908,8 @@ impl Frame { Frame::HandshakeDone => qlog::QuicFrame::handshake_done(), - Frame::Datagram { .. } => qlog::QuicFrame::unknown(0x30), + Frame::Datagram { data } => + qlog::QuicFrame::datagram(data.len().to_string(), None), } } } @@ -911,6 +956,10 @@ impl std::fmt::Debug for Frame { write!(f, "CRYPTO off={} len={}", data.off(), data.len())?; }, + Frame::CryptoHeader { offset, length } => { + write!(f, "CRYPTO off={} len={}", offset, length)?; + }, + Frame::NewToken { .. } => { write!(f, "NEW_TOKEN (TODO)")?; }, @@ -926,6 +975,19 @@ impl std::fmt::Debug for Frame { )?; }, + Frame::StreamHeader { + stream_id, + offset, + length, + fin, + } => { + write!( + f, + "STREAM id={} off={} len={} fin={}", + stream_id, offset, length, fin + )?; + }, + Frame::MaxData { max } => { write!(f, "MAX_DATA max={}", max)?; }, @@ -1051,6 +1113,46 @@ fn parse_ack_frame(_ty: u64, b: &mut octets::Octets) -> Result<Frame> { Ok(Frame::ACK { ack_delay, ranges }) } +pub fn encode_crypto_header( + offset: u64, length: u64, b: &mut octets::OctetsMut, +) -> Result<()> { + b.put_varint(0x06)?; + + b.put_varint(offset)?; + + // Always encode length field as 2-byte varint. + b.put_varint_with_len(length, 2)?; + + Ok(()) +} + +pub fn encode_stream_header( + stream_id: u64, offset: u64, length: u64, fin: bool, + b: &mut octets::OctetsMut, +) -> Result<()> { + let mut ty: u8 = 0x08; + + // Always encode offset. + ty |= 0x04; + + // Always encode length. + ty |= 0x02; + + if fin { + ty |= 0x01; + } + + b.put_varint(u64::from(ty))?; + + b.put_varint(stream_id)?; + b.put_varint(offset)?; + + // Always encode length field as 2-byte varint. + b.put_varint_with_len(length, 2)?; + + Ok(()) +} + fn parse_stream_frame(ty: u64, b: &mut octets::Octets) -> Result<Frame> { let first = ty as u8; @@ -1262,7 +1364,7 @@ mod tests { frame.to_bytes(&mut b).unwrap() }; - assert_eq!(wire_len, 18); + assert_eq!(wire_len, 19); let mut b = octets::Octets::with_slice(&d); assert_eq!(Frame::from_bytes(&mut b, packet::Type::Short), Ok(frame)); @@ -1321,7 +1423,7 @@ mod tests { frame.to_bytes(&mut b).unwrap() }; - assert_eq!(wire_len, 19); + assert_eq!(wire_len, 20); let mut b = octets::Octets::with_slice(&d); assert_eq!(Frame::from_bytes(&mut b, packet::Type::Short), Ok(frame)); @@ -1352,7 +1454,7 @@ mod tests { frame.to_bytes(&mut b).unwrap() }; - assert_eq!(wire_len, 23); + assert_eq!(wire_len, 24); let mut b = octets::Octets::with_slice(&d); assert_eq!( diff --git a/src/h3/ffi.rs b/src/h3/ffi.rs index 2d278c7..fc254b0 100644 --- a/src/h3/ffi.rs +++ b/src/h3/ffi.rs @@ -27,7 +27,6 @@ use std::ffi; use std::ptr; use std::slice; -use std::str; use libc::c_char; use libc::c_int; @@ -264,6 +263,13 @@ pub extern fn quiche_h3_recv_body( } #[no_mangle] +pub extern fn quiche_h3_dgram_enabled_by_peer( + conn: &h3::Connection, quic_conn: &Connection, +) -> bool { + conn.dgram_enabled_by_peer(quic_conn) +} + +#[no_mangle] pub extern fn quiche_h3_send_dgram( conn: &mut h3::Connection, quic_conn: &mut Connection, flow_id: u64, data: *const u8, data_len: size_t, @@ -317,15 +323,8 @@ fn headers_from_ptr<'a>( for h in headers { out.push({ - let name = unsafe { - let slice = slice::from_raw_parts(h.name, h.name_len); - str::from_utf8_unchecked(slice) - }; - - let value = unsafe { - let slice = slice::from_raw_parts(h.value, h.value_len); - str::from_utf8_unchecked(slice) - }; + let name = unsafe { slice::from_raw_parts(h.name, h.name_len) }; + let value = unsafe { slice::from_raw_parts(h.value, h.value_len) }; h3::HeaderRef::new(name, value) }); diff --git a/src/h3/frame.rs b/src/h3/frame.rs index bee2b3b..085524b 100644 --- a/src/h3/frame.rs +++ b/src/h3/frame.rs @@ -39,6 +39,10 @@ pub const MAX_PUSH_FRAME_TYPE_ID: u64 = 0xD; const SETTINGS_QPACK_MAX_TABLE_CAPACITY: u64 = 0x1; const SETTINGS_MAX_HEADER_LIST_SIZE: u64 = 0x6; const SETTINGS_QPACK_BLOCKED_STREAMS: u64 = 0x7; +const SETTINGS_H3_DATAGRAM: u64 = 0x276; + +// Permit between 16 maximally-encoded and 128 minimally-encoded SETTINGS. +const MAX_SETTINGS_PAYLOAD_SIZE: usize = 256; #[derive(Clone, PartialEq)] pub enum Frame { @@ -58,6 +62,7 @@ pub enum Frame { max_header_list_size: Option<u64>, qpack_max_table_capacity: Option<u64>, qpack_blocked_streams: Option<u64>, + h3_datagram: Option<u64>, grease: Option<(u64, u64)>, }, @@ -146,6 +151,7 @@ impl Frame { max_header_list_size, qpack_max_table_capacity, qpack_blocked_streams, + h3_datagram, grease, } => { let mut len = 0; @@ -165,6 +171,11 @@ impl Frame { len += octets::varint_len(*val); } + if let Some(val) = h3_datagram { + len += octets::varint_len(SETTINGS_H3_DATAGRAM); + len += octets::varint_len(*val); + } + if let Some(val) = grease { len += octets::varint_len(val.0); len += octets::varint_len(val.1); @@ -188,6 +199,11 @@ impl Frame { b.put_varint(*val as u64)?; } + if let Some(val) = h3_datagram { + b.put_varint(SETTINGS_H3_DATAGRAM)?; + b.put_varint(*val as u64)?; + } + if let Some(val) = grease { b.put_varint(val.0)?; b.put_varint(val.1)?; @@ -286,6 +302,12 @@ fn parse_settings_frame( let mut max_header_list_size = None; let mut qpack_max_table_capacity = None; let mut qpack_blocked_streams = None; + let mut h3_datagram = None; + + // Reject SETTINGS frames that are too long. + if settings_length > MAX_SETTINGS_PAYLOAD_SIZE { + return Err(super::Error::ExcessiveLoad); + } while b.off() < settings_length { let setting_ty = b.get_varint()?; @@ -304,6 +326,18 @@ fn parse_settings_frame( qpack_blocked_streams = Some(settings_val); }, + SETTINGS_H3_DATAGRAM => { + if settings_val > 1 { + return Err(super::Error::SettingsError); + } + + h3_datagram = Some(settings_val); + }, + + // Reserved values overlap with HTTP/2 and MUST be rejected + 0x0 | 0x2 | 0x3 | 0x4 | 0x5 => + return Err(super::Error::SettingsError), + // Unknown Settings parameters must be ignored. _ => (), } @@ -313,6 +347,7 @@ fn parse_settings_frame( max_header_list_size, qpack_max_table_capacity, qpack_blocked_streams, + h3_datagram, grease: None, }) } @@ -425,10 +460,11 @@ mod tests { max_header_list_size: Some(0), qpack_max_table_capacity: Some(0), qpack_blocked_streams: Some(0), + h3_datagram: Some(0), grease: None, }; - let frame_payload_len = 6; + let frame_payload_len = 9; let frame_header_len = 2; let wire_len = { @@ -457,6 +493,7 @@ mod tests { max_header_list_size: Some(0), qpack_max_table_capacity: Some(0), qpack_blocked_streams: Some(0), + h3_datagram: Some(0), grease: Some((33, 33)), }; @@ -465,10 +502,11 @@ mod tests { max_header_list_size: Some(0), qpack_max_table_capacity: Some(0), qpack_blocked_streams: Some(0), + h3_datagram: Some(0), grease: None, }; - let frame_payload_len = 8; + let frame_payload_len = 11; let frame_header_len = 2; let wire_len = { @@ -497,6 +535,7 @@ mod tests { max_header_list_size: Some(1024), qpack_max_table_capacity: None, qpack_blocked_streams: None, + h3_datagram: None, grease: None, }; @@ -522,6 +561,71 @@ mod tests { } #[test] + fn settings_h3_dgram_only() { + let mut d = [42; 128]; + + let frame = Frame::Settings { + max_header_list_size: None, + qpack_max_table_capacity: None, + qpack_blocked_streams: None, + h3_datagram: Some(1), + grease: None, + }; + + let frame_payload_len = 3; + let frame_header_len = 2; + + let wire_len = { + let mut b = octets::OctetsMut::with_slice(&mut d); + frame.to_bytes(&mut b).unwrap() + }; + + assert_eq!(wire_len, frame_header_len + frame_payload_len); + + assert_eq!( + Frame::from_bytes( + SETTINGS_FRAME_TYPE_ID, + frame_payload_len as u64, + &d[frame_header_len..] + ) + .unwrap(), + frame + ); + } + + #[test] + fn settings_h3_dgram_bad() { + let mut d = [42; 128]; + + let frame = Frame::Settings { + max_header_list_size: None, + qpack_max_table_capacity: None, + qpack_blocked_streams: None, + h3_datagram: Some(5), + grease: None, + }; + + let frame_payload_len = 3; + let frame_header_len = 2; + + let wire_len = { + let mut b = octets::OctetsMut::with_slice(&mut d); + frame.to_bytes(&mut b).unwrap() + }; + + assert_eq!(wire_len, frame_header_len + frame_payload_len); + + assert_eq!( + Frame::from_bytes( + SETTINGS_FRAME_TYPE_ID, + frame_payload_len as u64, + &d[frame_header_len..] + ), + Err(crate::h3::Error::SettingsError) + ); + } + + #[test] fn settings_qpack_only() { let mut d = [42; 128]; @@ -529,6 +633,7 @@ mod tests { max_header_list_size: None, qpack_max_table_capacity: Some(0), qpack_blocked_streams: Some(0), + h3_datagram: None, grease: None, }; @@ -554,6 +659,99 @@ mod tests { } #[test] + fn settings_h2_prohibited() { + // We need to test the prohibited values (0x0 | 0x2 | 0x3 | 0x4 | 0x5) + // but the quiche API doesn't support that, so use a manually created + // frame data buffer where d[frame_header_len] is the SETTING type field. + let frame_payload_len = 2u64; + let frame_header_len = 2; + let mut d = [ + SETTINGS_FRAME_TYPE_ID as u8, + frame_payload_len as u8, + 0x0, + 1, + ]; + + assert_eq!( + Frame::from_bytes( + SETTINGS_FRAME_TYPE_ID, + frame_payload_len, + &d[frame_header_len..] + ), + Err(crate::h3::Error::SettingsError) + ); + + d[frame_header_len] = 0x2; + + assert_eq!( + Frame::from_bytes( + SETTINGS_FRAME_TYPE_ID, + frame_payload_len, + &d[frame_header_len..] + ), + Err(crate::h3::Error::SettingsError) + ); + + d[frame_header_len] = 0x3; + + assert_eq!( + Frame::from_bytes( + SETTINGS_FRAME_TYPE_ID, + frame_payload_len, + &d[frame_header_len..] + ), + Err(crate::h3::Error::SettingsError) + ); + + d[frame_header_len] = 0x4; + + assert_eq!( + Frame::from_bytes( + SETTINGS_FRAME_TYPE_ID, + frame_payload_len, + &d[frame_header_len..] + ), + Err(crate::h3::Error::SettingsError) + ); + + d[frame_header_len] = 0x5; + + assert_eq!( + Frame::from_bytes( + SETTINGS_FRAME_TYPE_ID, + frame_payload_len, + &d[frame_header_len..] + ), + Err(crate::h3::Error::SettingsError) + ); + } + + #[test] + fn settings_too_big() { + // We need to test a SETTINGS frame that exceeds + // MAX_SETTINGS_PAYLOAD_SIZE, so just craft a special buffer that look + // likes the frame. The payload content doesn't matter since quiche + // should abort before then. + let frame_payload_len = MAX_SETTINGS_PAYLOAD_SIZE + 1; + let frame_header_len = 2; + let d = [ + SETTINGS_FRAME_TYPE_ID as u8, + frame_payload_len as u8, + 0x1, + 1, + ]; + + assert_eq!( + Frame::from_bytes( + SETTINGS_FRAME_TYPE_ID, + frame_payload_len as u64, + &d[frame_header_len..] + ), + Err(crate::h3::Error::ExcessiveLoad) + ); + } + + #[test] fn push_promise() { let mut d = [42; 128]; diff --git a/src/h3/mod.rs b/src/h3/mod.rs index 6248688..dd2a51a 100644 --- a/src/h3/mod.rs +++ b/src/h3/mod.rs @@ -59,8 +59,9 @@ //! //! ```no_run //! # let mut config = quiche::Config::new(quiche::PROTOCOL_VERSION).unwrap(); -//! # let scid = [0xba; 16]; -//! # let mut conn = quiche::connect(None, &scid, &mut config).unwrap(); +//! # let scid = quiche::ConnectionId::from_ref(&[0xba; 16]); +//! # let from = "127.0.0.1:1234".parse().unwrap(); +//! # let mut conn = quiche::accept(&scid, None, from, &mut config).unwrap(); //! # let h3_config = quiche::h3::Config::new()?; //! let h3_conn = quiche::h3::Connection::with_transport(&mut conn, &h3_config)?; //! # Ok::<(), quiche::h3::Error>(()) @@ -74,16 +75,17 @@ //! //! ```no_run //! # let mut config = quiche::Config::new(quiche::PROTOCOL_VERSION).unwrap(); -//! # let scid = [0xba; 16]; -//! # let mut conn = quiche::connect(None, &scid, &mut config).unwrap(); +//! # let scid = quiche::ConnectionId::from_ref(&[0xba; 16]); +//! # let to = "127.0.0.1:1234".parse().unwrap(); +//! # let mut conn = quiche::connect(None, &scid, to, &mut config).unwrap(); //! # let h3_config = quiche::h3::Config::new()?; //! # let mut h3_conn = quiche::h3::Connection::with_transport(&mut conn, &h3_config)?; //! let req = vec![ -//! quiche::h3::Header::new(":method", "GET"), -//! quiche::h3::Header::new(":scheme", "https"), -//! quiche::h3::Header::new(":authority", "quic.tech"), -//! quiche::h3::Header::new(":path", "/"), -//! quiche::h3::Header::new("user-agent", "quiche"), +//! quiche::h3::Header::new(b":method", b"GET"), +//! quiche::h3::Header::new(b":scheme", b"https"), +//! quiche::h3::Header::new(b":authority", b"quic.tech"), +//! quiche::h3::Header::new(b":path", b"/"), +//! quiche::h3::Header::new(b"user-agent", b"quiche"), //! ]; //! //! h3_conn.send_request(&mut conn, &req, true)?; @@ -95,16 +97,17 @@ //! //! ```no_run //! # let mut config = quiche::Config::new(quiche::PROTOCOL_VERSION).unwrap(); -//! # let scid = [0xba; 16]; -//! # let mut conn = quiche::connect(None, &scid, &mut config).unwrap(); +//! # let scid = quiche::ConnectionId::from_ref(&[0xba; 16]); +//! # let to = "127.0.0.1:1234".parse().unwrap(); +//! # let mut conn = quiche::connect(None, &scid, to, &mut config).unwrap(); //! # let h3_config = quiche::h3::Config::new()?; //! # let mut h3_conn = quiche::h3::Connection::with_transport(&mut conn, &h3_config)?; //! let req = vec![ -//! quiche::h3::Header::new(":method", "GET"), -//! quiche::h3::Header::new(":scheme", "https"), -//! quiche::h3::Header::new(":authority", "quic.tech"), -//! quiche::h3::Header::new(":path", "/"), -//! quiche::h3::Header::new("user-agent", "quiche"), +//! quiche::h3::Header::new(b":method", b"GET"), +//! quiche::h3::Header::new(b":scheme", b"https"), +//! quiche::h3::Header::new(b":authority", b"quic.tech"), +//! quiche::h3::Header::new(b":path", b"/"), +//! quiche::h3::Header::new(b"user-agent", b"quiche"), //! ]; //! //! let stream_id = h3_conn.send_request(&mut conn, &req, false)?; @@ -125,8 +128,9 @@ //! use quiche::h3::NameValue; //! //! # let mut config = quiche::Config::new(quiche::PROTOCOL_VERSION).unwrap(); -//! # let scid = [0xba; 16]; -//! # let mut conn = quiche::accept(&scid, None, &mut config).unwrap(); +//! # let scid = quiche::ConnectionId::from_ref(&[0xba; 16]); +//! # let from = "127.0.0.1:1234".parse().unwrap(); +//! # let mut conn = quiche::accept(&scid, None, from, &mut config).unwrap(); //! # let h3_config = quiche::h3::Config::new()?; //! # let mut h3_conn = quiche::h3::Connection::with_transport(&mut conn, &h3_config)?; //! loop { @@ -135,15 +139,15 @@ //! let mut headers = list.into_iter(); //! //! // Look for the request's method. -//! let method = headers.find(|h| h.name() == ":method").unwrap(); +//! let method = headers.find(|h| h.name() == b":method").unwrap(); //! //! // Look for the request's path. -//! let path = headers.find(|h| h.name() == ":path").unwrap(); +//! let path = headers.find(|h| h.name() == b":path").unwrap(); //! -//! if method.value() == "GET" && path.value() == "/" { +//! if method.value() == b"GET" && path.value() == b"/" { //! let resp = vec![ -//! quiche::h3::Header::new(":status", &200.to_string()), -//! quiche::h3::Header::new("server", "quiche"), +//! quiche::h3::Header::new(b":status", 200.to_string().as_bytes()), +//! quiche::h3::Header::new(b"server", b"quiche"), //! ]; //! //! h3_conn.send_response(&mut conn, stream_id, &resp, false)?; @@ -186,22 +190,25 @@ //! use quiche::h3::NameValue; //! //! # let mut config = quiche::Config::new(quiche::PROTOCOL_VERSION).unwrap(); -//! # let scid = [0xba; 16]; -//! # let mut conn = quiche::connect(None, &scid, &mut config).unwrap(); +//! # let scid = quiche::ConnectionId::from_ref(&[0xba; 16]); +//! # let to = "127.0.0.1:1234".parse().unwrap(); +//! # let mut conn = quiche::connect(None, &scid, to, &mut config).unwrap(); //! # let h3_config = quiche::h3::Config::new()?; //! # let mut h3_conn = quiche::h3::Connection::with_transport(&mut conn, &h3_config)?; //! loop { //! match h3_conn.poll(&mut conn) { //! Ok((stream_id, quiche::h3::Event::Headers{list, has_body})) => { -//! let status = list.iter().find(|h| h.name() == ":status").unwrap(); +//! let status = list.iter().find(|h| h.name() == b":status").unwrap(); //! println!("Received {} response on stream {}", -//! status.value(), stream_id); +//! std::str::from_utf8(status.value()).unwrap(), +//! stream_id); //! }, //! //! Ok((stream_id, quiche::h3::Event::Data)) => { //! let mut body = vec![0; 4096]; //! -//! if let Ok(read) = +//! // Consume all body data received on the stream. +//! while let Ok(read) = //! h3_conn.recv_body(&mut conn, stream_id, &mut body) //! { //! println!("Received {} bytes of payload on stream {}", @@ -274,7 +281,7 @@ use crate::octets; /// /// [`Config::set_application_protos()`]: /// ../struct.Config.html#method.set_application_protos -pub const APPLICATION_PROTOCOL: &[u8] = b"\x05h3-29\x05h3-28\x05h3-27"; +pub const APPLICATION_PROTOCOL: &[u8] = b"\x02h3\x05h3-29\x05h3-28\x05h3-27"; // The offset used when converting HTTP/3 urgency to quiche urgency. const PRIORITY_URGENCY_OFFSET: u8 = 124; @@ -333,6 +340,30 @@ pub enum Error { /// The underlying QUIC stream (or connection) doesn't have enough capacity /// for the operation to complete. The application should retry later on. StreamBlocked, + + /// Error in the payload of a SETTINGS frame. + SettingsError, + + /// Server rejected request. + RequestRejected, + + /// Request or its response cancelled. + RequestCancelled, + + /// Client's request stream terminated without containing a full-formed + /// request. + RequestIncomplete, + + /// An HTTP message was malformed and cannot be processed. + MessageError, + + /// The TCP connection established in response to a CONNECT request was + /// reset or abnormally closed. + ConnectError, + + /// The requested operation cannot be served over HTTP/3. Peer should retry + /// over HTTP/1.1. + VersionFallback, } impl Error { @@ -351,9 +382,17 @@ impl Error { Error::BufferTooShort => 0x999, Error::TransportError { .. } => 0xFF, Error::StreamBlocked => 0xFF, + Error::SettingsError => 0x109, + Error::RequestRejected => 0x10B, + Error::RequestCancelled => 0x10C, + Error::RequestIncomplete => 0x10D, + Error::MessageError => 0x10E, + Error::ConnectError => 0x10F, + Error::VersionFallback => 0x110, } } + #[cfg(feature = "ffi")] fn to_c(self) -> libc::ssize_t { match self { Error::Done => -1, @@ -369,6 +408,13 @@ impl Error { Error::QpackDecompressionFailed => -11, Error::TransportError { .. } => -12, Error::StreamBlocked => -13, + Error::SettingsError => -14, + Error::RequestRejected => -15, + Error::RequestCancelled => -16, + Error::RequestIncomplete => -17, + Error::MessageError => -18, + Error::ConnectError => -19, + Error::VersionFallback => -20, } } } @@ -420,7 +466,13 @@ impl Config { /// Sets the `SETTINGS_MAX_HEADER_LIST_SIZE` setting. /// - /// By default no limit is enforced. + /// By default no limit is enforced. When a request whose headers exceed + /// the limit set by the application is received, the call to the [`poll()`] + /// method will return the [`Error::ExcessiveLoad`] error, and the + /// connection will be closed. + /// + /// [`poll()`]: struct.Connection.html#method.poll + /// [`Error::ExcessiveLoad`]: enum.Error.html#variant.ExcessiveLoad pub fn set_max_header_list_size(&mut self, v: u64) { self.max_header_list_size = Some(v); } @@ -443,52 +495,52 @@ impl Config { /// A trait for types with associated string name and value. pub trait NameValue { /// Returns the object's name. - fn name(&self) -> &str; + fn name(&self) -> &[u8]; /// Returns the object's value. - fn value(&self) -> &str; + fn value(&self) -> &[u8]; } /// An owned name-value pair representing a raw HTTP header. #[derive(Clone, Debug, PartialEq)] -pub struct Header(String, String); +pub struct Header(Vec<u8>, Vec<u8>); impl Header { /// Creates a new header. /// /// Both `name` and `value` will be cloned. - pub fn new(name: &str, value: &str) -> Self { - Self(String::from(name), String::from(value)) + pub fn new(name: &[u8], value: &[u8]) -> Self { + Self(name.to_vec(), value.to_vec()) } } impl NameValue for Header { - fn name(&self) -> &str { + fn name(&self) -> &[u8] { &self.0 } - fn value(&self) -> &str { + fn value(&self) -> &[u8] { &self.1 } } /// A non-owned name-value pair representing a raw HTTP header. #[derive(Clone, Debug, PartialEq)] -pub struct HeaderRef<'a>(&'a str, &'a str); +pub struct HeaderRef<'a>(&'a [u8], &'a [u8]); impl<'a> HeaderRef<'a> { /// Creates a new header. - pub fn new(name: &'a str, value: &'a str) -> Self { + pub fn new(name: &'a [u8], value: &'a [u8]) -> Self { Self(name, value) } } impl<'a> NameValue for HeaderRef<'a> { - fn name(&self) -> &str { + fn name(&self) -> &[u8] { self.0 } - fn value(&self) -> &str { + fn value(&self) -> &[u8] { self.1 } } @@ -511,16 +563,28 @@ pub enum Event { /// This indicates that the application can use the [`recv_body()`] method /// to retrieve the data from the stream. /// - /// This event will keep being reported until all the available data is - /// retrieved by the application. + /// Note that [`recv_body()`] will need to be called repeatedly until the + /// [`Done`] value is returned, as the event will not be re-armed until all + /// buffered data is read. /// /// [`recv_body()`]: struct.Connection.html#method.recv_body + /// [`Done`]: enum.Error.html#variant.Done Data, /// Stream was closed, Finished, /// DATAGRAM was received. + /// + /// This indicates that the application can use the [`recv_dgram()`] method + /// to retrieve the HTTP/3 DATAGRAM. + /// + /// Note that [`recv_dgram()`] will need to be called repeatedly until the + /// [`Done`] value is returned, as the event will not be re-armed until all + /// buffered DATAGRAMs with the same flow ID are read. + /// + /// [`recv_dgram()`]: struct.Connection.html#method.recv_dgram + /// [`Done`]: enum.Error.html#variant.Done Datagram, /// GOAWAY was received. @@ -531,6 +595,7 @@ struct ConnectionSettings { pub max_header_list_size: Option<u64>, pub qpack_max_table_capacity: Option<u64>, pub qpack_blocked_streams: Option<u64>, + pub h3_datagram: Option<u64>, } struct QpackStreams { @@ -556,6 +621,7 @@ pub struct Connection { qpack_encoder: qpack::Encoder, qpack_decoder: qpack::Decoder, + #[allow(dead_code)] local_qpack_streams: QpackStreams, peer_qpack_streams: QpackStreams, @@ -567,11 +633,17 @@ pub struct Connection { local_goaway_id: Option<u64>, peer_goaway_id: Option<u64>, + + dgram_event_triggered: bool, } impl Connection { - fn new(config: &Config, is_server: bool) -> Result<Connection> { + #[allow(clippy::unnecessary_wraps)] + fn new( + config: &Config, is_server: bool, enable_dgram: bool, + ) -> Result<Connection> { let initial_uni_stream_id = if is_server { 0x3 } else { 0x2 }; + let h3_datagram = if enable_dgram { Some(1) } else { None }; Ok(Connection { is_server, @@ -586,12 +658,14 @@ impl Connection { max_header_list_size: config.max_header_list_size, qpack_max_table_capacity: config.qpack_max_table_capacity, qpack_blocked_streams: config.qpack_blocked_streams, + h3_datagram, }, peer_settings: ConnectionSettings { max_header_list_size: None, qpack_max_table_capacity: None, qpack_blocked_streams: None, + h3_datagram: None, }, control_stream_id: None, @@ -618,6 +692,8 @@ impl Connection { local_goaway_id: None, peer_goaway_id: None, + + dgram_event_triggered: false, }) } @@ -625,12 +701,27 @@ impl Connection { /// /// This will also initiate the HTTP/3 handshake with the peer by opening /// all control streams (including QPACK) and sending the local settings. + /// + /// On success the new connection is returned. + /// + /// The [`StreamLimit`] error is returned when the HTTP/3 control stream + /// cannot be created. + /// + /// [`StreamLimit`]: ../enum.Error.html#variant.InvalidState pub fn with_transport( conn: &mut super::Connection, config: &Config, ) -> Result<Connection> { - let mut http3_conn = Connection::new(config, conn.is_server)?; + let mut http3_conn = + Connection::new(config, conn.is_server, conn.dgram_enabled())?; - http3_conn.send_settings(conn)?; + match http3_conn.send_settings(conn) { + Ok(_) => (), + + Err(e) => { + conn.close(true, e.to_wire(), b"Error opening control stream")?; + return Err(e); + }, + }; // Try opening QPACK streams, but ignore errors if it fails since we // don't need them right now. @@ -680,7 +771,11 @@ impl Connection { // stream_capacity() will fail. By writing a 0-length buffer, we force // the creation of the QUIC stream state, without actually writing // anything. - conn.stream_send(stream_id, b"", false)?; + if let Err(e) = conn.stream_send(stream_id, b"", false) { + self.streams.remove(&stream_id); + + return Err(e.into()); + }; self.send_headers(conn, stream_id, headers, fin)?; @@ -737,7 +832,7 @@ impl Connection { return Err(Error::FrameUnexpected); } - let mut urgency = 3; + let mut urgency = 3u8.saturating_add(PRIORITY_URGENCY_OFFSET); let mut incremental = false; for param in priority.split(',') { @@ -755,11 +850,14 @@ impl Connection { // TODO: this also detects when u is not an sh-integer and // clamps it in the same way. A real structured header parser // would actually fail to parse. - let mut u = - i64::from_str_radix(param.rsplit('=').next().unwrap(), 10) - .unwrap_or(7); - - if u < 0 || u > 7 { + let mut u = param + .rsplit('=') + .next() + .unwrap() + .parse::<i64>() + .unwrap_or(7); + + if !(0..=7).contains(&u) { u = 7; } @@ -806,7 +904,17 @@ impl Connection { self.frames_greased = true; } - let stream_cap = conn.stream_capacity(stream_id)?; + let stream_cap = match conn.stream_capacity(stream_id) { + Ok(v) => v, + + Err(e) => { + if conn.stream_finished(stream_id) { + self.streams.remove(&stream_id); + } + + return Err(e.into()); + }, + }; let header_block = self.encode_header_block(headers)?; @@ -881,15 +989,28 @@ impl Connection { }, }; + // Avoid sending 0-length DATA frames when the fin flag is false. + if body.is_empty() && !fin { + return Err(Error::Done); + } + let overhead = octets::varint_len(frame::DATA_FRAME_TYPE_ID) + octets::varint_len(body.len() as u64); - let stream_cap = conn.stream_capacity(stream_id)?; + let stream_cap = match conn.stream_capacity(stream_id) { + Ok(v) => v, + + Err(e) => { + if conn.stream_finished(stream_id) { + self.streams.remove(&stream_id); + } + + return Err(e.into()); + }, + }; - // Make sure there is enough capacity to send the frame header and at - // least one byte of frame payload (this to avoid sending 0-length DATA - // frames). - if stream_cap <= overhead { + // Make sure there is enough capacity to send the DATA frame header. + if stream_cap < overhead { return Err(Error::Done); } @@ -900,6 +1021,11 @@ impl Connection { // application can try again later. let fin = if body_len != body.len() { false } else { fin }; + // Again, avoid sending 0-length DATA frames when the fin flag is false. + if body_len == 0 && !fin { + return Err(Error::Done); + } + trace!( "{} tx frm DATA stream={} len={} fin={}", conn.trace_id(), @@ -924,6 +1050,18 @@ impl Connection { Ok(written) } + /// Returns whether the peer enabled HTTP/3 DATAGRAM frame support. + /// + /// Support is signalled by the peer's SETTINGS, so this method always + /// returns false until they have been processed using the [`poll()`] + /// method. + /// + /// [`poll()`]: struct.Connection.html#method.poll + pub fn dgram_enabled_by_peer(&self, conn: &super::Connection) -> bool { + self.peer_settings.h3_datagram == Some(1) && + conn.dgram_max_writable_len().is_some() + } + /// Sends an HTTP/3 DATAGRAM with the specified flow ID. pub fn send_dgram( &mut self, conn: &mut super::Connection, flow_id: u64, buf: &[u8], @@ -977,6 +1115,35 @@ impl Connection { } } + // A helper function for determining if there is a DATAGRAM event. + fn process_dgrams( + &mut self, conn: &mut super::Connection, + ) -> Result<(u64, Event)> { + let mut d = [0; 8]; + + match conn.dgram_recv_peek(&mut d, 8) { + Ok(_) => { + if self.dgram_event_triggered { + return Err(Error::Done); + } + + self.dgram_event_triggered = true; + + Ok((0, Event::Datagram)) + }, + + Err(crate::Error::Done) => { + // The dgram recv queue is empty, so re-arm the Datagram event + // so it is issued next time a DATAGRAM is received. + self.dgram_event_triggered = false; + + Err(Error::Done) + }, + + Err(e) => Err(Error::TransportError(e)), + } + } + /// Reads request or response body data into the provided buffer. /// /// Applications should call this method whenever the [`poll()`] method @@ -991,32 +1158,78 @@ impl Connection { pub fn recv_body( &mut self, conn: &mut super::Connection, stream_id: u64, out: &mut [u8], ) -> Result<usize> { - let stream = self.streams.get_mut(&stream_id).ok_or(Error::Done)?; + let mut total = 0; - if stream.state() != stream::State::Data { - return Err(Error::Done); - } + // Try to consume all buffered data for the stream, even across multiple + // DATA frames. + while total < out.len() { + let stream = self.streams.get_mut(&stream_id).ok_or(Error::Done)?; + + if stream.state() != stream::State::Data { + break; + } + + let (read, fin) = + match stream.try_consume_data(conn, &mut out[total..]) { + Ok(v) => v, + + Err(Error::Done) => break, + + Err(e) => return Err(e), + }; + + total += read; + + // No more data to read, we are done. + if read == 0 || fin { + break; + } + + // Process incoming data from the stream. For example, if a whole + // DATA frame was consumed, and another one is queued behind it, + // this will ensure the additional data will also be returned to + // the application. + match self.process_readable_stream(conn, stream_id, false) { + Ok(_) => unreachable!(), + + Err(Error::Done) => (), - let read = stream.try_consume_data(conn, out)?; + Err(e) => return Err(e), + }; + + if conn.stream_finished(stream_id) { + break; + } + } // While body is being received, the stream is marked as finished only // when all data is read by the application. if conn.stream_finished(stream_id) { - self.finished_streams.push_back(stream_id); + self.process_finished_stream(stream_id); + } + + if total == 0 { + return Err(Error::Done); } - Ok(read) + Ok(total) } /// Processes HTTP/3 data received from the peer. /// - /// On success it returns an [`Event`] and an ID. + /// On success it returns an [`Event`] and an ID, or [`Done`] when there are + /// no events to report. + /// + /// Note that all events are edge-triggered, meaning that once reported they + /// will not be reported again by calling this method again, until the event + /// is re-armed. /// /// The events [`Headers`], [`Data`] and [`Finished`] return a stream ID, /// which is used in methods [`recv_body()`], [`send_response()`] or /// [`send_body()`]. /// - /// The event [`Datagram`] returns a flow ID. + /// The event [`Datagram`] returns a dummy value of `0`, this should be + /// ignored by the application. /// /// The event [`GoAway`] returns an ID that depends on the connection role. /// A client receives the largest processed stream ID. A server receives the @@ -1026,6 +1239,7 @@ impl Connection { /// the appropriate error code, using the transport's [`close()`] method. /// /// [`Event`]: enum.Event.html + /// [`Done`]: enum.Error.html#variant.Done /// [`Headers`]: enum.Event.html#variant.Headers /// [`Data`]: enum.Event.html#variant.Data /// [`Finished`]: enum.Event.html#variant.Finished @@ -1040,7 +1254,7 @@ impl Connection { // When connection close is initiated by the local application (e.g. due // to a protocol error), the connection itself might be in a broken // state, so return early. - if conn.error.is_some() || conn.app_error.is_some() { + if conn.local_error.is_some() { return Err(Error::Done); } @@ -1080,26 +1294,20 @@ impl Connection { return Ok((finished, Event::Finished)); } - // Process DATAGRAMs - let mut d = [0; 8]; + // Process queued DATAGRAMs if the poll threshold allows it. + match self.process_dgrams(conn) { + Ok(v) => return Ok(v), - match conn.dgram_recv_peek(&mut d, 8) { - Ok(_) => { - let mut b = octets::Octets::with_slice(&d); - let flow_id = b.get_varint()?; - return Ok((flow_id, Event::Datagram)); - }, - - Err(crate::Error::Done) => (), + Err(Error::Done) => (), - Err(e) => return Err(Error::TransportError(e)), + Err(e) => return Err(e), }; // Process HTTP/3 data from readable streams. for s in conn.readable() { trace!("{} stream id {} is readable", conn.trace_id(), s); - let ev = match self.process_readable_stream(conn, s) { + let ev = match self.process_readable_stream(conn, s, true) { Ok(v) => Some(v), Err(Error::Done) => None, @@ -1108,16 +1316,22 @@ impl Connection { }; if conn.stream_finished(s) { - self.finished_streams.push_back(s); + self.process_finished_stream(s); } // TODO: check if stream is completed so it can be freed - if let Some(ev) = ev { return Ok(ev); } } + // Process finished streams list once again, to make sure `Finished` + // events are returned when receiving empty stream frames with the fin + // flag set. + if let Some(finished) = self.finished_streams.pop_front() { + return Ok((finished, Event::Finished)); + } + Err(Error::Done) } @@ -1135,9 +1349,13 @@ impl Connection { pub fn send_goaway( &mut self, conn: &mut super::Connection, id: u64, ) -> Result<()> { + let mut id = id; + + // TODO: server push + // + // In the meantime always send 0 from client. if !self.is_server { - // TODO: server push - return Ok(()); + id = 0; } if self.is_server && id % 4 != 0 { @@ -1237,7 +1455,17 @@ impl Connection { ) -> Result<()> { let mut d = [0; 8]; - let stream_cap = conn.stream_capacity(stream_id)?; + let stream_cap = match conn.stream_capacity(stream_id) { + Ok(v) => v, + + Err(e) => { + if conn.stream_finished(stream_id) { + self.streams.remove(&stream_id); + } + + return Err(e.into()); + }, + }; let grease_frame1 = grease_value(); let grease_frame2 = grease_value(); @@ -1283,7 +1511,7 @@ impl Connection { Ok(stream_id) => { trace!("{} open GREASE stream {}", conn.trace_id(), stream_id); - conn.stream_send(stream_id, b"GREASE is the word", false)?; + conn.stream_send(stream_id, b"GREASE is the word", true)?; }, Err(Error::IdError) => { @@ -1316,6 +1544,7 @@ impl Connection { .local_settings .qpack_max_table_capacity, qpack_blocked_streams: self.local_settings.qpack_blocked_streams, + h3_datagram: self.local_settings.h3_datagram, grease, }; @@ -1346,7 +1575,7 @@ impl Connection { return Err(Error::ClosedCriticalStream); } - match self.process_readable_stream(conn, stream_id) { + match self.process_readable_stream(conn, stream_id, true) { Ok(ev) => return Ok(ev), Err(Error::Done) => (), @@ -1368,7 +1597,7 @@ impl Connection { } fn process_readable_stream( - &mut self, conn: &mut super::Connection, stream_id: u64, + &mut self, conn: &mut super::Connection, stream_id: u64, polling: bool, ) -> Result<(u64, Event)> { self.streams .entry(stream_id) @@ -1541,6 +1770,11 @@ impl Connection { }, stream::State::FramePayload => { + // Do not emit events when not polling. + if !polling { + break; + } + stream.try_fill_buffer(conn)?; let frame = match stream.try_consume_frame() { @@ -1569,6 +1803,15 @@ impl Connection { }, stream::State::Data => { + // Do not emit events when not polling. + if !polling { + break; + } + + if !stream.try_trigger_data_event() { + break; + } + return Ok((stream_id, Event::Data)); }, @@ -1583,16 +1826,44 @@ impl Connection { stream::State::Drain => { // Discard incoming data on the stream. - conn.stream_shutdown(stream_id, crate::Shutdown::Read, 0)?; + conn.stream_shutdown( + stream_id, + crate::Shutdown::Read, + 0x100, + )?; break; }, + + stream::State::Finished => break, } } Err(Error::Done) } + fn process_finished_stream(&mut self, stream_id: u64) { + let stream = match self.streams.get_mut(&stream_id) { + Some(v) => v, + + None => return, + }; + + if stream.state() == stream::State::Finished { + return; + } + + match stream.ty() { + Some(stream::Type::Request) | Some(stream::Type::Push) => { + stream.finished(); + + self.finished_streams.push_back(stream_id); + }, + + _ => (), + }; + } + fn process_frame( &mut self, conn: &mut super::Connection, stream_id: u64, frame: frame::Frame, @@ -1609,13 +1880,28 @@ impl Connection { max_header_list_size, qpack_max_table_capacity, qpack_blocked_streams, + h3_datagram, .. } => { self.peer_settings = ConnectionSettings { max_header_list_size, qpack_max_table_capacity, qpack_blocked_streams, + h3_datagram, }; + + if let Some(1) = h3_datagram { + // The peer MUST have also enabled DATAGRAM with a TP + if conn.dgram_max_writable_len().is_none() { + conn.close( + true, + Error::SettingsError.to_wire(), + b"H3_DATAGRAM sent with value 1 but max_datagram_frame_size TP not set.", + )?; + + return Err(Error::SettingsError); + } + } }, frame::Frame::Headers { header_block } => { @@ -1636,14 +1922,25 @@ impl Connection { .max_header_list_size .unwrap_or(std::u64::MAX); - let headers = self + let headers = match self .qpack_decoder .decode(&header_block[..], max_size) - .map_err(|e| match e { - qpack::Error::HeaderListTooLarge => Error::ExcessiveLoad, + { + Ok(v) => v, + + Err(e) => { + let e = match e { + qpack::Error::HeaderListTooLarge => + Error::ExcessiveLoad, - _ => Error::QpackDecompressionFailed, - })?; + _ => Error::QpackDecompressionFailed, + }; + + conn.close(true, e.to_wire(), b"Error parsing headers.")?; + + return Err(e); + }, + }; let has_body = !conn.stream_finished(stream_id); @@ -1814,8 +2111,6 @@ pub mod testing { pub pipe: testing::Pipe, pub client: Connection, pub server: Connection, - - buf: [u8; 65535], } impl Session { @@ -1831,6 +2126,7 @@ pub mod testing { config.set_initial_max_streams_bidi(5); config.set_initial_max_streams_uni(5); config.verify_peer(false); + config.enable_dgram(true, 3, 3); let h3_config = Config::new()?; Session::with_configs(&mut config, &h3_config) @@ -1839,47 +2135,49 @@ pub mod testing { pub fn with_configs( config: &mut crate::Config, h3_config: &Config, ) -> Result<Session> { + let pipe = testing::Pipe::with_config(config)?; + let client_dgram = pipe.client.dgram_enabled(); + let server_dgram = pipe.server.dgram_enabled(); Ok(Session { - pipe: testing::Pipe::with_config(config)?, - client: Connection::new(&h3_config, false)?, - server: Connection::new(&h3_config, true)?, - buf: [0; 65535], + pipe, + client: Connection::new(&h3_config, false, client_dgram)?, + server: Connection::new(&h3_config, true, server_dgram)?, }) } /// Do the HTTP/3 handshake so both ends are in sane initial state. pub fn handshake(&mut self) -> Result<()> { - self.pipe.handshake(&mut self.buf)?; + self.pipe.handshake()?; // Client streams. self.client.send_settings(&mut self.pipe.client)?; - self.pipe.advance(&mut self.buf).ok(); + self.pipe.advance().ok(); self.client .open_qpack_encoder_stream(&mut self.pipe.client)?; - self.pipe.advance(&mut self.buf).ok(); + self.pipe.advance().ok(); self.client .open_qpack_decoder_stream(&mut self.pipe.client)?; - self.pipe.advance(&mut self.buf).ok(); + self.pipe.advance().ok(); if self.pipe.client.grease { self.client.open_grease_stream(&mut self.pipe.client)?; } - self.pipe.advance(&mut self.buf).ok(); + self.pipe.advance().ok(); // Server streams. self.server.send_settings(&mut self.pipe.server)?; - self.pipe.advance(&mut self.buf).ok(); + self.pipe.advance().ok(); self.server .open_qpack_encoder_stream(&mut self.pipe.server)?; - self.pipe.advance(&mut self.buf).ok(); + self.pipe.advance().ok(); self.server .open_qpack_decoder_stream(&mut self.pipe.server)?; - self.pipe.advance(&mut self.buf).ok(); + self.pipe.advance().ok(); if self.pipe.server.grease { self.server.open_grease_stream(&mut self.pipe.server)?; @@ -1900,7 +2198,7 @@ pub mod testing { /// Advances the session pipe over the buffer. pub fn advance(&mut self) -> crate::Result<()> { - self.pipe.advance(&mut self.buf) + self.pipe.advance() } /// Polls the client for events. @@ -1918,11 +2216,11 @@ pub mod testing { /// On success it returns the newly allocated stream and the headers. pub fn send_request(&mut self, fin: bool) -> Result<(u64, Vec<Header>)> { let req = vec![ - Header::new(":method", "GET"), - Header::new(":scheme", "https"), - Header::new(":authority", "quic.tech"), - Header::new(":path", "/test"), - Header::new("user-agent", "quiche-test"), + Header::new(b":method", b"GET"), + Header::new(b":scheme", b"https"), + Header::new(b":authority", b"quic.tech"), + Header::new(b":path", b"/test"), + Header::new(b"user-agent", b"quiche-test"), ]; let stream = @@ -1940,8 +2238,8 @@ pub mod testing { &mut self, stream: u64, fin: bool, ) -> Result<Vec<Header>> { let resp = vec![ - Header::new(":status", "200"), - Header::new("server", "quiche-test"), + Header::new(b":status", b"200"), + Header::new(b"server", b"quiche-test"), ]; self.server.send_response( @@ -2024,6 +2322,54 @@ pub mod testing { Ok(()) } + /// Send an HTTP/3 DATAGRAM with default data from the client. + /// + /// On success it returns the data. + pub fn send_dgram_client(&mut self, flow_id: u64) -> Result<Vec<u8>> { + let bytes = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; + + self.client + .send_dgram(&mut self.pipe.client, flow_id, &bytes)?; + + self.advance().ok(); + + Ok(bytes) + } + + /// Receives an HTTP/3 DATAGRAM from the server. + /// + /// On success it returns the DATAGRAM length, flow ID and flow ID + /// length. + pub fn recv_dgram_client( + &mut self, buf: &mut [u8], + ) -> Result<(usize, u64, usize)> { + self.client.recv_dgram(&mut self.pipe.client, buf) + } + + /// Send an HTTP/3 DATAGRAM with default data from the server + /// + /// On success it returns the data. + pub fn send_dgram_server(&mut self, flow_id: u64) -> Result<Vec<u8>> { + let bytes = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; + + self.server + .send_dgram(&mut self.pipe.server, flow_id, &bytes)?; + + self.advance().ok(); + + Ok(bytes) + } + + /// Receives an HTTP/3 DATAGRAM from the client. + /// + /// On success it returns the DATAGRAM length, flow ID and flow ID + /// length. + pub fn recv_dgram_server( + &mut self, buf: &mut [u8], + ) -> Result<(usize, u64, usize)> { + self.server.recv_dgram(&mut self.pipe.server, buf) + } + /// Sends a single HTTP/3 frame from the server. pub fn send_frame_server( &mut self, frame: frame::Frame, stream_id: u64, fin: bool, @@ -2041,6 +2387,28 @@ pub mod testing { Ok(()) } + + /// Sends an arbitrary buffer of HTTP/3 stream data from the client. + pub fn send_arbitrary_stream_data_client( + &mut self, data: &[u8], stream_id: u64, fin: bool, + ) -> Result<()> { + self.pipe.client.stream_send(stream_id, data, fin)?; + + self.advance().ok(); + + Ok(()) + } + + /// Sends an arbitrary buffer of HTTP/3 stream data from the server. + pub fn send_arbitrary_stream_data_server( + &mut self, data: &[u8], stream_id: u64, fin: bool, + ) -> Result<()> { + self.pipe.server.stream_send(stream_id, data, fin)?; + + self.advance().ok(); + + Ok(()) + } } } @@ -2158,9 +2526,10 @@ mod tests { }; assert_eq!(s.poll_client(), Ok((stream, ev_headers))); + assert_eq!(s.poll_client(), Ok((stream, Event::Data))); + assert_eq!(s.poll_client(), Err(Error::Done)); for _ in 0..total_data_frames { - assert_eq!(s.poll_client(), Ok((stream, Event::Data))); assert_eq!(s.recv_body_client(stream, &mut recv_buf), Ok(body.len())); } @@ -2227,9 +2596,10 @@ mod tests { }; assert_eq!(s.poll_server(), Ok((stream, ev_headers))); + assert_eq!(s.poll_server(), Ok((stream, Event::Data))); + assert_eq!(s.poll_server(), Err(Error::Done)); for _ in 0..total_data_frames { - assert_eq!(s.poll_server(), Ok((stream, Event::Data))); assert_eq!(s.recv_body_server(stream, &mut recv_buf), Ok(body.len())); } @@ -2288,7 +2658,8 @@ mod tests { assert_eq!(ev, ev_headers); assert_eq!(s.poll_server(), Ok((stream, Event::Data))); assert_eq!(s.recv_body_server(stream, &mut recv_buf), Ok(body.len())); - assert_eq!(s.poll_server(), Ok((stream, Event::Data))); + assert_eq!(s.poll_client(), Err(Error::Done)); + assert_eq!(s.recv_body_server(stream, &mut recv_buf), Ok(body.len())); assert_eq!(s.poll_server(), Ok((stream, Event::Finished))); } @@ -2320,6 +2691,46 @@ mod tests { } #[test] + /// Send a request with no body, get a response with one DATA frame and an + /// empty FIN after reception from the client. + fn request_no_body_response_one_chunk_empty_fin() { + let mut s = Session::default().unwrap(); + s.handshake().unwrap(); + + let (stream, req) = s.send_request(true).unwrap(); + + let ev_headers = Event::Headers { + list: req, + has_body: false, + }; + + assert_eq!(s.poll_server(), Ok((stream, ev_headers))); + assert_eq!(s.poll_server(), Ok((stream, Event::Finished))); + + let resp = s.send_response(stream, false).unwrap(); + + let body = s.send_body_server(stream, false).unwrap(); + + let mut recv_buf = vec![0; body.len()]; + + let ev_headers = Event::Headers { + list: resp, + has_body: true, + }; + + assert_eq!(s.poll_client(), Ok((stream, ev_headers))); + + assert_eq!(s.poll_client(), Ok((stream, Event::Data))); + assert_eq!(s.recv_body_client(stream, &mut recv_buf), Ok(body.len())); + + assert_eq!(s.pipe.server.stream_send(stream, &[], true), Ok(0)); + s.advance().ok(); + + assert_eq!(s.poll_client(), Ok((stream, Event::Finished))); + assert_eq!(s.poll_client(), Err(Error::Done)); + } + + #[test] /// Try to send DATA frames before HEADERS. fn body_response_before_headers() { let mut s = Session::default().unwrap(); @@ -2620,12 +3031,12 @@ mod tests { let mut s = Session::default().unwrap(); s.handshake().unwrap(); - s.client.send_goaway(&mut s.pipe.client, 1).unwrap(); + s.client.send_goaway(&mut s.pipe.client, 100).unwrap(); s.advance().ok(); // TODO: server push - assert_eq!(s.poll_server(), Err(Error::Done)); + assert_eq!(s.poll_server(), Ok((0, Event::GoAway))); } #[test] @@ -2703,10 +3114,10 @@ mod tests { fn uni_stream_local_counting() { let config = Config::new().unwrap(); - let h3_cln = Connection::new(&config, false).unwrap(); + let h3_cln = Connection::new(&config, false, false).unwrap(); assert_eq!(h3_cln.next_uni_stream_id, 2); - let h3_srv = Connection::new(&config, true).unwrap(); + let h3_srv = Connection::new(&config, true, false).unwrap(); assert_eq!(h3_srv.next_uni_stream_id, 3); } @@ -2842,10 +3253,32 @@ mod tests { #[test] /// Tests limits for the stream state buffer maximum size. fn max_state_buf_size() { - // DATA frames don't consume the state buffer, so can be of any size. let mut s = Session::default().unwrap(); s.handshake().unwrap(); + let req = vec![ + Header::new(b":method", b"GET"), + Header::new(b":scheme", b"https"), + Header::new(b":authority", b"quic.tech"), + Header::new(b":path", b"/test"), + Header::new(b"user-agent", b"quiche-test"), + ]; + + assert_eq!( + s.client.send_request(&mut s.pipe.client, &req, false), + Ok(0) + ); + + s.advance().ok(); + + let ev_headers = Event::Headers { + list: req, + has_body: true, + }; + + assert_eq!(s.server.poll(&mut s.pipe.server), Ok((0, ev_headers))); + + // DATA frames don't consume the state buffer, so can be of any size. let mut d = [42; 128]; let mut b = octets::OctetsMut::with_slice(&mut d); @@ -2919,16 +3352,16 @@ mod tests { }; assert_eq!(s.poll_server(), Ok((stream, ev_headers))); + assert_eq!(s.poll_server(), Ok((stream, Event::Data))); + assert_eq!(s.poll_server(), Err(Error::Done)); for _ in 0..total_data_frames { - assert_eq!(s.poll_server(), Ok((stream, Event::Data))); assert_eq!( s.recv_body_server(stream, &mut recv_buf), Ok(bytes.len()) ); } - assert_eq!(s.poll_server(), Ok((stream, Event::Data))); assert_eq!( s.recv_body_server(stream, &mut recv_buf), Ok(bytes.len() - 2) @@ -2966,11 +3399,11 @@ mod tests { s.handshake().unwrap(); let req = vec![ - Header::new(":method", "GET"), - Header::new(":scheme", "https"), - Header::new(":authority", "quic.tech"), - Header::new(":path", "/test"), - Header::new("aaaaaaa", "aaaaaaaa"), + Header::new(b":method", b"GET"), + Header::new(b":scheme", b"https"), + Header::new(b":authority", b"quic.tech"), + Header::new(b":path", b"/test"), + Header::new(b"aaaaaaa", b"aaaaaaaa"), ]; let stream = s @@ -2983,6 +3416,11 @@ mod tests { assert_eq!(stream, 0); assert_eq!(s.poll_server(), Err(Error::ExcessiveLoad)); + + assert_eq!( + s.pipe.server.local_error.as_ref().unwrap().error_code, + Error::to_wire(Error::ExcessiveLoad) + ); } #[test] @@ -2992,11 +3430,11 @@ mod tests { s.handshake().unwrap(); let req = vec![ - Header::new(":method", "GET"), - Header::new(":scheme", "https"), - Header::new(":authority", "quic.tech"), - Header::new(":path", "/test"), - Header::new("user-agent", "quiche-test"), + Header::new(b":method", b"GET"), + Header::new(b":scheme", b"https"), + Header::new(b":authority", b"quic.tech"), + Header::new(b":path", b"/test"), + Header::new(b"user-agent", b"quiche-test"), ]; // We need to open all streams in the same flight, so we can't use the @@ -3022,9 +3460,8 @@ mod tests { } #[test] - /// Tests that calling poll() after an error occured does nothing. - fn poll_after_error() { - // DATA frames don't consume the state buffer, so can be of any size. + /// Tests that sending DATA before HEADERS causes an error. + fn data_before_headers() { let mut s = Session::default().unwrap(); s.handshake().unwrap(); @@ -3034,16 +3471,22 @@ mod tests { let frame_type = b.put_varint(frame::DATA_FRAME_TYPE_ID).unwrap(); s.pipe.client.stream_send(0, frame_type, false).unwrap(); - let frame_len = b.put_varint(1 << 24).unwrap(); + let frame_len = b.put_varint(5).unwrap(); s.pipe.client.stream_send(0, frame_len, false).unwrap(); - s.pipe.client.stream_send(0, &d, false).unwrap(); + s.pipe.client.stream_send(0, b"hello", false).unwrap(); s.advance().ok(); - assert_eq!(s.server.poll(&mut s.pipe.server), Ok((0, Event::Data))); + assert_eq!( + s.server.poll(&mut s.pipe.server), + Err(Error::FrameUnexpected) + ); + } - // GREASE frames consume the state buffer, so need to be limited. + #[test] + /// Tests that calling poll() after an error occured does nothing. + fn poll_after_error() { let mut s = Session::default().unwrap(); s.handshake().unwrap(); @@ -3092,10 +3535,10 @@ mod tests { s.handshake().unwrap(); let req = vec![ - Header::new(":method", "GET"), - Header::new(":scheme", "https"), - Header::new(":authority", "quic.tech"), - Header::new(":path", "/test"), + Header::new(b":method", b"GET"), + Header::new(b":scheme", b"https"), + Header::new(b":authority", b"quic.tech"), + Header::new(b":path", b"/test"), ]; assert_eq!(s.client.send_request(&mut s.pipe.client, &req, true), Ok(0)); @@ -3113,6 +3556,61 @@ mod tests { } #[test] + /// Test handling of 0-length DATA writes with and without fin. + fn zero_length_data() { + let mut s = Session::default().unwrap(); + s.handshake().unwrap(); + + let (stream, req) = s.send_request(false).unwrap(); + + assert_eq!( + s.client.send_body(&mut s.pipe.client, 0, b"", false), + Err(Error::Done) + ); + assert_eq!(s.client.send_body(&mut s.pipe.client, 0, b"", true), Ok(0)); + + s.advance().ok(); + + let mut recv_buf = vec![0; 100]; + + let ev_headers = Event::Headers { + list: req, + has_body: true, + }; + + assert_eq!(s.poll_server(), Ok((stream, ev_headers))); + + assert_eq!(s.poll_server(), Ok((stream, Event::Data))); + assert_eq!(s.recv_body_server(stream, &mut recv_buf), Err(Error::Done)); + + assert_eq!(s.poll_server(), Ok((stream, Event::Finished))); + assert_eq!(s.poll_server(), Err(Error::Done)); + + let resp = s.send_response(stream, false).unwrap(); + + assert_eq!( + s.server.send_body(&mut s.pipe.server, 0, b"", false), + Err(Error::Done) + ); + assert_eq!(s.server.send_body(&mut s.pipe.server, 0, b"", true), Ok(0)); + + s.advance().ok(); + + let ev_headers = Event::Headers { + list: resp, + has_body: true, + }; + + assert_eq!(s.poll_client(), Ok((stream, ev_headers))); + + assert_eq!(s.poll_client(), Ok((stream, Event::Data))); + assert_eq!(s.recv_body_client(stream, &mut recv_buf), Err(Error::Done)); + + assert_eq!(s.poll_client(), Ok((stream, Event::Finished))); + assert_eq!(s.poll_client(), Err(Error::Done)); + } + + #[test] /// Tests that blocked 0-length DATA writes are reported correctly. fn zero_length_data_blocked() { let mut config = crate::Config::new(crate::PROTOCOL_VERSION).unwrap(); @@ -3123,7 +3621,7 @@ mod tests { .load_priv_key_from_pem_file("examples/cert.key") .unwrap(); config.set_application_protos(b"\x02h3").unwrap(); - config.set_initial_max_data(70); + config.set_initial_max_data(69); config.set_initial_max_stream_data_bidi_local(150); config.set_initial_max_stream_data_bidi_remote(150); config.set_initial_max_stream_data_uni(150); @@ -3138,10 +3636,10 @@ mod tests { s.handshake().unwrap(); let req = vec![ - Header::new(":method", "GET"), - Header::new(":scheme", "https"), - Header::new(":authority", "quic.tech"), - Header::new(":path", "/test"), + Header::new(b":method", b"GET"), + Header::new(b":scheme", b"https"), + Header::new(b":authority", b"quic.tech"), + Header::new(b":path", b"/test"), ]; assert_eq!( @@ -3159,8 +3657,837 @@ mod tests { // Once the server gives flow control credits back, we can send the body. assert_eq!(s.client.send_body(&mut s.pipe.client, 0, b"", true), Ok(0)); } + + #[test] + /// Tests that receiving a H3_DATAGRAM setting is ok. + fn dgram_setting() { + let mut config = crate::Config::new(crate::PROTOCOL_VERSION).unwrap(); + config + .load_cert_chain_from_pem_file("examples/cert.crt") + .unwrap(); + config + .load_priv_key_from_pem_file("examples/cert.key") + .unwrap(); + config.set_application_protos(b"\x02h3").unwrap(); + config.set_initial_max_data(70); + config.set_initial_max_stream_data_bidi_local(150); + config.set_initial_max_stream_data_bidi_remote(150); + config.set_initial_max_stream_data_uni(150); + config.set_initial_max_streams_bidi(100); + config.set_initial_max_streams_uni(5); + config.enable_dgram(true, 1000, 1000); + config.verify_peer(false); + + let h3_config = Config::new().unwrap(); + + let mut s = Session::with_configs(&mut config, &h3_config).unwrap(); + assert_eq!(s.pipe.handshake(), Ok(())); + + s.client.send_settings(&mut s.pipe.client).unwrap(); + assert_eq!(s.pipe.advance(), Ok(())); + + // Before processing SETTINGS (via poll), HTTP/3 DATAGRAMS are not + // enabled. + assert!(!s.server.dgram_enabled_by_peer(&s.pipe.server)); + + // When everything is ok, poll returns Done and DATAGRAM is enabled. + assert_eq!(s.server.poll(&mut s.pipe.server), Err(Error::Done)); + assert!(s.server.dgram_enabled_by_peer(&s.pipe.server)); + + // Now detect things on the client + s.server.send_settings(&mut s.pipe.server).unwrap(); + assert_eq!(s.pipe.advance(), Ok(())); + assert!(!s.client.dgram_enabled_by_peer(&s.pipe.client)); + assert_eq!(s.client.poll(&mut s.pipe.client), Err(Error::Done)); + assert!(s.client.dgram_enabled_by_peer(&s.pipe.client)); + } + + #[test] + /// Tests that receiving a H3_DATAGRAM setting when no TP is set generates + /// an error. + fn dgram_setting_no_tp() { + let mut config = crate::Config::new(crate::PROTOCOL_VERSION).unwrap(); + config + .load_cert_chain_from_pem_file("examples/cert.crt") + .unwrap(); + config + .load_priv_key_from_pem_file("examples/cert.key") + .unwrap(); + config.set_application_protos(b"\x02h3").unwrap(); + config.set_initial_max_data(70); + config.set_initial_max_stream_data_bidi_local(150); + config.set_initial_max_stream_data_bidi_remote(150); + config.set_initial_max_stream_data_uni(150); + config.set_initial_max_streams_bidi(100); + config.set_initial_max_streams_uni(5); + config.verify_peer(false); + + let h3_config = Config::new().unwrap(); + + let mut s = Session::with_configs(&mut config, &h3_config).unwrap(); + assert_eq!(s.pipe.handshake(), Ok(())); + + s.client.control_stream_id = Some( + s.client + .open_uni_stream( + &mut s.pipe.client, + stream::HTTP3_CONTROL_STREAM_TYPE_ID, + ) + .unwrap(), + ); + + let settings = frame::Frame::Settings { + max_header_list_size: None, + qpack_max_table_capacity: None, + qpack_blocked_streams: None, + h3_datagram: Some(1), + grease: None, + }; + + s.send_frame_client(settings, s.client.control_stream_id.unwrap(), false) + .unwrap(); + + assert_eq!(s.pipe.advance(), Ok(())); + + assert_eq!(s.server.poll(&mut s.pipe.server), Err(Error::SettingsError)); + } + + #[test] + /// Tests that receiving SETTINGS with prohibited values generates an error. + fn settings_h2_prohibited() { + let mut config = crate::Config::new(crate::PROTOCOL_VERSION).unwrap(); + config + .load_cert_chain_from_pem_file("examples/cert.crt") + .unwrap(); + config + .load_priv_key_from_pem_file("examples/cert.key") + .unwrap(); + config.set_application_protos(b"\x02h3").unwrap(); + config.set_initial_max_data(70); + config.set_initial_max_stream_data_bidi_local(150); + config.set_initial_max_stream_data_bidi_remote(150); + config.set_initial_max_stream_data_uni(150); + config.set_initial_max_streams_bidi(100); + config.set_initial_max_streams_uni(5); + config.verify_peer(false); + + let h3_config = Config::new().unwrap(); + + let mut s = Session::with_configs(&mut config, &h3_config).unwrap(); + assert_eq!(s.pipe.handshake(), Ok(())); + + s.client.control_stream_id = Some( + s.client + .open_uni_stream( + &mut s.pipe.client, + stream::HTTP3_CONTROL_STREAM_TYPE_ID, + ) + .unwrap(), + ); + + s.server.control_stream_id = Some( + s.server + .open_uni_stream( + &mut s.pipe.server, + stream::HTTP3_CONTROL_STREAM_TYPE_ID, + ) + .unwrap(), + ); + + let frame_payload_len = 2u64; + let settings = [ + frame::SETTINGS_FRAME_TYPE_ID as u8, + frame_payload_len as u8, + 0x2, // 0x2 is a reserved setting type + 1, + ]; + + s.send_arbitrary_stream_data_client( + &settings, + s.client.control_stream_id.unwrap(), + false, + ) + .unwrap(); + + s.send_arbitrary_stream_data_server( + &settings, + s.server.control_stream_id.unwrap(), + false, + ) + .unwrap(); + + assert_eq!(s.pipe.advance(), Ok(())); + + assert_eq!(s.server.poll(&mut s.pipe.server), Err(Error::SettingsError)); + + assert_eq!(s.client.poll(&mut s.pipe.client), Err(Error::SettingsError)); + } + + #[test] + /// Send a single DATAGRAM. + fn single_dgram() { + let mut buf = [0; 65535]; + let mut s = Session::default().unwrap(); + s.handshake().unwrap(); + + // We'll send default data of 10 bytes on flow ID 0. + let result = (11, 0, 1); + + s.send_dgram_client(0).unwrap(); + + assert_eq!(s.poll_server(), Ok((0, Event::Datagram))); + assert_eq!(s.recv_dgram_server(&mut buf), Ok(result)); + assert_eq!(s.poll_server(), Err(Error::Done)); + + s.send_dgram_server(0).unwrap(); + assert_eq!(s.poll_client(), Ok((0, Event::Datagram))); + assert_eq!(s.recv_dgram_client(&mut buf), Ok(result)); + } + + #[test] + /// Send multiple DATAGRAMs. + fn multiple_dgram() { + let mut buf = [0; 65535]; + let mut s = Session::default().unwrap(); + s.handshake().unwrap(); + + // We'll send default data of 10 bytes on flow ID 0. + let result = (11, 0, 1); + + s.send_dgram_client(0).unwrap(); + s.send_dgram_client(0).unwrap(); + s.send_dgram_client(0).unwrap(); + + assert_eq!(s.poll_server(), Ok((0, Event::Datagram))); + assert_eq!(s.recv_dgram_server(&mut buf), Ok(result)); + assert_eq!(s.poll_server(), Err(Error::Done)); + assert_eq!(s.recv_dgram_server(&mut buf), Ok(result)); + assert_eq!(s.poll_server(), Err(Error::Done)); + assert_eq!(s.recv_dgram_server(&mut buf), Ok(result)); + assert_eq!(s.poll_server(), Err(Error::Done)); + assert_eq!(s.recv_dgram_server(&mut buf), Err(Error::Done)); + assert_eq!(s.poll_server(), Err(Error::Done)); + + s.send_dgram_server(0).unwrap(); + s.send_dgram_server(0).unwrap(); + s.send_dgram_server(0).unwrap(); + + assert_eq!(s.poll_client(), Ok((0, Event::Datagram))); + assert_eq!(s.poll_server(), Err(Error::Done)); + assert_eq!(s.recv_dgram_client(&mut buf), Ok(result)); + assert_eq!(s.poll_client(), Err(Error::Done)); + assert_eq!(s.recv_dgram_client(&mut buf), Ok(result)); + assert_eq!(s.poll_client(), Err(Error::Done)); + assert_eq!(s.recv_dgram_client(&mut buf), Ok(result)); + assert_eq!(s.poll_client(), Err(Error::Done)); + assert_eq!(s.recv_dgram_client(&mut buf), Err(Error::Done)); + assert_eq!(s.poll_client(), Err(Error::Done)); + } + + #[test] + /// Send more DATAGRAMs than the send queue allows. + fn multiple_dgram_overflow() { + let mut buf = [0; 65535]; + let mut s = Session::default().unwrap(); + s.handshake().unwrap(); + + // We'll send default data of 10 bytes on flow ID 0. + let result = (11, 0, 1); + + // Five DATAGRAMs + s.send_dgram_client(0).unwrap(); + s.send_dgram_client(0).unwrap(); + s.send_dgram_client(0).unwrap(); + s.send_dgram_client(0).unwrap(); + s.send_dgram_client(0).unwrap(); + + // Only 3 independent DATAGRAM events will fire. + assert_eq!(s.poll_server(), Ok((0, Event::Datagram))); + assert_eq!(s.recv_dgram_server(&mut buf), Ok(result)); + assert_eq!(s.poll_server(), Err(Error::Done)); + assert_eq!(s.recv_dgram_server(&mut buf), Ok(result)); + assert_eq!(s.poll_server(), Err(Error::Done)); + assert_eq!(s.recv_dgram_server(&mut buf), Ok(result)); + assert_eq!(s.poll_server(), Err(Error::Done)); + assert_eq!(s.recv_dgram_server(&mut buf), Err(Error::Done)); + assert_eq!(s.poll_server(), Err(Error::Done)); + } + + #[test] + /// Send a single DATAGRAM and request. Ensure that poll continuously cycles + /// between the two types if the data is not read. + fn poll_yield_cycling() { + let mut config = crate::Config::new(crate::PROTOCOL_VERSION).unwrap(); + config + .load_cert_chain_from_pem_file("examples/cert.crt") + .unwrap(); + config + .load_priv_key_from_pem_file("examples/cert.key") + .unwrap(); + config.set_application_protos(b"\x02h3").unwrap(); + config.set_initial_max_data(1500); + config.set_initial_max_stream_data_bidi_local(150); + config.set_initial_max_stream_data_bidi_remote(150); + config.set_initial_max_stream_data_uni(150); + config.set_initial_max_streams_bidi(100); + config.set_initial_max_streams_uni(5); + config.verify_peer(false); + config.enable_dgram(true, 100, 100); + + let mut h3_config = Config::new().unwrap(); + let mut s = Session::with_configs(&mut config, &mut h3_config).unwrap(); + s.handshake().unwrap(); + + // Send request followed by DATAGRAM on client side. + let (stream, req) = s.send_request(false).unwrap(); + + s.send_body_client(stream, true).unwrap(); + + let ev_headers = Event::Headers { + list: req, + has_body: true, + }; + + s.send_dgram_client(0).unwrap(); + + // Now let's test the poll counts and yielding. + assert_eq!(s.poll_server(), Ok((0, Event::Datagram))); + + assert_eq!(s.poll_server(), Ok((stream, ev_headers))); + assert_eq!(s.poll_server(), Ok((stream, Event::Data))); + + assert_eq!(s.poll_server(), Err(Error::Done)); + } + + #[test] + /// Send a single DATAGRAM and request. Ensure that poll + /// yield cycles and cleanly exits if data is read. + fn poll_yield_single_read() { + let mut buf = [0; 65535]; + + let mut config = crate::Config::new(crate::PROTOCOL_VERSION).unwrap(); + config + .load_cert_chain_from_pem_file("examples/cert.crt") + .unwrap(); + config + .load_priv_key_from_pem_file("examples/cert.key") + .unwrap(); + config.set_application_protos(b"\x02h3").unwrap(); + config.set_initial_max_data(1500); + config.set_initial_max_stream_data_bidi_local(150); + config.set_initial_max_stream_data_bidi_remote(150); + config.set_initial_max_stream_data_uni(150); + config.set_initial_max_streams_bidi(100); + config.set_initial_max_streams_uni(5); + config.verify_peer(false); + config.enable_dgram(true, 100, 100); + + let mut h3_config = Config::new().unwrap(); + let mut s = Session::with_configs(&mut config, &mut h3_config).unwrap(); + s.handshake().unwrap(); + + // We'll send default data of 10 bytes on flow ID 0. + let result = (11, 0, 1); + + // Send request followed by DATAGRAM on client side. + let (stream, req) = s.send_request(false).unwrap(); + + let body = s.send_body_client(stream, true).unwrap(); + + let mut recv_buf = vec![0; body.len()]; + + let ev_headers = Event::Headers { + list: req, + has_body: true, + }; + + s.send_dgram_client(0).unwrap(); + + // Now let's test the poll counts and yielding. + assert_eq!(s.poll_server(), Ok((0, Event::Datagram))); + + assert_eq!(s.poll_server(), Ok((stream, ev_headers))); + assert_eq!(s.poll_server(), Ok((stream, Event::Data))); + + assert_eq!(s.poll_server(), Err(Error::Done)); + + assert_eq!(s.recv_dgram_server(&mut buf), Ok(result)); + + assert_eq!(s.poll_server(), Err(Error::Done)); + + assert_eq!(s.recv_body_server(stream, &mut recv_buf), Ok(body.len())); + assert_eq!(s.poll_server(), Ok((stream, Event::Finished))); + assert_eq!(s.poll_server(), Err(Error::Done)); + + // Send response followed by DATAGRAM on server side + let resp = s.send_response(stream, false).unwrap(); + + let body = s.send_body_server(stream, true).unwrap(); + + let mut recv_buf = vec![0; body.len()]; + + let ev_headers = Event::Headers { + list: resp, + has_body: true, + }; + + s.send_dgram_server(0).unwrap(); + + // Now let's test the poll counts and yielding. + assert_eq!(s.poll_client(), Ok((0, Event::Datagram))); + + assert_eq!(s.poll_client(), Ok((stream, ev_headers))); + assert_eq!(s.poll_client(), Ok((stream, Event::Data))); + + assert_eq!(s.poll_client(), Err(Error::Done)); + + assert_eq!(s.recv_dgram_client(&mut buf), Ok(result)); + + assert_eq!(s.poll_client(), Err(Error::Done)); + + assert_eq!(s.recv_body_client(stream, &mut recv_buf), Ok(body.len())); + + assert_eq!(s.poll_client(), Ok((stream, Event::Finished))); + assert_eq!(s.poll_client(), Err(Error::Done)); + } + + #[test] + /// Send a multiple DATAGRAMs and requests. Ensure that poll + /// yield cycles and cleanly exits if data is read. + fn poll_yield_multi_read() { + let mut buf = [0; 65535]; + + let mut config = crate::Config::new(crate::PROTOCOL_VERSION).unwrap(); + config + .load_cert_chain_from_pem_file("examples/cert.crt") + .unwrap(); + config + .load_priv_key_from_pem_file("examples/cert.key") + .unwrap(); + config.set_application_protos(b"\x02h3").unwrap(); + config.set_initial_max_data(1500); + config.set_initial_max_stream_data_bidi_local(150); + config.set_initial_max_stream_data_bidi_remote(150); + config.set_initial_max_stream_data_uni(150); + config.set_initial_max_streams_bidi(100); + config.set_initial_max_streams_uni(5); + config.verify_peer(false); + config.enable_dgram(true, 100, 100); + + let mut h3_config = Config::new().unwrap(); + let mut s = Session::with_configs(&mut config, &mut h3_config).unwrap(); + s.handshake().unwrap(); + + // 10 bytes on flow ID 0 and 2. + let flow_0_result = (11, 0, 1); + let flow_2_result = (11, 2, 1); + + // Send requests followed by DATAGRAMs on client side. + let (stream, req) = s.send_request(false).unwrap(); + + let body = s.send_body_client(stream, true).unwrap(); + + let mut recv_buf = vec![0; body.len()]; + + let ev_headers = Event::Headers { + list: req, + has_body: true, + }; + + s.send_dgram_client(0).unwrap(); + s.send_dgram_client(0).unwrap(); + s.send_dgram_client(0).unwrap(); + s.send_dgram_client(0).unwrap(); + s.send_dgram_client(0).unwrap(); + s.send_dgram_client(2).unwrap(); + s.send_dgram_client(2).unwrap(); + s.send_dgram_client(2).unwrap(); + s.send_dgram_client(2).unwrap(); + s.send_dgram_client(2).unwrap(); + + // Now let's test the poll counts and yielding. + assert_eq!(s.poll_server(), Ok((0, Event::Datagram))); + + assert_eq!(s.poll_server(), Ok((stream, ev_headers))); + assert_eq!(s.poll_server(), Ok((stream, Event::Data))); + + assert_eq!(s.poll_server(), Err(Error::Done)); + + // Second cycle, start to read + assert_eq!(s.recv_dgram_server(&mut buf), Ok(flow_0_result)); + assert_eq!(s.poll_server(), Err(Error::Done)); + assert_eq!(s.recv_dgram_server(&mut buf), Ok(flow_0_result)); + assert_eq!(s.poll_server(), Err(Error::Done)); + assert_eq!(s.recv_dgram_server(&mut buf), Ok(flow_0_result)); + assert_eq!(s.poll_server(), Err(Error::Done)); + + assert_eq!(s.recv_body_server(stream, &mut recv_buf), Ok(body.len())); + assert_eq!(s.poll_server(), Ok((stream, Event::Finished))); + + assert_eq!(s.poll_server(), Err(Error::Done)); + + // Third cycle. + assert_eq!(s.recv_dgram_server(&mut buf), Ok(flow_0_result)); + assert_eq!(s.poll_server(), Err(Error::Done)); + assert_eq!(s.recv_dgram_server(&mut buf), Ok(flow_0_result)); + assert_eq!(s.poll_server(), Err(Error::Done)); + assert_eq!(s.recv_dgram_server(&mut buf), Ok(flow_2_result)); + assert_eq!(s.poll_server(), Err(Error::Done)); + assert_eq!(s.recv_dgram_server(&mut buf), Ok(flow_2_result)); + assert_eq!(s.poll_server(), Err(Error::Done)); + assert_eq!(s.recv_dgram_server(&mut buf), Ok(flow_2_result)); + assert_eq!(s.poll_server(), Err(Error::Done)); + assert_eq!(s.recv_dgram_server(&mut buf), Ok(flow_2_result)); + assert_eq!(s.poll_server(), Err(Error::Done)); + assert_eq!(s.recv_dgram_server(&mut buf), Ok(flow_2_result)); + assert_eq!(s.poll_server(), Err(Error::Done)); + + // Send response followed by DATAGRAM on server side + let resp = s.send_response(stream, false).unwrap(); + + let body = s.send_body_server(stream, true).unwrap(); + + let mut recv_buf = vec![0; body.len()]; + + let ev_headers = Event::Headers { + list: resp, + has_body: true, + }; + + s.send_dgram_server(0).unwrap(); + s.send_dgram_server(0).unwrap(); + s.send_dgram_server(0).unwrap(); + s.send_dgram_server(0).unwrap(); + s.send_dgram_server(0).unwrap(); + s.send_dgram_server(2).unwrap(); + s.send_dgram_server(2).unwrap(); + s.send_dgram_server(2).unwrap(); + s.send_dgram_server(2).unwrap(); + s.send_dgram_server(2).unwrap(); + + assert_eq!(s.poll_client(), Ok((0, Event::Datagram))); + + assert_eq!(s.poll_client(), Ok((stream, ev_headers))); + assert_eq!(s.poll_client(), Ok((stream, Event::Data))); + + assert_eq!(s.poll_client(), Err(Error::Done)); + + // Second cycle, start to read + assert_eq!(s.recv_dgram_client(&mut buf), Ok(flow_0_result)); + assert_eq!(s.poll_client(), Err(Error::Done)); + assert_eq!(s.recv_dgram_client(&mut buf), Ok(flow_0_result)); + assert_eq!(s.poll_client(), Err(Error::Done)); + assert_eq!(s.recv_dgram_client(&mut buf), Ok(flow_0_result)); + assert_eq!(s.poll_client(), Err(Error::Done)); + + assert_eq!(s.recv_body_client(stream, &mut recv_buf), Ok(body.len())); + assert_eq!(s.poll_client(), Ok((stream, Event::Finished))); + + assert_eq!(s.poll_client(), Err(Error::Done)); + + // Third cycle. + assert_eq!(s.recv_dgram_client(&mut buf), Ok(flow_0_result)); + assert_eq!(s.poll_client(), Err(Error::Done)); + assert_eq!(s.recv_dgram_client(&mut buf), Ok(flow_0_result)); + assert_eq!(s.poll_client(), Err(Error::Done)); + assert_eq!(s.recv_dgram_client(&mut buf), Ok(flow_2_result)); + assert_eq!(s.poll_client(), Err(Error::Done)); + assert_eq!(s.recv_dgram_client(&mut buf), Ok(flow_2_result)); + assert_eq!(s.poll_client(), Err(Error::Done)); + assert_eq!(s.recv_dgram_client(&mut buf), Ok(flow_2_result)); + assert_eq!(s.poll_client(), Err(Error::Done)); + assert_eq!(s.recv_dgram_client(&mut buf), Ok(flow_2_result)); + assert_eq!(s.poll_client(), Err(Error::Done)); + assert_eq!(s.recv_dgram_client(&mut buf), Ok(flow_2_result)); + assert_eq!(s.poll_client(), Err(Error::Done)); + } + + #[test] + /// Tests that the Finished event is not issued for streams of unknown type + /// (e.g. GREASE). + fn finished_is_for_requests() { + let mut s = Session::default().unwrap(); + s.handshake().unwrap(); + + assert_eq!(s.poll_client(), Err(Error::Done)); + assert_eq!(s.poll_server(), Err(Error::Done)); + + assert_eq!(s.client.open_grease_stream(&mut s.pipe.client), Ok(())); + assert_eq!(s.pipe.advance(), Ok(())); + + assert_eq!(s.poll_client(), Err(Error::Done)); + assert_eq!(s.poll_server(), Err(Error::Done)); + } + + #[test] + /// Tests that streams are marked as finished only once. + fn finished_once() { + let mut s = Session::default().unwrap(); + s.handshake().unwrap(); + + let (stream, req) = s.send_request(false).unwrap(); + let body = s.send_body_client(stream, true).unwrap(); + + let mut recv_buf = vec![0; body.len()]; + + let ev_headers = Event::Headers { + list: req, + has_body: true, + }; + + assert_eq!(s.poll_server(), Ok((stream, ev_headers))); + assert_eq!(s.poll_server(), Ok((stream, Event::Data))); + + assert_eq!(s.recv_body_server(stream, &mut recv_buf), Ok(body.len())); + assert_eq!(s.poll_server(), Ok((stream, Event::Finished))); + + assert_eq!(s.recv_body_server(stream, &mut recv_buf), Err(Error::Done)); + assert_eq!(s.poll_server(), Err(Error::Done)); + } + + #[test] + /// Tests that the Data event is properly re-armed. + fn data_event_rearm() { + let bytes = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; + + let mut s = Session::default().unwrap(); + s.handshake().unwrap(); + + let (stream, req) = s.send_request(false).unwrap(); + + let mut recv_buf = vec![0; bytes.len()]; + + let ev_headers = Event::Headers { + list: req, + has_body: true, + }; + + // Manually send an incomplete DATA frame (i.e. the frame size is longer + // than the actual data sent). + { + let mut d = [42; 10]; + let mut b = octets::OctetsMut::with_slice(&mut d); + + b.put_varint(frame::DATA_FRAME_TYPE_ID).unwrap(); + b.put_varint(bytes.len() as u64).unwrap(); + let off = b.off(); + s.pipe.client.stream_send(stream, &d[..off], false).unwrap(); + + assert_eq!( + s.pipe.client.stream_send(stream, &bytes[..5], false), + Ok(5) + ); + + s.advance().ok(); + } + + assert_eq!(s.poll_server(), Ok((stream, ev_headers))); + assert_eq!(s.poll_server(), Ok((stream, Event::Data))); + assert_eq!(s.poll_server(), Err(Error::Done)); + + // Read the available body data. + assert_eq!(s.recv_body_server(stream, &mut recv_buf), Ok(5)); + + // Send the remaining DATA payload. + assert_eq!(s.pipe.client.stream_send(stream, &bytes[5..], false), Ok(5)); + s.advance().ok(); + + assert_eq!(s.poll_server(), Ok((stream, Event::Data))); + assert_eq!(s.poll_server(), Err(Error::Done)); + + // Read the rest of the body data. + assert_eq!(s.recv_body_server(stream, &mut recv_buf), Ok(5)); + assert_eq!(s.poll_server(), Err(Error::Done)); + + // Send more data. + let body = s.send_body_client(stream, false).unwrap(); + + assert_eq!(s.poll_server(), Ok((stream, Event::Data))); + assert_eq!(s.poll_server(), Err(Error::Done)); + + assert_eq!(s.recv_body_server(stream, &mut recv_buf), Ok(body.len())); + + // Send more data, then HEADERS, then more data. + let body = s.send_body_client(stream, false).unwrap(); + + let trailers = vec![Header::new(b"hello", b"world")]; + + s.client + .send_headers(&mut s.pipe.client, stream, &trailers, false) + .unwrap(); + + let ev_trailers = Event::Headers { + list: trailers, + has_body: true, + }; + + s.advance().ok(); + + s.send_body_client(stream, false).unwrap(); + + assert_eq!(s.poll_server(), Ok((stream, Event::Data))); + assert_eq!(s.recv_body_server(stream, &mut recv_buf), Ok(body.len())); + + assert_eq!(s.poll_server(), Ok((stream, ev_trailers))); + + assert_eq!(s.poll_server(), Ok((stream, Event::Data))); + assert_eq!(s.recv_body_server(stream, &mut recv_buf), Ok(body.len())); + + let (stream, req) = s.send_request(false).unwrap(); + + let ev_headers = Event::Headers { + list: req, + has_body: true, + }; + + // Manually send an incomplete DATA frame (i.e. only the header is sent). + { + let mut d = [42; 10]; + let mut b = octets::OctetsMut::with_slice(&mut d); + + b.put_varint(frame::DATA_FRAME_TYPE_ID).unwrap(); + b.put_varint(bytes.len() as u64).unwrap(); + let off = b.off(); + s.pipe.client.stream_send(stream, &d[..off], false).unwrap(); + + s.advance().ok(); + } + + assert_eq!(s.poll_server(), Ok((stream, ev_headers))); + assert_eq!(s.poll_server(), Ok((stream, Event::Data))); + assert_eq!(s.poll_server(), Err(Error::Done)); + + assert_eq!(s.recv_body_server(stream, &mut recv_buf), Err(Error::Done)); + + assert_eq!(s.pipe.client.stream_send(stream, &bytes[..5], false), Ok(5)); + + s.advance().ok(); + + assert_eq!(s.poll_server(), Ok((stream, Event::Data))); + assert_eq!(s.poll_server(), Err(Error::Done)); + + assert_eq!(s.recv_body_server(stream, &mut recv_buf), Ok(5)); + + assert_eq!(s.pipe.client.stream_send(stream, &bytes[5..], false), Ok(5)); + s.advance().ok(); + + assert_eq!(s.poll_server(), Ok((stream, Event::Data))); + assert_eq!(s.poll_server(), Err(Error::Done)); + + assert_eq!(s.recv_body_server(stream, &mut recv_buf), Ok(5)); + + // Buffer multiple data frames. + let body = s.send_body_client(stream, false).unwrap(); + s.send_body_client(stream, false).unwrap(); + s.send_body_client(stream, false).unwrap(); + + assert_eq!(s.poll_server(), Ok((stream, Event::Data))); + assert_eq!(s.poll_server(), Err(Error::Done)); + + { + let mut d = [42; 10]; + let mut b = octets::OctetsMut::with_slice(&mut d); + + b.put_varint(frame::DATA_FRAME_TYPE_ID).unwrap(); + b.put_varint(0).unwrap(); + let off = b.off(); + s.pipe.client.stream_send(stream, &d[..off], true).unwrap(); + + s.advance().ok(); + } + + let mut recv_buf = vec![0; bytes.len() * 3]; + + assert_eq!( + s.recv_body_server(stream, &mut recv_buf), + Ok(body.len() * 3) + ); + } + + #[test] + /// Tests that the Datagram event is properly re-armed. + fn dgram_event_rearm() { + let mut buf = [0; 65535]; + + let mut config = crate::Config::new(crate::PROTOCOL_VERSION).unwrap(); + config + .load_cert_chain_from_pem_file("examples/cert.crt") + .unwrap(); + config + .load_priv_key_from_pem_file("examples/cert.key") + .unwrap(); + config.set_application_protos(b"\x02h3").unwrap(); + config.set_initial_max_data(1500); + config.set_initial_max_stream_data_bidi_local(150); + config.set_initial_max_stream_data_bidi_remote(150); + config.set_initial_max_stream_data_uni(150); + config.set_initial_max_streams_bidi(100); + config.set_initial_max_streams_uni(5); + config.verify_peer(false); + config.enable_dgram(true, 100, 100); + + let mut h3_config = Config::new().unwrap(); + let mut s = Session::with_configs(&mut config, &mut h3_config).unwrap(); + s.handshake().unwrap(); + + // 10 bytes on flow ID 0 and 2. + let flow_0_result = (11, 0, 1); + let flow_2_result = (11, 2, 1); + + // Send requests followed by DATAGRAMs on client side. + let (stream, req) = s.send_request(false).unwrap(); + + let body = s.send_body_client(stream, true).unwrap(); + + let mut recv_buf = vec![0; body.len()]; + + let ev_headers = Event::Headers { + list: req, + has_body: true, + }; + + s.send_dgram_client(0).unwrap(); + s.send_dgram_client(0).unwrap(); + s.send_dgram_client(2).unwrap(); + s.send_dgram_client(2).unwrap(); + + assert_eq!(s.poll_server(), Ok((0, Event::Datagram))); + + assert_eq!(s.poll_server(), Ok((stream, ev_headers))); + assert_eq!(s.poll_server(), Ok((stream, Event::Data))); + + assert_eq!(s.poll_server(), Err(Error::Done)); + assert_eq!(s.recv_dgram_server(&mut buf), Ok(flow_0_result)); + + assert_eq!(s.poll_server(), Err(Error::Done)); + assert_eq!(s.recv_dgram_server(&mut buf), Ok(flow_0_result)); + + assert_eq!(s.poll_server(), Err(Error::Done)); + assert_eq!(s.recv_dgram_server(&mut buf), Ok(flow_2_result)); + + assert_eq!(s.poll_server(), Err(Error::Done)); + assert_eq!(s.recv_dgram_server(&mut buf), Ok(flow_2_result)); + + assert_eq!(s.poll_server(), Err(Error::Done)); + + s.send_dgram_client(0).unwrap(); + s.send_dgram_client(2).unwrap(); + + assert_eq!(s.poll_server(), Ok((0, Event::Datagram))); + assert_eq!(s.poll_server(), Err(Error::Done)); + + assert_eq!(s.recv_dgram_server(&mut buf), Ok(flow_0_result)); + assert_eq!(s.poll_server(), Err(Error::Done)); + + assert_eq!(s.recv_dgram_server(&mut buf), Ok(flow_2_result)); + assert_eq!(s.poll_server(), Err(Error::Done)); + + assert_eq!(s.recv_body_server(stream, &mut recv_buf), Ok(body.len())); + assert_eq!(s.poll_server(), Ok((stream, Event::Finished))); + } } +#[cfg(feature = "ffi")] mod ffi; mod frame; #[doc(hidden)] diff --git a/src/h3/qpack/decoder.rs b/src/h3/qpack/decoder.rs index 240ca69..1bc5755 100644 --- a/src/h3/qpack/decoder.rs +++ b/src/h3/qpack/decoder.rs @@ -149,9 +149,7 @@ impl Decoder { name.to_vec() }; - let name = String::from_utf8(name) - .map_err(|_| Error::InvalidHeaderValue)?; - + let name = name.to_vec(); let value = decode_str(&mut b)?; trace!( @@ -198,7 +196,7 @@ impl Decoder { // Instead of calling Header::new(), create Header directly // from `value`, which is already String, but clone `name` // as it is just a reference. - let hdr = Header(name.to_string(), value); + let hdr = Header(name.to_vec(), value); out.push(hdr); }, @@ -215,7 +213,7 @@ impl Decoder { } } -fn lookup_static(idx: u64) -> Result<(&'static str, &'static str)> { +fn lookup_static(idx: u64) -> Result<(&'static [u8], &'static [u8])> { if idx >= super::static_table::STATIC_TABLE.len() as u64 { return Err(Error::InvalidStaticTableIndex); } @@ -254,7 +252,7 @@ fn decode_int(b: &mut octets::Octets, prefix: usize) -> Result<u64> { Err(Error::BufferTooShort) } -fn decode_str<'a>(b: &'a mut octets::Octets) -> Result<String> { +fn decode_str(b: &mut octets::Octets) -> Result<Vec<u8>> { let first = b.peek_u8()?; let huff = first & 0x80 == 0x80; @@ -269,7 +267,6 @@ fn decode_str<'a>(b: &'a mut octets::Octets) -> Result<String> { val.to_vec() }; - let val = String::from_utf8(val).map_err(|_| Error::InvalidHeaderValue)?; Ok(val) } diff --git a/src/h3/qpack/encoder.rs b/src/h3/qpack/encoder.rs index 1307df3..09c8b08 100644 --- a/src/h3/qpack/encoder.rs +++ b/src/h3/qpack/encoder.rs @@ -55,7 +55,7 @@ impl Encoder { ) -> Result<usize> { let mut b = octets::OctetsMut::with_slice(out); - // Request Insert Count. + // Required Insert Count. encode_int(0, 0, 8, &mut b)?; // Base. @@ -80,14 +80,12 @@ impl Encoder { None => { // Encode as fully literal. - let name_len = super::huffman::encode_output_length( - h.name().as_bytes(), - true, - )?; + let name_len = + super::huffman::encode_output_length(h.name(), true)?; encode_int(name_len as u64, LITERAL | 0x08, 3, &mut b)?; - super::huffman::encode(h.name().as_bytes(), &mut b, true)?; + super::huffman::encode(h.name(), &mut b, true)?; encode_str(h.value(), 7, &mut b)?; }, @@ -151,12 +149,12 @@ fn encode_int( Ok(()) } -fn encode_str(v: &str, prefix: usize, b: &mut octets::OctetsMut) -> Result<()> { - let len = super::huffman::encode_output_length(v.as_bytes(), false)?; +fn encode_str(v: &[u8], prefix: usize, b: &mut octets::OctetsMut) -> Result<()> { + let len = super::huffman::encode_output_length(v, false)?; encode_int(len as u64, 0x80, prefix, b)?; - super::huffman::encode(v.as_bytes(), b, false)?; + super::huffman::encode(v, b, false)?; Ok(()) } diff --git a/src/h3/qpack/mod.rs b/src/h3/qpack/mod.rs index 6f2bdda..0a23306 100644 --- a/src/h3/qpack/mod.rs +++ b/src/h3/qpack/mod.rs @@ -87,15 +87,15 @@ mod tests { let mut encoded = [0u8; 240]; let headers = vec![ - h3::Header::new(":path", "/rsrc.php/v3/yn/r/rIPZ9Qkrdd9.png"), - h3::Header::new("accept-encoding", "gzip, deflate, br"), - h3::Header::new("accept-language", "en-US,en;q=0.9"), - h3::Header::new("user-agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.70 Safari/537.36"), - h3::Header::new("accept", "image/webp,image/apng,image/*,*/*;q=0.8"), - h3::Header::new("referer", "https://static.xx.fbcdn.net/rsrc.php/v3/yT/l/0,cross/dzXGESIlGQQ.css"), - h3::Header::new(":authority", "static.xx.fbcdn.net"), - h3::Header::new(":scheme", "https"), - h3::Header::new(":method", "GET"), + h3::Header::new(b":path", b"/rsrc.php/v3/yn/r/rIPZ9Qkrdd9.png"), + h3::Header::new(b"accept-encoding", b"gzip, deflate, br"), + h3::Header::new(b"accept-language", b"en-US,en;q=0.9"), + h3::Header::new(b"user-agent", b"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.70 Safari/537.36"), + h3::Header::new(b"accept", b"image/webp,image/apng,image/*,*/*;q=0.8"), + h3::Header::new(b"referer", b"https://static.xx.fbcdn.net/rsrc.php/v3/yT/l/0,cross/dzXGESIlGQQ.css"), + h3::Header::new(b":authority", b"static.xx.fbcdn.net"), + h3::Header::new(b":scheme", b"https"), + h3::Header::new(b":method", b"GET"), ]; let mut enc = Encoder::new(); @@ -110,20 +110,20 @@ mod tests { let mut encoded = [0u8; 35]; let headers_expected = vec![ - crate::h3::Header::new(":status", "200"), - crate::h3::Header::new(":path", "/HeLlO"), - crate::h3::Header::new("woot", "woot"), - crate::h3::Header::new("hello", "WorlD"), - crate::h3::Header::new("foo", "BaR"), + crate::h3::Header::new(b":status", b"200"), + crate::h3::Header::new(b":path", b"/HeLlO"), + crate::h3::Header::new(b"woot", b"woot"), + crate::h3::Header::new(b"hello", b"WorlD"), + crate::h3::Header::new(b"foo", b"BaR"), ]; // Header. let headers_in = vec![ - crate::h3::Header::new(":StAtUs", "200"), - crate::h3::Header::new(":PaTh", "/HeLlO"), - crate::h3::Header::new("WooT", "woot"), - crate::h3::Header::new("hello", "WorlD"), - crate::h3::Header::new("fOo", "BaR"), + crate::h3::Header::new(b":StAtUs", b"200"), + crate::h3::Header::new(b":PaTh", b"/HeLlO"), + crate::h3::Header::new(b"WooT", b"woot"), + crate::h3::Header::new(b"hello", b"WorlD"), + crate::h3::Header::new(b"fOo", b"BaR"), ]; let mut enc = Encoder::new(); @@ -136,11 +136,11 @@ mod tests { // HeaderRef. let headers_in = vec![ - crate::h3::HeaderRef::new(":StAtUs", "200"), - crate::h3::HeaderRef::new(":PaTh", "/HeLlO"), - crate::h3::HeaderRef::new("WooT", "woot"), - crate::h3::HeaderRef::new("hello", "WorlD"), - crate::h3::HeaderRef::new("fOo", "BaR"), + crate::h3::HeaderRef::new(b":StAtUs", b"200"), + crate::h3::HeaderRef::new(b":PaTh", b"/HeLlO"), + crate::h3::HeaderRef::new(b"WooT", b"woot"), + crate::h3::HeaderRef::new(b"hello", b"WorlD"), + crate::h3::HeaderRef::new(b"fOo", b"BaR"), ]; let mut enc = Encoder::new(); diff --git a/src/h3/qpack/static_table.rs b/src/h3/qpack/static_table.rs index 4010d12..3cc10d4 100644 --- a/src/h3/qpack/static_table.rs +++ b/src/h3/qpack/static_table.rs @@ -24,113 +24,113 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -pub const STATIC_TABLE: [(&str, &str); 99] = [ - (":authority", ""), - (":path", "/"), - ("age", "0"), - ("content-disposition", ""), - ("content-length", "0"), - ("cookie", ""), - ("date", ""), - ("etag", ""), - ("if-modified-since", ""), - ("if-none-match", ""), - ("last-modified", ""), - ("link", ""), - ("location", ""), - ("referer", ""), - ("set-cookie", ""), - (":method", "CONNECT"), - (":method", "DELETE"), - (":method", "GET"), - (":method", "HEAD"), - (":method", "OPTIONS"), - (":method", "POST"), - (":method", "PUT"), - (":scheme", "http"), - (":scheme", "https"), - (":status", "103"), - (":status", "200"), - (":status", "304"), - (":status", "404"), - (":status", "503"), - ("accept", "*/*"), - ("accept", "application/dns-message"), - ("accept-encoding", "gzip, deflate, br"), - ("accept-ranges", "bytes"), - ("access-control-allow-headers", "cache-control"), - ("access-control-allow-headers", "content-type"), - ("access-control-allow-origin", "*"), - ("cache-control", "max-age=0"), - ("cache-control", "max-age=2592000"), - ("cache-control", "max-age=604800"), - ("cache-control", "no-cache"), - ("cache-control", "no-store"), - ("cache-control", "public, max-age=31536000"), - ("content-encoding", "br"), - ("content-encoding", "gzip"), - ("content-type", "application/dns-message"), - ("content-type", "application/javascript"), - ("content-type", "application/json"), - ("content-type", "application/x-www-form-urlencoded"), - ("content-type", "image/gif"), - ("content-type", "image/jpeg"), - ("content-type", "image/png"), - ("content-type", "text/css"), - ("content-type", "text/html; charset=utf-8"), - ("content-type", "text/plain"), - ("content-type", "text/plain;charset=utf-8"), - ("range", "bytes=0-"), - ("strict-transport-security", "max-age=31536000"), +pub const STATIC_TABLE: [(&[u8], &[u8]); 99] = [ + (b":authority", b""), + (b":path", b"/"), + (b"age", b"0"), + (b"content-disposition", b""), + (b"content-length", b"0"), + (b"cookie", b""), + (b"date", b""), + (b"etag", b""), + (b"if-modified-since", b""), + (b"if-none-match", b""), + (b"last-modified", b""), + (b"link", b""), + (b"location", b""), + (b"referer", b""), + (b"set-cookie", b""), + (b":method", b"CONNECT"), + (b":method", b"DELETE"), + (b":method", b"GET"), + (b":method", b"HEAD"), + (b":method", b"OPTIONS"), + (b":method", b"POST"), + (b":method", b"PUT"), + (b":scheme", b"http"), + (b":scheme", b"https"), + (b":status", b"103"), + (b":status", b"200"), + (b":status", b"304"), + (b":status", b"404"), + (b":status", b"503"), + (b"accept", b"*/*"), + (b"accept", b"application/dns-message"), + (b"accept-encoding", b"gzip, deflate, br"), + (b"accept-ranges", b"bytes"), + (b"access-control-allow-headers", b"cache-control"), + (b"access-control-allow-headers", b"content-type"), + (b"access-control-allow-origin", b"*"), + (b"cache-control", b"max-age=0"), + (b"cache-control", b"max-age=2592000"), + (b"cache-control", b"max-age=604800"), + (b"cache-control", b"no-cache"), + (b"cache-control", b"no-store"), + (b"cache-control", b"public, max-age=31536000"), + (b"content-encoding", b"br"), + (b"content-encoding", b"gzip"), + (b"content-type", b"application/dns-message"), + (b"content-type", b"application/javascript"), + (b"content-type", b"application/json"), + (b"content-type", b"application/x-www-form-urlencoded"), + (b"content-type", b"image/gif"), + (b"content-type", b"image/jpeg"), + (b"content-type", b"image/png"), + (b"content-type", b"text/css"), + (b"content-type", b"text/html; charset=utf-8"), + (b"content-type", b"text/plain"), + (b"content-type", b"text/plain;charset=utf-8"), + (b"range", b"bytes=0-"), + (b"strict-transport-security", b"max-age=31536000"), ( - "strict-transport-security", - "max-age=31536000; includesubdomains", + b"strict-transport-security", + b"max-age=31536000; includesubdomains", ), ( - "strict-transport-security", - "max-age=31536000; includesubdomains; preload", + b"strict-transport-security", + b"max-age=31536000; includesubdomains; preload", ), - ("vary", "accept-encoding"), - ("vary", "origin"), - ("x-content-type-options", "nosniff"), - ("x-xss-protection", "1; mode=block"), - (":status", "100"), - (":status", "204"), - (":status", "206"), - (":status", "302"), - (":status", "400"), - (":status", "403"), - (":status", "421"), - (":status", "425"), - (":status", "500"), - ("accept-language", ""), - ("access-control-allow-credentials", "FALSE"), - ("access-control-allow-credentials", "TRUE"), - ("access-control-allow-headers", "*"), - ("access-control-allow-methods", "get"), - ("access-control-allow-methods", "get, post, options"), - ("access-control-allow-methods", "options"), - ("access-control-expose-headers", "content-length"), - ("access-control-request-headers", "content-type"), - ("access-control-request-method", "get"), - ("access-control-request-method", "post"), - ("alt-svc", "clear"), - ("authorization", ""), + (b"vary", b"accept-encoding"), + (b"vary", b"origin"), + (b"x-content-type-options", b"nosniff"), + (b"x-xss-protection", b"1; mode=block"), + (b":status", b"100"), + (b":status", b"204"), + (b":status", b"206"), + (b":status", b"302"), + (b":status", b"400"), + (b":status", b"403"), + (b":status", b"421"), + (b":status", b"425"), + (b":status", b"500"), + (b"accept-language", b""), + (b"access-control-allow-credentials", b"FALSE"), + (b"access-control-allow-credentials", b"TRUE"), + (b"access-control-allow-headers", b"*"), + (b"access-control-allow-methods", b"get"), + (b"access-control-allow-methods", b"get, post, options"), + (b"access-control-allow-methods", b"options"), + (b"access-control-expose-headers", b"content-length"), + (b"access-control-request-headers", b"content-type"), + (b"access-control-request-method", b"get"), + (b"access-control-request-method", b"post"), + (b"alt-svc", b"clear"), + (b"authorization", b""), ( - "content-security-policy", - "script-src 'none'; object-src 'none'; base-uri 'none'", + b"content-security-policy", + b"script-src 'none'; object-src 'none'; base-uri 'none'", ), - ("early-data", "1"), - ("expect-ct", ""), - ("forwarded", ""), - ("if-range", ""), - ("origin", ""), - ("purpose", "prefetch"), - ("server", ""), - ("timing-allow-origin", "*"), - ("upgrade-insecure-requests", "1"), - ("user-agent", ""), - ("x-forwarded-for", ""), - ("x-frame-options", "deny"), - ("x-frame-options", "sameorigin"), + (b"early-data", b"1"), + (b"expect-ct", b""), + (b"forwarded", b""), + (b"if-range", b""), + (b"origin", b""), + (b"purpose", b"prefetch"), + (b"server", b""), + (b"timing-allow-origin", b"*"), + (b"upgrade-insecure-requests", b"1"), + (b"user-agent", b""), + (b"x-forwarded-for", b""), + (b"x-frame-options", b"deny"), + (b"x-frame-options", b"sameorigin"), ]; diff --git a/src/h3/stream.rs b/src/h3/stream.rs index f2f8f0c..58e2873 100644 --- a/src/h3/stream.rs +++ b/src/h3/stream.rs @@ -73,6 +73,9 @@ pub enum State { /// Reading and discarding data. Drain, + + /// All data has been read. + Finished, } impl Type { @@ -135,6 +138,9 @@ pub struct Stream { /// Whether the stream has been locally initialized. local_initialized: bool, + + /// Whether a `Data` event has been triggered for this stream. + data_event_triggered: bool, } impl Stream { @@ -171,9 +177,15 @@ impl Stream { is_local, remote_initialized: false, local_initialized: false, + + data_event_triggered: false, } } + pub fn ty(&self) -> Option<Type> { + self.ty + } + pub fn state(&self) -> State { self.state } @@ -261,6 +273,9 @@ impl Stream { (frame::HEADERS_FRAME_TYPE_ID, false) => self.remote_initialized = true, + (frame::DATA_FRAME_TYPE_ID, false) => + return Err(Error::FrameUnexpected), + (frame::CANCEL_PUSH_FRAME_TYPE_ID, _) => return Err(Error::FrameUnexpected), @@ -275,9 +290,7 @@ impl Stream { // All other frames can be ignored regardless of stream // state. - (_, false) => (), - - (_, true) => (), + _ => (), } } }, @@ -347,7 +360,18 @@ impl Stream { ) -> Result<()> { let buf = &mut self.state_buf[self.state_off..self.state_len]; - let (read, _) = conn.stream_recv(self.id, buf)?; + let read = match conn.stream_recv(self.id, buf) { + Ok((len, _)) => len, + + Err(e) => { + // The stream is not readable anymore, so re-arm the Data event. + if e == crate::Error::Done { + self.reset_data_event(); + } + + return Err(e.into()); + }, + }; trace!( "{} read {} bytes on stream {}", @@ -359,6 +383,8 @@ impl Stream { self.state_off += read; if !self.state_buffer_complete() { + self.reset_data_event(); + return Err(Error::Done); } @@ -416,6 +442,9 @@ impl Stream { /// Tries to parse a frame from the state buffer. pub fn try_consume_frame(&mut self) -> Result<frame::Frame> { + // Processing a frame other than DATA, so re-arm the Data event. + self.reset_data_event(); + // TODO: properly propagate frame parsing errors. let frame = frame::Frame::from_bytes( self.frame_type.unwrap(), @@ -431,18 +460,39 @@ impl Stream { /// Tries to read DATA payload from the transport stream. pub fn try_consume_data( &mut self, conn: &mut crate::Connection, out: &mut [u8], - ) -> Result<usize> { + ) -> Result<(usize, bool)> { let left = std::cmp::min(out.len(), self.state_len - self.state_off); - let (len, _) = conn.stream_recv(self.id, &mut out[..left])?; + let (len, fin) = match conn.stream_recv(self.id, &mut out[..left]) { + Ok(v) => v, + + Err(e) => { + // The stream is not readable anymore, so re-arm the Data event. + if e == crate::Error::Done { + self.reset_data_event(); + } + + return Err(e.into()); + }, + }; self.state_off += len; + // The stream is not readable anymore, so re-arm the Data event. + if !conn.stream_readable(self.id) { + self.reset_data_event(); + } + if self.state_buffer_complete() { self.state_transition(State::FrameType, 1, true)?; } - Ok(len) + Ok((len, fin)) + } + + /// Marks the stream as finished. + pub fn finished(&mut self) { + let _ = self.state_transition(State::Finished, 0, false); } /// Tries to read DATA payload from the given cursor. @@ -466,6 +516,25 @@ impl Stream { Ok(len) } + /// Tries to update the data triggered state for the stream. + /// + /// This returns `true` if a Data event was not already triggered before + /// the last reset, and updates the state. Returns `false` otherwise. + pub fn try_trigger_data_event(&mut self) -> bool { + if self.data_event_triggered { + return false; + } + + self.data_event_triggered = true; + + true + } + + /// Resets the data triggered state. + fn reset_data_event(&mut self) { + self.data_event_triggered = false; + } + /// Returns true if the state buffer has enough data to complete the state. fn state_buffer_complete(&self) -> bool { self.state_off == self.state_len @@ -514,6 +583,7 @@ mod tests { max_header_list_size: Some(0), qpack_max_table_capacity: Some(0), qpack_blocked_streams: Some(0), + h3_datagram: None, grease: None, }; @@ -569,6 +639,7 @@ mod tests { max_header_list_size: Some(0), qpack_max_table_capacity: Some(0), qpack_blocked_streams: Some(0), + h3_datagram: None, grease: None, }; @@ -633,6 +704,7 @@ mod tests { max_header_list_size: Some(0), qpack_max_table_capacity: Some(0), qpack_blocked_streams: Some(0), + h3_datagram: None, grease: None, }; @@ -675,6 +747,7 @@ mod tests { max_header_list_size: Some(0), qpack_max_table_capacity: Some(0), qpack_blocked_streams: Some(0), + h3_datagram: None, grease: None, }; @@ -910,4 +983,28 @@ mod tests { .unwrap(); assert_eq!(stream.state, State::Drain); } + + #[test] + fn data_before_headers() { + let mut stream = Stream::new(0, false); + + let mut d = vec![42; 128]; + let mut b = octets::OctetsMut::with_slice(&mut d); + + let data = frame::Frame::Data { + payload: vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12], + }; + + data.to_bytes(&mut b).unwrap(); + + let mut cursor = std::io::Cursor::new(d); + + // Parse the DATA frame type. + stream.try_fill_buffer_for_tests(&mut cursor).unwrap(); + + let frame_ty = stream.try_consume_varint().unwrap(); + assert_eq!(frame_ty, frame::DATA_FRAME_TYPE_ID); + + assert_eq!(stream.set_frame_type(frame_ty), Err(Error::FrameUnexpected)); + } } @@ -54,15 +54,25 @@ //! ``` //! # let mut config = quiche::Config::new(quiche::PROTOCOL_VERSION)?; //! # let server_name = "quic.tech"; -//! # let scid = [0xba; 16]; +//! # let scid = quiche::ConnectionId::from_ref(&[0xba; 16]); +//! # let to = "127.0.0.1:1234".parse().unwrap(); //! // Client connection. -//! let conn = quiche::connect(Some(&server_name), &scid, &mut config)?; +//! let conn = quiche::connect(Some(&server_name), &scid, to, &mut config)?; //! //! // Server connection. -//! let conn = quiche::accept(&scid, None, &mut config)?; +//! # let from = "127.0.0.1:1234".parse().unwrap(); +//! let conn = quiche::accept(&scid, None, from, &mut config)?; //! # Ok::<(), quiche::Error>(()) //! ``` //! +//! In both cases, the application is responsible for generating a new source +//! connection ID that will be used to identify the new connection. +//! +//! The application also need to pass the address of the remote peer of the +//! connection: in the case of a client that would be the address of the server +//! it is trying to connect to, and for a server that is the address of the +//! client that initiated the connection. +//! //! ## Handling incoming packets //! //! Using the connection's [`recv()`] method the application can process @@ -72,12 +82,15 @@ //! # let mut buf = [0; 512]; //! # let socket = std::net::UdpSocket::bind("127.0.0.1:0").unwrap(); //! # let mut config = quiche::Config::new(quiche::PROTOCOL_VERSION)?; -//! # let scid = [0xba; 16]; -//! # let mut conn = quiche::accept(&scid, None, &mut config)?; +//! # let scid = quiche::ConnectionId::from_ref(&[0xba; 16]); +//! # let from = "127.0.0.1:1234".parse().unwrap(); +//! # let mut conn = quiche::accept(&scid, None, from, &mut config)?; //! loop { -//! let read = socket.recv(&mut buf).unwrap(); +//! let (read, from) = socket.recv_from(&mut buf).unwrap(); //! -//! let read = match conn.recv(&mut buf[..read]) { +//! let recv_info = quiche::RecvInfo { from }; +//! +//! let read = match conn.recv(&mut buf[..read], recv_info) { //! Ok(v) => v, //! //! Err(quiche::Error::Done) => { @@ -94,6 +107,10 @@ //! # Ok::<(), quiche::Error>(()) //! ``` //! +//! The application has to pass a [`RecvInfo`] structure in order to provide +//! additional information about the received packet (such as the address it +//! was received from). +//! //! ## Generating outgoing packets //! //! Outgoing packet are generated using the connection's [`send()`] method @@ -103,10 +120,11 @@ //! # let mut out = [0; 512]; //! # let socket = std::net::UdpSocket::bind("127.0.0.1:0").unwrap(); //! # let mut config = quiche::Config::new(quiche::PROTOCOL_VERSION)?; -//! # let scid = [0xba; 16]; -//! # let mut conn = quiche::accept(&scid, None, &mut config)?; +//! # let scid = quiche::ConnectionId::from_ref(&[0xba; 16]); +//! # let from = "127.0.0.1:1234".parse().unwrap(); +//! # let mut conn = quiche::accept(&scid, None, from, &mut config)?; //! loop { -//! let write = match conn.send(&mut out) { +//! let (write, send_info) = match conn.send(&mut out) { //! Ok(v) => v, //! //! Err(quiche::Error::Done) => { @@ -120,19 +138,24 @@ //! }, //! }; //! -//! socket.send(&out[..write]).unwrap(); +//! socket.send_to(&out[..write], &send_info.to).unwrap(); //! } //! # Ok::<(), quiche::Error>(()) //! ``` //! +//! The application will be provided with a [`SendInfo`] structure providing +//! additional information about the newly created packet (such as the address +//! the packet should be sent to). +//! //! When packets are sent, the application is responsible for maintaining a //! timer to react to time-based connection events. The timer expiration can be //! obtained using the connection's [`timeout()`] method. //! //! ``` //! # let mut config = quiche::Config::new(quiche::PROTOCOL_VERSION)?; -//! # let scid = [0xba; 16]; -//! # let mut conn = quiche::accept(&scid, None, &mut config)?; +//! # let scid = quiche::ConnectionId::from_ref(&[0xba; 16]); +//! # let from = "127.0.0.1:1234".parse().unwrap(); +//! # let mut conn = quiche::accept(&scid, None, from, &mut config)?; //! let timeout = conn.timeout(); //! # Ok::<(), quiche::Error>(()) //! ``` @@ -146,14 +169,15 @@ //! # let mut out = [0; 512]; //! # let socket = std::net::UdpSocket::bind("127.0.0.1:0").unwrap(); //! # let mut config = quiche::Config::new(quiche::PROTOCOL_VERSION)?; -//! # let scid = [0xba; 16]; -//! # let mut conn = quiche::accept(&scid, None, &mut config)?; +//! # let scid = quiche::ConnectionId::from_ref(&[0xba; 16]); +//! # let from = "127.0.0.1:1234".parse().unwrap(); +//! # let mut conn = quiche::accept(&scid, None, from, &mut config)?; //! // Timeout expired, handle it. //! conn.on_timeout(); //! //! // Send more packets as needed after timeout. //! loop { -//! let write = match conn.send(&mut out) { +//! let (write, send_info) = match conn.send(&mut out) { //! Ok(v) => v, //! //! Err(quiche::Error::Done) => { @@ -167,7 +191,7 @@ //! }, //! }; //! -//! socket.send(&out[..write]).unwrap(); +//! socket.send_to(&out[..write], &send_info.to).unwrap(); //! } //! # Ok::<(), quiche::Error>(()) //! ``` @@ -181,8 +205,9 @@ //! //! ```no_run //! # let mut config = quiche::Config::new(quiche::PROTOCOL_VERSION)?; -//! # let scid = [0xba; 16]; -//! # let mut conn = quiche::accept(&scid, None, &mut config)?; +//! # let scid = quiche::ConnectionId::from_ref(&[0xba; 16]); +//! # let from = "127.0.0.1:1234".parse().unwrap(); +//! # let mut conn = quiche::accept(&scid, None, from, &mut config)?; //! if conn.is_established() { //! // Handshake completed, send some data on stream 0. //! conn.stream_send(0, b"hello", true)?; @@ -200,8 +225,9 @@ //! ```no_run //! # let mut buf = [0; 512]; //! # let mut config = quiche::Config::new(quiche::PROTOCOL_VERSION)?; -//! # let scid = [0xba; 16]; -//! # let mut conn = quiche::accept(&scid, None, &mut config)?; +//! # let scid = quiche::ConnectionId::from_ref(&[0xba; 16]); +//! # let from = "127.0.0.1:1234".parse().unwrap(); +//! # let mut conn = quiche::accept(&scid, None, from, &mut config)?; //! if conn.is_established() { //! // Iterate over readable streams. //! for stream_id in conn.readable() { @@ -222,7 +248,9 @@ //! [`connect()`]: fn.connect.html //! [`accept()`]: fn.accept.html //! [`recv()`]: struct.Connection.html#method.recv +//! [`RecvInfo`]: struct.RecvInfo.html //! [`send()`]: struct.Connection.html#method.send +//! [`SendInfo`]: struct.SendInfo.html //! [`timeout()`]: struct.Connection.html#method.timeout //! [`on_timeout()`]: struct.Connection.html#method.on_timeout //! [`stream_send()`]: struct.Connection.html#method.stream_send @@ -260,6 +288,8 @@ //! [`CongestionControlAlgorithm`]: enum.CongestionControlAlgorithm.html #![allow(improper_ctypes)] +#![allow(clippy::suspicious_operation_groupings)] +#![allow(clippy::upper_case_acronyms)] #![warn(missing_docs)] #[macro_use] @@ -268,15 +298,22 @@ extern crate log; use std::cmp; use std::time; +use std::net::SocketAddr; + use std::pin::Pin; use std::str::FromStr; +use std::sync::Mutex; + +use std::collections::VecDeque; + /// The current QUIC wire version. -pub const PROTOCOL_VERSION: u32 = PROTOCOL_VERSION_DRAFT29; +pub const PROTOCOL_VERSION: u32 = PROTOCOL_VERSION_V1; /// Supported QUIC versions. /// /// Note that the older ones might not be fully supported. +const PROTOCOL_VERSION_V1: u32 = 0x0000_0001; const PROTOCOL_VERSION_DRAFT27: u32 = 0xff00_001b; const PROTOCOL_VERSION_DRAFT28: u32 = 0xff00_001c; const PROTOCOL_VERSION_DRAFT29: u32 = 0xff00_001d; @@ -306,6 +343,9 @@ const MAX_ACK_RANGES: usize = 68; // The highest possible stream ID allowed. const MAX_STREAM_ID: u64 = 1 << 60; +// The default max_datagram_size used in congestion control. +const MAX_SEND_UDP_PAYLOAD_SIZE: usize = 1200; + // The default length of DATAGRAM queues. const DEFAULT_MAX_DGRAM_QUEUE_LEN: usize = 0; @@ -313,6 +353,14 @@ const DEFAULT_MAX_DGRAM_QUEUE_LEN: usize = 0; // frames size. We enforce the recommendation for forward compatibility. const MAX_DGRAM_FRAME_SIZE: u64 = 65536; +// The length of the payload length field. +const PAYLOAD_LENGTH_LEN: usize = 2; + +// The number of undecryptable that can be buffered. +const MAX_UNDECRYPTABLE_PACKETS: usize = 10; + +const RESERVED_VERSION_MASK: u32 = 0xfafafafa; + /// A specialized [`Result`] type for quiche operations. /// /// This type is used throughout quiche's public API for any operation that @@ -323,52 +371,59 @@ pub type Result<T> = std::result::Result<T, Error>; /// A QUIC error. #[derive(Clone, Copy, Debug, PartialEq)] -#[repr(C)] pub enum Error { /// There is no more work to do. - Done = -1, + Done, /// The provided buffer is too short. - BufferTooShort = -2, + BufferTooShort, /// The provided packet cannot be parsed because its version is unknown. - UnknownVersion = -3, + UnknownVersion, /// The provided packet cannot be parsed because it contains an invalid /// frame. - InvalidFrame = -4, + InvalidFrame, /// The provided packet cannot be parsed. - InvalidPacket = -5, + InvalidPacket, /// The operation cannot be completed because the connection is in an /// invalid state. - InvalidState = -6, + InvalidState, /// The operation cannot be completed because the stream is in an /// invalid state. - InvalidStreamState = -7, + /// + /// The stream ID is provided as associated data. + InvalidStreamState(u64), /// The peer's transport params cannot be parsed. - InvalidTransportParam = -8, + InvalidTransportParam, /// A cryptographic operation failed. - CryptoFail = -9, + CryptoFail, /// The TLS handshake failed. - TlsFail = -10, + TlsFail, /// The peer violated the local flow control limits. - FlowControl = -11, + FlowControl, /// The peer violated the local stream limits. - StreamLimit = -12, + StreamLimit, + + /// The specified stream was stopped by the peer. + /// + /// The error code sent as part of the `STOP_SENDING` frame is provided as + /// associated data. + StreamStopped(u64), /// The received data exceeds the stream's final size. - FinalSize = -13, + FinalSize, /// Error in congestion control. - CongestionControl = -14, + CongestionControl, } impl Error { @@ -376,7 +431,7 @@ impl Error { match self { Error::Done => 0x0, Error::InvalidFrame => 0x7, - Error::InvalidStreamState => 0x5, + Error::InvalidStreamState(..) => 0x5, Error::InvalidTransportParam => 0x8, Error::FlowControl => 0x3, Error::StreamLimit => 0x4, @@ -385,8 +440,25 @@ impl Error { } } + #[cfg(feature = "ffi")] fn to_c(self) -> libc::ssize_t { - self as _ + match self { + Error::Done => -1, + Error::BufferTooShort => -2, + Error::UnknownVersion => -3, + Error::InvalidFrame => -4, + Error::InvalidPacket => -5, + Error::InvalidState => -6, + Error::InvalidStreamState(_) => -7, + Error::InvalidTransportParam => -8, + Error::CryptoFail => -9, + Error::TlsFail => -10, + Error::FlowControl => -11, + Error::StreamLimit => -12, + Error::FinalSize => -13, + Error::CongestionControl => -14, + Error::StreamStopped { .. } => -15, + } } } @@ -408,6 +480,36 @@ impl std::convert::From<octets::BufferTooShortError> for Error { } } +/// Ancillary information about incoming packets. +#[derive(Clone, Copy, Debug, PartialEq)] +pub struct RecvInfo { + /// The address the packet was received from. + pub from: SocketAddr, +} + +/// Ancillary information about outgoing packets. +#[derive(Clone, Copy, Debug, PartialEq)] +pub struct SendInfo { + /// The address the packet should be sent to. + pub to: SocketAddr, + + /// The time to send the packet out. + pub at: time::Instant, +} + +/// Represents information carried by `CONNECTION_CLOSE` frames. +#[derive(Clone, Debug, PartialEq)] +pub struct ConnectionError { + /// Whether the error came from the application or the transport layer. + pub is_app: bool, + + /// The error code carried by the `CONNECTION_CLOSE` frame. + pub error_code: u64, + + /// The reason carried by the `CONNECTION_CLOSE` frame. + pub reason: Vec<u8>, +} + /// The stream's side to shutdown. /// /// This should be used when calling [`stream_shutdown()`]. @@ -428,7 +530,11 @@ pub struct Config { version: u32, - tls_ctx: tls::Context, + // BoringSSL's SSL_CTX structure is technically safe to share across threads + // but once shared, functions that modify it can't be used any more. We can't + // encode that in Rust, so just make it Send+Sync with a mutex to fulfill + // the Sync constraint. + tls_ctx: Mutex<tls::Context>, application_protos: Vec<Vec<u8>>, @@ -440,6 +546,13 @@ pub struct Config { dgram_recv_max_queue_len: usize, dgram_send_max_queue_len: usize, + + max_send_udp_payload_size: usize, +} + +// See https://quicwg.org/base-drafts/rfc9000.html#section-15 +fn is_reserved_version(version: u32) -> bool { + version & RESERVED_VERSION_MASK == version } impl Config { @@ -452,7 +565,11 @@ impl Config { /// # Ok::<(), quiche::Error>(()) /// ``` pub fn new(version: u32) -> Result<Config> { - let tls_ctx = tls::Context::new()?; + if !is_reserved_version(version) && !version_is_supported(version) { + return Err(Error::UnknownVersion); + } + + let tls_ctx = Mutex::new(tls::Context::new()?); Ok(Config { local_transport_params: TransportParams::default(), @@ -465,6 +582,8 @@ impl Config { dgram_recv_max_queue_len: DEFAULT_MAX_DGRAM_QUEUE_LEN, dgram_send_max_queue_len: DEFAULT_MAX_DGRAM_QUEUE_LEN, + + max_send_udp_payload_size: MAX_SEND_UDP_PAYLOAD_SIZE, }) } @@ -481,7 +600,10 @@ impl Config { /// # Ok::<(), quiche::Error>(()) /// ``` pub fn load_cert_chain_from_pem_file(&mut self, file: &str) -> Result<()> { - self.tls_ctx.use_certificate_chain_file(file) + self.tls_ctx + .lock() + .unwrap() + .use_certificate_chain_file(file) } /// Configures the given private key. @@ -496,7 +618,7 @@ impl Config { /// # Ok::<(), quiche::Error>(()) /// ``` pub fn load_priv_key_from_pem_file(&mut self, file: &str) -> Result<()> { - self.tls_ctx.use_privkey_file(file) + self.tls_ctx.lock().unwrap().use_privkey_file(file) } /// Specifies a file where trusted CA certificates are stored for the @@ -512,7 +634,10 @@ impl Config { /// # Ok::<(), quiche::Error>(()) /// ``` pub fn load_verify_locations_from_file(&mut self, file: &str) -> Result<()> { - self.tls_ctx.load_verify_locations_from_file(file) + self.tls_ctx + .lock() + .unwrap() + .load_verify_locations_from_file(file) } /// Specifies a directory where trusted CA certificates are stored for the @@ -530,7 +655,10 @@ impl Config { pub fn load_verify_locations_from_directory( &mut self, dir: &str, ) -> Result<()> { - self.tls_ctx.load_verify_locations_from_directory(dir) + self.tls_ctx + .lock() + .unwrap() + .load_verify_locations_from_directory(dir) } /// Configures whether to verify the peer's certificate. @@ -538,7 +666,7 @@ impl Config { /// The default value is `true` for client connections, and `false` for /// server ones. pub fn verify_peer(&mut self, verify: bool) { - self.tls_ctx.set_verify(verify); + self.tls_ctx.lock().unwrap().set_verify(verify); } /// Configures whether to send GREASE values. @@ -557,12 +685,26 @@ impl Config { /// [`set_keylog()`]: struct.Connection.html#method.set_keylog /// [keylog]: https://developer.mozilla.org/en-US/docs/Mozilla/Projects/NSS/Key_Log_Format pub fn log_keys(&mut self) { - self.tls_ctx.enable_keylog(); + self.tls_ctx.lock().unwrap().enable_keylog(); + } + + /// Configures the session ticket key material. + /// + /// On the server this key will be used to encrypt and decrypt session + /// tickets, used to perform session resumption without server-side state. + /// + /// By default a key is generated internally, and rotated regularly, so + /// applications don't need to call this unless they need to use a + /// specific key (e.g. in order to support resumption across multiple + /// servers), in which case the application is also responsible for + /// rotating the key to provide forward secrecy. + pub fn set_ticket_key(&mut self, key: &[u8]) -> Result<()> { + self.tls_ctx.lock().unwrap().set_ticket_key(key) } /// Enables sending or receiving early data. pub fn enable_early_data(&mut self) { - self.tls_ctx.set_early_data_enabled(true); + self.tls_ctx.lock().unwrap().set_early_data_enabled(true); } /// Configures the list of supported application protocols. @@ -596,10 +738,13 @@ impl Config { self.application_protos = protos_list; - self.tls_ctx.set_alpn(&self.application_protos) + self.tls_ctx + .lock() + .unwrap() + .set_alpn(&self.application_protos) } - /// Sets the `max_idle_timeout` transport parameter. + /// Sets the `max_idle_timeout` transport parameter, in milliseconds. /// /// The default value is infinite, that is, no timeout is used. pub fn set_max_idle_timeout(&mut self, v: u64) { @@ -609,8 +754,15 @@ impl Config { /// Sets the `max_udp_payload_size transport` parameter. /// /// The default value is `65527`. - pub fn set_max_udp_payload_size(&mut self, v: u64) { - self.local_transport_params.max_udp_payload_size = v; + pub fn set_max_recv_udp_payload_size(&mut self, v: usize) { + self.local_transport_params.max_udp_payload_size = v as u64; + } + + /// Sets the maximum outgoing UDP payload size. + /// + /// The default and minimum value is `1200`. + pub fn set_max_send_udp_payload_size(&mut self, v: usize) { + self.max_send_udp_payload_size = cmp::max(v, MAX_SEND_UDP_PAYLOAD_SIZE); } /// Sets the `initial_max_data` transport parameter. @@ -722,7 +874,7 @@ impl Config { /// Sets the congestion control algorithm used by string. /// - /// The default value is `reno`. On error `Error::CongestionControl` + /// The default value is `cubic`. On error `Error::CongestionControl` /// will be returned. /// /// ## Examples: @@ -777,10 +929,10 @@ pub struct Connection { version: u32, /// Peer's connection ID. - dcid: Vec<u8>, + dcid: ConnectionId<'static>, /// Local connection ID. - scid: Vec<u8>, + scid: ConnectionId<'static>, /// Unique opaque ID for the connection that can be used for logging. trace_id: String, @@ -795,11 +947,23 @@ pub struct Connection { local_transport_params: TransportParams, /// TLS handshake state. - handshake: tls::Handshake, + /// + /// Due to the requirement for `Connection` to be Send+Sync, and the fact + /// that BoringSSL's SSL structure is not thread safe, we need to wrap the + /// handshake object in a mutex. + handshake: Mutex<tls::Handshake>, + + /// Serialized TLS session buffer. + /// + /// This field is populated when a new session ticket is processed on the + /// client. On the server this is empty. + session: Option<Vec<u8>>, /// Loss recovery and congestion control state. recovery: recovery::Recovery, + peer_addr: SocketAddr, + /// List of supported application protocols. application_protos: Vec<Vec<u8>>, @@ -822,6 +986,9 @@ pub struct Connection { /// Whether we send MAX_DATA frame. almost_full: bool, + /// Number of stream data bytes that can be buffered. + tx_cap: usize, + /// Total number of bytes sent to the peer. tx_data: u64, @@ -837,23 +1004,22 @@ pub struct Connection { /// Peer's original destination connection ID. Used by the client to /// validate the server's transport parameter. - odcid: Option<Vec<u8>>, + odcid: Option<ConnectionId<'static>>, /// Peer's retry source connection ID. Used by the client during stateless /// retry to validate the server's transport parameter. - rscid: Option<Vec<u8>>, + rscid: Option<ConnectionId<'static>>, /// Received address verification token. token: Option<Vec<u8>>, - /// Error code to be sent to the peer in CONNECTION_CLOSE. - error: Option<u64>, - - /// Error code to be sent to the peer in APPLICATION_CLOSE. - app_error: Option<u64>, + /// Error code and reason to be sent to the peer in a CONNECTION_CLOSE + /// frame. + local_error: Option<ConnectionError>, - /// Error reason to be sent to the peer in APPLICATION_CLOSE. - app_reason: Vec<u8>, + /// Error code and reason received from the peer in a CONNECTION_CLOSE + /// frame. + peer_error: Option<ConnectionError>, /// Received path challenge. challenge: Option<Vec<u8>>, @@ -867,6 +1033,12 @@ pub struct Connection { /// Draining timeout expiration time. draining_timer: Option<time::Instant>, + /// List of raw packets that were received before they could be decrypted. + undecryptable_pkts: VecDeque<(Vec<u8>, RecvInfo)>, + + /// The negotiated ALPN protocol. + alpn: Vec<u8>, + /// Whether this is a server-side connection. is_server: bool, @@ -892,6 +1064,9 @@ pub struct Connection { /// Whether the peer's transport parameters were parsed. parsed_peer_transport_params: bool, + /// Whether the connection handshake has been completed. + handshake_completed: bool, + /// Whether the HANDSHAKE_DONE has been sent. handshake_done_sent: bool, @@ -909,7 +1084,7 @@ pub struct Connection { grease: bool, /// TLS keylog writer. - keylog: Option<Box<dyn std::io::Write + Send>>, + keylog: Option<Box<dyn std::io::Write + Send + Sync>>, /// Qlog streaming output. #[cfg(feature = "qlog")] @@ -922,6 +1097,9 @@ pub struct Connection { /// DATAGRAM queues. dgram_recv_queue: dgram::DatagramQueue, dgram_send_queue: dgram::DatagramQueue, + + /// Whether to emit DATAGRAM frames in the next packet. + emit_dgram: bool, } /// Creates a new server-side connection. @@ -937,14 +1115,17 @@ pub struct Connection { /// /// ```no_run /// # let mut config = quiche::Config::new(0xbabababa)?; -/// # let scid = [0xba; 16]; -/// let conn = quiche::accept(&scid, None, &mut config)?; +/// # let scid = quiche::ConnectionId::from_ref(&[0xba; 16]); +/// # let from = "127.0.0.1:1234".parse().unwrap(); +/// let conn = quiche::accept(&scid, None, from, &mut config)?; /// # Ok::<(), quiche::Error>(()) /// ``` +#[inline] pub fn accept( - scid: &[u8], odcid: Option<&[u8]>, config: &mut Config, + scid: &ConnectionId, odcid: Option<&ConnectionId>, from: SocketAddr, + config: &mut Config, ) -> Result<Pin<Box<Connection>>> { - let conn = Connection::new(scid, odcid, config, true)?; + let conn = Connection::new(scid, odcid, from, config, true)?; Ok(conn) } @@ -960,17 +1141,20 @@ pub fn accept( /// ```no_run /// # let mut config = quiche::Config::new(0xbabababa)?; /// # let server_name = "quic.tech"; -/// # let scid = [0xba; 16]; -/// let conn = quiche::connect(Some(&server_name), &scid, &mut config)?; +/// # let scid = quiche::ConnectionId::from_ref(&[0xba; 16]); +/// # let to = "127.0.0.1:1234".parse().unwrap(); +/// let conn = quiche::connect(Some(&server_name), &scid, to, &mut config)?; /// # Ok::<(), quiche::Error>(()) /// ``` +#[inline] pub fn connect( - server_name: Option<&str>, scid: &[u8], config: &mut Config, + server_name: Option<&str>, scid: &ConnectionId, to: SocketAddr, + config: &mut Config, ) -> Result<Pin<Box<Connection>>> { - let conn = Connection::new(scid, None, config, false)?; + let conn = Connection::new(scid, None, to, config, false)?; if let Some(server_name) = server_name { - conn.handshake.set_host_name(server_name)?; + conn.handshake.lock().unwrap().set_host_name(server_name)?; } Ok(conn) @@ -999,8 +1183,9 @@ pub fn connect( /// } /// # Ok::<(), quiche::Error>(()) /// ``` +#[inline] pub fn negotiate_version( - scid: &[u8], dcid: &[u8], out: &mut [u8], + scid: &ConnectionId, dcid: &ConnectionId, out: &mut [u8], ) -> Result<usize> { packet::negotiate_version(scid, dcid, out) } @@ -1026,12 +1211,12 @@ pub fn negotiate_version( /// # let mut config = quiche::Config::new(0xbabababa)?; /// # let mut buf = [0; 512]; /// # let mut out = [0; 512]; -/// # let scid = [0xba; 16]; +/// # let scid = quiche::ConnectionId::from_ref(&[0xba; 16]); /// # let socket = std::net::UdpSocket::bind("127.0.0.1:0").unwrap(); /// # fn mint_token(hdr: &quiche::Header, src: &std::net::SocketAddr) -> Vec<u8> { /// # vec![] /// # } -/// # fn validate_token<'a>(src: &std::net::SocketAddr, token: &'a [u8]) -> Option<&'a [u8]> { +/// # fn validate_token<'a>(src: &std::net::SocketAddr, token: &'a [u8]) -> Option<quiche::ConnectionId<'a>> { /// # None /// # } /// let (len, src) = socket.recv_from(&mut buf).unwrap(); @@ -1055,26 +1240,29 @@ pub fn negotiate_version( /// // Client sent token, validate it. /// let odcid = validate_token(&src, token); /// -/// if odcid == None { +/// if odcid.is_none() { /// // Invalid address validation token. /// return Ok(()); /// } /// -/// let conn = quiche::accept(&scid, odcid, &mut config)?; +/// let conn = quiche::accept(&scid, odcid.as_ref(), src, &mut config)?; /// # Ok::<(), quiche::Error>(()) /// ``` +#[inline] pub fn retry( - scid: &[u8], dcid: &[u8], new_scid: &[u8], token: &[u8], version: u32, - out: &mut [u8], + scid: &ConnectionId, dcid: &ConnectionId, new_scid: &ConnectionId, + token: &[u8], version: u32, out: &mut [u8], ) -> Result<usize> { packet::retry(scid, dcid, new_scid, token, version, out) } /// Returns true if the given protocol version is supported. +#[inline] pub fn version_is_supported(version: u32) -> bool { matches!( version, - PROTOCOL_VERSION_DRAFT27 | + PROTOCOL_VERSION_V1 | + PROTOCOL_VERSION_DRAFT27 | PROTOCOL_VERSION_DRAFT28 | PROTOCOL_VERSION_DRAFT29 ) @@ -1086,11 +1274,12 @@ pub fn version_is_supported(version: u32) -> bool { /// there is no room to add the frame in the packet. You may retry to add the /// frame later. macro_rules! push_frame_to_pkt { - ($frames:expr, $frame:expr, $payload_len: expr, $left:expr) => {{ + ($out:expr, $frames:expr, $frame:expr, $left:expr) => {{ if $frame.wire_len() <= $left { - $payload_len += $frame.wire_len(); $left -= $frame.wire_len(); + $frame.to_bytes(&mut $out)?; + $frames.push($frame); true @@ -1117,15 +1306,16 @@ macro_rules! qlog_with { impl Connection { fn new( - scid: &[u8], odcid: Option<&[u8]>, config: &mut Config, is_server: bool, + scid: &ConnectionId, odcid: Option<&ConnectionId>, peer: SocketAddr, + config: &mut Config, is_server: bool, ) -> Result<Pin<Box<Connection>>> { - let tls = config.tls_ctx.new_handshake()?; - Connection::with_tls(scid, odcid, config, tls, is_server) + let tls = config.tls_ctx.lock().unwrap().new_handshake()?; + Connection::with_tls(scid, odcid, peer, config, tls, is_server) } fn with_tls( - scid: &[u8], odcid: Option<&[u8]>, config: &mut Config, - tls: tls::Handshake, is_server: bool, + scid: &ConnectionId, odcid: Option<&ConnectionId>, peer: SocketAddr, + config: &mut Config, tls: tls::Handshake, is_server: bool, ) -> Result<Pin<Box<Connection>>> { let max_rx_data = config.local_transport_params.initial_max_data; @@ -1135,8 +1325,8 @@ impl Connection { let mut conn = Box::pin(Connection { version: config.version, - dcid: Vec::new(), - scid: scid.to_vec(), + dcid: ConnectionId::default(), + scid: scid.to_vec().into(), trace_id: scid_as_hex.join(""), @@ -1150,10 +1340,14 @@ impl Connection { local_transport_params: config.local_transport_params.clone(), - handshake: tls, + handshake: Mutex::new(tls), + + session: None, recovery: recovery::Recovery::new(&config), + peer_addr: peer, + application_protos: config.application_protos.clone(), recv_count: 0, @@ -1164,6 +1358,8 @@ impl Connection { max_rx_data_next: max_rx_data, almost_full: false, + tx_cap: 0, + tx_data: 0, max_tx_data: 0, @@ -1180,10 +1376,9 @@ impl Connection { token: None, - error: None, + local_error: None, - app_error: None, - app_reason: Vec::new(), + peer_error: None, challenge: None, @@ -1193,6 +1388,10 @@ impl Connection { draining_timer: None, + undecryptable_pkts: VecDeque::new(), + + alpn: Vec::new(), + is_server, derived_initial_secrets: false, @@ -1211,6 +1410,8 @@ impl Connection { parsed_peer_transport_params: false, + handshake_completed: false, + handshake_done_sent: false, handshake_confirmed: false, @@ -1236,22 +1437,29 @@ impl Connection { dgram_send_queue: dgram::DatagramQueue::new( config.dgram_send_max_queue_len, ), + + emit_dgram: true, }); if let Some(odcid) = odcid { conn.local_transport_params - .original_destination_connection_id = Some(odcid.to_vec()); + .original_destination_connection_id = Some(odcid.to_vec().into()); conn.local_transport_params.retry_source_connection_id = - Some(scid.to_vec()); + Some(scid.to_vec().into()); conn.did_retry = true; } conn.local_transport_params.initial_source_connection_id = - Some(scid.to_vec()); + Some(scid.to_vec().into()); - conn.handshake.init(&conn)?; + conn.handshake.lock().unwrap().init(&conn)?; + + conn.handshake + .lock() + .unwrap() + .use_legacy_codepoint(config.version != PROTOCOL_VERSION_V1); conn.encode_transport_params()?; @@ -1267,7 +1475,7 @@ impl Connection { conn.is_server, )?; - conn.dcid.extend_from_slice(&dcid); + conn.dcid = dcid.to_vec().into(); conn.pkt_num_spaces[packet::EPOCH_INITIAL].crypto_open = Some(aead_open); @@ -1286,7 +1494,8 @@ impl Connection { /// missing some early logs. /// /// [`Writer`]: https://doc.rust-lang.org/std/io/trait.Write.html - pub fn set_keylog(&mut self, writer: Box<dyn std::io::Write + Send>) { + #[inline] + pub fn set_keylog(&mut self, writer: Box<dyn std::io::Write + Send + Sync>) { self.keylog = Some(writer); } @@ -1298,7 +1507,7 @@ impl Connection { /// [`Writer`]: https://doc.rust-lang.org/std/io/trait.Write.html #[cfg(feature = "qlog")] pub fn set_qlog( - &mut self, writer: Box<dyn std::io::Write + Send>, title: String, + &mut self, writer: Box<dyn std::io::Write + Send + Sync>, title: String, description: String, ) { let vp = if self.is_server { @@ -1335,11 +1544,13 @@ impl Connection { streamer.start_log().ok(); + let handshake = self.handshake.lock().unwrap(); + let ev = self.local_transport_params.to_qlog( qlog::TransportOwner::Local, self.version, - self.handshake.alpn_protocol(), - self.handshake.cipher(), + handshake.alpn_protocol(), + handshake.cipher(), ); streamer.add_event(ev).ok(); @@ -1347,6 +1558,38 @@ impl Connection { self.qlog_streamer = Some(streamer); } + /// Configures the given session for resumption. + /// + /// On the client, this can be used to offer the given serialized session, + /// as returned by [`session()`], for resumption. + /// + /// This must only be called immediately after creating a connection, that + /// is, before any packet is sent or received. + /// + /// [`session()`]: struct.Connection.html#method.session + #[inline] + pub fn set_session(&mut self, session: &[u8]) -> Result<()> { + let mut b = octets::Octets::with_slice(session); + + let session_len = b.get_u64()? as usize; + let session_bytes = b.get_bytes(session_len)?; + + self.handshake + .lock() + .unwrap() + .set_session(session_bytes.as_ref())?; + + let raw_params_len = b.get_u64()? as usize; + let raw_params_bytes = b.get_bytes(raw_params_len)?; + + let peer_params = + TransportParams::decode(raw_params_bytes.as_ref(), self.is_server)?; + + self.process_peer_transport_params(peer_params); + + Ok(()) + } + /// Processes QUIC packets received from the peer. /// /// On success the number of bytes processed from the input buffer is @@ -1366,12 +1609,15 @@ impl Connection { /// # let mut buf = [0; 512]; /// # let socket = std::net::UdpSocket::bind("127.0.0.1:0").unwrap(); /// # let mut config = quiche::Config::new(quiche::PROTOCOL_VERSION)?; - /// # let scid = [0xba; 16]; - /// # let mut conn = quiche::accept(&scid, None, &mut config)?; + /// # let scid = quiche::ConnectionId::from_ref(&[0xba; 16]); + /// # let from = "127.0.0.1:1234".parse().unwrap(); + /// # let mut conn = quiche::accept(&scid, None, from, &mut config)?; /// loop { - /// let read = socket.recv(&mut buf).unwrap(); + /// let (read, from) = socket.recv_from(&mut buf).unwrap(); + /// + /// let recv_info = quiche::RecvInfo { from }; /// - /// let read = match conn.recv(&mut buf[..read]) { + /// let read = match conn.recv(&mut buf[..read], recv_info) { /// Ok(v) => v, /// /// Err(e) => { @@ -1382,9 +1628,13 @@ impl Connection { /// } /// # Ok::<(), quiche::Error>(()) /// ``` - pub fn recv(&mut self, buf: &mut [u8]) -> Result<usize> { + pub fn recv(&mut self, buf: &mut [u8], info: RecvInfo) -> Result<usize> { let len = buf.len(); + if len == 0 { + return Err(Error::BufferTooShort); + } + // Keep track of how many bytes we received from the client, so we // can limit bytes sent back before address validation, to a multiple // of this. The limit needs to be increased early on, so that if there @@ -1401,7 +1651,7 @@ impl Connection { // Process coalesced packets. while left > 0 { - let read = match self.recv_single(&mut buf[len - left..len]) { + let read = match self.recv_single(&mut buf[len - left..len], &info) { Ok(v) => v, Err(Error::Done) => left, @@ -1418,6 +1668,25 @@ impl Connection { left -= read; } + // Process previously undecryptable 0-RTT packets if the decryption key + // is now available. + if self.pkt_num_spaces[packet::EPOCH_APPLICATION] + .crypto_0rtt_open + .is_some() + { + while let Some((mut pkt, info)) = self.undecryptable_pkts.pop_front() + { + if let Err(e) = self.recv(&mut pkt, info) { + self.undecryptable_pkts.clear(); + + // Even though the packet was previously "accepted", it + // should be safe to forward the error, as it also comes + // from the `recv()` method. + return Err(e); + } + } + } + Ok(done) } @@ -1430,18 +1699,18 @@ impl Connection { /// On error, an error other than [`Done`] is returned. /// /// [`Done`]: enum.Error.html#variant.Done - fn recv_single(&mut self, buf: &mut [u8]) -> Result<usize> { + fn recv_single(&mut self, buf: &mut [u8], info: &RecvInfo) -> Result<usize> { let now = time::Instant::now(); if buf.is_empty() { return Err(Error::Done); } - if self.is_closed() || self.draining_timer.is_some() { + if self.is_closed() || self.is_draining() { return Err(Error::Done); } - let is_closing = self.error.is_some() || self.app_error.is_some(); + let is_closing = self.local_error.is_some(); if is_closing { return Err(Error::Done); @@ -1494,20 +1763,33 @@ impl Connection { return Err(Error::Done); } - match versions.iter().filter(|&&v| version_is_supported(v)).max() { - Some(v) => self.version = *v, + let supported_versions = + versions.iter().filter(|&&v| version_is_supported(v)); - None => { - // We don't support any of the versions offered. - // - // While a man-in-the-middle attacker might be able to - // inject a version negotiation packet that triggers this - // failure, the window of opportunity is very small and - // this error is quite useful for debugging, so don't just - // ignore the packet. - return Err(Error::UnknownVersion); - }, - }; + let mut found_version = false; + + for &v in supported_versions { + found_version = true; + + // The final version takes precedence over draft ones. + if v == PROTOCOL_VERSION_V1 { + self.version = v; + break; + } + + self.version = cmp::max(self.version, v); + } + + if !found_version { + // We don't support any of the versions offered. + // + // While a man-in-the-middle attacker might be able to + // inject a version negotiation packet that triggers this + // failure, the window of opportunity is very small and + // this error is quite useful for debugging, so don't just + // ignore the packet. + return Err(Error::UnknownVersion); + } self.did_version_negotiation = true; @@ -1521,13 +1803,18 @@ impl Connection { // Reset connection state to force sending another Initial packet. self.drop_epoch_state(packet::EPOCH_INITIAL, now); self.got_peer_conn_id = false; - self.handshake.clear()?; + self.handshake.lock().unwrap().clear()?; self.pkt_num_spaces[packet::EPOCH_INITIAL].crypto_open = Some(aead_open); self.pkt_num_spaces[packet::EPOCH_INITIAL].crypto_seal = Some(aead_seal); + self.handshake + .lock() + .unwrap() + .use_legacy_codepoint(self.version != PROTOCOL_VERSION_V1); + // Encode transport parameters again, as the new version might be // using a different format. self.encode_transport_params()?; @@ -1561,8 +1848,7 @@ impl Connection { // Remember peer's new connection ID. self.odcid = Some(self.dcid.clone()); - self.dcid.resize(hdr.scid.len(), 0); - self.dcid.copy_from_slice(&hdr.scid); + self.dcid = hdr.scid.clone(); self.rscid = Some(self.dcid.clone()); @@ -1576,7 +1862,7 @@ impl Connection { // Reset connection state to force sending another Initial packet. self.drop_epoch_state(packet::EPOCH_INITIAL, now); self.got_peer_conn_id = false; - self.handshake.clear()?; + self.handshake.lock().unwrap().clear()?; self.pkt_num_spaces[packet::EPOCH_INITIAL].crypto_open = Some(aead_open); @@ -1594,6 +1880,11 @@ impl Connection { self.version = hdr.version; self.did_version_negotiation = true; + self.handshake + .lock() + .unwrap() + .use_legacy_codepoint(self.version != PROTOCOL_VERSION_V1); + // Encode transport parameters again, as the new version might be // using a different format. self.encode_transport_params()?; @@ -1620,6 +1911,17 @@ impl Connection { })? as usize }; + // Make sure the buffer is same or larger than an explicit + // payload length. + if payload_len > b.cap() { + return Err(drop_pkt_on_err( + Error::InvalidPacket, + self.recv_count, + self.is_server, + &self.trace_id, + )); + } + // Derive initial secrets on the server. if !self.derived_initial_secrets { let (aead_open, aead_seal) = crypto::derive_initial_key_material( @@ -1640,24 +1942,45 @@ impl Connection { let epoch = hdr.ty.to_epoch()?; // Select AEAD context used to open incoming packet. - #[allow(clippy::or_fun_call)] - let aead = (self.pkt_num_spaces[epoch].crypto_0rtt_open.as_ref()) + let aead = if hdr.ty == packet::Type::ZeroRTT { // Only use 0-RTT key if incoming packet is 0-RTT. - .filter(|_| hdr.ty == packet::Type::ZeroRTT) + self.pkt_num_spaces[epoch].crypto_0rtt_open.as_ref() + } else { // Otherwise use the packet number space's main key. - .or(self.pkt_num_spaces[epoch].crypto_open.as_ref()) - // Finally, discard packet if no usable key is available. - // - // TODO: buffer 0-RTT/1-RTT packets instead of discarding when the - // required key is not available yet, as an optimization. - .ok_or_else(|| { - drop_pkt_on_err( + self.pkt_num_spaces[epoch].crypto_open.as_ref() + }; + + // Finally, discard packet if no usable key is available. + let aead = match aead { + Some(v) => v, + + None => { + if hdr.ty == packet::Type::ZeroRTT && + self.undecryptable_pkts.len() < MAX_UNDECRYPTABLE_PACKETS && + !self.is_established() + { + // Buffer 0-RTT packets when the required read key is not + // available yet, and process them later. + // + // TODO: in the future we might want to buffer other types + // of undecryptable packets as well. + let pkt_len = b.off() + payload_len; + let pkt = (b.buf()[..pkt_len]).to_vec(); + + self.undecryptable_pkts.push_back((pkt, *info)); + return Ok(pkt_len); + } + + let e = drop_pkt_on_err( Error::CryptoFail, self.recv_count, self.is_server, &self.trace_id, - ) - })?; + ); + + return Err(e); + }, + }; let aead_tag_len = aead.alg().tag_len(); @@ -1721,6 +2044,11 @@ impl Connection { return Err(Error::Done); } + // Packets with no frames are invalid. + if payload.cap() == 0 { + return Err(Error::InvalidPacket); + } + if !self.is_server && !self.got_peer_conn_id { if self.odcid.is_none() { self.odcid = Some(self.dcid.clone()); @@ -1728,18 +2056,21 @@ impl Connection { // Replace the randomly generated destination connection ID with // the one supplied by the server. - self.dcid.resize(hdr.scid.len(), 0); - self.dcid.copy_from_slice(&hdr.scid); + self.dcid = hdr.scid.clone(); self.got_peer_conn_id = true; } if self.is_server && !self.got_peer_conn_id { - self.dcid.extend_from_slice(&hdr.scid); + self.dcid = hdr.scid.clone(); - if !self.did_retry && self.version >= PROTOCOL_VERSION_DRAFT28 { + if !self.did_retry && + (self.version >= PROTOCOL_VERSION_DRAFT28 || + self.version == PROTOCOL_VERSION_V1) + { self.local_transport_params - .original_destination_connection_id = Some(hdr.dcid.to_vec()); + .original_destination_connection_id = + Some(hdr.dcid.to_vec().into()); self.encode_transport_params()?; } @@ -1790,11 +2121,13 @@ impl Connection { if self.is_established() { qlog_with!(self.qlog_streamer, q, { if !self.qlogged_peer_params { + let handshake = self.handshake.lock().unwrap(); + let ev = self.peer_transport_params.to_qlog( qlog::TransportOwner::Remote, self.version, - self.handshake.alpn_protocol(), - self.handshake.cipher(), + handshake.alpn_protocol(), + handshake.cipher(), ); q.add_event(ev).ok(); @@ -1818,23 +2151,47 @@ impl Connection { } }, - frame::Frame::Crypto { data } => { + frame::Frame::CryptoHeader { offset, length } => { self.pkt_num_spaces[epoch] .crypto_stream .send - .ack(data.off(), data.len()); + .ack_and_drop(offset, length); }, - frame::Frame::Stream { stream_id, data } => { + frame::Frame::StreamHeader { + stream_id, + offset, + length, + .. + } => { let stream = match self.streams.get_mut(stream_id) { Some(v) => v, None => continue, }; - stream.send.ack(data.off(), data.len()); + stream.send.ack_and_drop(offset, length); - if stream.is_complete() { + // Only collect the stream if it is complete and not + // readable. If it is readable, it will get collected when + // stream_recv() is used. + if stream.is_complete() && !stream.is_readable() { + let local = stream.local; + self.streams.collect(stream_id, local); + } + }, + + frame::Frame::ResetStream { stream_id, .. } => { + let stream = match self.streams.get_mut(stream_id) { + Some(v) => v, + + None => continue, + }; + + // Only collect the stream if it is complete and not + // readable. If it is readable, it will get collected when + // stream_recv() is used. + if stream.is_complete() && !stream.is_readable() { let local = stream.local; self.streams.collect(stream_id, local); } @@ -1864,6 +2221,12 @@ impl Connection { self.idle_timer = Some(now + idle_timeout); } + // Update send capacity. + self.tx_cap = cmp::min( + self.recovery.cwnd_available() as u64, + self.max_tx_data - self.tx_data, + ) as usize; + self.recv_count += 1; let read = b.off() + aead_tag_len; @@ -1912,10 +2275,11 @@ impl Connection { /// # let mut out = [0; 512]; /// # let socket = std::net::UdpSocket::bind("127.0.0.1:0").unwrap(); /// # let mut config = quiche::Config::new(quiche::PROTOCOL_VERSION)?; - /// # let scid = [0xba; 16]; - /// # let mut conn = quiche::accept(&scid, None, &mut config)?; + /// # let scid = quiche::ConnectionId::from_ref(&[0xba; 16]); + /// # let from = "127.0.0.1:1234".parse().unwrap(); + /// # let mut conn = quiche::accept(&scid, None, from, &mut config)?; /// loop { - /// let write = match conn.send(&mut out) { + /// let (write, send_info) = match conn.send(&mut out) { /// Ok(v) => v, /// /// Err(quiche::Error::Done) => { @@ -1929,47 +2293,162 @@ impl Connection { /// }, /// }; /// - /// socket.send(&out[..write]).unwrap(); + /// socket.send_to(&out[..write], &send_info.to).unwrap(); /// } /// # Ok::<(), quiche::Error>(()) /// ``` - pub fn send(&mut self, out: &mut [u8]) -> Result<usize> { - let now = time::Instant::now(); - + pub fn send(&mut self, out: &mut [u8]) -> Result<(usize, SendInfo)> { if out.is_empty() { return Err(Error::BufferTooShort); } - if self.is_closed() || self.draining_timer.is_some() { + if self.is_closed() || self.is_draining() { return Err(Error::Done); } - // If the Initial secrets have not been derived yet, there's no point - // in trying to send a packet, so return early. + if self.local_error.is_none() { + self.do_handshake()?; + } + + // Process previously undecryptable 0-RTT packets if the decryption key + // is now available. + if self.pkt_num_spaces[packet::EPOCH_APPLICATION] + .crypto_0rtt_open + .is_some() + { + while let Some((mut pkt, info)) = self.undecryptable_pkts.pop_front() + { + if self.recv(&mut pkt, info).is_err() { + self.undecryptable_pkts.clear(); + + // Forwarding the error value here could confuse + // applications, as they may not expect getting a `recv()` + // error when calling `send()`. + // + // We simply fall-through to sending packets, which should + // take care of terminating the connection as needed. + break; + } + } + } + + // There's no point in trying to send a packet if the Initial secrets + // have not been derived yet, so return early. if !self.derived_initial_secrets { return Err(Error::Done); } - let is_closing = self.error.is_some() || self.app_error.is_some(); + let mut has_initial = false; - if !is_closing { - self.do_handshake()?; + let mut done = 0; + + // Limit output packet size to respect the sender and receiver's + // maximum UDP payload size limit. + let mut left = cmp::min(out.len(), self.max_send_udp_payload_size()); + + // Limit data sent by the server based on the amount of data received + // from the client before its address is validated. + if !self.verified_peer_address && self.is_server { + left = cmp::min(left, self.max_send_bytes); } + // Generate coalesced packets. + while left > 0 { + let (ty, written) = match self + .send_single(&mut out[done..done + left], has_initial) + { + Ok(v) => v, + + Err(Error::BufferTooShort) | Err(Error::Done) => break, + + Err(e) => return Err(e), + }; + + done += written; + left -= written; + + match ty { + packet::Type::Initial => has_initial = true, + + // No more packets can be coalesced after a 1-RTT. + packet::Type::Short => break, + + _ => (), + }; + + // When sending multiple PTO probes, don't coalesce them together, + // so they are sent on separate UDP datagrams. + if let Ok(epoch) = ty.to_epoch() { + if self.recovery.loss_probes[epoch] > 0 { + break; + } + } + } + + if done == 0 { + return Err(Error::Done); + } + + // Pad UDP datagram if it contains a QUIC Initial packet. + if has_initial && left > 0 && done < MIN_CLIENT_INITIAL_LEN { + let pad_len = cmp::min(left, MIN_CLIENT_INITIAL_LEN - done); + + // Fill padding area with null bytes, to avoid leaking information + // in case the application reuses the packet buffer. + out[done..done + pad_len].fill(0); + + done += pad_len; + } + + let info = SendInfo { + to: self.peer_addr, + + at: self + .recovery + .get_packet_send_time() + .unwrap_or_else(time::Instant::now), + }; + + Ok((done, info)) + } + + fn send_single( + &mut self, out: &mut [u8], has_initial: bool, + ) -> Result<(packet::Type, usize)> { + let now = time::Instant::now(); + + if out.is_empty() { + return Err(Error::BufferTooShort); + } + + if self.is_draining() { + return Err(Error::Done); + } + + let is_closing = self.local_error.is_some(); + let mut b = octets::OctetsMut::with_slice(out); - let epoch = self.write_epoch()?; + let pkt_type = self.write_pkt_type()?; - let pkt_type = packet::Type::from_epoch(epoch); + let epoch = pkt_type.to_epoch()?; // Process lost frames. for lost in self.recovery.lost[epoch].drain(..) { match lost { - frame::Frame::Crypto { data } => { - self.pkt_num_spaces[epoch].crypto_stream.send.push(data)?; + frame::Frame::CryptoHeader { offset, length } => { + self.pkt_num_spaces[epoch] + .crypto_stream + .send + .retransmit(offset, length); }, - frame::Frame::Stream { stream_id, data } => { + frame::Frame::StreamHeader { + stream_id, + offset, + length, + fin, + } => { let stream = match self.streams.get_mut(stream_id) { Some(v) => v, @@ -1978,9 +2457,9 @@ impl Connection { let was_flushable = stream.is_flushable(); - let empty_fin = data.is_empty() && data.fin(); + let empty_fin = length == 0 && fin; - stream.send.push(data)?; + stream.send.retransmit(offset, length); // If the stream is now flushable push it to the flushable // queue, but only if it wasn't already queued. @@ -2002,6 +2481,16 @@ impl Connection { self.pkt_num_spaces[epoch].ack_elicited = true; }, + frame::Frame::ResetStream { + stream_id, + error_code, + final_size, + } => + if self.streams.get(stream_id).is_some() { + self.streams + .mark_reset(stream_id, true, error_code, final_size); + }, + frame::Frame::HandshakeDone => { self.handshake_done_sent = false; }, @@ -2022,18 +2511,9 @@ impl Connection { let mut left = b.cap(); - // Limit output packet size to respect peer's max_packet_size limit. - left = cmp::min(left, self.max_send_udp_payload_len()); - // Limit output packet size by congestion window size. left = cmp::min(left, self.recovery.cwnd_available()); - // Limit data sent by the server based on the amount of data received - // from the client before its address is validated. - if !self.verified_peer_address && self.is_server { - left = cmp::min(left, self.max_send_bytes); - } - let pn = self.pkt_num_spaces[epoch].next_pkt_num; let pn_len = packet::pkt_num_len(pn)?; @@ -2044,16 +2524,11 @@ impl Connection { let hdr = Header { ty: pkt_type, + version: self.version, - dcid: self.dcid.clone(), - // Don't needlessly clone the source connection ID for 1-RTT packets - // as it is not used. - scid: if pkt_type != packet::Type::Short { - self.scid.clone() - } else { - Vec::new() - }, + dcid: ConnectionId::from_ref(&self.dcid), + scid: ConnectionId::from_ref(&self.scid), pkt_num: 0, pkt_num_len: pn_len, @@ -2080,10 +2555,10 @@ impl Connection { // We assume that the payload length, which is only present in long // header packets, can always be encoded with a 2-byte varint. if pkt_type != packet::Type::Short { - overhead += 2; + overhead += PAYLOAD_LENGTH_LEN; } - // Make sure we have enough space left for the packet. + // Make sure we have enough space left for the packet overhead. match left.checked_sub(overhead) { Some(v) => left = v, @@ -2099,13 +2574,31 @@ impl Connection { }, } + // Make sure there is enough space for the minimum payload length. + if left < PAYLOAD_MIN_LEN { + self.recovery.update_app_limited(false); + return Err(Error::Done); + } + let mut frames: Vec<frame::Frame> = Vec::new(); let mut ack_eliciting = false; let mut in_flight = false; let mut has_data = false; - let mut payload_len = 0; + let header_offset = b.off(); + + // Reserve space for payload length in advance. Since we don't yet know + // what the final length will be, we reserve 2 bytes in all cases. + // + // Only long header packets have an explicit length field. + if pkt_type != packet::Type::Short { + b.skip(PAYLOAD_LENGTH_LEN)?; + } + + packet::encode_pkt_num(pn, &mut b)?; + + let payload_offset = b.off(); // Create ACK frame. if self.pkt_num_spaces[epoch].recv_pkt_need_ack.len() > 0 && @@ -2125,7 +2618,7 @@ impl Connection { ranges: self.pkt_num_spaces[epoch].recv_pkt_need_ack.clone(), }; - if push_frame_to_pkt!(frames, frame, payload_len, left) { + if push_frame_to_pkt!(b, frames, frame, left) { self.pkt_num_spaces[epoch].ack_elicited = false; } } @@ -2138,7 +2631,7 @@ impl Connection { { let frame = frame::Frame::HandshakeDone; - if push_frame_to_pkt!(frames, frame, payload_len, left) { + if push_frame_to_pkt!(b, frames, frame, left) { self.handshake_done_sent = true; ack_eliciting = true; @@ -2152,7 +2645,7 @@ impl Connection { max: self.streams.max_streams_bidi_next(), }; - if push_frame_to_pkt!(frames, frame, payload_len, left) { + if push_frame_to_pkt!(b, frames, frame, left) { self.streams.update_max_streams_bidi(); ack_eliciting = true; @@ -2166,7 +2659,7 @@ impl Connection { max: self.streams.max_streams_uni_next(), }; - if push_frame_to_pkt!(frames, frame, payload_len, left) { + if push_frame_to_pkt!(b, frames, frame, left) { self.streams.update_max_streams_uni(); ack_eliciting = true; @@ -2174,28 +2667,11 @@ impl Connection { } } - // Create MAX_DATA frame as needed. - if self.almost_full { - let frame = frame::Frame::MaxData { - max: self.max_rx_data_next, - }; - - if push_frame_to_pkt!(frames, frame, payload_len, left) { - self.almost_full = false; - - // Commits the new max_rx_data limit. - self.max_rx_data = self.max_rx_data_next; - - ack_eliciting = true; - in_flight = true; - } - } - // Create DATA_BLOCKED frame. if let Some(limit) = self.blocked_limit { let frame = frame::Frame::DataBlocked { limit }; - if push_frame_to_pkt!(frames, frame, payload_len, left) { + if push_frame_to_pkt!(b, frames, frame, left) { self.blocked_limit = None; ack_eliciting = true; @@ -2221,13 +2697,75 @@ impl Connection { max: stream.recv.max_data_next(), }; - if push_frame_to_pkt!(frames, frame, payload_len, left) { + if push_frame_to_pkt!(b, frames, frame, left) { stream.recv.update_max_data(); self.streams.mark_almost_full(stream_id, false); ack_eliciting = true; in_flight = true; + + // Also send MAX_DATA when MAX_STREAM_DATA is sent, to avoid a + // potential race condition. + self.almost_full = true; + } + } + + // Create MAX_DATA frame as needed. + if self.almost_full && self.max_rx_data < self.max_rx_data_next { + let frame = frame::Frame::MaxData { + max: self.max_rx_data_next, + }; + + if push_frame_to_pkt!(b, frames, frame, left) { + self.almost_full = false; + + // Commits the new max_rx_data limit. + self.max_rx_data = self.max_rx_data_next; + + ack_eliciting = true; + in_flight = true; + } + } + + // Create STOP_SENDING frames as needed. + for (stream_id, error_code) in self + .streams + .stopped() + .map(|(&k, &v)| (k, v)) + .collect::<Vec<(u64, u64)>>() + { + let frame = frame::Frame::StopSending { + stream_id, + error_code, + }; + + if push_frame_to_pkt!(b, frames, frame, left) { + self.streams.mark_stopped(stream_id, false, 0); + + ack_eliciting = true; + in_flight = true; + } + } + + // Create RESET_STREAM frames as needed. + for (stream_id, (error_code, final_size)) in self + .streams + .reset() + .map(|(&k, &v)| (k, v)) + .collect::<Vec<(u64, (u64, u64))>>() + { + let frame = frame::Frame::ResetStream { + stream_id, + error_code, + final_size, + }; + + if push_frame_to_pkt!(b, frames, frame, left) { + self.streams.mark_reset(stream_id, false, 0, 0); + + ack_eliciting = true; + in_flight = true; } } @@ -2240,7 +2778,7 @@ impl Connection { { let frame = frame::Frame::StreamDataBlocked { stream_id, limit }; - if push_frame_to_pkt!(frames, frame, payload_len, left) { + if push_frame_to_pkt!(b, frames, frame, left) { self.streams.mark_blocked(stream_id, false, 0); ack_eliciting = true; @@ -2250,30 +2788,32 @@ impl Connection { } // Create CONNECTION_CLOSE frame. - if let Some(err) = self.error { - let frame = frame::Frame::ConnectionClose { - error_code: err, - frame_type: 0, - reason: Vec::new(), - }; + if let Some(conn_err) = self.local_error.as_ref() { + if conn_err.is_app { + // Create ApplicationClose frame. + if pkt_type == packet::Type::Short { + let frame = frame::Frame::ApplicationClose { + error_code: conn_err.error_code, + reason: conn_err.reason.clone(), + }; - if push_frame_to_pkt!(frames, frame, payload_len, left) { - self.draining_timer = Some(now + (self.recovery.pto() * 3)); + if push_frame_to_pkt!(b, frames, frame, left) { + self.draining_timer = + Some(now + (self.recovery.pto() * 3)); - ack_eliciting = true; - in_flight = true; - } - } - - // Create APPLICATION_CLOSE frame. - if let Some(err) = self.app_error { - if pkt_type == packet::Type::Short { - let frame = frame::Frame::ApplicationClose { - error_code: err, - reason: self.app_reason.clone(), + ack_eliciting = true; + in_flight = true; + } + } + } else { + // Create ConnectionClose frame. + let frame = frame::Frame::ConnectionClose { + error_code: conn_err.error_code, + frame_type: 0, + reason: conn_err.reason.clone(), }; - if push_frame_to_pkt!(frames, frame, payload_len, left) { + if push_frame_to_pkt!(b, frames, frame, left) { self.draining_timer = Some(now + (self.recovery.pto() * 3)); ack_eliciting = true; @@ -2288,7 +2828,7 @@ impl Connection { data: challenge.clone(), }; - if push_frame_to_pkt!(frames, frame, payload_len, left) { + if push_frame_to_pkt!(b, frames, frame, left) { self.challenge = None; ack_eliciting = true; @@ -2301,25 +2841,88 @@ impl Connection { left > frame::MAX_CRYPTO_OVERHEAD && !is_closing { - let crypto_len = left - frame::MAX_CRYPTO_OVERHEAD; - let crypto_buf = self.pkt_num_spaces[epoch] - .crypto_stream - .send - .pop(crypto_len)?; + let crypto_off = + self.pkt_num_spaces[epoch].crypto_stream.send.off_front(); + + // Encode the frame. + // + // Instead of creating a `frame::Frame` object, encode the frame + // directly into the packet buffer. + // + // First we reserve some space in the output buffer for writing the + // frame header (we assume the length field is always a 2-byte + // varint as we don't know the value yet). + // + // Then we emit the data from the crypto stream's send buffer. + // + // Finally we go back and encode the frame header with the now + // available information. + let hdr_off = b.off(); + let hdr_len = 1 + // frame type + octets::varint_len(crypto_off) + // offset + 2; // length, always encode as 2-byte varint + + if let Some(max_len) = left.checked_sub(hdr_len) { + let (mut crypto_hdr, mut crypto_payload) = + b.split_at(hdr_off + hdr_len)?; + + // Write stream data into the packet buffer. + let (len, _) = self.pkt_num_spaces[epoch] + .crypto_stream + .send + .emit(&mut crypto_payload.as_mut()[..max_len])?; + + // Encode the frame's header. + // + // Due to how `OctetsMut::split_at()` works, `crypto_hdr` starts + // from the initial offset of `b` (rather than the current + // offset), so it needs to be advanced to the + // initial frame offset. + crypto_hdr.skip(hdr_off)?; + + frame::encode_crypto_header( + crypto_off, + len as u64, + &mut crypto_hdr, + )?; - let frame = frame::Frame::Crypto { data: crypto_buf }; + // Advance the packet buffer's offset. + b.skip(hdr_len + len)?; - if push_frame_to_pkt!(frames, frame, payload_len, left) { - ack_eliciting = true; - in_flight = true; - has_data = true; + let frame = frame::Frame::CryptoHeader { + offset: crypto_off, + length: len, + }; + + if push_frame_to_pkt!(b, frames, frame, left) { + ack_eliciting = true; + in_flight = true; + has_data = true; + } } } + // The preference of data-bearing frame to include in a packet + // is managed by `self.emit_dgram`. However, whether any frames + // can be sent depends on the state of their buffers. In the case + // where one type is preferred but its buffer is empty, fall back + // to the other type in order not to waste this function call. + let mut dgram_emitted = false; + let dgrams_to_emit = self.dgram_max_writable_len().is_some(); + let stream_to_emit = self.streams.has_flushable(); + + let mut do_dgram = self.emit_dgram && dgrams_to_emit; + let do_stream = !self.emit_dgram && stream_to_emit; + + if !do_stream && dgrams_to_emit { + do_dgram = true; + } + // Create DATAGRAM frame. - if pkt_type == packet::Type::Short && + if (pkt_type == packet::Type::Short || pkt_type == packet::Type::ZeroRTT) && left > frame::MAX_DGRAM_OVERHEAD && - !is_closing + !is_closing && + do_dgram { if let Some(max_dgram_payload) = self.dgram_max_writable_len() { while let Some(len) = self.dgram_send_queue.peek_front_len() { @@ -2329,14 +2932,10 @@ impl Connection { Some(data) => { let frame = frame::Frame::Datagram { data }; - if push_frame_to_pkt!( - frames, - frame, - payload_len, - left - ) { + if push_frame_to_pkt!(b, frames, frame, left) { ack_eliciting = true; in_flight = true; + dgram_emitted = true; } }, @@ -2353,9 +2952,10 @@ impl Connection { } // Create a single STREAM frame for the first stream that is flushable. - if pkt_type == packet::Type::Short && + if (pkt_type == packet::Type::Short || pkt_type == packet::Type::ZeroRTT) && left > frame::MAX_STREAM_OVERHEAD && - !is_closing + !is_closing && + !dgram_emitted { while let Some(stream_id) = self.streams.pop_flushable() { let stream = match self.streams.get_mut(stream_id) { @@ -2364,34 +2964,76 @@ impl Connection { None => continue, }; - let off = stream.send.off_front(); + // Avoid sending frames for streams that were already stopped. + // + // This might happen if stream data was buffered but not yet + // flushed on the wire when a STOP_SENDING frame is received. + if stream.send.is_stopped() { + continue; + } - // Try to accurately account for the STREAM frame's overhead, - // such that we can fill as much of the packet buffer as - // possible. - let overhead = 1 + - octets::varint_len(stream_id) + - octets::varint_len(off) + - octets::varint_len(left as u64); + let stream_off = stream.send.off_front(); - let max_len = match left.checked_sub(overhead) { + // Encode the frame. + // + // Instead of creating a `frame::Frame` object, encode the frame + // directly into the packet buffer. + // + // First we reserve some space in the output buffer for writing + // the frame header (we assume the length field is + // always a 2-byte varint as we don't know the + // value yet). + // + // Then we emit the data from the stream's send buffer. + // + // Finally we go back and encode the frame header with the now + // available information. + let hdr_off = b.off(); + let hdr_len = 1 + // frame type + octets::varint_len(stream_id) + // stream_id + octets::varint_len(stream_off) + // offset + 2; // length, always encode as 2-byte varint + + let max_len = match left.checked_sub(hdr_len) { Some(v) => v, None => continue, }; - let stream_buf = stream.send.pop(max_len)?; + let (mut stream_hdr, mut stream_payload) = + b.split_at(hdr_off + hdr_len)?; - if stream_buf.is_empty() && !stream_buf.fin() { - continue; - } + // Write stream data into the packet buffer. + let (len, fin) = + stream.send.emit(&mut stream_payload.as_mut()[..max_len])?; + + // Encode the frame's header. + // + // Due to how `OctetsMut::split_at()` works, `stream_hdr` starts + // from the initial offset of `b` (rather than the current + // offset), so it needs to be advanced to the initial frame + // offset. + stream_hdr.skip(hdr_off)?; - let frame = frame::Frame::Stream { + frame::encode_stream_header( stream_id, - data: stream_buf, + stream_off, + len as u64, + fin, + &mut stream_hdr, + )?; + + // Advance the packet buffer's offset. + b.skip(hdr_len + len)?; + + let frame = frame::Frame::StreamHeader { + stream_id, + offset: stream_off, + length: len, + fin, }; - if push_frame_to_pkt!(frames, frame, payload_len, left) { + if push_frame_to_pkt!(b, frames, frame, left) { ack_eliciting = true; in_flight = true; has_data = true; @@ -2416,6 +3058,9 @@ impl Connection { } } + // Alternate trying to send DATAGRAMs next time. + self.emit_dgram = !dgram_emitted; + // Create PING for PTO probe if no other ack-elicitng frame is sent. if self.recovery.loss_probes[epoch] > 0 && !ack_eliciting && @@ -2424,7 +3069,7 @@ impl Connection { { let frame = frame::Frame::Ping; - if push_frame_to_pkt!(frames, frame, payload_len, left) { + if push_frame_to_pkt!(b, frames, frame, left) { ack_eliciting = true; in_flight = true; } @@ -2442,45 +3087,44 @@ impl Connection { return Err(Error::Done); } - // Pad the client's initial packet. - if !self.is_server && pkt_type == packet::Type::Initial { - let pkt_len = pn_len + payload_len + crypto_overhead; - - let frame = frame::Frame::Padding { - len: cmp::min(MIN_CLIENT_INITIAL_LEN - pkt_len, left), - }; - - payload_len += frame.wire_len(); - - frames.push(frame); + // When coalescing a 1-RTT packet, we can't add padding in the UDP + // datagram, so use PADDING frames instead. + // + // This is only needed if an Initial packet has already been written to + // the UDP datagram, as Initial always requires padding. + if has_initial && pkt_type == packet::Type::Short && left >= 1 { + let frame = frame::Frame::Padding { len: left }; - in_flight = true; + if push_frame_to_pkt!(b, frames, frame, left) { + in_flight = true; + } } // Pad payload so that it's always at least 4 bytes. - if payload_len < PAYLOAD_MIN_LEN { + if b.off() - payload_offset < PAYLOAD_MIN_LEN { + let payload_len = b.off() - payload_offset; + let frame = frame::Frame::Padding { len: PAYLOAD_MIN_LEN - payload_len, }; - payload_len += frame.wire_len(); - - frames.push(frame); - - in_flight = true; + #[allow(unused_assignments)] + if push_frame_to_pkt!(b, frames, frame, left) { + in_flight = true; + } } - payload_len += crypto_overhead; + let payload_len = b.off() - payload_offset; + let payload_len = payload_len + crypto_overhead; - // Only long header packets have an explicit length field. + // Fill in payload length. if pkt_type != packet::Type::Short { let len = pn_len + payload_len; - b.put_varint(len as u64)?; - } - - packet::encode_pkt_num(pn, &mut b)?; - let payload_offset = b.off(); + let (_, mut payload_with_len) = b.split_at(header_offset)?; + payload_with_len + .put_varint_with_len(len as u64, PAYLOAD_LENGTH_LEN)?; + } trace!( "{} tx pkt {:?} len={} pn={}", @@ -2510,12 +3154,9 @@ impl Connection { q.add_event(packet_sent_ev).ok(); }); - // Encode frames into the output packet. for frame in &mut frames { trace!("{} tx frm {:?}", self.trace_id, frame); - frame.to_bytes(&mut b)?; - qlog_with!(self.qlog_streamer, q, { q.add_frame(frame.to_qlog(), false).ok(); }); @@ -2600,23 +3241,7 @@ impl Connection { self.ack_eliciting_sent = true; } - Ok(written) - } - - // Returns the maximum len of a packet to be sent. This is max_packet_size - // as sent by the peer, except during the handshake when we haven't parsed - // transport parameters yet, so use a default value then. - fn max_send_udp_payload_len(&self) -> usize { - if self.is_established() { - // We cap the maximum packet size to 16KB or so, so that it can be - // always encoded with a 2-byte varint. - cmp::min(16383, self.peer_transport_params.max_udp_payload_size) - as usize - } else { - // Allow for 1200 bytes (minimum QUIC packet size) during the - // handshake. - MIN_CLIENT_INITIAL_LEN - } + Ok((pkt_type, written)) } /// Reads contiguous data from a stream into the provided slice. @@ -2635,8 +3260,9 @@ impl Connection { /// # let mut buf = [0; 512]; /// # let socket = std::net::UdpSocket::bind("127.0.0.1:0").unwrap(); /// # let mut config = quiche::Config::new(quiche::PROTOCOL_VERSION)?; - /// # let scid = [0xba; 16]; - /// # let mut conn = quiche::accept(&scid, None, &mut config)?; + /// # let scid = quiche::ConnectionId::from_ref(&[0xba; 16]); + /// # let from = "127.0.0.1:1234".parse().unwrap(); + /// # let mut conn = quiche::accept(&scid, None, from, &mut config)?; /// # let stream_id = 0; /// while let Ok((read, fin)) = conn.stream_recv(stream_id, &mut buf) { /// println!("Got {} bytes on stream {}", read, stream_id); @@ -2650,13 +3276,13 @@ impl Connection { if !stream::is_bidi(stream_id) && stream::is_local(stream_id, self.is_server) { - return Err(Error::InvalidStreamState); + return Err(Error::InvalidStreamState(stream_id)); } let stream = self .streams .get_mut(stream_id) - .ok_or(Error::InvalidStreamState)?; + .ok_or(Error::InvalidStreamState(stream_id))?; if !stream.is_readable() { return Err(Error::Done); @@ -2665,7 +3291,7 @@ impl Connection { #[cfg(feature = "qlog")] let offset = stream.recv.off_front(); - let (read, fin) = stream.recv.pop(out)?; + let (read, fin) = stream.recv.emit(out)?; self.max_rx_data_next = self.max_rx_data_next.saturating_add(read as u64); @@ -2711,6 +3337,10 @@ impl Connection { /// On success the number of bytes written is returned, or [`Done`] if no /// data was written (e.g. because the stream has no capacity). /// + /// In addition, if the peer has signalled that it doesn't want to receive + /// any more data from this stream by sending the `STOP_SENDING` frame, the + /// [`StreamStopped`] error will be returned instead of any data. + /// /// Note that in order to avoid buffering an infinite amount of data in the /// stream's send buffer, streams are only allowed to buffer outgoing data /// up to the amount that the peer allows it to send (that is, up to the @@ -2726,6 +3356,7 @@ impl Connection { /// early data if enabled (whenever [`is_in_early_data()`] returns `true`). /// /// [`Done`]: enum.Error.html#variant.Done + /// [`StreamStopped`]: enum.Error.html#variant.StreamStopped /// [`is_established()`]: struct.Connection.html#method.is_established /// [`is_in_early_data()`]: struct.Connection.html#method.is_in_early_data /// @@ -2735,8 +3366,9 @@ impl Connection { /// # let mut buf = [0; 512]; /// # let socket = std::net::UdpSocket::bind("127.0.0.1:0").unwrap(); /// # let mut config = quiche::Config::new(quiche::PROTOCOL_VERSION)?; - /// # let scid = [0xba; 16]; - /// # let mut conn = quiche::accept(&scid, None, &mut config)?; + /// # let scid = quiche::ConnectionId::from_ref(&[0xba; 16]); + /// # let from = "127.0.0.1:1234".parse().unwrap(); + /// # let mut conn = quiche::accept(&scid, None, from, &mut config)?; /// # let stream_id = 0; /// conn.stream_send(stream_id, b"hello", true)?; /// # Ok::<(), quiche::Error>(()) @@ -2748,7 +3380,7 @@ impl Connection { if !stream::is_bidi(stream_id) && !stream::is_local(stream_id, self.is_server) { - return Err(Error::InvalidStreamState); + return Err(Error::InvalidStreamState(stream_id)); } // Mark the connection as blocked if the connection-level flow control @@ -2762,7 +3394,7 @@ impl Connection { // Truncate the input buffer based on the connection's send capacity if // necessary. - let cap = self.send_capacity(); + let cap = self.tx_cap; let (buf, fin) = if cap < buf.len() { (&buf[..cap], false) @@ -2778,7 +3410,14 @@ impl Connection { let was_flushable = stream.is_flushable(); - let sent = stream.send.push_slice(buf, fin)?; + let sent = match stream.send.write(buf, fin) { + Ok(v) => v, + + Err(e) => { + self.streams.mark_writable(stream_id, false); + return Err(e); + }, + }; let urgency = stream.urgency; let incremental = stream.incremental; @@ -2810,6 +3449,8 @@ impl Connection { self.streams.mark_writable(stream_id, false); } + self.tx_cap -= sent; + self.tx_data += sent as u64; self.recovery.rate_check_app_limited(); @@ -2868,7 +3509,8 @@ impl Connection { /// data in the stream's receive buffer is dropped, and no additional data /// is added to it. Data received after calling this method is still /// validated and acked but not stored, and [`stream_recv()`] will not - /// return it to the application. + /// return it to the application. In addition, a `STOP_SENDING` frame will + /// be sent to the peer to signal it to stop sending data. /// /// When the `direction` argument is set to [`Shutdown::Write`], outstanding /// data in the stream's send buffer is dropped, and no additional data @@ -2880,23 +3522,27 @@ impl Connection { /// [`stream_recv()`]: struct.Connection.html#method.stream_recv /// [`stream_send()`]: struct.Connection.html#method.stream_send pub fn stream_shutdown( - &mut self, stream_id: u64, direction: Shutdown, _err: u64, + &mut self, stream_id: u64, direction: Shutdown, err: u64, ) -> Result<()> { // Get existing stream. let stream = self.streams.get_mut(stream_id).ok_or(Error::Done)?; match direction { - // TODO: send STOP_SENDING Shutdown::Read => { stream.recv.shutdown()?; + if !stream.recv.is_fin() { + self.streams.mark_stopped(stream_id, true, err); + } + // Once shutdown, the stream is guaranteed to be non-readable. self.streams.mark_readable(stream_id, false); }, - // TODO: send RESET_STREAM Shutdown::Write => { - stream.send.shutdown()?; + let final_size = stream.send.shutdown()?; + + self.streams.mark_reset(stream_id, true, err, final_size); // Once shutdown, the stream is guaranteed to be non-writable. self.streams.mark_writable(stream_id, false); @@ -2907,13 +3553,36 @@ impl Connection { } /// Returns the stream's send capacity in bytes. + /// + /// If the specified stream doesn't exist (including when it has already + /// been completed and closed), the [`InvalidStreamState`] error will be + /// returned. + /// + /// In addition, if the peer has signalled that it doesn't want to receive + /// any more data from this stream by sending the `STOP_SENDING` frame, the + /// [`StreamStopped`] error will be returned. + /// + /// [`InvalidStreamState`]: enum.Error.html#variant.InvalidStreamState + /// [`StreamStopped`]: enum.Error.html#variant.StreamStopped + #[inline] pub fn stream_capacity(&self, stream_id: u64) -> Result<usize> { if let Some(stream) = self.streams.get(stream_id) { - let cap = cmp::min(self.send_capacity(), stream.send.cap()); + let cap = cmp::min(self.tx_cap, stream.send.cap()?); return Ok(cap); }; - Err(Error::InvalidStreamState) + Err(Error::InvalidStreamState(stream_id)) + } + + /// Returns true if the stream has data that can be read. + pub fn stream_readable(&self, stream_id: u64) -> bool { + let stream = match self.streams.get(stream_id) { + Some(v) => v, + + None => return false, + }; + + stream.is_readable() } /// Returns true if all the data has been read from the specified stream. @@ -2924,6 +3593,7 @@ impl Connection { /// /// Basically this returns true when the peer either set the `fin` flag /// for the stream, or sent `RESET_STREAM`. + #[inline] pub fn stream_finished(&self, stream_id: u64) -> bool { let stream = match self.streams.get(stream_id) { Some(v) => v, @@ -2934,6 +3604,26 @@ impl Connection { stream.recv.is_fin() } + /// Returns the number of bidirectional streams that can be created + /// before the peer's stream count limit is reached. + /// + /// This can be useful to know if it's possible to create a bidirectional + /// stream without trying it first. + #[inline] + pub fn peer_streams_left_bidi(&self) -> u64 { + self.streams.peer_streams_left_bidi() + } + + /// Returns the number of unidirectional streams that can be created + /// before the peer's stream count limit is reached. + /// + /// This can be useful to know if it's possible to create a unidirectional + /// stream without trying it first. + #[inline] + pub fn peer_streams_left_uni(&self) -> u64 { + self.streams.peer_streams_left_uni() + } + /// Initializes the stream's application data. /// /// This can be used by applications to store per-stream information without @@ -2947,7 +3637,7 @@ impl Connection { &mut self, stream_id: u64, data: T, ) -> Result<()> where - T: std::any::Any + Send, + T: std::any::Any + Send + Sync, { // Get existing stream. let stream = self.streams.get_mut(stream_id).ok_or(Error::Done)?; @@ -2994,8 +3684,9 @@ impl Connection { /// # let mut buf = [0; 512]; /// # let socket = std::net::UdpSocket::bind("127.0.0.1:0").unwrap(); /// # let mut config = quiche::Config::new(quiche::PROTOCOL_VERSION)?; - /// # let scid = [0xba; 16]; - /// # let mut conn = quiche::accept(&scid, None, &mut config)?; + /// # let scid = quiche::ConnectionId::from_ref(&[0xba; 16]); + /// # let from = "127.0.0.1:1234".parse().unwrap(); + /// # let mut conn = quiche::accept(&scid, None, from, &mut config)?; /// // Iterate over readable streams. /// for stream_id in conn.readable() { /// // Stream is readable, read until there's no more data. @@ -3005,6 +3696,7 @@ impl Connection { /// } /// # Ok::<(), quiche::Error>(()) /// ``` + #[inline] pub fn readable(&self) -> StreamIter { self.streams.readable() } @@ -3027,8 +3719,9 @@ impl Connection { /// # let mut buf = [0; 512]; /// # let socket = std::net::UdpSocket::bind("127.0.0.1:0").unwrap(); /// # let mut config = quiche::Config::new(quiche::PROTOCOL_VERSION)?; - /// # let scid = [0xba; 16]; - /// # let mut conn = quiche::accept(&scid, None, &mut config)?; + /// # let scid = quiche::ConnectionId::from_ref(&[0xba; 16]); + /// # let from = "127.0.0.1:1234".parse().unwrap(); + /// # let mut conn = quiche::accept(&scid, None, from, &mut config)?; /// // Iterate over writable streams. /// for stream_id in conn.writable() { /// // Stream is writable, write some data. @@ -3038,16 +3731,42 @@ impl Connection { /// } /// # Ok::<(), quiche::Error>(()) /// ``` + #[inline] pub fn writable(&self) -> StreamIter { // If there is not enough connection-level send capacity, none of the // streams are writable, so return an empty iterator. - if self.send_capacity() == 0 { + if self.tx_cap == 0 { return StreamIter::default(); } self.streams.writable() } + /// Returns the maximum possible size of egress UDP payloads. + /// + /// This is the maximum size of UDP payloads that can be sent, and depends + /// on both the configured maximum send payload size of the local endpoint + /// (as configured with [`set_max_send_udp_payload_size()`]), as well as + /// the transport parameter advertised by the remote peer. + /// + /// Note that this value can change during the lifetime of the connection, + /// but should remain stable across consecutive calls to [`send()`]. + /// + /// [`set_max_send_udp_payload_size()`]: + /// struct.Config.html#method.set_max_send_udp_payload_size + /// [`send()`]: struct.Connection.html#method.send + pub fn max_send_udp_payload_size(&self) -> usize { + if self.is_established() { + // We cap the maximum packet size to 16KB or so, so that it can be + // always encoded with a 2-byte varint. + cmp::min(16383, self.recovery.max_datagram_size()) + } else { + // Allow for 1200 bytes (minimum QUIC packet size) during the + // handshake. + MIN_CLIENT_INITIAL_LEN + } + } + /// Reads the first received DATAGRAM. /// /// On success the DATAGRAM's data is returned along with its size. @@ -3066,14 +3785,16 @@ impl Connection { /// # let mut buf = [0; 512]; /// # let socket = std::net::UdpSocket::bind("127.0.0.1:0").unwrap(); /// # let mut config = quiche::Config::new(quiche::PROTOCOL_VERSION)?; - /// # let scid = [0xba; 16]; - /// # let mut conn = quiche::accept(&scid, None, &mut config)?; + /// # let scid = quiche::ConnectionId::from_ref(&[0xba; 16]); + /// # let from = "127.0.0.1:1234".parse().unwrap(); + /// # let mut conn = quiche::accept(&scid, None, from, &mut config)?; /// let mut dgram_buf = [0; 512]; /// while let Ok((len)) = conn.dgram_recv(&mut dgram_buf) { /// println!("Got {} bytes of DATAGRAM", len); /// } /// # Ok::<(), quiche::Error>(()) /// ``` + #[inline] pub fn dgram_recv(&mut self, buf: &mut [u8]) -> Result<usize> { match self.dgram_recv_queue.pop() { Some(d) => { @@ -3102,15 +3823,41 @@ impl Connection { /// /// [`Done`]: enum.Error.html#variant.Done /// [`BufferTooShort`]: enum.Error.html#variant.BufferTooShort + #[inline] pub fn dgram_recv_peek(&self, buf: &mut [u8], len: usize) -> Result<usize> { self.dgram_recv_queue.peek_front_bytes(buf, len) } /// Returns the length of the first stored DATAGRAM. + #[inline] pub fn dgram_recv_front_len(&self) -> Option<usize> { self.dgram_recv_queue.peek_front_len() } + /// Returns the number of items in the DATAGRAM receive queue. + #[inline] + pub fn dgram_recv_queue_len(&self) -> usize { + self.dgram_recv_queue.len() + } + + /// Returns the total size of all items in the DATAGRAM receive queue. + #[inline] + pub fn dgram_recv_queue_byte_size(&self) -> usize { + self.dgram_recv_queue.byte_size() + } + + /// Returns the number of items in the DATAGRAM send queue. + #[inline] + pub fn dgram_send_queue_len(&self) -> usize { + self.dgram_send_queue.len() + } + + /// Returns the total size of all items in the DATAGRAM send queue. + #[inline] + pub fn dgram_send_queue_byte_size(&self) -> usize { + self.dgram_send_queue.byte_size() + } + /// Sends data in a DATAGRAM frame. /// /// [`Done`] is returned if no data was written. @@ -3136,8 +3883,9 @@ impl Connection { /// # let mut buf = [0; 512]; /// # let socket = std::net::UdpSocket::bind("127.0.0.1:0").unwrap(); /// # let mut config = quiche::Config::new(quiche::PROTOCOL_VERSION)?; - /// # let scid = [0xba; 16]; - /// # let mut conn = quiche::accept(&scid, None, &mut config)?; + /// # let scid = quiche::ConnectionId::from_ref(&[0xba; 16]); + /// # let from = "127.0.0.1:1234".parse().unwrap(); + /// # let mut conn = quiche::accept(&scid, None, from, &mut config)?; /// conn.dgram_send(b"hello")?; /// # Ok::<(), quiche::Error>(()) /// ``` @@ -3170,12 +3918,14 @@ impl Connection { /// ```no_run /// # let socket = std::net::UdpSocket::bind("127.0.0.1:0").unwrap(); /// # let mut config = quiche::Config::new(quiche::PROTOCOL_VERSION)?; - /// # let scid = [0xba; 16]; - /// # let mut conn = quiche::accept(&scid, None, &mut config)?; + /// # let scid = quiche::ConnectionId::from_ref(&[0xba; 16]); + /// # let from = "127.0.0.1:1234".parse().unwrap(); + /// # let mut conn = quiche::accept(&scid, None, from, &mut config)?; /// conn.dgram_send(b"hello")?; /// conn.dgram_purge_outgoing(&|d: &[u8]| -> bool { d[0] == 0 }); /// # Ok::<(), quiche::Error>(()) /// ``` + #[inline] pub fn dgram_purge_outgoing<F: Fn(&[u8]) -> bool>(&mut self, f: F) { self.dgram_send_queue.purge(f); } @@ -3191,8 +3941,9 @@ impl Connection { /// # let mut buf = [0; 512]; /// # let socket = std::net::UdpSocket::bind("127.0.0.1:0").unwrap(); /// # let mut config = quiche::Config::new(quiche::PROTOCOL_VERSION)?; - /// # let scid = [0xba; 16]; - /// # let mut conn = quiche::accept(&scid, None, &mut config)?; + /// # let scid = quiche::ConnectionId::from_ref(&[0xba; 16]); + /// # let from = "127.0.0.1:1234".parse().unwrap(); + /// # let mut conn = quiche::accept(&scid, None, from, &mut config)?; /// if let Some(payload_size) = conn.dgram_max_writable_len() { /// if payload_size > 5 { /// conn.dgram_send(b"hello")?; @@ -3200,12 +3951,13 @@ impl Connection { /// } /// # Ok::<(), quiche::Error>(()) /// ``` + #[inline] pub fn dgram_max_writable_len(&self) -> Option<usize> { match self.peer_transport_params.max_datagram_frame_size { None => None, Some(peer_frame_len) => { // Start from the maximum packet size... - let mut max_len = self.max_send_udp_payload_len(); + let mut max_len = self.max_send_udp_payload_size(); // ...subtract the Short packet header overhead... // (1 byte of pkt_len + len of dcid) max_len = max_len.saturating_sub(1 + self.dcid.len()); @@ -3219,7 +3971,8 @@ impl Connection { // ...clamp to what peer can support... max_len = cmp::min(peer_frame_len as usize, max_len); // ...subtract frame overhead, checked for underflow. - max_len.checked_sub(frame::MAX_DGRAM_OVERHEAD) + // (1 byte of frame type + len of length ) + max_len.checked_sub(1 + frame::MAX_DGRAM_OVERHEAD) }, } } @@ -3227,7 +3980,7 @@ impl Connection { fn dgram_enabled(&self) -> bool { self.local_transport_params .max_datagram_frame_size - .is_none() + .is_some() } /// Returns the amount of time until the next timeout event. @@ -3241,7 +3994,7 @@ impl Connection { return None; } - let timeout = if self.draining_timer.is_some() { + let timeout = if self.is_draining() { // Draining timer takes precedence over all other timers. If it is // set it means the connection is closing so there's no point in // processing the other timers. @@ -3333,29 +4086,30 @@ impl Connection { /// Returns [`Done`] if the connection had already been closed. /// /// Note that the connection will not be closed immediately. An application - /// should continue calling [`recv()`], [`send()`] and [`timeout()`] as - /// normal, until the [`is_closed()`] method returns `true`. + /// should continue calling the [`recv()`], [`send()`], [`timeout()`] and + /// [`on_timeout()`] methods as normal, until the [`is_closed()`] method + /// returns `true`. /// /// [`Done`]: enum.Error.html#variant.Done /// [`recv()`]: struct.Connection.html#method.recv /// [`send()`]: struct.Connection.html#method.send /// [`timeout()`]: struct.Connection.html#method.timeout + /// [`on_timeout()`]: struct.Connection.html#method.on_timeout /// [`is_closed()`]: struct.Connection.html#method.is_closed pub fn close(&mut self, app: bool, err: u64, reason: &[u8]) -> Result<()> { - if self.is_closed() || self.draining_timer.is_some() { + if self.is_closed() || self.is_draining() { return Err(Error::Done); } - if self.error.is_some() || self.app_error.is_some() { + if self.local_error.is_some() { return Err(Error::Done); } - if app { - self.app_error = Some(err); - self.app_reason.extend_from_slice(reason); - } else { - self.error = Some(err); - } + self.local_error = Some(ConnectionError { + is_app: app, + error_code: err, + reason: reason.to_vec(), + }); // When no packet was successfully processed close connection immediately. if self.recv_count == 0 { @@ -3369,6 +4123,7 @@ impl Connection { /// /// This can be used for logging purposes to differentiate between multiple /// connections. + #[inline] pub fn trace_id(&self) -> &str { &self.trace_id } @@ -3376,39 +4131,114 @@ impl Connection { /// Returns the negotiated ALPN protocol. /// /// If no protocol has been negotiated, the returned value is empty. + #[inline] pub fn application_proto(&self) -> &[u8] { - self.handshake.alpn_protocol() + self.alpn.as_ref() } /// Returns the peer's leaf certificate (if any) as a DER-encoded buffer. + #[inline] pub fn peer_cert(&self) -> Option<Vec<u8>> { - self.handshake.peer_cert() + self.handshake.lock().unwrap().peer_cert() + } + + /// Returns the serialized cryptographic session for the connection. + /// + /// This can be used by a client to cache a connection's session, and resume + /// it later using the [`set_session()`] method. + /// + /// [`set_session()`]: struct.Connection.html#method.set_session + #[inline] + pub fn session(&self) -> Option<Vec<u8>> { + self.session.clone() + } + + /// Returns the source connection ID. + /// + /// Note that the value returned can change throughout the connection's + /// lifetime. + #[inline] + pub fn source_id(&self) -> ConnectionId { + ConnectionId::from_ref(self.scid.as_ref()) + } + + /// Returns the destination connection ID. + /// + /// Note that the value returned can change throughout the connection's + /// lifetime. + #[inline] + pub fn destination_id(&self) -> ConnectionId { + ConnectionId::from_ref(self.dcid.as_ref()) } /// Returns true if the connection handshake is complete. + #[inline] pub fn is_established(&self) -> bool { - self.handshake.is_completed() + self.handshake_completed } /// Returns true if the connection is resumed. + #[inline] pub fn is_resumed(&self) -> bool { - self.handshake.is_resumed() + self.handshake.lock().unwrap().is_resumed() } /// Returns true if the connection has a pending handshake that has /// progressed enough to send or receive early data. + #[inline] pub fn is_in_early_data(&self) -> bool { - self.handshake.is_in_early_data() + self.handshake.lock().unwrap().is_in_early_data() + } + + /// Returns whether there is stream or DATAGRAM data available to read. + #[inline] + pub fn is_readable(&self) -> bool { + self.streams.has_readable() || self.dgram_recv_front_len().is_some() + } + + /// Returns true if the connection is draining. + /// + /// If this returns true, the connection object cannot yet be dropped, but + /// no new application data can be sent or received. An application should + /// continue calling the [`recv()`], [`send()`], [`timeout()`], and + /// [`on_timeout()`] methods as normal, until the [`is_closed()`] method + /// returns `true`. + /// + /// [`recv()`]: struct.Connection.html#method.recv + /// [`send()`]: struct.Connection.html#method.send + /// [`timeout()`]: struct.Connection.html#method.timeout + /// [`on_timeout()`]: struct.Connection.html#method.on_timeout + /// [`is_closed()`]: struct.Connection.html#method.is_closed + #[inline] + pub fn is_draining(&self) -> bool { + self.draining_timer.is_some() } /// Returns true if the connection is closed. /// /// If this returns true, the connection object can be dropped. + #[inline] pub fn is_closed(&self) -> bool { self.closed } + /// Returns the error received from the peer, if any. + /// + /// The values contained in the tuple are symmetric with the [`close()`] + /// method. + /// + /// Note that a `Some` return value does not necessarily imply + /// [`is_closed()`] or any other connection state. + /// + /// [`close()`]: struct.Connection.html#method.close + /// [`is_closed()`]: struct.Connection.html#method.is_closed + #[inline] + pub fn peer_error(&self) -> Option<&ConnectionError> { + self.peer_error.as_ref() + } + /// Collects and returns statistics about the connection. + #[inline] pub fn stats(&self) -> Stats { Stats { recv: self.recv_count, @@ -3429,53 +4259,185 @@ impl Connection { &mut raw_params, )?; - self.handshake.set_quic_transport_params(raw_params)?; + self.handshake + .lock() + .unwrap() + .set_quic_transport_params(raw_params)?; Ok(()) } + fn parse_peer_transport_params( + &mut self, peer_params: TransportParams, + ) -> Result<()> { + if self.version >= PROTOCOL_VERSION_DRAFT28 || + self.version == PROTOCOL_VERSION_V1 + { + // Validate initial_source_connection_id. + match &peer_params.initial_source_connection_id { + Some(v) if v != &self.dcid => + return Err(Error::InvalidTransportParam), + + Some(_) => (), + + // initial_source_connection_id must be sent by + // both endpoints. + None => return Err(Error::InvalidTransportParam), + } + + // Validate original_destination_connection_id. + if let Some(odcid) = &self.odcid { + match &peer_params.original_destination_connection_id { + Some(v) if v != odcid => + return Err(Error::InvalidTransportParam), + + Some(_) => (), + + // original_destination_connection_id must be + // sent by the server. + None if !self.is_server => + return Err(Error::InvalidTransportParam), + + None => (), + } + } + + // Validate retry_source_connection_id. + if let Some(rscid) = &self.rscid { + match &peer_params.retry_source_connection_id { + Some(v) if v != rscid => + return Err(Error::InvalidTransportParam), + + Some(_) => (), + + // retry_source_connection_id must be sent by + // the server. + None => return Err(Error::InvalidTransportParam), + } + } + } else { + // Legacy validation of the original connection ID when + // stateless retry is performed, for drafts < 28. + if self.did_retry && + peer_params.original_destination_connection_id != self.odcid + { + return Err(Error::InvalidTransportParam); + } + } + + self.process_peer_transport_params(peer_params); + + self.parsed_peer_transport_params = true; + + Ok(()) + } + + fn process_peer_transport_params(&mut self, peer_params: TransportParams) { + self.max_tx_data = peer_params.initial_max_data; + + // Update send capacity. + self.tx_cap = cmp::min( + self.recovery.cwnd_available() as u64, + self.max_tx_data - self.tx_data, + ) as usize; + + self.streams + .update_peer_max_streams_bidi(peer_params.initial_max_streams_bidi); + self.streams + .update_peer_max_streams_uni(peer_params.initial_max_streams_uni); + + self.recovery.max_ack_delay = + time::Duration::from_millis(peer_params.max_ack_delay); + + self.recovery + .update_max_datagram_size(peer_params.max_udp_payload_size as usize); + + self.peer_transport_params = peer_params; + } + /// Continues the handshake. /// /// If the connection is already established, it does nothing. fn do_handshake(&mut self) -> Result<()> { - // Handshake is already complete, there's nothing to do. - if self.is_established() { + let handshake = self.handshake.lock().unwrap(); + + // Handshake is already complete, nothing more to do. + if handshake.is_completed() { return Ok(()); } - match self.handshake.do_handshake() { + match handshake.do_handshake() { Ok(_) => (), - Err(Error::Done) => return Ok(()), + Err(Error::Done) => { + // Try to parse transport parameters as soon as the first flight + // of handshake data is processed. + // + // This is potentially dangerous as the handshake hasn't been + // completed yet, though it's required to be able to send data + // in 0.5 RTT. + let raw_params = handshake.quic_transport_params(); + + if !self.parsed_peer_transport_params && !raw_params.is_empty() { + let peer_params = + TransportParams::decode(&raw_params, self.is_server)?; + + // Unlock handshake object. + drop(handshake); + + self.parse_peer_transport_params(peer_params)?; + } + + return Ok(()); + }, Err(e) => return Err(e), }; - if self.application_proto().is_empty() { - // Send no_application_proto TLS alert when no protocol - // can be negotiated. - self.error = Some(0x178); - return Err(Error::TlsFail); + self.handshake_completed = handshake.is_completed(); + + self.alpn = handshake.alpn_protocol().to_vec(); + + let cipher = handshake.cipher(); + let curve = handshake.curve(); + let sigalg = handshake.sigalg(); + let is_resumed = handshake.is_resumed(); + + let raw_params = handshake.quic_transport_params(); + + if !self.parsed_peer_transport_params && !raw_params.is_empty() { + let peer_params = + TransportParams::decode(&raw_params, self.is_server)?; + + // Unlock handshake object. + drop(handshake); + + self.parse_peer_transport_params(peer_params)?; + } + + // Once the handshake is completed there's no point in processing 0-RTT + // packets anymore, so clear the buffer now. + if self.handshake_completed { + self.undecryptable_pkts.clear(); } trace!("{} connection established: proto={:?} cipher={:?} curve={:?} sigalg={:?} resumed={} {:?}", - &self.trace_id, - std::str::from_utf8(self.application_proto()), - self.handshake.cipher(), - self.handshake.curve(), - self.handshake.sigalg(), - self.is_resumed(), - self.peer_transport_params); + &self.trace_id, std::str::from_utf8(self.application_proto()), + cipher, curve, sigalg, is_resumed, self.peer_transport_params); Ok(()) } - /// Selects the packet number space for outgoing packets. - fn write_epoch(&self) -> Result<packet::Epoch> { + /// Selects the packet type for the next outgoing packet. + fn write_pkt_type(&self) -> Result<packet::Type> { // On error send packet in the latest epoch available, but only send // 1-RTT ones when the handshake is completed. - if self.error.is_some() { - let epoch = match self.handshake.write_level() { + if self + .local_error + .as_ref() + .map_or(false, |conn_err| !conn_err.is_app) + { + let epoch = match self.handshake.lock().unwrap().write_level() { crypto::Level::Initial => packet::EPOCH_INITIAL, crypto::Level::ZeroRTT => unreachable!(), crypto::Level::Handshake => packet::EPOCH_HANDSHAKE, @@ -3485,10 +4447,10 @@ impl Connection { if epoch == packet::EPOCH_APPLICATION && !self.is_established() { // Downgrade the epoch to handshake as the handshake is not // completed yet. - return Ok(packet::EPOCH_HANDSHAKE); + return Ok(packet::Type::Handshake); } - return Ok(epoch); + return Ok(packet::Type::from_epoch(epoch)); } for epoch in packet::EPOCH_INITIAL..packet::EPOCH_COUNT { @@ -3499,33 +4461,43 @@ impl Connection { // We are ready to send data for this packet number space. if self.pkt_num_spaces[epoch].ready() { - return Ok(epoch); + return Ok(packet::Type::from_epoch(epoch)); } // There are lost frames in this packet number space. if !self.recovery.lost[epoch].is_empty() { - return Ok(epoch); + return Ok(packet::Type::from_epoch(epoch)); } // We need to send PTO probe packets. if self.recovery.loss_probes[epoch] > 0 { - return Ok(epoch); + return Ok(packet::Type::from_epoch(epoch)); } } // If there are flushable, almost full or blocked streams, use the // Application epoch. if (self.is_established() || self.is_in_early_data()) && - (self.almost_full || + ((self.is_server && !self.handshake_done_sent) || + self.almost_full || self.blocked_limit.is_some() || self.dgram_send_queue.has_pending() || + self.local_error + .as_ref() + .map_or(false, |conn_err| conn_err.is_app) || self.streams.should_update_max_streams_bidi() || self.streams.should_update_max_streams_uni() || self.streams.has_flushable() || self.streams.has_almost_full() || - self.streams.has_blocked()) + self.streams.has_blocked() || + self.streams.has_reset() || + self.streams.has_stopped()) { - return Ok(packet::EPOCH_APPLICATION); + if self.is_in_early_data() && !self.is_server { + return Ok(packet::Type::ZeroRTT); + } + + return Ok(packet::Type::Short); } Err(Error::Done) @@ -3599,7 +4571,7 @@ impl Connection { if !stream::is_bidi(stream_id) && stream::is_local(stream_id, self.is_server) { - return Err(Error::InvalidStreamState); + return Err(Error::InvalidStreamState(stream_id)); } // Get existing stream or create a new one, but if the stream @@ -3627,18 +4599,51 @@ impl Connection { } }, - frame::Frame::StopSending { stream_id, .. } => { + frame::Frame::StopSending { + stream_id, + error_code, + } => { // STOP_SENDING on a receive-only stream is a fatal error. if !stream::is_local(stream_id, self.is_server) && !stream::is_bidi(stream_id) { - return Err(Error::InvalidStreamState); + return Err(Error::InvalidStreamState(stream_id)); + } + + // Get existing stream or create a new one, but if the stream + // has already been closed and collected, ignore the frame. + // + // This can happen if e.g. an ACK frame is lost, and the peer + // retransmits another frame before it realizes that the stream + // is gone. + // + // Note that it makes it impossible to check if the frame is + // illegal, since we have no state, but since we ignore the + // frame, it should be fine. + let stream = match self.get_or_create_stream(stream_id, false) { + Ok(v) => v, + + Err(Error::Done) => return Ok(()), + + Err(e) => return Err(e), + }; + + let was_writable = stream.is_writable(); + + // Try stopping the stream. + if let Ok(final_size) = stream.send.stop(error_code) { + self.streams + .mark_reset(stream_id, true, error_code, final_size); + + if !was_writable { + self.streams.mark_writable(stream_id, true); + } } }, frame::Frame::Crypto { data } => { // Push the data to the stream so it can be re-ordered. - self.pkt_num_spaces[epoch].crypto_stream.recv.push(data)?; + self.pkt_num_spaces[epoch].crypto_stream.recv.write(data)?; // Feed crypto data to the TLS state, if there's data // available at the expected offset. @@ -3648,99 +4653,23 @@ impl Connection { let stream = &mut self.pkt_num_spaces[epoch].crypto_stream; - while let Ok((read, _)) = stream.recv.pop(&mut crypto_buf) { + while let Ok((read, _)) = stream.recv.emit(&mut crypto_buf) { let recv_buf = &crypto_buf[..read]; - self.handshake.provide_data(level, &recv_buf)?; + self.handshake + .lock() + .unwrap() + .provide_data(level, &recv_buf)?; } - self.do_handshake()?; - - // Try to parse transport parameters as soon as the first flight - // of handshake data is processed. - // - // This is potentially dangerous as the handshake hasn't been - // completed yet, though it's required to be able to send data - // in 0.5 RTT. - let raw_params = self.handshake.quic_transport_params(); - - if !self.parsed_peer_transport_params && !raw_params.is_empty() { - let peer_params = - TransportParams::decode(&raw_params, self.is_server)?; - - if self.version >= PROTOCOL_VERSION_DRAFT28 { - // Validate initial_source_connection_id. - match &peer_params.initial_source_connection_id { - Some(v) if v != &self.dcid => - return Err(Error::InvalidTransportParam), - - Some(_) => (), - - // initial_source_connection_id must be sent by - // both endpoints. - None => return Err(Error::InvalidTransportParam), - } - - // Validate original_destination_connection_id. - if let Some(odcid) = &self.odcid { - match &peer_params.original_destination_connection_id - { - Some(v) if v != odcid => - return Err(Error::InvalidTransportParam), - - Some(_) => (), - - // original_destination_connection_id must be - // sent by the server. - None if !self.is_server => - return Err(Error::InvalidTransportParam), - - None => (), - } - } - - // Validate retry_source_connection_id. - if let Some(rscid) = &self.rscid { - match &peer_params.retry_source_connection_id { - Some(v) if v != rscid => - return Err(Error::InvalidTransportParam), - - Some(_) => (), - - // retry_source_connection_id must be sent by - // the server. - None => return Err(Error::InvalidTransportParam), - } - } - } else { - // Legacy validation of the original connection ID when - // stateless retry is performed, for drafts < 28. - if self.did_retry && - peer_params.original_destination_connection_id != - self.odcid - { - return Err(Error::InvalidTransportParam); - } - } - - // Update flow control limits. - self.max_tx_data = peer_params.initial_max_data; - - self.streams.update_peer_max_streams_bidi( - peer_params.initial_max_streams_bidi, - ); - self.streams.update_peer_max_streams_uni( - peer_params.initial_max_streams_uni, - ); - - self.recovery.max_ack_delay = - time::Duration::from_millis(peer_params.max_ack_delay); - - self.peer_transport_params = peer_params; - - self.parsed_peer_transport_params = true; + if self.is_established() { + self.handshake.lock().unwrap().process_post_handshake()?; + } else { + self.do_handshake()?; } }, + frame::Frame::CryptoHeader { .. } => unreachable!(), + // TODO: implement stateless retry frame::Frame::NewToken { .. } => (), @@ -3749,7 +4678,7 @@ impl Connection { if !stream::is_bidi(stream_id) && stream::is_local(stream_id, self.is_server) { - return Err(Error::InvalidStreamState); + return Err(Error::InvalidStreamState(stream_id)); } let max_rx_data_left = self.max_rx_data - self.rx_data; @@ -3780,7 +4709,7 @@ impl Connection { return Err(Error::FlowControl); } - stream.recv.push(data)?; + stream.recv.write(data)?; if stream.is_readable() { self.streams.mark_readable(stream_id, true); @@ -3789,11 +4718,20 @@ impl Connection { self.rx_data += max_off_delta; }, + frame::Frame::StreamHeader { .. } => unreachable!(), + frame::Frame::MaxData { max } => { self.max_tx_data = cmp::max(self.max_tx_data, max); }, frame::Frame::MaxStreamData { stream_id, max } => { + // Peer can't receive on its own unidirectional streams. + if !stream::is_bidi(stream_id) && + !stream::is_local(stream_id, self.is_server) + { + return Err(Error::InvalidStreamState(stream_id)); + } + // Get existing stream or create a new one, but if the stream // has already been closed and collected, ignore the frame. // @@ -3873,11 +4811,23 @@ impl Connection { frame::Frame::PathResponse { .. } => (), - frame::Frame::ConnectionClose { .. } => { + frame::Frame::ConnectionClose { + error_code, reason, .. + } => { + self.peer_error = Some(ConnectionError { + is_app: false, + error_code, + reason, + }); self.draining_timer = Some(now + (self.recovery.pto() * 3)); }, - frame::Frame::ApplicationClose { .. } => { + frame::Frame::ApplicationClose { error_code, reason } => { + self.peer_error = Some(ConnectionError { + is_app: true, + error_code, + reason, + }); self.draining_timer = Some(now + (self.recovery.pto() * 3)); }, @@ -3899,7 +4849,7 @@ impl Connection { // quiche always advertises support for 64K sized DATAGRAM // frames, as recommended by the standard, so we don't need a // size check. - if self.dgram_enabled() { + if !self.dgram_enabled() { return Err(Error::InvalidState); } @@ -3975,12 +4925,6 @@ impl Connection { Some(idle_timeout) } - /// Returns the connection's overall send capacity. - fn send_capacity(&self) -> usize { - let cap = self.max_tx_data - self.tx_data; - cmp::min(cap, self.recovery.cwnd_available() as u64) as usize - } - /// Returns the connection's handshake status for use in loss recovery. fn handshake_status(&self) -> recovery::HandshakeStatus { recovery::HandshakeStatus { @@ -4031,15 +4975,15 @@ fn drop_pkt_on_err( /// Statistics about the connection. /// -/// A connections's statistics can be collected using the [`stats()`] method. +/// A connection's statistics can be collected using the [`stats()`] method. /// /// [`stats()`]: struct.Connection.html#method.stats #[derive(Clone)] pub struct Stats { - /// The number of QUIC packets received on this connection. + /// The number of QUIC packets received. pub recv: usize, - /// The number of QUIC packets sent on this connection. + /// The number of QUIC packets sent. pub sent: usize, /// The number of QUIC packets that were lost. @@ -4051,28 +4995,24 @@ pub struct Stats { /// The size of the connection's congestion window in bytes. pub cwnd: usize, - /// The estimated data delivery rate in bytes/s. + /// The most recent data delivery rate estimate in bytes/s. pub delivery_rate: u64, } impl std::fmt::Debug for Stats { + #[inline] fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { write!( f, - "recv={} sent={} lost={} rtt={:?} cwnd={} delivery_rate={}", - self.recv, - self.sent, - self.lost, - self.rtt, - self.cwnd, - self.delivery_rate + "recv={} sent={} lost={} rtt={:?} cwnd={}", + self.recv, self.sent, self.lost, self.rtt, self.cwnd, ) } } #[derive(Clone, Debug, PartialEq)] struct TransportParams { - pub original_destination_connection_id: Option<Vec<u8>>, + pub original_destination_connection_id: Option<ConnectionId<'static>>, pub max_idle_timeout: u64, pub stateless_reset_token: Option<Vec<u8>>, pub max_udp_payload_size: u64, @@ -4087,8 +5027,8 @@ struct TransportParams { pub disable_active_migration: bool, // pub preferred_address: ..., pub active_conn_id_limit: u64, - pub initial_source_connection_id: Option<Vec<u8>>, - pub retry_source_connection_id: Option<Vec<u8>>, + pub initial_source_connection_id: Option<ConnectionId<'static>>, + pub retry_source_connection_id: Option<ConnectionId<'static>>, pub max_datagram_frame_size: Option<u64>, } @@ -4135,7 +5075,8 @@ impl TransportParams { return Err(Error::InvalidTransportParam); } - tp.original_destination_connection_id = Some(val.to_vec()); + tp.original_destination_connection_id = + Some(val.to_vec().into()); }, 0x0001 => { @@ -4237,7 +5178,7 @@ impl TransportParams { }, 0x000f => { - tp.initial_source_connection_id = Some(val.to_vec()); + tp.initial_source_connection_id = Some(val.to_vec().into()); }, 0x00010 => { @@ -4245,7 +5186,7 @@ impl TransportParams { return Err(Error::InvalidTransportParam); } - tp.retry_source_connection_id = Some(val.to_vec()); + tp.retry_source_connection_id = Some(val.to_vec().into()); }, 0x0020 => { @@ -4480,6 +5421,7 @@ pub mod testing { config.set_initial_max_streams_uni(3); config.set_max_idle_timeout(180_000); config.verify_peer(false); + config.set_ack_delay_exponent(5); Pipe::with_config(&mut config) } @@ -4487,22 +5429,35 @@ pub mod testing { pub fn with_config(config: &mut Config) -> Result<Pipe> { let mut client_scid = [0; 16]; rand::rand_bytes(&mut client_scid[..]); + let client_scid = ConnectionId::from_ref(&client_scid); + let client_addr = "127.0.0.1:1234".parse().unwrap(); let mut server_scid = [0; 16]; rand::rand_bytes(&mut server_scid[..]); + let server_scid = ConnectionId::from_ref(&server_scid); + let server_addr = "127.0.0.1:4321".parse().unwrap(); Ok(Pipe { - client: connect(Some("quic.tech"), &client_scid, config)?, - server: accept(&server_scid, None, config)?, + client: connect( + Some("quic.tech"), + &client_scid, + client_addr, + config, + )?, + server: accept(&server_scid, None, server_addr, config)?, }) } pub fn with_client_config(client_config: &mut Config) -> Result<Pipe> { let mut client_scid = [0; 16]; rand::rand_bytes(&mut client_scid[..]); + let client_scid = ConnectionId::from_ref(&client_scid); + let client_addr = "127.0.0.1:1234".parse().unwrap(); let mut server_scid = [0; 16]; rand::rand_bytes(&mut server_scid[..]); + let server_scid = ConnectionId::from_ref(&server_scid); + let server_addr = "127.0.0.1:4321".parse().unwrap(); let mut config = Config::new(crate::PROTOCOL_VERSION)?; config.load_cert_chain_from_pem_file("examples/cert.crt")?; @@ -4515,17 +5470,26 @@ pub mod testing { config.set_initial_max_streams_uni(3); Ok(Pipe { - client: connect(Some("quic.tech"), &client_scid, client_config)?, - server: accept(&server_scid, None, &mut config)?, + client: connect( + Some("quic.tech"), + &client_scid, + client_addr, + client_config, + )?, + server: accept(&server_scid, None, server_addr, &mut config)?, }) } pub fn with_server_config(server_config: &mut Config) -> Result<Pipe> { let mut client_scid = [0; 16]; rand::rand_bytes(&mut client_scid[..]); + let client_scid = ConnectionId::from_ref(&client_scid); + let client_addr = "127.0.0.1:1234".parse().unwrap(); let mut server_scid = [0; 16]; rand::rand_bytes(&mut server_scid[..]); + let server_scid = ConnectionId::from_ref(&server_scid); + let server_addr = "127.0.0.1:4321".parse().unwrap(); let mut config = Config::new(crate::PROTOCOL_VERSION)?; config.set_application_protos(b"\x06proto1\x06proto2")?; @@ -4536,83 +5500,67 @@ pub mod testing { config.set_initial_max_streams_uni(3); Ok(Pipe { - client: connect(Some("quic.tech"), &client_scid, &mut config)?, - server: accept(&server_scid, None, server_config)?, + client: connect( + Some("quic.tech"), + &client_scid, + client_addr, + &mut config, + )?, + server: accept(&server_scid, None, server_addr, server_config)?, }) } - pub fn handshake(&mut self, buf: &mut [u8]) -> Result<()> { - let mut len = self.client.send(buf)?; + pub fn handshake(&mut self) -> Result<()> { + while !self.client.is_established() || !self.server.is_established() { + let flight = emit_flight(&mut self.client)?; + process_flight(&mut self.server, flight)?; - while !self.client.is_established() && !self.server.is_established() { - len = recv_send(&mut self.server, buf, len)?; - len = recv_send(&mut self.client, buf, len)?; + let flight = emit_flight(&mut self.server)?; + process_flight(&mut self.client, flight)?; } - recv_send(&mut self.server, buf, len)?; - Ok(()) } - pub fn flush_client(&mut self, buf: &mut [u8]) -> Result<()> { - loop { - let len = match self.client.send(buf) { - Ok(v) => v, - - Err(Error::Done) => break, - - Err(e) => return Err(e), - }; - - match self.server.recv(&mut buf[..len]) { - Ok(_) => (), - - Err(Error::Done) => (), - - Err(e) => return Err(e), - } - } - - Ok(()) - } + pub fn advance(&mut self) -> Result<()> { + let mut client_done = false; + let mut server_done = false; - pub fn flush_server(&mut self, buf: &mut [u8]) -> Result<()> { - loop { - let len = match self.server.send(buf) { - Ok(v) => v, + while !client_done || !server_done { + match emit_flight(&mut self.client) { + Ok(flight) => process_flight(&mut self.server, flight)?, - Err(Error::Done) => break, + Err(Error::Done) => client_done = true, Err(e) => return Err(e), }; - match self.client.recv(&mut buf[..len]) { - Ok(_) => (), + match emit_flight(&mut self.server) { + Ok(flight) => process_flight(&mut self.client, flight)?, - Err(Error::Done) => (), + Err(Error::Done) => server_done = true, Err(e) => return Err(e), - } + }; } Ok(()) } - pub fn advance(&mut self, buf: &mut [u8]) -> Result<()> { - let mut client_done = false; - let mut server_done = false; - - let mut len = 0; + pub fn client_recv(&mut self, buf: &mut [u8]) -> Result<usize> { + let info = RecvInfo { + from: self.client.peer_addr, + }; - while !client_done || !server_done { - len = recv_send(&mut self.client, buf, len)?; - client_done = len == 0; + self.client.recv(buf, info) + } - len = recv_send(&mut self.server, buf, len)?; - server_done = len == 0; - } + pub fn server_recv(&mut self, buf: &mut [u8]) -> Result<usize> { + let info = RecvInfo { + from: self.server.peer_addr, + }; - Ok(()) + self.server.recv(buf, info) } pub fn send_pkt_to_server( @@ -4627,33 +5575,61 @@ pub mod testing { pub fn recv_send( conn: &mut Connection, buf: &mut [u8], len: usize, ) -> Result<usize> { - let mut left = len; + let info = RecvInfo { + from: conn.peer_addr, + }; - while left > 0 { - match conn.recv(&mut buf[len - left..len]) { - Ok(read) => left -= read, + conn.recv(&mut buf[..len], info)?; - Err(Error::Done) => break, + let mut off = 0; - Err(e) => return Err(e), - } + match conn.send(&mut buf[off..]) { + Ok((write, _)) => off += write, + + Err(Error::Done) => (), + + Err(e) => return Err(e), } - assert_eq!(left, 0); + Ok(off) + } - let mut off = 0; + pub fn process_flight( + conn: &mut Connection, flight: Vec<Vec<u8>>, + ) -> Result<()> { + for mut pkt in flight { + let info = RecvInfo { + from: conn.peer_addr, + }; + + conn.recv(&mut pkt, info)?; + } - while off < buf.len() { - match conn.send(&mut buf[off..]) { - Ok(write) => off += write, + Ok(()) + } + + pub fn emit_flight(conn: &mut Connection) -> Result<Vec<Vec<u8>>> { + let mut flight = Vec::new(); + + loop { + let mut out = vec![0u8; 65535]; + + match conn.send(&mut out) { + Ok((written, _)) => out.truncate(written), Err(Error::Done) => break, Err(e) => return Err(e), - } + }; + + flight.push(out); } - Ok(off) + if flight.is_empty() { + return Err(Error::Done); + } + + Ok(flight) } pub fn encode_pkt( @@ -4667,13 +5643,13 @@ pub mod testing { let space = &mut conn.pkt_num_spaces[epoch]; let pn = space.next_pkt_num; - let pn_len = packet::pkt_num_len(pn)?; + let pn_len = 4; let hdr = Header { ty: pkt_type, version: conn.version, - dcid: conn.dcid.clone(), - scid: conn.scid.clone(), + dcid: ConnectionId::from_ref(&conn.dcid), + scid: ConnectionId::from_ref(&conn.scid), pkt_num: 0, pkt_num_len: pn_len, token: conn.token.clone(), @@ -4691,7 +5667,9 @@ pub mod testing { b.put_varint(len as u64)?; } - packet::encode_pkt_num(pn, &mut b)?; + // Always encode packet number in 4 bytes, to allow encoding packets + // with empty payloads. + b.put_u32(pn as u32)?; let payload_offset = b.off(); @@ -4776,8 +5754,8 @@ mod tests { max_ack_delay: 2_u64.pow(14) - 1, disable_active_migration: true, active_conn_id_limit: 8, - initial_source_connection_id: Some(b"woot woot".to_vec()), - retry_source_connection_id: Some(b"retry".to_vec()), + initial_source_connection_id: Some(b"woot woot".to_vec().into()), + retry_source_connection_id: Some(b"retry".to_vec().into()), max_datagram_frame_size: Some(32), }; @@ -4806,7 +5784,7 @@ mod tests { max_ack_delay: 2_u64.pow(14) - 1, disable_active_migration: true, active_conn_id_limit: 8, - initial_source_connection_id: Some(b"woot woot".to_vec()), + initial_source_connection_id: Some(b"woot woot".to_vec().into()), retry_source_connection_id: None, max_datagram_frame_size: Some(32), }; @@ -4824,14 +5802,28 @@ mod tests { #[test] #[ignore = "Android: failure reason unkown."] fn unknown_version() { - let mut buf = [0; 65535]; - let mut config = Config::new(0xbabababa).unwrap(); + config + .set_application_protos(b"\x06proto1\x06proto2") + .unwrap(); config.verify_peer(false); let mut pipe = testing::Pipe::with_client_config(&mut config).unwrap(); + assert_eq!(pipe.handshake(), Err(Error::UnknownVersion)); + } - assert_eq!(pipe.handshake(&mut buf), Err(Error::UnknownVersion)); + #[test] + fn config_version_reserved() { + Config::new(0xbabababa).unwrap(); + Config::new(0x1a2a3a4a).unwrap(); + } + + #[test] + fn config_version_invalid() { + assert_eq!( + Config::new(0xb1bababa).err().unwrap(), + Error::UnknownVersion + ); } #[test] @@ -4846,14 +5838,14 @@ mod tests { let mut pipe = testing::Pipe::with_client_config(&mut config).unwrap(); - let mut len = pipe.client.send(&mut buf).unwrap(); + let (mut len, _) = pipe.client.send(&mut buf).unwrap(); let hdr = packet::Header::from_slice(&mut buf[..len], 0).unwrap(); len = crate::negotiate_version(&hdr.scid, &hdr.dcid, &mut buf).unwrap(); - assert_eq!(pipe.client.recv(&mut buf[..len]), Ok(len)); + assert_eq!(pipe.client_recv(&mut buf[..len]), Ok(len)); - assert_eq!(pipe.handshake(&mut buf), Ok(())); + assert_eq!(pipe.handshake(), Ok(())); assert_eq!(pipe.client.version, PROTOCOL_VERSION); assert_eq!(pipe.server.version, PROTOCOL_VERSION); @@ -4861,8 +5853,6 @@ mod tests { #[test] fn verify_custom_root() { - let mut buf = [0; 65535]; - let mut config = Config::new(PROTOCOL_VERSION).unwrap(); config.verify_peer(true); config @@ -4873,7 +5863,7 @@ mod tests { .unwrap(); let mut pipe = testing::Pipe::with_client_config(&mut config).unwrap(); - assert_eq!(pipe.handshake(&mut buf), Ok(())); + assert_eq!(pipe.handshake(), Ok(())); } #[test] @@ -4889,11 +5879,11 @@ mod tests { assert_eq!(pipe.client.encode_transport_params(), Ok(())); // Client sends initial flight. - let len = pipe.client.send(&mut buf).unwrap(); + let (len, _) = pipe.client.send(&mut buf).unwrap(); // Server rejects transport parameters. assert_eq!( - testing::recv_send(&mut pipe.server, &mut buf, len), + pipe.server_recv(&mut buf[..len]), Err(Error::InvalidTransportParam) ); } @@ -4907,26 +5897,23 @@ mod tests { // Scramble initial_source_connection_id. pipe.client .local_transport_params - .initial_source_connection_id = Some(b"bogus value".to_vec()); + .initial_source_connection_id = Some(b"bogus value".to_vec().into()); assert_eq!(pipe.client.encode_transport_params(), Ok(())); // Client sends initial flight. - let len = pipe.client.send(&mut buf).unwrap(); + let (len, _) = pipe.client.send(&mut buf).unwrap(); // Server rejects transport parameters. assert_eq!( - testing::recv_send(&mut pipe.server, &mut buf, len), + pipe.server_recv(&mut buf[..len]), Err(Error::InvalidTransportParam) ); } #[test] fn handshake() { - let mut buf = [0; 65535]; - let mut pipe = testing::Pipe::default().unwrap(); - - assert_eq!(pipe.handshake(&mut buf), Ok(())); + assert_eq!(pipe.handshake(), Ok(())); assert_eq!( pipe.client.application_proto(), @@ -4935,16 +5922,32 @@ mod tests { } #[test] - fn handshake_confirmation() { - let mut buf = [0; 65535]; + fn handshake_done() { + let mut pipe = testing::Pipe::default().unwrap(); + // Disable session tickets on the server (SSL_OP_NO_TICKET) to avoid + // triggering 1-RTT packet send with a CRYPTO frame. + pipe.server + .handshake + .lock() + .unwrap() + .set_options(0x0000_4000); + + assert_eq!(pipe.handshake(), Ok(())); + + assert!(pipe.server.handshake_done_sent); + } + + #[test] + fn handshake_confirmation() { let mut pipe = testing::Pipe::default().unwrap(); // Client sends initial flight. - let mut len = pipe.client.send(&mut buf).unwrap(); + let flight = testing::emit_flight(&mut pipe.client).unwrap(); + testing::process_flight(&mut pipe.server, flight).unwrap(); // Server sends initial flight. - len = testing::recv_send(&mut pipe.server, &mut buf, len).unwrap(); + let flight = testing::emit_flight(&mut pipe.server).unwrap(); assert!(!pipe.client.is_established()); assert!(!pipe.client.handshake_confirmed); @@ -4952,8 +5955,10 @@ mod tests { assert!(!pipe.server.is_established()); assert!(!pipe.server.handshake_confirmed); + testing::process_flight(&mut pipe.client, flight).unwrap(); + // Client sends Handshake packet and completes handshake. - len = testing::recv_send(&mut pipe.client, &mut buf, len).unwrap(); + let flight = testing::emit_flight(&mut pipe.client).unwrap(); assert!(pipe.client.is_established()); assert!(!pipe.client.handshake_confirmed); @@ -4961,8 +5966,10 @@ mod tests { assert!(!pipe.server.is_established()); assert!(!pipe.server.handshake_confirmed); + testing::process_flight(&mut pipe.server, flight).unwrap(); + // Server completes handshake and sends HANDSHAKE_DONE. - len = testing::recv_send(&mut pipe.server, &mut buf, len).unwrap(); + let flight = testing::emit_flight(&mut pipe.server).unwrap(); assert!(pipe.client.is_established()); assert!(!pipe.client.handshake_confirmed); @@ -4970,8 +5977,10 @@ mod tests { assert!(pipe.server.is_established()); assert!(!pipe.server.handshake_confirmed); + testing::process_flight(&mut pipe.client, flight).unwrap(); + // Client acks 1-RTT packet, and confirms handshake. - len = testing::recv_send(&mut pipe.client, &mut buf, len).unwrap(); + let flight = testing::emit_flight(&mut pipe.client).unwrap(); assert!(pipe.client.is_established()); assert!(pipe.client.handshake_confirmed); @@ -4979,9 +5988,9 @@ mod tests { assert!(pipe.server.is_established()); assert!(!pipe.server.handshake_confirmed); - // Server handshake is confirmed. - testing::recv_send(&mut pipe.server, &mut buf, len).unwrap(); + testing::process_flight(&mut pipe.server, flight).unwrap(); + // Server handshake is confirmed. assert!(pipe.client.is_established()); assert!(pipe.client.handshake_confirmed); @@ -4990,6 +5999,68 @@ mod tests { } #[test] + fn handshake_resumption() { + const SESSION_TICKET_KEY: [u8; 48] = [0xa; 48]; + + let mut config = Config::new(crate::PROTOCOL_VERSION).unwrap(); + config + .load_cert_chain_from_pem_file("examples/cert.crt") + .unwrap(); + config + .load_priv_key_from_pem_file("examples/cert.key") + .unwrap(); + config + .set_application_protos(b"\x06proto1\x06proto2") + .unwrap(); + config.set_initial_max_data(30); + config.set_initial_max_stream_data_bidi_local(15); + config.set_initial_max_stream_data_bidi_remote(15); + config.set_initial_max_streams_bidi(3); + config.set_ticket_key(&SESSION_TICKET_KEY).unwrap(); + + // Perform initial handshake. + let mut pipe = testing::Pipe::with_server_config(&mut config).unwrap(); + assert_eq!(pipe.handshake(), Ok(())); + + assert_eq!(pipe.client.is_established(), true); + assert_eq!(pipe.server.is_established(), true); + + assert_eq!(pipe.client.is_resumed(), false); + assert_eq!(pipe.server.is_resumed(), false); + + // Extract session, + let session = pipe.client.session().unwrap(); + + // Configure session on new connection and perform handshake. + let mut config = Config::new(crate::PROTOCOL_VERSION).unwrap(); + config + .load_cert_chain_from_pem_file("examples/cert.crt") + .unwrap(); + config + .load_priv_key_from_pem_file("examples/cert.key") + .unwrap(); + config + .set_application_protos(b"\x06proto1\x06proto2") + .unwrap(); + config.set_initial_max_data(30); + config.set_initial_max_stream_data_bidi_local(15); + config.set_initial_max_stream_data_bidi_remote(15); + config.set_initial_max_streams_bidi(3); + config.set_ticket_key(&SESSION_TICKET_KEY).unwrap(); + + let mut pipe = testing::Pipe::with_server_config(&mut config).unwrap(); + + assert_eq!(pipe.client.set_session(&session), Ok(())); + assert_eq!(pipe.handshake(), Ok(())); + + assert_eq!(pipe.client.is_established(), true); + assert_eq!(pipe.server.is_established(), true); + + assert_eq!(pipe.client.is_resumed(), true); + assert_eq!(pipe.server.is_resumed(), true); + } + + #[test] fn handshake_alpn_mismatch() { let mut buf = [0; 65535]; @@ -5000,17 +6071,228 @@ mod tests { config.verify_peer(false); let mut pipe = testing::Pipe::with_client_config(&mut config).unwrap(); - - assert_eq!(pipe.handshake(&mut buf), Err(Error::TlsFail)); + assert_eq!(pipe.handshake(), Err(Error::TlsFail)); assert_eq!(pipe.client.application_proto(), b""); assert_eq!(pipe.server.application_proto(), b""); + + // Server should only send one packet in response to ALPN mismatch. + let (len, _) = pipe.server.send(&mut buf).unwrap(); + assert_eq!(len, 1200); + + assert_eq!(pipe.server.send(&mut buf), Err(Error::Done)); + assert_eq!(pipe.server.sent_count, 1); } #[test] - fn limit_handshake_data() { + fn handshake_0rtt() { + let mut buf = [0; 65535]; + + let mut config = Config::new(crate::PROTOCOL_VERSION).unwrap(); + config + .load_cert_chain_from_pem_file("examples/cert.crt") + .unwrap(); + config + .load_priv_key_from_pem_file("examples/cert.key") + .unwrap(); + config + .set_application_protos(b"\x06proto1\x06proto2") + .unwrap(); + config.set_initial_max_data(30); + config.set_initial_max_stream_data_bidi_local(15); + config.set_initial_max_stream_data_bidi_remote(15); + config.set_initial_max_streams_bidi(3); + config.enable_early_data(); + config.verify_peer(false); + + // Perform initial handshake. + let mut pipe = testing::Pipe::with_config(&mut config).unwrap(); + assert_eq!(pipe.handshake(), Ok(())); + + // Extract session, + let session = pipe.client.session().unwrap(); + + // Configure session on new connection. + let mut pipe = testing::Pipe::with_config(&mut config).unwrap(); + assert_eq!(pipe.client.set_session(&session), Ok(())); + + // Client sends initial flight. + let (len, _) = pipe.client.send(&mut buf).unwrap(); + assert_eq!(pipe.server_recv(&mut buf[..len]), Ok(len)); + + // Client sends 0-RTT packet. + let pkt_type = packet::Type::ZeroRTT; + + let frames = [frame::Frame::Stream { + stream_id: 4, + data: stream::RangeBuf::from(b"aaaaa", 0, true), + }]; + + assert_eq!( + pipe.send_pkt_to_server(pkt_type, &frames, &mut buf), + Ok(1200) + ); + + assert_eq!(pipe.server.undecryptable_pkts.len(), 0); + + // 0-RTT stream data is readable. + let mut r = pipe.server.readable(); + assert_eq!(r.next(), Some(4)); + assert_eq!(r.next(), None); + + let mut b = [0; 15]; + assert_eq!(pipe.server.stream_recv(4, &mut b), Ok((5, true))); + assert_eq!(&b[..5], b"aaaaa"); + } + + #[test] + fn handshake_0rtt_reordered() { + let mut buf = [0; 65535]; + + let mut config = Config::new(crate::PROTOCOL_VERSION).unwrap(); + config + .load_cert_chain_from_pem_file("examples/cert.crt") + .unwrap(); + config + .load_priv_key_from_pem_file("examples/cert.key") + .unwrap(); + config + .set_application_protos(b"\x06proto1\x06proto2") + .unwrap(); + config.set_initial_max_data(30); + config.set_initial_max_stream_data_bidi_local(15); + config.set_initial_max_stream_data_bidi_remote(15); + config.set_initial_max_streams_bidi(3); + config.enable_early_data(); + config.verify_peer(false); + + // Perform initial handshake. + let mut pipe = testing::Pipe::with_config(&mut config).unwrap(); + assert_eq!(pipe.handshake(), Ok(())); + + // Extract session, + let session = pipe.client.session().unwrap(); + + // Configure session on new connection. + let mut pipe = testing::Pipe::with_config(&mut config).unwrap(); + assert_eq!(pipe.client.set_session(&session), Ok(())); + + // Client sends initial flight. + let (len, _) = pipe.client.send(&mut buf).unwrap(); + let mut initial = (&buf[..len]).to_vec(); + + // Client sends 0-RTT packet. + let pkt_type = packet::Type::ZeroRTT; + + let frames = [frame::Frame::Stream { + stream_id: 4, + data: stream::RangeBuf::from(b"aaaaa", 0, true), + }]; + + let len = + testing::encode_pkt(&mut pipe.client, pkt_type, &frames, &mut buf) + .unwrap(); + let mut zrtt = (&buf[..len]).to_vec(); + + // 0-RTT packet is received before the Initial one. + assert_eq!(pipe.server_recv(&mut zrtt), Ok(zrtt.len())); + + assert_eq!(pipe.server.undecryptable_pkts.len(), 1); + assert_eq!(pipe.server.undecryptable_pkts[0].0.len(), zrtt.len()); + + let mut r = pipe.server.readable(); + assert_eq!(r.next(), None); + + // Initial packet is also received. + assert_eq!(pipe.server_recv(&mut initial), Ok(initial.len())); + + // 0-RTT stream data is readable. + let mut r = pipe.server.readable(); + assert_eq!(r.next(), Some(4)); + assert_eq!(r.next(), None); + + let mut b = [0; 15]; + assert_eq!(pipe.server.stream_recv(4, &mut b), Ok((5, true))); + assert_eq!(&b[..5], b"aaaaa"); + } + + #[test] + fn handshake_0rtt_truncated() { let mut buf = [0; 65535]; + let mut config = Config::new(crate::PROTOCOL_VERSION).unwrap(); + config + .load_cert_chain_from_pem_file("examples/cert.crt") + .unwrap(); + config + .load_priv_key_from_pem_file("examples/cert.key") + .unwrap(); + config + .set_application_protos(b"\x06proto1\x06proto2") + .unwrap(); + config.set_initial_max_data(30); + config.set_initial_max_stream_data_bidi_local(15); + config.set_initial_max_stream_data_bidi_remote(15); + config.set_initial_max_streams_bidi(3); + config.enable_early_data(); + config.verify_peer(false); + + // Perform initial handshake. + let mut pipe = testing::Pipe::with_config(&mut config).unwrap(); + assert_eq!(pipe.handshake(), Ok(())); + + // Extract session, + let session = pipe.client.session().unwrap(); + + // Configure session on new connection. + let mut pipe = testing::Pipe::with_config(&mut config).unwrap(); + assert_eq!(pipe.client.set_session(&session), Ok(())); + + // Client sends initial flight. + pipe.client.send(&mut buf).unwrap(); + + // Client sends 0-RTT packet. + let pkt_type = packet::Type::ZeroRTT; + + let frames = [frame::Frame::Stream { + stream_id: 4, + data: stream::RangeBuf::from(b"aaaaa", 0, true), + }]; + + let len = + testing::encode_pkt(&mut pipe.client, pkt_type, &frames, &mut buf) + .unwrap(); + + // Simulate a truncated packet by sending one byte less. + let mut zrtt = (&buf[..len - 1]).to_vec(); + + // 0-RTT packet is received before the Initial one. + assert_eq!(pipe.server_recv(&mut zrtt), Err(Error::InvalidPacket)); + + assert_eq!(pipe.server.undecryptable_pkts.len(), 0); + + assert!(pipe.server.is_closed()); + } + + #[test] + /// Tests that a pre-v1 client can connect to a v1-enabled server, by making + /// the server downgrade to the pre-v1 version. + fn handshake_downgrade_v1() { + let mut config = Config::new(PROTOCOL_VERSION_DRAFT29).unwrap(); + config + .set_application_protos(b"\x06proto1\x06proto2") + .unwrap(); + config.verify_peer(false); + + let mut pipe = testing::Pipe::with_client_config(&mut config).unwrap(); + assert_eq!(pipe.handshake(), Ok(())); + + assert_eq!(pipe.client.version, PROTOCOL_VERSION_DRAFT29); + assert_eq!(pipe.server.version, PROTOCOL_VERSION_DRAFT29); + } + + #[test] + fn limit_handshake_data() { let mut config = Config::new(PROTOCOL_VERSION).unwrap(); config .load_cert_chain_from_pem_file("examples/cert-big.crt") @@ -5024,24 +6306,23 @@ mod tests { let mut pipe = testing::Pipe::with_server_config(&mut config).unwrap(); - let client_sent = pipe.client.send(&mut buf).unwrap(); - let server_sent = - testing::recv_send(&mut pipe.server, &mut buf, client_sent).unwrap(); + let flight = testing::emit_flight(&mut pipe.client).unwrap(); + let client_sent = flight.iter().fold(0, |out, p| out + p.len()); + testing::process_flight(&mut pipe.server, flight).unwrap(); + + let flight = testing::emit_flight(&mut pipe.server).unwrap(); + let server_sent = flight.iter().fold(0, |out, p| out + p.len()); - assert_eq!(server_sent, (client_sent - 1) * MAX_AMPLIFICATION_FACTOR); + assert_eq!(server_sent, client_sent * MAX_AMPLIFICATION_FACTOR); } #[test] fn stream() { - let mut buf = [0; 65535]; - let mut pipe = testing::Pipe::default().unwrap(); - - assert_eq!(pipe.handshake(&mut buf), Ok(())); + assert_eq!(pipe.handshake(), Ok(())); assert_eq!(pipe.client.stream_send(4, b"hello, world", true), Ok(12)); - - assert_eq!(pipe.advance(&mut buf), Ok(())); + assert_eq!(pipe.advance(), Ok(())); assert!(!pipe.server.stream_finished(4)); @@ -5057,7 +6338,7 @@ mod tests { } #[test] - fn stream_send_on_32bit_arch() { + fn zero_rtt() { let mut buf = [0; 65535]; let mut config = Config::new(crate::PROTOCOL_VERSION).unwrap(); @@ -5070,6 +6351,64 @@ mod tests { config .set_application_protos(b"\x06proto1\x06proto2") .unwrap(); + config.set_initial_max_data(30); + config.set_initial_max_stream_data_bidi_local(15); + config.set_initial_max_stream_data_bidi_remote(15); + config.set_initial_max_streams_bidi(3); + config.enable_early_data(); + config.verify_peer(false); + + // Perform initial handshake. + let mut pipe = testing::Pipe::with_config(&mut config).unwrap(); + assert_eq!(pipe.handshake(), Ok(())); + + // Extract session, + let session = pipe.client.session().unwrap(); + + // Configure session on new connection. + let mut pipe = testing::Pipe::with_config(&mut config).unwrap(); + assert_eq!(pipe.client.set_session(&session), Ok(())); + + // Client sends initial flight. + let (len, _) = pipe.client.send(&mut buf).unwrap(); + let mut initial = (&buf[..len]).to_vec(); + + assert_eq!(pipe.client.is_in_early_data(), true); + + // Client sends 0-RTT data. + assert_eq!(pipe.client.stream_send(4, b"hello, world", true), Ok(12)); + + let (len, _) = pipe.client.send(&mut buf).unwrap(); + let mut zrtt = (&buf[..len]).to_vec(); + + // Server receives packets. + assert_eq!(pipe.server_recv(&mut initial), Ok(initial.len())); + assert_eq!(pipe.server.is_in_early_data(), true); + + assert_eq!(pipe.server_recv(&mut zrtt), Ok(zrtt.len())); + + // 0-RTT stream data is readable. + let mut r = pipe.server.readable(); + assert_eq!(r.next(), Some(4)); + assert_eq!(r.next(), None); + + let mut b = [0; 15]; + assert_eq!(pipe.server.stream_recv(4, &mut b), Ok((12, true))); + assert_eq!(&b[..12], b"hello, world"); + } + + #[test] + fn stream_send_on_32bit_arch() { + let mut config = Config::new(crate::PROTOCOL_VERSION).unwrap(); + config + .load_cert_chain_from_pem_file("examples/cert.crt") + .unwrap(); + config + .load_priv_key_from_pem_file("examples/cert.key") + .unwrap(); + config + .set_application_protos(b"\x06proto1\x06proto2") + .unwrap(); config.set_initial_max_data(2_u64.pow(32) + 5); config.set_initial_max_stream_data_bidi_local(15); config.set_initial_max_stream_data_bidi_remote(15); @@ -5079,14 +6418,13 @@ mod tests { config.verify_peer(false); let mut pipe = testing::Pipe::with_config(&mut config).unwrap(); - - assert_eq!(pipe.handshake(&mut buf), Ok(())); + assert_eq!(pipe.handshake(), Ok(())); // In 32bit arch, send_capacity() should be min(2^32+5, cwnd), // not min(5, cwnd) assert_eq!(pipe.client.stream_send(4, b"hello, world", true), Ok(12)); - assert_eq!(pipe.advance(&mut buf), Ok(())); + assert_eq!(pipe.advance(), Ok(())); assert!(!pipe.server.stream_finished(4)); } @@ -5096,8 +6434,7 @@ mod tests { let mut buf = [0; 65535]; let mut pipe = testing::Pipe::default().unwrap(); - - assert_eq!(pipe.handshake(&mut buf), Ok(())); + assert_eq!(pipe.handshake(), Ok(())); let frames = [frame::Frame::Stream { stream_id: 4, @@ -5138,12 +6475,78 @@ mod tests { } #[test] - fn flow_control_limit() { + /// Tests that receiving a MAX_STREAM_DATA frame for a receive-only + /// unidirectional stream is forbidden. + fn max_stream_data_receive_uni() { let mut buf = [0; 65535]; let mut pipe = testing::Pipe::default().unwrap(); + assert_eq!(pipe.handshake(), Ok(())); + + // Client opens unidirectional stream. + assert_eq!(pipe.client.stream_send(2, b"hello", false), Ok(5)); + assert_eq!(pipe.advance(), Ok(())); + + // Client sends MAX_STREAM_DATA on local unidirectional stream. + let frames = [frame::Frame::MaxStreamData { + stream_id: 2, + max: 1024, + }]; - assert_eq!(pipe.handshake(&mut buf), Ok(())); + let pkt_type = packet::Type::Short; + assert_eq!( + pipe.send_pkt_to_server(pkt_type, &frames, &mut buf), + Err(Error::InvalidStreamState(2)), + ); + } + + #[test] + fn empty_payload() { + let mut buf = [0; 65535]; + + let mut pipe = testing::Pipe::default().unwrap(); + assert_eq!(pipe.handshake(), Ok(())); + + // Send a packet with no frames. + let pkt_type = packet::Type::Short; + assert_eq!( + pipe.send_pkt_to_server(pkt_type, &[], &mut buf), + Err(Error::InvalidPacket) + ); + } + + #[test] + fn min_payload() { + let mut buf = [0; 65535]; + + let mut pipe = testing::Pipe::default().unwrap(); + + // Send a non-ack-eliciting packet. + let frames = [frame::Frame::Padding { len: 4 }]; + + let pkt_type = packet::Type::Initial; + let written = + testing::encode_pkt(&mut pipe.client, pkt_type, &frames, &mut buf) + .unwrap(); + assert_eq!(pipe.server_recv(&mut buf[..written]), Ok(written)); + + assert_eq!(pipe.server.max_send_bytes, 195); + + // Force server to send a single PING frame. + pipe.server.recovery.loss_probes[packet::EPOCH_INITIAL] = 1; + + // Artifically limit the amount of bytes the server can send. + pipe.server.max_send_bytes = 60; + + assert_eq!(pipe.server.send(&mut buf), Err(Error::Done)); + } + + #[test] + fn flow_control_limit() { + let mut buf = [0; 65535]; + + let mut pipe = testing::Pipe::default().unwrap(); + assert_eq!(pipe.handshake(), Ok(())); let frames = [ frame::Frame::Stream { @@ -5172,8 +6575,7 @@ mod tests { let mut buf = [0; 65535]; let mut pipe = testing::Pipe::default().unwrap(); - - assert_eq!(pipe.handshake(&mut buf), Ok(())); + assert_eq!(pipe.handshake(), Ok(())); let frames = [ // One byte less than stream limit. @@ -5201,8 +6603,7 @@ mod tests { let mut buf = [0; 65535]; let mut pipe = testing::Pipe::default().unwrap(); - - assert_eq!(pipe.handshake(&mut buf), Ok(())); + assert_eq!(pipe.handshake(), Ok(())); let frames = [ frame::Frame::Stream { @@ -5240,16 +6641,56 @@ mod tests { // Ignore ACK. iter.next().unwrap(); + assert_eq!( + iter.next(), + Some(&frame::Frame::MaxStreamData { + stream_id: 4, + max: 30 + }) + ); assert_eq!(iter.next(), Some(&frame::Frame::MaxData { max: 46 })); } #[test] + /// Tests that flow control is properly updated even when a stream is shut + /// down. + fn flow_control_drain() { + let mut pipe = testing::Pipe::default().unwrap(); + assert_eq!(pipe.handshake(), Ok(())); + + // Client opens a stream and sends some data. + assert_eq!(pipe.client.stream_send(4, b"aaaaa", false), Ok(5)); + assert_eq!(pipe.advance(), Ok(())); + + // Server receives data, without reading it. + let mut r = pipe.server.readable(); + assert_eq!(r.next(), Some(4)); + assert_eq!(r.next(), None); + + // In the meantime, client sends more data. + assert_eq!(pipe.client.stream_send(4, b"aaaaa", false), Ok(5)); + assert_eq!(pipe.client.stream_send(4, b"aaaaa", true), Ok(5)); + + assert_eq!(pipe.client.stream_send(8, b"aaaaa", false), Ok(5)); + assert_eq!(pipe.client.stream_send(8, b"aaaaa", false), Ok(5)); + assert_eq!(pipe.client.stream_send(8, b"aaaaa", true), Ok(5)); + + // Server shuts down one stream. + assert_eq!(pipe.server.stream_shutdown(4, Shutdown::Read, 42), Ok(())); + + let mut r = pipe.server.readable(); + assert_eq!(r.next(), None); + + // Flush connection. + assert_eq!(pipe.advance(), Ok(())); + } + + #[test] fn stream_flow_control_limit_bidi() { let mut buf = [0; 65535]; let mut pipe = testing::Pipe::default().unwrap(); - - assert_eq!(pipe.handshake(&mut buf), Ok(())); + assert_eq!(pipe.handshake(), Ok(())); let frames = [frame::Frame::Stream { stream_id: 4, @@ -5268,8 +6709,7 @@ mod tests { let mut buf = [0; 65535]; let mut pipe = testing::Pipe::default().unwrap(); - - assert_eq!(pipe.handshake(&mut buf), Ok(())); + assert_eq!(pipe.handshake(), Ok(())); let frames = [frame::Frame::Stream { stream_id: 2, @@ -5288,8 +6728,7 @@ mod tests { let mut buf = [0; 65535]; let mut pipe = testing::Pipe::default().unwrap(); - - assert_eq!(pipe.handshake(&mut buf), Ok(())); + assert_eq!(pipe.handshake(), Ok(())); let frames = [frame::Frame::Stream { stream_id: 4, @@ -5330,12 +6769,63 @@ mod tests { } #[test] - fn stream_limit_bidi() { + fn stream_left_bidi() { let mut buf = [0; 65535]; let mut pipe = testing::Pipe::default().unwrap(); + assert_eq!(pipe.handshake(), Ok(())); + + assert_eq!(3, pipe.client.peer_streams_left_bidi()); + assert_eq!(3, pipe.server.peer_streams_left_bidi()); - assert_eq!(pipe.handshake(&mut buf), Ok(())); + pipe.server.stream_send(1, b"a", false).ok(); + assert_eq!(2, pipe.server.peer_streams_left_bidi()); + pipe.server.stream_send(5, b"a", false).ok(); + assert_eq!(1, pipe.server.peer_streams_left_bidi()); + + pipe.server.stream_send(9, b"a", false).ok(); + assert_eq!(0, pipe.server.peer_streams_left_bidi()); + + let frames = [frame::Frame::MaxStreamsBidi { max: MAX_STREAM_ID }]; + + let pkt_type = packet::Type::Short; + assert!(pipe.send_pkt_to_server(pkt_type, &frames, &mut buf).is_ok()); + + assert_eq!(MAX_STREAM_ID - 3, pipe.server.peer_streams_left_bidi()); + } + + #[test] + fn stream_left_uni() { + let mut buf = [0; 65535]; + + let mut pipe = testing::Pipe::default().unwrap(); + assert_eq!(pipe.handshake(), Ok(())); + + assert_eq!(3, pipe.client.peer_streams_left_uni()); + assert_eq!(3, pipe.server.peer_streams_left_uni()); + + pipe.server.stream_send(3, b"a", false).ok(); + assert_eq!(2, pipe.server.peer_streams_left_uni()); + pipe.server.stream_send(7, b"a", false).ok(); + assert_eq!(1, pipe.server.peer_streams_left_uni()); + + pipe.server.stream_send(11, b"a", false).ok(); + assert_eq!(0, pipe.server.peer_streams_left_uni()); + + let frames = [frame::Frame::MaxStreamsUni { max: MAX_STREAM_ID }]; + + let pkt_type = packet::Type::Short; + assert!(pipe.send_pkt_to_server(pkt_type, &frames, &mut buf).is_ok()); + + assert_eq!(MAX_STREAM_ID - 3, pipe.server.peer_streams_left_uni()); + } + + #[test] + fn stream_limit_bidi() { + let mut buf = [0; 65535]; + + let mut pipe = testing::Pipe::default().unwrap(); + assert_eq!(pipe.handshake(), Ok(())); let frames = [ frame::Frame::Stream { @@ -5380,8 +6870,7 @@ mod tests { let mut buf = [0; 65535]; let mut pipe = testing::Pipe::default().unwrap(); - - assert_eq!(pipe.handshake(&mut buf), Ok(())); + assert_eq!(pipe.handshake(), Ok(())); let frames = [frame::Frame::MaxStreamsBidi { max: MAX_STREAM_ID }]; @@ -5404,8 +6893,7 @@ mod tests { let mut buf = [0; 65535]; let mut pipe = testing::Pipe::default().unwrap(); - - assert_eq!(pipe.handshake(&mut buf), Ok(())); + assert_eq!(pipe.handshake(), Ok(())); let frames = [ frame::Frame::Stream { @@ -5450,8 +6938,7 @@ mod tests { let mut buf = [0; 65535]; let mut pipe = testing::Pipe::default().unwrap(); - - assert_eq!(pipe.handshake(&mut buf), Ok(())); + assert_eq!(pipe.handshake(), Ok(())); let frames = [frame::Frame::MaxStreamsUni { max: MAX_STREAM_ID }]; @@ -5474,8 +6961,7 @@ mod tests { let mut buf = [0; 65535]; let mut pipe = testing::Pipe::default().unwrap(); - - assert_eq!(pipe.handshake(&mut buf), Ok(())); + assert_eq!(pipe.handshake(), Ok(())); let frames = [frame::Frame::StreamsBlockedBidi { limit: MAX_STREAM_ID, @@ -5500,8 +6986,7 @@ mod tests { let mut buf = [0; 65535]; let mut pipe = testing::Pipe::default().unwrap(); - - assert_eq!(pipe.handshake(&mut buf), Ok(())); + assert_eq!(pipe.handshake(), Ok(())); let frames = [frame::Frame::StreamsBlockedUni { limit: MAX_STREAM_ID, @@ -5526,8 +7011,7 @@ mod tests { let mut buf = [0; 65535]; let mut pipe = testing::Pipe::default().unwrap(); - - assert_eq!(pipe.handshake(&mut buf), Ok(())); + assert_eq!(pipe.handshake(), Ok(())); let frames = [ frame::Frame::Stream { @@ -5557,8 +7041,7 @@ mod tests { let mut buf = [0; 65535]; let mut pipe = testing::Pipe::default().unwrap(); - - assert_eq!(pipe.handshake(&mut buf), Ok(())); + assert_eq!(pipe.handshake(), Ok(())); let frames = [ frame::Frame::Stream { @@ -5588,8 +7071,7 @@ mod tests { let mut buf = [0; 65535]; let mut pipe = testing::Pipe::default().unwrap(); - - assert_eq!(pipe.handshake(&mut buf), Ok(())); + assert_eq!(pipe.handshake(), Ok(())); let frames = [ frame::Frame::Stream { @@ -5623,8 +7105,7 @@ mod tests { let mut buf = [0; 65535]; let mut pipe = testing::Pipe::default().unwrap(); - - assert_eq!(pipe.handshake(&mut buf), Ok(())); + assert_eq!(pipe.handshake(), Ok(())); let frames = [frame::Frame::PathChallenge { data: vec![0xba; 8], @@ -5662,18 +7143,21 @@ mod tests { let mut pipe = testing::Pipe::default().unwrap(); // Client sends initial flight - let mut len = pipe.client.send(&mut buf).unwrap(); + let flight = testing::emit_flight(&mut pipe.client).unwrap(); + testing::process_flight(&mut pipe.server, flight).unwrap(); - // Server sends initial flight.. - len = testing::recv_send(&mut pipe.server, &mut buf, len).unwrap(); + // Server sends initial flight. + let flight = testing::emit_flight(&mut pipe.server).unwrap(); + testing::process_flight(&mut pipe.client, flight).unwrap(); // Client sends Handshake packet. - len = testing::recv_send(&mut pipe.client, &mut buf, len).unwrap(); + let flight = testing::emit_flight(&mut pipe.client).unwrap(); // Emulate handshake packet delay by not making server process client // packet. - let mut delayed = (&buf[..len]).to_vec(); - testing::recv_send(&mut pipe.server, &mut buf, 0).unwrap(); + let delayed = flight.clone(); + + testing::emit_flight(&mut pipe.server).ok(); assert!(pipe.client.is_established()); @@ -5687,7 +7171,8 @@ mod tests { let written = testing::encode_pkt(&mut pipe.client, pkt_type, &frames, &mut buf) .unwrap(); - assert_eq!(pipe.server.recv(&mut buf[..written]), Ok(written)); + + assert_eq!(pipe.server_recv(&mut buf[..written]), Ok(written)); // Send 1-RTT packet #1. let frames = [frame::Frame::Stream { @@ -5698,7 +7183,8 @@ mod tests { let written = testing::encode_pkt(&mut pipe.client, pkt_type, &frames, &mut buf) .unwrap(); - assert_eq!(pipe.server.recv(&mut buf[..written]), Ok(written)); + + assert_eq!(pipe.server_recv(&mut buf[..written]), Ok(written)); assert!(!pipe.server.is_established()); @@ -5713,7 +7199,7 @@ mod tests { ); // Process delayed packet. - pipe.server.recv(&mut delayed).unwrap(); + testing::process_flight(&mut pipe.server, delayed).unwrap(); assert!(pipe.server.is_established()); @@ -5725,31 +7211,310 @@ mod tests { } #[test] - fn stream_shutdown_read() { + fn stop_sending() { + let mut b = [0; 15]; + let mut buf = [0; 65535]; let mut pipe = testing::Pipe::default().unwrap(); + assert_eq!(pipe.handshake(), Ok(())); - assert_eq!(pipe.handshake(&mut buf), Ok(())); + // Client sends some data, and closes stream. + assert_eq!(pipe.client.stream_send(4, b"hello", true), Ok(5)); + assert_eq!(pipe.advance(), Ok(())); + // Server gets data. + let mut r = pipe.server.readable(); + assert_eq!(r.next(), Some(4)); + assert_eq!(r.next(), None); + + assert_eq!(pipe.server.stream_recv(4, &mut b), Ok((5, true))); + assert!(pipe.server.stream_finished(4)); + + let mut r = pipe.server.readable(); + assert_eq!(r.next(), None); + + // Server sends data, until blocked. + let mut r = pipe.server.writable(); + assert_eq!(r.next(), Some(4)); + assert_eq!(r.next(), None); + + loop { + if pipe.server.stream_send(4, b"world", false) == Ok(0) { + break; + } + + assert_eq!(pipe.advance(), Ok(())); + } + + let mut r = pipe.server.writable(); + assert_eq!(r.next(), None); + + // Client sends STOP_SENDING. + let frames = [frame::Frame::StopSending { + stream_id: 4, + error_code: 42, + }]; + + let pkt_type = packet::Type::Short; + let len = pipe + .send_pkt_to_server(pkt_type, &frames, &mut buf) + .unwrap(); + + // Server sent a RESET_STREAM frame in response. + let frames = + testing::decode_pkt(&mut pipe.client, &mut buf, len).unwrap(); + + let mut iter = frames.iter(); + + // Skip ACK frame. + iter.next(); + + assert_eq!( + iter.next(), + Some(&frame::Frame::ResetStream { + stream_id: 4, + error_code: 42, + final_size: 15, + }) + ); + + // Stream is writable, but writing returns an error. + let mut r = pipe.server.writable(); + assert_eq!(r.next(), Some(4)); + assert_eq!(r.next(), None); + + assert_eq!( + pipe.server.stream_send(4, b"world", true), + Err(Error::StreamStopped(42)), + ); + + assert_eq!(pipe.server.streams.len(), 1); + + // Client acks RESET_STREAM frame. + let mut ranges = ranges::RangeSet::default(); + ranges.insert(0..6); + + let frames = [frame::Frame::ACK { + ack_delay: 15, + ranges, + }]; + + assert_eq!(pipe.send_pkt_to_server(pkt_type, &frames, &mut buf), Ok(0)); + + // Stream is collected on the server after RESET_STREAM is acked. + assert_eq!(pipe.server.streams.len(), 0); + + // Sending STOP_SENDING again shouldn't trigger RESET_STREAM again. + let frames = [frame::Frame::StopSending { + stream_id: 4, + error_code: 42, + }]; + + let len = pipe + .send_pkt_to_server(pkt_type, &frames, &mut buf) + .unwrap(); + + let frames = + testing::decode_pkt(&mut pipe.client, &mut buf, len).unwrap(); + + assert_eq!(frames.len(), 1); + + match frames.iter().next() { + Some(frame::Frame::ACK { .. }) => (), + + f => panic!("expected ACK frame, got {:?}", f), + }; + + let mut r = pipe.server.writable(); + assert_eq!(r.next(), None); + } + + #[test] + fn stop_sending_fin() { + let mut b = [0; 15]; + + let mut buf = [0; 65535]; + + let mut pipe = testing::Pipe::default().unwrap(); + assert_eq!(pipe.handshake(), Ok(())); + + // Client sends some data, and closes stream. + assert_eq!(pipe.client.stream_send(4, b"hello", true), Ok(5)); + assert_eq!(pipe.advance(), Ok(())); + + // Server gets data. + let mut r = pipe.server.readable(); + assert_eq!(r.next(), Some(4)); + assert_eq!(r.next(), None); + + assert_eq!(pipe.server.stream_recv(4, &mut b), Ok((5, true))); + assert!(pipe.server.stream_finished(4)); + + let mut r = pipe.server.readable(); + assert_eq!(r.next(), None); + + // Server sends data, and closes stream. + let mut r = pipe.server.writable(); + assert_eq!(r.next(), Some(4)); + assert_eq!(r.next(), None); + + assert_eq!(pipe.server.stream_send(4, b"world", true), Ok(5)); + + // Client sends STOP_SENDING before server flushes stream. + let frames = [frame::Frame::StopSending { + stream_id: 4, + error_code: 42, + }]; + + let pkt_type = packet::Type::Short; + let len = pipe + .send_pkt_to_server(pkt_type, &frames, &mut buf) + .unwrap(); + + // Server sent a RESET_STREAM frame in response. + let frames = + testing::decode_pkt(&mut pipe.client, &mut buf, len).unwrap(); + + let mut iter = frames.iter(); + + // Skip ACK frame. + iter.next(); + + assert_eq!( + iter.next(), + Some(&frame::Frame::ResetStream { + stream_id: 4, + error_code: 42, + final_size: 5, + }) + ); + + // No more frames are sent by the server. + assert_eq!(iter.next(), None); + } + + #[test] + fn stream_shutdown_read() { + let mut buf = [0; 65535]; + + let mut pipe = testing::Pipe::default().unwrap(); + assert_eq!(pipe.handshake(), Ok(())); + + // Client sends some data. assert_eq!(pipe.client.stream_send(4, b"hello, world", false), Ok(12)); - assert_eq!(pipe.advance(&mut buf), Ok(())); + assert_eq!(pipe.advance(), Ok(())); let mut r = pipe.server.readable(); assert_eq!(r.next(), Some(4)); assert_eq!(r.next(), None); - assert_eq!(pipe.server.stream_shutdown(4, Shutdown::Read, 0), Ok(())); + assert_eq!(pipe.client.streams.len(), 1); + assert_eq!(pipe.server.streams.len(), 1); + + // Server shuts down stream. + assert_eq!(pipe.server.stream_shutdown(4, Shutdown::Read, 42), Ok(())); + + let mut r = pipe.server.readable(); + assert_eq!(r.next(), None); + + let (len, _) = pipe.server.send(&mut buf).unwrap(); + + let mut dummy = buf[..len].to_vec(); + + let frames = + testing::decode_pkt(&mut pipe.client, &mut dummy, len).unwrap(); + let mut iter = frames.iter(); + + assert_eq!( + iter.next(), + Some(&frame::Frame::StopSending { + stream_id: 4, + error_code: 42, + }) + ); + + assert_eq!(pipe.client_recv(&mut buf[..len]), Ok(len)); + + assert_eq!(pipe.advance(), Ok(())); + + // Sending more data is forbidden. + let mut r = pipe.client.writable(); + assert_eq!(r.next(), Some(4)); + assert_eq!(r.next(), None); + + assert_eq!( + pipe.client.stream_send(4, b"bye", false), + Err(Error::StreamStopped(42)) + ); + + // Server sends some data, without reading the incoming data, and closes + // the stream. + assert_eq!(pipe.server.stream_send(4, b"hello, world", true), Ok(12)); + assert_eq!(pipe.advance(), Ok(())); + + // Client reads the data. + let mut r = pipe.client.readable(); + assert_eq!(r.next(), Some(4)); + assert_eq!(r.next(), None); + + assert_eq!(pipe.client.stream_recv(4, &mut buf), Ok((12, true))); + + // Stream is collected on both sides. + assert_eq!(pipe.client.streams.len(), 0); + assert_eq!(pipe.server.streams.len(), 0); + + assert_eq!( + pipe.server.stream_shutdown(4, Shutdown::Read, 0), + Err(Error::Done) + ); + } + + #[test] + fn stream_shutdown_read_after_fin() { + let mut buf = [0; 65535]; + + let mut pipe = testing::Pipe::default().unwrap(); + assert_eq!(pipe.handshake(), Ok(())); + + // Client sends some data. + assert_eq!(pipe.client.stream_send(4, b"hello, world", true), Ok(12)); + assert_eq!(pipe.advance(), Ok(())); let mut r = pipe.server.readable(); + assert_eq!(r.next(), Some(4)); assert_eq!(r.next(), None); - assert_eq!(pipe.client.stream_send(4, b"bye", false), Ok(3)); - assert_eq!(pipe.advance(&mut buf), Ok(())); + assert_eq!(pipe.client.streams.len(), 1); + assert_eq!(pipe.server.streams.len(), 1); + + // Server shuts down stream. + assert_eq!(pipe.server.stream_shutdown(4, Shutdown::Read, 42), Ok(())); let mut r = pipe.server.readable(); assert_eq!(r.next(), None); + // Server has nothing to send. + assert_eq!(pipe.server.send(&mut buf), Err(Error::Done)); + + assert_eq!(pipe.advance(), Ok(())); + + // Server sends some data, without reading the incoming data, and closes + // the stream. + assert_eq!(pipe.server.stream_send(4, b"hello, world", true), Ok(12)); + assert_eq!(pipe.advance(), Ok(())); + + // Client reads the data. + let mut r = pipe.client.readable(); + assert_eq!(r.next(), Some(4)); + assert_eq!(r.next(), None); + + assert_eq!(pipe.client.stream_recv(4, &mut buf), Ok((12, true))); + + // Stream is collected on both sides. + assert_eq!(pipe.client.streams.len(), 0); + assert_eq!(pipe.server.streams.len(), 0); + assert_eq!( pipe.server.stream_shutdown(4, Shutdown::Read, 0), Err(Error::Done) @@ -5761,34 +7526,76 @@ mod tests { let mut buf = [0; 65535]; let mut pipe = testing::Pipe::default().unwrap(); + assert_eq!(pipe.handshake(), Ok(())); - assert_eq!(pipe.handshake(&mut buf), Ok(())); - + // Client sends some data. assert_eq!(pipe.client.stream_send(4, b"hello, world", false), Ok(12)); - assert_eq!(pipe.advance(&mut buf), Ok(())); + assert_eq!(pipe.advance(), Ok(())); let mut r = pipe.server.readable(); assert_eq!(r.next(), Some(4)); assert_eq!(r.next(), None); - let mut b = [0; 15]; - pipe.server.stream_recv(4, &mut b).unwrap(); + let mut r = pipe.server.writable(); + assert_eq!(r.next(), Some(4)); + assert_eq!(r.next(), None); - assert_eq!(pipe.client.stream_send(4, b"a", false), Ok(1)); - assert_eq!(pipe.client.stream_shutdown(4, Shutdown::Write, 0), Ok(())); - assert_eq!(pipe.advance(&mut buf), Ok(())); + assert_eq!(pipe.client.streams.len(), 1); + assert_eq!(pipe.server.streams.len(), 1); - let mut r = pipe.server.readable(); + // Server sends some data. + assert_eq!(pipe.server.stream_send(4, b"goodbye, world", false), Ok(14)); + + // Server shuts down stream. + assert_eq!(pipe.server.stream_shutdown(4, Shutdown::Write, 42), Ok(())); + + let mut r = pipe.server.writable(); assert_eq!(r.next(), None); - assert_eq!(pipe.client.stream_send(4, b"bye", false), Ok(3)); - assert_eq!(pipe.advance(&mut buf), Ok(())); + let (len, _) = pipe.server.send(&mut buf).unwrap(); + let mut dummy = buf[..len].to_vec(); + + let frames = + testing::decode_pkt(&mut pipe.client, &mut dummy, len).unwrap(); + let mut iter = frames.iter(); + + assert_eq!( + iter.next(), + Some(&frame::Frame::ResetStream { + stream_id: 4, + error_code: 42, + final_size: 14, + }) + ); + + assert_eq!(pipe.client_recv(&mut buf[..len]), Ok(len)); + + assert_eq!(pipe.advance(), Ok(())); + + // Sending more data is forbidden. + assert_eq!( + pipe.server.stream_send(4, b"bye", false), + Err(Error::FinalSize) + ); + + // Client sends some data and closes the stream. + assert_eq!(pipe.client.stream_send(4, b"bye", true), Ok(3)); + assert_eq!(pipe.advance(), Ok(())); + + // Server reads the data. let mut r = pipe.server.readable(); + assert_eq!(r.next(), Some(4)); assert_eq!(r.next(), None); + assert_eq!(pipe.server.stream_recv(4, &mut buf), Ok((15, true))); + + // Stream is collected on both sides. + // TODO: assert_eq!(pipe.client.streams.len(), 0); + assert_eq!(pipe.server.streams.len(), 0); + assert_eq!( - pipe.client.stream_shutdown(4, Shutdown::Write, 0), + pipe.server.stream_shutdown(4, Shutdown::Write, 0), Err(Error::Done) ); } @@ -5800,27 +7607,31 @@ mod tests { let mut buf = [0; 65535]; let mut pipe = testing::Pipe::default().unwrap(); - - assert_eq!(pipe.handshake(&mut buf), Ok(())); + assert_eq!(pipe.handshake(), Ok(())); assert_eq!(pipe.client.stream_send(8, b"aaaaa", false), Ok(5)); assert_eq!(pipe.client.stream_send(0, b"aaaaa", false), Ok(5)); assert_eq!(pipe.client.stream_send(4, b"aaaaa", false), Ok(5)); - let len = pipe.client.send(&mut buf).unwrap(); + let (len, _) = pipe.client.send(&mut buf).unwrap(); let frames = testing::decode_pkt(&mut pipe.server, &mut buf, len).unwrap(); + let mut iter = frames.iter(); + + // Skip ACK frame. + iter.next(); + assert_eq!( - frames.iter().next(), + iter.next(), Some(&frame::Frame::Stream { stream_id: 8, data: stream::RangeBuf::from(b"aaaaa", 0, false), }) ); - let len = pipe.client.send(&mut buf).unwrap(); + let (len, _) = pipe.client.send(&mut buf).unwrap(); let frames = testing::decode_pkt(&mut pipe.server, &mut buf, len).unwrap(); @@ -5833,7 +7644,7 @@ mod tests { }) ); - let len = pipe.client.send(&mut buf).unwrap(); + let (len, _) = pipe.client.send(&mut buf).unwrap(); let frames = testing::decode_pkt(&mut pipe.server, &mut buf, len).unwrap(); @@ -5850,11 +7661,8 @@ mod tests { #[test] /// Tests the readable iterator. fn stream_readable() { - let mut buf = [0; 65535]; - let mut pipe = testing::Pipe::default().unwrap(); - - assert_eq!(pipe.handshake(&mut buf), Ok(())); + assert_eq!(pipe.handshake(), Ok(())); // No readable streams. let mut r = pipe.client.readable(); @@ -5868,7 +7676,7 @@ mod tests { let mut r = pipe.server.readable(); assert_eq!(r.next(), None); - assert_eq!(pipe.advance(&mut buf), Ok(())); + assert_eq!(pipe.advance(), Ok(())); // Server received stream. let mut r = pipe.server.readable(); @@ -5879,7 +7687,7 @@ mod tests { pipe.server.stream_send(4, b"aaaaaaaaaaaaaaa", false), Ok(15) ); - assert_eq!(pipe.advance(&mut buf), Ok(())); + assert_eq!(pipe.advance(), Ok(())); let mut r = pipe.client.readable(); assert_eq!(r.next(), Some(4)); @@ -5888,7 +7696,7 @@ mod tests { // Client drains stream. let mut b = [0; 15]; pipe.client.stream_recv(4, &mut b).unwrap(); - assert_eq!(pipe.advance(&mut buf), Ok(())); + assert_eq!(pipe.advance(), Ok(())); let mut r = pipe.client.readable(); assert_eq!(r.next(), None); @@ -5905,10 +7713,10 @@ mod tests { // Client creates multiple streams. assert_eq!(pipe.client.stream_send(8, b"aaaaa", false), Ok(5)); - assert_eq!(pipe.advance(&mut buf), Ok(())); + assert_eq!(pipe.advance(), Ok(())); assert_eq!(pipe.client.stream_send(12, b"aaaaa", false), Ok(5)); - assert_eq!(pipe.advance(&mut buf), Ok(())); + assert_eq!(pipe.advance(), Ok(())); let mut r = pipe.server.readable(); assert_eq!(r.len(), 2); @@ -5923,11 +7731,8 @@ mod tests { #[test] /// Tests the writable iterator. fn stream_writable() { - let mut buf = [0; 65535]; - let mut pipe = testing::Pipe::default().unwrap(); - - assert_eq!(pipe.handshake(&mut buf), Ok(())); + assert_eq!(pipe.handshake(), Ok(())); // No writable streams. let mut w = pipe.client.writable(); @@ -5940,7 +7745,7 @@ mod tests { assert_eq!(w.next(), Some(4)); assert_eq!(w.next(), None); - assert_eq!(pipe.advance(&mut buf), Ok(())); + assert_eq!(pipe.advance(), Ok(())); // Server created stream. let mut w = pipe.server.writable(); @@ -5956,12 +7761,12 @@ mod tests { let mut w = pipe.server.writable(); assert_eq!(w.next(), None); - assert_eq!(pipe.advance(&mut buf), Ok(())); + assert_eq!(pipe.advance(), Ok(())); // Client drains stream. let mut b = [0; 15]; pipe.client.stream_recv(4, &mut b).unwrap(); - assert_eq!(pipe.advance(&mut buf), Ok(())); + assert_eq!(pipe.advance(), Ok(())); // Server stream is writable again. let mut w = pipe.server.writable(); @@ -5976,10 +7781,10 @@ mod tests { // Client creates multiple streams. assert_eq!(pipe.client.stream_send(8, b"aaaaa", false), Ok(5)); - assert_eq!(pipe.advance(&mut buf), Ok(())); + assert_eq!(pipe.advance(), Ok(())); assert_eq!(pipe.client.stream_send(12, b"aaaaa", false), Ok(5)); - assert_eq!(pipe.advance(&mut buf), Ok(())); + assert_eq!(pipe.advance(), Ok(())); let mut w = pipe.server.writable(); assert_eq!(w.len(), 2); @@ -6002,24 +7807,21 @@ mod tests { /// Tests that we don't exceed the per-connection flow control limit set by /// the peer. fn flow_control_limit_send() { - let mut buf = [0; 65535]; - let mut pipe = testing::Pipe::default().unwrap(); - - assert_eq!(pipe.handshake(&mut buf), Ok(())); + assert_eq!(pipe.handshake(), Ok(())); assert_eq!( pipe.client.stream_send(0, b"aaaaaaaaaaaaaaa", false), Ok(15) ); - assert_eq!(pipe.advance(&mut buf), Ok(())); + assert_eq!(pipe.advance(), Ok(())); assert_eq!( pipe.client.stream_send(4, b"aaaaaaaaaaaaaaa", false), Ok(15) ); - assert_eq!(pipe.advance(&mut buf), Ok(())); + assert_eq!(pipe.advance(), Ok(())); assert_eq!(pipe.client.stream_send(8, b"a", false), Ok(0)); - assert_eq!(pipe.advance(&mut buf), Ok(())); + assert_eq!(pipe.advance(), Ok(())); let mut r = pipe.server.readable(); assert!(r.next().is_some()); @@ -6052,7 +7854,7 @@ mod tests { assert_eq!(pipe.server.timeout(), None); assert_eq!( - pipe.server.recv(&mut buf[..written]), + pipe.server_recv(&mut buf[..written]), Err(Error::CryptoFail) ); @@ -6067,10 +7869,10 @@ mod tests { let mut pipe = testing::Pipe::default().unwrap(); // Client sends initial flight. - let len = pipe.client.send(&mut buf).unwrap(); + let (len, _) = pipe.client.send(&mut buf).unwrap(); // Server sends initial flight. - assert_eq!(pipe.server.recv(&mut buf[..len]), Ok(1200)); + assert_eq!(pipe.server_recv(&mut buf[..len]), Ok(1200)); let frames = [frame::Frame::Padding { len: 10 }]; @@ -6088,7 +7890,7 @@ mod tests { buf[written - 1] = !buf[written - 1]; // Client will ignore invalid packet. - assert_eq!(pipe.client.recv(&mut buf[..written]), Ok(68)); + assert_eq!(pipe.client_recv(&mut buf[..written]), Ok(71)); // The connection should be alive... assert_eq!(pipe.client.is_closed(), false); @@ -6114,8 +7916,8 @@ mod tests { let hdr = Header { ty: packet::Type::Initial, version: pipe.client.version, - dcid: pipe.client.dcid.clone(), - scid: pipe.client.scid.clone(), + dcid: ConnectionId::from_ref(&pipe.client.dcid), + scid: ConnectionId::from_ref(&pipe.client.scid), pkt_num: 0, pkt_num_len: pn_len, token: pipe.client.token.clone(), @@ -6162,8 +7964,8 @@ mod tests { assert_eq!(pipe.server.timeout(), None); assert_eq!( - pipe.server.recv(&mut buf[..written]), - Err(Error::BufferTooShort) + pipe.server_recv(&mut buf[..written]), + Err(Error::InvalidPacket) ); assert!(pipe.server.is_closed()); @@ -6173,9 +7975,9 @@ mod tests { /// Tests that invalid packets don't cause the connection to be closed. fn invalid_packet() { let mut buf = [0; 65535]; - let mut pipe = testing::Pipe::default().unwrap(); - assert_eq!(pipe.handshake(&mut buf), Ok(())); + let mut pipe = testing::Pipe::default().unwrap(); + assert_eq!(pipe.handshake(), Ok(())); let frames = [frame::Frame::Padding { len: 10 }]; @@ -6192,19 +7994,27 @@ mod tests { // cannot be authenticated during decryption). buf[written - 1] = !buf[written - 1]; - assert_eq!(pipe.server.recv(&mut buf[..written]), Ok(written)); + assert_eq!(pipe.server_recv(&mut buf[..written]), Ok(written)); // Corrupt the packets's first byte to make the header fail decoding. buf[0] = 255; - assert_eq!(pipe.server.recv(&mut buf[..written]), Ok(written)); + assert_eq!(pipe.server_recv(&mut buf[..written]), Ok(written)); } #[test] - /// Tests that the MAX_STREAMS frame is sent for bidirectional streams. - fn stream_limit_update_bidi() { + fn recv_empty_buffer() { let mut buf = [0; 65535]; + let mut pipe = testing::Pipe::default().unwrap(); + assert_eq!(pipe.handshake(), Ok(())); + + assert_eq!(pipe.server_recv(&mut buf[..0]), Err(Error::BufferTooShort)); + } + + #[test] + /// Tests that the MAX_STREAMS frame is sent for bidirectional streams. + fn stream_limit_update_bidi() { let mut config = Config::new(crate::PROTOCOL_VERSION).unwrap(); config .load_cert_chain_from_pem_file("examples/cert.crt") @@ -6224,51 +8034,51 @@ mod tests { config.verify_peer(false); let mut pipe = testing::Pipe::with_config(&mut config).unwrap(); - assert_eq!(pipe.handshake(&mut buf), Ok(())); + assert_eq!(pipe.handshake(), Ok(())); // Client sends stream data. assert_eq!(pipe.client.stream_send(0, b"a", false), Ok(1)); - assert_eq!(pipe.advance(&mut buf), Ok(())); + assert_eq!(pipe.advance(), Ok(())); assert_eq!(pipe.client.stream_send(4, b"a", false), Ok(1)); - assert_eq!(pipe.advance(&mut buf), Ok(())); + assert_eq!(pipe.advance(), Ok(())); assert_eq!(pipe.client.stream_send(4, b"b", true), Ok(1)); - assert_eq!(pipe.advance(&mut buf), Ok(())); + assert_eq!(pipe.advance(), Ok(())); assert_eq!(pipe.client.stream_send(0, b"b", true), Ok(1)); - assert_eq!(pipe.advance(&mut buf), Ok(())); + assert_eq!(pipe.advance(), Ok(())); // Server reads stream data. let mut b = [0; 15]; pipe.server.stream_recv(0, &mut b).unwrap(); pipe.server.stream_recv(4, &mut b).unwrap(); - assert_eq!(pipe.advance(&mut buf), Ok(())); + assert_eq!(pipe.advance(), Ok(())); // Server sends stream data, with fin. assert_eq!(pipe.server.stream_send(0, b"a", false), Ok(1)); - assert_eq!(pipe.advance(&mut buf), Ok(())); + assert_eq!(pipe.advance(), Ok(())); assert_eq!(pipe.server.stream_send(4, b"a", false), Ok(1)); - assert_eq!(pipe.advance(&mut buf), Ok(())); + assert_eq!(pipe.advance(), Ok(())); assert_eq!(pipe.server.stream_send(4, b"b", true), Ok(1)); - assert_eq!(pipe.advance(&mut buf), Ok(())); + assert_eq!(pipe.advance(), Ok(())); assert_eq!(pipe.server.stream_send(0, b"b", true), Ok(1)); // Server sends MAX_STREAMS. - assert_eq!(pipe.advance(&mut buf), Ok(())); + assert_eq!(pipe.advance(), Ok(())); // Client tries to create new streams. assert_eq!(pipe.client.stream_send(8, b"a", false), Ok(1)); - assert_eq!(pipe.advance(&mut buf), Ok(())); + assert_eq!(pipe.advance(), Ok(())); assert_eq!(pipe.client.stream_send(12, b"a", false), Ok(1)); - assert_eq!(pipe.advance(&mut buf), Ok(())); + assert_eq!(pipe.advance(), Ok(())); assert_eq!(pipe.client.stream_send(16, b"a", false), Ok(1)); - assert_eq!(pipe.advance(&mut buf), Ok(())); + assert_eq!(pipe.advance(), Ok(())); assert_eq!( pipe.client.stream_send(20, b"a", false), @@ -6281,8 +8091,6 @@ mod tests { #[test] /// Tests that the MAX_STREAMS frame is sent for unirectional streams. fn stream_limit_update_uni() { - let mut buf = [0; 65535]; - let mut config = Config::new(crate::PROTOCOL_VERSION).unwrap(); config .load_cert_chain_from_pem_file("examples/cert.crt") @@ -6302,20 +8110,20 @@ mod tests { config.verify_peer(false); let mut pipe = testing::Pipe::with_config(&mut config).unwrap(); - assert_eq!(pipe.handshake(&mut buf), Ok(())); + assert_eq!(pipe.handshake(), Ok(())); // Client sends stream data. assert_eq!(pipe.client.stream_send(2, b"a", false), Ok(1)); - assert_eq!(pipe.advance(&mut buf), Ok(())); + assert_eq!(pipe.advance(), Ok(())); assert_eq!(pipe.client.stream_send(6, b"a", false), Ok(1)); - assert_eq!(pipe.advance(&mut buf), Ok(())); + assert_eq!(pipe.advance(), Ok(())); assert_eq!(pipe.client.stream_send(6, b"b", true), Ok(1)); - assert_eq!(pipe.advance(&mut buf), Ok(())); + assert_eq!(pipe.advance(), Ok(())); assert_eq!(pipe.client.stream_send(2, b"b", true), Ok(1)); - assert_eq!(pipe.advance(&mut buf), Ok(())); + assert_eq!(pipe.advance(), Ok(())); // Server reads stream data. let mut b = [0; 15]; @@ -6323,17 +8131,17 @@ mod tests { pipe.server.stream_recv(6, &mut b).unwrap(); // Server sends MAX_STREAMS. - assert_eq!(pipe.advance(&mut buf), Ok(())); + assert_eq!(pipe.advance(), Ok(())); // Client tries to create new streams. assert_eq!(pipe.client.stream_send(10, b"a", false), Ok(1)); - assert_eq!(pipe.advance(&mut buf), Ok(())); + assert_eq!(pipe.advance(), Ok(())); assert_eq!(pipe.client.stream_send(14, b"a", false), Ok(1)); - assert_eq!(pipe.advance(&mut buf), Ok(())); + assert_eq!(pipe.advance(), Ok(())); assert_eq!(pipe.client.stream_send(18, b"a", false), Ok(1)); - assert_eq!(pipe.advance(&mut buf), Ok(())); + assert_eq!(pipe.advance(), Ok(())); assert_eq!( pipe.client.stream_send(22, b"a", false), @@ -6348,17 +8156,59 @@ mod tests { /// data in the buffer, and that the buffer becomes readable on the other /// side. fn stream_zero_length_fin() { - let mut buf = [0; 65535]; - let mut pipe = testing::Pipe::default().unwrap(); + assert_eq!(pipe.handshake(), Ok(())); - assert_eq!(pipe.handshake(&mut buf), Ok(())); + assert_eq!( + pipe.client.stream_send(0, b"aaaaaaaaaaaaaaa", false), + Ok(15) + ); + assert_eq!(pipe.advance(), Ok(())); + + let mut r = pipe.server.readable(); + assert_eq!(r.next(), Some(0)); + assert!(r.next().is_none()); + + let mut b = [0; 15]; + pipe.server.stream_recv(0, &mut b).unwrap(); + assert_eq!(pipe.advance(), Ok(())); + + // Client sends zero-length frame. + assert_eq!(pipe.client.stream_send(0, b"", true), Ok(0)); + assert_eq!(pipe.advance(), Ok(())); + + // Stream should be readable on the server after receiving empty fin. + let mut r = pipe.server.readable(); + assert_eq!(r.next(), Some(0)); + assert!(r.next().is_none()); + + let mut b = [0; 15]; + pipe.server.stream_recv(0, &mut b).unwrap(); + assert_eq!(pipe.advance(), Ok(())); + + // Client sends zero-length frame (again). + assert_eq!(pipe.client.stream_send(0, b"", true), Ok(0)); + assert_eq!(pipe.advance(), Ok(())); + + // Stream should _not_ be readable on the server after receiving empty + // fin, because it was already finished. + let mut r = pipe.server.readable(); + assert_eq!(r.next(), None); + } + + #[test] + /// Tests that the stream's fin flag is properly flushed even if there's no + /// data in the buffer, that the buffer becomes readable on the other + /// side and stays readable even if the stream is fin'd locally. + fn stream_zero_length_fin_deferred_collection() { + let mut pipe = testing::Pipe::default().unwrap(); + assert_eq!(pipe.handshake(), Ok(())); assert_eq!( pipe.client.stream_send(0, b"aaaaaaaaaaaaaaa", false), Ok(15) ); - assert_eq!(pipe.advance(&mut buf), Ok(())); + assert_eq!(pipe.advance(), Ok(())); let mut r = pipe.server.readable(); assert_eq!(r.next(), Some(0)); @@ -6366,11 +8216,15 @@ mod tests { let mut b = [0; 15]; pipe.server.stream_recv(0, &mut b).unwrap(); - assert_eq!(pipe.advance(&mut buf), Ok(())); + assert_eq!(pipe.advance(), Ok(())); // Client sends zero-length frame. assert_eq!(pipe.client.stream_send(0, b"", true), Ok(0)); - assert_eq!(pipe.advance(&mut buf), Ok(())); + assert_eq!(pipe.advance(), Ok(())); + + // Server sends zero-length frame. + assert_eq!(pipe.server.stream_send(0, b"", true), Ok(0)); + assert_eq!(pipe.advance(), Ok(())); // Stream should be readable on the server after receiving empty fin. let mut r = pipe.server.readable(); @@ -6379,16 +8233,27 @@ mod tests { let mut b = [0; 15]; pipe.server.stream_recv(0, &mut b).unwrap(); - assert_eq!(pipe.advance(&mut buf), Ok(())); + assert_eq!(pipe.advance(), Ok(())); // Client sends zero-length frame (again). assert_eq!(pipe.client.stream_send(0, b"", true), Ok(0)); - assert_eq!(pipe.advance(&mut buf), Ok(())); + assert_eq!(pipe.advance(), Ok(())); // Stream should _not_ be readable on the server after receiving empty // fin, because it was already finished. let mut r = pipe.server.readable(); assert_eq!(r.next(), None); + + // Stream _is_readable on the client side. + let mut r = pipe.client.readable(); + assert_eq!(r.next(), Some(0)); + + pipe.client.stream_recv(0, &mut b).unwrap(); + assert_eq!(pipe.advance(), Ok(())); + + // Stream is completed and _is not_ readable. + let mut r = pipe.client.readable(); + assert_eq!(r.next(), None); } #[test] @@ -6397,14 +8262,13 @@ mod tests { let mut buf = [0; 65535]; let mut pipe = testing::Pipe::default().unwrap(); - - assert_eq!(pipe.handshake(&mut buf), Ok(())); + assert_eq!(pipe.handshake(), Ok(())); assert_eq!(pipe.client.streams.len(), 0); assert_eq!(pipe.server.streams.len(), 0); assert_eq!(pipe.client.stream_send(0, b"aaaaa", true), Ok(5)); - assert_eq!(pipe.advance(&mut buf), Ok(())); + assert_eq!(pipe.advance(), Ok(())); assert!(!pipe.client.stream_finished(0)); assert!(!pipe.server.stream_finished(0)); @@ -6414,10 +8278,10 @@ mod tests { let mut b = [0; 5]; pipe.server.stream_recv(0, &mut b).unwrap(); - assert_eq!(pipe.advance(&mut buf), Ok(())); + assert_eq!(pipe.advance(), Ok(())); assert_eq!(pipe.server.stream_send(0, b"aaaaa", true), Ok(5)); - assert_eq!(pipe.advance(&mut buf), Ok(())); + assert_eq!(pipe.advance(), Ok(())); assert!(!pipe.client.stream_finished(0)); assert!(pipe.server.stream_finished(0)); @@ -6427,7 +8291,7 @@ mod tests { let mut b = [0; 5]; pipe.client.stream_recv(0, &mut b).unwrap(); - assert_eq!(pipe.advance(&mut buf), Ok(())); + assert_eq!(pipe.advance(), Ok(())); assert_eq!(pipe.client.streams.len(), 0); assert_eq!(pipe.server.streams.len(), 0); @@ -6461,11 +8325,8 @@ mod tests { #[test] fn peer_cert() { - let mut buf = [0; 65535]; - let mut pipe = testing::Pipe::default().unwrap(); - - assert_eq!(pipe.handshake(&mut buf), Ok(())); + assert_eq!(pipe.handshake(), Ok(())); match pipe.client.peer_cert() { Some(c) => assert_eq!(c.len(), 753), @@ -6492,15 +8353,16 @@ mod tests { let mut pipe = testing::Pipe::with_server_config(&mut config).unwrap(); // Client sends initial flight. - let mut len = pipe.client.send(&mut buf).unwrap(); + let (mut len, _) = pipe.client.send(&mut buf).unwrap(); // Server sends Retry packet. let hdr = Header::from_slice(&mut buf[..len], MAX_CONN_ID_LEN).unwrap(); - let odcid = hdr.dcid.to_vec(); + let odcid = hdr.dcid.clone(); let mut scid = [0; MAX_CONN_ID_LEN]; rand::rand_bytes(&mut scid[..]); + let scid = ConnectionId::from_ref(&scid); let token = b"quiche test retry token"; @@ -6515,19 +8377,19 @@ mod tests { .unwrap(); // Client receives Retry and sends new Initial. - assert_eq!(pipe.client.recv(&mut buf[..len]), Ok(len)); + assert_eq!(pipe.client_recv(&mut buf[..len]), Ok(len)); - len = pipe.client.send(&mut buf).unwrap(); + let (len, _) = pipe.client.send(&mut buf).unwrap(); let hdr = Header::from_slice(&mut buf[..len], MAX_CONN_ID_LEN).unwrap(); assert_eq!(&hdr.token.unwrap(), token); - // Server accepts connection and send first flight. - pipe.server = accept(&scid, Some(&odcid), &mut config).unwrap(); + // Server accepts connection. + let from = "127.0.0.1:1234".parse().unwrap(); + pipe.server = accept(&scid, Some(&odcid), from, &mut config).unwrap(); + assert_eq!(pipe.server_recv(&mut buf[..len]), Ok(len)); - len = testing::recv_send(&mut pipe.server, &mut buf, len).unwrap(); - len = testing::recv_send(&mut pipe.client, &mut buf, len).unwrap(); - testing::recv_send(&mut pipe.server, &mut buf, len).unwrap(); + assert_eq!(pipe.advance(), Ok(())); assert!(pipe.client.is_established()); assert!(pipe.server.is_established()); @@ -6551,13 +8413,14 @@ mod tests { let mut pipe = testing::Pipe::with_server_config(&mut config).unwrap(); // Client sends initial flight. - let mut len = pipe.client.send(&mut buf).unwrap(); + let (mut len, _) = pipe.client.send(&mut buf).unwrap(); // Server sends Retry packet. let hdr = Header::from_slice(&mut buf[..len], MAX_CONN_ID_LEN).unwrap(); let mut scid = [0; MAX_CONN_ID_LEN]; rand::rand_bytes(&mut scid[..]); + let scid = ConnectionId::from_ref(&scid); let token = b"quiche test retry token"; @@ -6572,18 +8435,20 @@ mod tests { .unwrap(); // Client receives Retry and sends new Initial. - assert_eq!(pipe.client.recv(&mut buf[..len]), Ok(len)); + assert_eq!(pipe.client_recv(&mut buf[..len]), Ok(len)); - len = pipe.client.send(&mut buf).unwrap(); + let (len, _) = pipe.client.send(&mut buf).unwrap(); // Server accepts connection and send first flight. But original // destination connection ID is ignored. - pipe.server = accept(&scid, None, &mut config).unwrap(); + let from = "127.0.0.1:1234".parse().unwrap(); + pipe.server = accept(&scid, None, from, &mut config).unwrap(); + assert_eq!(pipe.server_recv(&mut buf[..len]), Ok(len)); - len = testing::recv_send(&mut pipe.server, &mut buf, len).unwrap(); + let flight = testing::emit_flight(&mut pipe.server).unwrap(); assert_eq!( - pipe.client.recv(&mut buf[..len]), + testing::process_flight(&mut pipe.client, flight), Err(Error::InvalidTransportParam) ); } @@ -6606,13 +8471,14 @@ mod tests { let mut pipe = testing::Pipe::with_server_config(&mut config).unwrap(); // Client sends initial flight. - let mut len = pipe.client.send(&mut buf).unwrap(); + let (mut len, _) = pipe.client.send(&mut buf).unwrap(); // Server sends Retry packet. let hdr = Header::from_slice(&mut buf[..len], MAX_CONN_ID_LEN).unwrap(); let mut scid = [0; MAX_CONN_ID_LEN]; rand::rand_bytes(&mut scid[..]); + let scid = ConnectionId::from_ref(&scid); let token = b"quiche test retry token"; @@ -6627,18 +8493,21 @@ mod tests { .unwrap(); // Client receives Retry and sends new Initial. - assert_eq!(pipe.client.recv(&mut buf[..len]), Ok(len)); + assert_eq!(pipe.client_recv(&mut buf[..len]), Ok(len)); - len = pipe.client.send(&mut buf).unwrap(); + let (len, _) = pipe.client.send(&mut buf).unwrap(); // Server accepts connection and send first flight. But original // destination connection ID is invalid. - pipe.server = accept(&scid, Some(b"bogus value"), &mut config).unwrap(); + let from = "127.0.0.1:1234".parse().unwrap(); + let odcid = ConnectionId::from_ref(b"bogus value"); + pipe.server = accept(&scid, Some(&odcid), from, &mut config).unwrap(); + assert_eq!(pipe.server_recv(&mut buf[..len]), Ok(len)); - len = testing::recv_send(&mut pipe.server, &mut buf, len).unwrap(); + let flight = testing::emit_flight(&mut pipe.server).unwrap(); assert_eq!( - pipe.client.recv(&mut buf[..len]), + testing::process_flight(&mut pipe.client, flight), Err(Error::InvalidTransportParam) ); } @@ -6646,31 +8515,50 @@ mod tests { fn check_send(_: &mut impl Send) {} #[test] + fn config_must_be_send() { + let mut config = Config::new(crate::PROTOCOL_VERSION).unwrap(); + check_send(&mut config); + } + + #[test] fn connection_must_be_send() { let mut pipe = testing::Pipe::default().unwrap(); check_send(&mut pipe.client); } + fn check_sync(_: &mut impl Sync) {} + + #[test] + fn config_must_be_sync() { + let mut config = Config::new(crate::PROTOCOL_VERSION).unwrap(); + check_sync(&mut config); + } + + #[test] + fn connection_must_be_sync() { + let mut pipe = testing::Pipe::default().unwrap(); + check_sync(&mut pipe.client); + } + #[test] fn data_blocked() { let mut buf = [0; 65535]; let mut pipe = testing::Pipe::default().unwrap(); - - assert_eq!(pipe.handshake(&mut buf), Ok(())); + assert_eq!(pipe.handshake(), Ok(())); assert_eq!(pipe.client.stream_send(0, b"aaaaaaaaaa", false), Ok(10)); assert_eq!(pipe.client.blocked_limit, None); - assert_eq!(pipe.advance(&mut buf), Ok(())); + assert_eq!(pipe.advance(), Ok(())); assert_eq!(pipe.client.stream_send(4, b"aaaaaaaaaa", false), Ok(10)); assert_eq!(pipe.client.blocked_limit, None); - assert_eq!(pipe.advance(&mut buf), Ok(())); + assert_eq!(pipe.advance(), Ok(())); assert_eq!(pipe.client.stream_send(8, b"aaaaaaaaaaa", false), Ok(10)); assert_eq!(pipe.client.blocked_limit, Some(30)); - let len = pipe.client.send(&mut buf).unwrap(); + let (len, _) = pipe.client.send(&mut buf).unwrap(); assert_eq!(pipe.client.blocked_limit, None); let frames = @@ -6696,8 +8584,7 @@ mod tests { let mut buf = [0; 65535]; let mut pipe = testing::Pipe::default().unwrap(); - - assert_eq!(pipe.handshake(&mut buf), Ok(())); + assert_eq!(pipe.handshake(), Ok(())); assert_eq!(pipe.client.stream_send(0, b"aaaaa", false), Ok(5)); assert_eq!(pipe.client.streams.blocked().len(), 0); @@ -6708,7 +8595,7 @@ mod tests { assert_eq!(pipe.client.stream_send(0, b"aaaaaa", false), Ok(5)); assert_eq!(pipe.client.streams.blocked().len(), 1); - let len = pipe.client.send(&mut buf).unwrap(); + let (len, _) = pipe.client.send(&mut buf).unwrap(); assert_eq!(pipe.client.streams.blocked().len(), 0); let frames = @@ -6716,6 +8603,9 @@ mod tests { let mut iter = frames.iter(); + // Skip ACK frame. + iter.next(); + assert_eq!( iter.next(), Some(&frame::Frame::StreamDataBlocked { @@ -6738,7 +8628,7 @@ mod tests { // again. assert_eq!(pipe.client.stream_send(4, b"a", false), Ok(1)); - let len = pipe.client.send(&mut buf).unwrap(); + let (len, _) = pipe.client.send(&mut buf).unwrap(); assert_eq!(pipe.client.streams.blocked().len(), 0); let frames = @@ -6761,7 +8651,7 @@ mod tests { assert_eq!(pipe.client.stream_send(0, b"aaaaaa", false), Ok(0)); assert_eq!(pipe.client.streams.blocked().len(), 1); - let len = pipe.client.send(&mut buf).unwrap(); + let (len, _) = pipe.client.send(&mut buf).unwrap(); assert_eq!(pipe.client.streams.blocked().len(), 0); let frames = @@ -6784,8 +8674,6 @@ mod tests { #[test] fn app_limited_true() { - let mut buf = [0; 65535]; - let mut config = Config::new(PROTOCOL_VERSION).unwrap(); config .set_application_protos(b"\x06proto1\x06proto2") @@ -6793,26 +8681,25 @@ mod tests { config.set_initial_max_data(50000); config.set_initial_max_stream_data_bidi_local(50000); config.set_initial_max_stream_data_bidi_remote(50000); - config.set_max_udp_payload_size(1200); + config.set_max_recv_udp_payload_size(1200); config.verify_peer(false); let mut pipe = testing::Pipe::with_client_config(&mut config).unwrap(); - - assert_eq!(pipe.handshake(&mut buf), Ok(())); + assert_eq!(pipe.handshake(), Ok(())); // Client sends stream data. assert_eq!(pipe.client.stream_send(0, b"a", true), Ok(1)); - assert_eq!(pipe.advance(&mut buf), Ok(())); + assert_eq!(pipe.advance(), Ok(())); // Server reads stream data. let mut b = [0; 15]; pipe.server.stream_recv(0, &mut b).unwrap(); - assert_eq!(pipe.advance(&mut buf), Ok(())); + assert_eq!(pipe.advance(), Ok(())); // Server sends stream data smaller than cwnd. let send_buf = [0; 10000]; assert_eq!(pipe.server.stream_send(0, &send_buf, false), Ok(10000)); - assert_eq!(pipe.advance(&mut buf), Ok(())); + assert_eq!(pipe.advance(), Ok(())); // app_limited should be true because we send less than cwnd. assert_eq!(pipe.server.recovery.app_limited(), true); @@ -6820,8 +8707,6 @@ mod tests { #[test] fn app_limited_false() { - let mut buf = [0; 65535]; - let mut config = Config::new(PROTOCOL_VERSION).unwrap(); config .set_application_protos(b"\x06proto1\x06proto2") @@ -6829,26 +8714,26 @@ mod tests { config.set_initial_max_data(50000); config.set_initial_max_stream_data_bidi_local(50000); config.set_initial_max_stream_data_bidi_remote(50000); - config.set_max_udp_payload_size(1200); + config.set_max_recv_udp_payload_size(1200); config.verify_peer(false); let mut pipe = testing::Pipe::with_client_config(&mut config).unwrap(); - - assert_eq!(pipe.handshake(&mut buf), Ok(())); + assert_eq!(pipe.handshake(), Ok(())); // Client sends stream data. assert_eq!(pipe.client.stream_send(0, b"a", true), Ok(1)); - assert_eq!(pipe.advance(&mut buf), Ok(())); + assert_eq!(pipe.advance(), Ok(())); // Server reads stream data. let mut b = [0; 15]; pipe.server.stream_recv(0, &mut b).unwrap(); - assert_eq!(pipe.advance(&mut buf), Ok(())); + assert_eq!(pipe.advance(), Ok(())); // Server sends stream data bigger than cwnd. let send_buf1 = [0; 20000]; - assert_eq!(pipe.server.stream_send(0, &send_buf1, false), Ok(14085)); - assert_eq!(pipe.advance(&mut buf), Ok(())); + assert_eq!(pipe.server.stream_send(0, &send_buf1, false), Ok(12000)); + + testing::emit_flight(&mut pipe.server).ok(); // We can't create a new packet header because there is no room by cwnd. // app_limited should be false because we can't send more by cwnd. @@ -6857,8 +8742,6 @@ mod tests { #[test] fn app_limited_false_no_frame() { - let mut buf = [0; 65535]; - let mut config = Config::new(PROTOCOL_VERSION).unwrap(); config .set_application_protos(b"\x06proto1\x06proto2") @@ -6866,26 +8749,26 @@ mod tests { config.set_initial_max_data(50000); config.set_initial_max_stream_data_bidi_local(50000); config.set_initial_max_stream_data_bidi_remote(50000); - config.set_max_udp_payload_size(1405); + config.set_max_recv_udp_payload_size(1405); config.verify_peer(false); let mut pipe = testing::Pipe::with_client_config(&mut config).unwrap(); - - assert_eq!(pipe.handshake(&mut buf), Ok(())); + assert_eq!(pipe.handshake(), Ok(())); // Client sends stream data. assert_eq!(pipe.client.stream_send(0, b"a", true), Ok(1)); - assert_eq!(pipe.advance(&mut buf), Ok(())); + assert_eq!(pipe.advance(), Ok(())); // Server reads stream data. let mut b = [0; 15]; pipe.server.stream_recv(0, &mut b).unwrap(); - assert_eq!(pipe.advance(&mut buf), Ok(())); + assert_eq!(pipe.advance(), Ok(())); // Server sends stream data bigger than cwnd. let send_buf1 = [0; 20000]; - assert_eq!(pipe.server.stream_send(0, &send_buf1, false), Ok(14085)); - assert_eq!(pipe.advance(&mut buf), Ok(())); + assert_eq!(pipe.server.stream_send(0, &send_buf1, false), Ok(12000)); + + testing::emit_flight(&mut pipe.server).ok(); // We can't create a new packet header because there is no room by cwnd. // app_limited should be false because we can't send more by cwnd. @@ -6894,8 +8777,6 @@ mod tests { #[test] fn app_limited_false_no_header() { - let mut buf = [0; 65535]; - let mut config = Config::new(PROTOCOL_VERSION).unwrap(); config .set_application_protos(b"\x06proto1\x06proto2") @@ -6903,26 +8784,26 @@ mod tests { config.set_initial_max_data(50000); config.set_initial_max_stream_data_bidi_local(50000); config.set_initial_max_stream_data_bidi_remote(50000); - config.set_max_udp_payload_size(1406); + config.set_max_recv_udp_payload_size(1406); config.verify_peer(false); let mut pipe = testing::Pipe::with_client_config(&mut config).unwrap(); - - assert_eq!(pipe.handshake(&mut buf), Ok(())); + assert_eq!(pipe.handshake(), Ok(())); // Client sends stream data. assert_eq!(pipe.client.stream_send(0, b"a", true), Ok(1)); - assert_eq!(pipe.advance(&mut buf), Ok(())); + assert_eq!(pipe.advance(), Ok(())); // Server reads stream data. let mut b = [0; 15]; pipe.server.stream_recv(0, &mut b).unwrap(); - assert_eq!(pipe.advance(&mut buf), Ok(())); + assert_eq!(pipe.advance(), Ok(())); // Server sends stream data bigger than cwnd. let send_buf1 = [0; 20000]; - assert_eq!(pipe.server.stream_send(0, &send_buf1, false), Ok(14085)); - assert_eq!(pipe.advance(&mut buf), Ok(())); + assert_eq!(pipe.server.stream_send(0, &send_buf1, false), Ok(12000)); + + testing::emit_flight(&mut pipe.server).ok(); // We can't create a new frame because there is no room by cwnd. // app_limited should be false because we can't send more by cwnd. @@ -6930,12 +8811,46 @@ mod tests { } #[test] + fn app_limited_not_changed_on_no_new_frames() { + let mut config = Config::new(PROTOCOL_VERSION).unwrap(); + config + .set_application_protos(b"\x06proto1\x06proto2") + .unwrap(); + config.set_initial_max_data(50000); + config.set_initial_max_stream_data_bidi_local(50000); + config.set_initial_max_stream_data_bidi_remote(50000); + config.set_max_recv_udp_payload_size(1200); + config.verify_peer(false); + + let mut pipe = testing::Pipe::with_client_config(&mut config).unwrap(); + assert_eq!(pipe.handshake(), Ok(())); + + // Client sends stream data. + assert_eq!(pipe.client.stream_send(0, b"a", true), Ok(1)); + assert_eq!(pipe.advance(), Ok(())); + + // Server reads stream data. + let mut b = [0; 15]; + pipe.server.stream_recv(0, &mut b).unwrap(); + assert_eq!(pipe.advance(), Ok(())); + + // Client's app_limited is true because its bytes-in-flight + // is much smaller than the current cwnd. + assert_eq!(pipe.client.recovery.app_limited(), true); + + // Client has no new frames to send - returns Done. + assert_eq!(testing::emit_flight(&mut pipe.client), Err(Error::Done)); + + // Client's app_limited should remain the same. + assert_eq!(pipe.client.recovery.app_limited(), true); + } + + #[test] fn limit_ack_ranges() { let mut buf = [0; 65535]; let mut pipe = testing::Pipe::default().unwrap(); - - assert_eq!(pipe.handshake(&mut buf), Ok(())); + assert_eq!(pipe.handshake(), Ok(())); let epoch = packet::EPOCH_APPLICATION; @@ -7004,25 +8919,25 @@ mod tests { config.verify_peer(false); let mut pipe = testing::Pipe::with_config(&mut config).unwrap(); - assert_eq!(pipe.handshake(&mut buf), Ok(())); + assert_eq!(pipe.handshake(), Ok(())); assert_eq!(pipe.client.stream_send(0, b"a", false), Ok(1)); - assert_eq!(pipe.advance(&mut buf), Ok(())); + assert_eq!(pipe.advance(), Ok(())); assert_eq!(pipe.client.stream_send(4, b"a", false), Ok(1)); - assert_eq!(pipe.advance(&mut buf), Ok(())); + assert_eq!(pipe.advance(), Ok(())); assert_eq!(pipe.client.stream_send(8, b"a", false), Ok(1)); - assert_eq!(pipe.advance(&mut buf), Ok(())); + assert_eq!(pipe.advance(), Ok(())); assert_eq!(pipe.client.stream_send(12, b"a", false), Ok(1)); - assert_eq!(pipe.advance(&mut buf), Ok(())); + assert_eq!(pipe.advance(), Ok(())); assert_eq!(pipe.client.stream_send(16, b"a", false), Ok(1)); - assert_eq!(pipe.advance(&mut buf), Ok(())); + assert_eq!(pipe.advance(), Ok(())); assert_eq!(pipe.client.stream_send(20, b"a", false), Ok(1)); - assert_eq!(pipe.advance(&mut buf), Ok(())); + assert_eq!(pipe.advance(), Ok(())); let mut b = [0; 1]; @@ -7074,7 +8989,8 @@ mod tests { let mut off = 0; for _ in 1..=3 { - let len = pipe.server.send(&mut buf[..MAX_TEST_PACKET_SIZE]).unwrap(); + let (len, _) = + pipe.server.send(&mut buf[..MAX_TEST_PACKET_SIZE]).unwrap(); let frames = testing::decode_pkt(&mut pipe.client, &mut buf, len).unwrap(); @@ -7096,7 +9012,8 @@ mod tests { let mut off = 0; for _ in 1..=3 { - let len = pipe.server.send(&mut buf[..MAX_TEST_PACKET_SIZE]).unwrap(); + let (len, _) = + pipe.server.send(&mut buf[..MAX_TEST_PACKET_SIZE]).unwrap(); let frames = testing::decode_pkt(&mut pipe.client, &mut buf, len).unwrap(); @@ -7118,7 +9035,8 @@ mod tests { let mut off = 0; for _ in 1..=3 { - let len = pipe.server.send(&mut buf[..MAX_TEST_PACKET_SIZE]).unwrap(); + let (len, _) = + pipe.server.send(&mut buf[..MAX_TEST_PACKET_SIZE]).unwrap(); let frames = testing::decode_pkt(&mut pipe.client, &mut buf, len).unwrap(); @@ -7140,7 +9058,8 @@ mod tests { let mut off = 0; for _ in 1..=3 { - let len = pipe.server.send(&mut buf[..MAX_TEST_PACKET_SIZE]).unwrap(); + let (len, _) = + pipe.server.send(&mut buf[..MAX_TEST_PACKET_SIZE]).unwrap(); let frames = testing::decode_pkt(&mut pipe.client, &mut buf, len).unwrap(); @@ -7153,7 +9072,8 @@ mod tests { }) ); - let len = pipe.server.send(&mut buf[..MAX_TEST_PACKET_SIZE]).unwrap(); + let (len, _) = + pipe.server.send(&mut buf[..MAX_TEST_PACKET_SIZE]).unwrap(); let frames = testing::decode_pkt(&mut pipe.client, &mut buf, len).unwrap(); @@ -7176,7 +9096,8 @@ mod tests { let mut off = 0; for _ in 1..=3 { - let len = pipe.server.send(&mut buf[..MAX_TEST_PACKET_SIZE]).unwrap(); + let (len, _) = + pipe.server.send(&mut buf[..MAX_TEST_PACKET_SIZE]).unwrap(); let frames = testing::decode_pkt(&mut pipe.client, &mut buf, len).unwrap(); @@ -7224,19 +9145,19 @@ mod tests { config.verify_peer(false); let mut pipe = testing::Pipe::with_config(&mut config).unwrap(); - assert_eq!(pipe.handshake(&mut buf), Ok(())); + assert_eq!(pipe.handshake(), Ok(())); assert_eq!(pipe.client.stream_send(0, b"a", false), Ok(1)); - assert_eq!(pipe.advance(&mut buf), Ok(())); + assert_eq!(pipe.advance(), Ok(())); assert_eq!(pipe.client.stream_send(4, b"a", false), Ok(1)); - assert_eq!(pipe.advance(&mut buf), Ok(())); + assert_eq!(pipe.advance(), Ok(())); assert_eq!(pipe.client.stream_send(8, b"a", false), Ok(1)); - assert_eq!(pipe.advance(&mut buf), Ok(())); + assert_eq!(pipe.advance(), Ok(())); assert_eq!(pipe.client.stream_send(12, b"a", false), Ok(1)); - assert_eq!(pipe.advance(&mut buf), Ok(())); + assert_eq!(pipe.advance(), Ok(())); let mut b = [0; 1]; @@ -7260,7 +9181,7 @@ mod tests { assert_eq!(pipe.server.stream_priority(0, 20, true), Ok(())); // First is stream 8. - let len = pipe.server.send(&mut buf).unwrap(); + let (len, _) = pipe.server.send(&mut buf).unwrap(); let frames = testing::decode_pkt(&mut pipe.client, &mut buf, len).unwrap(); @@ -7274,7 +9195,7 @@ mod tests { ); // Then is stream 0. - let len = pipe.server.send(&mut buf).unwrap(); + let (len, _) = pipe.server.send(&mut buf).unwrap(); let frames = testing::decode_pkt(&mut pipe.client, &mut buf, len).unwrap(); @@ -7288,7 +9209,7 @@ mod tests { ); // Then are stream 12 and 4, with the same priority. - let len = pipe.server.send(&mut buf).unwrap(); + let (len, _) = pipe.server.send(&mut buf).unwrap(); let frames = testing::decode_pkt(&mut pipe.client, &mut buf, len).unwrap(); @@ -7301,7 +9222,7 @@ mod tests { }) ); - let len = pipe.server.send(&mut buf).unwrap(); + let (len, _) = pipe.server.send(&mut buf).unwrap(); let frames = testing::decode_pkt(&mut pipe.client, &mut buf, len).unwrap(); @@ -7318,16 +9239,151 @@ mod tests { } #[test] + /// Tests that streams and datagrams are correctly scheduled. + fn stream_datagram_priority() { + // Limit 1-RTT packet size to avoid congestion control interference. + const MAX_TEST_PACKET_SIZE: usize = 540; + + let mut buf = [0; 65535]; + + let mut config = Config::new(crate::PROTOCOL_VERSION).unwrap(); + config + .load_cert_chain_from_pem_file("examples/cert.crt") + .unwrap(); + config + .load_priv_key_from_pem_file("examples/cert.key") + .unwrap(); + config + .set_application_protos(b"\x06proto1\x06proto2") + .unwrap(); + config.set_initial_max_data(1_000_000); + config.set_initial_max_stream_data_bidi_local(1_000_000); + config.set_initial_max_stream_data_bidi_remote(1_000_000); + config.set_initial_max_stream_data_uni(0); + config.set_initial_max_streams_bidi(100); + config.set_initial_max_streams_uni(0); + config.enable_dgram(true, 10, 10); + config.verify_peer(false); + + let mut pipe = testing::Pipe::with_config(&mut config).unwrap(); + assert_eq!(pipe.handshake(), Ok(())); + + assert_eq!(pipe.client.stream_send(0, b"a", false), Ok(1)); + assert_eq!(pipe.advance(), Ok(())); + + assert_eq!(pipe.client.stream_send(4, b"a", false), Ok(1)); + assert_eq!(pipe.advance(), Ok(())); + + let mut b = [0; 1]; + + let out = [b'b'; 500]; + + // Server prioritizes Stream 0 and 4 with the same urgency with + // incremental, meaning the frames should be sent in round-robin + // fashion. It also sends DATAGRAMS which are always interleaved with + // STREAM frames. So we'll expect a mix of frame types regardless + // of the order that the application writes things in. + + pipe.server.stream_recv(0, &mut b).unwrap(); + assert_eq!(pipe.server.stream_priority(0, 255, true), Ok(())); + pipe.server.stream_send(0, &out, false).unwrap(); + pipe.server.stream_send(0, &out, false).unwrap(); + pipe.server.stream_send(0, &out, false).unwrap(); + + assert_eq!(pipe.server.stream_priority(4, 255, true), Ok(())); + pipe.server.stream_send(4, &out, false).unwrap(); + pipe.server.stream_send(4, &out, false).unwrap(); + pipe.server.stream_send(4, &out, false).unwrap(); + + for _ in 1..=6 { + assert_eq!(pipe.server.dgram_send(&out), Ok(())); + } + + let mut off_0 = 0; + let mut off_4 = 0; + + for _ in 1..=3 { + // DATAGRAM + let (len, _) = + pipe.server.send(&mut buf[..MAX_TEST_PACKET_SIZE]).unwrap(); + + let frames = + testing::decode_pkt(&mut pipe.client, &mut buf, len).unwrap(); + let mut frame_iter = frames.iter(); + + assert_eq!(frame_iter.next().unwrap(), &frame::Frame::Datagram { + data: out.into(), + }); + assert_eq!(frame_iter.next(), None); + + // STREAM 0 + let (len, _) = + pipe.server.send(&mut buf[..MAX_TEST_PACKET_SIZE]).unwrap(); + + let frames = + testing::decode_pkt(&mut pipe.client, &mut buf, len).unwrap(); + let mut frame_iter = frames.iter(); + let stream = frame_iter.next().unwrap(); + + assert_eq!(stream, &frame::Frame::Stream { + stream_id: 0, + data: stream::RangeBuf::from(&out, off_0, false), + }); + + off_0 = match stream { + frame::Frame::Stream { data, .. } => data.max_off(), + + _ => unreachable!(), + }; + assert_eq!(frame_iter.next(), None); + + // DATAGRAM + let (len, _) = + pipe.server.send(&mut buf[..MAX_TEST_PACKET_SIZE]).unwrap(); + + let frames = + testing::decode_pkt(&mut pipe.client, &mut buf, len).unwrap(); + let mut frame_iter = frames.iter(); + + assert_eq!(frame_iter.next().unwrap(), &frame::Frame::Datagram { + data: out.into(), + }); + assert_eq!(frame_iter.next(), None); + + // STREAM 4 + let (len, _) = + pipe.server.send(&mut buf[..MAX_TEST_PACKET_SIZE]).unwrap(); + + let frames = + testing::decode_pkt(&mut pipe.client, &mut buf, len).unwrap(); + let mut frame_iter = frames.iter(); + let stream = frame_iter.next().unwrap(); + + assert_eq!(stream, &frame::Frame::Stream { + stream_id: 4, + data: stream::RangeBuf::from(&out, off_4, false), + }); + + off_4 = match stream { + frame::Frame::Stream { data, .. } => data.max_off(), + + _ => unreachable!(), + }; + assert_eq!(frame_iter.next(), None); + } + } + + #[test] /// Tests that old data is retransmitted on PTO. fn early_retransmit() { let mut buf = [0; 65535]; let mut pipe = testing::Pipe::default().unwrap(); - assert_eq!(pipe.handshake(&mut buf), Ok(())); + assert_eq!(pipe.handshake(), Ok(())); // Client sends stream data. assert_eq!(pipe.client.stream_send(0, b"a", false), Ok(1)); - assert_eq!(pipe.advance(&mut buf), Ok(())); + assert_eq!(pipe.advance(), Ok(())); // Client sends more stream data, but packet is lost assert_eq!(pipe.client.stream_send(4, b"b", false), Ok(1)); @@ -7343,7 +9399,7 @@ mod tests { assert_eq!(pipe.client.recovery.loss_probes[epoch], 1); // Client retransmits stream data in PTO probe. - let len = pipe.client.send(&mut buf).unwrap(); + let (len, _) = pipe.client.send(&mut buf).unwrap(); assert_eq!(pipe.client.recovery.loss_probes[epoch], 0); let frames = @@ -7364,6 +9420,83 @@ mod tests { } #[test] + /// Tests that PTO probe packets are not coalesced together. + fn dont_coalesce_probes() { + let mut buf = [0; 65535]; + + let mut pipe = testing::Pipe::default().unwrap(); + + // Client sends Initial packet. + let (len, _) = pipe.client.send(&mut buf).unwrap(); + assert_eq!(len, 1200); + + // Wait for PTO to expire. + let timer = pipe.client.timeout().unwrap(); + std::thread::sleep(timer + time::Duration::from_millis(1)); + + pipe.client.on_timeout(); + + let epoch = packet::EPOCH_INITIAL; + assert_eq!(pipe.client.recovery.loss_probes[epoch], 1); + + // Client sends PTO probe. + let (len, _) = pipe.client.send(&mut buf).unwrap(); + assert_eq!(len, 1200); + assert_eq!(pipe.client.recovery.loss_probes[epoch], 0); + + // Wait for PTO to expire. + let timer = pipe.client.timeout().unwrap(); + std::thread::sleep(timer + time::Duration::from_millis(1)); + + pipe.client.on_timeout(); + + assert_eq!(pipe.client.recovery.loss_probes[epoch], 2); + + // Client sends first PTO probe. + let (len, _) = pipe.client.send(&mut buf).unwrap(); + assert_eq!(len, 1200); + assert_eq!(pipe.client.recovery.loss_probes[epoch], 1); + + // Client sends second PTO probe. + let (len, _) = pipe.client.send(&mut buf).unwrap(); + assert_eq!(len, 1200); + assert_eq!(pipe.client.recovery.loss_probes[epoch], 0); + } + + #[test] + fn coalesce_padding_short() { + let mut buf = [0; 65535]; + + let mut pipe = testing::Pipe::default().unwrap(); + + // Client sends first flight. + let (len, _) = pipe.client.send(&mut buf).unwrap(); + assert_eq!(len, MIN_CLIENT_INITIAL_LEN); + assert_eq!(pipe.server_recv(&mut buf[..len]), Ok(len)); + + // Server sends first flight. + let (len, _) = pipe.server.send(&mut buf).unwrap(); + assert_eq!(len, MIN_CLIENT_INITIAL_LEN); + assert_eq!(pipe.client_recv(&mut buf[..len]), Ok(len)); + + let (len, _) = pipe.server.send(&mut buf).unwrap(); + assert_eq!(pipe.client_recv(&mut buf[..len]), Ok(len)); + + // Client sends stream data. + assert_eq!(pipe.client.is_established(), true); + assert_eq!(pipe.client.stream_send(4, b"hello", true), Ok(5)); + + // Client sends second flight. + let (len, _) = pipe.client.send(&mut buf).unwrap(); + assert_eq!(len, MIN_CLIENT_INITIAL_LEN); + assert_eq!(pipe.server_recv(&mut buf[..len]), Ok(len)); + + // None of the sent packets should have been dropped. + assert_eq!(pipe.client.sent_count, pipe.server.recv_count); + assert_eq!(pipe.server.sent_count, pipe.client.recv_count); + } + + #[test] /// Tests that client avoids handshake deadlock by arming PTO. fn handshake_anti_deadlock() { let mut buf = [0; 65535]; @@ -7387,13 +9520,13 @@ mod tests { assert_eq!(pipe.server.handshake_status().peer_verified_address, true); // Client sends padded Initial. - let len = pipe.client.send(&mut buf).unwrap(); + let (len, _) = pipe.client.send(&mut buf).unwrap(); assert_eq!(len, 1200); // Server receives client's Initial and sends own Initial and Handshake // until it's blocked by the anti-amplification limit. - let len = testing::recv_send(&mut pipe.server, &mut buf, len).unwrap(); - assert_eq!(pipe.server.send(&mut buf[len..]), Err(Error::Done)); + assert_eq!(pipe.server_recv(&mut buf[..len]), Ok(len)); + let flight = testing::emit_flight(&mut pipe.server).unwrap(); assert_eq!(pipe.client.handshake_status().has_handshake_keys, false); assert_eq!(pipe.client.handshake_status().peer_verified_address, false); @@ -7402,7 +9535,8 @@ mod tests { // Client receives the server flight and sends Handshake ACK, but it is // lost. - assert!(testing::recv_send(&mut pipe.client, &mut buf, len).is_ok()); + testing::process_flight(&mut pipe.client, flight).unwrap(); + testing::emit_flight(&mut pipe.client).unwrap(); assert_eq!(pipe.client.handshake_status().has_handshake_keys, true); assert_eq!(pipe.client.handshake_status().peer_verified_address, false); @@ -7422,44 +9556,39 @@ mod tests { let mut pipe = testing::Pipe::default().unwrap(); // Client sends padded Initial. - let len = pipe.client.send(&mut buf).unwrap(); + let (len, _) = pipe.client.send(&mut buf).unwrap(); assert_eq!(len, 1200); // Server receives client's Initial and sends own Initial and Handshake. - let len = testing::recv_send(&mut pipe.server, &mut buf, len).unwrap(); - assert_eq!(pipe.client.recv(&mut buf[..len]), Ok(len)); + assert_eq!(pipe.server_recv(&mut buf[..len]), Ok(len)); - // Client sends Initial packet with ACK. - let len = pipe.client.send(&mut buf).unwrap(); + let flight = testing::emit_flight(&mut pipe.server).unwrap(); + testing::process_flight(&mut pipe.client, flight).unwrap(); - let hdr = Header::from_slice(&mut buf[..len], 0).unwrap(); - assert_eq!(hdr.ty, Type::Initial); + // Client sends Initial packet with ACK. + let (ty, len) = pipe.client.send_single(&mut buf, false).unwrap(); + assert_eq!(ty, Type::Initial); - assert_eq!(pipe.server.recv(&mut buf[..len]), Ok(len)); + assert_eq!(pipe.server_recv(&mut buf[..len]), Ok(len)); // Client sends Handshake packet. - let len = pipe.client.send(&mut buf).unwrap(); + let (ty, len) = pipe.client.send_single(&mut buf, false).unwrap(); + assert_eq!(ty, Type::Handshake); - let hdr = Header::from_slice(&mut buf[..len], 0).unwrap(); - assert_eq!(hdr.ty, Type::Handshake); - - // Packet type is corrupted to Initial.. + // Packet type is corrupted to Initial. buf[0] &= !(0x20); let hdr = Header::from_slice(&mut buf[..len], 0).unwrap(); assert_eq!(hdr.ty, Type::Initial); // Server receives corrupted packet without returning an error. - assert_eq!(pipe.server.recv(&mut buf[..len]), Ok(len)); + assert_eq!(pipe.server_recv(&mut buf[..len]), Ok(len)); } #[test] fn dgram_send_fails_invalidstate() { - let mut buf = [0; 65535]; - let mut pipe = testing::Pipe::default().unwrap(); - - assert_eq!(pipe.handshake(&mut buf), Ok(())); + assert_eq!(pipe.handshake(), Ok(())); assert_eq!( pipe.client.dgram_send(b"hello, world"), @@ -7489,13 +9618,11 @@ mod tests { config.set_initial_max_streams_bidi(3); config.set_initial_max_streams_uni(3); config.enable_dgram(true, 1000, 1000); - config.set_max_udp_payload_size(1200); + config.set_max_recv_udp_payload_size(1200); config.verify_peer(false); let mut pipe = testing::Pipe::with_config(&mut config).unwrap(); - - assert_eq!(pipe.handshake(&mut buf), Ok(())); - assert_eq!(pipe.advance(&mut buf), Ok(())); + assert_eq!(pipe.handshake(), Ok(())); for _ in 0..1000 { assert_eq!(pipe.client.dgram_send(&send_buf), Ok(())); @@ -7504,14 +9631,19 @@ mod tests { assert!(!pipe.client.recovery.app_limited()); assert_eq!(pipe.client.dgram_send_queue.byte_size(), 1_000_000); - let len = pipe.client.send(&mut buf).unwrap(); + let (len, _) = pipe.client.send(&mut buf).unwrap(); assert_ne!(pipe.client.dgram_send_queue.byte_size(), 0); assert_ne!(pipe.client.dgram_send_queue.byte_size(), 1_000_000); assert!(!pipe.client.recovery.app_limited()); - testing::recv_send(&mut pipe.client, &mut buf, len).unwrap(); - testing::recv_send(&mut pipe.server, &mut buf, len).unwrap(); + assert_eq!(pipe.server_recv(&mut buf[..len]), Ok(len)); + + let flight = testing::emit_flight(&mut pipe.client).unwrap(); + testing::process_flight(&mut pipe.server, flight).unwrap(); + + let flight = testing::emit_flight(&mut pipe.server).unwrap(); + testing::process_flight(&mut pipe.client, flight).unwrap(); assert_ne!(pipe.client.dgram_send_queue.byte_size(), 0); assert_ne!(pipe.client.dgram_send_queue.byte_size(), 1_000_000); @@ -7543,12 +9675,11 @@ mod tests { config.verify_peer(false); let mut pipe = testing::Pipe::with_config(&mut config).unwrap(); - - assert_eq!(pipe.handshake(&mut buf), Ok(())); + assert_eq!(pipe.handshake(), Ok(())); assert_eq!(pipe.client.dgram_send(b"hello, world"), Ok(())); - assert_eq!(pipe.advance(&mut buf), Ok(())); + assert_eq!(pipe.advance(), Ok(())); let result1 = pipe.server.dgram_recv(&mut buf); assert_eq!(result1, Ok(12)); @@ -7581,17 +9712,34 @@ mod tests { config.verify_peer(false); let mut pipe = testing::Pipe::with_config(&mut config).unwrap(); + assert_eq!(pipe.handshake(), Ok(())); - assert_eq!(pipe.handshake(&mut buf), Ok(())); + assert_eq!(pipe.client.dgram_send_queue_len(), 0); + assert_eq!(pipe.client.dgram_send_queue_byte_size(), 0); assert_eq!(pipe.client.dgram_send(b"hello, world"), Ok(())); assert_eq!(pipe.client.dgram_send(b"ciao, mondo"), Ok(())); assert_eq!(pipe.client.dgram_send(b"hola, mundo"), Ok(())); + assert_eq!(pipe.client.dgram_send_queue_byte_size(), 34); + pipe.client .dgram_purge_outgoing(|d: &[u8]| -> bool { d[0] == b'c' }); - assert_eq!(pipe.advance(&mut buf), Ok(())); + assert_eq!(pipe.client.dgram_send_queue_len(), 2); + assert_eq!(pipe.client.dgram_send_queue_byte_size(), 23); + + // Before packets exchanged, no dgrams on server receive side. + assert_eq!(pipe.server.dgram_recv_queue_len(), 0); + + assert_eq!(pipe.advance(), Ok(())); + + // After packets exchanged, no dgrams on client send side. + assert_eq!(pipe.client.dgram_send_queue_len(), 0); + assert_eq!(pipe.client.dgram_send_queue_byte_size(), 0); + + assert_eq!(pipe.server.dgram_recv_queue_len(), 2); + assert_eq!(pipe.server.dgram_recv_queue_byte_size(), 23); let result1 = pipe.server.dgram_recv(&mut buf); assert_eq!(result1, Ok(12)); @@ -7605,6 +9753,9 @@ mod tests { let result3 = pipe.server.dgram_recv(&mut buf); assert_eq!(result3, Err(Error::Done)); + + assert_eq!(pipe.server.dgram_recv_queue_len(), 0); + assert_eq!(pipe.server.dgram_recv_queue_byte_size(), 0); } #[test] @@ -7631,14 +9782,13 @@ mod tests { config.verify_peer(false); let mut pipe = testing::Pipe::with_config(&mut config).unwrap(); - - assert_eq!(pipe.advance(&mut buf), Ok(())); + assert_eq!(pipe.handshake(), Ok(())); assert_eq!(pipe.client.dgram_send(b"hello, world"), Ok(())); assert_eq!(pipe.client.dgram_send(b"ciao, mondo"), Ok(())); assert_eq!(pipe.client.dgram_send(b"hola, mundo"), Err(Error::Done)); - assert_eq!(pipe.advance(&mut buf), Ok(())); + assert_eq!(pipe.advance(), Ok(())); let result1 = pipe.server.dgram_recv(&mut buf); assert_eq!(result1, Ok(12)); @@ -7675,18 +9825,17 @@ mod tests { config.set_initial_max_streams_bidi(3); config.set_initial_max_streams_uni(3); config.enable_dgram(true, 2, 10); - config.set_max_udp_payload_size(1200); + config.set_max_recv_udp_payload_size(1200); config.verify_peer(false); let mut pipe = testing::Pipe::with_config(&mut config).unwrap(); - - assert_eq!(pipe.handshake(&mut buf), Ok(())); + assert_eq!(pipe.handshake(), Ok(())); assert_eq!(pipe.client.dgram_send(b"hello, world"), Ok(())); assert_eq!(pipe.client.dgram_send(b"ciao, mondo"), Ok(())); assert_eq!(pipe.client.dgram_send(b"hola, mundo"), Ok(())); - assert_eq!(pipe.advance(&mut buf), Ok(())); + assert_eq!(pipe.advance(), Ok(())); let result1 = pipe.server.dgram_recv(&mut buf); assert_eq!(result1, Ok(11)); @@ -7723,7 +9872,7 @@ mod tests { config.set_initial_max_streams_bidi(3); config.set_initial_max_streams_uni(3); config.enable_dgram(true, 10, 10); - config.set_max_udp_payload_size(1452); + config.set_max_recv_udp_payload_size(1452); config.verify_peer(false); let mut pipe = testing::Pipe::with_config(&mut config).unwrap(); @@ -7731,15 +9880,19 @@ mod tests { // Before handshake (before peer settings) we don't know max dgram size assert_eq!(pipe.client.dgram_max_writable_len(), None); - assert_eq!(pipe.handshake(&mut buf), Ok(())); + assert_eq!(pipe.handshake(), Ok(())); let max_dgram_size = pipe.client.dgram_max_writable_len().unwrap(); + // Tests use a 16-byte connection ID, so the max datagram frame payload + // size is (1200 byte-long packet - 40 bytes overhead) + assert_eq!(max_dgram_size, 1160); + let dgram_packet: Vec<u8> = vec![42; max_dgram_size]; assert_eq!(pipe.client.dgram_send(&dgram_packet), Ok(())); - assert_eq!(pipe.advance(&mut buf), Ok(())); + assert_eq!(pipe.advance(), Ok(())); let result1 = pipe.server.dgram_recv(&mut buf); assert_eq!(result1, Ok(max_dgram_size)); @@ -7747,15 +9900,314 @@ mod tests { let result2 = pipe.server.dgram_recv(&mut buf); assert_eq!(result2, Err(Error::Done)); } + + #[test] + /// Tests is_readable check. + fn is_readable() { + let mut buf = [0; 65535]; + + let mut config = Config::new(crate::PROTOCOL_VERSION).unwrap(); + config + .load_cert_chain_from_pem_file("examples/cert.crt") + .unwrap(); + config + .load_priv_key_from_pem_file("examples/cert.key") + .unwrap(); + config + .set_application_protos(b"\x06proto1\x06proto2") + .unwrap(); + config.set_initial_max_data(30); + config.set_initial_max_stream_data_bidi_local(15); + config.set_initial_max_stream_data_bidi_remote(15); + config.set_initial_max_stream_data_uni(10); + config.set_initial_max_streams_bidi(3); + config.set_initial_max_streams_uni(3); + config.enable_dgram(true, 10, 10); + config.set_max_recv_udp_payload_size(1452); + config.verify_peer(false); + + let mut pipe = testing::Pipe::with_config(&mut config).unwrap(); + assert_eq!(pipe.handshake(), Ok(())); + + // No readable data. + assert_eq!(pipe.client.is_readable(), false); + assert_eq!(pipe.server.is_readable(), false); + + assert_eq!(pipe.client.stream_send(4, b"aaaaa", false), Ok(5)); + assert_eq!(pipe.advance(), Ok(())); + + // Server received stream. + assert_eq!(pipe.client.is_readable(), false); + assert_eq!(pipe.server.is_readable(), true); + + assert_eq!( + pipe.server.stream_send(4, b"aaaaaaaaaaaaaaa", false), + Ok(15) + ); + assert_eq!(pipe.advance(), Ok(())); + + // Client received stream. + assert_eq!(pipe.client.is_readable(), true); + assert_eq!(pipe.server.is_readable(), true); + + // Client drains stream. + let mut b = [0; 15]; + pipe.client.stream_recv(4, &mut b).unwrap(); + assert_eq!(pipe.advance(), Ok(())); + + assert_eq!(pipe.client.is_readable(), false); + assert_eq!(pipe.server.is_readable(), true); + + // Server shuts down stream. + assert_eq!(pipe.server.stream_shutdown(4, Shutdown::Read, 0), Ok(())); + assert_eq!(pipe.server.is_readable(), false); + + // Server received dgram. + assert_eq!(pipe.client.dgram_send(b"dddddddddddddd"), Ok(())); + assert_eq!(pipe.advance(), Ok(())); + + assert_eq!(pipe.client.is_readable(), false); + assert_eq!(pipe.server.is_readable(), true); + + // Client received dgram. + assert_eq!(pipe.server.dgram_send(b"dddddddddddddd"), Ok(())); + assert_eq!(pipe.advance(), Ok(())); + + assert_eq!(pipe.client.is_readable(), true); + assert_eq!(pipe.server.is_readable(), true); + + // Drain the dgram queues. + let r = pipe.server.dgram_recv(&mut buf); + assert_eq!(r, Ok(14)); + assert_eq!(pipe.server.is_readable(), false); + + let r = pipe.client.dgram_recv(&mut buf); + assert_eq!(r, Ok(14)); + assert_eq!(pipe.client.is_readable(), false); + } + + #[test] + fn close() { + let mut buf = [0; 65535]; + + let mut pipe = testing::Pipe::default().unwrap(); + assert_eq!(pipe.handshake(), Ok(())); + + assert_eq!(pipe.client.close(false, 0x1234, b"hello?"), Ok(())); + + assert_eq!( + pipe.client.close(false, 0x4321, b"hello?"), + Err(Error::Done) + ); + + let (len, _) = pipe.client.send(&mut buf).unwrap(); + + let frames = + testing::decode_pkt(&mut pipe.server, &mut buf, len).unwrap(); + + assert_eq!( + frames.iter().next(), + Some(&frame::Frame::ConnectionClose { + error_code: 0x1234, + frame_type: 0, + reason: b"hello?".to_vec(), + }) + ); + } + + #[test] + fn app_close() { + let mut buf = [0; 65535]; + + let mut pipe = testing::Pipe::default().unwrap(); + assert_eq!(pipe.handshake(), Ok(())); + + assert_eq!(pipe.client.close(true, 0x1234, b"hello!"), Ok(())); + + assert_eq!(pipe.client.close(true, 0x4321, b"hello!"), Err(Error::Done)); + + let (len, _) = pipe.client.send(&mut buf).unwrap(); + + let frames = + testing::decode_pkt(&mut pipe.server, &mut buf, len).unwrap(); + + assert_eq!( + frames.iter().next(), + Some(&frame::Frame::ApplicationClose { + error_code: 0x1234, + reason: b"hello!".to_vec(), + }) + ); + } + + #[test] + fn peer_error() { + let mut pipe = testing::Pipe::default().unwrap(); + assert_eq!(pipe.handshake(), Ok(())); + + assert_eq!(pipe.server.close(false, 0x1234, b"hello?"), Ok(())); + assert_eq!(pipe.advance(), Ok(())); + + assert_eq!( + pipe.client.peer_error(), + Some(&ConnectionError { + is_app: false, + error_code: 0x1234u64, + reason: b"hello?".to_vec() + }) + ); + } + + #[test] + fn app_peer_error() { + let mut pipe = testing::Pipe::default().unwrap(); + assert_eq!(pipe.handshake(), Ok(())); + + assert_eq!(pipe.server.close(true, 0x1234, b"hello!"), Ok(())); + assert_eq!(pipe.advance(), Ok(())); + + assert_eq!( + pipe.client.peer_error(), + Some(&ConnectionError { + is_app: true, + error_code: 0x1234u64, + reason: b"hello!".to_vec() + }) + ); + } + + #[test] + fn update_max_datagram_size() { + let mut client_scid = [0; 16]; + rand::rand_bytes(&mut client_scid[..]); + let client_scid = ConnectionId::from_ref(&client_scid); + let client_addr = "127.0.0.1:1234".parse().unwrap(); + + let mut server_scid = [0; 16]; + rand::rand_bytes(&mut server_scid[..]); + let server_scid = ConnectionId::from_ref(&server_scid); + let server_addr = "127.0.0.1:4321".parse().unwrap(); + + let mut client_config = Config::new(crate::PROTOCOL_VERSION).unwrap(); + client_config + .set_application_protos(b"\x06proto1\x06proto2") + .unwrap(); + client_config.set_max_recv_udp_payload_size(1200); + + let mut server_config = Config::new(crate::PROTOCOL_VERSION).unwrap(); + server_config + .load_cert_chain_from_pem_file("examples/cert.crt") + .unwrap(); + server_config + .load_priv_key_from_pem_file("examples/cert.key") + .unwrap(); + server_config + .set_application_protos(b"\x06proto1\x06proto2") + .unwrap(); + server_config.verify_peer(false); + server_config + .set_application_protos(b"\x06proto1\x06proto2") + .unwrap(); + // Larger than the client + server_config.set_max_send_udp_payload_size(1500); + + let mut pipe = testing::Pipe { + client: connect( + Some("quic.tech"), + &client_scid, + client_addr, + &mut client_config, + ) + .unwrap(), + server: accept(&server_scid, None, server_addr, &mut server_config) + .unwrap(), + }; + + // Before handshake + assert_eq!(pipe.server.recovery.max_datagram_size(), 1500); + + assert_eq!(pipe.handshake(), Ok(())); + + // After handshake, max_datagram_size should match to client's + // max_recv_udp_payload_size which is smaller + assert_eq!(pipe.server.recovery.max_datagram_size(), 1200); + assert_eq!(pipe.server.recovery.cwnd(), 12000); + } + + #[test] + /// Tests that connection-level send capacity decreases as more stream data + /// is buffered. + fn send_capacity() { + let mut buf = [0; 65535]; + + let mut config = Config::new(crate::PROTOCOL_VERSION).unwrap(); + config + .load_cert_chain_from_pem_file("examples/cert.crt") + .unwrap(); + config + .load_priv_key_from_pem_file("examples/cert.key") + .unwrap(); + config + .set_application_protos(b"\x06proto1\x06proto2") + .unwrap(); + config.set_initial_max_data(100000); + config.set_initial_max_stream_data_bidi_local(10000); + config.set_initial_max_stream_data_bidi_remote(10000); + config.set_initial_max_streams_bidi(10); + config.verify_peer(false); + + let mut pipe = testing::Pipe::with_config(&mut config).unwrap(); + assert_eq!(pipe.handshake(), Ok(())); + + assert_eq!(pipe.client.stream_send(0, b"hello!", true), Ok(6)); + assert_eq!(pipe.advance(), Ok(())); + + assert_eq!(pipe.client.stream_send(4, b"hello!", true), Ok(6)); + assert_eq!(pipe.advance(), Ok(())); + + assert_eq!(pipe.client.stream_send(8, b"hello!", true), Ok(6)); + assert_eq!(pipe.advance(), Ok(())); + + assert_eq!(pipe.client.stream_send(12, b"hello!", true), Ok(6)); + assert_eq!(pipe.advance(), Ok(())); + + let mut r = pipe.server.readable().collect::<Vec<u64>>(); + assert_eq!(r.len(), 4); + + r.sort(); + + assert_eq!(r, [0, 4, 8, 12]); + + assert_eq!(pipe.server.stream_recv(0, &mut buf), Ok((6, true))); + assert_eq!(pipe.server.stream_recv(4, &mut buf), Ok((6, true))); + assert_eq!(pipe.server.stream_recv(8, &mut buf), Ok((6, true))); + assert_eq!(pipe.server.stream_recv(12, &mut buf), Ok((6, true))); + + assert_eq!(pipe.server.tx_cap, 12000); + + assert_eq!(pipe.server.stream_send(0, &buf[..5000], false), Ok(5000)); + assert_eq!(pipe.server.stream_send(4, &buf[..5000], false), Ok(5000)); + assert_eq!(pipe.server.stream_send(8, &buf[..5000], false), Ok(2000)); + + // No more connection send capacity. + assert_eq!(pipe.server.stream_send(12, &buf[..5000], false), Ok(0)); + assert_eq!(pipe.server.tx_cap, 0); + + assert_eq!(pipe.advance(), Ok(())); + } } +pub use crate::packet::ConnectionId; pub use crate::packet::Header; pub use crate::packet::Type; + pub use crate::recovery::CongestionControlAlgorithm; + pub use crate::stream::StreamIter; mod crypto; mod dgram; +#[cfg(feature = "ffi")] mod ffi; mod frame; pub mod h3; diff --git a/src/octets.rs b/src/octets.rs index 2b36707..3983667 100644 --- a/src/octets.rs +++ b/src/octets.rs @@ -183,18 +183,15 @@ impl<'a> Octets<'a> { return Err(BufferTooShortError); } - let mut vec = self.get_bytes(len)?.to_vec(); + let out = match len { + 1 => u64::from(self.get_u8()?), - // Mask the 2 most significant bits to remove the encoded length. - vec[0] &= 0x3f; + 2 => u64::from(self.get_u16()? & 0x3fff), - let mut b = OctetsMut::with_slice(&mut vec); + 4 => u64::from(self.get_u32()? & 0x3fffffff), + + 8 => self.get_u64()? & 0x3fffffffffffffff, - let out = match len { - 1 => u64::from(b.get_u8()?), - 2 => u64::from(b.get_u16()?), - 4 => u64::from(b.get_u32()?), - 8 => b.get_u64()?, _ => unreachable!(), }; @@ -275,6 +272,17 @@ impl<'a> Octets<'a> { Ok(&self.buf[cap - len..]) } + /// Advances the buffer's offset. + pub fn skip(&mut self, skip: usize) -> Result<()> { + if skip > self.cap() { + return Err(BufferTooShortError); + } + + self.off += skip; + + Ok(()) + } + /// Returns the remaining capacity in the buffer. pub fn cap(&self) -> usize { self.buf.len() - self.off @@ -402,18 +410,15 @@ impl<'a> OctetsMut<'a> { return Err(BufferTooShortError); } - let mut vec = self.get_bytes(len)?.to_vec(); + let out = match len { + 1 => u64::from(self.get_u8()?), - // Mask the 2 most significant bits to remove the encoded length. - vec[0] &= 0x3f; + 2 => u64::from(self.get_u16()? & 0x3fff), - let mut b = OctetsMut::with_slice(&mut vec); + 4 => u64::from(self.get_u32()? & 0x3fffffff), + + 8 => self.get_u64()? & 0x3fffffffffffffff, - let out = match len { - 1 => u64::from(b.get_u8()?), - 2 => u64::from(b.get_u16()?), - 4 => u64::from(b.get_u32()?), - 8 => b.get_u64()?, _ => unreachable!(), }; @@ -603,6 +608,17 @@ impl<'a> OctetsMut<'a> { Ok(&mut self.buf[cap - len..]) } + /// Advances the buffer's offset. + pub fn skip(&mut self, skip: usize) -> Result<()> { + if skip > self.cap() { + return Err(BufferTooShortError); + } + + self.off += skip; + + Ok(()) + } + /// Returns the remaining capacity in the buffer. pub fn cap(&self) -> usize { self.buf.len() - self.off diff --git a/src/packet.rs b/src/packet.rs index 0534df5..e6180f2 100644 --- a/src/packet.rs +++ b/src/packet.rs @@ -129,9 +129,117 @@ impl Type { } } +/// A QUIC connection ID. +pub struct ConnectionId<'a>(ConnectionIdInner<'a>); + +enum ConnectionIdInner<'a> { + Vec(Vec<u8>), + Ref(&'a [u8]), +} + +impl<'a> ConnectionId<'a> { + /// Creates a new connection ID from the given vector. + #[inline] + pub const fn from_vec(cid: Vec<u8>) -> Self { + Self(ConnectionIdInner::Vec(cid)) + } + + /// Creates a new connection ID from the given slice. + #[inline] + pub const fn from_ref(cid: &'a [u8]) -> Self { + Self(ConnectionIdInner::Ref(cid)) + } + + /// Returns a new owning connection ID from the given existing one. + #[inline] + pub fn into_owned(self) -> ConnectionId<'static> { + ConnectionId::from_vec(self.into()) + } +} + +impl<'a> Default for ConnectionId<'a> { + #[inline] + fn default() -> Self { + Self::from_vec(Vec::new()) + } +} + +impl<'a> From<Vec<u8>> for ConnectionId<'a> { + #[inline] + fn from(v: Vec<u8>) -> Self { + Self::from_vec(v) + } +} + +impl<'a> From<ConnectionId<'a>> for Vec<u8> { + #[inline] + fn from(id: ConnectionId<'a>) -> Self { + match id.0 { + ConnectionIdInner::Vec(cid) => cid, + ConnectionIdInner::Ref(cid) => cid.to_vec(), + } + } +} + +impl<'a> PartialEq for ConnectionId<'a> { + #[inline] + fn eq(&self, other: &Self) -> bool { + self.as_ref() == other.as_ref() + } +} + +impl<'a> Eq for ConnectionId<'a> {} + +impl<'a> AsRef<[u8]> for ConnectionId<'a> { + #[inline] + fn as_ref(&self) -> &[u8] { + match &self.0 { + ConnectionIdInner::Vec(v) => v.as_ref(), + ConnectionIdInner::Ref(v) => v, + } + } +} + +impl<'a> std::hash::Hash for ConnectionId<'a> { + #[inline] + fn hash<H: std::hash::Hasher>(&self, state: &mut H) { + self.as_ref().hash(state); + } +} + +impl<'a> std::ops::Deref for ConnectionId<'a> { + type Target = [u8]; + + #[inline] + fn deref(&self) -> &[u8] { + match &self.0 { + ConnectionIdInner::Vec(v) => v.as_ref(), + ConnectionIdInner::Ref(v) => v, + } + } +} + +impl<'a> Clone for ConnectionId<'a> { + #[inline] + fn clone(&self) -> Self { + Self::from_vec(self.as_ref().to_vec()) + } +} + +impl<'a> std::fmt::Debug for ConnectionId<'a> { + #[inline] + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + for c in self.as_ref() { + write!(f, "{:02x}", c)?; + } + + Ok(()) + } +} + /// A QUIC packet's header. #[derive(Clone, PartialEq)] -pub struct Header { +pub struct Header<'a> { /// The type of the packet. pub ty: Type, @@ -139,10 +247,10 @@ pub struct Header { pub version: u32, /// The destination connection ID of the packet. - pub dcid: Vec<u8>, + pub dcid: ConnectionId<'a>, /// The source connection ID of the packet. - pub scid: Vec<u8>, + pub scid: ConnectionId<'a>, /// The packet number. It's only meaningful after the header protection is /// removed. @@ -165,7 +273,7 @@ pub struct Header { pub(crate) key_phase: bool, } -impl Header { +impl<'a> Header<'a> { /// Parses a QUIC packet header from the given buffer. /// /// The `dcid_len` parameter is the length of the destination connection ID, @@ -183,14 +291,17 @@ impl Header { /// let hdr = quiche::Header::from_slice(&mut buf[..len], LOCAL_CONN_ID_LEN)?; /// # Ok::<(), quiche::Error>(()) /// ``` - pub fn from_slice(buf: &mut [u8], dcid_len: usize) -> Result<Header> { + #[inline] + pub fn from_slice<'b>( + buf: &'b mut [u8], dcid_len: usize, + ) -> Result<Header<'a>> { let mut b = octets::OctetsMut::with_slice(buf); Header::from_bytes(&mut b, dcid_len) } - pub(crate) fn from_bytes( - b: &mut octets::OctetsMut, dcid_len: usize, - ) -> Result<Header> { + pub(crate) fn from_bytes<'b>( + b: &'b mut octets::OctetsMut, dcid_len: usize, + ) -> Result<Header<'a>> { let first = b.get_u8()?; if !Header::is_long(first) { @@ -200,8 +311,8 @@ impl Header { return Ok(Header { ty: Type::Short, version: 0, - dcid: dcid.to_vec(), - scid: Vec::new(), + dcid: dcid.to_vec().into(), + scid: ConnectionId::default(), pkt_num: 0, pkt_num_len: 0, token: None, @@ -274,8 +385,8 @@ impl Header { Ok(Header { ty, version, - dcid, - scid, + dcid: dcid.into(), + scid: scid.into(), pkt_num: 0, pkt_num_len: 0, token, @@ -367,7 +478,7 @@ impl Header { } } -impl std::fmt::Debug for Header { +impl<'a> std::fmt::Debug for Header<'a> { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { write!(f, "{:?}", self.ty)?; @@ -375,16 +486,10 @@ impl std::fmt::Debug for Header { write!(f, " version={:x}", self.version)?; } - write!(f, " dcid=")?; - for b in &self.dcid { - write!(f, "{:02x}", b)?; - } + write!(f, " dcid={:?}", self.dcid)?; if self.ty != Type::Short { - write!(f, " scid=")?; - for b in &self.scid { - write!(f, "{:02x}", b)?; - } + write!(f, " scid={:?}", self.scid)?; } if let Some(ref token) = self.token { @@ -589,6 +694,7 @@ pub fn negotiate_version( b.put_bytes(&scid)?; b.put_u8(dcid.len() as u8)?; b.put_bytes(&dcid)?; + b.put_u32(crate::PROTOCOL_VERSION_V1)?; b.put_u32(crate::PROTOCOL_VERSION_DRAFT29)?; b.put_u32(crate::PROTOCOL_VERSION_DRAFT28)?; b.put_u32(crate::PROTOCOL_VERSION_DRAFT27)?; @@ -609,8 +715,8 @@ pub fn retry( let hdr = Header { ty: Type::Retry, version, - dcid: scid.to_vec(), - scid: new_scid.to_vec(), + dcid: ConnectionId::from_ref(scid), + scid: ConnectionId::from_ref(new_scid), pkt_num: 0, pkt_num_len: 0, token: Some(token.to_vec()), @@ -644,29 +750,41 @@ pub fn verify_retry_integrity( fn compute_retry_integrity_tag( b: &octets::OctetsMut, odcid: &[u8], version: u32, ) -> Result<aead::Tag> { - const RETRY_INTEGRITY_KEY: [u8; 16] = [ + const RETRY_INTEGRITY_KEY_V1: [u8; 16] = [ + 0xbe, 0x0c, 0x69, 0x0b, 0x9f, 0x66, 0x57, 0x5a, 0x1d, 0x76, 0x6b, 0x54, + 0xe3, 0x68, 0xc8, 0x4e, + ]; + + const RETRY_INTEGRITY_NONCE_V1: [u8; aead::NONCE_LEN] = [ + 0x46, 0x15, 0x99, 0xd3, 0x5d, 0x63, 0x2b, 0xf2, 0x23, 0x98, 0x25, 0xbb, + ]; + + const RETRY_INTEGRITY_KEY_DRAFT29: [u8; 16] = [ 0xcc, 0xce, 0x18, 0x7e, 0xd0, 0x9a, 0x09, 0xd0, 0x57, 0x28, 0x15, 0x5a, 0x6c, 0xb9, 0x6b, 0xe1, ]; - const RETRY_INTEGRITY_NONCE: [u8; aead::NONCE_LEN] = [ + const RETRY_INTEGRITY_NONCE_DRAFT29: [u8; aead::NONCE_LEN] = [ 0xe5, 0x49, 0x30, 0xf9, 0x7f, 0x21, 0x36, 0xf0, 0x53, 0x0a, 0x8c, 0x1c, ]; - const RETRY_INTEGRITY_KEY_OLD: [u8; 16] = [ + const RETRY_INTEGRITY_KEY_DRAFT27: [u8; 16] = [ 0x4d, 0x32, 0xec, 0xdb, 0x2a, 0x21, 0x33, 0xc8, 0x41, 0xe4, 0x04, 0x3d, 0xf2, 0x7d, 0x44, 0x30, ]; - const RETRY_INTEGRITY_NONCE_OLD: [u8; aead::NONCE_LEN] = [ + const RETRY_INTEGRITY_NONCE_DRAFT27: [u8; aead::NONCE_LEN] = [ 0x4d, 0x16, 0x11, 0xd0, 0x55, 0x13, 0xa5, 0x52, 0xc5, 0x87, 0xd5, 0x75, ]; let (key, nonce) = match version { crate::PROTOCOL_VERSION_DRAFT27 | crate::PROTOCOL_VERSION_DRAFT28 => - (&RETRY_INTEGRITY_KEY_OLD, RETRY_INTEGRITY_NONCE_OLD), + (&RETRY_INTEGRITY_KEY_DRAFT27, RETRY_INTEGRITY_NONCE_DRAFT27), + + crate::PROTOCOL_VERSION_DRAFT29 => + (&RETRY_INTEGRITY_KEY_DRAFT29, RETRY_INTEGRITY_NONCE_DRAFT29), - _ => (&RETRY_INTEGRITY_KEY, RETRY_INTEGRITY_NONCE), + _ => (&RETRY_INTEGRITY_KEY_V1, RETRY_INTEGRITY_NONCE_V1), }; let hdr_len = b.off(); @@ -823,8 +941,9 @@ mod tests { let hdr = Header { ty: Type::Retry, version: 0xafafafaf, - dcid: vec![0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba], - scid: vec![0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb], + dcid: vec![0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba] + .into(), + scid: vec![0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb].into(), pkt_num: 0, pkt_num_len: 0, token: Some(vec![0xba; 24]), @@ -849,8 +968,9 @@ mod tests { let hdr = Header { ty: Type::Initial, version: 0xafafafaf, - dcid: vec![0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba], - scid: vec![0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb], + dcid: vec![0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba] + .into(), + scid: vec![0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb].into(), pkt_num: 0, pkt_num_len: 0, token: Some(vec![0x05, 0x06, 0x07, 0x08]), @@ -875,8 +995,9 @@ mod tests { dcid: vec![ 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, - ], - scid: vec![0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb], + ] + .into(), + scid: vec![0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb].into(), pkt_num: 0, pkt_num_len: 0, token: Some(vec![0x05, 0x06, 0x07, 0x08]), @@ -898,11 +1019,13 @@ mod tests { let hdr = Header { ty: Type::Initial, version: crate::PROTOCOL_VERSION, - dcid: vec![0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba], + dcid: vec![0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba] + .into(), scid: vec![ 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, - ], + ] + .into(), pkt_num: 0, pkt_num_len: 0, token: Some(vec![0x05, 0x06, 0x07, 0x08]), @@ -924,11 +1047,13 @@ mod tests { let hdr = Header { ty: Type::Initial, version: 0xafafafaf, - dcid: vec![0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba], + dcid: vec![0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba] + .into(), scid: vec![ 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, - ], + ] + .into(), pkt_num: 0, pkt_num_len: 0, token: Some(vec![0x05, 0x06, 0x07, 0x08]), @@ -950,8 +1075,9 @@ mod tests { let hdr = Header { ty: Type::Handshake, version: 0xafafafaf, - dcid: vec![0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba], - scid: vec![0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb], + dcid: vec![0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba] + .into(), + scid: vec![0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb].into(), pkt_num: 0, pkt_num_len: 0, token: None, @@ -973,8 +1099,9 @@ mod tests { let hdr = Header { ty: Type::Short, version: 0, - dcid: vec![0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba], - scid: vec![], + dcid: vec![0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba] + .into(), + scid: ConnectionId::default(), pkt_num: 0, pkt_num_len: 0, token: None, @@ -1132,7 +1259,153 @@ mod tests { } #[test] - fn decrypt_client_initial() { + fn decrypt_client_initial_v1() { + let mut pkt = [ + 0xc0, 0x00, 0x00, 0x00, 0x01, 0x08, 0x83, 0x94, 0xc8, 0xf0, 0x3e, + 0x51, 0x57, 0x08, 0x00, 0x00, 0x44, 0x9e, 0x7b, 0x9a, 0xec, 0x34, + 0xd1, 0xb1, 0xc9, 0x8d, 0xd7, 0x68, 0x9f, 0xb8, 0xec, 0x11, 0xd2, + 0x42, 0xb1, 0x23, 0xdc, 0x9b, 0xd8, 0xba, 0xb9, 0x36, 0xb4, 0x7d, + 0x92, 0xec, 0x35, 0x6c, 0x0b, 0xab, 0x7d, 0xf5, 0x97, 0x6d, 0x27, + 0xcd, 0x44, 0x9f, 0x63, 0x30, 0x00, 0x99, 0xf3, 0x99, 0x1c, 0x26, + 0x0e, 0xc4, 0xc6, 0x0d, 0x17, 0xb3, 0x1f, 0x84, 0x29, 0x15, 0x7b, + 0xb3, 0x5a, 0x12, 0x82, 0xa6, 0x43, 0xa8, 0xd2, 0x26, 0x2c, 0xad, + 0x67, 0x50, 0x0c, 0xad, 0xb8, 0xe7, 0x37, 0x8c, 0x8e, 0xb7, 0x53, + 0x9e, 0xc4, 0xd4, 0x90, 0x5f, 0xed, 0x1b, 0xee, 0x1f, 0xc8, 0xaa, + 0xfb, 0xa1, 0x7c, 0x75, 0x0e, 0x2c, 0x7a, 0xce, 0x01, 0xe6, 0x00, + 0x5f, 0x80, 0xfc, 0xb7, 0xdf, 0x62, 0x12, 0x30, 0xc8, 0x37, 0x11, + 0xb3, 0x93, 0x43, 0xfa, 0x02, 0x8c, 0xea, 0x7f, 0x7f, 0xb5, 0xff, + 0x89, 0xea, 0xc2, 0x30, 0x82, 0x49, 0xa0, 0x22, 0x52, 0x15, 0x5e, + 0x23, 0x47, 0xb6, 0x3d, 0x58, 0xc5, 0x45, 0x7a, 0xfd, 0x84, 0xd0, + 0x5d, 0xff, 0xfd, 0xb2, 0x03, 0x92, 0x84, 0x4a, 0xe8, 0x12, 0x15, + 0x46, 0x82, 0xe9, 0xcf, 0x01, 0x2f, 0x90, 0x21, 0xa6, 0xf0, 0xbe, + 0x17, 0xdd, 0xd0, 0xc2, 0x08, 0x4d, 0xce, 0x25, 0xff, 0x9b, 0x06, + 0xcd, 0xe5, 0x35, 0xd0, 0xf9, 0x20, 0xa2, 0xdb, 0x1b, 0xf3, 0x62, + 0xc2, 0x3e, 0x59, 0x6d, 0xee, 0x38, 0xf5, 0xa6, 0xcf, 0x39, 0x48, + 0x83, 0x8a, 0x3a, 0xec, 0x4e, 0x15, 0xda, 0xf8, 0x50, 0x0a, 0x6e, + 0xf6, 0x9e, 0xc4, 0xe3, 0xfe, 0xb6, 0xb1, 0xd9, 0x8e, 0x61, 0x0a, + 0xc8, 0xb7, 0xec, 0x3f, 0xaf, 0x6a, 0xd7, 0x60, 0xb7, 0xba, 0xd1, + 0xdb, 0x4b, 0xa3, 0x48, 0x5e, 0x8a, 0x94, 0xdc, 0x25, 0x0a, 0xe3, + 0xfd, 0xb4, 0x1e, 0xd1, 0x5f, 0xb6, 0xa8, 0xe5, 0xeb, 0xa0, 0xfc, + 0x3d, 0xd6, 0x0b, 0xc8, 0xe3, 0x0c, 0x5c, 0x42, 0x87, 0xe5, 0x38, + 0x05, 0xdb, 0x05, 0x9a, 0xe0, 0x64, 0x8d, 0xb2, 0xf6, 0x42, 0x64, + 0xed, 0x5e, 0x39, 0xbe, 0x2e, 0x20, 0xd8, 0x2d, 0xf5, 0x66, 0xda, + 0x8d, 0xd5, 0x99, 0x8c, 0xca, 0xbd, 0xae, 0x05, 0x30, 0x60, 0xae, + 0x6c, 0x7b, 0x43, 0x78, 0xe8, 0x46, 0xd2, 0x9f, 0x37, 0xed, 0x7b, + 0x4e, 0xa9, 0xec, 0x5d, 0x82, 0xe7, 0x96, 0x1b, 0x7f, 0x25, 0xa9, + 0x32, 0x38, 0x51, 0xf6, 0x81, 0xd5, 0x82, 0x36, 0x3a, 0xa5, 0xf8, + 0x99, 0x37, 0xf5, 0xa6, 0x72, 0x58, 0xbf, 0x63, 0xad, 0x6f, 0x1a, + 0x0b, 0x1d, 0x96, 0xdb, 0xd4, 0xfa, 0xdd, 0xfc, 0xef, 0xc5, 0x26, + 0x6b, 0xa6, 0x61, 0x17, 0x22, 0x39, 0x5c, 0x90, 0x65, 0x56, 0xbe, + 0x52, 0xaf, 0xe3, 0xf5, 0x65, 0x63, 0x6a, 0xd1, 0xb1, 0x7d, 0x50, + 0x8b, 0x73, 0xd8, 0x74, 0x3e, 0xeb, 0x52, 0x4b, 0xe2, 0x2b, 0x3d, + 0xcb, 0xc2, 0xc7, 0x46, 0x8d, 0x54, 0x11, 0x9c, 0x74, 0x68, 0x44, + 0x9a, 0x13, 0xd8, 0xe3, 0xb9, 0x58, 0x11, 0xa1, 0x98, 0xf3, 0x49, + 0x1d, 0xe3, 0xe7, 0xfe, 0x94, 0x2b, 0x33, 0x04, 0x07, 0xab, 0xf8, + 0x2a, 0x4e, 0xd7, 0xc1, 0xb3, 0x11, 0x66, 0x3a, 0xc6, 0x98, 0x90, + 0xf4, 0x15, 0x70, 0x15, 0x85, 0x3d, 0x91, 0xe9, 0x23, 0x03, 0x7c, + 0x22, 0x7a, 0x33, 0xcd, 0xd5, 0xec, 0x28, 0x1c, 0xa3, 0xf7, 0x9c, + 0x44, 0x54, 0x6b, 0x9d, 0x90, 0xca, 0x00, 0xf0, 0x64, 0xc9, 0x9e, + 0x3d, 0xd9, 0x79, 0x11, 0xd3, 0x9f, 0xe9, 0xc5, 0xd0, 0xb2, 0x3a, + 0x22, 0x9a, 0x23, 0x4c, 0xb3, 0x61, 0x86, 0xc4, 0x81, 0x9e, 0x8b, + 0x9c, 0x59, 0x27, 0x72, 0x66, 0x32, 0x29, 0x1d, 0x6a, 0x41, 0x82, + 0x11, 0xcc, 0x29, 0x62, 0xe2, 0x0f, 0xe4, 0x7f, 0xeb, 0x3e, 0xdf, + 0x33, 0x0f, 0x2c, 0x60, 0x3a, 0x9d, 0x48, 0xc0, 0xfc, 0xb5, 0x69, + 0x9d, 0xbf, 0xe5, 0x89, 0x64, 0x25, 0xc5, 0xba, 0xc4, 0xae, 0xe8, + 0x2e, 0x57, 0xa8, 0x5a, 0xaf, 0x4e, 0x25, 0x13, 0xe4, 0xf0, 0x57, + 0x96, 0xb0, 0x7b, 0xa2, 0xee, 0x47, 0xd8, 0x05, 0x06, 0xf8, 0xd2, + 0xc2, 0x5e, 0x50, 0xfd, 0x14, 0xde, 0x71, 0xe6, 0xc4, 0x18, 0x55, + 0x93, 0x02, 0xf9, 0x39, 0xb0, 0xe1, 0xab, 0xd5, 0x76, 0xf2, 0x79, + 0xc4, 0xb2, 0xe0, 0xfe, 0xb8, 0x5c, 0x1f, 0x28, 0xff, 0x18, 0xf5, + 0x88, 0x91, 0xff, 0xef, 0x13, 0x2e, 0xef, 0x2f, 0xa0, 0x93, 0x46, + 0xae, 0xe3, 0x3c, 0x28, 0xeb, 0x13, 0x0f, 0xf2, 0x8f, 0x5b, 0x76, + 0x69, 0x53, 0x33, 0x41, 0x13, 0x21, 0x19, 0x96, 0xd2, 0x00, 0x11, + 0xa1, 0x98, 0xe3, 0xfc, 0x43, 0x3f, 0x9f, 0x25, 0x41, 0x01, 0x0a, + 0xe1, 0x7c, 0x1b, 0xf2, 0x02, 0x58, 0x0f, 0x60, 0x47, 0x47, 0x2f, + 0xb3, 0x68, 0x57, 0xfe, 0x84, 0x3b, 0x19, 0xf5, 0x98, 0x40, 0x09, + 0xdd, 0xc3, 0x24, 0x04, 0x4e, 0x84, 0x7a, 0x4f, 0x4a, 0x0a, 0xb3, + 0x4f, 0x71, 0x95, 0x95, 0xde, 0x37, 0x25, 0x2d, 0x62, 0x35, 0x36, + 0x5e, 0x9b, 0x84, 0x39, 0x2b, 0x06, 0x10, 0x85, 0x34, 0x9d, 0x73, + 0x20, 0x3a, 0x4a, 0x13, 0xe9, 0x6f, 0x54, 0x32, 0xec, 0x0f, 0xd4, + 0xa1, 0xee, 0x65, 0xac, 0xcd, 0xd5, 0xe3, 0x90, 0x4d, 0xf5, 0x4c, + 0x1d, 0xa5, 0x10, 0xb0, 0xff, 0x20, 0xdc, 0xc0, 0xc7, 0x7f, 0xcb, + 0x2c, 0x0e, 0x0e, 0xb6, 0x05, 0xcb, 0x05, 0x04, 0xdb, 0x87, 0x63, + 0x2c, 0xf3, 0xd8, 0xb4, 0xda, 0xe6, 0xe7, 0x05, 0x76, 0x9d, 0x1d, + 0xe3, 0x54, 0x27, 0x01, 0x23, 0xcb, 0x11, 0x45, 0x0e, 0xfc, 0x60, + 0xac, 0x47, 0x68, 0x3d, 0x7b, 0x8d, 0x0f, 0x81, 0x13, 0x65, 0x56, + 0x5f, 0xd9, 0x8c, 0x4c, 0x8e, 0xb9, 0x36, 0xbc, 0xab, 0x8d, 0x06, + 0x9f, 0xc3, 0x3b, 0xd8, 0x01, 0xb0, 0x3a, 0xde, 0xa2, 0xe1, 0xfb, + 0xc5, 0xaa, 0x46, 0x3d, 0x08, 0xca, 0x19, 0x89, 0x6d, 0x2b, 0xf5, + 0x9a, 0x07, 0x1b, 0x85, 0x1e, 0x6c, 0x23, 0x90, 0x52, 0x17, 0x2f, + 0x29, 0x6b, 0xfb, 0x5e, 0x72, 0x40, 0x47, 0x90, 0xa2, 0x18, 0x10, + 0x14, 0xf3, 0xb9, 0x4a, 0x4e, 0x97, 0xd1, 0x17, 0xb4, 0x38, 0x13, + 0x03, 0x68, 0xcc, 0x39, 0xdb, 0xb2, 0xd1, 0x98, 0x06, 0x5a, 0xe3, + 0x98, 0x65, 0x47, 0x92, 0x6c, 0xd2, 0x16, 0x2f, 0x40, 0xa2, 0x9f, + 0x0c, 0x3c, 0x87, 0x45, 0xc0, 0xf5, 0x0f, 0xba, 0x38, 0x52, 0xe5, + 0x66, 0xd4, 0x45, 0x75, 0xc2, 0x9d, 0x39, 0xa0, 0x3f, 0x0c, 0xda, + 0x72, 0x19, 0x84, 0xb6, 0xf4, 0x40, 0x59, 0x1f, 0x35, 0x5e, 0x12, + 0xd4, 0x39, 0xff, 0x15, 0x0a, 0xab, 0x76, 0x13, 0x49, 0x9d, 0xbd, + 0x49, 0xad, 0xab, 0xc8, 0x67, 0x6e, 0xef, 0x02, 0x3b, 0x15, 0xb6, + 0x5b, 0xfc, 0x5c, 0xa0, 0x69, 0x48, 0x10, 0x9f, 0x23, 0xf3, 0x50, + 0xdb, 0x82, 0x12, 0x35, 0x35, 0xeb, 0x8a, 0x74, 0x33, 0xbd, 0xab, + 0xcb, 0x90, 0x92, 0x71, 0xa6, 0xec, 0xbc, 0xb5, 0x8b, 0x93, 0x6a, + 0x88, 0xcd, 0x4e, 0x8f, 0x2e, 0x6f, 0xf5, 0x80, 0x01, 0x75, 0xf1, + 0x13, 0x25, 0x3d, 0x8f, 0xa9, 0xca, 0x88, 0x85, 0xc2, 0xf5, 0x52, + 0xe6, 0x57, 0xdc, 0x60, 0x3f, 0x25, 0x2e, 0x1a, 0x8e, 0x30, 0x8f, + 0x76, 0xf0, 0xbe, 0x79, 0xe2, 0xfb, 0x8f, 0x5d, 0x5f, 0xbb, 0xe2, + 0xe3, 0x0e, 0xca, 0xdd, 0x22, 0x07, 0x23, 0xc8, 0xc0, 0xae, 0xa8, + 0x07, 0x8c, 0xdf, 0xcb, 0x38, 0x68, 0x26, 0x3f, 0xf8, 0xf0, 0x94, + 0x00, 0x54, 0xda, 0x48, 0x78, 0x18, 0x93, 0xa7, 0xe4, 0x9a, 0xd5, + 0xaf, 0xf4, 0xaf, 0x30, 0x0c, 0xd8, 0x04, 0xa6, 0xb6, 0x27, 0x9a, + 0xb3, 0xff, 0x3a, 0xfb, 0x64, 0x49, 0x1c, 0x85, 0x19, 0x4a, 0xab, + 0x76, 0x0d, 0x58, 0xa6, 0x06, 0x65, 0x4f, 0x9f, 0x44, 0x00, 0xe8, + 0xb3, 0x85, 0x91, 0x35, 0x6f, 0xbf, 0x64, 0x25, 0xac, 0xa2, 0x6d, + 0xc8, 0x52, 0x44, 0x25, 0x9f, 0xf2, 0xb1, 0x9c, 0x41, 0xb9, 0xf9, + 0x6f, 0x3c, 0xa9, 0xec, 0x1d, 0xde, 0x43, 0x4d, 0xa7, 0xd2, 0xd3, + 0x92, 0xb9, 0x05, 0xdd, 0xf3, 0xd1, 0xf9, 0xaf, 0x93, 0xd1, 0xaf, + 0x59, 0x50, 0xbd, 0x49, 0x3f, 0x5a, 0xa7, 0x31, 0xb4, 0x05, 0x6d, + 0xf3, 0x1b, 0xd2, 0x67, 0xb6, 0xb9, 0x0a, 0x07, 0x98, 0x31, 0xaa, + 0xf5, 0x79, 0xbe, 0x0a, 0x39, 0x01, 0x31, 0x37, 0xaa, 0xc6, 0xd4, + 0x04, 0xf5, 0x18, 0xcf, 0xd4, 0x68, 0x40, 0x64, 0x7e, 0x78, 0xbf, + 0xe7, 0x06, 0xca, 0x4c, 0xf5, 0xe9, 0xc5, 0x45, 0x3e, 0x9f, 0x7c, + 0xfd, 0x2b, 0x8b, 0x4c, 0x8d, 0x16, 0x9a, 0x44, 0xe5, 0x5c, 0x88, + 0xd4, 0xa9, 0xa7, 0xf9, 0x47, 0x42, 0x41, 0x10, 0x92, 0xab, 0xbd, + 0xf8, 0xb8, 0x89, 0xe5, 0xc1, 0x99, 0xd0, 0x96, 0xe3, 0xf2, 0x47, + 0x88, + ]; + + let dcid = [0x83, 0x94, 0xc8, 0xf0, 0x3e, 0x51, 0x57, 0x08]; + + let frames = [ + 0x06, 0x00, 0x40, 0xf1, 0x01, 0x00, 0x00, 0xed, 0x03, 0x03, 0xeb, + 0xf8, 0xfa, 0x56, 0xf1, 0x29, 0x39, 0xb9, 0x58, 0x4a, 0x38, 0x96, + 0x47, 0x2e, 0xc4, 0x0b, 0xb8, 0x63, 0xcf, 0xd3, 0xe8, 0x68, 0x04, + 0xfe, 0x3a, 0x47, 0xf0, 0x6a, 0x2b, 0x69, 0x48, 0x4c, 0x00, 0x00, + 0x04, 0x13, 0x01, 0x13, 0x02, 0x01, 0x00, 0x00, 0xc0, 0x00, 0x00, + 0x00, 0x10, 0x00, 0x0e, 0x00, 0x00, 0x0b, 0x65, 0x78, 0x61, 0x6d, + 0x70, 0x6c, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0xff, 0x01, 0x00, 0x01, + 0x00, 0x00, 0x0a, 0x00, 0x08, 0x00, 0x06, 0x00, 0x1d, 0x00, 0x17, + 0x00, 0x18, 0x00, 0x10, 0x00, 0x07, 0x00, 0x05, 0x04, 0x61, 0x6c, + 0x70, 0x6e, 0x00, 0x05, 0x00, 0x05, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x33, 0x00, 0x26, 0x00, 0x24, 0x00, 0x1d, 0x00, 0x20, 0x93, + 0x70, 0xb2, 0xc9, 0xca, 0xa4, 0x7f, 0xba, 0xba, 0xf4, 0x55, 0x9f, + 0xed, 0xba, 0x75, 0x3d, 0xe1, 0x71, 0xfa, 0x71, 0xf5, 0x0f, 0x1c, + 0xe1, 0x5d, 0x43, 0xe9, 0x94, 0xec, 0x74, 0xd7, 0x48, 0x00, 0x2b, + 0x00, 0x03, 0x02, 0x03, 0x04, 0x00, 0x0d, 0x00, 0x10, 0x00, 0x0e, + 0x04, 0x03, 0x05, 0x03, 0x06, 0x03, 0x02, 0x03, 0x08, 0x04, 0x08, + 0x05, 0x08, 0x06, 0x00, 0x2d, 0x00, 0x02, 0x01, 0x01, 0x00, 0x1c, + 0x00, 0x02, 0x40, 0x01, 0xff, 0xa5, 0x00, 0x32, 0x04, 0x08, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x05, 0x04, 0x80, 0x00, + 0xff, 0xff, 0x07, 0x04, 0x80, 0x00, 0xff, 0xff, 0x08, 0x01, 0x10, + 0x01, 0x04, 0x80, 0x00, 0x75, 0x30, 0x09, 0x01, 0x10, 0x0f, 0x08, + 0x83, 0x94, 0xc8, 0xf0, 0x3e, 0x51, 0x57, 0x08, 0x06, 0x04, 0x80, + 0x00, 0xff, 0xff, + ]; + + assert_decrypt_initial_pkt(&mut pkt, &dcid, true, &frames, 2, 4); + } + + #[test] + fn decrypt_client_initial_draft29() { let mut pkt = [ 0xc5, 0xff, 0x00, 0x00, 0x1d, 0x08, 0x83, 0x94, 0xc8, 0xf0, 0x3e, 0x51, 0x57, 0x08, 0x00, 0x00, 0x44, 0x9e, 0x4a, 0x95, 0x24, 0x5b, @@ -1274,7 +1547,7 @@ mod tests { } #[test] - fn decrypt_client_initial_old() { + fn decrypt_client_initial_draft28() { let mut pkt = [ 0xc0, 0xff, 0x00, 0x00, 0x1c, 0x08, 0x83, 0x94, 0xc8, 0xf0, 0x3e, 0x51, 0x57, 0x08, 0x00, 0x00, 0x44, 0x9e, 0x3b, 0x34, 0x3a, 0xa8, @@ -1416,7 +1689,42 @@ mod tests { } #[test] - fn decrypt_server_initial() { + fn decrypt_server_initial_v1() { + let mut pkt = [ + 0xcf, 0x00, 0x00, 0x00, 0x01, 0x00, 0x08, 0xf0, 0x67, 0xa5, 0x50, + 0x2a, 0x42, 0x62, 0xb5, 0x00, 0x40, 0x75, 0xc0, 0xd9, 0x5a, 0x48, + 0x2c, 0xd0, 0x99, 0x1c, 0xd2, 0x5b, 0x0a, 0xac, 0x40, 0x6a, 0x58, + 0x16, 0xb6, 0x39, 0x41, 0x00, 0xf3, 0x7a, 0x1c, 0x69, 0x79, 0x75, + 0x54, 0x78, 0x0b, 0xb3, 0x8c, 0xc5, 0xa9, 0x9f, 0x5e, 0xde, 0x4c, + 0xf7, 0x3c, 0x3e, 0xc2, 0x49, 0x3a, 0x18, 0x39, 0xb3, 0xdb, 0xcb, + 0xa3, 0xf6, 0xea, 0x46, 0xc5, 0xb7, 0x68, 0x4d, 0xf3, 0x54, 0x8e, + 0x7d, 0xde, 0xb9, 0xc3, 0xbf, 0x9c, 0x73, 0xcc, 0x3f, 0x3b, 0xde, + 0xd7, 0x4b, 0x56, 0x2b, 0xfb, 0x19, 0xfb, 0x84, 0x02, 0x2f, 0x8e, + 0xf4, 0xcd, 0xd9, 0x37, 0x95, 0xd7, 0x7d, 0x06, 0xed, 0xbb, 0x7a, + 0xaf, 0x2f, 0x58, 0x89, 0x18, 0x50, 0xab, 0xbd, 0xca, 0x3d, 0x20, + 0x39, 0x8c, 0x27, 0x64, 0x56, 0xcb, 0xc4, 0x21, 0x58, 0x40, 0x7d, + 0xd0, 0x74, 0xee, + ]; + + let dcid = [0x83, 0x94, 0xc8, 0xf0, 0x3e, 0x51, 0x57, 0x08]; + + let frames = [ + 0x02, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x40, 0x5a, 0x02, 0x00, + 0x00, 0x56, 0x03, 0x03, 0xee, 0xfc, 0xe7, 0xf7, 0xb3, 0x7b, 0xa1, + 0xd1, 0x63, 0x2e, 0x96, 0x67, 0x78, 0x25, 0xdd, 0xf7, 0x39, 0x88, + 0xcf, 0xc7, 0x98, 0x25, 0xdf, 0x56, 0x6d, 0xc5, 0x43, 0x0b, 0x9a, + 0x04, 0x5a, 0x12, 0x00, 0x13, 0x01, 0x00, 0x00, 0x2e, 0x00, 0x33, + 0x00, 0x24, 0x00, 0x1d, 0x00, 0x20, 0x9d, 0x3c, 0x94, 0x0d, 0x89, + 0x69, 0x0b, 0x84, 0xd0, 0x8a, 0x60, 0x99, 0x3c, 0x14, 0x4e, 0xca, + 0x68, 0x4d, 0x10, 0x81, 0x28, 0x7c, 0x83, 0x4d, 0x53, 0x11, 0xbc, + 0xf3, 0x2b, 0xb9, 0xda, 0x1a, 0x00, 0x2b, 0x00, 0x02, 0x03, 0x04, + ]; + + assert_decrypt_initial_pkt(&mut pkt, &dcid, false, &frames, 1, 2); + } + + #[test] + fn decrypt_server_initial_draft29() { let mut pkt = [ 0xca, 0xff, 0x00, 0x00, 0x1d, 0x00, 0x08, 0xf0, 0x67, 0xa5, 0x50, 0x2a, 0x42, 0x62, 0xb5, 0x00, 0x40, 0x74, 0xaa, 0xf2, 0xf0, 0x07, @@ -1451,7 +1759,7 @@ mod tests { } #[test] - fn decrypt_server_initial_old() { + fn decrypt_server_initial_draft28() { let mut pkt = [ 0xc9, 0xff, 0x00, 0x00, 0x1c, 0x00, 0x08, 0xf0, 0x67, 0xa5, 0x50, 0x2a, 0x42, 0x62, 0xb5, 0x00, 0x40, 0x74, 0x16, 0x8b, 0xf2, 0x2b, @@ -1557,7 +1865,249 @@ mod tests { } #[test] - fn encrypt_client_initial() { + fn encrypt_client_initial_v1() { + let mut header = [ + 0xc3, 0x00, 0x00, 0x00, 0x01, 0x08, 0x83, 0x94, 0xc8, 0xf0, 0x3e, + 0x51, 0x57, 0x08, 0x00, 0x00, 0x44, 0x9e, 0x00, 0x00, 0x00, 0x02, + ]; + + let dcid = [0x83, 0x94, 0xc8, 0xf0, 0x3e, 0x51, 0x57, 0x08]; + + let frames = [ + 0x06, 0x00, 0x40, 0xf1, 0x01, 0x00, 0x00, 0xed, 0x03, 0x03, 0xeb, + 0xf8, 0xfa, 0x56, 0xf1, 0x29, 0x39, 0xb9, 0x58, 0x4a, 0x38, 0x96, + 0x47, 0x2e, 0xc4, 0x0b, 0xb8, 0x63, 0xcf, 0xd3, 0xe8, 0x68, 0x04, + 0xfe, 0x3a, 0x47, 0xf0, 0x6a, 0x2b, 0x69, 0x48, 0x4c, 0x00, 0x00, + 0x04, 0x13, 0x01, 0x13, 0x02, 0x01, 0x00, 0x00, 0xc0, 0x00, 0x00, + 0x00, 0x10, 0x00, 0x0e, 0x00, 0x00, 0x0b, 0x65, 0x78, 0x61, 0x6d, + 0x70, 0x6c, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0xff, 0x01, 0x00, 0x01, + 0x00, 0x00, 0x0a, 0x00, 0x08, 0x00, 0x06, 0x00, 0x1d, 0x00, 0x17, + 0x00, 0x18, 0x00, 0x10, 0x00, 0x07, 0x00, 0x05, 0x04, 0x61, 0x6c, + 0x70, 0x6e, 0x00, 0x05, 0x00, 0x05, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x33, 0x00, 0x26, 0x00, 0x24, 0x00, 0x1d, 0x00, 0x20, 0x93, + 0x70, 0xb2, 0xc9, 0xca, 0xa4, 0x7f, 0xba, 0xba, 0xf4, 0x55, 0x9f, + 0xed, 0xba, 0x75, 0x3d, 0xe1, 0x71, 0xfa, 0x71, 0xf5, 0x0f, 0x1c, + 0xe1, 0x5d, 0x43, 0xe9, 0x94, 0xec, 0x74, 0xd7, 0x48, 0x00, 0x2b, + 0x00, 0x03, 0x02, 0x03, 0x04, 0x00, 0x0d, 0x00, 0x10, 0x00, 0x0e, + 0x04, 0x03, 0x05, 0x03, 0x06, 0x03, 0x02, 0x03, 0x08, 0x04, 0x08, + 0x05, 0x08, 0x06, 0x00, 0x2d, 0x00, 0x02, 0x01, 0x01, 0x00, 0x1c, + 0x00, 0x02, 0x40, 0x01, 0xff, 0xa5, 0x00, 0x32, 0x04, 0x08, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x05, 0x04, 0x80, 0x00, + 0xff, 0xff, 0x07, 0x04, 0x80, 0x00, 0xff, 0xff, 0x08, 0x01, 0x10, + 0x01, 0x04, 0x80, 0x00, 0x75, 0x30, 0x09, 0x01, 0x10, 0x0f, 0x08, + 0x83, 0x94, 0xc8, 0xf0, 0x3e, 0x51, 0x57, 0x08, 0x06, 0x04, 0x80, + 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + ]; + + let pkt = [ + 0xc0, 0x00, 0x00, 0x00, 0x01, 0x08, 0x83, 0x94, 0xc8, 0xf0, 0x3e, + 0x51, 0x57, 0x08, 0x00, 0x00, 0x44, 0x9e, 0x7b, 0x9a, 0xec, 0x34, + 0xd1, 0xb1, 0xc9, 0x8d, 0xd7, 0x68, 0x9f, 0xb8, 0xec, 0x11, 0xd2, + 0x42, 0xb1, 0x23, 0xdc, 0x9b, 0xd8, 0xba, 0xb9, 0x36, 0xb4, 0x7d, + 0x92, 0xec, 0x35, 0x6c, 0x0b, 0xab, 0x7d, 0xf5, 0x97, 0x6d, 0x27, + 0xcd, 0x44, 0x9f, 0x63, 0x30, 0x00, 0x99, 0xf3, 0x99, 0x1c, 0x26, + 0x0e, 0xc4, 0xc6, 0x0d, 0x17, 0xb3, 0x1f, 0x84, 0x29, 0x15, 0x7b, + 0xb3, 0x5a, 0x12, 0x82, 0xa6, 0x43, 0xa8, 0xd2, 0x26, 0x2c, 0xad, + 0x67, 0x50, 0x0c, 0xad, 0xb8, 0xe7, 0x37, 0x8c, 0x8e, 0xb7, 0x53, + 0x9e, 0xc4, 0xd4, 0x90, 0x5f, 0xed, 0x1b, 0xee, 0x1f, 0xc8, 0xaa, + 0xfb, 0xa1, 0x7c, 0x75, 0x0e, 0x2c, 0x7a, 0xce, 0x01, 0xe6, 0x00, + 0x5f, 0x80, 0xfc, 0xb7, 0xdf, 0x62, 0x12, 0x30, 0xc8, 0x37, 0x11, + 0xb3, 0x93, 0x43, 0xfa, 0x02, 0x8c, 0xea, 0x7f, 0x7f, 0xb5, 0xff, + 0x89, 0xea, 0xc2, 0x30, 0x82, 0x49, 0xa0, 0x22, 0x52, 0x15, 0x5e, + 0x23, 0x47, 0xb6, 0x3d, 0x58, 0xc5, 0x45, 0x7a, 0xfd, 0x84, 0xd0, + 0x5d, 0xff, 0xfd, 0xb2, 0x03, 0x92, 0x84, 0x4a, 0xe8, 0x12, 0x15, + 0x46, 0x82, 0xe9, 0xcf, 0x01, 0x2f, 0x90, 0x21, 0xa6, 0xf0, 0xbe, + 0x17, 0xdd, 0xd0, 0xc2, 0x08, 0x4d, 0xce, 0x25, 0xff, 0x9b, 0x06, + 0xcd, 0xe5, 0x35, 0xd0, 0xf9, 0x20, 0xa2, 0xdb, 0x1b, 0xf3, 0x62, + 0xc2, 0x3e, 0x59, 0x6d, 0xee, 0x38, 0xf5, 0xa6, 0xcf, 0x39, 0x48, + 0x83, 0x8a, 0x3a, 0xec, 0x4e, 0x15, 0xda, 0xf8, 0x50, 0x0a, 0x6e, + 0xf6, 0x9e, 0xc4, 0xe3, 0xfe, 0xb6, 0xb1, 0xd9, 0x8e, 0x61, 0x0a, + 0xc8, 0xb7, 0xec, 0x3f, 0xaf, 0x6a, 0xd7, 0x60, 0xb7, 0xba, 0xd1, + 0xdb, 0x4b, 0xa3, 0x48, 0x5e, 0x8a, 0x94, 0xdc, 0x25, 0x0a, 0xe3, + 0xfd, 0xb4, 0x1e, 0xd1, 0x5f, 0xb6, 0xa8, 0xe5, 0xeb, 0xa0, 0xfc, + 0x3d, 0xd6, 0x0b, 0xc8, 0xe3, 0x0c, 0x5c, 0x42, 0x87, 0xe5, 0x38, + 0x05, 0xdb, 0x05, 0x9a, 0xe0, 0x64, 0x8d, 0xb2, 0xf6, 0x42, 0x64, + 0xed, 0x5e, 0x39, 0xbe, 0x2e, 0x20, 0xd8, 0x2d, 0xf5, 0x66, 0xda, + 0x8d, 0xd5, 0x99, 0x8c, 0xca, 0xbd, 0xae, 0x05, 0x30, 0x60, 0xae, + 0x6c, 0x7b, 0x43, 0x78, 0xe8, 0x46, 0xd2, 0x9f, 0x37, 0xed, 0x7b, + 0x4e, 0xa9, 0xec, 0x5d, 0x82, 0xe7, 0x96, 0x1b, 0x7f, 0x25, 0xa9, + 0x32, 0x38, 0x51, 0xf6, 0x81, 0xd5, 0x82, 0x36, 0x3a, 0xa5, 0xf8, + 0x99, 0x37, 0xf5, 0xa6, 0x72, 0x58, 0xbf, 0x63, 0xad, 0x6f, 0x1a, + 0x0b, 0x1d, 0x96, 0xdb, 0xd4, 0xfa, 0xdd, 0xfc, 0xef, 0xc5, 0x26, + 0x6b, 0xa6, 0x61, 0x17, 0x22, 0x39, 0x5c, 0x90, 0x65, 0x56, 0xbe, + 0x52, 0xaf, 0xe3, 0xf5, 0x65, 0x63, 0x6a, 0xd1, 0xb1, 0x7d, 0x50, + 0x8b, 0x73, 0xd8, 0x74, 0x3e, 0xeb, 0x52, 0x4b, 0xe2, 0x2b, 0x3d, + 0xcb, 0xc2, 0xc7, 0x46, 0x8d, 0x54, 0x11, 0x9c, 0x74, 0x68, 0x44, + 0x9a, 0x13, 0xd8, 0xe3, 0xb9, 0x58, 0x11, 0xa1, 0x98, 0xf3, 0x49, + 0x1d, 0xe3, 0xe7, 0xfe, 0x94, 0x2b, 0x33, 0x04, 0x07, 0xab, 0xf8, + 0x2a, 0x4e, 0xd7, 0xc1, 0xb3, 0x11, 0x66, 0x3a, 0xc6, 0x98, 0x90, + 0xf4, 0x15, 0x70, 0x15, 0x85, 0x3d, 0x91, 0xe9, 0x23, 0x03, 0x7c, + 0x22, 0x7a, 0x33, 0xcd, 0xd5, 0xec, 0x28, 0x1c, 0xa3, 0xf7, 0x9c, + 0x44, 0x54, 0x6b, 0x9d, 0x90, 0xca, 0x00, 0xf0, 0x64, 0xc9, 0x9e, + 0x3d, 0xd9, 0x79, 0x11, 0xd3, 0x9f, 0xe9, 0xc5, 0xd0, 0xb2, 0x3a, + 0x22, 0x9a, 0x23, 0x4c, 0xb3, 0x61, 0x86, 0xc4, 0x81, 0x9e, 0x8b, + 0x9c, 0x59, 0x27, 0x72, 0x66, 0x32, 0x29, 0x1d, 0x6a, 0x41, 0x82, + 0x11, 0xcc, 0x29, 0x62, 0xe2, 0x0f, 0xe4, 0x7f, 0xeb, 0x3e, 0xdf, + 0x33, 0x0f, 0x2c, 0x60, 0x3a, 0x9d, 0x48, 0xc0, 0xfc, 0xb5, 0x69, + 0x9d, 0xbf, 0xe5, 0x89, 0x64, 0x25, 0xc5, 0xba, 0xc4, 0xae, 0xe8, + 0x2e, 0x57, 0xa8, 0x5a, 0xaf, 0x4e, 0x25, 0x13, 0xe4, 0xf0, 0x57, + 0x96, 0xb0, 0x7b, 0xa2, 0xee, 0x47, 0xd8, 0x05, 0x06, 0xf8, 0xd2, + 0xc2, 0x5e, 0x50, 0xfd, 0x14, 0xde, 0x71, 0xe6, 0xc4, 0x18, 0x55, + 0x93, 0x02, 0xf9, 0x39, 0xb0, 0xe1, 0xab, 0xd5, 0x76, 0xf2, 0x79, + 0xc4, 0xb2, 0xe0, 0xfe, 0xb8, 0x5c, 0x1f, 0x28, 0xff, 0x18, 0xf5, + 0x88, 0x91, 0xff, 0xef, 0x13, 0x2e, 0xef, 0x2f, 0xa0, 0x93, 0x46, + 0xae, 0xe3, 0x3c, 0x28, 0xeb, 0x13, 0x0f, 0xf2, 0x8f, 0x5b, 0x76, + 0x69, 0x53, 0x33, 0x41, 0x13, 0x21, 0x19, 0x96, 0xd2, 0x00, 0x11, + 0xa1, 0x98, 0xe3, 0xfc, 0x43, 0x3f, 0x9f, 0x25, 0x41, 0x01, 0x0a, + 0xe1, 0x7c, 0x1b, 0xf2, 0x02, 0x58, 0x0f, 0x60, 0x47, 0x47, 0x2f, + 0xb3, 0x68, 0x57, 0xfe, 0x84, 0x3b, 0x19, 0xf5, 0x98, 0x40, 0x09, + 0xdd, 0xc3, 0x24, 0x04, 0x4e, 0x84, 0x7a, 0x4f, 0x4a, 0x0a, 0xb3, + 0x4f, 0x71, 0x95, 0x95, 0xde, 0x37, 0x25, 0x2d, 0x62, 0x35, 0x36, + 0x5e, 0x9b, 0x84, 0x39, 0x2b, 0x06, 0x10, 0x85, 0x34, 0x9d, 0x73, + 0x20, 0x3a, 0x4a, 0x13, 0xe9, 0x6f, 0x54, 0x32, 0xec, 0x0f, 0xd4, + 0xa1, 0xee, 0x65, 0xac, 0xcd, 0xd5, 0xe3, 0x90, 0x4d, 0xf5, 0x4c, + 0x1d, 0xa5, 0x10, 0xb0, 0xff, 0x20, 0xdc, 0xc0, 0xc7, 0x7f, 0xcb, + 0x2c, 0x0e, 0x0e, 0xb6, 0x05, 0xcb, 0x05, 0x04, 0xdb, 0x87, 0x63, + 0x2c, 0xf3, 0xd8, 0xb4, 0xda, 0xe6, 0xe7, 0x05, 0x76, 0x9d, 0x1d, + 0xe3, 0x54, 0x27, 0x01, 0x23, 0xcb, 0x11, 0x45, 0x0e, 0xfc, 0x60, + 0xac, 0x47, 0x68, 0x3d, 0x7b, 0x8d, 0x0f, 0x81, 0x13, 0x65, 0x56, + 0x5f, 0xd9, 0x8c, 0x4c, 0x8e, 0xb9, 0x36, 0xbc, 0xab, 0x8d, 0x06, + 0x9f, 0xc3, 0x3b, 0xd8, 0x01, 0xb0, 0x3a, 0xde, 0xa2, 0xe1, 0xfb, + 0xc5, 0xaa, 0x46, 0x3d, 0x08, 0xca, 0x19, 0x89, 0x6d, 0x2b, 0xf5, + 0x9a, 0x07, 0x1b, 0x85, 0x1e, 0x6c, 0x23, 0x90, 0x52, 0x17, 0x2f, + 0x29, 0x6b, 0xfb, 0x5e, 0x72, 0x40, 0x47, 0x90, 0xa2, 0x18, 0x10, + 0x14, 0xf3, 0xb9, 0x4a, 0x4e, 0x97, 0xd1, 0x17, 0xb4, 0x38, 0x13, + 0x03, 0x68, 0xcc, 0x39, 0xdb, 0xb2, 0xd1, 0x98, 0x06, 0x5a, 0xe3, + 0x98, 0x65, 0x47, 0x92, 0x6c, 0xd2, 0x16, 0x2f, 0x40, 0xa2, 0x9f, + 0x0c, 0x3c, 0x87, 0x45, 0xc0, 0xf5, 0x0f, 0xba, 0x38, 0x52, 0xe5, + 0x66, 0xd4, 0x45, 0x75, 0xc2, 0x9d, 0x39, 0xa0, 0x3f, 0x0c, 0xda, + 0x72, 0x19, 0x84, 0xb6, 0xf4, 0x40, 0x59, 0x1f, 0x35, 0x5e, 0x12, + 0xd4, 0x39, 0xff, 0x15, 0x0a, 0xab, 0x76, 0x13, 0x49, 0x9d, 0xbd, + 0x49, 0xad, 0xab, 0xc8, 0x67, 0x6e, 0xef, 0x02, 0x3b, 0x15, 0xb6, + 0x5b, 0xfc, 0x5c, 0xa0, 0x69, 0x48, 0x10, 0x9f, 0x23, 0xf3, 0x50, + 0xdb, 0x82, 0x12, 0x35, 0x35, 0xeb, 0x8a, 0x74, 0x33, 0xbd, 0xab, + 0xcb, 0x90, 0x92, 0x71, 0xa6, 0xec, 0xbc, 0xb5, 0x8b, 0x93, 0x6a, + 0x88, 0xcd, 0x4e, 0x8f, 0x2e, 0x6f, 0xf5, 0x80, 0x01, 0x75, 0xf1, + 0x13, 0x25, 0x3d, 0x8f, 0xa9, 0xca, 0x88, 0x85, 0xc2, 0xf5, 0x52, + 0xe6, 0x57, 0xdc, 0x60, 0x3f, 0x25, 0x2e, 0x1a, 0x8e, 0x30, 0x8f, + 0x76, 0xf0, 0xbe, 0x79, 0xe2, 0xfb, 0x8f, 0x5d, 0x5f, 0xbb, 0xe2, + 0xe3, 0x0e, 0xca, 0xdd, 0x22, 0x07, 0x23, 0xc8, 0xc0, 0xae, 0xa8, + 0x07, 0x8c, 0xdf, 0xcb, 0x38, 0x68, 0x26, 0x3f, 0xf8, 0xf0, 0x94, + 0x00, 0x54, 0xda, 0x48, 0x78, 0x18, 0x93, 0xa7, 0xe4, 0x9a, 0xd5, + 0xaf, 0xf4, 0xaf, 0x30, 0x0c, 0xd8, 0x04, 0xa6, 0xb6, 0x27, 0x9a, + 0xb3, 0xff, 0x3a, 0xfb, 0x64, 0x49, 0x1c, 0x85, 0x19, 0x4a, 0xab, + 0x76, 0x0d, 0x58, 0xa6, 0x06, 0x65, 0x4f, 0x9f, 0x44, 0x00, 0xe8, + 0xb3, 0x85, 0x91, 0x35, 0x6f, 0xbf, 0x64, 0x25, 0xac, 0xa2, 0x6d, + 0xc8, 0x52, 0x44, 0x25, 0x9f, 0xf2, 0xb1, 0x9c, 0x41, 0xb9, 0xf9, + 0x6f, 0x3c, 0xa9, 0xec, 0x1d, 0xde, 0x43, 0x4d, 0xa7, 0xd2, 0xd3, + 0x92, 0xb9, 0x05, 0xdd, 0xf3, 0xd1, 0xf9, 0xaf, 0x93, 0xd1, 0xaf, + 0x59, 0x50, 0xbd, 0x49, 0x3f, 0x5a, 0xa7, 0x31, 0xb4, 0x05, 0x6d, + 0xf3, 0x1b, 0xd2, 0x67, 0xb6, 0xb9, 0x0a, 0x07, 0x98, 0x31, 0xaa, + 0xf5, 0x79, 0xbe, 0x0a, 0x39, 0x01, 0x31, 0x37, 0xaa, 0xc6, 0xd4, + 0x04, 0xf5, 0x18, 0xcf, 0xd4, 0x68, 0x40, 0x64, 0x7e, 0x78, 0xbf, + 0xe7, 0x06, 0xca, 0x4c, 0xf5, 0xe9, 0xc5, 0x45, 0x3e, 0x9f, 0x7c, + 0xfd, 0x2b, 0x8b, 0x4c, 0x8d, 0x16, 0x9a, 0x44, 0xe5, 0x5c, 0x88, + 0xd4, 0xa9, 0xa7, 0xf9, 0x47, 0x42, 0x41, 0x10, 0x92, 0xab, 0xbd, + 0xf8, 0xb8, 0x89, 0xe5, 0xc1, 0x99, 0xd0, 0x96, 0xe3, 0xf2, 0x47, + 0x88, + ]; + + assert_encrypt_initial_pkt( + &mut header, + &dcid, + &frames, + 2, + 4, + false, + &pkt, + ); + } + + #[test] + fn encrypt_client_initial_draft29() { let mut header = [ 0xc3, 0xff, 0x00, 0x00, 0x1d, 0x08, 0x83, 0x94, 0xc8, 0xf0, 0x3e, 0x51, 0x57, 0x08, 0x00, 0x00, 0x44, 0x9e, 0x00, 0x00, 0x00, 0x02, @@ -1799,7 +2349,7 @@ mod tests { } #[test] - fn encrypt_client_initial_old() { + fn encrypt_client_initial_draft28() { let mut header = [ 0xc3, 0xff, 0x00, 0x00, 0x1c, 0x08, 0x83, 0x94, 0xc8, 0xf0, 0x3e, 0x51, 0x57, 0x08, 0x00, 0x00, 0x44, 0x9e, 0x00, 0x00, 0x00, 0x02, @@ -2041,7 +2591,47 @@ mod tests { } #[test] - fn encrypt_server_initial() { + fn encrypt_server_initial_v1() { + let mut header = [ + 0xc1, 0x00, 0x00, 0x00, 0x01, 0x00, 0x08, 0xf0, 0x67, 0xa5, 0x50, + 0x2a, 0x42, 0x62, 0xb5, 0x00, 0x40, 0x75, 0x00, 0x01, + ]; + + let dcid = [0x83, 0x94, 0xc8, 0xf0, 0x3e, 0x51, 0x57, 0x08]; + + let frames = [ + 0x02, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x40, 0x5a, 0x02, 0x00, + 0x00, 0x56, 0x03, 0x03, 0xee, 0xfc, 0xe7, 0xf7, 0xb3, 0x7b, 0xa1, + 0xd1, 0x63, 0x2e, 0x96, 0x67, 0x78, 0x25, 0xdd, 0xf7, 0x39, 0x88, + 0xcf, 0xc7, 0x98, 0x25, 0xdf, 0x56, 0x6d, 0xc5, 0x43, 0x0b, 0x9a, + 0x04, 0x5a, 0x12, 0x00, 0x13, 0x01, 0x00, 0x00, 0x2e, 0x00, 0x33, + 0x00, 0x24, 0x00, 0x1d, 0x00, 0x20, 0x9d, 0x3c, 0x94, 0x0d, 0x89, + 0x69, 0x0b, 0x84, 0xd0, 0x8a, 0x60, 0x99, 0x3c, 0x14, 0x4e, 0xca, + 0x68, 0x4d, 0x10, 0x81, 0x28, 0x7c, 0x83, 0x4d, 0x53, 0x11, 0xbc, + 0xf3, 0x2b, 0xb9, 0xda, 0x1a, 0x00, 0x2b, 0x00, 0x02, 0x03, 0x04, + ]; + + let pkt = [ + 0xcf, 0x00, 0x00, 0x00, 0x01, 0x00, 0x08, 0xf0, 0x67, 0xa5, 0x50, + 0x2a, 0x42, 0x62, 0xb5, 0x00, 0x40, 0x75, 0xc0, 0xd9, 0x5a, 0x48, + 0x2c, 0xd0, 0x99, 0x1c, 0xd2, 0x5b, 0x0a, 0xac, 0x40, 0x6a, 0x58, + 0x16, 0xb6, 0x39, 0x41, 0x00, 0xf3, 0x7a, 0x1c, 0x69, 0x79, 0x75, + 0x54, 0x78, 0x0b, 0xb3, 0x8c, 0xc5, 0xa9, 0x9f, 0x5e, 0xde, 0x4c, + 0xf7, 0x3c, 0x3e, 0xc2, 0x49, 0x3a, 0x18, 0x39, 0xb3, 0xdb, 0xcb, + 0xa3, 0xf6, 0xea, 0x46, 0xc5, 0xb7, 0x68, 0x4d, 0xf3, 0x54, 0x8e, + 0x7d, 0xde, 0xb9, 0xc3, 0xbf, 0x9c, 0x73, 0xcc, 0x3f, 0x3b, 0xde, + 0xd7, 0x4b, 0x56, 0x2b, 0xfb, 0x19, 0xfb, 0x84, 0x02, 0x2f, 0x8e, + 0xf4, 0xcd, 0xd9, 0x37, 0x95, 0xd7, 0x7d, 0x06, 0xed, 0xbb, 0x7a, + 0xaf, 0x2f, 0x58, 0x89, 0x18, 0x50, 0xab, 0xbd, 0xca, 0x3d, 0x20, + 0x39, 0x8c, 0x27, 0x64, 0x56, 0xcb, 0xc4, 0x21, 0x58, 0x40, 0x7d, + 0xd0, 0x74, 0xee, + ]; + + assert_encrypt_initial_pkt(&mut header, &dcid, &frames, 1, 2, true, &pkt); + } + + #[test] + fn encrypt_server_initial_draft29() { let mut header = [ 0xc1, 0xff, 0x00, 0x00, 0x1d, 0x00, 0x08, 0xf0, 0x67, 0xa5, 0x50, 0x2a, 0x42, 0x62, 0xb5, 0x00, 0x40, 0x74, 0x00, 0x01, @@ -2081,7 +2671,7 @@ mod tests { } #[test] - fn encrypt_server_initial_old() { + fn encrypt_server_initial_draft28() { let mut header = [ 0xc1, 0xff, 0x00, 0x00, 0x1c, 0x00, 0x08, 0xf0, 0x67, 0xa5, 0x50, 0x2a, 0x42, 0x62, 0xb5, 0x00, 0x40, 0x74, 0x00, 0x01, @@ -2178,8 +2768,8 @@ mod tests { let hdr = Header { ty: Type::Initial, version: crate::PROTOCOL_VERSION, - dcid: Vec::new(), - scid: Vec::new(), + dcid: ConnectionId::default(), + scid: ConnectionId::default(), pkt_num: 0, pkt_num_len: 0, token: None, diff --git a/src/recovery/cubic.rs b/src/recovery/cubic.rs index a53c68b..8b091c4 100644 --- a/src/recovery/cubic.rs +++ b/src/recovery/cubic.rs @@ -26,8 +26,8 @@ //! CUBIC Congestion Control //! -//! This implementation is based on the following RFC: -//! https://tools.ietf.org/html/rfc8312 +//! This implementation is based on the following draft: +//! <https://tools.ietf.org/html/draft-ietf-tcpm-rfc8312bis-02> //! //! Note that Slow Start can use HyStart++ when enabled. @@ -49,6 +49,9 @@ pub static CUBIC: CongestionControlOps = CongestionControlOps { on_packet_acked, congestion_event, collapse_cwnd, + checkpoint, + rollback, + has_custom_pacing, }; /// CUBIC Constants. @@ -58,23 +61,55 @@ const BETA_CUBIC: f64 = 0.7; const C: f64 = 0.4; +/// The packet count threshold to restore to the prior state if the +/// lost packet count since the last checkpoint is less than the threshold. +const RESTORE_COUNT_THRESHOLD: usize = 10; + +/// Default value of alpha_aimd in the beginning of congestion avoidance. +const ALPHA_AIMD: f64 = 3.0 * (1.0 - BETA_CUBIC) / (1.0 + BETA_CUBIC); + /// CUBIC State Variables. /// /// We need to keep those variables across the connection. -/// k, w_max, w_last_max is described in the RFC. +/// k, w_max, w_est are described in the RFC. #[derive(Debug, Default)] pub struct State { k: f64, w_max: f64, - w_last_max: f64, + w_est: f64, + + alpha_aimd: f64, // Used in CUBIC fix (see on_packet_sent()) last_sent_time: Option<Instant>, // Store cwnd increment during congestion avoidance. cwnd_inc: usize, + + // CUBIC state checkpoint preceding the last congestion event. + prior: PriorState, +} + +/// Stores the CUBIC state from before the last congestion event. +/// +/// <https://tools.ietf.org/id/draft-ietf-tcpm-rfc8312bis-00.html#section-4.9> +#[derive(Debug, Default)] +struct PriorState { + congestion_window: usize, + + ssthresh: usize, + + w_max: f64, + + w_last_max: f64, + + k: f64, + + epoch_start: Option<Instant>, + + lost_count: usize, } /// CUBIC Functions. @@ -83,28 +118,27 @@ pub struct State { /// not packets. /// Unit of t (duration) and RTT are based on seconds (f64). impl State { - // K = cbrt(w_max * (1 - beta_cubic) / C) (Eq. 2) - fn cubic_k(&self) -> f64 { - let w_max = self.w_max / recovery::MAX_DATAGRAM_SIZE as f64; - libm::cbrt(w_max * (1.0 - BETA_CUBIC) / C) + // K = cubic_root ((w_max - cwnd) / C) (Eq. 2) + fn cubic_k(&self, cwnd: usize, max_datagram_size: usize) -> f64 { + let w_max = self.w_max / max_datagram_size as f64; + let cwnd = cwnd as f64 / max_datagram_size as f64; + + libm::cbrt((w_max - cwnd) / C) } - // W_cubic(t) = C * (t - K)^3 - w_max (Eq. 1) - fn w_cubic(&self, t: Duration) -> f64 { - let w_max = self.w_max / recovery::MAX_DATAGRAM_SIZE as f64; + // W_cubic(t) = C * (t - K)^3 + w_max (Eq. 1) + fn w_cubic(&self, t: Duration, max_datagram_size: usize) -> f64 { + let w_max = self.w_max / max_datagram_size as f64; (C * (t.as_secs_f64() - self.k).powi(3) + w_max) * - recovery::MAX_DATAGRAM_SIZE as f64 + max_datagram_size as f64 } - // W_est(t) = w_max * beta_cubic + 3 * (1 - beta_cubic) / (1 + beta_cubic) * - // (t / RTT) (Eq. 4) - fn w_est(&self, t: Duration, rtt: Duration) -> f64 { - let w_max = self.w_max / recovery::MAX_DATAGRAM_SIZE as f64; - (w_max * BETA_CUBIC + - 3.0 * (1.0 - BETA_CUBIC) / (1.0 + BETA_CUBIC) * t.as_secs_f64() / - rtt.as_secs_f64()) * - recovery::MAX_DATAGRAM_SIZE as f64 + // W_est = W_est + alpha_aimd * (segments_acked / cwnd) (Eq. 4) + fn w_est_inc( + &self, acked: usize, cwnd: usize, max_datagram_size: usize, + ) -> f64 { + self.alpha_aimd * (acked as f64 / cwnd as f64) * max_datagram_size as f64 } } @@ -113,12 +147,14 @@ fn collapse_cwnd(r: &mut Recovery) { r.congestion_recovery_start_time = None; - cubic.w_last_max = r.congestion_window as f64; - cubic.w_max = cubic.w_last_max; + cubic.w_max = r.congestion_window as f64; // 4.7 Timeout - reduce ssthresh based on BETA_CUBIC r.ssthresh = (r.congestion_window as f64 * BETA_CUBIC) as usize; - r.ssthresh = cmp::max(r.ssthresh, recovery::MINIMUM_WINDOW); + r.ssthresh = cmp::max( + r.ssthresh, + r.max_datagram_size * recovery::MINIMUM_WINDOW_PACKETS, + ); cubic.cwnd_inc = 0; @@ -165,32 +201,60 @@ fn on_packet_acked( return; } + // Detecting spurious congestion events. + // <https://tools.ietf.org/id/draft-ietf-tcpm-rfc8312bis-00.html#section-4.9> + // + // When the recovery episode ends with recovering + // a few packets (less than RESTORE_COUNT_THRESHOLD), it's considered + // as spurious and restore to the previous state. + if r.congestion_recovery_start_time.is_some() { + let new_lost = r.lost_count - r.cubic_state.prior.lost_count; + + if r.congestion_window < r.cubic_state.prior.congestion_window && + new_lost < RESTORE_COUNT_THRESHOLD + { + rollback(r); + return; + } + } + if r.congestion_window < r.ssthresh { - // Slow start. - if r.hystart.enabled() && epoch == packet::EPOCH_APPLICATION { - let (cwnd, ssthresh) = r.hystart_on_packet_acked(packet, now); + // In Slow slart, bytes_acked_sl is used for counting + // acknowledged bytes. + r.bytes_acked_sl += packet.size; - r.congestion_window = cwnd; - r.ssthresh = ssthresh; - } else { - // Reno Slow Start. - r.congestion_window += packet.size; + if r.bytes_acked_sl >= r.max_datagram_size { + r.congestion_window += r.max_datagram_size; + r.bytes_acked_sl -= r.max_datagram_size; + } + + if r.hystart.enabled() && + epoch == packet::EPOCH_APPLICATION && + r.hystart.try_enter_lss( + packet, + r.latest_rtt, + r.congestion_window, + now, + r.max_datagram_size, + ) + { + r.ssthresh = r.congestion_window; } } else { // Congestion avoidance. let ca_start_time; // In LSS, use lss_start_time instead of congestion_recovery_start_time. - if r.hystart.enabled() && - epoch == packet::EPOCH_APPLICATION && - r.hystart.lss_start_time().is_some() - { + if r.hystart.in_lss(epoch) { ca_start_time = r.hystart.lss_start_time().unwrap(); // Reset w_max and k when LSS started. if r.cubic_state.w_max == 0.0 { r.cubic_state.w_max = r.congestion_window as f64; r.cubic_state.k = 0.0; + + r.cubic_state.w_est = r.congestion_window as f64; + r.cubic_state.alpha_aimd = ALPHA_AIMD; } } else { match r.congestion_recovery_start_time { @@ -203,51 +267,65 @@ fn on_packet_acked( r.cubic_state.w_max = r.congestion_window as f64; r.cubic_state.k = 0.0; + + r.cubic_state.w_est = r.congestion_window as f64; + r.cubic_state.alpha_aimd = ALPHA_AIMD; }, } } let t = now - ca_start_time; - // w_cubic(t + rtt) - let w_cubic = r.cubic_state.w_cubic(t + r.min_rtt); + // target = w_cubic(t + rtt) + let target = r.cubic_state.w_cubic(t + r.min_rtt, r.max_datagram_size); + + // Clipping target to [cwnd, 1.5 x cwnd] + let target = f64::max(target, r.congestion_window as f64); + let target = f64::min(target, r.congestion_window as f64 * 1.5); - // w_est(t) - let w_est = r.cubic_state.w_est(t, r.min_rtt); + // Update w_est. + let w_est_inc = r.cubic_state.w_est_inc( + packet.size, + r.congestion_window, + r.max_datagram_size, + ); + r.cubic_state.w_est += w_est_inc; + + if r.cubic_state.w_est >= r.cubic_state.w_max { + r.cubic_state.alpha_aimd = 1.0; + } let mut cubic_cwnd = r.congestion_window; - if w_cubic < w_est { - // TCP friendly region. - cubic_cwnd = cmp::max(cubic_cwnd, w_est as usize); - } else if cubic_cwnd < w_cubic as usize { + if r.cubic_state.w_cubic(t, r.max_datagram_size) < r.cubic_state.w_est { + // AIMD friendly region (W_cubic(t) < W_est) + cubic_cwnd = cmp::max(cubic_cwnd, r.cubic_state.w_est as usize); + } else { // Concave region or convex region use same increment. - let cubic_inc = (w_cubic - cubic_cwnd as f64) / cubic_cwnd as f64 * - recovery::MAX_DATAGRAM_SIZE as f64; + let cubic_inc = + r.max_datagram_size * (target as usize - cubic_cwnd) / cubic_cwnd; - cubic_cwnd += cubic_inc as usize; + cubic_cwnd += cubic_inc; } // When in Limited Slow Start, take the max of CA cwnd and // LSS cwnd. - if r.hystart.enabled() && - epoch == packet::EPOCH_APPLICATION && - r.hystart.lss_start_time().is_some() - { - let (lss_cwnd, _) = r.hystart_on_packet_acked(packet, now); + if r.hystart.in_lss(epoch) { + let lss_cwnd_inc = r.hystart.lss_cwnd_inc( + packet.size, + r.congestion_window, + r.ssthresh, + ); - cubic_cwnd = cmp::max(cubic_cwnd, lss_cwnd); + cubic_cwnd = cmp::max(cubic_cwnd, r.congestion_window + lss_cwnd_inc); } // Update the increment and increase cwnd by MSS. r.cubic_state.cwnd_inc += cubic_cwnd - r.congestion_window; - // cwnd_inc can be more than 1 MSS in the late stage of max probing. - // however QUIC recovery draft 7.4 (Congestion Avoidance) limits - // the increase of cwnd to 1 max packet size per cwnd acknowledged. - if r.cubic_state.cwnd_inc >= recovery::MAX_DATAGRAM_SIZE { - r.congestion_window += recovery::MAX_DATAGRAM_SIZE; - r.cubic_state.cwnd_inc -= recovery::MAX_DATAGRAM_SIZE; + if r.cubic_state.cwnd_inc >= r.max_datagram_size { + r.congestion_window += r.max_datagram_size; + r.cubic_state.cwnd_inc -= r.max_datagram_size; } } } @@ -263,29 +341,60 @@ fn congestion_event( r.congestion_recovery_start_time = Some(now); // Fast convergence - if r.cubic_state.w_max < r.cubic_state.w_last_max { - r.cubic_state.w_last_max = r.cubic_state.w_max; + if (r.congestion_window as f64) < r.cubic_state.w_max { r.cubic_state.w_max = - r.cubic_state.w_max as f64 * (1.0 + BETA_CUBIC) / 2.0; + r.congestion_window as f64 * (1.0 + BETA_CUBIC) / 2.0; } else { - r.cubic_state.w_last_max = r.cubic_state.w_max; + r.cubic_state.w_max = r.congestion_window as f64; } - r.cubic_state.w_max = r.congestion_window as f64; - r.ssthresh = (r.cubic_state.w_max * BETA_CUBIC) as usize; - r.ssthresh = cmp::max(r.ssthresh, recovery::MINIMUM_WINDOW); + r.ssthresh = (r.congestion_window as f64 * BETA_CUBIC) as usize; + r.ssthresh = cmp::max( + r.ssthresh, + r.max_datagram_size * recovery::MINIMUM_WINDOW_PACKETS, + ); r.congestion_window = r.ssthresh; - r.cubic_state.k = r.cubic_state.cubic_k(); + + r.cubic_state.k = if r.cubic_state.w_max < r.congestion_window as f64 { + 0.0 + } else { + r.cubic_state + .cubic_k(r.congestion_window, r.max_datagram_size) + }; r.cubic_state.cwnd_inc = (r.cubic_state.cwnd_inc as f64 * BETA_CUBIC) as usize; - if r.hystart.enabled() && epoch == packet::EPOCH_APPLICATION { + r.cubic_state.w_est = r.congestion_window as f64; + r.cubic_state.alpha_aimd = ALPHA_AIMD; + + if r.hystart.in_lss(epoch) { r.hystart.congestion_event(); } } } +fn checkpoint(r: &mut Recovery) { + r.cubic_state.prior.congestion_window = r.congestion_window; + r.cubic_state.prior.ssthresh = r.ssthresh; + r.cubic_state.prior.w_max = r.cubic_state.w_max; + r.cubic_state.prior.k = r.cubic_state.k; + r.cubic_state.prior.epoch_start = r.congestion_recovery_start_time; + r.cubic_state.prior.lost_count = r.lost_count; +} + +fn rollback(r: &mut Recovery) { + r.congestion_window = r.cubic_state.prior.congestion_window; + r.ssthresh = r.cubic_state.prior.ssthresh; + r.cubic_state.w_max = r.cubic_state.prior.w_max; + r.cubic_state.k = r.cubic_state.prior.k; + r.congestion_recovery_start_time = r.cubic_state.prior.epoch_start; +} + +fn has_custom_pacing() -> bool { + false +} + #[cfg(test)] mod tests { use super::*; @@ -328,7 +437,7 @@ mod tests { time_sent: now, time_acked: None, time_lost: None, - size: 5000, + size: r.max_datagram_size, ack_eliciting: true, in_flight: true, delivered: 0, @@ -338,12 +447,10 @@ mod tests { has_data: false, }; - // Send 5k x 4 = 20k, higher than default cwnd(~15k) - // to become no longer app limited - r.on_packet_sent_cc(p.size, now); - r.on_packet_sent_cc(p.size, now); - r.on_packet_sent_cc(p.size, now); - r.on_packet_sent_cc(p.size, now); + // Send initcwnd full MSS packets to become no longer app limited + for _ in 0..recovery::INITIAL_WINDOW_PACKETS { + r.on_packet_sent_cc(p.size, now); + } let cwnd_prev = r.cwnd(); @@ -360,6 +467,61 @@ mod tests { } #[test] + fn cubic_slow_start_multi_acks() { + let mut cfg = crate::Config::new(crate::PROTOCOL_VERSION).unwrap(); + cfg.set_cc_algorithm(recovery::CongestionControlAlgorithm::CUBIC); + + let mut r = Recovery::new(&cfg); + let now = Instant::now(); + + let p = recovery::Sent { + pkt_num: 0, + frames: vec![], + time_sent: now, + time_acked: None, + time_lost: None, + size: r.max_datagram_size, + ack_eliciting: true, + in_flight: true, + delivered: 0, + delivered_time: now, + recent_delivered_packet_sent_time: now, + is_app_limited: false, + has_data: false, + }; + + // Send initcwnd full MSS packets to become no longer app limited + for _ in 0..recovery::INITIAL_WINDOW_PACKETS { + r.on_packet_sent_cc(p.size, now); + } + + let cwnd_prev = r.cwnd(); + + let acked = vec![ + Acked { + pkt_num: p.pkt_num, + time_sent: p.time_sent, + size: p.size, + }, + Acked { + pkt_num: p.pkt_num, + time_sent: p.time_sent, + size: p.size, + }, + Acked { + pkt_num: p.pkt_num, + time_sent: p.time_sent, + size: p.size, + }, + ]; + + r.on_packets_acked(acked, packet::EPOCH_APPLICATION, now); + + // Acked 3 packets. + assert_eq!(r.cwnd(), cwnd_prev + p.size * 3); + } + + #[test] fn cubic_congestion_event() { let mut cfg = crate::Config::new(crate::PROTOCOL_VERSION).unwrap(); cfg.set_cc_algorithm(recovery::CongestionControlAlgorithm::CUBIC); @@ -381,11 +543,13 @@ mod tests { cfg.set_cc_algorithm(recovery::CongestionControlAlgorithm::CUBIC); let mut r = Recovery::new(&cfg); - let now = Instant::now(); + let mut now = Instant::now(); let prev_cwnd = r.cwnd(); - // Fill up bytes_in_flight to avoid app_limited=true - r.on_packet_sent_cc(20000, now); + // Send initcwnd full MSS packets to become no longer app limited + for _ in 0..recovery::INITIAL_WINDOW_PACKETS { + r.on_packet_sent_cc(r.max_datagram_size, now); + } // Trigger congestion event to update ssthresh r.congestion_event(now, packet::EPOCH_APPLICATION, now); @@ -394,21 +558,31 @@ mod tests { let cur_cwnd = (prev_cwnd as f64 * BETA_CUBIC) as usize; assert_eq!(r.cwnd(), cur_cwnd); + // Shift current time by 1 RTT. let rtt = Duration::from_millis(100); - let acked = vec![Acked { - pkt_num: 0, - // To exit from recovery - time_sent: now + rtt, - size: 8000, - }]; - - // Ack more than cwnd bytes with rtt=100ms r.update_rtt(rtt, Duration::from_millis(0), now); - r.on_packets_acked(acked, packet::EPOCH_APPLICATION, now + rtt * 3); - // After acking more than cwnd, expect cwnd increased by MSS - assert_eq!(r.cwnd(), cur_cwnd + recovery::MAX_DATAGRAM_SIZE); + // Exit from the recovery. + now += rtt; + + // To avoid rollback + r.lost_count += RESTORE_COUNT_THRESHOLD; + + // During Congestion Avoidance, it will take + // 5 ACKs to increase cwnd by 1 MSS. + for _ in 0..5 { + let acked = vec![Acked { + pkt_num: 0, + time_sent: now, + size: r.max_datagram_size, + }]; + + r.on_packets_acked(acked, packet::EPOCH_APPLICATION, now); + now += rtt; + } + + assert_eq!(r.cwnd(), cur_cwnd + r.max_datagram_size); } #[test] @@ -425,29 +599,27 @@ mod tests { // Trigger congestion event to update ssthresh r.congestion_event(now, packet::EPOCH_APPLICATION, now); - // After persistent congestion, cwnd should be MINIMUM_WINDOW + // After persistent congestion, cwnd should be the minimum window r.collapse_cwnd(); - assert_eq!(r.cwnd(), recovery::MINIMUM_WINDOW); + assert_eq!( + r.cwnd(), + r.max_datagram_size * recovery::MINIMUM_WINDOW_PACKETS + ); let acked = vec![Acked { pkt_num: 0, // To exit from recovery time_sent: now + Duration::from_millis(1), - size: 10000, + size: r.max_datagram_size, }]; - // rtt = 100ms - let rtt = Duration::from_millis(100); - std::thread::sleep(rtt); - - // Ack 10000 x 2 to exit from slow start - r.on_packets_acked(acked.clone(), packet::EPOCH_APPLICATION, now); - std::thread::sleep(rtt); - - // This will make CC into congestion avoidance mode r.on_packets_acked(acked, packet::EPOCH_APPLICATION, now); - assert_eq!(r.cwnd(), recovery::MINIMUM_WINDOW + 10000); + // Slow start again - cwnd will be increased by 1 MSS + assert_eq!( + r.cwnd(), + r.max_datagram_size * (recovery::MINIMUM_WINDOW_PACKETS + 1) + ); } #[test] @@ -467,7 +639,7 @@ mod tests { time_sent: now, time_acked: None, time_lost: None, - size: recovery::MAX_DATAGRAM_SIZE, + size: r.max_datagram_size, ack_eliciting: true, in_flight: true, delivered: 0, @@ -511,13 +683,13 @@ mod tests { assert_eq!(r.hystart.lss_start_time().is_some(), false); // 2nd round. - r.hystart.start_round(pkts_1st_round * 2 + 1); + r.hystart.start_round(pkts_1st_round * 2); let mut rtt_2nd = 100; let now = now + Duration::from_millis(rtt_2nd); // Send 2nd round packets. - for _ in 0..n_rtt_sample + 1 { + for _ in 0..n_rtt_sample { r.on_packet_sent_cc(p.size, now); } @@ -525,7 +697,7 @@ mod tests { // Last ack will cause to exit to LSS. let mut cwnd_prev = r.cwnd(); - for _ in 0..n_rtt_sample + 1 { + for _ in 0..n_rtt_sample { cwnd_prev = r.cwnd(); r.update_rtt( Duration::from_millis(rtt_2nd), @@ -547,10 +719,14 @@ mod tests { // Now we are in LSS. assert_eq!(r.hystart.lss_start_time().is_some(), true); - assert_eq!(r.cwnd(), cwnd_prev); + assert_eq!(r.cwnd(), cwnd_prev + r.max_datagram_size); + + // Send a full cwnd. + r.on_packet_sent_cc(r.cwnd(), now); - // Ack'ing more packet to increase cwnd by 1 MSS - for _ in 0..3 { + // Ack'ing 4 packets to increase cwnd by 1 MSS during LSS + cwnd_prev = r.cwnd(); + for _ in 0..4 { let acked = vec![Acked { pkt_num: p.pkt_num, time_sent: p.time_sent, @@ -559,6 +735,115 @@ mod tests { r.on_packets_acked(acked, epoch, now); } - assert_eq!(r.cwnd(), cwnd_prev + recovery::MAX_DATAGRAM_SIZE); + // During LSS cwnd will be increased less than usual slow start. + assert_eq!(r.cwnd(), cwnd_prev + r.max_datagram_size); + } + + #[test] + fn cubic_spurious_congestion_event() { + let mut cfg = crate::Config::new(crate::PROTOCOL_VERSION).unwrap(); + cfg.set_cc_algorithm(recovery::CongestionControlAlgorithm::CUBIC); + + let mut r = Recovery::new(&cfg); + let now = Instant::now(); + let prev_cwnd = r.cwnd(); + + // Send initcwnd full MSS packets to become no longer app limited + for _ in 0..recovery::INITIAL_WINDOW_PACKETS { + r.on_packet_sent_cc(r.max_datagram_size, now); + } + + // Trigger congestion event to update ssthresh + r.congestion_event(now, packet::EPOCH_APPLICATION, now); + + // After congestion event, cwnd will be reduced. + let cur_cwnd = (prev_cwnd as f64 * BETA_CUBIC) as usize; + assert_eq!(r.cwnd(), cur_cwnd); + + let rtt = Duration::from_millis(100); + + let acked = vec![Acked { + pkt_num: 0, + // To exit from recovery + time_sent: now + rtt, + size: r.max_datagram_size, + }]; + + // Ack more than cwnd bytes with rtt=100ms + r.update_rtt(rtt, Duration::from_millis(0), now); + + // Trigger detecting sprurious congestion event + r.on_packets_acked( + acked, + packet::EPOCH_APPLICATION, + now + rtt + Duration::from_millis(5), + ); + + // cwnd is restored to the previous one. + assert_eq!(r.cwnd(), prev_cwnd); + } + + #[test] + fn cubic_fast_convergence() { + let mut cfg = crate::Config::new(crate::PROTOCOL_VERSION).unwrap(); + cfg.set_cc_algorithm(recovery::CongestionControlAlgorithm::CUBIC); + + let mut r = Recovery::new(&cfg); + let mut now = Instant::now(); + let prev_cwnd = r.cwnd(); + + // Send initcwnd full MSS packets to become no longer app limited + for _ in 0..recovery::INITIAL_WINDOW_PACKETS { + r.on_packet_sent_cc(r.max_datagram_size, now); + } + + // Trigger congestion event to update ssthresh + r.congestion_event(now, packet::EPOCH_APPLICATION, now); + + // After 1st congestion event, cwnd will be reduced. + let cur_cwnd = (prev_cwnd as f64 * BETA_CUBIC) as usize; + assert_eq!(r.cwnd(), cur_cwnd); + + // Shift current time by 1 RTT. + let rtt = Duration::from_millis(100); + r.update_rtt(rtt, Duration::from_millis(0), now); + + // Exit from the recovery. + now += rtt; + + // To avoid rollback + r.lost_count += RESTORE_COUNT_THRESHOLD; + + // During Congestion Avoidance, it will take + // 5 ACKs to increase cwnd by 1 MSS. + for _ in 0..5 { + let acked = vec![Acked { + pkt_num: 0, + time_sent: now, + size: r.max_datagram_size, + }]; + + r.on_packets_acked(acked, packet::EPOCH_APPLICATION, now); + now += rtt; + } + + assert_eq!(r.cwnd(), cur_cwnd + r.max_datagram_size); + + let prev_cwnd = r.cwnd(); + + // Fast convergence: now there is 2nd congestion event and + // cwnd is not fully recovered to w_max, w_max will be + // further reduced. + r.congestion_event(now, packet::EPOCH_APPLICATION, now); + + // After 2nd congestion event, cwnd will be reduced. + let cur_cwnd = (prev_cwnd as f64 * BETA_CUBIC) as usize; + assert_eq!(r.cwnd(), cur_cwnd); + + // w_max will be further reduced, not prev_cwnd + assert_eq!( + r.cubic_state.w_max, + prev_cwnd as f64 * (1.0 + BETA_CUBIC) / 2.0 + ); } } diff --git a/src/recovery/delivery_rate.rs b/src/recovery/delivery_rate.rs index 77fd248..97edeba 100644 --- a/src/recovery/delivery_rate.rs +++ b/src/recovery/delivery_rate.rs @@ -27,7 +27,7 @@ //! Delivery rate estimation. //! //! This implements the algorithm for estimating delivery rate as described in -//! https://tools.ietf.org/html/draft-cheng-iccrg-delivery-rate-estimation-00 +//! <https://tools.ietf.org/html/draft-cheng-iccrg-delivery-rate-estimation-00> use std::cmp; @@ -76,7 +76,6 @@ impl Rate { if pkt.delivered > self.rate_sample.prior_delivered { self.rate_sample.prior_delivered = pkt.delivered; - self.rate_sample.is_app_limited = pkt.is_app_limited; self.rate_sample.send_elapsed = pkt.time_sent - pkt.recent_delivered_packet_sent_time; @@ -149,8 +148,6 @@ impl std::fmt::Debug for Rate { struct RateSample { delivery_rate: u64, - is_app_limited: bool, - interval: Duration, delivered: usize, diff --git a/src/recovery/hystart.rs b/src/recovery/hystart.rs index 5d02739..6a275bc 100644 --- a/src/recovery/hystart.rs +++ b/src/recovery/hystart.rs @@ -28,12 +28,13 @@ //! //! This implementation is based on the following I-D: //! -//! https://tools.ietf.org/html/draft-balasubramanian-tcpm-hystartplusplus-02 +//! <https://tools.ietf.org/html/draft-balasubramanian-tcpm-hystartplusplus-03> use std::cmp; use std::time::Duration; use std::time::Instant; +use crate::packet; use crate::recovery; /// Constants from I-D. @@ -91,6 +92,12 @@ impl Hystart { self.lss_start_time } + pub fn in_lss(&self, epoch: packet::Epoch) -> bool { + self.enabled && + epoch == packet::EPOCH_APPLICATION && + self.lss_start_time().is_some() + } + pub fn start_round(&mut self, pkt_num: u64) { if self.window_end.is_none() { *self = Hystart { @@ -109,18 +116,12 @@ impl Hystart { } } - // Returns a new (ssthresh, cwnd) during slow start. - pub fn on_packet_acked( + // Returns true if LSS started. + pub fn try_enter_lss( &mut self, packet: &recovery::Acked, rtt: Duration, cwnd: usize, - ssthresh: usize, now: Instant, - ) -> (usize, usize) { - let mut ssthresh = ssthresh; - let mut cwnd = cwnd; - + now: Instant, max_datagram_size: usize, + ) -> bool { if self.lss_start_time().is_none() { - // Reno Slow Start. - cwnd += packet.size; - if let Some(current_round_min_rtt) = self.current_round_min_rtt { self.current_round_min_rtt = Some(cmp::min(current_round_min_rtt, rtt)); @@ -130,7 +131,7 @@ impl Hystart { self.rtt_sample_count += 1; - if cwnd >= (LOW_CWND * recovery::MAX_DATAGRAM_SIZE) && + if cwnd >= (LOW_CWND * max_datagram_size) && self.rtt_sample_count >= N_RTT_SAMPLE && self.current_round_min_rtt.is_some() && self.last_round_min_rtt.is_some() @@ -147,8 +148,6 @@ impl Hystart { if self.current_round_min_rtt.unwrap() >= (self.last_round_min_rtt.unwrap() + rtt_thresh) { - ssthresh = cwnd; - self.lss_start_time = Some(now); } } @@ -160,23 +159,22 @@ impl Hystart { self.window_end = None; } } - } else { - // LSS (Limited Slow Start). - let k = cwnd as f64 / (LSS_DIVISOR * ssthresh as f64); - - cwnd += (packet.size as f64 / k) as usize; } - (cwnd, ssthresh) + self.lss_start_time.is_some() + } + + // Return a cwnd increment during LSS (Limited Slow Start). + pub fn lss_cwnd_inc( + &self, pkt_size: usize, cwnd: usize, ssthresh: usize, + ) -> usize { + pkt_size / (cwnd as f64 / (LSS_DIVISOR * ssthresh as f64)) as usize } // Exit HyStart++ when entering congestion avoidance. pub fn congestion_event(&mut self) { - if self.window_end.is_some() { - self.window_end = None; - - self.lss_start_time = None; - } + self.window_end = None; + self.lss_start_time = None; } } @@ -196,111 +194,16 @@ mod tests { } #[test] - fn reno_slow_start() { - let mut hspp = Hystart::default(); - let pkt_num = 100; - let size = 1000; - let now = Instant::now(); - - hspp.start_round(pkt_num); - - assert_eq!(hspp.window_end, Some(pkt_num)); - - let p = recovery::Acked { - pkt_num, - time_sent: now + Duration::from_millis(10), - size, - }; - - let init_cwnd = 30000; - let init_ssthresh = 1000000; - - let (cwnd, ssthresh) = hspp.on_packet_acked( - &p, - Duration::from_millis(10), - init_cwnd, - init_ssthresh, - now, - ); - - // Expecting Reno slow start. - assert_eq!(hspp.lss_start_time().is_some(), false); - assert_eq!((cwnd, ssthresh), (init_cwnd + size, init_ssthresh)); - } - - #[test] - fn limited_slow_start() { - let mut hspp = Hystart::default(); - let size = 1000; - let now = Instant::now(); - - // 1st round rtt = 50ms - let rtt_1st = 50; - - // end of 1st round - let pkt_1st = N_RTT_SAMPLE as u64; - - hspp.start_round(pkt_1st); - - assert_eq!(hspp.window_end, Some(pkt_1st)); - - let (mut cwnd, mut ssthresh) = (30000, 1000000); - let mut pkt_num = 0; - - // 1st round. - for _ in 0..N_RTT_SAMPLE + 1 { - let p = recovery::Acked { - pkt_num, - time_sent: now + Duration::from_millis(pkt_num), - size, - }; - - // We use a fixed rtt for 1st round. - let rtt = Duration::from_millis(rtt_1st); - - let (new_cwnd, new_ssthresh) = - hspp.on_packet_acked(&p, rtt, cwnd, ssthresh, now); - - cwnd = new_cwnd; - ssthresh = new_ssthresh; - - pkt_num += 1; - } - - // 2nd round. rtt = 100ms to trigger LSS. - let rtt_2nd = 100; - - hspp.start_round(pkt_1st * 2 + 1); - - for _ in 0..N_RTT_SAMPLE + 1 { - let p = recovery::Acked { - pkt_num, - time_sent: now + Duration::from_millis(pkt_num), - size, - }; - - // Keep increasing rtt to simulate buffer queueing delay - // This is to exit from slow slart to LSS. - let rtt = Duration::from_millis(rtt_2nd + pkt_num * 4); - - let (new_cwnd, new_ssthresh) = - hspp.on_packet_acked(&p, rtt, cwnd, ssthresh, now); - - cwnd = new_cwnd; - ssthresh = new_ssthresh; - - pkt_num += 1; - } + fn lss_cwnd_inc() { + let hspp = Hystart::default(); - // At this point, cwnd exits to LSS mode. - assert_eq!(hspp.lss_start_time().is_some(), true); + let datagram_size = 1200; + let cwnd = 24000; + let ssthresh = 24000; - // Check if current cwnd is in LSS. - let cur_ssthresh = 47000; - let k = cur_ssthresh as f64 / (LSS_DIVISOR * cur_ssthresh as f64); - let lss_cwnd = cur_ssthresh as f64 + size as f64 / k; + let lss_cwnd_inc = hspp.lss_cwnd_inc(datagram_size, cwnd, ssthresh); - assert_eq!((cwnd, ssthresh), (lss_cwnd as usize, cur_ssthresh)); + assert_eq!((datagram_size as f64 * LSS_DIVISOR) as usize, lss_cwnd_inc); } #[test] diff --git a/src/recovery/mod.rs b/src/recovery/mod.rs index 4621e24..2bffa79 100644 --- a/src/recovery/mod.rs +++ b/src/recovery/mod.rs @@ -60,11 +60,7 @@ const MAX_PTO_PROBES_COUNT: usize = 2; // Congestion Control const INITIAL_WINDOW_PACKETS: usize = 10; -const INITIAL_WINDOW: usize = INITIAL_WINDOW_PACKETS * MAX_DATAGRAM_SIZE; - -const MINIMUM_WINDOW: usize = 2 * MAX_DATAGRAM_SIZE; - -const MAX_DATAGRAM_SIZE: usize = 1452; +const MINIMUM_WINDOW_PACKETS: usize = 2; const LOSS_REDUCTION_FACTOR: f64 = 0.5; @@ -118,14 +114,25 @@ pub struct Recovery { ssthresh: usize, - bytes_acked: usize, + bytes_acked_sl: usize, + + bytes_acked_ca: usize, + + bytes_sent: usize, congestion_recovery_start_time: Option<Instant>, + max_datagram_size: usize, + cubic_state: cubic::State, // HyStart++. hystart: hystart::Hystart, + + // Pacing. + pacing_rate: u64, + + last_packet_scheduled_time: Option<Instant>, } impl Recovery { @@ -171,16 +178,23 @@ impl Recovery { in_flight_count: [0; packet::EPOCH_COUNT], - congestion_window: INITIAL_WINDOW, + congestion_window: config.max_send_udp_payload_size * + INITIAL_WINDOW_PACKETS, bytes_in_flight: 0, ssthresh: std::usize::MAX, - bytes_acked: 0, + bytes_acked_sl: 0, + + bytes_acked_ca: 0, + + bytes_sent: 0, congestion_recovery_start_time: None, + max_datagram_size: config.max_send_udp_payload_size, + cc_ops: config.cc_algorithm.into(), delivery_rate: delivery_rate::Rate::default(), @@ -190,6 +204,10 @@ impl Recovery { app_limited: false, hystart: hystart::Hystart::new(config.hystart), + + pacing_rate: 0, + + last_packet_scheduled_time: None, } } @@ -232,6 +250,18 @@ impl Recovery { self.hystart.start_round(pkt_num); } + // Pacing: Set the pacing rate if CC doesn't do its own. + if !(self.cc_ops.has_custom_pacing)() { + if let Some(srtt) = self.smoothed_rtt { + let rate = (self.congestion_window as u64 * 1000000) / + srtt.as_micros() as u64; + self.set_pacing_rate(rate); + } + } + + self.schedule_next_packet(epoch, now, sent_bytes); + + self.bytes_sent += sent_bytes; trace!("{} {:?}", trace_id, self); } @@ -239,6 +269,45 @@ impl Recovery { (self.cc_ops.on_packet_sent)(self, sent_bytes, now); } + pub fn set_pacing_rate(&mut self, rate: u64) { + if rate != 0 { + self.pacing_rate = rate; + } + } + + pub fn get_packet_send_time(&self) -> Option<Instant> { + self.last_packet_scheduled_time + } + + fn schedule_next_packet( + &mut self, epoch: packet::Epoch, now: Instant, packet_size: usize, + ) { + // Don't pace in any of these cases: + // * Packet epoch is not EPOCH_APPLICATION. + // * Packet contains only ACK frames. + // * The start of the connection. + if epoch != packet::EPOCH_APPLICATION || + packet_size == 0 || + self.bytes_sent <= self.congestion_window || + self.pacing_rate == 0 + { + self.last_packet_scheduled_time = Some(now); + return; + } + + self.last_packet_scheduled_time = match self.last_packet_scheduled_time { + Some(last_scheduled_time) => { + let interval: u64 = + (packet_size as u64 * 1000000) / self.pacing_rate; + let interval = Duration::from_micros(interval); + let next_schedule_time = last_scheduled_time + interval; + Some(cmp::max(now, next_schedule_time)) + }, + + None => Some(now), + }; + } + pub fn on_ack_received( &mut self, ranges: &ranges::RangeSet, ack_delay: u64, epoch: packet::Epoch, handshake_status: HandshakeStatus, now: Instant, @@ -347,8 +416,6 @@ impl Recovery { self.drain_packets(epoch); - trace!("{} {:?}", trace_id, self); - Ok(()) } @@ -469,6 +536,24 @@ impl Recovery { self.delivery_rate.delivery_rate() } + pub fn max_datagram_size(&self) -> usize { + self.max_datagram_size + } + + pub fn update_max_datagram_size(&mut self, new_max_datagram_size: usize) { + let max_datagram_size = + cmp::min(self.max_datagram_size, new_max_datagram_size); + + // Congestion Window is updated only when it's not updated already. + if self.congestion_window == + self.max_datagram_size * INITIAL_WINDOW_PACKETS + { + self.congestion_window = max_datagram_size * INITIAL_WINDOW_PACKETS; + } + + self.max_datagram_size = max_datagram_size; + } + fn update_rtt( &mut self, latest_rtt: Duration, ack_delay: Duration, now: Instant, ) { @@ -729,6 +814,10 @@ impl Recovery { fn congestion_event( &mut self, time_sent: Instant, epoch: packet::Epoch, now: Instant, ) { + if !self.in_congestion_recovery(time_sent) { + (self.cc_ops.checkpoint)(self); + } + (self.cc_ops.congestion_event)(self, time_sent, epoch, now); } @@ -742,18 +831,6 @@ impl Recovery { } } - fn hystart_on_packet_acked( - &mut self, packet: &Acked, now: Instant, - ) -> (usize, usize) { - self.hystart.on_packet_acked( - packet, - self.latest_rtt, - self.congestion_window, - self.ssthresh, - now, - ) - } - pub fn update_app_limited(&mut self, v: bool) { self.app_limited = v; } @@ -825,6 +902,12 @@ pub struct CongestionControlOps { ), pub collapse_cwnd: fn(r: &mut Recovery), + + pub checkpoint: fn(r: &mut Recovery), + + pub rollback: fn(r: &mut Recovery), + + pub has_custom_pacing: fn() -> bool, } impl From<CongestionControlAlgorithm> for &'static CongestionControlOps { @@ -871,6 +954,12 @@ impl std::fmt::Debug for Recovery { self.congestion_recovery_start_time )?; write!(f, "{:?} ", self.delivery_rate)?; + write!(f, "pacing_rate={:?} ", self.pacing_rate)?; + write!( + f, + "last_packet_scheduled_time={:?} ", + self.last_packet_scheduled_time + )?; if self.hystart.enabled() { write!(f, "hystart={:?} ", self.hystart)?; @@ -994,7 +1083,7 @@ mod tests { // cwnd will be reset. r.collapse_cwnd(); - assert_eq!(r.cwnd(), MINIMUM_WINDOW); + assert_eq!(r.cwnd(), r.max_datagram_size * MINIMUM_WINDOW_PACKETS); } #[test] @@ -1534,6 +1623,142 @@ mod tests { // Spurious loss. assert_eq!(r.lost_count, 1); } + + #[test] + fn pacing() { + let mut cfg = crate::Config::new(crate::PROTOCOL_VERSION).unwrap(); + cfg.set_cc_algorithm(CongestionControlAlgorithm::CUBIC); + + let mut r = Recovery::new(&cfg); + + let mut now = Instant::now(); + + assert_eq!(r.sent[packet::EPOCH_APPLICATION].len(), 0); + + // send out first packet. + let p = Sent { + pkt_num: 0, + frames: vec![], + time_sent: now, + time_acked: None, + time_lost: None, + size: 6500, + ack_eliciting: true, + in_flight: true, + delivered: 0, + delivered_time: now, + recent_delivered_packet_sent_time: now, + is_app_limited: false, + has_data: false, + }; + + r.on_packet_sent( + p, + packet::EPOCH_APPLICATION, + HandshakeStatus::default(), + now, + "", + ); + + assert_eq!(r.sent[packet::EPOCH_APPLICATION].len(), 1); + assert_eq!(r.bytes_in_flight, 6500); + + // First packet will be sent out immidiately. + assert_eq!(r.pacing_rate, 0); + assert_eq!(r.get_packet_send_time().unwrap(), now); + + // Wait 50ms for ACK. + now += Duration::from_millis(50); + + let mut acked = ranges::RangeSet::default(); + acked.insert(0..1); + + assert_eq!( + r.on_ack_received( + &acked, + 10, + packet::EPOCH_APPLICATION, + HandshakeStatus::default(), + now, + "" + ), + Ok(()) + ); + + assert_eq!(r.sent[packet::EPOCH_APPLICATION].len(), 0); + assert_eq!(r.bytes_in_flight, 0); + assert_eq!(r.smoothed_rtt.unwrap(), Duration::from_millis(50)); + + // Send out second packet. + let p = Sent { + pkt_num: 1, + frames: vec![], + time_sent: now, + time_acked: None, + time_lost: None, + size: 6500, + ack_eliciting: true, + in_flight: true, + delivered: 0, + delivered_time: now, + recent_delivered_packet_sent_time: now, + is_app_limited: false, + has_data: false, + }; + + r.on_packet_sent( + p, + packet::EPOCH_APPLICATION, + HandshakeStatus::default(), + now, + "", + ); + + assert_eq!(r.sent[packet::EPOCH_APPLICATION].len(), 1); + assert_eq!(r.bytes_in_flight, 6500); + + // Pacing is not done during intial phase of connection. + assert_eq!(r.get_packet_send_time().unwrap(), now); + + // Send the third packet out. + let p = Sent { + pkt_num: 2, + frames: vec![], + time_sent: now, + time_acked: None, + time_lost: None, + size: 6500, + ack_eliciting: true, + in_flight: true, + delivered: 0, + delivered_time: now, + recent_delivered_packet_sent_time: now, + is_app_limited: false, + has_data: false, + }; + + r.on_packet_sent( + p, + packet::EPOCH_APPLICATION, + HandshakeStatus::default(), + now, + "", + ); + + assert_eq!(r.sent[packet::EPOCH_APPLICATION].len(), 2); + assert_eq!(r.bytes_in_flight, 13000); + assert_eq!(r.smoothed_rtt.unwrap(), Duration::from_millis(50)); + + // We pace this outgoing packet. as all conditions for pacing + // are passed. + assert_eq!(r.pacing_rate, (12000.0 / 0.05) as u64); + assert_eq!( + r.get_packet_send_time().unwrap(), + now + Duration::from_micros( + (6500 * 1000000) / (12000.0 / 0.05) as u64 + ) + ); + } } mod cubic; diff --git a/src/recovery/reno.rs b/src/recovery/reno.rs index b5019f7..404b63f 100644 --- a/src/recovery/reno.rs +++ b/src/recovery/reno.rs @@ -43,6 +43,9 @@ pub static RENO: CongestionControlOps = CongestionControlOps { on_packet_acked, congestion_event, collapse_cwnd, + checkpoint, + rollback, + has_custom_pacing, }; pub fn on_packet_sent(r: &mut Recovery, sent_bytes: usize, _now: Instant) { @@ -63,38 +66,52 @@ fn on_packet_acked( } if r.congestion_window < r.ssthresh { - // Slow start. - if r.hystart.enabled() && epoch == packet::EPOCH_APPLICATION { - let (cwnd, ssthresh) = r.hystart_on_packet_acked(packet, now); + // In Slow slart, bytes_acked_sl is used for counting + // acknowledged bytes. + r.bytes_acked_sl += packet.size; - r.congestion_window = cwnd; - r.ssthresh = ssthresh; - } else { - r.congestion_window += packet.size; + if r.bytes_acked_sl >= r.max_datagram_size { + r.congestion_window += r.max_datagram_size; + r.bytes_acked_sl -= r.max_datagram_size; + } + + if r.hystart.enabled() && + epoch == packet::EPOCH_APPLICATION && + r.hystart.try_enter_lss( + packet, + r.latest_rtt, + r.congestion_window, + now, + r.max_datagram_size, + ) + { + r.ssthresh = r.congestion_window; } } else { // Congestion avoidance. let mut reno_cwnd = r.congestion_window; - r.bytes_acked += packet.size; + r.bytes_acked_ca += packet.size; - if r.bytes_acked >= r.congestion_window { - r.bytes_acked -= r.congestion_window; - reno_cwnd += recovery::MAX_DATAGRAM_SIZE; + if r.bytes_acked_ca >= r.congestion_window { + r.bytes_acked_ca -= r.congestion_window; + reno_cwnd += r.max_datagram_size; } // When in Limited Slow Start, take the max of CA cwnd and // LSS cwnd. - if r.hystart.enabled() && - epoch == packet::EPOCH_APPLICATION && - r.hystart.lss_start_time().is_some() - { - let (lss_cwnd, _) = r.hystart_on_packet_acked(packet, now); - - reno_cwnd = cmp::max(reno_cwnd, lss_cwnd); + if r.hystart.in_lss(epoch) { + let lss_cwnd_inc = r.hystart.lss_cwnd_inc( + packet.size, + r.congestion_window, + r.ssthresh, + ); + + r.congestion_window = + cmp::max(reno_cwnd, r.congestion_window + lss_cwnd_inc); + } else { + r.congestion_window = reno_cwnd; } - - r.congestion_window = reno_cwnd; } } @@ -110,23 +127,34 @@ fn congestion_event( recovery::LOSS_REDUCTION_FACTOR) as usize; - r.congestion_window = - cmp::max(r.congestion_window, recovery::MINIMUM_WINDOW); + r.congestion_window = cmp::max( + r.congestion_window, + r.max_datagram_size * recovery::MINIMUM_WINDOW_PACKETS, + ); - r.bytes_acked = (r.congestion_window as f64 * + r.bytes_acked_ca = (r.congestion_window as f64 * recovery::LOSS_REDUCTION_FACTOR) as usize; r.ssthresh = r.congestion_window; - if r.hystart.enabled() && epoch == packet::EPOCH_APPLICATION { + if r.hystart.in_lss(epoch) { r.hystart.congestion_event(); } } } pub fn collapse_cwnd(r: &mut Recovery) { - r.congestion_window = recovery::MINIMUM_WINDOW; - r.bytes_acked = 0; + r.congestion_window = r.max_datagram_size * recovery::MINIMUM_WINDOW_PACKETS; + r.bytes_acked_sl = 0; + r.bytes_acked_ca = 0; +} + +fn checkpoint(_r: &mut Recovery) {} + +fn rollback(_r: &mut Recovery) {} + +fn has_custom_pacing() -> bool { + false } #[cfg(test)] @@ -175,7 +203,7 @@ mod tests { time_sent: now, time_acked: None, time_lost: None, - size: 5000, + size: r.max_datagram_size, ack_eliciting: true, in_flight: true, delivered: 0, @@ -185,12 +213,10 @@ mod tests { has_data: false, }; - // Send 5k x 4 = 20k, higher than default cwnd(~15k) - // to become no longer app limited. - r.on_packet_sent_cc(p.size, now); - r.on_packet_sent_cc(p.size, now); - r.on_packet_sent_cc(p.size, now); - r.on_packet_sent_cc(p.size, now); + // Send initcwnd full MSS packets to become no longer app limited + for _ in 0..recovery::INITIAL_WINDOW_PACKETS { + r.on_packet_sent_cc(p.size, now); + } let cwnd_prev = r.cwnd(); @@ -207,6 +233,62 @@ mod tests { } #[test] + fn reno_slow_start_multi_acks() { + let mut cfg = crate::Config::new(crate::PROTOCOL_VERSION).unwrap(); + cfg.set_cc_algorithm(recovery::CongestionControlAlgorithm::Reno); + + let mut r = Recovery::new(&cfg); + + let now = Instant::now(); + + let p = recovery::Sent { + pkt_num: 0, + frames: vec![], + time_sent: now, + time_acked: None, + time_lost: None, + size: r.max_datagram_size, + ack_eliciting: true, + in_flight: true, + delivered: 0, + delivered_time: std::time::Instant::now(), + recent_delivered_packet_sent_time: std::time::Instant::now(), + is_app_limited: false, + has_data: false, + }; + + // Send initcwnd full MSS packets to become no longer app limited + for _ in 0..recovery::INITIAL_WINDOW_PACKETS { + r.on_packet_sent_cc(p.size, now); + } + + let cwnd_prev = r.cwnd(); + + let acked = vec![ + Acked { + pkt_num: p.pkt_num, + time_sent: p.time_sent, + size: p.size, + }, + Acked { + pkt_num: p.pkt_num, + time_sent: p.time_sent, + size: p.size, + }, + Acked { + pkt_num: p.pkt_num, + time_sent: p.time_sent, + size: p.size, + }, + ]; + + r.on_packets_acked(acked, packet::EPOCH_APPLICATION, now); + + // Acked 3 packets. + assert_eq!(r.cwnd(), cwnd_prev + p.size * 3); + } + + #[test] fn reno_congestion_event() { let mut cfg = crate::Config::new(crate::PROTOCOL_VERSION).unwrap(); cfg.set_cc_algorithm(recovery::CongestionControlAlgorithm::Reno); @@ -258,6 +340,6 @@ mod tests { r.on_packets_acked(acked, packet::EPOCH_APPLICATION, now + rtt * 2); // After acking more than cwnd, expect cwnd increased by MSS - assert_eq!(r.cwnd(), cur_cwnd + recovery::MAX_DATAGRAM_SIZE); + assert_eq!(r.cwnd(), cur_cwnd + r.max_datagram_size); } } diff --git a/src/stream.rs b/src/stream.rs index c2fbf36..dfb9b0d 100644 --- a/src/stream.rs +++ b/src/stream.rs @@ -26,7 +26,10 @@ use std::cmp; +use std::sync::Arc; + use std::collections::hash_map; + use std::collections::BTreeMap; use std::collections::BinaryHeap; use std::collections::HashMap; @@ -40,6 +43,12 @@ use crate::ranges; const DEFAULT_URGENCY: u8 = 127; +#[cfg(test)] +const SEND_BUFFER_SIZE: usize = 5; + +#[cfg(not(test))] +const SEND_BUFFER_SIZE: usize = 4096; + /// Keeps track of QUIC streams and enforces stream limits. #[derive(Default)] pub struct StreamMap { @@ -113,6 +122,16 @@ pub struct StreamMap { /// of the map elements represents the offset of the stream at which the /// blocking occurred. blocked: HashMap<u64, u64>, + + /// Set of stream IDs corresponding to streams that are reset. The value + /// of the map elements is a tuple of the error code and final size values + /// to include in the RESET_STREAM frame. + reset: HashMap<u64, (u64, u64)>, + + /// Set of stream IDs corresponding to streams that are shutdown on the + /// receive side, and need to send a STOP_SENDING frame. The value of the + /// map elements is the error code to include in the STOP_SENDING frame. + stopped: HashMap<u64, u64>, } impl StreamMap { @@ -162,7 +181,7 @@ impl StreamMap { } if local != is_local(id, is_server) { - return Err(Error::InvalidStreamState); + return Err(Error::InvalidStreamState(id)); } let (max_rx_data, max_tx_data) = match (local, is_bidi(id)) { @@ -355,6 +374,34 @@ impl StreamMap { } } + /// Adds or removes the stream ID to/from the reset streams set with the + /// given error code and final size values. + /// + /// If the stream was already in the list, this does nothing. + pub fn mark_reset( + &mut self, stream_id: u64, reset: bool, error_code: u64, final_size: u64, + ) { + if reset { + self.reset.insert(stream_id, (error_code, final_size)); + } else { + self.reset.remove(&stream_id); + } + } + + /// Adds or removes the stream ID to/from the stopped streams set with the + /// given error code. + /// + /// If the stream was already in the list, this does nothing. + pub fn mark_stopped( + &mut self, stream_id: u64, stopped: bool, error_code: u64, + ) { + if stopped { + self.stopped.insert(stream_id, error_code); + } else { + self.stopped.remove(&stream_id); + } + } + /// Updates the peer's maximum bidirectional stream count limit. pub fn update_peer_max_streams_bidi(&mut self, v: u64) { self.peer_max_streams_bidi = cmp::max(self.peer_max_streams_bidi, v); @@ -385,6 +432,18 @@ impl StreamMap { self.local_max_streams_uni_next } + /// Returns the number of bidirectional streams that can be created + /// before the peer's stream count limit is reached. + pub fn peer_streams_left_bidi(&self) -> u64 { + self.peer_max_streams_bidi - self.local_opened_streams_bidi + } + + /// Returns the number of unidirectional streams that can be created + /// before the peer's stream count limit is reached. + pub fn peer_streams_left_uni(&self) -> u64 { + self.peer_max_streams_uni - self.local_opened_streams_uni + } + /// Drops completed stream. /// /// This should only be called when Stream::is_complete() returns true for @@ -426,11 +485,26 @@ impl StreamMap { self.blocked.iter() } + /// Creates an iterator over streams that need to send RESET_STREAM. + pub fn reset(&self) -> hash_map::Iter<u64, (u64, u64)> { + self.reset.iter() + } + + /// Creates an iterator over streams that need to send STOP_SENDING. + pub fn stopped(&self) -> hash_map::Iter<u64, u64> { + self.stopped.iter() + } + /// Returns true if there are any streams that have data to write. pub fn has_flushable(&self) -> bool { !self.flushable.is_empty() } + /// Returns true if there are any streams that have data to read. + pub fn has_readable(&self) -> bool { + !self.readable.is_empty() + } + /// Returns true if there are any streams that need to update the local /// flow control limit. pub fn has_almost_full(&self) -> bool { @@ -442,6 +516,16 @@ impl StreamMap { !self.blocked.is_empty() } + /// Returns true if there are any streams that are reset. + pub fn has_reset(&self) -> bool { + !self.reset.is_empty() + } + + /// Returns true if there are any streams that need to send STOP_SENDING. + pub fn has_stopped(&self) -> bool { + !self.stopped.is_empty() + } + /// Returns true if the max bidirectional streams count needs to be updated /// by sending a MAX_STREAMS frame to the peer. pub fn should_update_max_streams_bidi(&self) -> bool { @@ -481,7 +565,7 @@ pub struct Stream { pub local: bool, /// Application data. - pub data: Option<Box<dyn Send + std::any::Any>>, + pub data: Option<Box<dyn std::any::Any + Send + Sync>>, /// The stream's urgency (lower is better). Default is `DEFAULT_URGENCY`. pub urgency: u8, @@ -568,6 +652,7 @@ pub struct StreamIter { } impl StreamIter { + #[inline] fn from(streams: &HashSet<u64>) -> Self { StreamIter { streams: streams.iter().copied().collect(), @@ -578,12 +663,14 @@ impl StreamIter { impl Iterator for StreamIter { type Item = u64; + #[inline] fn next(&mut self) -> Option<Self::Item> { self.streams.pop() } } impl ExactSizeIterator for StreamIter { + #[inline] fn len(&self) -> usize { self.streams.len() } @@ -634,7 +721,7 @@ impl RecvBuf { /// This also takes care of enforcing stream flow control limits, as well /// as handling incoming data that overlaps data that is already in the /// buffer. - pub fn push(&mut self, buf: RangeBuf) -> Result<()> { + pub fn write(&mut self, buf: RangeBuf) -> Result<()> { if buf.max_off() > self.max_data { return Err(Error::FlowControl); } @@ -690,10 +777,6 @@ impl RecvBuf { } } - if self.drain { - return Ok(()); - } - let mut tmp_buf = Some(buf); while let Some(mut buf) = tmp_buf { @@ -727,7 +810,9 @@ impl RecvBuf { self.len = cmp::max(self.len, buf.max_off()); - self.data.push(buf); + if !self.drain { + self.data.push(buf); + } } Ok(()) @@ -742,7 +827,7 @@ impl RecvBuf { /// /// On success the amount of data read, and a flag indicating if there is /// no more data in the buffer, are returned as a tuple. - pub fn pop(&mut self, out: &mut [u8]) -> Result<(usize, bool)> { + pub fn emit(&mut self, out: &mut [u8]) -> Result<(usize, bool)> { let mut len = 0; let mut cap = out.len(); @@ -822,6 +907,8 @@ impl RecvBuf { self.data.clear(); + self.off = self.max_off(); + Ok(()) } @@ -881,12 +968,15 @@ impl RecvBuf { #[derive(Debug, Default)] pub struct SendBuf { /// Chunks of data to be sent, ordered by offset. - data: BinaryHeap<RangeBuf>, + data: VecDeque<RangeBuf>, + + /// The index of the buffer that needs to be sent next. + pos: usize, /// The maximum offset of data buffered in the stream. off: u64, - /// The amount of data that was ever written to this stream. + /// The amount of data currently buffered. len: u64, /// The maximum offset we are allowed to send to the peer. @@ -900,6 +990,9 @@ pub struct SendBuf { /// Ranges of data offsets that have been acked. acked: ranges::RangeSet, + + /// The error code received via STOP_SENDING. + error: Option<u64>, } impl SendBuf { @@ -916,122 +1009,116 @@ impl SendBuf { /// The number of bytes that were actually stored in the buffer is returned /// (this may be lower than the size of the input buffer, in case of partial /// writes). - pub fn push_slice( - &mut self, mut data: &[u8], mut fin: bool, - ) -> Result<usize> { - if self.shutdown { - // Since we won't write any more data anyway, pretend that we sent - // all data that was passed in. - return Ok(data.len()); - } + pub fn write(&mut self, mut data: &[u8], mut fin: bool) -> Result<usize> { + let max_off = self.off + data.len() as u64; - if data.is_empty() { - // Create a dummy range buffer, in order to propagate the `fin` flag - // into `RangeBuf::push()`. This will be discarded later on. - let buf = RangeBuf::from(&[], self.off, fin); + // Get the stream send capacity. This will return an error if the stream + // was stopped. + let capacity = self.cap()?; - return self.push(buf).map(|_| 0); - } - - if data.len() > self.cap() { + if data.len() > capacity { // Truncate the input buffer according to the stream's capacity. - let len = self.cap(); + let len = capacity; data = &data[..len]; // We are not buffering the full input, so clear the fin flag. fin = false; } - let buf = RangeBuf::from(data, self.off, fin); - self.push(buf)?; - - self.off += data.len() as u64; - - Ok(data.len()) - } - - /// Inserts the given chunk of data in the buffer. - pub fn push(&mut self, buf: RangeBuf) -> Result<()> { if let Some(fin_off) = self.fin_off { // Can't write past final offset. - if buf.max_off() > fin_off { + if max_off > fin_off { return Err(Error::FinalSize); } // Can't "undo" final offset. - if buf.max_off() == fin_off && !buf.fin() { + if max_off == fin_off && !fin { return Err(Error::FinalSize); } } - if self.shutdown { - return Ok(()); - } - - if buf.fin() { - self.fin_off = Some(buf.max_off()); + if fin { + self.fin_off = Some(max_off); } // Don't queue data that was already fully acked. - if self.ack_off() >= buf.max_off() { - return Ok(()); + if self.ack_off() >= max_off { + return Ok(data.len()); } - self.len += buf.len() as u64; - // We already recorded the final offset, so we can just discard the // empty buffer now. - if buf.is_empty() { - return Ok(()); + if data.is_empty() { + return Ok(data.len()); } - self.data.push(buf); + let mut len = 0; + + // Split the remaining input data into consistently-sized buffers to + // avoid fragmentation. + for chunk in data.chunks(SEND_BUFFER_SIZE) { + len += chunk.len(); - Ok(()) + let fin = len == data.len() && fin; + + let buf = RangeBuf::from(chunk, self.off, fin); + + // The new data can simply be appended at the end of the send buffer. + self.data.push_back(buf); + + self.off += chunk.len() as u64; + self.len += chunk.len() as u64; + } + + Ok(len) } - /// Returns contiguous data from the send buffer as a single `RangeBuf`. - pub fn pop(&mut self, max_data: usize) -> Result<RangeBuf> { - let mut out = RangeBuf::default(); - out.data = - Vec::with_capacity(cmp::min(max_data as u64, self.len) as usize); - out.off = self.off; + /// Writes data from the send buffer into the given output buffer. + pub fn emit(&mut self, out: &mut [u8]) -> Result<(usize, bool)> { + let mut out_len = out.len(); + let out_off = self.off_front(); - let mut out_len = max_data; - let mut out_off = self.data.peek().map_or_else(|| out.off, RangeBuf::off); + let mut next_off = out_off; while out_len > 0 && self.ready() && - self.off_front() == out_off && + self.off_front() == next_off && self.off_front() < self.max_data { - let mut buf = match self.data.peek_mut() { + let buf = match self.data.get_mut(self.pos) { Some(v) => v, None => break, }; + if buf.is_empty() { + self.pos += 1; + continue; + } + let buf_len = cmp::min(buf.len(), out_len); - if out.is_empty() { - out.off = buf.off(); - } + // Copy data to the output buffer. + let out_pos = (next_off - out_off) as usize; + (&mut out[out_pos..out_pos + buf_len]) + .copy_from_slice(&buf[..buf_len]); self.len -= buf_len as u64; out_len -= buf_len; - out_off = buf.off() + buf_len as u64; - out.data.extend_from_slice(&buf[..buf_len]); + next_off = buf.off() + buf_len as u64; - if buf_len < buf.len() { + if !buf.is_empty() && buf_len < buf.len() { buf.consume(buf_len); // We reached the maximum capacity, so end here. break; } - std::collections::binary_heap::PeekMut::pop(buf); + buf.consume(buf_len); + + self.pos += 1; } // Override the `fin` flag set for the output buffer by matching the @@ -1040,9 +1127,9 @@ impl SendBuf { // This is more efficient than tracking `fin` using the range buffers // themselves, and lets us avoid queueing empty buffers just so we can // propagate the final size. - out.fin = self.fin_off == Some(out.max_off()); + let fin = self.fin_off == Some(next_off); - Ok(out) + Ok((out.len() - out_len, fin)) } /// Updates the max_data limit to the given value. @@ -1055,17 +1142,138 @@ impl SendBuf { self.acked.insert(off..off + len as u64); } + pub fn ack_and_drop(&mut self, off: u64, len: usize) { + self.ack(off, len); + + let ack_off = self.ack_off(); + + if self.data.is_empty() { + return; + } + + if off > ack_off { + return; + } + + let mut drop_until = None; + + // Drop contiguously acked data from the front of the buffer. + for (i, buf) in self.data.iter_mut().enumerate() { + // Newly acked range is past highest contiguous acked range, so we + // can't drop it. + if buf.off >= ack_off { + break; + } + + // Highest contiguous acked range falls within newly acked range, + // so we can't drop it. + if buf.off < ack_off && ack_off < buf.max_off() { + break; + } + + // Newly acked range can be dropped. + drop_until = Some(i); + } + + if let Some(drop) = drop_until { + self.data.drain(..=drop); + + // When a buffer is marked for retransmission, but then acked before + // it could be retransmitted, we might end up decreasing the SendBuf + // position too much, so make sure that doesn't happen. + self.pos = self.pos.saturating_sub(drop + 1); + } + } + + pub fn retransmit(&mut self, off: u64, len: usize) { + let max_off = off + len as u64; + let ack_off = self.ack_off(); + + if self.data.is_empty() { + return; + } + + if max_off <= ack_off { + return; + } + + for i in 0..self.data.len() { + let buf = &mut self.data[i]; + + if buf.off >= max_off { + break; + } + + if off > buf.max_off() { + continue; + } + + // Split the buffer into 2 if the retransmit range ends before the + // buffer's final offset. + let new_buf = if buf.off < max_off && max_off < buf.max_off() { + Some(buf.split_off((max_off - buf.off as u64) as usize)) + } else { + None + }; + + // Advance the buffer's position if the retransmit range is past + // the buffer's starting offset. + buf.pos = if off > buf.off && off <= buf.max_off() { + cmp::min(buf.pos, buf.start + (off - buf.off) as usize) + } else { + buf.start + }; + + self.pos = cmp::min(self.pos, i); + + self.len += buf.len() as u64; + + if let Some(b) = new_buf { + self.data.insert(i + 1, b); + } + } + } + + /// Resets the stream at the current offset and clears all buffered data. + pub fn reset(&mut self) -> Result<u64> { + self.write(b"", true)?; + + // Drop all buffered data. + self.data.clear(); + + // Mark all data as acked. + self.ack(0, self.off as usize); + + self.pos = 0; + self.len = 0; + + Ok(self.fin_off.unwrap()) + } + + /// Resets the streams and records the received error code. + /// + /// Calling this again after the first time has no effect. + pub fn stop(&mut self, error_code: u64) -> Result<u64> { + if self.error.is_some() { + return Err(Error::Done); + } + + let fin_off = self.reset()?; + + self.error = Some(error_code); + + Ok(fin_off) + } + /// Shuts down sending data. - pub fn shutdown(&mut self) -> Result<()> { + pub fn shutdown(&mut self) -> Result<u64> { if self.shutdown { return Err(Error::Done); } self.shutdown = true; - self.data.clear(); - - Ok(()) + self.reset() } /// Returns the largest offset of data buffered. @@ -1076,11 +1284,18 @@ impl SendBuf { /// Returns the lowest offset of data buffered. pub fn off_front(&self) -> u64 { - match self.data.peek() { - Some(v) => v.off(), + let mut pos = self.pos; - None => self.off, + // Skip empty buffers from the start of the queue. + while let Some(b) = self.data.get(pos) { + if !b.is_empty() { + return b.off(); + } + + pos += 1; } + + self.off } /// The maximum offset we are allowed to send to the peer. @@ -1114,9 +1329,14 @@ impl SendBuf { false } + /// Returns true if the stream was stopped before completion. + pub fn is_stopped(&self) -> bool { + self.error.is_some() + } + /// Returns true if there is data to be written. fn ready(&self) -> bool { - !self.data.is_empty() + !self.data.is_empty() && self.off_front() < self.off } /// Returns the highest contiguously acked offset. @@ -1131,22 +1351,49 @@ impl SendBuf { } /// Returns the outgoing flow control capacity. - pub fn cap(&self) -> usize { - (self.max_data - self.off) as usize + pub fn cap(&self) -> Result<usize> { + // The stream was stopped, so return the error code instead. + if let Some(e) = self.error { + return Err(Error::StreamStopped(e)); + } + + Ok((self.max_data - self.off) as usize) } } /// Buffer holding data at a specific offset. +/// +/// The data is stored in a `Vec<u8>` in such a way that it can be shared +/// between multiple `RangeBuf` objects. +/// +/// Each `RangeBuf` will have its own view of that buffer, where the `start` +/// value indicates the initial offset within the `Vec`, and `len` indicates the +/// number of bytes, starting from `start` that are included. +/// +/// In addition, `pos` indicates the current offset within the `Vec`, starting +/// from the very beginning of the `Vec`. +/// +/// Finally, `off` is the starting offset for the specific `RangeBuf` within the +/// stream the buffer belongs to. #[derive(Clone, Debug, Default, Eq)] pub struct RangeBuf { /// The internal buffer holding the data. - data: Vec<u8>, + /// + /// To avoid neeless allocations when a RangeBuf is split, this field is + /// reference-counted and can be shared between multiple RangeBuf objects, + /// and sliced using the `start` and `len` values. + data: Arc<Vec<u8>>, - /// The starting offset within `data`. This allows partially consuming a - /// buffer without duplicating the data. + /// The initial offset within the internal buffer. + start: usize, + + /// The current offset within the internal buffer. pos: usize, - /// The starting offset within a stream. + /// The number of bytes in the buffer, from the initial offset. + len: usize, + + /// The offset of the buffer within a stream. off: u64, /// Whether this contains the final byte in the stream. @@ -1155,10 +1402,12 @@ pub struct RangeBuf { impl RangeBuf { /// Creates a new `RangeBuf` from the given slice. - pub(crate) fn from(buf: &[u8], off: u64, fin: bool) -> RangeBuf { + pub fn from(buf: &[u8], off: u64, fin: bool) -> RangeBuf { RangeBuf { - data: Vec::from(buf), + data: Arc::new(Vec::from(buf)), + start: 0, pos: 0, + len: buf.len(), off, fin, } @@ -1171,7 +1420,7 @@ impl RangeBuf { /// Returns the starting offset of `self`. pub fn off(&self) -> u64 { - self.off + self.pos as u64 + (self.off - self.start as u64) + self.pos as u64 } /// Returns the final offset of `self`. @@ -1181,7 +1430,7 @@ impl RangeBuf { /// Returns the length of `self`. pub fn len(&self) -> usize { - self.data.len() - self.pos + self.len - (self.pos - self.start) } /// Returns true if `self` has a length of zero bytes. @@ -1196,13 +1445,24 @@ impl RangeBuf { /// Splits the buffer into two at the given index. pub fn split_off(&mut self, at: usize) -> RangeBuf { + if at > self.len { + panic!( + "`at` split index (is {}) should be <= len (is {})", + at, self.len + ); + } + let buf = RangeBuf { - data: self.data.split_off(at), - pos: 0, + data: self.data.clone(), + start: self.start + at, + pos: cmp::max(self.pos, self.start + at), + len: self.len - at, off: self.off + at as u64, fin: self.fin, }; + self.pos = cmp::min(self.pos, self.start + at); + self.len = at; self.fin = false; buf @@ -1213,13 +1473,7 @@ impl std::ops::Deref for RangeBuf { type Target = [u8]; fn deref(&self) -> &[u8] { - &self.data[self.pos..] - } -} - -impl std::ops::DerefMut for RangeBuf { - fn deref_mut(&mut self) -> &mut [u8] { - &mut self.data[self.pos..] + &self.data[self.pos..self.start + self.len] } } @@ -1253,7 +1507,7 @@ mod tests { let mut buf = [0; 32]; - assert_eq!(recv.pop(&mut buf), Err(Error::Done)); + assert_eq!(recv.emit(&mut buf), Err(Error::Done)); } #[test] @@ -1262,54 +1516,54 @@ mod tests { assert_eq!(recv.len, 0); let buf = RangeBuf::from(b"hello", 0, false); - assert!(recv.push(buf).is_ok()); + assert!(recv.write(buf).is_ok()); assert_eq!(recv.len, 5); assert_eq!(recv.off, 0); assert_eq!(recv.data.len(), 1); let mut buf = [0; 32]; - assert_eq!(recv.pop(&mut buf), Ok((5, false))); + assert_eq!(recv.emit(&mut buf), Ok((5, false))); // Don't store non-fin empty buffer. let buf = RangeBuf::from(b"", 10, false); - assert!(recv.push(buf).is_ok()); + assert!(recv.write(buf).is_ok()); assert_eq!(recv.len, 5); assert_eq!(recv.off, 5); assert_eq!(recv.data.len(), 0); // Check flow control for empty buffer. let buf = RangeBuf::from(b"", 16, false); - assert_eq!(recv.push(buf), Err(Error::FlowControl)); + assert_eq!(recv.write(buf), Err(Error::FlowControl)); // Store fin empty buffer. let buf = RangeBuf::from(b"", 5, true); - assert!(recv.push(buf).is_ok()); + assert!(recv.write(buf).is_ok()); assert_eq!(recv.len, 5); assert_eq!(recv.off, 5); assert_eq!(recv.data.len(), 1); // Don't store additional fin empty buffers. let buf = RangeBuf::from(b"", 5, true); - assert!(recv.push(buf).is_ok()); + assert!(recv.write(buf).is_ok()); assert_eq!(recv.len, 5); assert_eq!(recv.off, 5); assert_eq!(recv.data.len(), 1); // Don't store additional fin non-empty buffers. let buf = RangeBuf::from(b"aa", 3, true); - assert!(recv.push(buf).is_ok()); + assert!(recv.write(buf).is_ok()); assert_eq!(recv.len, 5); assert_eq!(recv.off, 5); assert_eq!(recv.data.len(), 1); // Validate final size with fin empty buffers. let buf = RangeBuf::from(b"", 6, true); - assert_eq!(recv.push(buf), Err(Error::FinalSize)); + assert_eq!(recv.write(buf), Err(Error::FinalSize)); let buf = RangeBuf::from(b"", 4, true); - assert_eq!(recv.push(buf), Err(Error::FinalSize)); + assert_eq!(recv.write(buf), Err(Error::FinalSize)); let mut buf = [0; 32]; - assert_eq!(recv.pop(&mut buf), Ok((0, true))); + assert_eq!(recv.emit(&mut buf), Ok((0, true))); } #[test] @@ -1323,30 +1577,30 @@ mod tests { let second = RangeBuf::from(b"world", 5, false); let third = RangeBuf::from(b"something", 10, true); - assert!(recv.push(second).is_ok()); + assert!(recv.write(second).is_ok()); assert_eq!(recv.len, 10); assert_eq!(recv.off, 0); - assert_eq!(recv.pop(&mut buf), Err(Error::Done)); + assert_eq!(recv.emit(&mut buf), Err(Error::Done)); - assert!(recv.push(third).is_ok()); + assert!(recv.write(third).is_ok()); assert_eq!(recv.len, 19); assert_eq!(recv.off, 0); - assert_eq!(recv.pop(&mut buf), Err(Error::Done)); + assert_eq!(recv.emit(&mut buf), Err(Error::Done)); - assert!(recv.push(first).is_ok()); + assert!(recv.write(first).is_ok()); assert_eq!(recv.len, 19); assert_eq!(recv.off, 0); - let (len, fin) = recv.pop(&mut buf).unwrap(); + let (len, fin) = recv.emit(&mut buf).unwrap(); assert_eq!(len, 19); assert_eq!(fin, true); assert_eq!(&buf[..len], b"helloworldsomething"); assert_eq!(recv.len, 19); assert_eq!(recv.off, 19); - assert_eq!(recv.pop(&mut buf), Err(Error::Done)); + assert_eq!(recv.emit(&mut buf), Err(Error::Done)); } #[test] @@ -1359,29 +1613,29 @@ mod tests { let first = RangeBuf::from(b"something", 0, false); let second = RangeBuf::from(b"helloworld", 9, true); - assert!(recv.push(first).is_ok()); + assert!(recv.write(first).is_ok()); assert_eq!(recv.len, 9); assert_eq!(recv.off, 0); - assert!(recv.push(second).is_ok()); + assert!(recv.write(second).is_ok()); assert_eq!(recv.len, 19); assert_eq!(recv.off, 0); - let (len, fin) = recv.pop(&mut buf[..10]).unwrap(); + let (len, fin) = recv.emit(&mut buf[..10]).unwrap(); assert_eq!(len, 10); assert_eq!(fin, false); assert_eq!(&buf[..len], b"somethingh"); assert_eq!(recv.len, 19); assert_eq!(recv.off, 10); - let (len, fin) = recv.pop(&mut buf[..5]).unwrap(); + let (len, fin) = recv.emit(&mut buf[..5]).unwrap(); assert_eq!(len, 5); assert_eq!(fin, false); assert_eq!(&buf[..len], b"ellow"); assert_eq!(recv.len, 19); assert_eq!(recv.off, 15); - let (len, fin) = recv.pop(&mut buf[..10]).unwrap(); + let (len, fin) = recv.emit(&mut buf[..10]).unwrap(); assert_eq!(len, 4); assert_eq!(fin, true); assert_eq!(&buf[..len], b"orld"); @@ -1399,17 +1653,17 @@ mod tests { let first = RangeBuf::from(b"something", 0, false); let second = RangeBuf::from(b"helloworld", 9, true); - assert!(recv.push(second).is_ok()); + assert!(recv.write(second).is_ok()); assert_eq!(recv.len, 19); assert_eq!(recv.off, 0); - assert_eq!(recv.pop(&mut buf), Err(Error::Done)); + assert_eq!(recv.emit(&mut buf), Err(Error::Done)); - assert!(recv.push(first).is_ok()); + assert!(recv.write(first).is_ok()); assert_eq!(recv.len, 19); assert_eq!(recv.off, 0); - let (len, fin) = recv.pop(&mut buf).unwrap(); + let (len, fin) = recv.emit(&mut buf).unwrap(); assert_eq!(len, 19); assert_eq!(fin, true); assert_eq!(&buf[..len], b"somethinghelloworld"); @@ -1427,17 +1681,17 @@ mod tests { let first = RangeBuf::from(b"something", 0, false); let second = RangeBuf::from(b"", 9, true); - assert!(recv.push(first).is_ok()); + assert!(recv.write(first).is_ok()); assert_eq!(recv.len, 9); assert_eq!(recv.off, 0); assert_eq!(recv.data.len(), 1); - assert!(recv.push(second).is_ok()); + assert!(recv.write(second).is_ok()); assert_eq!(recv.len, 9); assert_eq!(recv.off, 0); assert_eq!(recv.data.len(), 1); - let (len, fin) = recv.pop(&mut buf).unwrap(); + let (len, fin) = recv.emit(&mut buf).unwrap(); assert_eq!(len, 9); assert_eq!(fin, true); assert_eq!(&buf[..len], b"something"); @@ -1457,31 +1711,31 @@ mod tests { let third = RangeBuf::from(b"ello", 4, true); let fourth = RangeBuf::from(b"ello", 5, true); - assert!(recv.push(first).is_ok()); + assert!(recv.write(first).is_ok()); assert_eq!(recv.len, 9); assert_eq!(recv.off, 0); assert_eq!(recv.data.len(), 1); - let (len, fin) = recv.pop(&mut buf).unwrap(); + let (len, fin) = recv.emit(&mut buf).unwrap(); assert_eq!(len, 9); assert_eq!(fin, false); assert_eq!(&buf[..len], b"something"); assert_eq!(recv.len, 9); assert_eq!(recv.off, 9); - assert!(recv.push(second).is_ok()); + assert!(recv.write(second).is_ok()); assert_eq!(recv.len, 9); assert_eq!(recv.off, 9); assert_eq!(recv.data.len(), 0); - assert_eq!(recv.push(third), Err(Error::FinalSize)); + assert_eq!(recv.write(third), Err(Error::FinalSize)); - assert!(recv.push(fourth).is_ok()); + assert!(recv.write(fourth).is_ok()); assert_eq!(recv.len, 9); assert_eq!(recv.off, 9); assert_eq!(recv.data.len(), 0); - assert_eq!(recv.pop(&mut buf), Err(Error::Done)); + assert_eq!(recv.emit(&mut buf), Err(Error::Done)); } #[test] @@ -1494,17 +1748,17 @@ mod tests { let first = RangeBuf::from(b"something", 0, false); let second = RangeBuf::from(b"hello", 4, false); - assert!(recv.push(first).is_ok()); + assert!(recv.write(first).is_ok()); assert_eq!(recv.len, 9); assert_eq!(recv.off, 0); assert_eq!(recv.data.len(), 1); - assert!(recv.push(second).is_ok()); + assert!(recv.write(second).is_ok()); assert_eq!(recv.len, 9); assert_eq!(recv.off, 0); assert_eq!(recv.data.len(), 1); - let (len, fin) = recv.pop(&mut buf).unwrap(); + let (len, fin) = recv.emit(&mut buf).unwrap(); assert_eq!(len, 9); assert_eq!(fin, false); assert_eq!(&buf[..len], b"something"); @@ -1512,7 +1766,7 @@ mod tests { assert_eq!(recv.off, 9); assert_eq!(recv.data.len(), 0); - assert_eq!(recv.pop(&mut buf), Err(Error::Done)); + assert_eq!(recv.emit(&mut buf), Err(Error::Done)); } #[test] @@ -1525,17 +1779,17 @@ mod tests { let first = RangeBuf::from(b"something", 0, false); let second = RangeBuf::from(b"hello", 4, false); - assert!(recv.push(second).is_ok()); + assert!(recv.write(second).is_ok()); assert_eq!(recv.len, 9); assert_eq!(recv.off, 0); assert_eq!(recv.data.len(), 1); - assert!(recv.push(first).is_ok()); + assert!(recv.write(first).is_ok()); assert_eq!(recv.len, 9); assert_eq!(recv.off, 0); assert_eq!(recv.data.len(), 2); - let (len, fin) = recv.pop(&mut buf).unwrap(); + let (len, fin) = recv.emit(&mut buf).unwrap(); assert_eq!(len, 9); assert_eq!(fin, false); assert_eq!(&buf[..len], b"somehello"); @@ -1543,7 +1797,7 @@ mod tests { assert_eq!(recv.off, 9); assert_eq!(recv.data.len(), 0); - assert_eq!(recv.pop(&mut buf), Err(Error::Done)); + assert_eq!(recv.emit(&mut buf), Err(Error::Done)); } #[test] @@ -1556,17 +1810,17 @@ mod tests { let first = RangeBuf::from(b"something", 0, false); let second = RangeBuf::from(b"hello", 3, false); - assert!(recv.push(second).is_ok()); + assert!(recv.write(second).is_ok()); assert_eq!(recv.len, 8); assert_eq!(recv.off, 0); assert_eq!(recv.data.len(), 1); - assert!(recv.push(first).is_ok()); + assert!(recv.write(first).is_ok()); assert_eq!(recv.len, 9); assert_eq!(recv.off, 0); assert_eq!(recv.data.len(), 3); - let (len, fin) = recv.pop(&mut buf).unwrap(); + let (len, fin) = recv.emit(&mut buf).unwrap(); assert_eq!(len, 9); assert_eq!(fin, false); assert_eq!(&buf[..len], b"somhellog"); @@ -1574,7 +1828,7 @@ mod tests { assert_eq!(recv.off, 9); assert_eq!(recv.data.len(), 0); - assert_eq!(recv.pop(&mut buf), Err(Error::Done)); + assert_eq!(recv.emit(&mut buf), Err(Error::Done)); } #[test] @@ -1588,22 +1842,22 @@ mod tests { let second = RangeBuf::from(b"hello", 3, false); let third = RangeBuf::from(b"hello", 12, false); - assert!(recv.push(second).is_ok()); + assert!(recv.write(second).is_ok()); assert_eq!(recv.len, 8); assert_eq!(recv.off, 0); assert_eq!(recv.data.len(), 1); - assert!(recv.push(third).is_ok()); + assert!(recv.write(third).is_ok()); assert_eq!(recv.len, 17); assert_eq!(recv.off, 0); assert_eq!(recv.data.len(), 2); - assert!(recv.push(first).is_ok()); + assert!(recv.write(first).is_ok()); assert_eq!(recv.len, 18); assert_eq!(recv.off, 0); assert_eq!(recv.data.len(), 5); - let (len, fin) = recv.pop(&mut buf).unwrap(); + let (len, fin) = recv.emit(&mut buf).unwrap(); assert_eq!(len, 18); assert_eq!(fin, false); assert_eq!(&buf[..len], b"somhellogsomhellog"); @@ -1611,7 +1865,7 @@ mod tests { assert_eq!(recv.off, 18); assert_eq!(recv.data.len(), 0); - assert_eq!(recv.pop(&mut buf), Err(Error::Done)); + assert_eq!(recv.emit(&mut buf), Err(Error::Done)); } #[test] @@ -1624,24 +1878,24 @@ mod tests { let first = RangeBuf::from(b"something", 0, false); let second = RangeBuf::from(b"hello", 8, true); - assert!(recv.push(first).is_ok()); + assert!(recv.write(first).is_ok()); assert_eq!(recv.len, 9); assert_eq!(recv.off, 0); assert_eq!(recv.data.len(), 1); - assert!(recv.push(second).is_ok()); + assert!(recv.write(second).is_ok()); assert_eq!(recv.len, 13); assert_eq!(recv.off, 0); assert_eq!(recv.data.len(), 2); - let (len, fin) = recv.pop(&mut buf).unwrap(); + let (len, fin) = recv.emit(&mut buf).unwrap(); assert_eq!(len, 13); assert_eq!(fin, true); assert_eq!(&buf[..len], b"somethingello"); assert_eq!(recv.len, 13); assert_eq!(recv.off, 13); - assert_eq!(recv.pop(&mut buf), Err(Error::Done)); + assert_eq!(recv.emit(&mut buf), Err(Error::Done)); } #[test] @@ -1654,24 +1908,24 @@ mod tests { let first = RangeBuf::from(b"hello", 0, false); let second = RangeBuf::from(b"something", 3, true); - assert!(recv.push(second).is_ok()); + assert!(recv.write(second).is_ok()); assert_eq!(recv.len, 12); assert_eq!(recv.off, 0); assert_eq!(recv.data.len(), 1); - assert!(recv.push(first).is_ok()); + assert!(recv.write(first).is_ok()); assert_eq!(recv.len, 12); assert_eq!(recv.off, 0); assert_eq!(recv.data.len(), 2); - let (len, fin) = recv.pop(&mut buf).unwrap(); + let (len, fin) = recv.emit(&mut buf).unwrap(); assert_eq!(len, 12); assert_eq!(fin, true); assert_eq!(&buf[..len], b"helsomething"); assert_eq!(recv.len, 12); assert_eq!(recv.off, 12); - assert_eq!(recv.pop(&mut buf), Err(Error::Done)); + assert_eq!(recv.emit(&mut buf), Err(Error::Done)); } #[test] @@ -1685,22 +1939,22 @@ mod tests { let second = RangeBuf::from(b"something", 0, false); let third = RangeBuf::from(b"moar", 11, true); - assert!(recv.push(first).is_ok()); + assert!(recv.write(first).is_ok()); assert_eq!(recv.len, 13); assert_eq!(recv.off, 0); assert_eq!(recv.data.len(), 1); - assert!(recv.push(second).is_ok()); + assert!(recv.write(second).is_ok()); assert_eq!(recv.len, 13); assert_eq!(recv.off, 0); assert_eq!(recv.data.len(), 2); - assert!(recv.push(third).is_ok()); + assert!(recv.write(third).is_ok()); assert_eq!(recv.len, 15); assert_eq!(recv.off, 0); assert_eq!(recv.data.len(), 3); - let (len, fin) = recv.pop(&mut buf).unwrap(); + let (len, fin) = recv.emit(&mut buf).unwrap(); assert_eq!(len, 15); assert_eq!(fin, true); assert_eq!(&buf[..len], b"somethinhelloar"); @@ -1708,7 +1962,7 @@ mod tests { assert_eq!(recv.off, 15); assert_eq!(recv.data.len(), 0); - assert_eq!(recv.pop(&mut buf), Err(Error::Done)); + assert_eq!(recv.emit(&mut buf), Err(Error::Done)); } #[test] @@ -1725,37 +1979,37 @@ mod tests { let fifth = RangeBuf::from(b"eee", 9, false); let sixth = RangeBuf::from(b"fff", 11, false); - assert!(recv.push(second).is_ok()); + assert!(recv.write(second).is_ok()); assert_eq!(recv.len, 5); assert_eq!(recv.off, 0); assert_eq!(recv.data.len(), 1); - assert!(recv.push(fourth).is_ok()); + assert!(recv.write(fourth).is_ok()); assert_eq!(recv.len, 9); assert_eq!(recv.off, 0); assert_eq!(recv.data.len(), 2); - assert!(recv.push(third).is_ok()); + assert!(recv.write(third).is_ok()); assert_eq!(recv.len, 9); assert_eq!(recv.off, 0); assert_eq!(recv.data.len(), 3); - assert!(recv.push(first).is_ok()); + assert!(recv.write(first).is_ok()); assert_eq!(recv.len, 9); assert_eq!(recv.off, 0); assert_eq!(recv.data.len(), 4); - assert!(recv.push(sixth).is_ok()); + assert!(recv.write(sixth).is_ok()); assert_eq!(recv.len, 14); assert_eq!(recv.off, 0); assert_eq!(recv.data.len(), 5); - assert!(recv.push(fifth).is_ok()); + assert!(recv.write(fifth).is_ok()); assert_eq!(recv.len, 14); assert_eq!(recv.off, 0); assert_eq!(recv.data.len(), 6); - let (len, fin) = recv.pop(&mut buf).unwrap(); + let (len, fin) = recv.emit(&mut buf).unwrap(); assert_eq!(len, 14); assert_eq!(fin, false); assert_eq!(&buf[..len], b"aabbbcdddeefff"); @@ -1763,78 +2017,91 @@ mod tests { assert_eq!(recv.off, 14); assert_eq!(recv.data.len(), 0); - assert_eq!(recv.pop(&mut buf), Err(Error::Done)); + assert_eq!(recv.emit(&mut buf), Err(Error::Done)); } #[test] fn empty_write() { + let mut buf = [0; 5]; + let mut send = SendBuf::new(std::u64::MAX); assert_eq!(send.len, 0); - let write = send.pop(std::usize::MAX).unwrap(); - assert_eq!(write.len(), 0); - assert_eq!(write.fin(), false); + let (written, fin) = send.emit(&mut buf).unwrap(); + assert_eq!(written, 0); + assert_eq!(fin, false); } #[test] fn multi_write() { + let mut buf = [0; 128]; + let mut send = SendBuf::new(std::u64::MAX); assert_eq!(send.len, 0); let first = b"something"; let second = b"helloworld"; - assert!(send.push_slice(first, false).is_ok()); + assert!(send.write(first, false).is_ok()); assert_eq!(send.len, 9); - assert!(send.push_slice(second, true).is_ok()); + assert!(send.write(second, true).is_ok()); assert_eq!(send.len, 19); - let write = send.pop(128).unwrap(); - assert_eq!(write.len(), 19); - assert_eq!(write.fin(), true); - assert_eq!(&write[..], b"somethinghelloworld"); + let (written, fin) = send.emit(&mut buf[..128]).unwrap(); + assert_eq!(written, 19); + assert_eq!(fin, true); + assert_eq!(&buf[..written], b"somethinghelloworld"); assert_eq!(send.len, 0); } #[test] fn split_write() { + let mut buf = [0; 10]; + let mut send = SendBuf::new(std::u64::MAX); assert_eq!(send.len, 0); let first = b"something"; let second = b"helloworld"; - assert!(send.push_slice(first, false).is_ok()); + assert!(send.write(first, false).is_ok()); assert_eq!(send.len, 9); - assert!(send.push_slice(second, true).is_ok()); + assert!(send.write(second, true).is_ok()); assert_eq!(send.len, 19); - let write = send.pop(10).unwrap(); - assert_eq!(write.off(), 0); - assert_eq!(write.len(), 10); - assert_eq!(write.fin(), false); - assert_eq!(&write[..], b"somethingh"); + assert_eq!(send.off_front(), 0); + + let (written, fin) = send.emit(&mut buf[..10]).unwrap(); + assert_eq!(written, 10); + assert_eq!(fin, false); + assert_eq!(&buf[..written], b"somethingh"); assert_eq!(send.len, 9); - let write = send.pop(5).unwrap(); - assert_eq!(write.off(), 10); - assert_eq!(write.len(), 5); - assert_eq!(write.fin(), false); - assert_eq!(&write[..], b"ellow"); + assert_eq!(send.off_front(), 10); + + let (written, fin) = send.emit(&mut buf[..5]).unwrap(); + assert_eq!(written, 5); + assert_eq!(fin, false); + assert_eq!(&buf[..written], b"ellow"); assert_eq!(send.len, 4); - let write = send.pop(10).unwrap(); - assert_eq!(write.off(), 15); - assert_eq!(write.len(), 4); - assert_eq!(write.fin(), true); - assert_eq!(&write[..], b"orld"); + assert_eq!(send.off_front(), 15); + + let (written, fin) = send.emit(&mut buf[..10]).unwrap(); + assert_eq!(written, 4); + assert_eq!(fin, true); + assert_eq!(&buf[..written], b"orld"); assert_eq!(send.len, 0); + + assert_eq!(send.off_front(), 19); } #[test] fn resend() { + let mut buf = [0; 15]; + let mut send = SendBuf::new(std::u64::MAX); assert_eq!(send.len, 0); assert_eq!(send.off_front(), 0); @@ -1842,143 +2109,149 @@ mod tests { let first = b"something"; let second = b"helloworld"; - assert!(send.push_slice(first, false).is_ok()); + assert!(send.write(first, false).is_ok()); assert_eq!(send.off_front(), 0); - assert!(send.push_slice(second, true).is_ok()); + assert!(send.write(second, true).is_ok()); assert_eq!(send.off_front(), 0); - let write1 = send.pop(4).unwrap(); - assert_eq!(write1.off(), 0); - assert_eq!(write1.len(), 4); - assert_eq!(write1.fin(), false); - assert_eq!(&write1[..], b"some"); + assert_eq!(send.len, 19); + + let (written, fin) = send.emit(&mut buf[..4]).unwrap(); + assert_eq!(written, 4); + assert_eq!(fin, false); + assert_eq!(&buf[..written], b"some"); assert_eq!(send.len, 15); assert_eq!(send.off_front(), 4); - let write2 = send.pop(5).unwrap(); - assert_eq!(write2.off(), 4); - assert_eq!(write2.len(), 5); - assert_eq!(write2.fin(), false); - assert_eq!(&write2[..], b"thing"); + let (written, fin) = send.emit(&mut buf[..5]).unwrap(); + assert_eq!(written, 5); + assert_eq!(fin, false); + assert_eq!(&buf[..written], b"thing"); assert_eq!(send.len, 10); assert_eq!(send.off_front(), 9); - let write3 = send.pop(5).unwrap(); - assert_eq!(write3.off(), 9); - assert_eq!(write3.len(), 5); - assert_eq!(write3.fin(), false); - assert_eq!(&write3[..], b"hello"); + let (written, fin) = send.emit(&mut buf[..5]).unwrap(); + assert_eq!(written, 5); + assert_eq!(fin, false); + assert_eq!(&buf[..written], b"hello"); assert_eq!(send.len, 5); assert_eq!(send.off_front(), 14); - send.push(write2).unwrap(); + send.retransmit(4, 5); assert_eq!(send.len, 10); assert_eq!(send.off_front(), 4); - send.push(write1).unwrap(); + send.retransmit(0, 4); assert_eq!(send.len, 14); assert_eq!(send.off_front(), 0); - let write4 = send.pop(11).unwrap(); - assert_eq!(write4.off(), 0); - assert_eq!(write4.len(), 9); - assert_eq!(write4.fin(), false); - assert_eq!(&write4[..], b"something"); + let (written, fin) = send.emit(&mut buf[..11]).unwrap(); + assert_eq!(written, 9); + assert_eq!(fin, false); + assert_eq!(&buf[..written], b"something"); assert_eq!(send.len, 5); assert_eq!(send.off_front(), 14); - let write5 = send.pop(11).unwrap(); - assert_eq!(write5.off(), 14); - assert_eq!(write5.len(), 5); - assert_eq!(write5.fin(), true); - assert_eq!(&write5[..], b"world"); + let (written, fin) = send.emit(&mut buf[..11]).unwrap(); + assert_eq!(written, 5); + assert_eq!(fin, true); + assert_eq!(&buf[..written], b"world"); assert_eq!(send.len, 0); assert_eq!(send.off_front(), 19); } #[test] fn write_blocked_by_off() { + let mut buf = [0; 10]; + let mut send = SendBuf::default(); assert_eq!(send.len, 0); let first = b"something"; let second = b"helloworld"; - assert_eq!(send.push_slice(first, false), Ok(0)); + assert_eq!(send.write(first, false), Ok(0)); assert_eq!(send.len, 0); - assert_eq!(send.push_slice(second, true), Ok(0)); + assert_eq!(send.write(second, true), Ok(0)); assert_eq!(send.len, 0); send.update_max_data(5); - assert_eq!(send.push_slice(first, false), Ok(5)); + assert_eq!(send.write(first, false), Ok(5)); assert_eq!(send.len, 5); - assert_eq!(send.push_slice(second, true), Ok(0)); + assert_eq!(send.write(second, true), Ok(0)); assert_eq!(send.len, 5); - let write = send.pop(10).unwrap(); - assert_eq!(write.off(), 0); - assert_eq!(write.len(), 5); - assert_eq!(write.fin(), false); - assert_eq!(&write[..], b"somet"); + assert_eq!(send.off_front(), 0); + + let (written, fin) = send.emit(&mut buf[..10]).unwrap(); + assert_eq!(written, 5); + assert_eq!(fin, false); + assert_eq!(&buf[..written], b"somet"); assert_eq!(send.len, 0); - let write = send.pop(10).unwrap(); - assert_eq!(write.off(), 5); - assert_eq!(write.len(), 0); - assert_eq!(write.fin(), false); - assert_eq!(&write[..], b""); + assert_eq!(send.off_front(), 5); + + let (written, fin) = send.emit(&mut buf[..10]).unwrap(); + assert_eq!(written, 0); + assert_eq!(fin, false); + assert_eq!(&buf[..written], b""); assert_eq!(send.len, 0); send.update_max_data(15); - assert_eq!(send.push_slice(&first[5..], false), Ok(4)); + assert_eq!(send.write(&first[5..], false), Ok(4)); assert_eq!(send.len, 4); - assert_eq!(send.push_slice(second, true), Ok(6)); + assert_eq!(send.write(second, true), Ok(6)); assert_eq!(send.len, 10); - let write = send.pop(10).unwrap(); - assert_eq!(write.off(), 5); - assert_eq!(write.len(), 10); - assert_eq!(write.fin(), false); - assert_eq!(&write[..], b"hinghellow"); + assert_eq!(send.off_front(), 5); + + let (written, fin) = send.emit(&mut buf[..10]).unwrap(); + assert_eq!(written, 10); + assert_eq!(fin, false); + assert_eq!(&buf[..10], b"hinghellow"); assert_eq!(send.len, 0); send.update_max_data(25); - assert_eq!(send.push_slice(&second[6..], true), Ok(4)); + assert_eq!(send.write(&second[6..], true), Ok(4)); assert_eq!(send.len, 4); - let write = send.pop(10).unwrap(); - assert_eq!(write.off(), 15); - assert_eq!(write.len(), 4); - assert_eq!(write.fin(), true); - assert_eq!(&write[..], b"orld"); + assert_eq!(send.off_front(), 15); + + let (written, fin) = send.emit(&mut buf[..10]).unwrap(); + assert_eq!(written, 4); + assert_eq!(fin, true); + assert_eq!(&buf[..written], b"orld"); assert_eq!(send.len, 0); } #[test] fn zero_len_write() { + let mut buf = [0; 10]; + let mut send = SendBuf::new(std::u64::MAX); assert_eq!(send.len, 0); let first = b"something"; - assert!(send.push_slice(first, false).is_ok()); + assert!(send.write(first, false).is_ok()); assert_eq!(send.len, 9); - assert!(send.push_slice(&[], true).is_ok()); + assert!(send.write(&[], true).is_ok()); assert_eq!(send.len, 9); - let write = send.pop(10).unwrap(); - assert_eq!(write.off(), 0); - assert_eq!(write.len(), 9); - assert_eq!(write.fin(), true); - assert_eq!(&write[..], b"something"); + assert_eq!(send.off_front(), 0); + + let (written, fin) = send.emit(&mut buf[..10]).unwrap(); + assert_eq!(written, 9); + assert_eq!(fin, true); + assert_eq!(&buf[..written], b"something"); assert_eq!(send.len, 0); } @@ -1993,13 +2266,13 @@ mod tests { let second = RangeBuf::from(b"world", 5, false); let third = RangeBuf::from(b"something", 10, false); - assert_eq!(stream.recv.push(second), Ok(())); - assert_eq!(stream.recv.push(first), Ok(())); + assert_eq!(stream.recv.write(second), Ok(())); + assert_eq!(stream.recv.write(first), Ok(())); assert!(!stream.recv.almost_full()); - assert_eq!(stream.recv.push(third), Err(Error::FlowControl)); + assert_eq!(stream.recv.write(third), Err(Error::FlowControl)); - let (len, fin) = stream.recv.pop(&mut buf).unwrap(); + let (len, fin) = stream.recv.emit(&mut buf).unwrap(); assert_eq!(&buf[..len], b"helloworld"); assert_eq!(fin, false); @@ -2010,7 +2283,7 @@ mod tests { assert!(!stream.recv.almost_full()); let third = RangeBuf::from(b"something", 10, false); - assert_eq!(stream.recv.push(third), Ok(())); + assert_eq!(stream.recv.write(third), Ok(())); } #[test] @@ -2021,8 +2294,8 @@ mod tests { let first = RangeBuf::from(b"hello", 0, true); let second = RangeBuf::from(b"world", 5, false); - assert_eq!(stream.recv.push(first), Ok(())); - assert_eq!(stream.recv.push(second), Err(Error::FinalSize)); + assert_eq!(stream.recv.write(first), Ok(())); + assert_eq!(stream.recv.write(second), Err(Error::FinalSize)); } #[test] @@ -2033,12 +2306,12 @@ mod tests { let first = RangeBuf::from(b"hello", 0, true); let second = RangeBuf::from(b"hello", 0, true); - assert_eq!(stream.recv.push(first), Ok(())); - assert_eq!(stream.recv.push(second), Ok(())); + assert_eq!(stream.recv.write(first), Ok(())); + assert_eq!(stream.recv.write(second), Ok(())); let mut buf = [0; 32]; - let (len, fin) = stream.recv.pop(&mut buf).unwrap(); + let (len, fin) = stream.recv.emit(&mut buf).unwrap(); assert_eq!(&buf[..len], b"hello"); assert_eq!(fin, true); } @@ -2051,8 +2324,8 @@ mod tests { let first = RangeBuf::from(b"hello", 0, true); let second = RangeBuf::from(b"world", 5, true); - assert_eq!(stream.recv.push(second), Ok(())); - assert_eq!(stream.recv.push(first), Err(Error::FinalSize)); + assert_eq!(stream.recv.write(second), Ok(())); + assert_eq!(stream.recv.write(first), Err(Error::FinalSize)); } #[test] @@ -2063,8 +2336,8 @@ mod tests { let first = RangeBuf::from(b"hello", 0, true); let second = RangeBuf::from(b"world", 5, false); - assert_eq!(stream.recv.push(second), Ok(())); - assert_eq!(stream.recv.push(first), Err(Error::FinalSize)); + assert_eq!(stream.recv.write(second), Ok(())); + assert_eq!(stream.recv.write(first), Err(Error::FinalSize)); } #[test] @@ -2077,10 +2350,10 @@ mod tests { let first = RangeBuf::from(b"hello", 0, false); let second = RangeBuf::from(b"world", 5, true); - assert_eq!(stream.recv.push(first), Ok(())); - assert_eq!(stream.recv.push(second), Ok(())); + assert_eq!(stream.recv.write(first), Ok(())); + assert_eq!(stream.recv.write(second), Ok(())); - let (len, fin) = stream.recv.pop(&mut buf).unwrap(); + let (len, fin) = stream.recv.emit(&mut buf).unwrap(); assert_eq!(&buf[..len], b"helloworld"); assert_eq!(fin, true); @@ -2094,7 +2367,7 @@ mod tests { let first = RangeBuf::from(b"hello", 0, true); - assert_eq!(stream.recv.push(first), Ok(())); + assert_eq!(stream.recv.write(first), Ok(())); assert_eq!(stream.recv.reset(10), Err(Error::FinalSize)); } @@ -2105,7 +2378,7 @@ mod tests { let first = RangeBuf::from(b"hello", 0, false); - assert_eq!(stream.recv.push(first), Ok(())); + assert_eq!(stream.recv.write(first), Ok(())); assert_eq!(stream.recv.reset(5), Ok(0)); assert_eq!(stream.recv.reset(5), Ok(0)); } @@ -2117,7 +2390,7 @@ mod tests { let first = RangeBuf::from(b"hello", 0, false); - assert_eq!(stream.recv.push(first), Ok(())); + assert_eq!(stream.recv.write(first), Ok(())); assert_eq!(stream.recv.reset(5), Ok(0)); assert_eq!(stream.recv.reset(10), Err(Error::FinalSize)); } @@ -2129,48 +2402,53 @@ mod tests { let first = RangeBuf::from(b"hello", 0, false); - assert_eq!(stream.recv.push(first), Ok(())); + assert_eq!(stream.recv.write(first), Ok(())); assert_eq!(stream.recv.reset(4), Err(Error::FinalSize)); } #[test] fn send_flow_control() { + let mut buf = [0; 25]; + let mut stream = Stream::new(0, 15, true, true); let first = b"hello"; let second = b"world"; let third = b"something"; - assert!(stream.send.push_slice(first, false).is_ok()); - assert!(stream.send.push_slice(second, false).is_ok()); - assert!(stream.send.push_slice(third, false).is_ok()); + assert!(stream.send.write(first, false).is_ok()); + assert!(stream.send.write(second, false).is_ok()); + assert!(stream.send.write(third, false).is_ok()); + + assert_eq!(stream.send.off_front(), 0); - let write = stream.send.pop(25).unwrap(); - assert_eq!(write.off(), 0); - assert_eq!(write.len(), 15); - assert_eq!(write.fin(), false); - assert_eq!(write.data, b"helloworldsomet"); + let (written, fin) = stream.send.emit(&mut buf[..25]).unwrap(); + assert_eq!(written, 15); + assert_eq!(fin, false); + assert_eq!(&buf[..written], b"helloworldsomet"); - let write = stream.send.pop(25).unwrap(); - assert_eq!(write.off(), 15); - assert_eq!(write.len(), 0); - assert_eq!(write.fin(), false); - assert_eq!(write.data, b""); + assert_eq!(stream.send.off_front(), 15); + + let (written, fin) = stream.send.emit(&mut buf[..25]).unwrap(); + assert_eq!(written, 0); + assert_eq!(fin, false); + assert_eq!(&buf[..written], b""); - let first = RangeBuf::from(b"helloworldsomet", 0, false); - assert_eq!(stream.send.push(first), Ok(())); + stream.send.retransmit(0, 15); - let write = stream.send.pop(10).unwrap(); - assert_eq!(write.off(), 0); - assert_eq!(write.len(), 10); - assert_eq!(write.fin(), false); - assert_eq!(write.data, b"helloworld"); + assert_eq!(stream.send.off_front(), 0); - let write = stream.send.pop(10).unwrap(); - assert_eq!(write.off(), 10); - assert_eq!(write.len(), 5); - assert_eq!(write.fin(), false); - assert_eq!(write.data, b"somet"); + let (written, fin) = stream.send.emit(&mut buf[..10]).unwrap(); + assert_eq!(written, 10); + assert_eq!(fin, false); + assert_eq!(&buf[..written], b"helloworld"); + + assert_eq!(stream.send.off_front(), 10); + + let (written, fin) = stream.send.emit(&mut buf[..10]).unwrap(); + assert_eq!(written, 5); + assert_eq!(fin, false); + assert_eq!(&buf[..written], b"somet"); } #[test] @@ -2181,126 +2459,137 @@ mod tests { let second = b"world"; let third = b"third"; - assert_eq!(stream.send.push_slice(first, false), Ok(5)); + assert_eq!(stream.send.write(first, false), Ok(5)); - assert_eq!(stream.send.push_slice(second, true), Ok(5)); + assert_eq!(stream.send.write(second, true), Ok(5)); assert!(stream.send.is_fin()); - assert_eq!(stream.send.push_slice(third, false), Err(Error::FinalSize)); + assert_eq!(stream.send.write(third, false), Err(Error::FinalSize)); } #[test] fn send_fin_dup() { let mut stream = Stream::new(0, 15, true, true); - let first = RangeBuf::from(b"hello", 0, true); - let second = RangeBuf::from(b"hello", 0, true); + assert_eq!(stream.send.write(b"hello", true), Ok(5)); + assert!(stream.send.is_fin()); - assert_eq!(stream.send.push(first), Ok(())); - assert_eq!(stream.send.push(second), Ok(())); + assert_eq!(stream.send.write(b"", true), Ok(0)); + assert!(stream.send.is_fin()); } #[test] fn send_undo_fin() { let mut stream = Stream::new(0, 15, true, true); - let first = b"hello"; - let second = RangeBuf::from(b"hello", 0, false); - - assert_eq!(stream.send.push_slice(first, true), Ok(5)); + assert_eq!(stream.send.write(b"hello", true), Ok(5)); assert!(stream.send.is_fin()); - assert_eq!(stream.send.push(second), Err(Error::FinalSize)); + assert_eq!( + stream.send.write(b"helloworld", true), + Err(Error::FinalSize) + ); } #[test] fn send_fin_max_data_match() { + let mut buf = [0; 15]; + let mut stream = Stream::new(0, 15, true, true); let slice = b"hellohellohello"; - assert!(stream.send.push_slice(slice, true).is_ok()); + assert!(stream.send.write(slice, true).is_ok()); - let write = stream.send.pop(15).unwrap(); - assert_eq!(write.off(), 0); - assert_eq!(write.len(), 15); - assert_eq!(write.fin(), true); - assert_eq!(write.data, slice); + let (written, fin) = stream.send.emit(&mut buf[..15]).unwrap(); + assert_eq!(written, 15); + assert_eq!(fin, true); + assert_eq!(&buf[..written], slice); } #[test] fn send_fin_zero_length() { + let mut buf = [0; 5]; + let mut stream = Stream::new(0, 15, true, true); - assert_eq!(stream.send.push_slice(b"hello", false), Ok(5)); - assert_eq!(stream.send.push_slice(b"", true), Ok(0)); + assert_eq!(stream.send.write(b"hello", false), Ok(5)); + assert_eq!(stream.send.write(b"", true), Ok(0)); assert!(stream.send.is_fin()); - let write = stream.send.pop(5).unwrap(); - assert_eq!(write.off(), 0); - assert_eq!(write.len(), 5); - assert_eq!(write.fin(), true); - assert_eq!(write.data, b"hello"); + let (written, fin) = stream.send.emit(&mut buf[..5]).unwrap(); + assert_eq!(written, 5); + assert_eq!(fin, true); + assert_eq!(&buf[..written], b"hello"); } #[test] fn send_ack() { + let mut buf = [0; 5]; + let mut stream = Stream::new(0, 15, true, true); - assert_eq!(stream.send.push_slice(b"hello", false), Ok(5)); - assert_eq!(stream.send.push_slice(b"world", false), Ok(5)); - assert_eq!(stream.send.push_slice(b"", true), Ok(0)); + assert_eq!(stream.send.write(b"hello", false), Ok(5)); + assert_eq!(stream.send.write(b"world", false), Ok(5)); + assert_eq!(stream.send.write(b"", true), Ok(0)); assert!(stream.send.is_fin()); - let write = stream.send.pop(5).unwrap(); - assert_eq!(write.off(), 0); - assert_eq!(write.len(), 5); - assert_eq!(write.fin(), false); - assert_eq!(write.data, b"hello"); + assert_eq!(stream.send.off_front(), 0); + + let (written, fin) = stream.send.emit(&mut buf[..5]).unwrap(); + assert_eq!(written, 5); + assert_eq!(fin, false); + assert_eq!(&buf[..written], b"hello"); + + stream.send.ack_and_drop(0, 5); - stream.send.ack(write.off(), write.len()); + stream.send.retransmit(0, 5); - assert_eq!(stream.send.push(write), Ok(())); + assert_eq!(stream.send.off_front(), 5); - let write = stream.send.pop(5).unwrap(); - assert_eq!(write.off(), 5); - assert_eq!(write.len(), 5); - assert_eq!(write.fin(), true); - assert_eq!(write.data, b"world"); + let (written, fin) = stream.send.emit(&mut buf[..5]).unwrap(); + assert_eq!(written, 5); + assert_eq!(fin, true); + assert_eq!(&buf[..written], b"world"); } #[test] fn send_ack_reordering() { + let mut buf = [0; 5]; + let mut stream = Stream::new(0, 15, true, true); - assert_eq!(stream.send.push_slice(b"hello", false), Ok(5)); - assert_eq!(stream.send.push_slice(b"world", false), Ok(5)); - assert_eq!(stream.send.push_slice(b"", true), Ok(0)); + assert_eq!(stream.send.write(b"hello", false), Ok(5)); + assert_eq!(stream.send.write(b"world", false), Ok(5)); + assert_eq!(stream.send.write(b"", true), Ok(0)); assert!(stream.send.is_fin()); - let write1 = stream.send.pop(5).unwrap(); - assert_eq!(write1.off(), 0); - assert_eq!(write1.len(), 5); - assert_eq!(write1.fin(), false); - assert_eq!(write1.data, b"hello"); + assert_eq!(stream.send.off_front(), 0); - let write2 = stream.send.pop(1).unwrap(); - assert_eq!(write2.off(), 5); - assert_eq!(write2.len(), 1); - assert_eq!(write2.fin(), false); - assert_eq!(write2.data, b"w"); + let (written, fin) = stream.send.emit(&mut buf[..5]).unwrap(); + assert_eq!(written, 5); + assert_eq!(fin, false); + assert_eq!(&buf[..written], b"hello"); - stream.send.ack(write2.off(), write2.len()); - stream.send.ack(write1.off(), write1.len()); + assert_eq!(stream.send.off_front(), 5); - assert_eq!(stream.send.push(write1), Ok(())); - assert_eq!(stream.send.push(write2), Ok(())); + let (written, fin) = stream.send.emit(&mut buf[..1]).unwrap(); + assert_eq!(written, 1); + assert_eq!(fin, false); + assert_eq!(&buf[..written], b"w"); + + stream.send.ack_and_drop(5, 1); + stream.send.ack_and_drop(0, 5); + + stream.send.retransmit(0, 5); + stream.send.retransmit(5, 1); - let write = stream.send.pop(5).unwrap(); - assert_eq!(write.off(), 6); - assert_eq!(write.len(), 4); - assert_eq!(write.fin(), true); - assert_eq!(write.data, b"orld"); + assert_eq!(stream.send.off_front(), 6); + + let (written, fin) = stream.send.emit(&mut buf[..5]).unwrap(); + assert_eq!(written, 4); + assert_eq!(fin, true); + assert_eq!(&buf[..written], b"orld"); } #[test] @@ -2309,18 +2598,18 @@ mod tests { let first = RangeBuf::from(b"hello", 0, false); - assert_eq!(stream.recv.push(first), Ok(())); + assert_eq!(stream.recv.write(first), Ok(())); let mut buf = [0; 10]; - let (len, fin) = stream.recv.pop(&mut buf).unwrap(); + let (len, fin) = stream.recv.emit(&mut buf).unwrap(); assert_eq!(&buf[..len], b"hello"); assert_eq!(fin, false); let first = RangeBuf::from(b"elloworld", 1, true); - assert_eq!(stream.recv.push(first), Ok(())); + assert_eq!(stream.recv.write(first), Ok(())); - let (len, fin) = stream.recv.pop(&mut buf).unwrap(); + let (len, fin) = stream.recv.emit(&mut buf).unwrap(); assert_eq!(&buf[..len], b"world"); assert_eq!(fin, true); } @@ -2329,26 +2618,26 @@ mod tests { fn stream_complete() { let mut stream = Stream::new(30, 30, true, true); - assert_eq!(stream.send.push_slice(b"hello", false), Ok(5)); - assert_eq!(stream.send.push_slice(b"world", false), Ok(5)); + assert_eq!(stream.send.write(b"hello", false), Ok(5)); + assert_eq!(stream.send.write(b"world", false), Ok(5)); assert!(!stream.send.is_complete()); assert!(!stream.send.is_fin()); - assert_eq!(stream.send.push_slice(b"", true), Ok(0)); + assert_eq!(stream.send.write(b"", true), Ok(0)); assert!(!stream.send.is_complete()); assert!(stream.send.is_fin()); let buf = RangeBuf::from(b"hello", 0, true); - assert!(stream.recv.push(buf).is_ok()); + assert!(stream.recv.write(buf).is_ok()); assert!(!stream.recv.is_fin()); stream.send.ack(6, 4); assert!(!stream.send.is_complete()); let mut buf = [0; 2]; - assert_eq!(stream.recv.pop(&mut buf), Ok((2, false))); + assert_eq!(stream.recv.emit(&mut buf), Ok((2, false))); assert!(!stream.recv.is_fin()); stream.send.ack(1, 5); @@ -2360,7 +2649,7 @@ mod tests { assert!(!stream.is_complete()); let mut buf = [0; 3]; - assert_eq!(stream.recv.pop(&mut buf), Ok((3, true))); + assert_eq!(stream.recv.emit(&mut buf), Ok((3, true))); assert!(stream.recv.is_fin()); assert!(stream.is_complete()); @@ -2368,24 +2657,372 @@ mod tests { #[test] fn send_fin_zero_length_output() { + let mut buf = [0; 5]; + let mut stream = Stream::new(0, 15, true, true); - assert_eq!(stream.send.push_slice(b"hello", false), Ok(5)); + assert_eq!(stream.send.write(b"hello", false), Ok(5)); + assert_eq!(stream.send.off_front(), 0); assert!(!stream.send.is_fin()); - let write = stream.send.pop(5).unwrap(); - assert_eq!(write.off(), 0); - assert_eq!(write.len(), 5); - assert_eq!(write.fin(), false); - assert_eq!(write.data, b"hello"); + let (written, fin) = stream.send.emit(&mut buf).unwrap(); + assert_eq!(written, 5); + assert_eq!(fin, false); + assert_eq!(&buf[..written], b"hello"); - assert_eq!(stream.send.push_slice(b"", true), Ok(0)); + assert_eq!(stream.send.write(b"", true), Ok(0)); assert!(stream.send.is_fin()); + assert_eq!(stream.send.off_front(), 5); + + let (written, fin) = stream.send.emit(&mut buf).unwrap(); + assert_eq!(written, 0); + assert_eq!(fin, true); + assert_eq!(&buf[..written], b""); + } + + #[test] + fn send_emit() { + let mut buf = [0; 5]; + + let mut stream = Stream::new(0, 20, true, true); + + assert_eq!(stream.send.write(b"hello", false), Ok(5)); + assert_eq!(stream.send.write(b"world", false), Ok(5)); + assert_eq!(stream.send.write(b"olleh", false), Ok(5)); + assert_eq!(stream.send.write(b"dlrow", true), Ok(5)); + assert_eq!(stream.send.off_front(), 0); + assert_eq!(stream.send.data.len(), 4); + + assert!(stream.is_flushable()); + + assert!(stream.send.ready()); + assert_eq!(stream.send.emit(&mut buf[..4]), Ok((4, false))); + assert_eq!(stream.send.off_front(), 4); + assert_eq!(&buf[..4], b"hell"); + + assert!(stream.send.ready()); + assert_eq!(stream.send.emit(&mut buf[..4]), Ok((4, false))); + assert_eq!(stream.send.off_front(), 8); + assert_eq!(&buf[..4], b"owor"); + + assert!(stream.send.ready()); + assert_eq!(stream.send.emit(&mut buf[..2]), Ok((2, false))); + assert_eq!(stream.send.off_front(), 10); + assert_eq!(&buf[..2], b"ld"); + + assert!(stream.send.ready()); + assert_eq!(stream.send.emit(&mut buf[..1]), Ok((1, false))); + assert_eq!(stream.send.off_front(), 11); + assert_eq!(&buf[..1], b"o"); + + assert!(stream.send.ready()); + assert_eq!(stream.send.emit(&mut buf[..5]), Ok((5, false))); + assert_eq!(stream.send.off_front(), 16); + assert_eq!(&buf[..5], b"llehd"); + + assert!(stream.send.ready()); + assert_eq!(stream.send.emit(&mut buf[..5]), Ok((4, true))); + assert_eq!(stream.send.off_front(), 20); + assert_eq!(&buf[..4], b"lrow"); + + assert!(!stream.is_flushable()); + + assert!(!stream.send.ready()); + assert_eq!(stream.send.emit(&mut buf[..5]), Ok((0, true))); + assert_eq!(stream.send.off_front(), 20); + } + + #[test] + fn send_emit_ack() { + let mut buf = [0; 5]; + + let mut stream = Stream::new(0, 20, true, true); + + assert_eq!(stream.send.write(b"hello", false), Ok(5)); + assert_eq!(stream.send.write(b"world", false), Ok(5)); + assert_eq!(stream.send.write(b"olleh", false), Ok(5)); + assert_eq!(stream.send.write(b"dlrow", true), Ok(5)); + assert_eq!(stream.send.off_front(), 0); + assert_eq!(stream.send.data.len(), 4); + + assert!(stream.is_flushable()); + + assert!(stream.send.ready()); + assert_eq!(stream.send.emit(&mut buf[..4]), Ok((4, false))); + assert_eq!(stream.send.off_front(), 4); + assert_eq!(&buf[..4], b"hell"); + + assert!(stream.send.ready()); + assert_eq!(stream.send.emit(&mut buf[..4]), Ok((4, false))); + assert_eq!(stream.send.off_front(), 8); + assert_eq!(&buf[..4], b"owor"); + + stream.send.ack_and_drop(0, 5); + assert_eq!(stream.send.data.len(), 3); + + assert!(stream.send.ready()); + assert_eq!(stream.send.emit(&mut buf[..2]), Ok((2, false))); + assert_eq!(stream.send.off_front(), 10); + assert_eq!(&buf[..2], b"ld"); + + stream.send.ack_and_drop(7, 5); + assert_eq!(stream.send.data.len(), 3); + + assert!(stream.send.ready()); + assert_eq!(stream.send.emit(&mut buf[..1]), Ok((1, false))); + assert_eq!(stream.send.off_front(), 11); + assert_eq!(&buf[..1], b"o"); + + assert!(stream.send.ready()); + assert_eq!(stream.send.emit(&mut buf[..5]), Ok((5, false))); + assert_eq!(stream.send.off_front(), 16); + assert_eq!(&buf[..5], b"llehd"); + + stream.send.ack_and_drop(5, 7); + assert_eq!(stream.send.data.len(), 2); + + assert!(stream.send.ready()); + assert_eq!(stream.send.emit(&mut buf[..5]), Ok((4, true))); + assert_eq!(stream.send.off_front(), 20); + assert_eq!(&buf[..4], b"lrow"); + + assert!(!stream.is_flushable()); + + assert!(!stream.send.ready()); + assert_eq!(stream.send.emit(&mut buf[..5]), Ok((0, true))); + assert_eq!(stream.send.off_front(), 20); + + stream.send.ack_and_drop(22, 4); + assert_eq!(stream.send.data.len(), 2); + + stream.send.ack_and_drop(20, 1); + assert_eq!(stream.send.data.len(), 2); + } + + #[test] + fn send_emit_retransmit() { + let mut buf = [0; 5]; + + let mut stream = Stream::new(0, 20, true, true); + + assert_eq!(stream.send.write(b"hello", false), Ok(5)); + assert_eq!(stream.send.write(b"world", false), Ok(5)); + assert_eq!(stream.send.write(b"olleh", false), Ok(5)); + assert_eq!(stream.send.write(b"dlrow", true), Ok(5)); + assert_eq!(stream.send.off_front(), 0); + assert_eq!(stream.send.data.len(), 4); + + assert!(stream.is_flushable()); + + assert!(stream.send.ready()); + assert_eq!(stream.send.emit(&mut buf[..4]), Ok((4, false))); + assert_eq!(stream.send.off_front(), 4); + assert_eq!(&buf[..4], b"hell"); + + assert!(stream.send.ready()); + assert_eq!(stream.send.emit(&mut buf[..4]), Ok((4, false))); + assert_eq!(stream.send.off_front(), 8); + assert_eq!(&buf[..4], b"owor"); + + stream.send.retransmit(3, 3); + assert_eq!(stream.send.off_front(), 3); + + assert!(stream.send.ready()); + assert_eq!(stream.send.emit(&mut buf[..3]), Ok((3, false))); + assert_eq!(stream.send.off_front(), 8); + assert_eq!(&buf[..3], b"low"); + + assert!(stream.send.ready()); + assert_eq!(stream.send.emit(&mut buf[..2]), Ok((2, false))); + assert_eq!(stream.send.off_front(), 10); + assert_eq!(&buf[..2], b"ld"); + + stream.send.ack_and_drop(7, 2); + + stream.send.retransmit(8, 2); + + assert!(stream.send.ready()); + assert_eq!(stream.send.emit(&mut buf[..2]), Ok((2, false))); + assert_eq!(stream.send.off_front(), 10); + assert_eq!(&buf[..2], b"ld"); + + assert!(stream.send.ready()); + assert_eq!(stream.send.emit(&mut buf[..1]), Ok((1, false))); + assert_eq!(stream.send.off_front(), 11); + assert_eq!(&buf[..1], b"o"); + + assert!(stream.send.ready()); + assert_eq!(stream.send.emit(&mut buf[..5]), Ok((5, false))); + assert_eq!(stream.send.off_front(), 16); + assert_eq!(&buf[..5], b"llehd"); + + stream.send.retransmit(12, 2); + + assert!(stream.send.ready()); + assert_eq!(stream.send.emit(&mut buf[..2]), Ok((2, false))); + assert_eq!(stream.send.off_front(), 16); + assert_eq!(&buf[..2], b"le"); + + assert!(stream.send.ready()); + assert_eq!(stream.send.emit(&mut buf[..5]), Ok((4, true))); + assert_eq!(stream.send.off_front(), 20); + assert_eq!(&buf[..4], b"lrow"); + + assert!(!stream.is_flushable()); + + assert!(!stream.send.ready()); + assert_eq!(stream.send.emit(&mut buf[..5]), Ok((0, true))); + assert_eq!(stream.send.off_front(), 20); + + stream.send.retransmit(7, 12); + + assert!(stream.send.ready()); + assert_eq!(stream.send.emit(&mut buf[..5]), Ok((5, false))); + assert_eq!(stream.send.off_front(), 12); + assert_eq!(&buf[..5], b"rldol"); + + assert!(stream.send.ready()); + assert_eq!(stream.send.emit(&mut buf[..5]), Ok((5, false))); + assert_eq!(stream.send.off_front(), 17); + assert_eq!(&buf[..5], b"lehdl"); + + assert!(stream.send.ready()); + assert_eq!(stream.send.emit(&mut buf[..5]), Ok((2, false))); + assert_eq!(stream.send.off_front(), 20); + assert_eq!(&buf[..2], b"ro"); + + stream.send.ack_and_drop(12, 7); + + stream.send.retransmit(7, 12); + + assert!(stream.send.ready()); + assert_eq!(stream.send.emit(&mut buf[..5]), Ok((5, false))); + assert_eq!(stream.send.off_front(), 12); + assert_eq!(&buf[..5], b"rldol"); + + assert!(stream.send.ready()); + assert_eq!(stream.send.emit(&mut buf[..5]), Ok((5, false))); + assert_eq!(stream.send.off_front(), 17); + assert_eq!(&buf[..5], b"lehdl"); + + assert!(stream.send.ready()); + assert_eq!(stream.send.emit(&mut buf[..5]), Ok((2, false))); + assert_eq!(stream.send.off_front(), 20); + assert_eq!(&buf[..2], b"ro"); + } + + #[test] + fn rangebuf_split_off() { + let mut buf = RangeBuf::from(b"helloworld", 5, true); + assert_eq!(buf.start, 0); + assert_eq!(buf.pos, 0); + assert_eq!(buf.len, 10); + assert_eq!(buf.off, 5); + assert_eq!(buf.fin, true); + + assert_eq!(buf.len(), 10); + assert_eq!(buf.off(), 5); + assert_eq!(buf.fin(), true); + + assert_eq!(&buf[..], b"helloworld"); + + // Advance buffer. + buf.consume(5); + + assert_eq!(buf.start, 0); + assert_eq!(buf.pos, 5); + assert_eq!(buf.len, 10); + assert_eq!(buf.off, 5); + assert_eq!(buf.fin, true); + + assert_eq!(buf.len(), 5); + assert_eq!(buf.off(), 10); + assert_eq!(buf.fin(), true); + + assert_eq!(&buf[..], b"world"); + + // Split buffer before position. + let mut new_buf = buf.split_off(3); + + assert_eq!(buf.start, 0); + assert_eq!(buf.pos, 3); + assert_eq!(buf.len, 3); + assert_eq!(buf.off, 5); + assert_eq!(buf.fin, false); + + assert_eq!(buf.len(), 0); + assert_eq!(buf.off(), 8); + assert_eq!(buf.fin(), false); + + assert_eq!(&buf[..], b""); + + assert_eq!(new_buf.start, 3); + assert_eq!(new_buf.pos, 5); + assert_eq!(new_buf.len, 7); + assert_eq!(new_buf.off, 8); + assert_eq!(new_buf.fin, true); + + assert_eq!(new_buf.len(), 5); + assert_eq!(new_buf.off(), 10); + assert_eq!(new_buf.fin(), true); + + assert_eq!(&new_buf[..], b"world"); + + // Advance buffer. + new_buf.consume(2); + + assert_eq!(new_buf.start, 3); + assert_eq!(new_buf.pos, 7); + assert_eq!(new_buf.len, 7); + assert_eq!(new_buf.off, 8); + assert_eq!(new_buf.fin, true); + + assert_eq!(new_buf.len(), 3); + assert_eq!(new_buf.off(), 12); + assert_eq!(new_buf.fin(), true); + + assert_eq!(&new_buf[..], b"rld"); + + // Split buffer after position. + let mut new_new_buf = new_buf.split_off(5); + + assert_eq!(new_buf.start, 3); + assert_eq!(new_buf.pos, 7); + assert_eq!(new_buf.len, 5); + assert_eq!(new_buf.off, 8); + assert_eq!(new_buf.fin, false); + + assert_eq!(new_buf.len(), 1); + assert_eq!(new_buf.off(), 12); + assert_eq!(new_buf.fin(), false); + + assert_eq!(&new_buf[..], b"r"); + + assert_eq!(new_new_buf.start, 8); + assert_eq!(new_new_buf.pos, 8); + assert_eq!(new_new_buf.len, 2); + assert_eq!(new_new_buf.off, 13); + assert_eq!(new_new_buf.fin, true); + + assert_eq!(new_new_buf.len(), 2); + assert_eq!(new_new_buf.off(), 13); + assert_eq!(new_new_buf.fin(), true); + + assert_eq!(&new_new_buf[..], b"ld"); + + // Advance buffer. + new_new_buf.consume(2); + + assert_eq!(new_new_buf.start, 8); + assert_eq!(new_new_buf.pos, 10); + assert_eq!(new_new_buf.len, 2); + assert_eq!(new_new_buf.off, 13); + assert_eq!(new_new_buf.fin, true); + + assert_eq!(new_new_buf.len(), 0); + assert_eq!(new_new_buf.off(), 15); + assert_eq!(new_new_buf.fin(), true); - let write = stream.send.pop(5).unwrap(); - assert_eq!(write.off(), 5); - assert_eq!(write.len(), 0); - assert_eq!(write.fin(), true); - assert_eq!(write.data, b""); + assert_eq!(&new_new_buf[..], b""); } } @@ -28,6 +28,8 @@ use std::ffi; use std::ptr; use std::slice; +use std::io::Write; + use libc::c_char; use libc::c_int; use libc::c_long; @@ -38,6 +40,7 @@ use crate::Error; use crate::Result; use crate::Connection; +use crate::ConnectionError; use crate::crypto; use crate::octets; @@ -64,6 +67,10 @@ struct SSL_CIPHER(c_void); #[allow(non_camel_case_types)] #[repr(transparent)] +struct SSL_SESSION(c_void); + +#[allow(non_camel_case_types)] +#[repr(transparent)] struct X509_VERIFY_PARAM(c_void); #[allow(non_camel_case_types)] @@ -73,8 +80,17 @@ struct X509_STORE(c_void); #[allow(non_camel_case_types)] #[repr(transparent)] +#[cfg(windows)] struct X509(c_void); +#[allow(non_camel_case_types)] +#[repr(transparent)] +struct STACK_OF(c_void); + +#[allow(non_camel_case_types)] +#[repr(transparent)] +struct CRYPTO_BUFFER(c_void); + #[repr(C)] #[allow(non_camel_case_types)] struct SSL_QUIC_METHOD { @@ -130,6 +146,8 @@ impl Context { let mut ctx = Context(ctx_raw); + ctx.set_session_callback(); + ctx.load_ca_certs()?; Ok(ctx) @@ -235,6 +253,19 @@ impl Context { Ok(()) } + fn set_session_callback(&mut self) { + unsafe { + // This is needed to enable the session callback on the client. On + // the server it doesn't do anything. + SSL_CTX_set_session_cache_mode( + self.as_ptr(), + 0x0001, // SSL_SESS_CACHE_CLIENT + ); + + SSL_CTX_sess_set_new_cb(self.as_ptr(), new_session); + }; + } + pub fn set_verify(&mut self, verify: bool) { let mode = if verify { 0x01 // SSL_VERIFY_PEER @@ -276,6 +307,12 @@ impl Context { }) } + pub fn set_ticket_key(&mut self, key: &[u8]) -> Result<()> { + map_result(unsafe { + SSL_CTX_set_tlsext_ticket_keys(self.as_ptr(), key.as_ptr(), key.len()) + }) + } + pub fn set_early_data_enabled(&mut self, enabled: bool) { let enabled = if enabled { 1 } else { 0 }; @@ -300,6 +337,7 @@ impl Drop for Context { pub struct Handshake(*mut SSL); impl Handshake { + #[cfg(feature = "ffi")] pub unsafe fn from_ptr(ssl: *mut c_void) -> Handshake { let ssl = ssl as *mut SSL; Handshake(ssl) @@ -328,6 +366,12 @@ impl Handshake { Ok(()) } + pub fn use_legacy_codepoint(&self, use_legacy: bool) { + unsafe { + SSL_set_quic_use_legacy_codepoint(self.as_ptr(), use_legacy as c_int); + } + } + pub fn set_state(&self, is_server: bool) { unsafe { if is_server { @@ -392,6 +436,13 @@ impl Handshake { }) } + #[cfg(test)] + pub fn set_options(&mut self, opts: u32) { + unsafe { + SSL_set_options(self.as_ptr(), opts); + } + } + pub fn quic_transport_params(&self) -> &[u8] { let mut ptr: *const u8 = ptr::null(); let mut len: usize = 0; @@ -422,6 +473,28 @@ impl Handshake { unsafe { slice::from_raw_parts(ptr, len as usize) } } + pub fn set_session(&self, session: &[u8]) -> Result<()> { + unsafe { + let ctx = SSL_get_SSL_CTX(self.as_ptr()); + + if ctx.is_null() { + return Err(Error::TlsFail); + } + + let session = + SSL_SESSION_from_bytes(session.as_ptr(), session.len(), ctx); + + if session.is_null() { + return Err(Error::TlsFail); + } + + let rc = SSL_set_session(self.as_ptr(), session); + SSL_SESSION_free(session); + + map_result(rc) + } + } + pub fn provide_data(&self, level: crypto::Level, buf: &[u8]) -> Result<()> { map_result_ssl(self, unsafe { SSL_provide_quic_data(self.as_ptr(), level, buf.as_ptr(), buf.len()) @@ -432,6 +505,16 @@ impl Handshake { map_result_ssl(self, unsafe { SSL_do_handshake(self.as_ptr()) }) } + pub fn process_post_handshake(&self) -> Result<()> { + map_result_ssl(self, unsafe { + SSL_process_quic_post_handshake(self.as_ptr()) + }) + } + + pub fn reset_early_data_reject(&self) { + unsafe { SSL_reset_early_data_reject(self.as_ptr()) }; + } + pub fn write_level(&self) -> crypto::Level { unsafe { SSL_quic_write_level(self.as_ptr()) } } @@ -481,24 +564,23 @@ impl Handshake { pub fn peer_cert(&self) -> Option<Vec<u8>> { let peer_cert = unsafe { - let mut out: *mut libc::c_uchar = ptr::null_mut(); - - let x509 = SSL_get_peer_certificate(self.as_ptr()); - if x509.is_null() { + let chain = + map_result_ptr(SSL_get0_peer_certificates(self.as_ptr())).ok()?; + if sk_num(chain) <= 0 { return None; } - let out_len = i2d_X509(x509, &mut out); - if out_len <= 0 { + let buffer = + map_result_ptr(sk_value(chain, 0) as *const CRYPTO_BUFFER) + .ok()?; + let out_len = CRYPTO_BUFFER_len(buffer); + if out_len == 0 { return None; } + let out = CRYPTO_BUFFER_data(buffer); let der = slice::from_raw_parts(out, out_len as usize); - let der = der.to_vec(); - - OPENSSL_free(out as *mut c_void); - - der + der.to_vec() }; Some(peer_cert) @@ -676,7 +758,7 @@ extern fn add_handshake_data( &mut conn.pkt_num_spaces[packet::EPOCH_APPLICATION], }; - if space.crypto_stream.send.push_slice(buf, false).is_err() { + if space.crypto_stream.send.write(buf, false).is_err() { return 0; } @@ -706,7 +788,11 @@ extern fn send_alert(ssl: *mut SSL, level: crypto::Level, alert: u8) -> c_int { ); let error: u64 = TLS_ALERT_ERROR + u64::from(alert); - conn.error = Some(error); + conn.local_error = Some(ConnectionError { + is_app: false, + error_code: error, + reason: Vec::new(), + }); 1 } @@ -779,6 +865,68 @@ extern fn select_alpn( 3 // SSL_TLSEXT_ERR_NOACK } +#[no_mangle] +extern fn new_session(ssl: *mut SSL, session: *mut SSL_SESSION) -> c_int { + let conn = + match get_ex_data_from_ptr::<Connection>(ssl, *QUICHE_EX_DATA_INDEX) { + Some(v) => v, + + None => return 0, + }; + + let handshake = Handshake(ssl); + let peer_params = handshake.quic_transport_params(); + + // Serialize session object into buffer. + let session_bytes = unsafe { + let mut out: *mut u8 = std::ptr::null_mut(); + let mut out_len: usize = 0; + + if SSL_SESSION_to_bytes(session, &mut out, &mut out_len) == 0 { + return 0; + } + + let session_bytes = std::slice::from_raw_parts(out, out_len).to_vec(); + OPENSSL_free(out as *mut c_void); + + session_bytes + }; + + let mut buffer = + Vec::with_capacity(8 + peer_params.len() + 8 + session_bytes.len()); + + let session_bytes_len = session_bytes.len() as u64; + + if buffer.write(&session_bytes_len.to_be_bytes()).is_err() { + std::mem::forget(handshake); + return 0; + } + + if buffer.write(&session_bytes).is_err() { + std::mem::forget(handshake); + return 0; + } + + let peer_params_len = peer_params.len() as u64; + + if buffer.write(&peer_params_len.to_be_bytes()).is_err() { + std::mem::forget(handshake); + return 0; + } + + if buffer.write(&peer_params).is_err() { + std::mem::forget(handshake); + return 0; + } + + conn.session = Some(buffer); + + // Prevent handshake from being freed, as we still need it. + std::mem::forget(handshake); + + 0 +} + fn map_result(bssl_result: c_int) -> Result<()> { match bssl_result { 1 => Ok(()), @@ -826,6 +974,9 @@ fn map_result_ssl(ssl: &Handshake, bssl_result: c_int) -> Result<()> { // SSL_ERROR_SYSCALL 5 => Err(Error::TlsFail), + // SSL_ERROR_PENDING_SESSION + 11 => Err(Error::Done), + // SSL_ERROR_PENDING_CERTIFICATE 12 => Err(Error::Done), @@ -835,6 +986,15 @@ fn map_result_ssl(ssl: &Handshake, bssl_result: c_int) -> Result<()> { // SSL_ERROR_PENDING_TICKET 14 => Err(Error::Done), + // SSL_ERROR_EARLY_DATA_REJECTED + 15 => { + ssl.reset_early_data_reject(); + Err(Error::Done) + }, + + // SSL_ERROR_WANT_CERTIFICATE_VERIFY + 16 => Err(Error::Done), + _ => Err(Error::TlsFail), } }, @@ -884,6 +1044,10 @@ extern { ctx: *mut SSL_CTX, cb: extern fn(ssl: *mut SSL, line: *const c_char), ); + fn SSL_CTX_set_tlsext_ticket_keys( + ctx: *mut SSL_CTX, key: *const u8, key_len: usize, + ) -> c_int; + fn SSL_CTX_set_alpn_protos( ctx: *mut SSL_CTX, protos: *const u8, protos_len: usize, ) -> c_int; @@ -903,6 +1067,13 @@ extern { fn SSL_CTX_set_early_data_enabled(ctx: *mut SSL_CTX, enabled: i32); + fn SSL_CTX_set_session_cache_mode(ctx: *mut SSL_CTX, mode: c_int) -> c_int; + + fn SSL_CTX_sess_set_new_cb( + ctx: *mut SSL_CTX, + cb: extern fn(ssl: *mut SSL, session: *mut SSL_SESSION) -> c_int, + ); + // SSL fn SSL_get_ex_new_index( argl: c_long, argp: *const c_void, unused: *const c_void, @@ -931,7 +1102,11 @@ extern { sigalg: u16, include_curve: i32, ) -> *const c_char; - fn SSL_get_peer_certificate(ssl: *mut SSL) -> *const X509; + fn SSL_set_session(ssl: *mut SSL, session: *mut SSL_SESSION) -> c_int; + + fn SSL_get_SSL_CTX(ssl: *mut SSL) -> *mut SSL_CTX; + + fn SSL_get0_peer_certificates(ssl: *mut SSL) -> *const STACK_OF; fn SSL_set_min_proto_version(ssl: *mut SSL, version: u16); fn SSL_set_max_proto_version(ssl: *mut SSL, version: u16); @@ -944,10 +1119,15 @@ extern { ssl: *mut SSL, params: *const u8, params_len: usize, ) -> c_int; + #[cfg(test)] + fn SSL_set_options(ssl: *mut SSL, opts: u32) -> u32; + fn SSL_set_quic_method( ssl: *mut SSL, quic_method: *const SSL_QUIC_METHOD, ) -> c_int; + fn SSL_set_quic_use_legacy_codepoint(ssl: *mut SSL, use_legacy: c_int); + fn SSL_set_quic_early_data_context( ssl: *mut SSL, context: *const u8, context_len: usize, ) -> c_int; @@ -964,6 +1144,10 @@ extern { ssl: *mut SSL, level: crypto::Level, data: *const u8, len: usize, ) -> c_int; + fn SSL_process_quic_post_handshake(ssl: *mut SSL) -> c_int; + + fn SSL_reset_early_data_reject(ssl: *mut SSL); + fn SSL_do_handshake(ssl: *mut SSL) -> c_int; fn SSL_quic_write_level(ssl: *mut SSL) -> crypto::Level; @@ -981,6 +1165,17 @@ extern { // SSL_CIPHER fn SSL_CIPHER_get_id(cipher: *const SSL_CIPHER) -> c_uint; + // SSL_SESSION + fn SSL_SESSION_to_bytes( + session: *const SSL_SESSION, out: *mut *mut u8, out_len: *mut usize, + ) -> c_int; + + fn SSL_SESSION_from_bytes( + input: *const u8, input_len: usize, ctx: *const SSL_CTX, + ) -> *mut SSL_SESSION; + + fn SSL_SESSION_free(session: *mut SSL_SESSION); + // X509_VERIFY_PARAM fn X509_VERIFY_PARAM_set1_host( param: *mut X509_VERIFY_PARAM, name: *const c_char, namelen: usize, @@ -996,7 +1191,13 @@ extern { #[cfg(windows)] fn d2i_X509(px: *mut X509, input: *const *const u8, len: c_int) -> *mut X509; - fn i2d_X509(px: *const X509, out: *mut *mut u8) -> c_int; + // STACK_OF + fn sk_num(stack: *const STACK_OF) -> c_int; + fn sk_value(stack: *const STACK_OF, idx: c_int) -> *mut c_void; + + // CRYPTO_BUFFER + fn CRYPTO_BUFFER_len(buffer: *const CRYPTO_BUFFER) -> usize; + fn CRYPTO_BUFFER_data(buffer: *const CRYPTO_BUFFER) -> *const u8; // ERR fn ERR_peek_error() -> c_uint; |