// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

use core::fmt;
use s2n_quic_transport::connection::Connection as Inner;

#[macro_use]
mod acceptor;
#[macro_use]
mod handle;

pub use acceptor::*;
pub use handle::*;
pub use s2n_quic_core::connection::Error;

pub mod error {
    pub use s2n_quic_core::transport::error::Code;
}

pub type Result<T, E = Error> = core::result::Result<T, E>;

pub struct Connection(Inner);

impl fmt::Debug for Connection {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        self.0.fmt(f)
    }
}

impl Connection {
    #[inline]
    pub(crate) const fn new(inner: Inner) -> Self {
        Self(inner)
    }

    impl_accept_api!();
    impl_handle_api!(|handle, call| call!(handle));

    /// Returns a cloneable handle to the connection
    ///
    /// # Examples
    ///
    /// ```rust,no_run
    /// # async fn test() {
    /// #   let mut connection: s2n_quic::connection::Connection = todo!();
    /// #
    /// let handle = connection.handle();
    /// let another_handle = handle.clone();
    /// # }
    /// ```
    #[inline]
    pub fn handle(&self) -> Handle {
        Handle(self.0.clone())
    }

    /// Splits the connection into a  [`connection::Handle`](crate::connection::Handle) and
    /// [`connection::StreamAcceptor`](crate::connection::StreamAcceptor) halves
    ///
    /// # Examples
    ///
    /// ```rust,no_run
    /// # async fn test() -> s2n_quic::connection::Result<()> {
    /// #   let mut connection: s2n_quic::connection::Connection = todo!();
    /// #
    /// let (mut handle, mut acceptor) = connection.split();
    /// let mut send = handle.open_send_stream().await?;
    /// tokio::spawn(async move {
    ///     let _ = send.send(bytes::Bytes::from_static(&[1, 2, 3])).await;
    /// });
    ///
    /// while let Some(stream) = acceptor.accept().await? {
    ///     println!("accepted stream {}", stream.id());
    /// }
    ///
    /// #
    /// #   Ok(())
    /// # }
    /// ```
    #[inline]
    pub fn split(self) -> (Handle, StreamAcceptor) {
        let handle = Handle(self.0.clone());
        let acceptor = StreamAcceptor(self.0);
        (handle, acceptor)
    }
}


// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

//! An implementation of the [IETF QUIC protocol](https://quicwg.org/), featuring:
//! * a simple, easy-to-use API. See [an example](https://github.com/aws/s2n-quic/blob/main/examples/echo/src/bin/quic_echo_server.rs) of an s2n-quic echo server built with just a few API calls
//! * high configurability using [providers](https://docs.rs/s2n-quic/latest/s2n_quic/provider/index.html) for granular control of functionality
//! * extensive automated testing, including fuzz testing, integration testing, unit testing, snapshot testing, efficiency testing, performance benchmarking, interoperability testing and [more](https://github.com/aws/s2n-quic/blob/main/docs/ci.md)
//! * integration with [s2n-tls](https://github.com/aws/s2n-tls), AWS's simple, small, fast and secure TLS implementation, as well as [rustls](https://crates.io/crates/rustls)
//! * thorough [compliance coverage tracking](https://github.com/aws/s2n-quic/blob/main/docs/ci.md#compliance) of normative language in relevant standards
//! * and much more, including [CUBIC congestion controller](https://www.rfc-editor.org/rfc/rfc8312.html) support, [packet pacing](https://www.rfc-editor.org/rfc/rfc9002.html#name-pacing), [Generic Segmentation Offload](https://lwn.net/Articles/188489/) support, [Path MTU discovery](https://www.rfc-editor.org/rfc/rfc8899.html), and unique [connection identifiers](https://www.rfc-editor.org/rfc/rfc9000.html#name-connection-id) detached from the address
//!
//! See the [installation instructions](https://github.com/aws/s2n-quic#installation) and [examples](https://github.com/aws/s2n-quic/tree/main/examples) to get started with `s2n-quic`.
//!
//! ## Feature flags
//!
//! ### `provider-address-token-default`
//!
//! _Enabled by default_
//!
//! Enables the default address token provider, which
//! will securely generate address tokens for a single QUIC server. If your deployment requires
//! that multiple servers handle address tokens, this provider should not be used. Instead, a custom
//! implementation of [`provider::address_token::Format`] should be specified.
//!
//! ### `provider-event-tracing`
//!
//! Enables event integration with [`tracing`](https://docs.rs/tracing). The
//! default event provider will be set to [`provider::event::tracing::Provider`] and will emit
//! endpoint and connection events to the application's configured
//! [`tracing::Subscriber`](https://docs.rs/tracing/latest/tracing/trait.Subscriber.html).
//!
//! ### `provider-tls-default`
//!
//! _Enabled by default_
//!
//! Enables platform detection for the recommended implementation of TLS. Currently, this uses
//! [`s2n-tls`][s2n-tls] on unix-like platforms and [`rustls`][rustls] on everything else.
//!
//! ### `provider-tls-rustls`
//!
//! Enables the [`rustls`][rustls] TLS provider. The provider will be available at
//! [`provider::tls::rustls`].
//!
//! **NOTE**: this will override the platform detection and always use [`rustls`][rustls] by default.
//!
//! ### `provider-tls-s2n`
//!
//! Enables the [`s2n-tls`][s2n-tls] TLS provider. The provider will be available at
//! [`provider::tls::s2n_tls`].
//!
//! **NOTE**: this will override the platform detection and always use [`s2n-tls`][s2n-tls] by default.
//!
//! [s2n-tls]: https://github.com/aws/s2n-tls
//! [rustls]: https://github.com/rustls/rustls

#[macro_use]
pub mod provider;

pub mod client;
pub mod connection;
pub mod server;
pub mod stream;

pub mod application {
    pub use s2n_quic_core::application::Error;
}

pub use client::Client;
pub use connection::Connection;
pub use server::Server;

#[cfg(test)]
mod tests;

// Require `--cfg s2n_quic_unstable` is set when using unstable features
#[cfg(
    all(
        // add new unstable features to this list
        any(
            feature = "unstable_client_hello",
            feature = "unstable-provider-datagram",
            feature = "unstable-provider-io-testing",
            feature = "unstable-provider-io-turmoil",
            feature = "unstable-provider-io-xdp",
            feature = "unstable-provider-packet-interceptor",
            feature = "unstable-provider-random",
            feature = "unstable-congestion-controller",
        ),
        // any unstable features requires at least one of the following conditions
        not(any(
            // we're running tests
            test,
            doctest,
            // we're compiling docs for docs.rs
            docsrs,
            // we're developing s2n-quic
            s2n_internal_dev,
            // the application has explicitly opted into unstable features
            s2n_quic_unstable,
        ))
    )
)]
std::compile_error!("Application must be built with RUSTFLAGS=\"--cfg s2n_quic_unstable\" to use unstable features.");


// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

use cfg_if::cfg_if;
use core::fmt;

#[macro_use]
mod macros;

pub mod address_token;
pub mod congestion_controller;
pub mod connection_id;
pub mod endpoint_limits;
pub mod event;
pub mod io;
pub mod limits;
pub mod stateless_reset_token;
pub mod tls;

// These providers are not currently exposed to applications
pub(crate) mod connection_close_formatter;
pub(crate) mod path_migration;
pub(crate) mod sync;

cfg_if!(
    if #[cfg(any(test, feature = "unstable-provider-packet-interceptor"))] {
        pub mod packet_interceptor;
    } else {
        pub(crate) mod packet_interceptor;
    }
);

cfg_if!(
    if #[cfg(any(test, feature = "unstable-provider-random"))] {
        pub mod random;
    } else {
        pub(crate) mod random;
    }
);

cfg_if!(
    if #[cfg(any(test, feature = "unstable-provider-datagram"))] {
        pub mod datagram;
    } else {
        pub(crate) mod datagram;
    }
);

/// An error indicating a failure to start an endpoint
pub struct StartError(Box<dyn 'static + fmt::Display>);

impl std::error::Error for StartError {}

impl StartError {
    pub(crate) fn new<T: 'static + fmt::Display>(error: T) -> Self {
        Self(Box::new(error))
    }
}

impl fmt::Debug for StartError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.debug_tuple("StartError")
            .field(&format_args!("{}", self.0))
            .finish()
    }
}

impl fmt::Display for StartError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        self.0.fmt(f)
    }
}


// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

use crate::{connection::Connection, provider::*};
use core::{
    fmt,
    task::{Context, Poll},
};
use s2n_quic_transport::endpoint::handle::Acceptor;

mod builder;
mod providers;

pub use builder::*;
pub use providers::*;
pub use s2n_quic_core::application::ServerName as Name;

/// A QUIC server endpoint, capable of accepting connections
pub struct Server {
    acceptor: Acceptor,
    local_addr: s2n_quic_core::inet::SocketAddress,
}

impl fmt::Debug for Server {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.debug_struct("Server")
            .field("local_addr", &self.local_addr().ok())
            .finish()
    }
}

impl Server {
    /// Returns a [`Builder`] which is able to configure the [`Server`] components.
    ///
    /// # Examples
    ///
    /// ```rust,no_run
    /// # use std::{error::Error, path::Path};
    /// # use s2n_quic::Server;
    /// #
    /// # fn main() -> Result<(), Box<dyn Error>> {
    /// let server = Server::builder()
    ///     .with_tls((Path::new("./certs/cert.pem"), Path::new("./certs/key.pem")))?
    ///     .with_io("127.0.0.1:443")?
    ///     .start()?;
    /// #
    /// #    Ok(())
    /// # }
    /// ```
    pub fn builder() -> Builder<impl ServerProviders> {
        Builder::default()
    }

    /// Accepts a new incoming [`Connection`] from this [`Server`].
    ///
    /// This function will yield once a new QUIC connection is established. When established,
    /// the corresponding [`Connection`] will be returned.
    ///
    /// # Examples
    ///
    /// ```rust,no_run
    /// # use std::{error::Error, path::Path};
    /// # use s2n_quic::Server;
    /// #
    /// # #[tokio::main]
    /// # async fn main() -> Result<(), Box<dyn Error>> {
    /// let mut server = Server::builder()
    ///     .with_tls((Path::new("./certs/cert.pem"), Path::new("./certs/key.pem")))?
    ///     .with_io("127.0.0.1:443")?
    ///     .start()?;
    ///
    /// match server.accept().await {
    ///     Some(connection) => {
    ///         println!("new connection: {:?}", connection.remote_addr());
    ///     }
    ///     None => println!("server closed"),
    /// }
    /// #    Ok(())
    /// # }
    /// ```
    pub async fn accept(&mut self) -> Option<Connection> {
        futures::future::poll_fn(|cx| self.poll_accept(cx)).await
    }

    /// Attempts to accept a new incoming [`Connection`] from this [`Server`].
    ///
    /// # Return value
    ///
    /// This function returns:
    ///
    /// - `Poll::Pending` if no new connections have been established.
    /// - `Poll::Ready(Some(connection))` once a new connection has been established.
    /// This function can be called again to try and accept new connections.
    /// - `Poll::Ready(None)` the attempt failed because the server has closed. Once
    /// None is returned, this function should not be called again.
    pub fn poll_accept(&mut self, cx: &mut Context) -> Poll<Option<Connection>> {
        match self.acceptor.poll_accept(cx) {
            Poll::Ready(Some(connection)) => Poll::Ready(Some(Connection::new(connection))),
            Poll::Ready(None) => Poll::Ready(None),
            Poll::Pending => Poll::Pending,
        }
    }

