diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index ab144ffaf..257d20c9d 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -63,7 +63,10 @@ jobs: with: tool: protoc@${{ env.PROTOC_VERSION }} - uses: Swatinem/rust-cache@v2 - - run: cargo hack udeps --workspace --each-feature ${{ matrix.option }} + - run: cargo hack udeps --workspace --exclude-features tls --each-feature ${{ matrix.option }} + - run: cargo udeps --package tonic --features tls,transport + - run: cargo udeps --package tonic --features tls,server + - run: cargo udeps --package tonic --features tls,channel check: runs-on: ${{ matrix.os }} @@ -81,6 +84,8 @@ jobs: - uses: Swatinem/rust-cache@v2 - name: Check features run: cargo hack check --workspace --no-private --each-feature --no-dev-deps + - name: Check tonic feature powerset + run: cargo hack check --package tonic --feature-powerset --depth 2 - name: Check all targets run: cargo check --workspace --all-targets --all-features diff --git a/tonic/Cargo.toml b/tonic/Cargo.toml index cb77bcaed..beb559f63 100644 --- a/tonic/Cargo.toml +++ b/tonic/Cargo.toml @@ -26,29 +26,31 @@ version = "0.11.0" codegen = ["dep:async-trait"] gzip = ["dep:flate2"] zstd = ["dep:zstd"] -default = ["channel", "codegen", "prost"] +default = ["transport", "codegen", "prost"] prost = ["dep:prost"] -tls = ["dep:rustls-pemfile", "transport", "dep:tokio-rustls", "dep:tokio", "tokio?/rt", "tokio?/macros"] +tls = ["dep:rustls-pemfile", "dep:tokio-rustls", "dep:tokio", "tokio?/rt", "tokio?/macros"] tls-roots = ["tls-roots-common", "dep:rustls-native-certs"] tls-roots-common = ["tls", "channel"] tls-webpki-roots = ["tls-roots-common", "dep:webpki-roots"] router = ["dep:axum"] -transport = [ +server = [ "router", "dep:async-stream", "dep:h2", - "dep:hyper", "dep:hyper-util", + "dep:hyper", "hyper?/server", + "dep:hyper-util", "hyper-util?/service", "hyper-util?/server-auto", "dep:socket2", "dep:tokio", "tokio?/macros", "tokio?/net", "tokio?/time", "dep:tower", "tower?/util", "tower?/limit", ] channel = [ - "transport", "dep:hyper", "hyper?/client", "dep:hyper-util", "hyper-util?/client-legacy", - "dep:tower", "tower?/balance", "tower?/buffer", "tower?/discover", "tower?/load", "tower?/make", + "dep:tower", "tower?/balance", "tower?/buffer", "tower?/discover", "tower?/limit", + "dep:tokio", "tokio?/time", "dep:hyper-timeout", ] +transport = ["server", "channel"] # [[bench]] # name = "bench_main" @@ -76,8 +78,8 @@ async-trait = {version = "0.1.13", optional = true} # transport async-stream = {version = "0.3", optional = true} h2 = {version = "0.4", optional = true} -hyper = {version = "1", features = ["http1", "http2", "server"], optional = true} -hyper-util = { version = ">=0.1.4, <0.2", features = ["service", "server-auto", "tokio"], optional = true } +hyper = {version = "1", features = ["http1", "http2"], optional = true} +hyper-util = { version = ">=0.1.4, <0.2", features = ["tokio"], optional = true } socket2 = { version = ">=0.4.7, <0.6.0", optional = true, features = ["all"] } tokio = {version = "1", default-features = false, optional = true} tokio-stream = { version = "0.1", features = ["net"] } diff --git a/tonic/src/lib.rs b/tonic/src/lib.rs index 1ca537e52..db22bf56e 100644 --- a/tonic/src/lib.rs +++ b/tonic/src/lib.rs @@ -16,9 +16,10 @@ //! //! # Feature Flags //! -//! - `transport`: Enables just the full featured server portion of the `channel` feature. -//! - `channel`: Enables the fully featured, batteries included client and server +//! - `transport`: Enables the fully featured, batteries included client and server //! implementation based on [`hyper`], [`tower`] and [`tokio`]. Enabled by default. +//! - `server`: Enables just the full featured server portion of the `transport` feature. +//! - `channel`: Enables just the full featured channel portion of the `transport` feature. //! - `codegen`: Enables all the required exports and optional dependencies required //! for [`tonic-build`]. Enabled by default. //! - `tls`: Enables the `rustls` based TLS options for the `transport` feature. Not @@ -100,8 +101,8 @@ pub mod metadata; pub mod server; pub mod service; -#[cfg(feature = "transport")] -#[cfg_attr(docsrs, doc(cfg(feature = "transport")))] +#[cfg(any(feature = "server", feature = "channel"))] +#[cfg_attr(docsrs, doc(cfg(any(feature = "server", feature = "channel"))))] pub mod transport; mod extensions; diff --git a/tonic/src/request.rs b/tonic/src/request.rs index e9fe610b8..36957530b 100644 --- a/tonic/src/request.rs +++ b/tonic/src/request.rs @@ -1,15 +1,15 @@ use crate::metadata::{MetadataMap, MetadataValue}; -#[cfg(feature = "transport")] +#[cfg(feature = "server")] use crate::transport::server::TcpConnectInfo; -#[cfg(feature = "tls")] +#[cfg(all(feature = "server", feature = "tls"))] use crate::transport::server::TlsConnectInfo; use http::Extensions; -#[cfg(feature = "transport")] +#[cfg(feature = "server")] use std::net::SocketAddr; -#[cfg(feature = "tls")] +#[cfg(all(feature = "server", feature = "tls"))] use std::sync::Arc; use std::time::Duration; -#[cfg(feature = "tls")] +#[cfg(all(feature = "server", feature = "tls"))] use tokio_rustls::rustls::pki_types::CertificateDer; use tokio_stream::Stream; @@ -211,8 +211,8 @@ impl Request { /// This will return `None` if the `IO` type used /// does not implement `Connected` or when using a unix domain socket. /// This currently only works on the server side. - #[cfg(feature = "transport")] - #[cfg_attr(docsrs, doc(cfg(feature = "transport")))] + #[cfg(feature = "server")] + #[cfg_attr(docsrs, doc(cfg(feature = "server")))] pub fn local_addr(&self) -> Option { let addr = self .extensions() @@ -234,8 +234,8 @@ impl Request { /// This will return `None` if the `IO` type used /// does not implement `Connected` or when using a unix domain socket. /// This currently only works on the server side. - #[cfg(feature = "transport")] - #[cfg_attr(docsrs, doc(cfg(feature = "transport")))] + #[cfg(feature = "server")] + #[cfg_attr(docsrs, doc(cfg(feature = "server")))] pub fn remote_addr(&self) -> Option { let addr = self .extensions() @@ -258,8 +258,8 @@ impl Request { /// and is mostly used for mTLS. This currently only returns /// `Some` on the server side of the `transport` server with /// TLS enabled connections. - #[cfg(feature = "tls")] - #[cfg_attr(docsrs, doc(cfg(feature = "tls")))] + #[cfg(all(feature = "server", feature = "tls"))] + #[cfg_attr(docsrs, doc(all(feature = "server", feature = "tls")))] pub fn peer_certs(&self) -> Option>>> { self.extensions() .get::>() diff --git a/tonic/src/service/router.rs b/tonic/src/service/router.rs index c0dea4df8..690d28fc2 100644 --- a/tonic/src/service/router.rs +++ b/tonic/src/service/router.rs @@ -149,7 +149,7 @@ struct AxumBodyService { service: S, } -pub(crate) type BoxFuture<'a, T> = Pin + Send + 'a>>; +type BoxFuture<'a, T> = Pin + Send + 'a>>; impl Service> for AxumBodyService where diff --git a/tonic/src/status.rs b/tonic/src/status.rs index 6ded020ef..4f0c2bf90 100644 --- a/tonic/src/status.rs +++ b/tonic/src/status.rs @@ -305,7 +305,6 @@ impl Status { Status::new(Code::Unauthenticated, message) } - #[cfg_attr(not(feature = "transport"), allow(dead_code))] pub(crate) fn from_error_generic( err: impl Into>, ) -> Status { @@ -316,7 +315,6 @@ impl Status { /// /// Inspects the error source chain for recognizable errors, including statuses, HTTP2, and /// hyper, and attempts to maps them to a `Status`, or else returns an Unknown `Status`. - #[cfg_attr(not(feature = "transport"), allow(dead_code))] pub fn from_error(err: Box) -> Status { Status::try_from_error(err).unwrap_or_else(|err| { let mut status = Status::new(Code::Unknown, err.to_string()); @@ -342,7 +340,7 @@ impl Status { Err(err) => err, }; - #[cfg(feature = "transport")] + #[cfg(feature = "server")] let err = match err.downcast::() { Ok(h2) => { return Ok(Status::from_h2_error(h2)); @@ -359,7 +357,7 @@ impl Status { } // FIXME: bubble this into `transport` and expose generic http2 reasons. - #[cfg(feature = "transport")] + #[cfg(feature = "server")] fn from_h2_error(err: Box) -> Status { let code = Self::code_from_h2(&err); @@ -368,7 +366,7 @@ impl Status { status } - #[cfg(feature = "transport")] + #[cfg(feature = "server")] fn code_from_h2(err: &h2::Error) -> Code { // See https://github.com/grpc/grpc/blob/3977c30/doc/PROTOCOL-HTTP2.md#errors match err.reason() { @@ -388,7 +386,7 @@ impl Status { } } - #[cfg(feature = "transport")] + #[cfg(feature = "server")] fn to_h2_error(&self) -> h2::Error { // conservatively transform to h2 error codes... let reason = match self.code { @@ -404,7 +402,7 @@ impl Status { /// /// Returns Some if there's a way to handle the error, or None if the information from this /// hyper error, but perhaps not its source, should be ignored. - #[cfg(feature = "transport")] + #[cfg(any(feature = "server", feature = "channel"))] fn from_hyper_error(err: &hyper::Error) -> Option { // is_timeout results from hyper's keep-alive logic // (https://docs.rs/hyper/0.14.11/src/hyper/error.rs.html#192-194). Per the grpc spec @@ -420,6 +418,7 @@ impl Status { return Some(Status::cancelled(err.to_string())); } + #[cfg(feature = "server")] if let Some(h2_err) = err.source().and_then(|e| e.downcast_ref::()) { let code = Status::code_from_h2(h2_err); let status = Self::new(code, format!("h2 protocol error: {}", err)); @@ -607,7 +606,7 @@ fn find_status_in_source_chain(err: &(dyn Error + 'static)) -> Option { }); } - #[cfg(feature = "transport")] + #[cfg(feature = "server")] if let Some(timeout) = err.downcast_ref::() { return Some(Status::cancelled(timeout.to_string())); } @@ -624,7 +623,7 @@ fn find_status_in_source_chain(err: &(dyn Error + 'static)) -> Option { return Some(Status::unavailable(connect.to_string())); } - #[cfg(feature = "transport")] + #[cfg(any(feature = "server", feature = "channel"))] if let Some(hyper) = err .downcast_ref::() .and_then(Status::from_hyper_error) @@ -671,14 +670,14 @@ fn invalid_header_value_byte(err: Error) -> Status { ) } -#[cfg(feature = "transport")] +#[cfg(feature = "server")] impl From for Status { fn from(err: h2::Error) -> Self { Status::from_h2_error(Box::new(err)) } } -#[cfg(feature = "transport")] +#[cfg(feature = "server")] impl From for h2::Error { fn from(status: Status) -> Self { status.to_h2_error() @@ -927,7 +926,7 @@ mod tests { } #[test] - #[cfg(feature = "transport")] + #[cfg(feature = "server")] fn from_error_h2() { use std::error::Error as _; @@ -944,7 +943,7 @@ mod tests { } #[test] - #[cfg(feature = "transport")] + #[cfg(feature = "server")] fn to_h2_error() { let orig = Status::new(Code::Cancelled, "stop eet!"); let err = orig.to_h2_error(); diff --git a/tonic/src/transport/channel/mod.rs b/tonic/src/transport/channel/mod.rs index 16a6ec160..35212b1d1 100644 --- a/tonic/src/transport/channel/mod.rs +++ b/tonic/src/transport/channel/mod.rs @@ -36,6 +36,7 @@ use tower::{ Service, }; +type BoxFuture<'a, T> = Pin + Send + 'a>>; type Svc = Either, Response, crate::Error>>; const DEFAULT_BUFFER_SIZE: usize = 1024; @@ -186,7 +187,7 @@ impl Channel { D: Discover + Unpin + Send + 'static, D::Error: Into, D::Key: Hash + Send + Clone, - E: Executor> + Send + Sync + 'static, + E: Executor> + Send + Sync + 'static, { let svc = Balance::new(discover); diff --git a/tonic/src/transport/channel/service/add_origin.rs b/tonic/src/transport/channel/service/add_origin.rs index a25485937..dc205411b 100644 --- a/tonic/src/transport/channel/service/add_origin.rs +++ b/tonic/src/transport/channel/service/add_origin.rs @@ -1,4 +1,4 @@ -use crate::transport::BoxFuture; +use crate::transport::channel::BoxFuture; use http::uri::Authority; use http::uri::Scheme; use http::{Request, Uri}; diff --git a/tonic/src/transport/channel/service/connection.rs b/tonic/src/transport/channel/service/connection.rs index 2c34f3ed7..3d8ce0d1e 100644 --- a/tonic/src/transport/channel/service/connection.rs +++ b/tonic/src/transport/channel/service/connection.rs @@ -1,7 +1,7 @@ use super::{AddOrigin, Reconnect, SharedExec, UserAgent}; use crate::{ body::{boxed, BoxBody}, - transport::{service::GrpcTimeout, BoxFuture, Endpoint}, + transport::{channel::BoxFuture, service::GrpcTimeout, Endpoint}, }; use http::Uri; use hyper::rt; diff --git a/tonic/src/transport/channel/service/connector.rs b/tonic/src/transport/channel/service/connector.rs index 0bfe0a518..47628daf6 100644 --- a/tonic/src/transport/channel/service/connector.rs +++ b/tonic/src/transport/channel/service/connector.rs @@ -1,7 +1,7 @@ use super::BoxedIo; #[cfg(feature = "tls")] use super::TlsConnector; -use crate::transport::BoxFuture; +use crate::transport::channel::BoxFuture; use http::Uri; use std::fmt; use std::task::{Context, Poll}; diff --git a/tonic/src/transport/channel/service/executor.rs b/tonic/src/transport/channel/service/executor.rs index 7b699c307..2e04049d1 100644 --- a/tonic/src/transport/channel/service/executor.rs +++ b/tonic/src/transport/channel/service/executor.rs @@ -1,4 +1,4 @@ -use crate::transport::BoxFuture; +use crate::transport::channel::BoxFuture; use std::{future::Future, sync::Arc}; pub(crate) use hyper::rt::Executor; diff --git a/tonic/src/transport/mod.rs b/tonic/src/transport/mod.rs index 9ee24a557..62e754ab2 100644 --- a/tonic/src/transport/mod.rs +++ b/tonic/src/transport/mod.rs @@ -91,6 +91,7 @@ #[cfg(feature = "channel")] pub mod channel; +#[cfg(feature = "server")] pub mod server; mod error; @@ -104,6 +105,7 @@ mod tls; pub use self::channel::{Channel, Endpoint}; pub use self::error::Error; #[doc(inline)] +#[cfg(feature = "server")] pub use self::server::Server; #[doc(inline)] pub use self::service::grpc_timeout::TimeoutExpired; @@ -111,6 +113,8 @@ pub use self::service::grpc_timeout::TimeoutExpired; #[cfg(feature = "tls")] #[cfg_attr(docsrs, doc(cfg(feature = "tls")))] pub use self::tls::Certificate; +#[cfg(feature = "server")] +#[cfg_attr(docsrs, doc(cfg(feature = "server")))] pub use axum::{body::Body as AxumBoxBody, Router as AxumRouter}; pub use hyper::{body::Body, Uri}; #[cfg(feature = "tls")] @@ -119,12 +123,9 @@ pub use tokio_rustls::rustls::pki_types::CertificateDer; #[cfg(all(feature = "channel", feature = "tls"))] #[cfg_attr(docsrs, doc(cfg(all(feature = "channel", feature = "tls"))))] pub use self::channel::ClientTlsConfig; -#[cfg(feature = "tls")] -#[cfg_attr(docsrs, doc(cfg(feature = "tls")))] +#[cfg(all(feature = "server", feature = "tls"))] +#[cfg_attr(docsrs, doc(all(feature = "server", feature = "tls")))] pub use self::server::ServerTlsConfig; #[cfg(feature = "tls")] #[cfg_attr(docsrs, doc(cfg(feature = "tls")))] pub use self::tls::Identity; - -#[cfg(feature = "channel")] -use crate::service::router::BoxFuture; diff --git a/tonic/src/transport/server/incoming.rs b/tonic/src/transport/server/incoming.rs index 7f5f76c25..75f40650e 100644 --- a/tonic/src/transport/server/incoming.rs +++ b/tonic/src/transport/server/incoming.rs @@ -1,5 +1,4 @@ -use super::{Connected, Server}; -use crate::transport::service::ServerIo; +use super::{service::ServerIo, Connected, Server}; use std::{ net::{SocketAddr, TcpListener as StdTcpListener}, pin::{pin, Pin}, diff --git a/tonic/src/transport/server/mod.rs b/tonic/src/transport/server/mod.rs index 730551494..ab9afe6f3 100644 --- a/tonic/src/transport/server/mod.rs +++ b/tonic/src/transport/server/mod.rs @@ -3,6 +3,7 @@ mod conn; mod incoming; mod recover_error; +mod service; #[cfg(feature = "tls")] #[cfg_attr(docsrs, doc(cfg(feature = "tls")))] mod tls; @@ -27,7 +28,7 @@ pub use tls::ServerTlsConfig; pub use conn::TlsConnectInfo; #[cfg(feature = "tls")] -use super::service::TlsAcceptor; +use self::service::TlsAcceptor; #[cfg(unix)] pub use unix::UdsConnectInfo; @@ -40,8 +41,8 @@ pub(crate) use tokio_rustls::server::TlsStream; #[cfg(feature = "tls")] use crate::transport::Error; -use self::recover_error::RecoverError; -use super::service::{GrpcTimeout, ServerIo}; +use self::{recover_error::RecoverError, service::ServerIo}; +use super::service::GrpcTimeout; use crate::body::{boxed, BoxBody}; use crate::server::NamedService; use bytes::Bytes; diff --git a/tonic/src/transport/service/io.rs b/tonic/src/transport/server/service/io.rs similarity index 100% rename from tonic/src/transport/service/io.rs rename to tonic/src/transport/server/service/io.rs diff --git a/tonic/src/transport/server/service/mod.rs b/tonic/src/transport/server/service/mod.rs new file mode 100644 index 000000000..bcd9bbd73 --- /dev/null +++ b/tonic/src/transport/server/service/mod.rs @@ -0,0 +1,7 @@ +mod io; +pub(crate) use self::io::ServerIo; + +#[cfg(feature = "tls")] +mod tls; +#[cfg(feature = "tls")] +pub(crate) use self::tls::TlsAcceptor; diff --git a/tonic/src/transport/server/service/tls.rs b/tonic/src/transport/server/service/tls.rs new file mode 100644 index 000000000..acae1f611 --- /dev/null +++ b/tonic/src/transport/server/service/tls.rs @@ -0,0 +1,65 @@ +use std::{fmt, io::Cursor, sync::Arc}; + +use tokio::io::{AsyncRead, AsyncWrite}; +use tokio_rustls::{ + rustls::{server::WebPkiClientVerifier, RootCertStore, ServerConfig}, + TlsAcceptor as RustlsAcceptor, +}; + +use crate::transport::{ + server::{Connected, TlsStream}, + service::tls::{add_certs_from_pem, load_identity, ALPN_H2}, + Certificate, Identity, +}; + +#[derive(Clone)] +pub(crate) struct TlsAcceptor { + inner: Arc, +} + +impl TlsAcceptor { + pub(crate) fn new( + identity: Identity, + client_ca_root: Option, + client_auth_optional: bool, + ) -> Result { + let builder = ServerConfig::builder(); + + let builder = match client_ca_root { + None => builder.with_no_client_auth(), + Some(cert) => { + let mut roots = RootCertStore::empty(); + add_certs_from_pem(&mut Cursor::new(cert), &mut roots)?; + let verifier = if client_auth_optional { + WebPkiClientVerifier::builder(roots.into()).allow_unauthenticated() + } else { + WebPkiClientVerifier::builder(roots.into()) + } + .build()?; + builder.with_client_cert_verifier(verifier) + } + }; + + let (cert, key) = load_identity(identity)?; + let mut config = builder.with_single_cert(cert, key)?; + + config.alpn_protocols.push(ALPN_H2.into()); + Ok(Self { + inner: Arc::new(config), + }) + } + + pub(crate) async fn accept(&self, io: IO) -> Result, crate::Error> + where + IO: AsyncRead + AsyncWrite + Connected + Unpin + Send + 'static, + { + let acceptor = RustlsAcceptor::from(self.inner.clone()); + acceptor.accept(io).await.map_err(Into::into) + } +} + +impl fmt::Debug for TlsAcceptor { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("TlsAcceptor").finish() + } +} diff --git a/tonic/src/transport/server/tls.rs b/tonic/src/transport/server/tls.rs index d320b5024..2f329cb89 100644 --- a/tonic/src/transport/server/tls.rs +++ b/tonic/src/transport/server/tls.rs @@ -1,9 +1,8 @@ -use crate::transport::{ - service::TlsAcceptor, - tls::{Certificate, Identity}, -}; use std::fmt; +use super::service::TlsAcceptor; +use crate::transport::tls::{Certificate, Identity}; + /// Configures TLS settings for servers. #[derive(Clone, Default)] pub struct ServerTlsConfig { diff --git a/tonic/src/transport/service/mod.rs b/tonic/src/transport/service/mod.rs index b5904aa07..7f1e3fcae 100644 --- a/tonic/src/transport/service/mod.rs +++ b/tonic/src/transport/service/mod.rs @@ -1,9 +1,5 @@ pub(crate) mod grpc_timeout; -mod io; #[cfg(feature = "tls")] pub(crate) mod tls; pub(crate) use self::grpc_timeout::GrpcTimeout; -pub(crate) use self::io::ServerIo; -#[cfg(feature = "tls")] -pub(crate) use self::tls::TlsAcceptor; diff --git a/tonic/src/transport/service/tls.rs b/tonic/src/transport/service/tls.rs index 7bbf210b3..ea7d1fd6b 100644 --- a/tonic/src/transport/service/tls.rs +++ b/tonic/src/transport/service/tls.rs @@ -1,22 +1,11 @@ -use std::{ - io::Cursor, - {fmt, sync::Arc}, -}; +use std::{fmt, io::Cursor}; -use tokio::io::{AsyncRead, AsyncWrite}; -use tokio_rustls::{ - rustls::{ - pki_types::{CertificateDer, PrivateKeyDer}, - server::WebPkiClientVerifier, - RootCertStore, ServerConfig, - }, - TlsAcceptor as RustlsAcceptor, +use tokio_rustls::rustls::{ + pki_types::{CertificateDer, PrivateKeyDer}, + RootCertStore, }; -use crate::transport::{ - server::{Connected, TlsStream}, - Certificate, Identity, -}; +use crate::transport::Identity; /// h2 alpn in plain format for rustls. pub(crate) const ALPN_H2: &[u8] = b"h2"; @@ -29,58 +18,6 @@ pub(crate) enum TlsError { PrivateKeyParseError, } -#[derive(Clone)] -pub(crate) struct TlsAcceptor { - inner: Arc, -} - -impl TlsAcceptor { - pub(crate) fn new( - identity: Identity, - client_ca_root: Option, - client_auth_optional: bool, - ) -> Result { - let builder = ServerConfig::builder(); - - let builder = match client_ca_root { - None => builder.with_no_client_auth(), - Some(cert) => { - let mut roots = RootCertStore::empty(); - add_certs_from_pem(&mut Cursor::new(cert), &mut roots)?; - let verifier = if client_auth_optional { - WebPkiClientVerifier::builder(roots.into()).allow_unauthenticated() - } else { - WebPkiClientVerifier::builder(roots.into()) - } - .build()?; - builder.with_client_cert_verifier(verifier) - } - }; - - let (cert, key) = load_identity(identity)?; - let mut config = builder.with_single_cert(cert, key)?; - - config.alpn_protocols.push(ALPN_H2.into()); - Ok(Self { - inner: Arc::new(config), - }) - } - - pub(crate) async fn accept(&self, io: IO) -> Result, crate::Error> - where - IO: AsyncRead + AsyncWrite + Connected + Unpin + Send + 'static, - { - let acceptor = RustlsAcceptor::from(self.inner.clone()); - acceptor.accept(io).await.map_err(Into::into) - } -} - -impl fmt::Debug for TlsAcceptor { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("TlsAcceptor").finish() - } -} - impl fmt::Display for TlsError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self {