Skip to content

Commit 7f9c190

Browse files
committed
feat: add event and function to wait for resumption tickets
1 parent 0699545 commit 7f9c190

File tree

9 files changed

+204
-5
lines changed

9 files changed

+204
-5
lines changed

Cargo.lock

Lines changed: 1 addition & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,3 +63,6 @@ debug = true
6363

6464
[profile.release]
6565
debug = true
66+
67+
[patch.crates-io]
68+
rustls = { git = "https://github.com/rustls/rustls", branch = "main" }

perf/src/noprotection.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,10 @@ impl crypto::Session for NoProtectionSession {
127127
) -> Result<(), crypto::ExportKeyingMaterialError> {
128128
self.inner.export_keying_material(output, label, context)
129129
}
130+
131+
fn resumption_tickets_received(&self) -> Option<u32> {
132+
self.inner.resumption_tickets_received()
133+
}
130134
}
131135

132136
impl crypto::ClientConfig for NoProtectionClientConfig {

quinn-proto/src/connection/mod.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,8 @@ pub struct Connection {
233233
stats: ConnectionStats,
234234
/// QUIC version used for the connection.
235235
version: u32,
236+
/// True if we emitted the event for received resumption tickets
237+
resumption_tickets_received: bool,
236238
}
237239

238240
impl Connection {
@@ -350,6 +352,7 @@ impl Connection {
350352
rng,
351353
stats: ConnectionStats::default(),
352354
version,
355+
resumption_tickets_received: false,
353356
};
354357
if path_validated {
355358
this.on_path_validated();
@@ -2035,6 +2038,12 @@ impl Connection {
20352038
}
20362039
}
20372040

2041+
if !self.resumption_tickets_received && self.crypto.resumption_tickets_received() > Some(0)
2042+
{
2043+
self.resumption_tickets_received = true;
2044+
self.events.push_back(Event::ResumptionTicketsReceived)
2045+
}
2046+
20382047
Ok(())
20392048
}
20402049

@@ -3920,6 +3929,10 @@ pub enum Event {
39203929
DatagramReceived,
39213930
/// One or more application datagrams have been sent after blocking
39223931
DatagramsUnblocked,
3932+
/// One or more TLS session resumption tickets have been received
3933+
///
3934+
/// This event is only emitted on the client, and is emitted at most once per connection.
3935+
ResumptionTicketsReceived,
39233936
}
39243937

39253938
fn instant_saturating_sub(x: Instant, y: Instant) -> Duration {

quinn-proto/src/crypto.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,11 @@ pub trait Session: Send + Sync + 'static {
9191
label: &[u8],
9292
context: &[u8],
9393
) -> Result<(), ExportKeyingMaterialError>;
94+
95+
/// Returns the number of TLS1.3 session resumption tickets that were received
96+
///
97+
/// Returns `None` on the server side.
98+
fn resumption_tickets_received(&self) -> Option<u32>;
9499
}
95100

96101
/// A pair of keys for bidirectional communication

quinn-proto/src/crypto/rustls.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,13 @@ impl crypto::Session for TlsSession {
199199
.map_err(|_| ExportKeyingMaterialError)?;
200200
Ok(())
201201
}
202+
203+
fn resumption_tickets_received(&self) -> Option<u32> {
204+
match &self.inner {
205+
Connection::Client(conn) => Some(conn.tls13_tickets_received()),
206+
Connection::Server(_) => None,
207+
}
208+
}
202209
}
203210