    /// Returns the local address that this listener is bound to.
    ///
    /// This can be useful, for example, when binding to port `0` to figure out which
    /// port was actually bound.
    ///
    /// # Examples
    ///
    /// ```rust,no_run
    /// # use std::{error::Error, path::Path};
    /// # use s2n_quic::Server;
    /// #
    /// # fn main() -> Result<(), Box<dyn Error>> {
    /// let server = Server::builder()
    ///     .with_tls((Path::new("./certs/cert.pem"), Path::new("./certs/key.pem")))?
    ///     .with_io("127.0.0.1:443")?
    ///     .start()?;
    ///
    /// let local_addr = server.local_addr()?;
    ///
    /// #    Ok(())
    /// # }
    /// ```
    pub fn local_addr(&self) -> Result<std::net::SocketAddr, std::io::Error> {
        Ok(self.local_addr.into())
    }
}

impl futures::stream::Stream for Server {
    type Item = Connection;

    fn poll_next(
        mut self: core::pin::Pin<&mut Self>,
        cx: &mut core::task::Context<'_>,
    ) -> core::task::Poll<Option<Self::Item>> {
        self.poll_accept(cx)
    }
}


// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

#[macro_use]
mod connection;
#[macro_use]
mod splittable;
#[macro_use]
mod receive;
#[macro_use]
mod send;
#[macro_use]
mod bidirectional;

mod local;
mod peer;

pub use s2n_quic_core::stream::{StreamError as Error, StreamType as Type};

pub use bidirectional::*;
pub use local::*;
pub use peer::*;
pub use receive::*;
pub use send::*;
pub use splittable::*;

pub type Result<T, E = Error> = core::result::Result<T, E>;

/// An enum of all the possible types of QUIC streams.
#[derive(Debug)]
pub enum Stream {
    Bidirectional(BidirectionalStream),
    Receive(ReceiveStream),
    Send(SendStream),
}

impl Stream {
    /// Returns the stream's identifier
    ///
    /// This value is unique to a particular connection. The format follows the same as what is
    /// defined in the
    /// [QUIC Transport RFC](https://www.rfc-editor.org/rfc/rfc9000.html#name-stream-types-and-identifier).
    ///
    /// # Examples
    ///
    /// ```rust,no_run
    /// # async fn test() -> s2n_quic::stream::Result<()> {
    /// #   let connection: s2n_quic::connection::Connection = todo!();
    /// #
    /// use s2n_quic::stream::Stream;
    ///
    /// let stream = connection.open_bidirectional_stream().await?;
    /// let stream: Stream = stream.into();
    /// println!("New stream's id: {}", stream.id());
    /// #
    /// #   Ok(())
    /// # }
    /// ```
    #[inline]
    pub fn id(&self) -> u64 {
        match self {
            Self::Bidirectional(stream) => stream.id(),
            Self::Receive(stream) => stream.id(),
            Self::Send(stream) => stream.id(),
        }
    }

    impl_connection_api!(|stream| match stream {
        Stream::Bidirectional(stream) => stream.connection(),
        Stream::Receive(stream) => stream.connection(),
        Stream::Send(stream) => stream.connection(),
    });

    impl_receive_stream_api!(|stream, dispatch| match stream {
        Stream::Bidirectional(stream) => dispatch!(stream),
        Stream::Receive(stream) => dispatch!(stream),
        Stream::Send(_stream) => dispatch!(),
    });

    impl_send_stream_api!(|stream, dispatch| match stream {
        Stream::Bidirectional(stream) => dispatch!(stream),
        Stream::Receive(_stream) => dispatch!(),
        Stream::Send(stream) => dispatch!(stream),
    });

    impl_splittable_stream_api!();
}

impl_receive_stream_trait!(Stream, |stream, dispatch| match stream {
    Stream::Bidirectional(stream) => dispatch!(stream),
    Stream::Receive(stream) => dispatch!(stream),
    Stream::Send(_stream) => dispatch!(),
});
impl_send_stream_trait!(Stream, |stream, dispatch| match stream {
    Stream::Bidirectional(stream) => dispatch!(stream),
    Stream::Receive(_stream) => dispatch!(),
    Stream::Send(stream) => dispatch!(stream),
});
impl_splittable_stream_trait!(Stream, |stream| match stream {
    Stream::Bidirectional(stream) => SplittableStream::split(stream),
    Stream::Receive(stream) => SplittableStream::split(stream),
    Stream::Send(stream) => SplittableStream::split(stream),
});

impl From<ReceiveStream> for Stream {
    #[inline]
    fn from(stream: ReceiveStream) -> Self {
        Self::Receive(stream)
    }
}

impl From<SendStream> for Stream {
    #[inline]
    fn from(stream: SendStream) -> Self {
        Self::Send(stream)
    }
}

impl From<BidirectionalStream> for Stream {
    #[inline]
    fn from(stream: BidirectionalStream) -> Self {
        Self::Bidirectional(stream)
    }
}


// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

use crate::{
    client::Connect,
    provider::{
        self,
        event::{
            events::{MtuUpdated, MtuUpdatedCause, PacketSent, RecoveryMetrics},
            ConnectionInfo, ConnectionMeta, Subscriber,
        },
        io::testing::{rand, spawn, test, time::delay, Model},
        packet_interceptor::Loss,
    },
    Client, Server,
};
use bytes::Bytes;
#[cfg(not(target_os = "windows"))]
use s2n_quic_core::event::api::HandshakeStatusUpdated;
use s2n_quic_core::{crypto::tls::testing::certificates, stream::testing::Data};
use s2n_quic_platform::io::testing::{network::Packet, primary, TxRecorder};
use std::{
    net::SocketAddr,
    sync::{Arc, Mutex},
    time::Duration,
};
use tokio::io::{AsyncReadExt, AsyncWriteExt};

mod setup;
use setup::*;

#[cfg(not(target_os = "windows"))]
mod client_handshake_confirm;
mod no_tls;

#[test]
fn client_server_test() {
    test(Model::default(), client_server).unwrap();
}

fn blackhole(model: Model, blackhole_duration: Duration) {
    test(model.clone(), |handle| {
        spawn(async move {
            // switch back and forth between blackhole and not
            loop {
                delay(blackhole_duration).await;
                // drop all packets
                model.set_drop_rate(1.0);

                delay(blackhole_duration).await;
                model.set_drop_rate(0.0);
            }
        });
        client_server(handle)
    })
    .unwrap();
}

#[test]
fn blackhole_success_test() {
    let model = Model::default();

    let network_delay = Duration::from_millis(1000);
    model.set_delay(network_delay);

    // setting the blackhole time to `network_delay / 2` causes the connection to
    // succeed
    let blackhole_duration = network_delay / 2;
    blackhole(model, blackhole_duration);
}

#[test]
#[should_panic]
fn blackhole_failure_test() {
    let model = Model::default();

    let network_delay = Duration::from_millis(1000);
    model.set_delay(network_delay);

    // setting the blackhole time to `network_delay / 2 + 1` causes the connection to fail
    let blackhole_duration = network_delay / 2 + Duration::from_millis(1);
    blackhole(model, blackhole_duration);
}

fn intercept_loss(loss: Loss<Random>) {
    let model = Model::default();
    test(model, |handle| {
        let server = Server::builder()
            .with_io(handle.builder().build()?)?
            .with_tls(SERVER_CERTS)?
            .with_event(events())?
            .with_packet_interceptor(loss)?
            .start()?;
        let server_address = start_server(server)?;

        client(handle, server_address)
    })
    .unwrap();
}

#[test]
fn interceptor_success_test() {
    intercept_loss(
        Loss::builder(Random::with_seed(123))
            .with_rx_loss(0..20)
            .with_rx_pass(1..5)
            .build(),
    )
}

#[test]
#[should_panic]
fn interceptor_failure_test() {
    intercept_loss(
        Loss::builder(Random::with_seed(123))
            .with_rx_loss(0..20)
            .with_rx_pass(1..2)
            .build(),
    )
}

/// Ensures streams with STOP_SENDING are properly cleaned up
///
/// See https://github.com/aws/s2n-quic/pull/1361
#[test]
fn stream_reset_test() {
    let model = Model::default();
    test(model, |handle| {
        let mut server = Server::builder()
            .with_io(handle.builder().build()?)?
            .with_tls(SERVER_CERTS)?
            .with_event(events())?
            .with_limits(
                provider::limits::Limits::default()
                    // only allow 1 concurrent stream form the peer
                    .with_max_open_local_bidirectional_streams(1)
                    .unwrap(),
            )?
            .start()?;
        let server_addr = server.local_addr()?;

        spawn(async move {
            while let Some(mut connection) = server.accept().await {
                spawn(async move {
                    while let Some(mut stream) =
                        connection.accept_bidirectional_stream().await.unwrap()
                    {
                        spawn(async move {
                            // drain the receive stream
                            while stream.receive().await.unwrap().is_some() {}

                            // send data until the client resets the stream
                            while stream.send(Bytes::from_static(&[42; 1024])).await.is_ok() {}
                        });
                    }
                });
            }
        });

        let client = build_client(handle)?;

        primary::spawn(async move {
            let connect = Connect::new(server_addr).with_server_name("localhost");
            let mut connection = client.connect(connect).await.unwrap();

            for mut remaining_chunks in 0usize..4 {
                let mut stream = connection.open_bidirectional_stream().await.unwrap();

                primary::spawn(async move {
                    stream.send(Bytes::from_static(&[42])).await.unwrap();
                    stream.finish().unwrap();

                    loop {
                        stream.receive().await.unwrap().unwrap();
                        if let Some(next_value) = remaining_chunks.checked_sub(1) {
                            remaining_chunks = next_value;
                        } else {
                            let _ = stream.stop_sending(123u8.into());
                            break;
                        }
                    }
                });
            }
        });

        Ok(())
    })
    .unwrap();
}

/// Ensures tokio `AsyncRead` implementation functions properly
///
/// See https://github.com/aws/s2n-quic/issues/1427
#[test]
fn tokio_read_exact_test() {
    let model = Model::default();
    test(model, |handle| {
        let server_addr = server(handle)?;

        let client = build_client(handle)?;

        // send 5000 bytes
        const LEN: usize = 5000;

        primary::spawn(async move {
            let connect = Connect::new(server_addr).with_server_name("localhost");
            let mut connection = client.connect(connect).await.unwrap();
            let stream = connection.open_bidirectional_stream().await.unwrap();
            let (mut recv, mut send) = stream.split();

            primary::spawn(async move {
                let mut read_len = 0;
                let mut buf = [0u8; 1000];
                // try to read from the stream until we read LEN bytes
                while read_len < LEN {
                    let max_len = buf.len().min(LEN - read_len);
                    // generate a random amount of bytes to read
                    let len = rand::gen_range(1..=max_len);

                    let buf = &mut buf[0..len];
                    recv.read_exact(buf).await.unwrap();

                    // record the amount that was read
                    read_len += len;
                }
                assert_eq!(read_len, LEN);
            });

            let mut write_len = 0;
            let mut buf = &[42u8; LEN][..];
            while !buf.is_empty() {
                // split the `buf` until it's empty
                let chunk_len = write_len.min(buf.len());
                let (chunk, remaining) = buf.split_at(chunk_len);

                // ensure the chunk is written to the stream
                send.write_all(chunk).await.unwrap();

                buf = remaining;
                // slowly increase the size of the chunks written
                write_len += 1;

                // by slowing the rate at which we send, we exercise the receiver's buffering logic in `read_exact`
                delay(Duration::from_millis(10)).await;
            }
        });

        Ok(())
    })
    .unwrap();
}

