aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/build.rs45
-rw-r--r--src/crypto.rs105
-rw-r--r--src/dgram.rs4
-rw-r--r--src/ffi.rs387
-rw-r--r--src/frame.rs162
-rw-r--r--src/h3/ffi.rs19
-rw-r--r--src/h3/frame.rs202
-rw-r--r--src/h3/mod.rs1633
-rw-r--r--src/h3/qpack/decoder.rs11
-rw-r--r--src/h3/qpack/encoder.rs16
-rw-r--r--src/h3/qpack/mod.rs48
-rw-r--r--src/h3/qpack/static_table.rs206
-rw-r--r--src/h3/stream.rs111
-rw-r--r--src/lib.rs4302
-rw-r--r--src/octets.rs52
-rw-r--r--src/packet.rs696
-rw-r--r--src/recovery/cubic.rs503
-rw-r--r--src/recovery/delivery_rate.rs5
-rw-r--r--src/recovery/hystart.rs157
-rw-r--r--src/recovery/mod.rs271
-rw-r--r--src/recovery/reno.rs150
-rw-r--r--src/stream.rs1423
-rw-r--r--src/tls.rs231
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
}
diff --git a/src/ffi.rs b/src/ffi.rs
index 39564dc..3290964 100644
--- a/src/ffi.rs
+++ b/src/ffi.rs
@@ -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));
+ }
}
diff --git a/src/lib.rs b/src/lib.rs
index 351352c..8ad1194 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -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"");
}
}
diff --git a/src/tls.rs b/src/tls.rs
index 8044bb7..ed7a5bf 100644
--- a/src/tls.rs
+++ b/src/tls.rs
@@ -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;