-
Notifications
You must be signed in to change notification settings - Fork 10
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Support both rustls and native-tls #7
Conversation
mod compile_time_checks { | ||
#[cfg(not(any(feature = "rustls", feature = "native-tls")))] | ||
compile_error!("tls-listener requires either the `rustls` or `native-tls` feature"); | ||
|
||
#[cfg(all(feature = "rustls", feature = "native-tls"))] | ||
compile_error!("The `rustls` and `native-tls` features in tls-listener are mutually exclusive"); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These produce nice readable errors in the case that a user hasn't got their features setup right
@@ -78,23 +92,39 @@ pin_project! { | |||
/// Builder for `TlsListener`. | |||
#[derive(Clone)] | |||
pub struct Builder { | |||
server_config: Arc<ServerConfig>, | |||
acceptor: TlsAcceptor, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unfortunately native_tls doesn't have an infallible equivalent to ServerConfig, so for simplicity and consistency it made sense just to make the user provide a TlsAcceptor in both instances
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It might be worth have a from_config
function on the builder that accepts ServerConfig
if the rustls
option is active.
Although ServerConfig
in rustls is somewhat equivalent to native_tls::TlsAcceptor
, and it might make sense to keep the rustls behavior the same, and have the native-tls
feature take a native_tls::TlsAcceptor
instead of a tokio_native_tls::TlsAcceptor
.
You're probably already aware, but changing this breaks backwards compatibility.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have changed the functions to accept T: Into<TlsAcceptor>
, such that a user can pass in any of native_tls::TlsAcceptor
or tokio_native_tls::TlsAcceptor
, tokio_rustls::TlsAcceptor
or Arc<ServerConfig>
.
It remains a very minor breaking change but it is documented in the CHANGELOG now.
/// Wraps errors from either the listener or the TLS Acceptor | ||
#[derive(Debug, Error)] | ||
pub enum Error<A: std::error::Error> { | ||
/// An error that arose from the listener ([AsyncAccept::Error]) | ||
#[error("{0}")] | ||
ListenerError(#[source] A), | ||
/// An error that occurred during the TLS accept handshake | ||
#[cfg(feature = "rustls")] | ||
#[error("{0}")] | ||
TlsAcceptError(#[source] std::io::Error), | ||
/// An error that occurred during the TLS accept handshake | ||
#[cfg(feature = "native-tls")] | ||
#[error("{0}")] | ||
TlsAcceptError(#[source] tokio_native_tls::native_tls::Error), | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Before we were relying on the fact that all implementations of AsyncAccept happened to return io::Error, and also that the TLS accept future also returned an io::Error, but we can't do that anymore.
This also gets rid of the slightly odd downcasting from below and replaces the error with a type parameter
[features] | ||
default = ["tokio-net"] | ||
rustls = ["tokio-rustls"] | ||
native-tls = ["tokio-native-tls"] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let me know if you would like rustls to be in the defaults instead? I think this way is more consistent with the rust ecosystem from what I have seen of other libraries
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There are several backwards incompatible changes here. I'm not entirely opposed to them, especially since as far as I know this crate isn't particularly widely used, but I think it is at least worth documenting them in the README, or CHANGELOG (which doesn't exist yet).
The backwards incompatible changes I was able to find are:
- Changing the signature of
TlsListener::new
andbuilder
to taketokio_rustls::TlsAcceptor
instead ofrustls::ServerConfig
. - Changing the error type
- Adding
Send + Sync + 'static
bound on the connection type (I'd like to avoid this if possible for rustls at least)
src/lib.rs
Outdated
} | ||
} | ||
|
||
impl<A> TlsListener<A> | ||
where | ||
A: AsyncAccept, | ||
A::Connection: AsyncRead + AsyncWrite + Unpin, | ||
A::Error: Into<Box<dyn Error + Send + Sync>>, | ||
A::Connection: AsyncRead + AsyncWrite + Unpin + Sync + Send + 'static, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are the Sync, Send, and 'static bounds required for tls-native? Could we only add those bounds if the tls-native feature is enabled?
I couldn't find a super easy way to do that, but I'd also prefer not to have to add these bounds if we don't need to. And adding these bounds is backwards incompatible.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The Send + Sync is my mistake, those are not actually needed, so I have removed them.
I will do some further investigating around the 'static
constraint which is currently needed for the native-tls to compile:
error[E0310]: the associated type `<A as AsyncAccept>::Connection` may not live long enough
--> src\lib.rs:161:29
|
161 | ... Box::pin(async move { tls.accept(conn).await }),
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= help: consider adding an explicit lifetime bound `<A as AsyncAccept>::Connection: 'static`...
= note: ...so that the type `impl futures_util::Future<Output = [async output]>` will meet its required lifetime bounds
For more information about this error, try `rustc --explain E0310`.
@@ -78,23 +92,39 @@ pin_project! { | |||
/// Builder for `TlsListener`. | |||
#[derive(Clone)] | |||
pub struct Builder { | |||
server_config: Arc<ServerConfig>, | |||
acceptor: TlsAcceptor, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It might be worth have a from_config
function on the builder that accepts ServerConfig
if the rustls
option is active.
Although ServerConfig
in rustls is somewhat equivalent to native_tls::TlsAcceptor
, and it might make sense to keep the rustls behavior the same, and have the native-tls
feature take a native_tls::TlsAcceptor
instead of a tokio_native_tls::TlsAcceptor
.
You're probably already aware, but changing this breaks backwards compatibility.
features = ["server"] | ||
optional = true | ||
thiserror = "1.0.30" | ||
tokio = { version = "1.0", features = ["time"] } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a bug here such that if you try to use this crate in another project that doesn't itself also import tokio/time, then it will fail to compile.
You wouldn't have noticed this in the tests because of the weird way Cargo works, where it combines all the feature flag from different targets (i.e. the library + dev dependencies in this case) (something like this rust-lang/cargo#3620 )
I built on this to create this branch which uses generics so that it can support both rustls and native-tls features simultaneously. @jacob-pro I'd be curious to know what you think of that. |
Thanks @tmccombs that looks really good. To be honest I don't hugely mind how it is implemented, I thought I would have ago at doing it with conditional compilation just because that is how I had seen other crates do it, but the generics does actually allow us to get around those trait bounds nicely. If you are happy to merge your branch - feel free to either ammend or close this PR |
Following up on #6