/// Ensures the peer is notified of locally-created streams
///
/// # Client expectations
/// * The client connects to the server
/// * The client opens a bidirectional stream
/// * The client reads 100 bytes from the newly created stream
///
/// # Server expectations
/// * The server accepts a new connection
/// * The server accepts a new bidirectional stream
/// * The server writes 100 bytes to the newly accepted stream
///
/// Unless the client notifies the server of the stream creation, the connection
/// is dead-locked and will timeout.
///
/// See https://github.com/aws/s2n-quic/issues/1464
#[test]
fn local_stream_open_notify_test() {
    let model = Model::default();
    test(model, |handle| {
        let mut server = build_server(handle)?;
        let server_addr = server.local_addr()?;

        // send 100 bytes
        const LEN: usize = 100;

        spawn(async move {
            while let Some(mut conn) = server.accept().await {
                while let Ok(Some(mut stream)) = conn.accept_bidirectional_stream().await {
                    primary::spawn(async move {
                        stream.send(vec![42; LEN].into()).await.unwrap();
                    });
                }
            }
        });

        let client = build_client(handle)?;

        primary::spawn(async move {
            let connect = Connect::new(server_addr).with_server_name("localhost");
            let mut connection = client.connect(connect).await.unwrap();
            let mut stream = connection.open_bidirectional_stream().await.unwrap();

            let mut recv_len = 0;
            while let Ok(Some(chunk)) = stream.receive().await {
                recv_len += chunk.len();
            }

            assert_eq!(LEN, recv_len);
        });

        Ok(())
    })
    .unwrap();
}

macro_rules! event_recorder {
    ($sub:ident, $event:ty, $method:ident) => {
        event_recorder!($sub, $event, $method, $event, {
            |event: &$event, storage: &mut Vec<$event>| storage.push(event.clone())
        });
    };
    ($sub:ident, $event:ty, $method:ident, $storage:ty, $store:expr) => {
        #[derive(Clone, Default)]
        struct $sub {
            events: Arc<Mutex<Vec<$storage>>>,
        }

        #[allow(dead_code)]
        impl $sub {
            fn new() -> Self {
                Self::default()
            }

            fn events(&self) -> Arc<Mutex<Vec<$storage>>> {
                self.events.clone()
            }

            fn any<F: FnMut(&$storage) -> bool>(&self, f: F) -> bool {
                let events = self.events.lock().unwrap();
                events.iter().any(f)
            }
        }

        impl Subscriber for $sub {
            type ConnectionContext = $sub;

            fn create_connection_context(
                &mut self,
                _meta: &ConnectionMeta,
                _info: &ConnectionInfo,
            ) -> Self::ConnectionContext {
                self.clone()
            }

            fn $method(
                &mut self,
                context: &mut Self::ConnectionContext,
                _meta: &ConnectionMeta,
                event: &$event,
            ) {
                let store = $store;
                let mut buffer = context.events.lock().unwrap();
                store(event, &mut buffer);
            }
        }
    };
}

event_recorder!(PacketSentRecorder, PacketSent, on_packet_sent);
event_recorder!(MtuUpdatedRecorder, MtuUpdated, on_mtu_updated);
event_recorder!(
    PathUpdatedRecorder,
    RecoveryMetrics,
    on_recovery_metrics,
    SocketAddr,
    |event: &RecoveryMetrics, storage: &mut Vec<SocketAddr>| {
        let addr: SocketAddr = event.path.local_addr.to_string().parse().unwrap();
        if storage.last().map_or(true, |prev| *prev != addr) {
            storage.push(addr);
        }
    }
);
event_recorder!(
    PtoRecorder,
    RecoveryMetrics,
    on_recovery_metrics,
    u32,
    |event: &RecoveryMetrics, storage: &mut Vec<u32>| {
        storage.push(event.pto_count);
    }
);
#[cfg(not(target_os = "windows"))]
event_recorder!(
    HandshakeStatusRecorder,
    HandshakeStatusUpdated,
    on_handshake_status_updated
);

#[test]
fn packet_sent_event_test() {
    let recorder = TxRecorder::default();
    let network_packets = recorder.get_packets();
    let subscriber = PacketSentRecorder::new();
    let events = subscriber.events();
    let mut server_socket = None;
    test((recorder, Model::default()), |handle| {
        let server = Server::builder()
            .with_io(handle.builder().build()?)?
            .with_tls(SERVER_CERTS)?
            .with_event(subscriber)?
            .start()?;
        let addr = start_server(server)?;
        // store addr in exterior scope so we can use it to filter packets
        // after the test ends
        server_socket = Some(addr);
        client(handle, addr)?;
        Ok(addr)
    })
    .unwrap();

    let server_socket = server_socket.unwrap();
    let mut events = events.lock().unwrap();
    let mut server_tx_network_packets: Vec<Packet> = network_packets
        .lock()
        .unwrap()
        .iter()
        .cloned()
        .filter(|p| {
            let local_socket: SocketAddr = p.path.local_address.0.into();
            local_socket == server_socket
        })
        .collect();

    // transmitted quic packets may be coalesced into a single datagram (network packet)
    // so it might be the case that network_packet[0] = quic_packet[0] + quic_packet[1]
    while let Some(server_packet) = server_tx_network_packets.pop() {
        let expected_len = server_packet.payload.len();

        let mut event_len = 0;
        while expected_len > event_len {
            event_len += events.pop().unwrap().packet_len;
        }

        assert_eq!(expected_len, event_len)
    }
    assert!(events.is_empty());
}

// Construct a simulation where a client sends some data, which the server echos
// back. The MtuUpdated events that the server experiences are recorded and
// returns at the end of the simulation.
fn mtu_updates(max_mtu: u16) -> Vec<MtuUpdated> {
    let model = Model::default();
    model.set_max_udp_payload(max_mtu);

    let subscriber = MtuUpdatedRecorder::new();
    let events = subscriber.events();

    test(model, |handle| {
        let server = Server::builder()
            .with_io(handle.builder().with_max_mtu(max_mtu).build()?)?
            .with_tls(SERVER_CERTS)?
            .with_event(subscriber)?
            .start()?;
        let client = Client::builder()
            .with_io(handle.builder().with_max_mtu(max_mtu).build().unwrap())?
            .with_tls(certificates::CERT_PEM)?
            .start()?;
        let addr = start_server(server)?;
        // we need a large payload to allow for multiple rounds of MTU probing
        start_client(client, addr, Data::new(10_000_000))?;
        Ok(addr)
    })
    .unwrap();

    let events_handle = events.lock().unwrap();
    events_handle.clone()
}

// if we specify jumbo frames on the endpoint and the network supports them,
// then jumbo frames should be negotiated.
#[test]
fn mtu_probe_jumbo_frame_test() {
    let events = mtu_updates(9_001);

    // handshake is padded to 1200, so we should immediate have an mtu of 1200
    // since the handshake successfully completes
    let handshake_mtu = events[0].clone();
    assert_eq!(handshake_mtu.mtu, 1200);
    assert!(matches!(
        handshake_mtu.cause,
        MtuUpdatedCause::NewPath { .. }
    ));

    // we should then successfully probe for 1500 (minus headers = 1472)
    let first_probe = events[1].clone();
    assert_eq!(first_probe.mtu, 1472);

    // we binary search upwards 9001
    // this isn't the maximum mtu we'd find in practice, just the maximum mtu we
    // find with a payload of 10_000_000 bytes.
    let last_probe = events.last().unwrap();
    assert_eq!(last_probe.mtu, 8943);
}

// if we specify jumbo frames on the endpoint and the network does not support
// them, the connection should gracefully complete with a smaller mtu
#[test]
fn mtu_probe_jumbo_frame_unsupported_test() {
    let events = mtu_updates(1_500);
    let last_mtu = events.last().unwrap();
    // ETHERNET_MTU - UDP_HEADER_LEN - IPV4_HEADER_LEN
    assert_eq!(last_mtu.mtu, 1472);
}

// if we lose every packet during a round trip and then allow packets through,
// this is not determined to be an MTU black hole
#[test]
fn mtu_loss_no_blackhole() {
    let model = Model::default();
    let rtt = Duration::from_millis(100);
    let max_mtu = 9001;
    let subscriber = MtuUpdatedRecorder::new();
    let events = subscriber.events();

    model.set_delay(rtt / 2);
    model.set_max_udp_payload(max_mtu);

    test(model.clone(), |handle| {
        let server = Server::builder()
            .with_io(handle.builder().with_max_mtu(max_mtu).build()?)?
            .with_tls(SERVER_CERTS)?
            .with_event(subscriber)?
            .start()?;
        let client = Client::builder()
            .with_io(handle.builder().with_max_mtu(max_mtu).build()?)?
            .with_tls(certificates::CERT_PEM)?
            .start()?;
        let addr = start_server(server)?;
        // we need a large payload to allow for multiple rounds of MTU probing
        start_client(client, addr, Data::new(10_000_000))?;

        spawn(async move {
            // let all packets go through for 10 RTTs - this will reach the end of MTU probing
            model.set_drop_rate(0.0);
            delay(rtt * 10).await;

            // drop all packets for a single round trip
            model.set_drop_rate(1.0);
            delay(rtt * 1).await;

            // now let the rest of the packets through
            model.set_drop_rate(0.0);
        });

        Ok(addr)
    })
    .unwrap();

    // MTU remained jumbo despite the packet loss
    assert_eq!(8943, events.lock().unwrap().last().unwrap().mtu);
}

// if the MTU is decreased after an MTU probe previously raised the MTU for the path,
// we detect an MTU black hole and decrease the MTU to the minimum
#[test]
fn mtu_blackhole() {
    let model = Model::default();
    let rtt = Duration::from_millis(100);
    let max_mtu = 9001;
    let subscriber = MtuUpdatedRecorder::new();
    let events = subscriber.events();

    model.set_delay(rtt / 2);
    model.set_max_udp_payload(max_mtu);

    test(model.clone(), |handle| {
        let server = Server::builder()
            .with_io(handle.builder().with_max_mtu(max_mtu).build()?)?
            .with_tls(SERVER_CERTS)?
            .with_event(subscriber)?
            .start()?;
        let client = Client::builder()
            .with_io(handle.builder().with_max_mtu(max_mtu).build()?)?
            .with_tls(certificates::CERT_PEM)?
            .start()?;
        let addr = start_server(server)?;
        // we need a large payload to allow for multiple rounds of MTU probing
        start_client(client, addr, Data::new(10_000_000))?;

        spawn(async move {
            // let all packets go through for 10 RTTs - this will reach the end of MTU probing
            model.set_drop_rate(0.0);
            delay(rtt * 10).await;

            // decrease the MTU to trigger a blackhole
            model.set_max_udp_payload(1200);
        });

        Ok(addr)
    })
    .unwrap();

    // MTU dropped to the minimum
    assert_eq!(1200, events.lock().unwrap().last().unwrap().mtu);
}