204211
const RETRY_INTEGRITY_KEY_DRAFT: [u8; 16] = [

quinn-proto/src/tests/mod.rs

Lines changed: 52 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,10 @@ fn lifecycle() {
128128
let _guard = subscribe();
129129
let mut pair = Pair::default();
130130
let (client_ch, server_ch) = pair.connect();
131+
assert_matches!(
132+
pair.client_conn_mut(client_ch).poll(),
133+
Some(Event::ResumptionTicketsReceived)
134+
);
131135
assert_matches!(pair.client_conn_mut(client_ch).poll(), None);
132136
assert!(pair.client_conn_mut(client_ch).using_ecn());
133137
assert!(pair.server_conn_mut(server_ch).using_ecn());
@@ -161,6 +165,10 @@ fn draft_version_compat() {
161165
let mut pair = Pair::default();
162166
let (client_ch, server_ch) = pair.connect_with(client_config);
163167

168+
assert_matches!(
169+
pair.client_conn_mut(client_ch).poll(),
170+
Some(Event::ResumptionTicketsReceived)
171+
);
164172
assert_matches!(pair.client_conn_mut(client_ch).poll(), None);
165173
assert!(pair.client_conn_mut(client_ch).using_ecn());
166174
assert!(pair.server_conn_mut(server_ch).using_ecn());
@@ -206,6 +214,10 @@ fn server_stateless_reset() {
206214
pair.client.connections.get_mut(&client_ch).unwrap().ping();
207215
info!("resetting");
208216
pair.drive();
217+
assert_matches!(
218+
pair.client_conn_mut(client_ch).poll(),
219+
Some(Event::ResumptionTicketsReceived)
220+
);
209221
assert_matches!(
210222
pair.client_conn_mut(client_ch).poll(),
211223
Some(Event::ConnectionLost {
@@ -327,6 +339,10 @@ fn finish_stream_simple() {
327339
pair.client_send(client_ch, s).finish().unwrap();
328340
pair.drive();
329341

342+
assert_matches!(
343+
pair.client_conn_mut(client_ch).poll(),
344+
Some(Event::ResumptionTicketsReceived)
345+
);
330346
assert_matches!(
331347
pair.client_conn_mut(client_ch).poll(),
332348
Some(Event::Stream(StreamEvent::Finished { id })) if id == s
@@ -379,6 +395,10 @@ fn reset_stream() {
379395
let mut chunks = recv.read(false).unwrap();
380396
assert_matches!(chunks.next(usize::MAX), Err(ReadError::Reset(ERROR)));
381397
let _ = chunks.finalize();
398+
assert_matches!(
399+
pair.client_conn_mut(client_ch).poll(),
400+
Some(Event::ResumptionTicketsReceived)
401+
);
382402
assert_matches!(pair.client_conn_mut(client_ch).poll(), None);
383403
}
384404

@@ -596,7 +616,10 @@ fn zero_rtt_happypath() {
596616
Ok(Some(chunk)) if chunk.offset == 0 && chunk.bytes == MSG
597617
);
598618
let _ = chunks.finalize();
599-
assert_eq!(pair.client_conn_mut(client_ch).lost_packets(), 0);
619+
assert_matches!(
620+
pair.client_conn_mut(client_ch).poll(),
621+
Some(Event::ResumptionTicketsReceived)
622+
);
600623
}
601624

602625
#[test]
@@ -905,6 +928,10 @@ fn stream_id_limit() {
905928
pair.client_send(client_ch, s).write(MSG).unwrap();
906929
pair.client_send(client_ch, s).finish().unwrap();
907930
pair.drive();
931+
assert_matches!(
932+
pair.client_conn_mut(client_ch).poll(),
933+
Some(Event::ResumptionTicketsReceived)
934+
);
908935
assert_matches!(
909936
pair.client_conn_mut(client_ch).poll(),
910937
Some(Event::Stream(StreamEvent::Finished { id })) if id == s
@@ -1192,6 +1219,10 @@ fn idle_timeout() {
11921219
}
11931220

11941221
assert!(pair.time - start < Duration::from_millis(2 * IDLE_TIMEOUT));
1222+
assert_matches!(
1223+
pair.client_conn_mut(client_ch).poll(),
1224+
Some(Event::ResumptionTicketsReceived)
1225+
);
11951226
assert_matches!(
11961227
pair.client_conn_mut(client_ch).poll(),
11971228
Some(Event::ConnectionLost {
@@ -1271,6 +1302,10 @@ fn migration() {
12711302
assert_ne!(pair.server_conn_mut(server_ch).total_recvd(), 0);
12721303

12731304
pair.drive();
1305+
assert_matches!(
1306+
pair.client_conn_mut(client_ch).poll(),
1307+
Some(Event::ResumptionTicketsReceived)
1308+
);
12741309
assert_matches!(pair.client_conn_mut(client_ch).poll(), None);
12751310
assert_eq!(
12761311
pair.server_conn_mut(server_ch).remote_address(),
@@ -1657,6 +1692,10 @@ fn finish_stream_flow_control_reordered() {
16571692
pair.server.finish_delay(); // Add flow control packets after
16581693
pair.drive();
16591694

1695+
assert_matches!(
1696+
pair.client_conn_mut(client_ch).poll(),
1697+
Some(Event::ResumptionTicketsReceived)
1698+
);
16601699
assert_matches!(
16611700
pair.client_conn_mut(client_ch).poll(),
16621701
Some(Event::Stream(StreamEvent::Finished { id })) if id == s
@@ -1749,6 +1788,10 @@ fn stop_during_finish() {
17491788
pair.drive_server();
17501789
pair.client_send(client_ch, s).finish().unwrap();
17511790
pair.drive_client();
1791+
assert_matches!(
1792+
pair.client_conn_mut(client_ch).poll(),
1793+
Some(Event::ResumptionTicketsReceived)
1794+
);
17521795
assert_matches!(
17531796
pair.client_conn_mut(client_ch).poll(),
17541797
Some(Event::Stream(StreamEvent::Stopped { id, error_code: ERROR })) if id == s
@@ -2036,6 +2079,10 @@ fn finish_acked() {
20362079
// Send FIN, receive data ack
20372080
info!("client receives ACK, sends FIN");
20382081
pair.drive_client();
2082+
assert_matches!(
2083+
pair.client_conn_mut(client_ch).poll(),
2084+
Some(Event::ResumptionTicketsReceived)
2085+
);
20392086
// Check for premature finish from data ack
20402087
assert_matches!(pair.client_conn_mut(client_ch).poll(), None);
20412088
// Process FIN ack
@@ -2074,6 +2121,10 @@ fn finish_retransmit() {
20742121
// Receive FIN ack, but no data ack
20752122
pair.drive_client();
20762123
// Check for premature finish from FIN ack
2124+
assert_matches!(
2125+
pair.client_conn_mut(client_ch).poll(),
2126+
Some(Event::ResumptionTicketsReceived)
2127+
);
20772128
assert_matches!(pair.client_conn_mut(client_ch).poll(), None);
20782129
// Recover
20792130
pair.drive();

quinn/src/connection.rs

Lines changed: 54 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,8 @@ use crate::{
2525
udp_transmit,
2626
};
2727
use proto::{
28-
ConnectionError, ConnectionHandle, ConnectionStats, Dir, EndpointEvent, StreamEvent, StreamId,
29-
congestion::Controller,
28+
ConnectionError, ConnectionHandle, ConnectionStats, Dir, EndpointEvent, Side, StreamEvent,
29+
StreamId, congestion::Controller,
3030
};
3131

3232
/// In-progress connection attempt future
@@ -636,6 +636,37 @@ impl Connection {
636636
// May need to send MAX_STREAMS to make progress
637637
conn.wake();
638638
}
639+
640+
/// Waits until the connection received TLS resumption tickets
641+
///
642+
/// Yields `true` once resumption tickets were received. Resolves immediately
643+
/// if tickets were already received, otherwise it resolves once tickets arrive.
644+
/// If the server does not send any tickets, the returned future will remain pending forever.
645+
///
646+
/// This should only be used on the client side. On the server side, it will
647+
/// always resolve immediately and yield `false`.
648+
pub fn resumption_tickets_received(&self) -> impl Future<Output = bool> + Send + 'static {
649+
let conn = self.0.clone();
650+
async move {
651+
let notify;
652+
let (mut notified, out) = {
653+
let conn = conn.state.lock("resumption_tickets_received");
654+
let (notified, out) = match conn.resumption_tickets.as_ref() {
655+
Some(ResumptionTicketState::Received) => (None, true),
656+
Some(ResumptionTicketState::Pending(n)) => {
657+
notify = n.clone();
658+
(Some(notify.notified()), true)
659+
}
660+
None => (None, false),
661+
};
662+
(notified, out)
663+
};
664+
if let Some(notified) = notified.take() {
665+
notified.await;
666+
}
667+
out
668+
}
669+
}
639670
}
640671

641672
pin_project! {
@@ -870,6 +901,10 @@ impl ConnectionRef {
870901
socket: Arc<dyn AsyncUdpSocket>,
871902
runtime: Arc<dyn Runtime>,
872903
) -> Self {
904+
let resumption_tickets = match conn.side() {
905+
Side::Client => Some(ResumptionTicketState::Pending(Default::default())),
906+
Side::Server => None,
907+
};
873908
Self(Arc::new(ConnectionInner {
874909
state: Mutex::new(State {
875910
inner: conn,
@@ -892,6 +927,7 @@ impl ConnectionRef {
892927
runtime,
893928
send_buffer: Vec::new(),
894929
buffered_transmit: None,
930+
resumption_tickets,
895931
}),
896932
shared: Shared::default(),
897933
}))
@@ -974,6 +1010,8 @@ pub(crate) struct State {
9741010
send_buffer: Vec<u8>,
9751011
/// We buffer a transmit when the underlying I/O would block
9761012
buffered_transmit: Option<proto::Transmit>,
1013+
/// Whether we received resumption tickets. None on the server side.
1014+
resumption_tickets: Option<ResumptionTicketState>,
9771015
}
9781016

9791017
impl State {
@@ -1108,6 +1146,14 @@ impl State {
11081146
wake_all_notify(&mut self.stopped);
11091147
}
11101148
}
1149+
ResumptionTicketsReceived => {
1150+
if let Some(ResumptionTicketState::Pending(notify)) =
1151+
self.resumption_tickets.as_mut()
1152+
{
1153+
notify.notify_waiters();
1154+
self.resumption_tickets = Some(ResumptionTicketState::Received);
1155+
}
1156+
}
11111157
ConnectionLost { reason } => {
11121158
self.terminate(reason, shared);
11131159
}
@@ -1278,6 +1324,12 @@ fn wake_all_notify(wakers: &mut FxHashMap<StreamId, Arc<Notify>>) {
12781324
.for_each(|(_, notify)| notify.notify_waiters())
12791325
}
12801326

1327+
#[derive(Debug)]
1328+
enum ResumptionTicketState {
1329+
Received,
1330+
Pending(Arc<Notify>),
1331+
}
1332+
12811333
/// Errors that can arise when sending a datagram
12821334
#[derive(Debug, Error, Clone, Eq, PartialEq)]
12831335
pub enum SendDatagramError {

0 commit comments

Comments
 (0)