Skip to main content

s2n_quic_dc/stream/
shared.rs

1// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2// SPDX-License-Identifier: Apache-2.0
3
4use crate::{
5    clock::Clock,
6    credentials::Credentials,
7    event::{self, IntoEvent as _},
8    packet::stream,
9    path::secret::map::ApplicationData,
10    stream::{
11        recv::shared as recv,
12        send::{application, shared as send},
13        tls::S2nTlsConnection,
14    },
15};
16use core::{
17    cell::UnsafeCell,
18    ops,
19    sync::atomic::{AtomicU64, AtomicU8, Ordering},
20    time::Duration,
21};
22use s2n_quic_core::{
23    ensure,
24    inet::{IpAddress, SocketAddress},
25    time::Timestamp,
26    varint::VarInt,
27};
28use s2n_quic_platform::features;
29use std::sync::{atomic::AtomicU16, Arc};
30
31pub mod handshake;
32
33pub use crate::stream::crypto::Crypto;
34
35#[derive(Clone, Copy, Debug)]
36pub enum Half {
37    Read,
38    Write,
39}
40
41#[derive(Debug, Clone, Copy)]
42pub enum ShutdownKind {
43    Normal,
44    Panicking,
45    Pruned,
46}
47
48impl ShutdownKind {
49    pub const PRUNED_CODE: u8 = 0x02;
50
51    pub fn error_code(&self) -> Option<u8> {
52        match self {
53            ShutdownKind::Normal => None,
54            ShutdownKind::Panicking => Some(0x01),
55            ShutdownKind::Pruned => Some(Self::PRUNED_CODE),
56        }
57    }
58}
59
60/// The state of whether the stream has been accepted by the application
61#[derive(Debug, Clone, Copy, PartialEq, Eq)]
62pub enum AcceptState {
63    Waiting,
64    Accepted,
65}
66
67pub type ArcShared<Sub> = Arc<Shared<Sub, dyn Clock>>;
68
69pub struct Shared<Subscriber, Clk>
70where
71    Subscriber: event::Subscriber,
72    Clk: ?Sized + Clock,
73{
74    pub receiver: recv::State,
75    pub sender: send::State,
76    pub crypto: Crypto,
77    pub application_data: Option<ApplicationData>,
78    pub common: Common<Subscriber, Clk>,
79}
80
81impl<Sub, C> Shared<Sub, C>
82where
83    Sub: event::Subscriber,
84    C: Clock + ?Sized,
85{
86    #[inline]
87    pub fn on_valid_packet(
88        &self,
89        remote_addr: &SocketAddress,
90        remote_queue_id: Option<VarInt>,
91        handshake: &mut handshake::State,
92    ) {
93        match handshake {
94            handshake::State::ClientQueueIdObserved => {
95                // stop sending our `queue_id` since the server has observed it
96                self.local_queue_id.store(u64::MAX, Ordering::Relaxed);
97
98                // allow the server to pick a different port on the first response
99                self.remote_port
100                    .store(remote_addr.port(), Ordering::Relaxed);
101
102                // transition to steady state once the server provided its chosen `queue_id`
103                if let Some(server_queue_id) = remote_queue_id {
104                    // only accept queue_ids that can be encoded in a stream Id
105                    if stream::Id::normal(server_queue_id).is_some() {
106                        self.remote_queue_id
107                            .store(server_queue_id.as_u64(), Ordering::Relaxed);
108
109                        let _ = handshake.on_observation_finished();
110                    } else {
111                        use event::ConnectionPublisher as _;
112                        self.common.publisher().on_stream_handshake_packet_rejected(
113                            event::builder::StreamHandshakePacketRejected {
114                                reason: event::builder::StreamHandshakePacketRejectedReason::InvalidQueueId,
115                            },
116                        );
117                    }
118                }
119            }
120            handshake::State::ServerQueueIdObserved => {
121                // stop sending our `queue_id` since the client has observed it
122                self.local_queue_id.store(u64::MAX, Ordering::Relaxed);
123
124                // no need to update the remote_queue_id value since we saw it on the first packet
125                let _ = handshake.on_observation_finished();
126            }
127            _ => {}
128        }
129
130        // update the last time we've seen peer activity
131        self.on_peer_activity();
132    }
133
134    #[inline]
135    pub fn on_peer_activity(&self) {
136        self.last_peer_activity.fetch_max(
137            unsafe { self.clock.get_time().as_duration().as_micros() as _ },
138            Ordering::Relaxed,
139        );
140    }
141}
142
143impl<Sub, C> Shared<Sub, C>
144where
145    Sub: event::Subscriber,
146    C: ?Sized + Clock,
147{
148    #[inline]
149    pub fn last_peer_activity(&self) -> Timestamp {
150        let timestamp = self.last_peer_activity.load(Ordering::Relaxed);
151        let timestamp = Duration::from_micros(timestamp);
152        unsafe { Timestamp::from_duration(timestamp) }
153    }
154
155    #[inline]
156    pub fn stream_id(&self) -> stream::Id {
157        let queue_id = self.remote_queue_id.load(Ordering::Relaxed);
158        // TODO support alternative modes
159        stream::Id::normal(unsafe { VarInt::new_unchecked(queue_id) })
160            .expect("queue_id exceeds encoding limit")
161    }
162
163    #[inline]
164    pub fn local_queue_id(&self) -> Option<VarInt> {
165        let queue_id = self.local_queue_id.load(Ordering::Relaxed);
166        VarInt::new(queue_id).ok()
167    }
168
169    #[inline]
170    pub fn remote_addr(&self) -> SocketAddress {
171        unsafe {
172            // SAFETY: the fixed information doesn't change for the lifetime of the stream
173            *self.common.fixed.remote_ip.get()
174        }
175        .with_port(self.remote_port.load(Ordering::Relaxed))
176    }
177
178    #[inline]
179    pub fn application(&self) -> application::state::State {
180        unsafe {
181            // SAFETY: the fixed information doesn't change for the lifetime of the stream
182            *self.common.fixed.application.get()
183        }
184    }
185
186    #[inline]
187    pub fn credentials(&self) -> &Credentials {
188        unsafe {
189            // SAFETY: the fixed information doesn't change for the lifetime of the stream
190            &*self.common.fixed.credentials.get()
191        }
192    }
193
194    #[inline]
195    pub fn application_data(&self) -> Option<&ApplicationData> {
196        self.application_data.as_ref()
197    }
198}
199
200impl<Sub, C> ops::Deref for Shared<Sub, C>
201where
202    Sub: event::Subscriber,
203    C: ?Sized + Clock,
204{
205    type Target = Common<Sub, C>;
206
207    #[inline]
208    fn deref(&self) -> &Self::Target {
209        &self.common
210    }
211}
212
213pub struct Common<Sub, Clk>
214where
215    Sub: event::Subscriber,
216    Clk: ?Sized + Clock,
217{
218    pub gso: features::Gso,
219    pub(super) remote_port: AtomicU16,
220    pub(super) local_queue_id: AtomicU64,
221    pub(super) remote_queue_id: AtomicU64,
222    pub fixed: FixedValues,
223    /// The last time we received a packet from the peer
224    pub last_peer_activity: AtomicU64,
225    pub closed_halves: AtomicU8,
226    pub subscriber: Subscriber<Sub>,
227    pub s2n_connection: Option<S2nTlsConnection>,
228    pub clock: Clk,
229}
230
231impl<Sub, Clk> Common<Sub, Clk>
232where
233    Sub: event::Subscriber,
234    Clk: ?Sized + Clock,
235{
236    #[inline]
237    pub fn ensure_open(&self) -> std::io::Result<()> {
238        ensure!(
239            self.closed_halves.load(Ordering::Relaxed) < 2,
240            // macos returns a different error kind
241            Err(if cfg!(target_os = "macos") {
242                std::io::ErrorKind::InvalidInput
243            } else {
244                std::io::ErrorKind::NotConnected
245            }
246            .into())
247        );
248        Ok(())
249    }
250
251    #[inline]
252    pub fn publisher(&self) -> event::ConnectionPublisherSubscriber<'_, Sub> {
253        self.publisher_with_timestamp(self.clock.get_time())
254    }
255
256    #[inline]
257    pub fn publisher_with_timestamp(
258        &self,
259        timestamp: Timestamp,
260    ) -> event::ConnectionPublisherSubscriber<'_, Sub> {
261        self.subscriber.publisher(timestamp)
262    }
263
264    #[inline]
265    pub fn endpoint_publisher(
266        &self,
267        timestamp: Timestamp,
268    ) -> event::EndpointPublisherSubscriber<'_, Sub> {
269        self.subscriber.endpoint_publisher(timestamp)
270    }
271}
272
273pub struct Subscriber<Sub>
274where
275    Sub: event::Subscriber,
276{
277    pub subscriber: Sub,
278    pub context: Sub::ConnectionContext,
279}
280
281impl<Sub> Subscriber<Sub>
282where
283    Sub: event::Subscriber,
284{
285    #[inline]
286    pub fn publisher(&self, timestamp: Timestamp) -> event::ConnectionPublisherSubscriber<'_, Sub> {
287        event::ConnectionPublisherSubscriber::new(
288            event::builder::ConnectionMeta {
289                id: 0, // TODO
290                timestamp: timestamp.into_event(),
291            },
292            0,
293            &self.subscriber,
294            &self.context,
295        )
296    }
297
298    #[inline]
299    pub fn endpoint_publisher(
300        &self,
301        timestamp: Timestamp,
302    ) -> event::EndpointPublisherSubscriber<'_, Sub> {
303        event::EndpointPublisherSubscriber::new(
304            event::builder::EndpointMeta {
305                timestamp: timestamp.into_event(),
306            },
307            None,
308            &self.subscriber,
309        )
310    }
311}
312
313impl<Sub, Clk> Drop for Common<Sub, Clk>
314where
315    Sub: event::Subscriber,
316    Clk: ?Sized + Clock,
317{
318    #[inline]
319    fn drop(&mut self) {
320        use event::ConnectionPublisher as _;
321
322        self.publisher()
323            .on_connection_closed(event::builder::ConnectionClosed {});
324    }
325}
326
327/// Values that don't change while the state is shared between threads
328pub struct FixedValues {
329    pub remote_ip: UnsafeCell<IpAddress>,
330    pub application: UnsafeCell<application::state::State>,
331    pub credentials: UnsafeCell<Credentials>,
332}
333
334unsafe impl Send for FixedValues {}
335unsafe impl Sync for FixedValues {}