/// Ensures that the client's local path handle is updated after it receives a packet from the
/// server
///
/// See https://github.com/aws/s2n-quic/issues/954
#[test]
fn client_path_handle_update() {
    let model = Model::default();

    let subscriber = PathUpdatedRecorder::new();
    let events = subscriber.events();

    test(model, |handle| {
        let server = Server::builder()
            .with_io(handle.builder().build()?)?
            .with_tls(SERVER_CERTS)?
            .start()?;
        let client = Client::builder()
            .with_io(handle.builder().build().unwrap())?
            .with_tls(certificates::CERT_PEM)?
            .with_event(subscriber)?
            .start()?;
        let addr = start_server(server)?;
        start_client(client, addr, Data::new(1000))?;
        Ok(addr)
    })
    .unwrap();

    let events_handle = events.lock().unwrap();

    // initially, the client address should be unknown
    assert_eq!(events_handle[0], "0.0.0.0:0".parse().unwrap());
    // after receiving a packet, the client port should be the first available ephemeral port
    assert_eq!(events_handle[1], "1.0.0.1:49153".parse().unwrap());
    // there should only be a single update to the path handle
    assert_eq!(events_handle.len(), 2);
}

#[test]
fn increasing_pto_count_under_loss() {
    let delay_time = Duration::from_millis(10);

    let model = Model::default();
    model.set_delay(delay_time);
    let subscriber = PtoRecorder::new();
    let pto_events = subscriber.events();

    test(model.clone(), |handle| {
        spawn(async move {
            // allow for 1 RTT worth of data and then drop all packet after
            // the client gets an initial ACK from the server
            delay(delay_time * 2).await;
            model.set_drop_rate(1.0);
        });

        let mut server = Server::builder()
            .with_io(handle.builder().build()?)?
            .with_tls(SERVER_CERTS)?
            .start()?;

        let addr = server.local_addr()?;
        spawn(async move {
            if let Some(conn) = server.accept().await {
                delay(Duration::from_secs(10)).await;
                let _ = conn;
            }
        });

        let client = Client::builder()
            .with_io(handle.builder().build().unwrap())?
            .with_tls(certificates::CERT_PEM)?
            .with_event(subscriber)?
            .start()?;

        primary::spawn(async move {
            let connect = Connect::new(addr).with_server_name("localhost");
            let conn = client.connect(connect).await.unwrap();

            delay(Duration::from_secs(10)).await;
            let _ = conn;
        });

        Ok(addr)
    })
    .unwrap();

    let mut pto_events = pto_events.lock().unwrap();

    // assert that sufficient recovery events were captured
    let pto_len = pto_events.len();
    assert!(pto_len > 10);
    // the last recovery event is fired after we discard the handshake space so ignore it
    pto_events.truncate(pto_len - 1);

    let pto_count: u32 = *pto_events
        .iter()
        .reduce(|prev, new| {
            // assert that the value is monotonically increasing
            assert!(new >= prev, "prev_value {}, new_value {}", prev, new);
            new
        })
        .unwrap();

    // assert that the final pto_count increased to some large value over the
    // duration of the test
    assert!(
        pto_count > 5,
        "delay: {:?}. pto_count: {}",
        delay_time,
        pto_count
    );
}

// TODO: https://github.com/aws/s2n-quic/issues/1726
//
// The rustls tls provider is used on windows and has different
// build options than s2n-tls. We should build the rustls provider with
// mTLS enabled and remove the `cfg(target_os("windows"))`.
#[cfg(not(target_os = "windows"))]
#[test]
fn mtls_happy_case() {
    use crate::provider::event::events::HandshakeStatus;

    let model = Model::default();
    model.set_delay(Duration::from_millis(50));
    const LEN: usize = 1000;

    let server_subscriber = HandshakeStatusRecorder::new();
    let server_events = server_subscriber.clone();
    let client_subscriber = HandshakeStatusRecorder::new();
    let client_events = client_subscriber.clone();

    test(model, |handle| {
        let server_tls = build_server_mtls_provider(certificates::MTLS_CA_CERT)?;
        let mut server = Server::builder()
            .with_io(handle.builder().build()?)?
            .with_tls(server_tls)?
            .with_event(server_subscriber)?
            .start()?;

        let addr = server.local_addr()?;
        spawn(async move {
            let mut conn = server.accept().await.unwrap();
            let mut stream = conn.open_bidirectional_stream().await.unwrap();
            stream.send(vec![42; LEN].into()).await.unwrap();
            stream.flush().await.unwrap();
        });

        let client_tls = build_client_mtls_provider(certificates::MTLS_CA_CERT)?;
        let client = Client::builder()
            .with_io(handle.builder().build().unwrap())?
            .with_tls(client_tls)?
            .with_event(client_subscriber)?
            .start()?;

        primary::spawn(async move {
            let connect = Connect::new(addr).with_server_name("localhost");
            let mut conn = client.connect(connect).await.unwrap();
            let mut stream = conn.accept_bidirectional_stream().await.unwrap().unwrap();

            let mut recv_len = 0;
            while let Some(chunk) = stream.receive().await.unwrap() {
                recv_len += chunk.len();
            }
            assert_eq!(LEN, recv_len);
        });

        Ok(addr)
    })
    .unwrap();

    let server_handshake_complete =
        server_events.any(|x| matches!(x.status, HandshakeStatus::Complete { .. }));
    let server_handshake_confirmed =
        server_events.any(|x| matches!(x.status, HandshakeStatus::Confirmed { .. }));
    let client_handshake_complete =
        client_events.any(|x| matches!(x.status, HandshakeStatus::Complete { .. }));
    let client_handshake_confirmed =
        client_events.any(|x| matches!(x.status, HandshakeStatus::Confirmed { .. }));

    // assert handshake success for both the sever and client
    assert!(server_handshake_complete);
    assert!(server_handshake_confirmed);
    assert!(client_handshake_complete);
    assert!(client_handshake_confirmed);
}

// TODO: https://github.com/aws/s2n-quic/issues/1726
#[cfg(not(target_os = "windows"))]
#[test]
fn mtls_auth_failure() {
    use crate::provider::event::events::HandshakeStatus;
    use std::sync::atomic::{AtomicBool, Ordering};

    let model = Model::default();
    model.set_delay(Duration::from_millis(50));

    let server_subscriber = HandshakeStatusRecorder::new();
    let server_events = server_subscriber.clone();
    let client_subscriber = HandshakeStatusRecorder::new();
    let client_events = client_subscriber.clone();

    // check that server attempts to accept but rejects a connection
    let server_connection_closed = Arc::new(AtomicBool::new(false));
    let server_connection_closed_clone = server_connection_closed.clone();

    test(model, |handle| {
        let server_tls = build_server_mtls_provider(certificates::UNTRUSTED_CERT_PEM)?;
        let mut server = Server::builder()
            .with_io(handle.builder().build()?)?
            .with_tls(server_tls)?
            .with_event(server_subscriber)?
            .start()?;

        let addr = server.local_addr()?;
        spawn(async move {
            match server.accept().await {
                Some(_) => {
                    panic!("connection should not be accepted on auth failure");
                }
                None => {
                    assert!(
                        !server_connection_closed_clone.swap(true, Ordering::SeqCst),
                        "confirm that this is only called once"
                    );
                }
            }
        });

        let client_tls = build_client_mtls_provider(certificates::MTLS_CA_CERT)?;
        let client = Client::builder()
            .with_io(handle.builder().build().unwrap())?
            .with_tls(client_tls)?
            .with_event(client_subscriber)?
            .start()?;

        primary::spawn(async move {
            let connect = Connect::new(addr).with_server_name("localhost");
            let mut conn = client.connect(connect).await.unwrap();
            let stream_result = conn.accept_bidirectional_stream().await;

            assert!(stream_result.is_err(), "handshake should fail");
        });

        Ok(addr)
    })
    .unwrap();

    let server_handshake_complete =
        server_events.any(|x| matches!(x.status, HandshakeStatus::Complete { .. }));
    let server_handshake_confirmed =
        server_events.any(|x| matches!(x.status, HandshakeStatus::Confirmed { .. }));
    let client_handshake_complete =
        client_events.any(|x| matches!(x.status, HandshakeStatus::Complete { .. }));
    let client_handshake_confirmed =
        client_events.any(|x| matches!(x.status, HandshakeStatus::Confirmed { .. }));

    // expect server handshake to fail
    assert!(!server_handshake_complete);
    assert!(!server_handshake_confirmed);

    // expect the client handshake status to 'Complete' but not 'Confirmed'. We
    // expect server's client-certificate-authentication (mTLS) to fail, which
    // happens after client TLS handshake 'Complete'
    assert!(client_handshake_complete);
    assert!(!client_handshake_confirmed);

    // confirm server connection was attempted but failed
    assert!(server_connection_closed.load(Ordering::SeqCst));
}


// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

use crate::{
    client::{Client, ClientProviders, DefaultProviders},
    provider::*,
};

/// A builder for configuring [`Client`] providers
#[derive(Debug)]
pub struct Builder<Providers>(pub(crate) Providers);

impl Default for Builder<DefaultProviders> {
    fn default() -> Self {
        Self(Default::default())
    }
}

impl<Providers: ClientProviders> Builder<Providers> {
    impl_provider_method!(
        /// Sets the connection ID provider for the [`Client`]
        ///
        /// # Examples
        ///
        /// Uses the default connection ID provider with the default configuration.
        ///
        /// ```rust,no_run
        /// # use std::{error::Error, time::Duration};
        /// use s2n_quic::{Client, provider::connection_id};
        /// #
        /// # #[tokio::main]
        /// # async fn main() -> Result<(), Box<dyn Error>> {
        /// let client = Client::builder()
        ///     .with_connection_id(connection_id::Default::default())?
        ///     .start()?;
        /// #
        /// #    Ok(())
        /// # }
        /// ```
        ///
        /// Sets a custom connection ID provider for the client
        ///
        /// ```rust,no_run
        /// # use std::{error::Error, time::Duration};
        /// use s2n_quic::{Client, provider::connection_id};
        /// use rand::prelude::*;
        /// #
        /// # #[tokio::main]
        /// # async fn main() -> Result<(), Box<dyn Error>> {
        /// struct MyConnectionIdFormat;
        ///
        /// impl connection_id::Generator for MyConnectionIdFormat {
        ///     fn generate(&mut self, _conn_info: &connection_id::ConnectionInfo) -> connection_id::LocalId {
        ///         let mut id = [0u8; 16];
        ///         rand::thread_rng().fill_bytes(&mut id);
        ///         connection_id::LocalId::try_from_bytes(&id[..]).unwrap()
        ///     }
        /// }
        ///
        /// impl connection_id::Validator for MyConnectionIdFormat {
        ///     fn validate(&self, _conn_info: &connection_id::ConnectionInfo, packet: &[u8]) -> Option<usize> {
        ///         // this connection id format is always 16 bytes
        ///         Some(16)
        ///     }
        /// }
        ///
        /// let client = Client::builder()
        ///     .with_connection_id(MyConnectionIdFormat)?
        ///     .start()?;
        /// #
        /// #    Ok(())
        /// # }
        /// ```
        with_connection_id,
        connection_id,
        ClientProviders
    );

