-
Notifications
You must be signed in to change notification settings - Fork 183
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
Use native-tls on platforms that need it, and remove "tls" feature #391
Conversation
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.
Two teaspoons of bikeshed paint.
Alright, this mostly reflects my latest thinking in #319:
|
This is now complete and ready for a review. I think the result is pretty good! I've rewritten the PR description to reflect the latest state. |
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.
Hey this looks excellent. Very nice improvement on the inner workings of connections!
Are we sure about this removal of the tls
feature? In some respects the (re-)introduction of native-tls
is a pragmatic decision to help users in outdated/odd setups. Compiling without any TLS support is in similar territory – an odd thing to want, but is it right that we say it shouldn't be possible?
Saving compile time is another reason to keep deps slim. If you absolutely don't need TLS, it might be a bit irritating to see those crates compile…
I leave this as "approved", but would like to settle the discussion :)
.github/workflows/test.yml
Outdated
@@ -39,21 +39,19 @@ jobs: | |||
with: | |||
command: doc | |||
# Keep in sync with Cargo.toml's [package.metadata.docs.rs] | |||
args: --no-default-features --no-deps --features "tls json charset cookies socks-proxy" | |||
args: --no-default-features --no-deps --features "native-tls-adapter json charset cookies socks-proxy" |
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.
For brevity and discoverability, I think I prefer this to be 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.
I chose a different name because I thought people coming from 1.x might see a native-tls feature and assume it does the same thing it did in 1.x: turn on native-tls by default globally.
I agree it's good to keep compile times fast, but maintaining the As a way to quantify the maintenance burden, check out the diffs to test.sh and test.yml. Previously we had to test [tls, no-tls] x [every feature]. Now we just have to test [every feature]. Of course the tests are automated, but each combination in that matrix is a potential breakage when modifying code that the authors (us) have to fix. And that maintenance burden is laid out against a fairly small benefit: keeping compile times fast for the small number of users that might explicitly disable TLS. I would be interested in hearing from such users if this turns out to cause significant burden for them, though. |
From reading the changes to Cargo.toml it seems to me that I cannot build ureq without rustls on some platforms like x86/am64? I'm asking because we'd like to use ureq for some parts but need native TLS support (as we depend on OpenSSL for quite some other crypto stuff anyway) and while we do not have anything against rustls (great stuff) we'd like to avoid developers to mix usage of TLS implementations. And, FWIW we also have quite some concern about build time and a major refactoring/restructuring effort going on to improve on it, so being able to pull in as few crates as possible would be really nice for us. So, if I read the change correctly: do you plan to make ureq also use either native/rustls exclusively or would that be too big of a maintenance burden? |
Hi @ThomasLamprecht thanks for weighing in! This is exactly the kind of viewpoint we want to hear. I say the decision here is still up in the air. |
@jsha I pushed a commit reintroducing I think the rustls arch-selection is elegant, but I think Cargo is lacking what we need to take it all the way (rust-lang/cargo#1197). I wanted to see this maintenance burden for myself, so I decided to give it a go and I think you have already solved 99% of the problems we used to have in ureq 1.0 by your refactoring. Specifically:
|
d83e01f
to
6c822e9
Compare
Thanks for the input @ThomasLamprecht. That's useful to know, and a good reason to preserve the ability to build without one or the other. I might phrase it as "enforce at build time that only one TLS implementation is available to the program." Note that you are somewhat vulnerable to your dependencies here: if a dependency wants rustls, you'll still get rustls compiled in. @algesten said:
To clarify: you think we should not do the automatic selection of backend based on architecture? That's a bummer, but makes sense - it was fairly awkward to do, listing out the whole list of architectures multiple times. You left some vestige of this in Cargo.toml:
Maybe you meant to remove it? I see you went ahead and renamed In the latest version of the branch with your changes, you've restored some of the diamond dependency problem. If crate A has:
It is expecting that the "basic" methods like If crate B has:
It is expecting that the "basic" methods will always use rustls. If crate C depends on crate A and everything is working fine, then adding a dependency on crate B could mysteriously break crate A's activities, by changing the default from rustls to native-tls. That's why I think it's important to emphasize Agent-based configuration for picking a non-default TLS backend. If we change behavior based on features we'll always have spooky action at a distance. |
I don't want to shut anyone down here. So I'm open for challenge. It's like we want Cargo to do something it doesn't have.
I was sort of thinking that could stay, but maybe it's just messy.
Sorry I missed that. To have a guessable/shorterI think it's worth the potential confusion.
DOH!!! You're absolutely right, I fell right back into that trap. Ok, so what if we say |
Yes, I think this would work and has the advantage of being very simple to reason about. It's a bit of a hassle for folks on platforms that can't do rustls, since they can't take advantage of the
Yes, agreed. It's almost there, and I felt like we could hack our way towards it, but it felt like a dimly-lit path full of stumbling blocks. |
@jsha I pushed a follow-up commit to fix the diamond dep problem. Reading the code, I think we have a diamond dependency issue also with |
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've looked through the latest changes with the view that I'm integrating mbedtls.
I was 99% done ten days ago when I had to change priorities. The changes seem like progress to me.
I think that its a bug if any application has both rusttls and native-tls compiled in.
I also think that if there is some application which just uses the convenience methods, and if given an https: URL it fails, that might be a bug. I don't think we can solve these until there are new cargo features to deal with conflicts.
src/agent.rs
Outdated
/// The parameter can be a | ||
/// [`rustls::ClientConfig`](https://docs.rs/rustls/0.19.1/rustls/struct.ClientConfig.html), | ||
/// a [`native_tls::TlsConnector`](https://docs.rs/native-tls/0.2.7/native_tls/struct.TlsConnector.html), | ||
/// or any type for which you implement the [HttpsConnector] trait. |
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.
If I built without tls and without native-tls feature (because I don't want them in my dependency tree), then wouldn't I also lack the hooks for the HttpsConnector trait?
src/agent.rs
Outdated
#[cfg(all(not(feature = "tls"), not(feature = "native-tls")))] | ||
tls_config: Arc::new(crate::stream::NoopTlsConnector), | ||
#[cfg(feature = "tls")] | ||
tls_config: crate::rtls::default_tls_config(), | ||
#[cfg(feature = "native-tls")] | ||
tls_config: Arc::new(native_tls::TlsConnector::new().unwrap()), |
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.
As the check failure points out, this won't work if "tls" is enabled and you add native-tls.
Alas, features can't be mutually exclusive.
I think it needs to have a Box(Trait) inside of it to get the polymorphism we want.
Now we have just one implementation of connect_https. All choice of TLS backends is controlled by a feature gate in AgentBuilder::new.
This adds config gates in Cargo.toml and in the source code that make native-tls the default on platforms that don't support rustls. To keep our matrix of features and platforms simple, I've eliminated the `tls` feature. TLS is now always built-in, and which library gets used depends on your platform.
13bd193
to
b672c16
Compare
src/lib.rs
Outdated
_dns_name: &str, | ||
_tcp_stream: TcpStream, | ||
) -> Result<Box<dyn HttpsStream>, crate::error::Error> { | ||
panic!("No TLS backend. Use either feature 'tls' or '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's never panic. Instead, we can return an Error.
.github/workflows/test.yml
Outdated
feature: | ||
- "" | ||
- json | ||
- charset | ||
- cookies | ||
- socks-proxy | ||
- native-tls-adapter |
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.
- native-tls-adapter |
Cargo.toml
Outdated
webpki = { version = "0.21", optional = true } | ||
webpki-roots = { version = "0.21", optional = true } |
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.
webpki = { version = "0.21", optional = true } | |
webpki-roots = { version = "0.21", optional = true } | |
webpki = { version = "0.22", optional = true } | |
webpki-roots = { version = "0.22", optional = true } |
README.md
Outdated
* `native-tls` enables an adapter so you can pass a `native_tls::TlsConnector` instance | ||
to `AgentBuilder::tls_connector`. Due to the risk of diamond dependencies accidentally switching on an unwanted | ||
TLS implementation, `native-tls` is never picked up as a default or used by the crate level | ||
convencience calls (`ureq::get` etc) – it must be configured. |
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.
convencience calls (`ureq::get` etc) – it must be configured. | |
convenience calls (`ureq::get` etc) – it must be configured on an Agent. |
} | ||
} | ||
|
||
// TODO: After upgrading to rustls 0.20 or higher, we can remove these Read |
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 pushed some small changes with my review fixes. I think this is pretty much ready to go - what do you think @algesten ? |
This continues in #449 |
Per #319, we want to add back native-tls support. However, as discussed in #319 (comment), using Cargo features to select a TLS backend (as we did in 1.x) is a bad fit, due to diamond dependencies.
Instead, ureq takes an opinionated stance and allows runtime configuration to change it. On all platforms that support rustls, we use rustls. On other platforms, we use native-tls. In either case, you can override the default: supply your own TLS implementation by calling
AgentBuilder::tls_connector
. Since this is configured per-Agent, there's no risk that crate A can accidentally change the backend used by crate B.We now have a couple of new traits,
TlsConnector
andHttpsStream
, that abstract over TLS configuration and encrypted streams. There are impls ofTlsConnector
for bothrustls::ClientConfig
andnative_tls::TlsConnector
. To enable the native_tls impl on platforms that default to rustls, build with the native-tls-adapter feature (and callAgentBuilder::tls_connector
).To mange the complexity introduced by having platform-based configs, this removes the "tls" feature. TLS is now always enabled, on all platforms. For applications that want to omit TLS support for binary-size reasons, we may in the future offer a linker-friendly AgentBuilder method that disables TLS. That should allow the linker to remove the unused TLS-related symbols and build time.