s2n_quic/client/
providers.rs

1// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2// SPDX-License-Identifier: Apache-2.0
3
4use super::*;
5use core::marker::PhantomData;
6use s2n_quic_core::{connection::id::Generator, crypto, path};
7use s2n_quic_transport::{connection, endpoint, stream};
8
9impl_providers_state! {
10    #[derive(Debug, Default)]
11    struct Providers {
12        congestion_controller: CongestionController,
13        connection_close_formatter: ConnectionCloseFormatter,
14        connection_id: ConnectionID,
15        packet_interceptor: PacketInterceptor,
16        stateless_reset_token: StatelessResetToken,
17        random: Random,
18        event: Event,
19        limits: Limits,
20        mtu: Mtu,
21        io: IO,
22        sync: Sync,
23        tls: Tls,
24        datagram: Datagram,
25        dc: Dc,
26    }
27
28    /// Opaque trait containing all of the configured providers
29    trait ClientProviders {}
30}
31
32impl<
33        CongestionController: congestion_controller::Provider,
34        ConnectionCloseFormatter: connection_close_formatter::Provider,
35        ConnectionID: connection_id::Provider,
36        PacketInterceptor: packet_interceptor::Provider,
37        StatelessResetToken: stateless_reset_token::Provider,
38        Random: random::Provider,
39        Event: event::Provider,
40        Limits: limits::Provider,
41        Mtu: mtu::Provider,
42        IO: io::Provider,
43        Sync: sync::Provider,
44        Tls: tls::Provider,
45        Datagram: datagram::Provider,
46        Dc: dc::Provider,
47    >
48    Providers<
49        CongestionController,
50        ConnectionCloseFormatter,
51        ConnectionID,
52        PacketInterceptor,
53        StatelessResetToken,
54        Random,
55        Event,
56        Limits,
57        Mtu,
58        IO,
59        Sync,
60        Tls,
61        Datagram,
62        Dc,
63    >
64{
65    pub fn start(self) -> Result<Client, StartError> {
66        let Self {
67            congestion_controller,
68            connection_close_formatter,
69            connection_id,
70            packet_interceptor,
71            stateless_reset_token,
72            random,
73            event,
74            limits,
75            mtu,
76            io,
77            sync,
78            tls,
79            datagram,
80            dc,
81        } = self;
82
83        let congestion_controller = congestion_controller.start().map_err(StartError::new)?;
84        let connection_close_formatter = connection_close_formatter
85            .start()
86            .map_err(StartError::new)?;
87        let connection_id = connection_id.start().map_err(StartError::new)?;
88        let packet_interceptor = packet_interceptor.start().map_err(StartError::new)?;
89        let stateless_reset_token = stateless_reset_token.start().map_err(StartError::new)?;
90        let random = random.start().map_err(StartError::new)?;
91        let endpoint_limits = EndpointLimits;
92        let limits = limits.start().map_err(StartError::new)?;
93        let mtu = mtu.start().map_err(StartError::new)?;
94        let event = event.start().map_err(StartError::new)?;
95        let token = Token;
96        let sync = sync.start().map_err(StartError::new)?;
97        let path_migration = PathMigration;
98        let tls = tls.start_client().map_err(StartError::new)?;
99        let datagram = datagram.start().map_err(StartError::new)?;
100        let dc = dc.start().map_err(StartError::new)?;
101
102        // Validate providers
103        // TODO: Add more validation https://github.com/aws/s2n-quic/issues/285
104        let valid_lifetime = |lifetime| {
105            (connection::id::MIN_LIFETIME..=connection::id::MAX_LIFETIME).contains(&lifetime)
106        };
107        if connection_id
108            .lifetime()
109            .is_some_and(|lifetime| !valid_lifetime(lifetime))
110        {
111            return Err(StartError::new(connection::id::Error::InvalidLifetime));
112        };
113        let mtu = path::mtu::Manager::new(mtu);
114
115        let endpoint_config = EndpointConfig {
116            congestion_controller,
117            connection_close_formatter,
118            connection_id,
119            packet_interceptor,
120            stateless_reset_token,
121            random,
122            endpoint_limits,
123            event,
124            limits,
125            mtu,
126            sync,
127            tls,
128            token,
129            path_handle: PhantomData,
130            path_migration,
131            datagram,
132            dc,
133        };
134
135        let (endpoint, connector) = endpoint::Endpoint::new_client(endpoint_config);
136
137        // Start the IO last
138        let local_addr = io.start(endpoint).map_err(StartError::new)?;
139
140        Ok(Client {
141            connector,
142            local_addr,
143        })
144    }
145}
146
147#[derive(Debug)]
148struct EndpointLimits;
149
150impl endpoint::limits::Limiter for EndpointLimits {
151    fn on_connection_attempt(
152        &mut self,
153        _info: &endpoint::limits::ConnectionAttempt,
154    ) -> endpoint::limits::Outcome {
155        unreachable!("endpoint limits should not be used with clients")
156    }
157}
158
159#[derive(Debug)]
160struct Token;
161
162impl crate::provider::address_token::Format for Token {
163    const TOKEN_LEN: usize = 0;
164
165    fn generate_new_token(
166        &mut self,
167        _context: &mut s2n_quic_core::token::Context<'_>,
168        _source_connection_id: &s2n_quic_core::connection::LocalId,
169        _output_buffer: &mut [u8],
170    ) -> Option<()> {
171        unreachable!("tokens should not be generated with clients")
172    }
173
174    fn generate_retry_token(
175        &mut self,
176        _context: &mut s2n_quic_core::token::Context<'_>,
177        _original_destination_connection_id: &s2n_quic_core::connection::InitialId,
178        _output_buffer: &mut [u8],
179    ) -> Option<()> {
180        unreachable!("tokens should not be generated with clients")
181    }
182
183    fn validate_token(
184        &mut self,
185        _context: &mut s2n_quic_core::token::Context<'_>,
186        _token: &[u8],
187    ) -> Option<s2n_quic_core::connection::InitialId> {
188        unreachable!("tokens should not be generated with clients")
189    }
190}
191
192#[derive(Debug)]
193struct PathMigration;
194
195impl crate::provider::path_migration::Validator for PathMigration {
196    fn on_migration_attempt(
197        &mut self,
198        _attempt: &path::migration::Attempt,
199    ) -> path::migration::Outcome {
200        unreachable!("path migration should not be validated with clients")
201    }
202}
203
204#[allow(dead_code)] // don't warn on unused providers for now
205struct EndpointConfig<
206    CongestionController,
207    ConnectionCloseFormatter,
208    ConnectionID,
209    PacketInterceptor,
210    PathHandle,
211    StatelessResetToken,
212    Random,
213    Event,
214    Limits,
215    Mtu: path::Endpoint,
216    Sync,
217    Tls,
218    Datagram,
219    Dc,
220> {
221    congestion_controller: CongestionController,
222    connection_close_formatter: ConnectionCloseFormatter,
223    connection_id: ConnectionID,
224    packet_interceptor: PacketInterceptor,
225    stateless_reset_token: StatelessResetToken,
226    random: Random,
227    endpoint_limits: EndpointLimits,
228    event: Event,
229    limits: Limits,
230    mtu: path::mtu::Manager<Mtu>,
231    sync: Sync,
232    tls: Tls,
233    token: Token,
234    path_handle: PhantomData<PathHandle>,
235    path_migration: PathMigration,
236    datagram: Datagram,
237    dc: Dc,
238}
239
240impl<
241        CongestionController: congestion_controller::Endpoint,
242        ConnectionCloseFormatter: connection_close_formatter::Formatter,
243        ConnectionID: connection::id::Format,
244        PacketInterceptor: packet_interceptor::PacketInterceptor,
245        PathHandle: path::Handle,
246        StatelessResetToken: stateless_reset_token::Generator,
247        Random: s2n_quic_core::random::Generator,
248        Event: s2n_quic_core::event::Subscriber,
249        Limits: s2n_quic_core::connection::limits::Limiter,
250        Mtu: s2n_quic_core::path::mtu::Endpoint,
251        Sync,
252        Tls: crypto::tls::Endpoint,
253        Datagram: s2n_quic_core::datagram::Endpoint,
254        Dc: s2n_quic_core::dc::Endpoint,
255    > core::fmt::Debug
256    for EndpointConfig<
257        CongestionController,
258        ConnectionCloseFormatter,
259        ConnectionID,
260        PacketInterceptor,
261        PathHandle,
262        StatelessResetToken,
263        Random,
264        Event,
265        Limits,
266        Mtu,
267        Sync,
268        Tls,
269        Datagram,
270        Dc,
271    >
272{
273    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
274        f.debug_struct("ClientConfig").finish()
275    }
276}
277
278impl<
279        CongestionController: congestion_controller::Endpoint,
280        ConnectionCloseFormatter: connection_close_formatter::Formatter,
281        ConnectionID: connection::id::Format,
282        PacketInterceptor: packet_interceptor::PacketInterceptor,
283        PathHandle: path::Handle,
284        StatelessResetToken: stateless_reset_token::Generator,
285        Random: s2n_quic_core::random::Generator,
286        Event: s2n_quic_core::event::Subscriber,
287        Limits: s2n_quic_core::connection::limits::Limiter,
288        Mtu: s2n_quic_core::path::mtu::Endpoint,
289        Sync: 'static + Send,
290        Tls: crypto::tls::Endpoint,
291        Datagram: s2n_quic_core::datagram::Endpoint,
292        Dc: s2n_quic_core::dc::Endpoint,
293    > endpoint::Config
294    for EndpointConfig<
295        CongestionController,
296        ConnectionCloseFormatter,
297        ConnectionID,
298        PacketInterceptor,
299        PathHandle,
300        StatelessResetToken,
301        Random,
302        Event,
303        Limits,
304        Mtu,
305        Sync,
306        Tls,
307        Datagram,
308        Dc,
309    >
310{
311    type ConnectionIdFormat = ConnectionID;
312    type ConnectionCloseFormatter = ConnectionCloseFormatter;
313    type PathHandle = PathHandle;
314    type StatelessResetTokenGenerator = StatelessResetToken;
315    type RandomGenerator = Random;
316    type Connection = connection::Implementation<Self>;
317    // TODO allow users to specify another lock type
318    type ConnectionLock = std::sync::Mutex<Self::Connection>;
319    type CongestionControllerEndpoint = CongestionController;
320    type EndpointLimits = EndpointLimits;
321    type EventSubscriber = Event;
322    type TLSEndpoint = Tls;
323    type TokenFormat = Token;
324    type ConnectionLimits = Limits;
325    type Mtu = Mtu;
326    type StreamManager = stream::DefaultStreamManager;
327    type PathMigrationValidator = PathMigration;
328    type PacketInterceptor = PacketInterceptor;
329    type DatagramEndpoint = Datagram;
330    type DcEndpoint = Dc;
331
332    const ENDPOINT_TYPE: endpoint::Type = endpoint::Type::Client;
333
334    fn context(&mut self) -> endpoint::Context<'_, Self> {
335        endpoint::Context {
336            congestion_controller: &mut self.congestion_controller,
337            connection_close_formatter: &mut self.connection_close_formatter,
338            connection_id_format: &mut self.connection_id,
339            packet_interceptor: &mut self.packet_interceptor,
340            stateless_reset_token_generator: &mut self.stateless_reset_token,
341            random_generator: &mut self.random,
342            tls: &mut self.tls,
343            endpoint_limits: &mut self.endpoint_limits,
344            token: &mut self.token,
345            connection_limits: &mut self.limits,
346            mtu: &mut self.mtu,
347            event_subscriber: &mut self.event,
348            path_migration: &mut self.path_migration,
349            datagram: &mut self.datagram,
350            dc: &mut self.dc,
351        }
352    }
353}