    impl_provider_method!(
        /// Sets the stateless reset token provider for the [`Client`]
        ///
        /// # Examples
        ///
        /// Sets a custom stateless reset token provider for the client
        ///
        /// ```rust,ignore
        /// # use std::error::Error;
        /// use s2n_quic::Client;
        /// #
        /// # #[tokio::main]
        /// # async fn main() -> Result<(), Box<dyn Error>> {
        /// let client = Client::builder()
        ///     .with_stateless_reset_token(MyStatelessResetTokenGenerator::new())?
        ///     .start()?;
        /// #
        /// #    Ok(())
        /// # }
        /// ```
        with_stateless_reset_token,
        stateless_reset_token,
        ClientProviders
    );

    impl_provider_method!(
        /// Sets the limits provider for the [`Client`]
        ///
        /// # Examples
        ///
        /// Sets the max idle time, while inheriting the remaining default limits
        ///
        /// ```rust,no_run
        /// # use std::{error::Error, time::Duration};
        /// use s2n_quic::{Client, provider::limits};
        /// #
        /// # #[tokio::main]
        /// # async fn main() -> Result<(), Box<dyn Error>> {
        /// let client = Client::builder()
        ///     .with_limits(limits::Default::default())?
        ///     .start()?;
        /// #
        /// #    Ok(())
        /// # }
        /// ```
        with_limits,
        limits,
        ClientProviders
    );

    impl_provider_method!(
        /// Sets the event provider for the [`Client`]
        ///
        /// # Examples
        ///
        /// Sets a custom event subscriber for the client
        ///
        /// ```rust,ignore
        /// # use std::{error::Error, time::Duration};
        /// use s2n_quic::Client;
        /// #
        /// # #[tokio::main]
        /// # async fn main() -> Result<(), Box<dyn Error>> {
        /// let client = Client::builder()
        ///     .with_event(MyEventLogger::new())?
        ///     .start()?;
        /// #
        /// #    Ok(())
        /// # }
        /// ```
        with_event,
        event,
        ClientProviders
    );

    impl_provider_method!(
        /// Sets the IO provider for the [`Client`]
        ///
        /// # Examples
        ///
        /// Opens a socket on an address chosen by the operating system
        ///
        /// ```rust,no_run
        /// # use std::error::Error;
        /// use s2n_quic::Client;
        /// #
        /// # #[tokio::main]
        /// # async fn main() -> Result<(), Box<dyn Error>> {
        /// let client = Client::builder()
        ///     .with_io("0.0.0.0:0")?
        ///     .start()?;
        /// #
        /// #    Ok(())
        /// # }
        /// ```
        ///
        /// Configures a socket with the provided `Builder`
        ///
        /// ```rust,no_run
        /// # use std::error::Error;
        /// use s2n_quic::{Client, provider::io::tokio::Builder as IoBuilder};
        /// use std::net::ToSocketAddrs;
        /// #
        /// # #[tokio::main]
        /// # async fn main() -> Result<(), Box<dyn Error>> {
        /// let addr = "0.0.0.0:0".to_socket_addrs()?.next().unwrap();
        ///
        /// let io = IoBuilder::default()
        ///     .with_receive_address(addr)?
        ///     .build()?;
        ///
        /// let client = Client::builder()
        ///     .with_io(io)?
        ///     .start()?;
        /// #
        /// #    Ok(())
        /// # }
        /// ```
        with_io,
        io,
        ClientProviders
    );

    impl_provider_method!(
        /// Sets the TLS provider for the [`Client`]
        ///
        /// # Examples
        ///
        /// The default TLS provider and configuration will be used with the
        /// path to the private key.
        ///
        /// ```rust,no_run
        /// # use std::{error::Error, path::Path};
        /// # use s2n_quic::Client;
        /// #
        /// # #[tokio::main]
        /// # async fn main() -> Result<(), Box<dyn Error>> {
        /// let client = Client::builder()
        ///     .with_tls(Path::new("./certs/cert.pem"))?
        ///     .start()?;
        /// #
        /// #    Ok(())
        /// # }
        /// ```
        ///
        /// Sets the TLS provider to a TLS client builder
        ///
        /// ```rust,ignore
        /// # use std::{error::Error, path::Path};
        /// # use s2n_quic::Client;
        /// #
        /// # #[tokio::main]
        /// # async fn main() -> Result<(), Box<dyn Error>> {
        /// let tls = s2n::tls::Client::builder()
        ///     .with_certificate(Path::new("./certs/cert.pem"))?
        ///     .with_security_policy(s2n::tls::security_policy::S2N_20190802)?;
        ///
        /// let client = Client::builder()
        ///     .with_tls(tls)?
        ///     .start()?;
        /// #
        /// #    Ok(())
        /// # }
        /// ```
        with_tls,
        tls,
        ClientProviders
    );

    #[cfg(any(test, feature = "unstable-provider-packet-interceptor"))]
    impl_provider_method!(
        /// Sets the packet interceptor provider for the [`Client`]
        with_packet_interceptor,
        packet_interceptor,
        ClientProviders
    );

    #[cfg(any(test, feature = "unstable-provider-random"))]
    impl_provider_method!(
        /// Sets the random provider for the [`Client`]
        with_random,
        random,
        ClientProviders
    );

    #[cfg(feature = "unstable-provider-datagram")]
    impl_provider_method!(
        /// Sets the datagram provider for the [`Client`]
        with_datagram,
        datagram,
        ClientProviders
    );

    impl_provider_method!(
        /// Sets the congestion controller provider for the [`Client`]
        with_congestion_controller,
        congestion_controller,
        ClientProviders
    );

    /// Starts the [`Client`] with the configured providers
    ///
    /// # Examples
    ///
    /// ```rust,no_run
    /// # use std::{error::Error, path::Path};
    /// # use s2n_quic::Client;
    /// #
    /// # #[tokio::main]
    /// # async fn main() -> Result<(), Box<dyn Error>> {
    /// let client = Client::builder()
    ///     .with_tls(Path::new("./certs/cert.pem"))?
    ///     .with_io("0.0.0.0:0")?
    ///     .start()?;
    /// #
    /// #    Ok(())
    /// # }
    /// ```
    pub fn start(self) -> Result<Client, StartError> {
        self.0.build().start()
    }
}


// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

use super::*;
use core::marker::PhantomData;
use s2n_quic_core::{connection::id::Generator, crypto, path};
use s2n_quic_transport::{connection, endpoint, stream};

impl_providers_state! {
    #[derive(Debug, Default)]
    struct Providers {
        congestion_controller: CongestionController,
        connection_close_formatter: ConnectionCloseFormatter,
        connection_id: ConnectionID,
        packet_interceptor: PacketInterceptor,
        stateless_reset_token: StatelessResetToken,
        random: Random,
        event: Event,
        limits: Limits,
        io: IO,
        sync: Sync,
        tls: Tls,
        datagram: Datagram,
    }

    /// Opaque trait containing all of the configured providers
    trait ClientProviders {}
}

impl<
        CongestionController: congestion_controller::Provider,
        ConnectionCloseFormatter: connection_close_formatter::Provider,
        ConnectionID: connection_id::Provider,
        PacketInterceptor: packet_interceptor::Provider,
        StatelessResetToken: stateless_reset_token::Provider,
        Random: random::Provider,
        Event: event::Provider,
        Limits: limits::Provider,
        IO: io::Provider,
        Sync: sync::Provider,
        Tls: tls::Provider,
        Datagram: datagram::Provider,
    >
    Providers<
        CongestionController,
        ConnectionCloseFormatter,
        ConnectionID,
        PacketInterceptor,
        StatelessResetToken,
        Random,
        Event,
        Limits,
        IO,
        Sync,
        Tls,
        Datagram,
    >
{
    pub fn start(self) -> Result<Client, StartError> {
        let Self {
            congestion_controller,
            connection_close_formatter,
            connection_id,
            packet_interceptor,
            stateless_reset_token,
            random,
            event,
            limits,
            io,
            sync,
            tls,
            datagram,
        } = self;

        let congestion_controller = congestion_controller.start().map_err(StartError::new)?;
        let connection_close_formatter = connection_close_formatter
            .start()
            .map_err(StartError::new)?;
        let connection_id = connection_id.start().map_err(StartError::new)?;
        let packet_interceptor = packet_interceptor.start().map_err(StartError::new)?;
        let stateless_reset_token = stateless_reset_token.start().map_err(StartError::new)?;
        let random = random.start().map_err(StartError::new)?;
        let endpoint_limits = EndpointLimits;
        let limits = limits.start().map_err(StartError::new)?;
        let event = event.start().map_err(StartError::new)?;
        let token = Token;
        let sync = sync.start().map_err(StartError::new)?;
        let path_migration = PathMigration;
        let tls = tls.start_client().map_err(StartError::new)?;
        let datagram = datagram.start().map_err(StartError::new)?;

        // Validate providers
        // TODO: Add more validation https://github.com/aws/s2n-quic/issues/285
        let valid_lifetime = |lifetime| {
            (connection::id::MIN_LIFETIME..=connection::id::MAX_LIFETIME).contains(&lifetime)
        };
        if connection_id
            .lifetime()
            .map_or(false, |lifetime| !valid_lifetime(lifetime))
        {
            return Err(StartError::new(connection::id::Error::InvalidLifetime));
        };

        let endpoint_config = EndpointConfig {
            congestion_controller,
            connection_close_formatter,
            connection_id,
            packet_interceptor,
            stateless_reset_token,
            random,
            endpoint_limits,
            event,
            limits,
            sync,
            tls,
            token,
            path_handle: PhantomData,
            path_migration,
            datagram,
        };

        let (endpoint, connector) = endpoint::Endpoint::new_client(endpoint_config);

        // Start the IO last
        let local_addr = io.start(endpoint).map_err(StartError::new)?;

        Ok(Client {
            connector,
            local_addr,
        })
    }
}

#[derive(Debug)]
struct EndpointLimits;

impl endpoint::limits::Limiter for EndpointLimits {
    fn on_connection_attempt(
        &mut self,
        _info: &endpoint::limits::ConnectionAttempt,
    ) -> endpoint::limits::Outcome {
        unreachable!("endpoint limits should not be used with clients")
    }
}

#[derive(Debug)]
struct Token;

impl crate::provider::address_token::Format for Token {
    const TOKEN_LEN: usize = 0;

    fn generate_new_token(
        &mut self,
        _context: &mut s2n_quic_core::token::Context<'_>,
        _source_connection_id: &s2n_quic_core::connection::LocalId,
        _output_buffer: &mut [u8],
    ) -> Option<()> {
        unreachable!("tokens should not be generated with clients")
    }

