Skip to content

Support creating a client with a custom tls connector#562

Closed
Goirad wants to merge 1 commit intoseanmonstar:masterfrom
Goirad:allow-specifying-native-tls-client
Closed

Support creating a client with a custom tls connector#562
Goirad wants to merge 1 commit intoseanmonstar:masterfrom
Goirad:allow-specifying-native-tls-client

Conversation

@Goirad
Copy link
Copy Markdown

@Goirad Goirad commented Jul 8, 2019

This PR would allow a user to create a client by supplying a native-tls::TlsConnector, rather than just being able to specify either default-tls or rustls.

@seanmonstar
Copy link
Copy Markdown
Owner

Thanks for the PR! Do you have some specific case that can't be solved by the existing config options?

I'm wary of exposing such an API as it means that upgrade to a new version becomes a breaking change in reqwest.

@Goirad
Copy link
Copy Markdown
Author

Goirad commented Jul 8, 2019

For example, setting use_sni or min_protocol_version or max_protocol_version for the native_tls connector. Also I will be working on finishing the ability to specify identities with PKCS8 keys and cert chains, and that and any other changes in native_tls would require new settings here. It seems simpler to me to just pass in the already configured TlsConnector.

@jethrogb
Copy link
Copy Markdown

jethrogb commented Jul 9, 2019

I'd also like this feature. For example, I intend to add more advanced certificate validation options to native-tls and this API will probably be a lot bigger than a simple toggle function on the builder. It seems silly to have to add forwarding functions in reqwest for every builder option--however complex they are--that might ever be added in native-tls.

@seanmonstar
Copy link
Copy Markdown
Owner

The issue about not being able to upgrade versions internally seems important to me...

Since reqwest can potentially use different TLS backends, it seems reasonable to me to provide TLS options as needed on the ClientBuilder.

@jethrogb
Copy link
Copy Markdown

jethrogb commented Jul 9, 2019

Not all configuration options are going to be available on all backends. Clearly, if someone wants to put in their own builder, they've already decided on a particular TLS backend. It's good to have sane & safe defaults for most users, but reqwest has all kinds of functionality that's useful to TLS power users too ((de)serialization, proxy support, etc.).

I think these are the options:

  1. Commit to wrapping all possible TLS backend configuration options.
    • How would this work when different backends support different things?
  2. Support only a subset of TLS configuration options
    • This forces people to not use reqwest or use a fork of reqwest.
  3. Make native-tls part of the public API.
    • You would be able to upgrade native-tls with a major version bump of reqwest.
    • You could make a crate reqwest-native-tls that publicly depends on native-tls. reqwest would depend on that crate and wrap everything, except for the public native-tls part of the API, which is not exposed. If you want to bump native-tls you do a new (major) version bump of reqwest-native-tls and you can change to this version in reqwest. Users who want to depend on native-tls directly can use reqwest-native-tls. If there's a major version bump to reqwest-native-tls, they would need to update their use of native-tls to the new version anyway, so there's not a lot of extra effort needed for them to update to the new version.
    • You might be able to build a scheme similar to the previous point using Cargo features.
  4. Abstract over streams using something like hyper::client::connect::Connect.
    • It's not clear to me that this is going to be possible with dynamic dispatch, which is probably what you want here so you don't have to make everything generic.

@seanmonstar
Copy link
Copy Markdown
Owner

As I was curious how curl handles this, they have a CURLOPT_SSL_CTX_FUNCTION option of a callback receiving a void *, and requires the user to unsafely fiddle with the backend. We could provide something similar-ish, though a little safer.

An advanced option passing impl Fn(&mut dyn Any) could be used, and then you can use safe downcasting to fiddle all the toggles...

@jethrogb
Copy link
Copy Markdown

jethrogb commented Jul 9, 2019

Do you need a callback? You could also do something like fn builder(&mut self) -> &mut dyn Any. It's not clear to me this is a semver improvement. While this doesn't break compilation when you change the backend TLS version, it does probably stop code from working at run-time.

@seanmonstar
Copy link
Copy Markdown
Owner

As an advanced config option, it allows users to decide what to do if the backend isn't what you wanted. Maybe you can set the advanced options in multiple backends. Maybe the advanced option wasn't critical, just a nice-to-have, and so you do nothing. Or you could choose to just panic...

@jethrogb
Copy link
Copy Markdown

jethrogb commented Jul 9, 2019

I think your proposal sounds very similar to this:

impl ClientBuilder {
    /// Returns `false` if `reqwest` doesn't know how to use the provided `connector`.
    fn with_preconfigured_connector(&mut self, mut connector: impl Any) -> bool {
        if let Some(conn) = connector.downcast_mut::<NativeTlsConnector>() {
            self.connector = NativeTls(std::mem::replace(conn, Default::default()))
        } else if ... {
            ...
        } else {
            return false
        }
        true
    }
}

I'd think I'd prefer a compilation error over something that will panic at runtime? I guess one could test for this if they needed it to work. But then I feel that should be clearly documented.

@Goirad Goirad force-pushed the allow-specifying-native-tls-client branch from 5eaa295 to 33d407e Compare July 10, 2019 22:41
@Goirad
Copy link
Copy Markdown
Author

Goirad commented Jul 10, 2019

I have updated the PR to reflect Jethro's suggestion, and although it compiles I have not tested the new functionality, it is mostly just a proof of concept at this point.

@Goirad Goirad force-pushed the allow-specifying-native-tls-client branch from 33d407e to b0fe470 Compare July 10, 2019 23:42
@seanmonstar
Copy link
Copy Markdown
Owner

fn with_preconfigured_connector(&mut self, mut connector: impl Any) -> bool {

Ah, I didn't notice this was inverted to what I was thinking. My initial thoughts were to return &mut dyn Any of the internal connector, so a user could inspect it for multiple different types if need be...

@Goirad Goirad force-pushed the allow-specifying-native-tls-client branch from b0fe470 to 20fa2c5 Compare July 11, 2019 18:14
@seanmonstar
Copy link
Copy Markdown
Owner

I continued this in #606 with some changes and tests.

@seanmonstar
Copy link
Copy Markdown
Owner

I'm going to close in favor of #606, though let me know if #606 does or doesn't fit your needs. Thanks for working through the design and rationale here!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants