summaryrefslogtreecommitdiffstats
path: root/third_party/rust/neqo-transport/src/connection/tests/close.rs
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
commit26a029d407be480d791972afb5975cf62c9360a6 (patch)
treef435a8308119effd964b339f76abb83a57c29483 /third_party/rust/neqo-transport/src/connection/tests/close.rs
parentInitial commit. (diff)
downloadfirefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz
firefox-26a029d407be480d791972afb5975cf62c9360a6.zip
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/rust/neqo-transport/src/connection/tests/close.rs')
-rw-r--r--third_party/rust/neqo-transport/src/connection/tests/close.rs210
1 files changed, 210 insertions, 0 deletions
diff --git a/third_party/rust/neqo-transport/src/connection/tests/close.rs b/third_party/rust/neqo-transport/src/connection/tests/close.rs
new file mode 100644
index 0000000000..f45e77e549
--- /dev/null
+++ b/third_party/rust/neqo-transport/src/connection/tests/close.rs
@@ -0,0 +1,210 @@
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use std::time::Duration;
+
+use test_fixture::{self, datagram, now};
+
+use super::{
+ super::{Connection, Output, State},
+ connect, connect_force_idle, default_client, default_server, send_something,
+};
+use crate::{
+ tparams::{self, TransportParameter},
+ AppError, ConnectionError, Error, ERROR_APPLICATION_CLOSE,
+};
+
+fn assert_draining(c: &Connection, expected: &Error) {
+ assert!(c.state().closed());
+ if let State::Draining {
+ error: ConnectionError::Transport(error),
+ ..
+ } = c.state()
+ {
+ assert_eq!(error, expected);
+ } else {
+ panic!();
+ }
+}
+
+#[test]
+fn connection_close() {
+ let mut client = default_client();
+ let mut server = default_server();
+ connect(&mut client, &mut server);
+
+ let now = now();
+
+ client.close(now, 42, "");
+
+ let out = client.process(None, now);
+
+ server.process_input(&out.dgram().unwrap(), now);
+ assert_draining(&server, &Error::PeerApplicationError(42));
+}
+
+#[test]
+fn connection_close_with_long_reason_string() {
+ let mut client = default_client();
+ let mut server = default_server();
+ connect(&mut client, &mut server);
+
+ let now = now();
+ // Create a long string and use it as the close reason.
+ let long_reason = String::from_utf8([0x61; 2048].to_vec()).unwrap();
+ client.close(now, 42, long_reason);
+
+ let out = client.process(None, now);
+
+ server.process_input(&out.dgram().unwrap(), now);
+ assert_draining(&server, &Error::PeerApplicationError(42));
+}
+
+// During the handshake, an application close should be sanitized.
+#[test]
+fn early_application_close() {
+ let mut client = default_client();
+ let mut server = default_server();
+
+ // One flight each.
+ let dgram = client.process(None, now()).dgram();
+ assert!(dgram.is_some());
+ let dgram = server.process(dgram.as_ref(), now()).dgram();
+ assert!(dgram.is_some());
+
+ server.close(now(), 77, String::new());
+ assert!(server.state().closed());
+ let dgram = server.process(None, now()).dgram();
+ assert!(dgram.is_some());
+
+ client.process_input(&dgram.unwrap(), now());
+ assert_draining(&client, &Error::PeerError(ERROR_APPLICATION_CLOSE));
+}
+
+#[test]
+fn bad_tls_version() {
+ let mut client = default_client();
+ // Do a bad, bad thing.
+ client
+ .crypto
+ .tls
+ .set_option(neqo_crypto::Opt::Tls13CompatMode, true)
+ .unwrap();
+ let mut server = default_server();
+
+ let dgram = client.process(None, now()).dgram();
+ assert!(dgram.is_some());
+ let dgram = server.process(dgram.as_ref(), now()).dgram();
+ assert_eq!(
+ *server.state(),
+ State::Closed(ConnectionError::Transport(Error::ProtocolViolation))
+ );
+ assert!(dgram.is_some());
+ client.process_input(&dgram.unwrap(), now());
+ assert_draining(&client, &Error::PeerError(Error::ProtocolViolation.code()));
+}
+
+/// Test the interaction between the loss recovery timer
+/// and the closing timer.
+#[test]
+fn closing_timers_interation() {
+ let mut client = default_client();
+ let mut server = default_server();
+ connect(&mut client, &mut server);
+
+ let mut now = now();
+
+ // We're going to induce time-based loss recovery so that timer is set.
+ let _p1 = send_something(&mut client, now);
+ let p2 = send_something(&mut client, now);
+ let ack = server.process(Some(&p2), now).dgram();
+ assert!(ack.is_some()); // This is an ACK.
+
+ // After processing the ACK, we should be on the loss recovery timer.
+ let cb = client.process(ack.as_ref(), now).callback();
+ assert_ne!(cb, Duration::from_secs(0));
+ now += cb;
+
+ // Rather than let the timer pop, close the connection.
+ client.close(now, 0, "");
+ let client_close = client.process(None, now).dgram();
+ assert!(client_close.is_some());
+ // This should now report the end of the closing period, not a
+ // zero-duration wait driven by the (now defunct) loss recovery timer.
+ let client_close_timer = client.process(None, now).callback();
+ assert_ne!(client_close_timer, Duration::from_secs(0));
+}
+
+#[test]
+fn closing_and_draining() {
+ const APP_ERROR: AppError = 7;
+ let mut client = default_client();
+ let mut server = default_server();
+ connect(&mut client, &mut server);
+
+ // Save a packet from the client for later.
+ let p1 = send_something(&mut client, now());
+
+ // Close the connection.
+ client.close(now(), APP_ERROR, "");
+ let client_close = client.process(None, now()).dgram();
+ assert!(client_close.is_some());
+ let client_close_timer = client.process(None, now()).callback();
+ assert_ne!(client_close_timer, Duration::from_secs(0));
+
+ // The client will spit out the same packet in response to anything it receives.
+ let p3 = send_something(&mut server, now());
+ let client_close2 = client.process(Some(&p3), now()).dgram();
+ assert_eq!(
+ client_close.as_ref().unwrap().len(),
+ client_close2.as_ref().unwrap().len()
+ );
+
+ // After this time, the client should transition to closed.
+ let end = client.process(None, now() + client_close_timer);
+ assert_eq!(end, Output::None);
+ assert_eq!(
+ *client.state(),
+ State::Closed(ConnectionError::Application(APP_ERROR))
+ );
+
+ // When the server receives the close, it too should generate CONNECTION_CLOSE.
+ let server_close = server.process(client_close.as_ref(), now()).dgram();
+ assert!(server.state().closed());
+ assert!(server_close.is_some());
+ // .. but it ignores any further close packets.
+ let server_close_timer = server.process(client_close2.as_ref(), now()).callback();
+ assert_ne!(server_close_timer, Duration::from_secs(0));
+ // Even a legitimate packet without a close in it.
+ let server_close_timer2 = server.process(Some(&p1), now()).callback();
+ assert_eq!(server_close_timer, server_close_timer2);
+
+ let end = server.process(None, now() + server_close_timer);
+ assert_eq!(end, Output::None);
+ assert_eq!(
+ *server.state(),
+ State::Closed(ConnectionError::Transport(Error::PeerApplicationError(
+ APP_ERROR
+ )))
+ );
+}
+
+/// Test that a client can handle a stateless reset correctly.
+#[test]
+fn stateless_reset_client() {
+ let mut client = default_client();
+ let mut server = default_server();
+ server
+ .set_local_tparam(
+ tparams::STATELESS_RESET_TOKEN,
+ TransportParameter::Bytes(vec![77; 16]),
+ )
+ .unwrap();
+ connect_force_idle(&mut client, &mut server);
+
+ client.process_input(&datagram(vec![77; 21]), now());
+ assert_draining(&client, &Error::StatelessReset);
+}