    fn generate_retry_token(
        &mut self,
        _context: &mut s2n_quic_core::token::Context<'_>,
        _original_destination_connection_id: &s2n_quic_core::connection::InitialId,
        _output_buffer: &mut [u8],
    ) -> Option<()> {
        unreachable!("tokens should not be generated with clients")
    }

    fn validate_token(
        &mut self,
        _context: &mut s2n_quic_core::token::Context<'_>,
        _token: &[u8],
    ) -> Option<s2n_quic_core::connection::InitialId> {
        unreachable!("tokens should not be generated with clients")
    }
}

#[derive(Debug)]
struct PathMigration;

impl crate::provider::path_migration::Validator for PathMigration {
    fn on_migration_attempt(
        &mut self,
        _attempt: &path::migration::Attempt,
    ) -> path::migration::Outcome {
        unreachable!("path migration should not be validated with clients")
    }
}

#[allow(dead_code)] // don't warn on unused providers for now
struct EndpointConfig<
    CongestionController,
    ConnectionCloseFormatter,
    ConnectionID,
    PacketInterceptor,
    PathHandle,
    StatelessResetToken,
    Random,
    Event,
    Limits,
    Sync,
    Tls,
    Datagram,
> {
    congestion_controller: CongestionController,
    connection_close_formatter: ConnectionCloseFormatter,
    connection_id: ConnectionID,
    packet_interceptor: PacketInterceptor,
    stateless_reset_token: StatelessResetToken,
    random: Random,
    endpoint_limits: EndpointLimits,
    event: Event,
    limits: Limits,
    sync: Sync,
    tls: Tls,
    token: Token,
    path_handle: PhantomData<PathHandle>,
    path_migration: PathMigration,
    datagram: Datagram,
}

impl<
        CongestionController: congestion_controller::Endpoint,
        ConnectionCloseFormatter: connection_close_formatter::Formatter,
        ConnectionID: connection::id::Format,
        PacketInterceptor: packet_interceptor::PacketInterceptor,
        PathHandle: path::Handle,
        StatelessResetToken: stateless_reset_token::Generator,
        Random: s2n_quic_core::random::Generator,
        Event: s2n_quic_core::event::Subscriber,
        Limits: s2n_quic_core::connection::limits::Limiter,
        Sync,
        Tls: crypto::tls::Endpoint,
        Datagram: s2n_quic_core::datagram::Endpoint,
    > core::fmt::Debug
    for EndpointConfig<
        CongestionController,
        ConnectionCloseFormatter,
        ConnectionID,
        PacketInterceptor,
        PathHandle,
        StatelessResetToken,
        Random,
        Event,
        Limits,
        Sync,
        Tls,
        Datagram,
    >
{
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("ClientConfig").finish()
    }
}

impl<
        CongestionController: congestion_controller::Endpoint,
        ConnectionCloseFormatter: connection_close_formatter::Formatter,
        ConnectionID: connection::id::Format,
        PacketInterceptor: packet_interceptor::PacketInterceptor,
        PathHandle: path::Handle,
        StatelessResetToken: stateless_reset_token::Generator,
        Random: s2n_quic_core::random::Generator,
        Event: s2n_quic_core::event::Subscriber,
        Limits: s2n_quic_core::connection::limits::Limiter,
        Sync: 'static + Send,
        Tls: crypto::tls::Endpoint,
        Datagram: s2n_quic_core::datagram::Endpoint,
    > endpoint::Config
    for EndpointConfig<
        CongestionController,
        ConnectionCloseFormatter,
        ConnectionID,
        PacketInterceptor,
        PathHandle,
        StatelessResetToken,
        Random,
        Event,
        Limits,
        Sync,
        Tls,
        Datagram,
    >
{
    type ConnectionIdFormat = ConnectionID;
    type ConnectionCloseFormatter = ConnectionCloseFormatter;
    type PathHandle = PathHandle;
    type StatelessResetTokenGenerator = StatelessResetToken;
    type RandomGenerator = Random;
    type Connection = connection::Implementation<Self>;
    // TODO allow users to specify another lock type
    type ConnectionLock = std::sync::Mutex<Self::Connection>;
    type CongestionControllerEndpoint = CongestionController;
    type EndpointLimits = EndpointLimits;
    type EventSubscriber = Event;
    type TLSEndpoint = Tls;
    type TokenFormat = Token;
    type ConnectionLimits = Limits;
    type StreamManager = stream::DefaultStreamManager;
    type PathMigrationValidator = PathMigration;
    type PacketInterceptor = PacketInterceptor;
    type DatagramEndpoint = Datagram;

    const ENDPOINT_TYPE: endpoint::Type = endpoint::Type::Client;

    fn context(&mut self) -> endpoint::Context<Self> {
        endpoint::Context {
            congestion_controller: &mut self.congestion_controller,
            connection_close_formatter: &mut self.connection_close_formatter,
            connection_id_format: &mut self.connection_id,
            packet_interceptor: &mut self.packet_interceptor,
            stateless_reset_token_generator: &mut self.stateless_reset_token,
            random_generator: &mut self.random,
            tls: &mut self.tls,
            endpoint_limits: &mut self.endpoint_limits,
            token: &mut self.token,
            connection_limits: &mut self.limits,
            event_subscriber: &mut self.event,
            path_migration: &mut self.path_migration,
            datagram: &mut self.datagram,
        }
    }
}


// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

use crate::{
    connection,
    stream::{BidirectionalStream, PeerStream, ReceiveStream},
};

macro_rules! impl_accept_api {
    () => {
        /// Accepts an incoming [`PeerStream`](`crate::stream::PeerStream`)
        ///
        /// The method will return
        /// - `Ok(Some(stream)))` if a [`PeerStream`](`crate::stream::PeerStream`) was accepted
        /// - `Ok(None)` if the connection was closed without an error
        /// - `Err(stream_error)` if no stream could be accepted due to an error
        ///
        /// # Examples
        ///
        /// ```rust,no_run
        /// # async fn test() -> s2n_quic::stream::Result<()> {
        /// #   let mut acceptor: s2n_quic::connection::StreamAcceptor = todo!();
        /// #
        /// while let Some(stream) = acceptor.accept().await? {
        ///     println!("Stream opened from {:?}", stream.connection().remote_addr());
        /// }
        /// #
        /// #   Ok(())
        /// # }
        /// ```
        #[inline]
        pub async fn accept(
            &mut self,
        ) -> crate::connection::Result<Option<crate::stream::PeerStream>> {
            futures::future::poll_fn(|cx| self.poll_accept(cx)).await
        }

        /// Poll for accepting an incoming [`PeerStream`](`crate::stream::PeerStream`)
        ///
        /// The method will return
        /// - `Poll::Ready(Ok(Some(stream)))` if a [`PeerStream`](`crate::stream::PeerStream`) was accepted
        /// - `Poll::Ready(Ok(None))` if the connection was closed without an error
        /// - `Poll::Ready(Err(stream_error))` if no stream could be accepted due to an error
        /// - `Poll::Pending` if no new [`PeerStream`](`crate::stream::PeerStream`) was accepted by the connection yet.
        ///   In this case the caller must retry calling [`Self::poll_accept`].
        ///   For this purpose the method will save the [`core::task::Waker`]
        ///   which is provided as part of the [`core::task::Context`] parameter, and notify it
        ///   as soon as retrying the method will yield a different result.
        #[inline]
        pub fn poll_accept(
            &mut self,
            cx: &mut core::task::Context,
        ) -> core::task::Poll<crate::connection::Result<Option<crate::stream::PeerStream>>> {
            use s2n_quic_core::stream::StreamType;
            use $crate::stream::{BidirectionalStream, ReceiveStream};

            Ok(
                futures::ready!(self.0.poll_accept(None, cx))?.map(|stream| {
                    match stream.id().stream_type() {
                        StreamType::Unidirectional => ReceiveStream::new(stream.into()).into(),
                        StreamType::Bidirectional => BidirectionalStream::new(stream).into(),
                    }
                }),
            )
            .into()
        }

        impl_accept_bidirectional_api!();
        impl_accept_receive_api!();
    };
}

macro_rules! impl_accept_bidirectional_api {
    () => {
        /// Accepts an incoming [`BidirectionalStream`](`crate::stream::BidirectionalStream`)
        ///
        /// The method will return
        /// - `Ok(Some(stream)))` if a [`BidirectionalStream`](`crate::stream::BidirectionalStream`) was accepted
        /// - `Ok(None)` if the connection was closed without an error
        /// - `Err(stream_error)` if no stream could be accepted due to an error
        ///
        /// # Examples
        ///
        /// ```rust,no_run
        /// # async fn test() -> s2n_quic::stream::Result<()> {
        /// #   let mut acceptor: s2n_quic::connection::StreamAcceptor = todo!();
        /// #
        /// while let Ok(Some(mut stream)) = acceptor.accept_bidirectional_stream().await {
        ///     println!("Stream opened from {:?}", stream.connection().remote_addr());
        /// }
        /// #
        /// #   Ok(())
        /// # }
        /// ```
        #[inline]
        pub async fn accept_bidirectional_stream(
            &mut self,
        ) -> $crate::connection::Result<Option<$crate::stream::BidirectionalStream>> {
            futures::future::poll_fn(|cx| self.poll_accept_bidirectional_stream(cx)).await
        }

        /// Poll for accepting an incoming [`BidirectionalStream`](`crate::stream::BidirectionalStream`)
        ///
        /// The method will return
        /// - `Poll::Ready(Ok(Some(stream)))` if a [`BidirectionalStream`](`crate::stream::BidirectionalStream`) was accepted
        /// - `Poll::Ready(Ok(None))` if the connection was closed without an error
        /// - `Poll::Ready(Err(stream_error))` if no stream could be accepted due to an error
        /// - `Poll::Pending` if no new [`BidirectionalStream`](`crate::stream::BidirectionalStream`) was accepted by the connection yet.
        ///   In this case the caller must retry calling [`Self::poll_accept_bidirectional_stream`].
        ///   For this purpose the method will save the [`core::task::Waker`]
        ///   which is provided as part of the [`core::task::Context`] parameter, and notify it
        ///   as soon as retrying the method will yield a different result.
        #[inline]
        pub fn poll_accept_bidirectional_stream(
            &mut self,
            cx: &mut core::task::Context,
        ) -> core::task::Poll<$crate::connection::Result<Option<$crate::stream::BidirectionalStream>>> {
            Ok(
                futures::ready!(self
                    .0
                    .poll_accept(Some(s2n_quic_core::stream::StreamType::Bidirectional), cx)
                )?.map($crate::stream::BidirectionalStream::new)
            ).into()
        }
    };
}

