1 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
2 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
3 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
4 // option. This file may not be copied, modified, or distributed
5 // except according to those terms.
6
7 use super::super::{IdleTimeout, Output, State, LOCAL_IDLE_TIMEOUT};
8 use super::{
9 connect, connect_force_idle, connect_with_rtt, default_client, default_server,
10 maybe_authenticate, send_and_receive, send_something, AT_LEAST_PTO,
11 };
12 use crate::packet::PacketBuilder;
13 use crate::stats::FrameStats;
14 use crate::tparams::{self, TransportParameter};
15 use crate::tracking::PacketNumberSpace;
16 use crate::StreamType;
17
18 use neqo_common::Encoder;
19 use std::mem;
20 use std::time::Duration;
21 use test_fixture::{self, now, split_datagram};
22
23 #[test]
idle_timeout()24 fn idle_timeout() {
25 let mut client = default_client();
26 let mut server = default_server();
27 connect_force_idle(&mut client, &mut server);
28
29 let now = now();
30
31 let res = client.process(None, now);
32 assert_eq!(res, Output::Callback(LOCAL_IDLE_TIMEOUT));
33
34 // Still connected after 29 seconds. Idle timer not reset
35 mem::drop(client.process(None, now + LOCAL_IDLE_TIMEOUT - Duration::from_secs(1)));
36 assert!(matches!(client.state(), State::Confirmed));
37
38 mem::drop(client.process(None, now + LOCAL_IDLE_TIMEOUT));
39
40 // Not connected after LOCAL_IDLE_TIMEOUT seconds.
41 assert!(matches!(client.state(), State::Closed(_)));
42 }
43
44 #[test]
asymmetric_idle_timeout()45 fn asymmetric_idle_timeout() {
46 const LOWER_TIMEOUT_MS: u64 = 1000;
47 const LOWER_TIMEOUT: Duration = Duration::from_millis(LOWER_TIMEOUT_MS);
48 // Sanity check the constant.
49 assert!(LOWER_TIMEOUT < LOCAL_IDLE_TIMEOUT);
50
51 let mut client = default_client();
52 let mut server = default_server();
53
54 // Overwrite the default at the server.
55 server
56 .tps
57 .borrow_mut()
58 .local
59 .set_integer(tparams::IDLE_TIMEOUT, LOWER_TIMEOUT_MS);
60 server.idle_timeout = IdleTimeout::new(LOWER_TIMEOUT);
61
62 // Now connect and force idleness manually.
63 // We do that by following what `force_idle` does and have each endpoint
64 // send two packets, which are delivered out of order. See `force_idle`.
65 connect(&mut client, &mut server);
66 let c1 = send_something(&mut client, now());
67 let c2 = send_something(&mut client, now());
68 server.process_input(c2, now());
69 server.process_input(c1, now());
70 let s1 = send_something(&mut server, now());
71 let s2 = send_something(&mut server, now());
72 client.process_input(s2, now());
73 let ack = client.process(Some(s1), now()).dgram();
74 assert!(ack.is_some());
75 // Now both should have received ACK frames so should be idle.
76 assert_eq!(server.process(ack, now()), Output::Callback(LOWER_TIMEOUT));
77 assert_eq!(client.process(None, now()), Output::Callback(LOWER_TIMEOUT));
78 }
79
80 #[test]
tiny_idle_timeout()81 fn tiny_idle_timeout() {
82 const RTT: Duration = Duration::from_millis(500);
83 const LOWER_TIMEOUT_MS: u64 = 100;
84 const LOWER_TIMEOUT: Duration = Duration::from_millis(LOWER_TIMEOUT_MS);
85 // We won't respect a value that is lower than 3*PTO, sanity check.
86 assert!(LOWER_TIMEOUT < 3 * RTT);
87
88 let mut client = default_client();
89 let mut server = default_server();
90
91 // Overwrite the default at the server.
92 server
93 .set_local_tparam(
94 tparams::IDLE_TIMEOUT,
95 TransportParameter::Integer(LOWER_TIMEOUT_MS),
96 )
97 .unwrap();
98 server.idle_timeout = IdleTimeout::new(LOWER_TIMEOUT);
99
100 // Now connect with an RTT and force idleness manually.
101 let mut now = connect_with_rtt(&mut client, &mut server, now(), RTT);
102 let c1 = send_something(&mut client, now);
103 let c2 = send_something(&mut client, now);
104 now += RTT / 2;
105 server.process_input(c2, now);
106 server.process_input(c1, now);
107 let s1 = send_something(&mut server, now);
108 let s2 = send_something(&mut server, now);
109 now += RTT / 2;
110 client.process_input(s2, now);
111 let ack = client.process(Some(s1), now).dgram();
112 assert!(ack.is_some());
113
114 // The client should be idle now, but with a different timer.
115 if let Output::Callback(t) = client.process(None, now) {
116 assert!(t > LOWER_TIMEOUT);
117 } else {
118 panic!("Client not idle");
119 }
120
121 // The server should go idle after the ACK, but again with a larger timeout.
122 now += RTT / 2;
123 if let Output::Callback(t) = client.process(ack, now) {
124 assert!(t > LOWER_TIMEOUT);
125 } else {
126 panic!("Client not idle");
127 }
128 }
129
130 #[test]
idle_send_packet1()131 fn idle_send_packet1() {
132 const DELTA: Duration = Duration::from_millis(10);
133
134 let mut client = default_client();
135 let mut server = default_server();
136 let mut now = now();
137 connect_force_idle(&mut client, &mut server);
138
139 let timeout = client.process(None, now).callback();
140 assert_eq!(timeout, LOCAL_IDLE_TIMEOUT);
141
142 now += Duration::from_secs(10);
143 let dgram = send_and_receive(&mut client, &mut server, now);
144 assert!(dgram.is_none());
145
146 // Still connected after 39 seconds because idle timer reset by the
147 // outgoing packet.
148 now += LOCAL_IDLE_TIMEOUT - DELTA;
149 let dgram = client.process(None, now).dgram();
150 assert!(dgram.is_some()); // PTO
151 assert!(client.state().connected());
152
153 // Not connected after 40 seconds.
154 now += DELTA;
155 let out = client.process(None, now);
156 assert!(matches!(out, Output::None));
157 assert!(client.state().closed());
158 }
159
160 #[test]
idle_send_packet2()161 fn idle_send_packet2() {
162 const GAP: Duration = Duration::from_secs(10);
163 const DELTA: Duration = Duration::from_millis(10);
164
165 let mut client = default_client();
166 let mut server = default_server();
167 connect_force_idle(&mut client, &mut server);
168
169 let mut now = now();
170
171 let timeout = client.process(None, now).callback();
172 assert_eq!(timeout, LOCAL_IDLE_TIMEOUT);
173
174 // First transmission at t=GAP.
175 now += GAP;
176 mem::drop(send_something(&mut client, now));
177
178 // Second transmission at t=2*GAP.
179 mem::drop(send_something(&mut client, now + GAP));
180 assert!((GAP * 2 + DELTA) < LOCAL_IDLE_TIMEOUT);
181
182 // Still connected just before GAP + LOCAL_IDLE_TIMEOUT.
183 now += LOCAL_IDLE_TIMEOUT - DELTA;
184 let dgram = client.process(None, now).dgram();
185 assert!(dgram.is_some()); // PTO
186 assert!(matches!(client.state(), State::Confirmed));
187
188 // Not connected after 40 seconds because timer not reset by second
189 // outgoing packet
190 now += DELTA;
191 let out = client.process(None, now);
192 assert!(matches!(out, Output::None));
193 assert!(matches!(client.state(), State::Closed(_)));
194 }
195
196 #[test]
idle_recv_packet()197 fn idle_recv_packet() {
198 let mut client = default_client();
199 let mut server = default_server();
200 connect_force_idle(&mut client, &mut server);
201
202 let now = now();
203
204 let res = client.process(None, now);
205 assert_eq!(res, Output::Callback(LOCAL_IDLE_TIMEOUT));
206
207 assert_eq!(client.stream_create(StreamType::BiDi).unwrap(), 0);
208 assert_eq!(client.stream_send(0, b"hello").unwrap(), 5);
209
210 // Respond with another packet
211 let out = client.process(None, now + Duration::from_secs(10));
212 server.process_input(out.dgram().unwrap(), now + Duration::from_secs(10));
213 assert_eq!(server.stream_send(0, b"world").unwrap(), 5);
214 let out = server.process_output(now + Duration::from_secs(10));
215 assert_ne!(out.as_dgram_ref(), None);
216
217 mem::drop(client.process(out.dgram(), now + Duration::from_secs(20)));
218 assert!(matches!(client.state(), State::Confirmed));
219
220 // Still connected after 49 seconds because idle timer reset by received
221 // packet
222 mem::drop(client.process(None, now + LOCAL_IDLE_TIMEOUT + Duration::from_secs(19)));
223 assert!(matches!(client.state(), State::Confirmed));
224
225 // Not connected after 50 seconds.
226 mem::drop(client.process(None, now + LOCAL_IDLE_TIMEOUT + Duration::from_secs(20)));
227
228 assert!(matches!(client.state(), State::Closed(_)));
229 }
230
231 /// Caching packets should not cause the connection to become idle.
232 /// This requires a few tricks to keep the connection from going
233 /// idle while preventing any progress on the handshake.
234 #[test]
idle_caching()235 fn idle_caching() {
236 let mut client = default_client();
237 let mut server = default_server();
238 let start = now();
239 let mut builder = PacketBuilder::short(Encoder::new(), false, &[]);
240
241 // Perform the first round trip, but drop the Initial from the server.
242 // The client then caches the Handshake packet.
243 let dgram = client.process_output(start).dgram();
244 let dgram = server.process(dgram, start).dgram();
245 let (_, handshake) = split_datagram(&dgram.unwrap());
246 client.process_input(handshake.unwrap(), start);
247
248 // Perform an exchange and keep the connection alive.
249 // Only allow a packet containing a PING to pass.
250 let middle = start + AT_LEAST_PTO;
251 mem::drop(client.process_output(middle));
252 let dgram = client.process_output(middle).dgram();
253
254 // Get the server to send its first probe and throw that away.
255 mem::drop(server.process_output(middle).dgram());
256 // Now let the server process the client PING. This causes the server
257 // to send CRYPTO frames again, so manually extract and discard those.
258 let ping_before_s = server.stats().frame_rx.ping;
259 server.process_input(dgram.unwrap(), middle);
260 assert_eq!(server.stats().frame_rx.ping, ping_before_s + 1);
261 let mut tokens = Vec::new();
262 server
263 .crypto
264 .streams
265 .write_frame(
266 PacketNumberSpace::Initial,
267 &mut builder,
268 &mut tokens,
269 &mut FrameStats::default(),
270 )
271 .unwrap();
272 assert_eq!(tokens.len(), 1);
273 tokens.clear();
274 server
275 .crypto
276 .streams
277 .write_frame(
278 PacketNumberSpace::Initial,
279 &mut builder,
280 &mut tokens,
281 &mut FrameStats::default(),
282 )
283 .unwrap();
284 assert!(tokens.is_empty());
285 let dgram = server.process_output(middle).dgram();
286
287 // Now only allow the Initial packet from the server through;
288 // it shouldn't contain a CRYPTO frame.
289 let (initial, _) = split_datagram(&dgram.unwrap());
290 let ping_before_c = client.stats().frame_rx.ping;
291 let ack_before = client.stats().frame_rx.ack;
292 client.process_input(initial, middle);
293 assert_eq!(client.stats().frame_rx.ping, ping_before_c + 1);
294 assert_eq!(client.stats().frame_rx.ack, ack_before + 1);
295
296 let end = start + LOCAL_IDLE_TIMEOUT + (AT_LEAST_PTO / 2);
297 // Now let the server Initial through, with the CRYPTO frame.
298 let dgram = server.process_output(end).dgram();
299 let (initial, _) = split_datagram(&dgram.unwrap());
300 neqo_common::qwarn!("client ingests initial, finally");
301 mem::drop(client.process(Some(initial), end));
302 maybe_authenticate(&mut client);
303 let dgram = client.process_output(end).dgram();
304 let dgram = server.process(dgram, end).dgram();
305 client.process_input(dgram.unwrap(), end);
306 assert_eq!(*client.state(), State::Confirmed);
307 assert_eq!(*server.state(), State::Confirmed);
308 }
309