macro_rules! impl_accept_receive_api {
    () => {
        /// Accepts an incoming [`ReceiveStream`](`crate::stream::ReceiveStream`)
        ///
        /// The method will return
        /// - `Ok(Some(stream)))` if a [`ReceiveStream`](`crate::stream::ReceiveStream`) was accepted
        /// - `Ok(None)` if the connection was closed without an error
        /// - `Err(stream_error)` if no stream could be accepted due to an error
        ///
        /// # Examples
        ///
        /// ```rust,no_run
        /// # async fn test() -> s2n_quic::stream::Result<()> {
        /// #   let mut acceptor: s2n_quic::connection::StreamAcceptor = todo!();
        /// #
        /// while let Ok(Some(mut stream)) = acceptor.accept_receive_stream().await {
        ///     println!("Stream opened from {:?}", stream.connection().remote_addr());
        /// }
        /// #
        /// #   Ok(())
        /// # }
        /// ```
        #[inline]
        pub async fn accept_receive_stream(
            &mut self,
        ) -> $crate::connection::Result<Option<$crate::stream::ReceiveStream>> {
            futures::future::poll_fn(|cx| self.poll_accept_receive_stream(cx)).await
        }

        /// Poll for accepting an incoming [`ReceiveStream`](`crate::stream::ReceiveStream`)
        ///
        /// The method will return
        /// - `Poll::Ready(Ok(Some(stream)))` if a [`ReceiveStream`](`crate::stream::ReceiveStream`) was accepted
        /// - `Poll::Ready(Ok(None))` if the connection was closed without an error
        /// - `Poll::Ready(Err(stream_error))` if no stream could be accepted due to an error
        /// - `Poll::Pending` if no new [`ReceiveStream`](`crate::stream::ReceiveStream`) was accepted by the connection yet.
        ///   In this case the caller must retry calling [`Self::poll_accept_receive_stream`].
        ///   For this purpose the method will save the [`core::task::Waker`]
        ///   which is provided as part of the [`core::task::Context`] parameter, and notify it
        ///   as soon as retrying the method will yield a different result.
        #[inline]
        pub fn poll_accept_receive_stream(
            &mut self,
            cx: &mut core::task::Context,
        ) -> core::task::Poll<$crate::connection::Result<Option<$crate::stream::ReceiveStream>>> {
            Ok(futures::ready!(self
                .0
                .poll_accept(Some(s2n_quic_core::stream::StreamType::Unidirectional), cx))?
            .map(|stream| $crate::stream::ReceiveStream::new(stream.into())))
            .into()
        }
    };
}

#[derive(Debug)]
pub struct StreamAcceptor(pub(crate) s2n_quic_transport::connection::Connection);

impl StreamAcceptor {
    impl_accept_api!();

    /// Splits the [`StreamAcceptor`] into [`BidirectionalStreamAcceptor`] and [`ReceiveStreamAcceptor`] halves
    ///
    /// # Examples
    ///
    /// ```rust,no_run
    /// # use bytes::Bytes;
    /// # async fn test() -> s2n_quic::stream::Result<()> {
    /// #   let connection: s2n_quic::connection::Connection = todo!();
    /// #
    /// let (handle, acceptor) = connection.split();
    /// let (mut bidi, mut recv) = acceptor.split();
    ///
    /// tokio::spawn(async move {
    ///     while let Ok(Some(mut stream)) = bidi.accept_bidirectional_stream().await {
    ///       println!("Bidirectional stream opened from {:?}", stream.connection().remote_addr());
    ///     }
    /// });
    ///
    /// tokio::spawn(async move {
    ///     while let Ok(Some(mut stream)) = recv.accept_receive_stream().await {
    ///       println!("Receive stream opened from {:?}", stream.connection().remote_addr());
    ///     }
    /// });
    /// #
    /// #   Ok(())
    /// # }
    /// ```
    #[inline]
    pub fn split(self) -> (BidirectionalStreamAcceptor, ReceiveStreamAcceptor) {
        let bidi = BidirectionalStreamAcceptor(self.0.clone());
        let recv = ReceiveStreamAcceptor(self.0);
        (bidi, recv)
    }
}

impl futures::stream::Stream for StreamAcceptor {
    type Item = connection::Result<PeerStream>;

    #[inline]
    fn poll_next(
        mut self: core::pin::Pin<&mut Self>,
        cx: &mut core::task::Context<'_>,
    ) -> core::task::Poll<Option<Self::Item>> {
        match futures::ready!(self.poll_accept(cx)) {
            Ok(Some(stream)) => Some(Ok(stream)),
            Ok(None) => None,
            Err(err) => Some(Err(err)),
        }
        .into()
    }
}

#[derive(Debug)]
pub struct BidirectionalStreamAcceptor(s2n_quic_transport::connection::Connection);

impl BidirectionalStreamAcceptor {
    impl_accept_bidirectional_api!();
}

impl futures::stream::Stream for BidirectionalStreamAcceptor {
    type Item = connection::Result<BidirectionalStream>;

    #[inline]
    fn poll_next(
        mut self: core::pin::Pin<&mut Self>,
        cx: &mut core::task::Context<'_>,
    ) -> core::task::Poll<Option<Self::Item>> {
        match futures::ready!(self.poll_accept_bidirectional_stream(cx)) {
            Ok(Some(stream)) => Some(Ok(stream)),
            Ok(None) => None,
            Err(err) => Some(Err(err)),
        }
        .into()
    }
}

#[derive(Debug)]
pub struct ReceiveStreamAcceptor(s2n_quic_transport::connection::Connection);

impl ReceiveStreamAcceptor {
    impl_accept_receive_api!();
}

impl futures::stream::Stream for ReceiveStreamAcceptor {
    type Item = connection::Result<ReceiveStream>;

    #[inline]
    fn poll_next(
        mut self: core::pin::Pin<&mut Self>,
        cx: &mut core::task::Context<'_>,
    ) -> core::task::Poll<Option<Self::Item>> {
        match futures::ready!(self.poll_accept_receive_stream(cx)) {
            Ok(Some(stream)) => Some(Ok(stream)),
            Ok(None) => None,
            Err(err) => Some(Err(err)),
        }
        .into()
    }
}


// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

macro_rules! impl_handle_api {
    (| $handle:ident, $dispatch:ident | $dispatch_body:expr) => {
        /// Opens a new [`LocalStream`](`crate::stream::LocalStream`) with a specific type
        ///
        /// The method will return
        ///  - `Ok(stream)` if a stream of the requested type was opened
        ///  - `Err(stream_error)` if the stream could not be opened due to an error
        ///
        /// # Examples
        ///
        /// ```rust,no_run
        /// # async fn test() -> s2n_quic::connection::Result<()> {
        /// #   use s2n_quic::stream;
        /// #   let mut handle: s2n_quic::connection::Handle = todo!();
        /// #
        /// while let Ok(stream) = handle.open_stream(stream::Type::Bidirectional).await {
        ///     println!("Stream opened from {:?}", stream.connection().remote_addr());
        /// }
        /// #
        /// #   Ok(())
        /// # }
        /// ```
        #[inline]
        pub async fn open_stream(
            &mut self,
            stream_type: $crate::stream::Type,
        ) -> $crate::connection::Result<$crate::stream::LocalStream> {
            futures::future::poll_fn(|cx| self.poll_open_stream(stream_type, cx)).await
        }

        /// Polls opening a [`LocalStream`](`crate::stream::LocalStream`) with a specific type
        ///
        /// The method will return
        /// - `Poll::Ready(Ok(stream))` if a stream of the requested type was opened
        /// - `Poll::Ready(Err(stream_error))` if the stream could not be opened due to an error
        /// - `Poll::Pending` if the stream has not been opened yet
        #[inline]
        pub fn poll_open_stream(
            &mut self,
            stream_type: $crate::stream::Type,
            cx: &mut core::task::Context,
        ) -> core::task::Poll<$crate::connection::Result<$crate::stream::LocalStream>> {
            use s2n_quic_core::stream::StreamType;
            use $crate::stream::{BidirectionalStream, SendStream};

            Ok(
                match futures::ready!(self.0.poll_open_stream(stream_type, cx))? {
                    stream if stream_type == StreamType::Unidirectional => {
                        SendStream::new(stream.into()).into()
                    }
                    stream => BidirectionalStream::new(stream).into(),
                },
            )
            .into()
        }

        /// Opens a new [`BidirectionalStream`](`crate::stream::BidirectionalStream`)
        ///
        /// The method will return
        ///  - `Ok(stream)` if a bidirectional stream was opened
        ///  - `Err(stream_error)` if the stream could not be opened due to an error
        ///
        /// # Examples
        ///
        /// ```rust,no_run
        /// # async fn test() -> s2n_quic::connection::Result<()> {
        /// #   let mut handle: s2n_quic::connection::Handle = todo!();
        /// #
        /// while let Ok(mut stream) = handle.open_bidirectional_stream().await {
        ///     println!("Stream opened from {:?}", stream.connection().remote_addr());
        /// }
        /// #
        /// #   Ok(())
        /// # }
        /// ```
        #[inline]
        pub async fn open_bidirectional_stream(
            &mut self,
        ) -> $crate::connection::Result<$crate::stream::BidirectionalStream> {
            futures::future::poll_fn(|cx| self.poll_open_bidirectional_stream(cx)).await
        }

        /// Polls opening a [`BidirectionalStream`](`crate::stream::BidirectionalStream`)
        ///
        /// The method will return
        /// - `Poll::Ready(Ok(stream))` if a bidirectional stream was opened
        /// - `Poll::Ready(Err(stream_error))` if the stream could not be opened due to an error
        /// - `Poll::Pending` if the stream has not been opened yet
        #[inline]
        pub fn poll_open_bidirectional_stream(
            &mut self,
            cx: &mut core::task::Context,
        ) -> core::task::Poll<$crate::connection::Result<$crate::stream::BidirectionalStream>> {
            use s2n_quic_core::stream::StreamType;
            use $crate::stream::BidirectionalStream;

            let stream = futures::ready!(self.0.poll_open_stream(StreamType::Bidirectional, cx))?;

            Ok(BidirectionalStream::new(stream)).into()
        }

        /// Opens a [`SendStream`](`crate::stream::SendStream`)
        ///
        /// # Examples
        ///
        /// ```rust,no_run
        /// # async fn test() -> s2n_quic::connection::Result<()> {
        /// #   let mut connection: s2n_quic::connection::Handle = todo!();
        /// #
        /// let stream = connection.open_send_stream().await?;
        /// println!("Send stream opened with id: {}", stream.id());
        /// #
        /// #   Ok(())
        /// # }
        /// ```
        #[inline]
        pub async fn open_send_stream(
            &mut self,
        ) -> $crate::connection::Result<$crate::stream::SendStream> {
            futures::future::poll_fn(|cx| self.poll_open_send_stream(cx)).await
        }

        /// Polls opening a [`SendStream`](`crate::stream::SendStream`)
        #[inline]
        pub fn poll_open_send_stream(
            &mut self,
            cx: &mut core::task::Context,
        ) -> core::task::Poll<$crate::connection::Result<$crate::stream::SendStream>> {
            use s2n_quic_core::stream::StreamType;
            use $crate::stream::SendStream;

            let stream = futures::ready!(self.0.poll_open_stream(StreamType::Unidirectional, cx))?;

            Ok(SendStream::new(stream.into())).into()
        }

        /// Returns the local address that this connection is bound to.
        #[inline]
        pub fn local_addr(&self) -> $crate::connection::Result<std::net::SocketAddr> {
            self.0.local_address().map(std::net::SocketAddr::from)
        }

        /// Returns the remote address that this connection is connected to.
        #[inline]
        pub fn remote_addr(&self) -> $crate::connection::Result<std::net::SocketAddr> {
            self.0.remote_address().map(std::net::SocketAddr::from)
        }

        /// Returns the negotiated server name the connection is using.
        #[inline]
        pub fn server_name(&self) -> $crate::connection::Result<Option<$crate::server::Name>> {
            self.0.server_name()
        }

        /// Returns the negotiated application protocol the connection is using
        #[inline]
        pub fn application_protocol(&self) -> $crate::connection::Result<::bytes::Bytes> {
            self.0.application_protocol()
        }

        /// Returns the internal identifier for the [`Connection`](`crate::Connection`)
        ///
        /// Note: This internal identifier is not the same as the connection ID included in packet
        /// headers as described in [QUIC Transport RFC](https://www.rfc-editor.org/rfc/rfc9000.html#name-connection-id)
        #[inline]
        pub fn id(&self) -> u64 {
            self.0.id()
        }

        /// Sends a Ping frame to the peer
        #[inline]
        pub fn ping(&mut self) -> $crate::connection::Result<()> {
            self.0.ping()
        }

        /// Enables or disables the connection to actively keep the connection alive with the peer
        ///
        /// This can be useful for maintaining connections beyond the configured idle timeout. The
        /// connection will continue to be held open until the keep alive is disabled or the
        /// connection is no longer able to be maintained due to connectivity.
        #[inline]
        pub fn keep_alive(&mut self, enabled: bool) -> $crate::connection::Result<()> {
            self.0.keep_alive(enabled)
        }

        /// Closes the Connection with the provided error code
        ///
        /// This will immediately terminate all outstanding streams.
        ///
        /// # Examples
        ///
        /// ```rust,no_run
        /// # async fn test() -> s2n_quic::connection::Result<()> {
        /// #   let mut connection: s2n_quic::connection::Handle = todo!();
        /// #
        /// const MY_ERROR_CODE:u32 = 99;
        /// connection.close(MY_ERROR_CODE.into());
        /// #
        /// #   Ok(())
        /// # }
        /// ```
        #[inline]
        pub fn close(&self, error_code: $crate::application::Error) {
            self.0.close(error_code)
        }

        /// API for querying the connection's
        /// [`Subscriber::ConnectionContext`](crate::provider::event::Subscriber::ConnectionContext).
        ///
        /// The ConnectionContext provides a mechanism for users to provide a custom
        /// type and update it on each event. The query APIs (check
        /// [`Self::query_event_context_mut`] for mutable version) provide a way to inspect the
        /// ConnectionContext outside of events.
        ///
        /// This function takes a `FnOnce(&EventContext) -> Outcome`, where `EventContext`
        /// represents the type of `ConnectionContext`. If the `EventContext` type matches
        /// any of the types of the configured Subscriber's context, the query is executed
        /// and `Ok(Outcome)` is returned, else
        /// `Err(`[`query::Error`](s2n_quic_core::query::Error)`)`.
        ///
        /// Given that it is possible to compose Subscriber, which can have different
        /// ConnectionContext types, this function traverses all Subscribers, executes
        /// and short-circuiting on the first match.
        ///
        /// # Examples
        ///
        /// ```no_run
        /// use s2n_quic::{provider::event::{events, query, Subscriber}, Connection, Server};
        ///
        /// struct MySubscriber{}
        ///
        /// impl Subscriber for MySubscriber {
        ///     type ConnectionContext = MyEventContext;
        ///     fn create_connection_context(
        ///        &mut self, _meta: &events::ConnectionMeta,
        ///        _info: &events::ConnectionInfo,
        ///     ) -> Self::ConnectionContext {
        ///         MyEventContext { request: 0 }
        ///     }
        ///  }
        ///
        /// #[derive(Clone, Copy)]
        /// pub struct MyEventContext {
        ///     request: u64,
        /// }
        ///
        /// let mut server = Server::builder()
        ///   .with_event(MySubscriber {}).unwrap()
        ///   .start().unwrap();
        /// # let connection: Connection = todo!();
        ///
        /// let outcome: Result<MyEventContext, query::Error> = connection
        ///     .query_event_context(|event_context: &MyEventContext| *event_context);
        ///
        /// match outcome {
        ///     Ok(event_context) => {
        ///         // `MyEventContext` matched a Subscriber::ConnectionContext and the
        ///         // query executed.
        ///         //
        ///         // use the value event_context for logging, etc..
        ///     }
        ///     Err(query::Error::ConnectionLockPoisoned) => {
        ///         // The query did not execute because of a connection error.
        ///         //
        ///         // log an error, panic, etc..
        ///     }
        ///     Err(query::Error::ContextTypeMismatch) => {
        ///         // `MyEventContext` failed to match any Subscriber::ConnectionContext
        ///         // and the query did not execute.
        ///         //
        ///         // log an error, panic, etc..
        ///     }
        ///     Err(_) => {
        ///         // We encountered an unknown error so handle it generically, e.g. log,
        ///         // panic, etc.
        ///     }
        /// }
        /// ```
        ///
        /// # Traverse order
        /// Let's demonstrate the traversal order for matching on ConnectionContext in the
        /// example below. We provide a composed Subscriber type (Foo, Bar), where both
        /// Foo and Bar have a ConnectionContext type of `u64`. The query traverse order
        /// is as follows:
        /// - `(Foo::ConnectionContext, Bar::ConnectionContext)`
        /// - `Foo::ConnectionContext`
        /// - `Bar::ConnectionContext`
        ///
        /// Note: In this example the type `u64` will always match `Foo::u64` and
        /// `Bar::u64` will never be matched. If this is undesirable, applications should
        /// make unique associated `ConnectionContext`s by creating new types.
        ///
        /// ```no_run
        /// use s2n_quic::{provider::event::{events, Subscriber}, Connection, Server};
        ///
        /// struct Foo {}
        ///
        /// impl Subscriber for Foo {
        ///    type ConnectionContext = u64;
        ///    fn create_connection_context(
        ///        &mut self, _meta: &events::ConnectionMeta,
        ///        _info: &events::ConnectionInfo,
        ///    ) -> Self::ConnectionContext { 0 }
        /// }
        ///
        /// struct Bar {}
        ///
        /// impl Subscriber for Bar {
        ///    type ConnectionContext = u64;
        ///    fn create_connection_context(
        ///        &mut self, _meta: &events::ConnectionMeta,
        ///        _info: &events::ConnectionInfo,
        ///    ) -> Self::ConnectionContext { 0 }
        /// }
        ///
        /// let mut server = Server::builder()
        ///     .with_event((Foo {}, Bar {})).unwrap()
        ///     .start().unwrap();
        /// # let connection: Connection = todo!();
        ///
        /// // Matches Foo.
        /// //
        /// // Note: Because the `ConnectionContext` type is the same for
        /// // both `Foo` and `Bar`, only `Foo`'s context will be matched.
        /// let _ = connection.query_event_context(|ctx: &u64| *ctx );
        ///
        /// // Matches (Foo, Bar).
        /// let _ = connection.query_event_context(|ctx: &(u64, u64)| ctx.0 );
        /// ```
        pub fn query_event_context<Query, EventContext, Outcome>(
            &self,
            query: Query,
        ) -> core::result::Result<Outcome, s2n_quic_core::query::Error>
        where
            Query: FnOnce(&EventContext) -> Outcome,
            EventContext: 'static,
        {
            use s2n_quic_core::query;
            let mut query = query::Once::new(query);

            self.0
                .query_event_context(&mut query)
                .map_err(|_| query::Error::ConnectionLockPoisoned)?;

            query.into()
        }

        /// API for querying the connection's
        /// [`Subscriber::ConnectionContext`](crate::provider::event::Subscriber::ConnectionContext).
        ///
        /// Similar to [`Self::query_event_context`] but provides
        /// mutable access to `ConnectionContext`.
        ///
        /// ```ignore
        /// let outcome = connection
        ///     .query_event_context(
        ///         |event_context: &MyEventContext| event_context.request += 1
        ///     );
        /// ```
        pub fn query_event_context_mut<Query, EventContext, Outcome>(
            &mut self,
            query: Query,
        ) -> core::result::Result<Outcome, s2n_quic_core::query::Error>
        where
            Query: FnOnce(&mut EventContext) -> Outcome,
            EventContext: 'static,
        {
            use s2n_quic_core::query;
            let mut query = query::Once::new_mut(query);

            self.0
                .query_event_context_mut(&mut query)
                .map_err(|_| query::Error::ConnectionLockPoisoned)?;

            query.into()
        }

        /// API for querying the connection's datagram endpoint.
        ///
        ///  Provides mutable access to `Sender` or `Receiver`.
        ///
        /// ```ignore
        /// let outcome = connection
        ///     .datagram_mut(
        ///         |sender: &MySender| sender.send_datagram(Bytes::from_static(&[1, 2, 3]));
        ///     );
        /// ```
        pub fn datagram_mut<Query, ProviderType, Outcome>(
            &self,
            query: Query,
        ) -> core::result::Result<Outcome, s2n_quic_core::query::Error>
        where
            Query: FnOnce(&mut ProviderType) -> Outcome,
            ProviderType: 'static,
        {
            use s2n_quic_core::query;
            let mut query = query::Once::new_mut(query);

            self.0
                .datagram_mut(&mut query)
                .map_err(|_| query::Error::ConnectionLockPoisoned)?;

            query.into()
        }
    };
}

#[derive(Clone, Debug)]
pub struct Handle(pub(crate) s2n_quic_transport::connection::Connection);

impl Handle {
    impl_handle_api!(|handle, call| call!(handle));
}


// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

use cfg_if::cfg_if;
pub use s2n_quic_core::recovery::congestion_controller::Endpoint;

/// Provides congestion controller support for an endpoint
pub trait Provider {
    type Endpoint: Endpoint;
    type Error: 'static + core::fmt::Display;

    fn start(self) -> Result<Self::Endpoint, Self::Error>;
}

cfg_if! {
    if #[cfg(feature = "unstable-congestion-controller")] {
        // Export the types needed to implement the CongestionController trait
        pub use s2n_quic_core::{
            random::Generator as RandomGenerator,
            recovery::{congestion_controller::{CongestionController, PathInfo, Publisher}, RttEstimator},
            time::Timestamp,
        };
    }
}

pub use s2n_quic_core::recovery::{bbr::Endpoint as Bbr, cubic::Endpoint as Cubic};
pub type Default = Cubic;

impl_provider_utils!();

impl<T: Endpoint> Provider for T {
    type Endpoint = T;
    type Error = core::convert::Infallible;

    fn start(self) -> Result<Self::Endpoint, Self::Error> {
        Ok(self)
    }
}


// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

//! Provider for formatting CONNECTION_CLOSE frames

pub use s2n_quic_core::connection::close::*;

/// Provider for formatting CONNECTION_CLOSE frames
pub trait Provider: 'static {
    type Formatter: 'static + Formatter;
    type Error: core::fmt::Display;

    /// Starts the token provider
    fn start(self) -> Result<Self::Formatter, Self::Error>;
}

pub type Default = Production;

/// Implement Provider for all implementations of Formatter
impl<T: Formatter> Provider for T {
    type Formatter = T;
    type Error = core::convert::Infallible;

    fn start(self) -> Result<Self::Formatter, Self::Error> {
        Ok(self)
    }
}

impl_provider_utils!();