From dd581471997a92e6507aeae290d89bf4e405ec50 Mon Sep 17 00:00:00 2001 From: David Steele Date: Fri, 23 Jan 2026 10:29:14 +0000 Subject: [PATCH 1/2] feat: Support selective ssl/tls backend in rust-server to avoid always requiring openssl --- .github/workflows/samples-rust-server.yaml | 7 ++++ .../main/resources/rust-server/Cargo.mustache | 20 ++++++++-- .../resources/rust-server/README.mustache | 22 ++++++++++ .../resources/rust-server/client-mod.mustache | 40 +++++++++++++++---- .../rust-server/server-callbacks.mustache | 16 +++++--- .../output/multipart-v3/Cargo.toml | 18 +++++++-- .../rust-server/output/multipart-v3/README.md | 22 ++++++++++ .../output/multipart-v3/src/client/mod.rs | 40 +++++++++++++++---- .../output/no-example-v3/Cargo.toml | 18 +++++++-- .../output/no-example-v3/README.md | 22 ++++++++++ .../output/no-example-v3/src/client/mod.rs | 40 +++++++++++++++---- .../rust-server/output/openapi-v3/Cargo.toml | 20 ++++++++-- .../rust-server/output/openapi-v3/README.md | 22 ++++++++++ .../output/openapi-v3/src/client/mod.rs | 40 +++++++++++++++---- .../output/openapi-v3/src/server/callbacks.rs | 16 +++++--- .../rust-server/output/ops-v3/Cargo.toml | 18 +++++++-- .../rust-server/output/ops-v3/README.md | 22 ++++++++++ .../output/ops-v3/src/client/mod.rs | 40 +++++++++++++++---- .../Cargo.toml | 18 +++++++-- .../README.md | 22 ++++++++++ .../src/client/mod.rs | 40 +++++++++++++++---- .../output/ping-bearer-auth/Cargo.toml | 18 +++++++-- .../output/ping-bearer-auth/README.md | 22 ++++++++++ .../output/ping-bearer-auth/src/client/mod.rs | 40 +++++++++++++++---- .../output/rust-server-test/Cargo.toml | 18 +++++++-- .../output/rust-server-test/README.md | 22 ++++++++++ .../output/rust-server-test/src/client/mod.rs | 40 +++++++++++++++---- 27 files changed, 589 insertions(+), 94 deletions(-) diff --git a/.github/workflows/samples-rust-server.yaml b/.github/workflows/samples-rust-server.yaml index 2c6eb21cade5..249ac5b21883 100644 --- a/.github/workflows/samples-rust-server.yaml +++ b/.github/workflows/samples-rust-server.yaml @@ -64,6 +64,13 @@ jobs: if cargo read-manifest | grep -q '"validate"'; then cargo build --features validate --all-targets fi + # Client without TLS (HTTP-only) + if cargo read-manifest | grep -q '"client-tls"'; then + cargo build --no-default-features --features=client --lib + # Client with selective TLS backend + cargo build --no-default-features --features=client,client-tls --lib + cargo build --no-default-features --features=server --lib + fi cargo fmt cargo test cargo clippy diff --git a/modules/openapi-generator/src/main/resources/rust-server/Cargo.mustache b/modules/openapi-generator/src/main/resources/rust-server/Cargo.mustache index fe169216841c..4e62bb6a4a5d 100644 --- a/modules/openapi-generator/src/main/resources/rust-server/Cargo.mustache +++ b/modules/openapi-generator/src/main/resources/rust-server/Cargo.mustache @@ -32,7 +32,7 @@ homepage = "{{.}}" {{/homePageUrl}} [features] -default = ["client", "server"] +default = ["client", "server", "client-tls", "client-openssl"] client = [ {{#apiUsesMultipartFormData}} "multipart", "multipart/client", "swagger/multipart_form", @@ -47,7 +47,19 @@ client = [ "serde_ignored", "percent-encoding", {{^apiUsesByteArray}}"lazy_static", "regex",{{/apiUsesByteArray}} {{/hasCallbacks}} {{! Anything added to the list below, should probably be added to the callbacks list below }} - "hyper", "percent-encoding", "hyper-util/http1", "hyper-util/http2", "hyper-openssl", "hyper-tls", "native-tls", "openssl", "url" + "hyper", "percent-encoding", "hyper-util/http1", "hyper-util/http2", "url" +] +# TLS support using native-tls (macOS/Windows/iOS) or hyper-tls +client-tls = [ + "client", + "hyper-tls", "native-tls", + "swagger/tls" +] +# TLS support using OpenSSL (Linux and other platforms) +client-openssl = [ + "client", + "hyper-openssl", "openssl", + "swagger/tls" ] server = [ {{#apiUsesMultipartFormData}} @@ -57,7 +69,7 @@ server = [ "mime_multipart", "swagger/multipart_related", {{/apiUsesMultipartRelated}} {{#hasCallbacks}} - "native-tls", "hyper-openssl", "hyper-tls", "openssl", + "client-tls", {{/hasCallbacks}} {{! Anything added to the list below, should probably be added to the callbacks list above }} "serde_ignored", "hyper", "percent-encoding", "url", @@ -87,7 +99,7 @@ openssl = { version = "0.10", optional = true } async-trait = "0.1.88" chrono = { version = "0.4", features = ["serde"] } futures = "0.3" -swagger = { version = "7.0.0", features = ["serdejson", "server", "client", "tls"] } +swagger = { version = "7.0.0", features = ["serdejson", "server", "client"] } headers = "0.4.0" log = "0.4.27" diff --git a/modules/openapi-generator/src/main/resources/rust-server/README.mustache b/modules/openapi-generator/src/main/resources/rust-server/README.mustache index bee28c9363b6..ada4f15edf7e 100644 --- a/modules/openapi-generator/src/main/resources/rust-server/README.mustache +++ b/modules/openapi-generator/src/main/resources/rust-server/README.mustache @@ -126,6 +126,12 @@ The generated library has a few optional features that can be activated through * `client` * This defaults to enabled and creates the basic skeleton of a client implementation based on hyper * The constructed client implements the API trait by making remote API call. +* `client-tls` + * This defaults to enabled and provides HTTPS support using native-tls (macOS/Windows/iOS). + * Enable this feature for TLS support on Apple and Windows platforms. +* `client-openssl` + * This defaults to enabled and provides HTTPS support using OpenSSL (Linux and other platforms). + * Enable this feature for TLS support on Linux and other Unix-like platforms. * `conversions` * This defaults to disabled and creates extra derives on models to allow "transmogrification" between objects of structurally similar types. * `cli` @@ -134,6 +140,22 @@ The generated library has a few optional features that can be activated through * This defaults to disabled and allows JSON Schema validation of received data using `MakeService::set_validation` or `Service::set_validation`. * Note, enabling validation will have a performance penalty, especially if the API heavily uses regex based checks. +### Minimal dependencies (no TLS) + +If you only need HTTP support and want to minimize dependencies (e.g., to avoid OpenSSL build requirements), you can disable the default TLS features: + +```toml +[dependencies] +{{{packageName}}} = { version = "{{{packageVersion}}}", default-features = false, features = ["server"] } +``` + +Or for client-only without TLS: + +```toml +[dependencies] +{{{packageName}}} = { version = "{{{packageVersion}}}", default-features = false, features = ["client"] } +``` + See https://doc.rust-lang.org/cargo/reference/manifest.html#the-features-section for how to use features in your `Cargo.toml`. ## Documentation for API Endpoints diff --git a/modules/openapi-generator/src/main/resources/rust-server/client-mod.mustache b/modules/openapi-generator/src/main/resources/rust-server/client-mod.mustache index 9860a93c5865..bce3dba34a7e 100644 --- a/modules/openapi-generator/src/main/resources/rust-server/client-mod.mustache +++ b/modules/openapi-generator/src/main/resources/rust-server/client-mod.mustache @@ -121,6 +121,10 @@ impl Client< #[derive(Debug, Clone)] pub enum HyperClient { Http(hyper_util::client::legacy::Client>), + #[cfg(any( + all(feature = "client-tls", any(target_os = "macos", target_os = "windows", target_os = "ios")), + all(feature = "client-openssl", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))) + ))] Https(hyper_util::client::legacy::Client>), } @@ -132,7 +136,11 @@ impl Service>> for HyperClient { fn call(&self, req: Request>) -> Self::Future { match self { HyperClient::Http(client) => client.request(req), - HyperClient::Https(client) => client.request(req) + #[cfg(any( + all(feature = "client-tls", any(target_os = "macos", target_os = "windows", target_os = "ios")), + all(feature = "client-openssl", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))) + ))] + HyperClient::Https(client) => client.request(req), } } } @@ -158,12 +166,23 @@ impl Client, C> where "http" => { HyperClient::Http(hyper_util::client::legacy::Client::builder(hyper_util::rt::TokioExecutor::new()).build(connector.build())) }, + #[cfg(any( + all(feature = "client-tls", any(target_os = "macos", target_os = "windows", target_os = "ios")), + all(feature = "client-openssl", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))) + ))] "https" => { let connector = connector.https() .build() .map_err(ClientInitError::SslError)?; HyperClient::Https(hyper_util::client::legacy::Client::builder(hyper_util::rt::TokioExecutor::new()).build(connector)) }, + #[cfg(not(any( + all(feature = "client-tls", any(target_os = "macos", target_os = "windows", target_os = "ios")), + all(feature = "client-openssl", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))) + )))] + "https" => { + return Err(ClientInitError::TlsNotEnabled); + }, _ => { return Err(ClientInitError::InvalidScheme); } @@ -206,12 +225,16 @@ impl Client< } } -#[cfg(any(target_os = "macos", target_os = "windows", target_os = "ios"))] +#[cfg(all(feature = "client-tls", any(target_os = "macos", target_os = "windows", target_os = "ios")))] type HttpsConnector = hyper_tls::HttpsConnector; -#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))] +#[cfg(all(feature = "client-openssl", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))))] type HttpsConnector = hyper_openssl::client::legacy::HttpsConnector; +#[cfg(any( + all(feature = "client-tls", any(target_os = "macos", target_os = "windows", target_os = "ios")), + all(feature = "client-openssl", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))) +))] impl Client< DropContextService< hyper_util::service::TowerToHyperService< @@ -244,7 +267,7 @@ impl Client< /// # Arguments /// * `base_path` - base path of the client API, i.e. "" /// * `ca_certificate` - Path to CA certificate used to authenticate the server - #[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))] + #[cfg(all(feature = "client-openssl", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))))] pub fn try_new_https_pinned( base_path: &str, ca_certificate: CA, @@ -267,7 +290,7 @@ impl Client< /// * `ca_certificate` - Path to CA certificate used to authenticate the server /// * `client_key` - Path to the client private key /// * `client_certificate` - Path to the client's public certificate associated with the private key - #[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))] + #[cfg(all(feature = "client-openssl", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))))] pub fn try_new_https_mutual( base_path: &str, ca_certificate: CA, @@ -325,12 +348,15 @@ pub enum ClientInitError { /// Missing Hostname MissingHost, + /// HTTPS requested but TLS features not enabled + TlsNotEnabled, + /// SSL Connection Error - #[cfg(any(target_os = "macos", target_os = "windows", target_os = "ios"))] + #[cfg(all(feature = "client-tls", any(target_os = "macos", target_os = "windows", target_os = "ios")))] SslError(native_tls::Error), /// SSL Connection Error - #[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))] + #[cfg(all(feature = "client-openssl", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))))] SslError(openssl::error::ErrorStack), } diff --git a/modules/openapi-generator/src/main/resources/rust-server/server-callbacks.mustache b/modules/openapi-generator/src/main/resources/rust-server/server-callbacks.mustache index 301380b0a705..fa86bd6dff9d 100644 --- a/modules/openapi-generator/src/main/resources/rust-server/server-callbacks.mustache +++ b/modules/openapi-generator/src/main/resources/rust-server/server-callbacks.mustache @@ -97,17 +97,21 @@ impl Client; -#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))] +#[cfg(all(feature = "client-openssl", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))))] type HttpsConnector = hyper_openssl::client::legacy::HttpsConnector; +#[cfg(any( + all(feature = "client-tls", any(target_os = "macos", target_os = "windows", target_os = "ios")), + all(feature = "client-openssl", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))) +))] impl Client>>, C>, C> where C: Clone + Send + Sync + 'static { /// Create a client with a TLS connection to the server. - #[cfg(any(target_os = "macos", target_os = "windows", target_os = "ios"))] + #[cfg(all(feature = "client-tls", any(target_os = "macos", target_os = "windows", target_os = "ios")))] pub fn new_https() -> Result { let https_connector = Connector::builder().https().build()?; @@ -115,7 +119,7 @@ impl Client Result { let https_connector = Connector::builder().https().build()?; @@ -126,7 +130,7 @@ impl Client( ca_certificate: CA, ) -> Result where @@ -145,7 +149,7 @@ impl Client( ca_certificate: CA, client_key: K, diff --git a/samples/server/petstore/rust-server/output/multipart-v3/Cargo.toml b/samples/server/petstore/rust-server/output/multipart-v3/Cargo.toml index 11dde870fb8f..1623d54231a4 100644 --- a/samples/server/petstore/rust-server/output/multipart-v3/Cargo.toml +++ b/samples/server/petstore/rust-server/output/multipart-v3/Cargo.toml @@ -8,11 +8,23 @@ license = "Unlicense" edition = "2018" [features] -default = ["client", "server"] +default = ["client", "server", "client-tls", "client-openssl"] client = [ "multipart", "multipart/client", "swagger/multipart_form", "mime_multipart", "swagger/multipart_related", - "hyper", "percent-encoding", "hyper-util/http1", "hyper-util/http2", "hyper-openssl", "hyper-tls", "native-tls", "openssl", "url" + "hyper", "percent-encoding", "hyper-util/http1", "hyper-util/http2", "url" +] +# TLS support using native-tls (macOS/Windows/iOS) or hyper-tls +client-tls = [ + "client", + "hyper-tls", "native-tls", + "swagger/tls" +] +# TLS support using OpenSSL (Linux and other platforms) +client-openssl = [ + "client", + "hyper-openssl", "openssl", + "swagger/tls" ] server = [ "multipart", "multipart/server", "swagger/multipart_form", @@ -41,7 +53,7 @@ openssl = { version = "0.10", optional = true } async-trait = "0.1.88" chrono = { version = "0.4", features = ["serde"] } futures = "0.3" -swagger = { version = "7.0.0", features = ["serdejson", "server", "client", "tls"] } +swagger = { version = "7.0.0", features = ["serdejson", "server", "client"] } headers = "0.4.0" log = "0.4.27" diff --git a/samples/server/petstore/rust-server/output/multipart-v3/README.md b/samples/server/petstore/rust-server/output/multipart-v3/README.md index e3938cd7ab55..52645b8ce0d7 100644 --- a/samples/server/petstore/rust-server/output/multipart-v3/README.md +++ b/samples/server/petstore/rust-server/output/multipart-v3/README.md @@ -111,6 +111,12 @@ The generated library has a few optional features that can be activated through * `client` * This defaults to enabled and creates the basic skeleton of a client implementation based on hyper * The constructed client implements the API trait by making remote API call. +* `client-tls` + * This defaults to enabled and provides HTTPS support using native-tls (macOS/Windows/iOS). + * Enable this feature for TLS support on Apple and Windows platforms. +* `client-openssl` + * This defaults to enabled and provides HTTPS support using OpenSSL (Linux and other platforms). + * Enable this feature for TLS support on Linux and other Unix-like platforms. * `conversions` * This defaults to disabled and creates extra derives on models to allow "transmogrification" between objects of structurally similar types. * `cli` @@ -119,6 +125,22 @@ The generated library has a few optional features that can be activated through * This defaults to disabled and allows JSON Schema validation of received data using `MakeService::set_validation` or `Service::set_validation`. * Note, enabling validation will have a performance penalty, especially if the API heavily uses regex based checks. +### Minimal dependencies (no TLS) + +If you only need HTTP support and want to minimize dependencies (e.g., to avoid OpenSSL build requirements), you can disable the default TLS features: + +```toml +[dependencies] +multipart-v3 = { version = "1.0.7", default-features = false, features = ["server"] } +``` + +Or for client-only without TLS: + +```toml +[dependencies] +multipart-v3 = { version = "1.0.7", default-features = false, features = ["client"] } +``` + See https://doc.rust-lang.org/cargo/reference/manifest.html#the-features-section for how to use features in your `Cargo.toml`. ## Documentation for API Endpoints diff --git a/samples/server/petstore/rust-server/output/multipart-v3/src/client/mod.rs b/samples/server/petstore/rust-server/output/multipart-v3/src/client/mod.rs index df74eae468a1..22c64952d214 100644 --- a/samples/server/petstore/rust-server/output/multipart-v3/src/client/mod.rs +++ b/samples/server/petstore/rust-server/output/multipart-v3/src/client/mod.rs @@ -163,6 +163,10 @@ impl Client< #[derive(Debug, Clone)] pub enum HyperClient { Http(hyper_util::client::legacy::Client>), + #[cfg(any( + all(feature = "client-tls", any(target_os = "macos", target_os = "windows", target_os = "ios")), + all(feature = "client-openssl", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))) + ))] Https(hyper_util::client::legacy::Client>), } @@ -174,7 +178,11 @@ impl Service>> for HyperClient { fn call(&self, req: Request>) -> Self::Future { match self { HyperClient::Http(client) => client.request(req), - HyperClient::Https(client) => client.request(req) + #[cfg(any( + all(feature = "client-tls", any(target_os = "macos", target_os = "windows", target_os = "ios")), + all(feature = "client-openssl", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))) + ))] + HyperClient::Https(client) => client.request(req), } } } @@ -200,12 +208,23 @@ impl Client, C> where "http" => { HyperClient::Http(hyper_util::client::legacy::Client::builder(hyper_util::rt::TokioExecutor::new()).build(connector.build())) }, + #[cfg(any( + all(feature = "client-tls", any(target_os = "macos", target_os = "windows", target_os = "ios")), + all(feature = "client-openssl", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))) + ))] "https" => { let connector = connector.https() .build() .map_err(ClientInitError::SslError)?; HyperClient::Https(hyper_util::client::legacy::Client::builder(hyper_util::rt::TokioExecutor::new()).build(connector)) }, + #[cfg(not(any( + all(feature = "client-tls", any(target_os = "macos", target_os = "windows", target_os = "ios")), + all(feature = "client-openssl", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))) + )))] + "https" => { + return Err(ClientInitError::TlsNotEnabled); + }, _ => { return Err(ClientInitError::InvalidScheme); } @@ -248,12 +267,16 @@ impl Client< } } -#[cfg(any(target_os = "macos", target_os = "windows", target_os = "ios"))] +#[cfg(all(feature = "client-tls", any(target_os = "macos", target_os = "windows", target_os = "ios")))] type HttpsConnector = hyper_tls::HttpsConnector; -#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))] +#[cfg(all(feature = "client-openssl", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))))] type HttpsConnector = hyper_openssl::client::legacy::HttpsConnector; +#[cfg(any( + all(feature = "client-tls", any(target_os = "macos", target_os = "windows", target_os = "ios")), + all(feature = "client-openssl", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))) +))] impl Client< DropContextService< hyper_util::service::TowerToHyperService< @@ -286,7 +309,7 @@ impl Client< /// # Arguments /// * `base_path` - base path of the client API, i.e. "" /// * `ca_certificate` - Path to CA certificate used to authenticate the server - #[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))] + #[cfg(all(feature = "client-openssl", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))))] pub fn try_new_https_pinned( base_path: &str, ca_certificate: CA, @@ -309,7 +332,7 @@ impl Client< /// * `ca_certificate` - Path to CA certificate used to authenticate the server /// * `client_key` - Path to the client private key /// * `client_certificate` - Path to the client's public certificate associated with the private key - #[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))] + #[cfg(all(feature = "client-openssl", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))))] pub fn try_new_https_mutual( base_path: &str, ca_certificate: CA, @@ -367,12 +390,15 @@ pub enum ClientInitError { /// Missing Hostname MissingHost, + /// HTTPS requested but TLS features not enabled + TlsNotEnabled, + /// SSL Connection Error - #[cfg(any(target_os = "macos", target_os = "windows", target_os = "ios"))] + #[cfg(all(feature = "client-tls", any(target_os = "macos", target_os = "windows", target_os = "ios")))] SslError(native_tls::Error), /// SSL Connection Error - #[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))] + #[cfg(all(feature = "client-openssl", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))))] SslError(openssl::error::ErrorStack), } diff --git a/samples/server/petstore/rust-server/output/no-example-v3/Cargo.toml b/samples/server/petstore/rust-server/output/no-example-v3/Cargo.toml index c67e31f2c818..8612179de3d8 100644 --- a/samples/server/petstore/rust-server/output/no-example-v3/Cargo.toml +++ b/samples/server/petstore/rust-server/output/no-example-v3/Cargo.toml @@ -8,9 +8,21 @@ license = "Unlicense" edition = "2018" [features] -default = ["client", "server"] +default = ["client", "server", "client-tls", "client-openssl"] client = [ - "hyper", "percent-encoding", "hyper-util/http1", "hyper-util/http2", "hyper-openssl", "hyper-tls", "native-tls", "openssl", "url" + "hyper", "percent-encoding", "hyper-util/http1", "hyper-util/http2", "url" +] +# TLS support using native-tls (macOS/Windows/iOS) or hyper-tls +client-tls = [ + "client", + "hyper-tls", "native-tls", + "swagger/tls" +] +# TLS support using OpenSSL (Linux and other platforms) +client-openssl = [ + "client", + "hyper-openssl", "openssl", + "swagger/tls" ] server = [ "serde_ignored", "hyper", "percent-encoding", "url", @@ -37,7 +49,7 @@ openssl = { version = "0.10", optional = true } async-trait = "0.1.88" chrono = { version = "0.4", features = ["serde"] } futures = "0.3" -swagger = { version = "7.0.0", features = ["serdejson", "server", "client", "tls"] } +swagger = { version = "7.0.0", features = ["serdejson", "server", "client"] } headers = "0.4.0" log = "0.4.27" diff --git a/samples/server/petstore/rust-server/output/no-example-v3/README.md b/samples/server/petstore/rust-server/output/no-example-v3/README.md index b72555bb59de..d4fa89969ac6 100644 --- a/samples/server/petstore/rust-server/output/no-example-v3/README.md +++ b/samples/server/petstore/rust-server/output/no-example-v3/README.md @@ -108,6 +108,12 @@ The generated library has a few optional features that can be activated through * `client` * This defaults to enabled and creates the basic skeleton of a client implementation based on hyper * The constructed client implements the API trait by making remote API call. +* `client-tls` + * This defaults to enabled and provides HTTPS support using native-tls (macOS/Windows/iOS). + * Enable this feature for TLS support on Apple and Windows platforms. +* `client-openssl` + * This defaults to enabled and provides HTTPS support using OpenSSL (Linux and other platforms). + * Enable this feature for TLS support on Linux and other Unix-like platforms. * `conversions` * This defaults to disabled and creates extra derives on models to allow "transmogrification" between objects of structurally similar types. * `cli` @@ -116,6 +122,22 @@ The generated library has a few optional features that can be activated through * This defaults to disabled and allows JSON Schema validation of received data using `MakeService::set_validation` or `Service::set_validation`. * Note, enabling validation will have a performance penalty, especially if the API heavily uses regex based checks. +### Minimal dependencies (no TLS) + +If you only need HTTP support and want to minimize dependencies (e.g., to avoid OpenSSL build requirements), you can disable the default TLS features: + +```toml +[dependencies] +no-example-v3 = { version = "0.0.1", default-features = false, features = ["server"] } +``` + +Or for client-only without TLS: + +```toml +[dependencies] +no-example-v3 = { version = "0.0.1", default-features = false, features = ["client"] } +``` + See https://doc.rust-lang.org/cargo/reference/manifest.html#the-features-section for how to use features in your `Cargo.toml`. ## Documentation for API Endpoints diff --git a/samples/server/petstore/rust-server/output/no-example-v3/src/client/mod.rs b/samples/server/petstore/rust-server/output/no-example-v3/src/client/mod.rs index 6bdb31f4c2fc..55499e91acff 100644 --- a/samples/server/petstore/rust-server/output/no-example-v3/src/client/mod.rs +++ b/samples/server/petstore/rust-server/output/no-example-v3/src/client/mod.rs @@ -156,6 +156,10 @@ impl Client< #[derive(Debug, Clone)] pub enum HyperClient { Http(hyper_util::client::legacy::Client>), + #[cfg(any( + all(feature = "client-tls", any(target_os = "macos", target_os = "windows", target_os = "ios")), + all(feature = "client-openssl", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))) + ))] Https(hyper_util::client::legacy::Client>), } @@ -167,7 +171,11 @@ impl Service>> for HyperClient { fn call(&self, req: Request>) -> Self::Future { match self { HyperClient::Http(client) => client.request(req), - HyperClient::Https(client) => client.request(req) + #[cfg(any( + all(feature = "client-tls", any(target_os = "macos", target_os = "windows", target_os = "ios")), + all(feature = "client-openssl", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))) + ))] + HyperClient::Https(client) => client.request(req), } } } @@ -193,12 +201,23 @@ impl Client, C> where "http" => { HyperClient::Http(hyper_util::client::legacy::Client::builder(hyper_util::rt::TokioExecutor::new()).build(connector.build())) }, + #[cfg(any( + all(feature = "client-tls", any(target_os = "macos", target_os = "windows", target_os = "ios")), + all(feature = "client-openssl", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))) + ))] "https" => { let connector = connector.https() .build() .map_err(ClientInitError::SslError)?; HyperClient::Https(hyper_util::client::legacy::Client::builder(hyper_util::rt::TokioExecutor::new()).build(connector)) }, + #[cfg(not(any( + all(feature = "client-tls", any(target_os = "macos", target_os = "windows", target_os = "ios")), + all(feature = "client-openssl", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))) + )))] + "https" => { + return Err(ClientInitError::TlsNotEnabled); + }, _ => { return Err(ClientInitError::InvalidScheme); } @@ -241,12 +260,16 @@ impl Client< } } -#[cfg(any(target_os = "macos", target_os = "windows", target_os = "ios"))] +#[cfg(all(feature = "client-tls", any(target_os = "macos", target_os = "windows", target_os = "ios")))] type HttpsConnector = hyper_tls::HttpsConnector; -#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))] +#[cfg(all(feature = "client-openssl", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))))] type HttpsConnector = hyper_openssl::client::legacy::HttpsConnector; +#[cfg(any( + all(feature = "client-tls", any(target_os = "macos", target_os = "windows", target_os = "ios")), + all(feature = "client-openssl", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))) +))] impl Client< DropContextService< hyper_util::service::TowerToHyperService< @@ -279,7 +302,7 @@ impl Client< /// # Arguments /// * `base_path` - base path of the client API, i.e. "" /// * `ca_certificate` - Path to CA certificate used to authenticate the server - #[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))] + #[cfg(all(feature = "client-openssl", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))))] pub fn try_new_https_pinned( base_path: &str, ca_certificate: CA, @@ -302,7 +325,7 @@ impl Client< /// * `ca_certificate` - Path to CA certificate used to authenticate the server /// * `client_key` - Path to the client private key /// * `client_certificate` - Path to the client's public certificate associated with the private key - #[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))] + #[cfg(all(feature = "client-openssl", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))))] pub fn try_new_https_mutual( base_path: &str, ca_certificate: CA, @@ -360,12 +383,15 @@ pub enum ClientInitError { /// Missing Hostname MissingHost, + /// HTTPS requested but TLS features not enabled + TlsNotEnabled, + /// SSL Connection Error - #[cfg(any(target_os = "macos", target_os = "windows", target_os = "ios"))] + #[cfg(all(feature = "client-tls", any(target_os = "macos", target_os = "windows", target_os = "ios")))] SslError(native_tls::Error), /// SSL Connection Error - #[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))] + #[cfg(all(feature = "client-openssl", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))))] SslError(openssl::error::ErrorStack), } diff --git a/samples/server/petstore/rust-server/output/openapi-v3/Cargo.toml b/samples/server/petstore/rust-server/output/openapi-v3/Cargo.toml index 4bb5c8f63af7..ad01f1106fa3 100644 --- a/samples/server/petstore/rust-server/output/openapi-v3/Cargo.toml +++ b/samples/server/petstore/rust-server/output/openapi-v3/Cargo.toml @@ -8,14 +8,26 @@ license = "Unlicense" edition = "2018" [features] -default = ["client", "server"] +default = ["client", "server", "client-tls", "client-openssl"] client = [ "serde_urlencoded", "serde_ignored", "percent-encoding", - "hyper", "percent-encoding", "hyper-util/http1", "hyper-util/http2", "hyper-openssl", "hyper-tls", "native-tls", "openssl", "url" + "hyper", "percent-encoding", "hyper-util/http1", "hyper-util/http2", "url" +] +# TLS support using native-tls (macOS/Windows/iOS) or hyper-tls +client-tls = [ + "client", + "hyper-tls", "native-tls", + "swagger/tls" +] +# TLS support using OpenSSL (Linux and other platforms) +client-openssl = [ + "client", + "hyper-openssl", "openssl", + "swagger/tls" ] server = [ - "native-tls", "hyper-openssl", "hyper-tls", "openssl", + "client-tls", "serde_ignored", "hyper", "percent-encoding", "url", ] @@ -40,7 +52,7 @@ openssl = { version = "0.10", optional = true } async-trait = "0.1.88" chrono = { version = "0.4", features = ["serde"] } futures = "0.3" -swagger = { version = "7.0.0", features = ["serdejson", "server", "client", "tls"] } +swagger = { version = "7.0.0", features = ["serdejson", "server", "client"] } headers = "0.4.0" log = "0.4.27" diff --git a/samples/server/petstore/rust-server/output/openapi-v3/README.md b/samples/server/petstore/rust-server/output/openapi-v3/README.md index 2989d965d76c..0bf349381e3c 100644 --- a/samples/server/petstore/rust-server/output/openapi-v3/README.md +++ b/samples/server/petstore/rust-server/output/openapi-v3/README.md @@ -139,6 +139,12 @@ The generated library has a few optional features that can be activated through * `client` * This defaults to enabled and creates the basic skeleton of a client implementation based on hyper * The constructed client implements the API trait by making remote API call. +* `client-tls` + * This defaults to enabled and provides HTTPS support using native-tls (macOS/Windows/iOS). + * Enable this feature for TLS support on Apple and Windows platforms. +* `client-openssl` + * This defaults to enabled and provides HTTPS support using OpenSSL (Linux and other platforms). + * Enable this feature for TLS support on Linux and other Unix-like platforms. * `conversions` * This defaults to disabled and creates extra derives on models to allow "transmogrification" between objects of structurally similar types. * `cli` @@ -147,6 +153,22 @@ The generated library has a few optional features that can be activated through * This defaults to disabled and allows JSON Schema validation of received data using `MakeService::set_validation` or `Service::set_validation`. * Note, enabling validation will have a performance penalty, especially if the API heavily uses regex based checks. +### Minimal dependencies (no TLS) + +If you only need HTTP support and want to minimize dependencies (e.g., to avoid OpenSSL build requirements), you can disable the default TLS features: + +```toml +[dependencies] +openapi-v3 = { version = "1.0.7", default-features = false, features = ["server"] } +``` + +Or for client-only without TLS: + +```toml +[dependencies] +openapi-v3 = { version = "1.0.7", default-features = false, features = ["client"] } +``` + See https://doc.rust-lang.org/cargo/reference/manifest.html#the-features-section for how to use features in your `Cargo.toml`. ## Documentation for API Endpoints diff --git a/samples/server/petstore/rust-server/output/openapi-v3/src/client/mod.rs b/samples/server/petstore/rust-server/output/openapi-v3/src/client/mod.rs index f10dc0d32b95..b34d569df2e1 100644 --- a/samples/server/petstore/rust-server/output/openapi-v3/src/client/mod.rs +++ b/samples/server/petstore/rust-server/output/openapi-v3/src/client/mod.rs @@ -188,6 +188,10 @@ impl Client< #[derive(Debug, Clone)] pub enum HyperClient { Http(hyper_util::client::legacy::Client>), + #[cfg(any( + all(feature = "client-tls", any(target_os = "macos", target_os = "windows", target_os = "ios")), + all(feature = "client-openssl", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))) + ))] Https(hyper_util::client::legacy::Client>), } @@ -199,7 +203,11 @@ impl Service>> for HyperClient { fn call(&self, req: Request>) -> Self::Future { match self { HyperClient::Http(client) => client.request(req), - HyperClient::Https(client) => client.request(req) + #[cfg(any( + all(feature = "client-tls", any(target_os = "macos", target_os = "windows", target_os = "ios")), + all(feature = "client-openssl", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))) + ))] + HyperClient::Https(client) => client.request(req), } } } @@ -225,12 +233,23 @@ impl Client, C> where "http" => { HyperClient::Http(hyper_util::client::legacy::Client::builder(hyper_util::rt::TokioExecutor::new()).build(connector.build())) }, + #[cfg(any( + all(feature = "client-tls", any(target_os = "macos", target_os = "windows", target_os = "ios")), + all(feature = "client-openssl", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))) + ))] "https" => { let connector = connector.https() .build() .map_err(ClientInitError::SslError)?; HyperClient::Https(hyper_util::client::legacy::Client::builder(hyper_util::rt::TokioExecutor::new()).build(connector)) }, + #[cfg(not(any( + all(feature = "client-tls", any(target_os = "macos", target_os = "windows", target_os = "ios")), + all(feature = "client-openssl", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))) + )))] + "https" => { + return Err(ClientInitError::TlsNotEnabled); + }, _ => { return Err(ClientInitError::InvalidScheme); } @@ -273,12 +292,16 @@ impl Client< } } -#[cfg(any(target_os = "macos", target_os = "windows", target_os = "ios"))] +#[cfg(all(feature = "client-tls", any(target_os = "macos", target_os = "windows", target_os = "ios")))] type HttpsConnector = hyper_tls::HttpsConnector; -#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))] +#[cfg(all(feature = "client-openssl", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))))] type HttpsConnector = hyper_openssl::client::legacy::HttpsConnector; +#[cfg(any( + all(feature = "client-tls", any(target_os = "macos", target_os = "windows", target_os = "ios")), + all(feature = "client-openssl", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))) +))] impl Client< DropContextService< hyper_util::service::TowerToHyperService< @@ -311,7 +334,7 @@ impl Client< /// # Arguments /// * `base_path` - base path of the client API, i.e. "" /// * `ca_certificate` - Path to CA certificate used to authenticate the server - #[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))] + #[cfg(all(feature = "client-openssl", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))))] pub fn try_new_https_pinned( base_path: &str, ca_certificate: CA, @@ -334,7 +357,7 @@ impl Client< /// * `ca_certificate` - Path to CA certificate used to authenticate the server /// * `client_key` - Path to the client private key /// * `client_certificate` - Path to the client's public certificate associated with the private key - #[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))] + #[cfg(all(feature = "client-openssl", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))))] pub fn try_new_https_mutual( base_path: &str, ca_certificate: CA, @@ -392,12 +415,15 @@ pub enum ClientInitError { /// Missing Hostname MissingHost, + /// HTTPS requested but TLS features not enabled + TlsNotEnabled, + /// SSL Connection Error - #[cfg(any(target_os = "macos", target_os = "windows", target_os = "ios"))] + #[cfg(all(feature = "client-tls", any(target_os = "macos", target_os = "windows", target_os = "ios")))] SslError(native_tls::Error), /// SSL Connection Error - #[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))] + #[cfg(all(feature = "client-openssl", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))))] SslError(openssl::error::ErrorStack), } diff --git a/samples/server/petstore/rust-server/output/openapi-v3/src/server/callbacks.rs b/samples/server/petstore/rust-server/output/openapi-v3/src/server/callbacks.rs index 5a96b66851d4..7146cc3f2529 100644 --- a/samples/server/petstore/rust-server/output/openapi-v3/src/server/callbacks.rs +++ b/samples/server/petstore/rust-server/output/openapi-v3/src/server/callbacks.rs @@ -123,17 +123,21 @@ impl Client; -#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))] +#[cfg(all(feature = "client-openssl", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))))] type HttpsConnector = hyper_openssl::client::legacy::HttpsConnector; +#[cfg(any( + all(feature = "client-tls", any(target_os = "macos", target_os = "windows", target_os = "ios")), + all(feature = "client-openssl", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))) +))] impl Client>>, C>, C> where C: Clone + Send + Sync + 'static { /// Create a client with a TLS connection to the server. - #[cfg(any(target_os = "macos", target_os = "windows", target_os = "ios"))] + #[cfg(all(feature = "client-tls", any(target_os = "macos", target_os = "windows", target_os = "ios")))] pub fn new_https() -> Result { let https_connector = Connector::builder().https().build()?; @@ -141,7 +145,7 @@ impl Client Result { let https_connector = Connector::builder().https().build()?; @@ -152,7 +156,7 @@ impl Client( ca_certificate: CA, ) -> Result where @@ -171,7 +175,7 @@ impl Client( ca_certificate: CA, client_key: K, diff --git a/samples/server/petstore/rust-server/output/ops-v3/Cargo.toml b/samples/server/petstore/rust-server/output/ops-v3/Cargo.toml index 2aa4340664c7..d4d17bcca5a8 100644 --- a/samples/server/petstore/rust-server/output/ops-v3/Cargo.toml +++ b/samples/server/petstore/rust-server/output/ops-v3/Cargo.toml @@ -8,9 +8,21 @@ license = "Unlicense" edition = "2018" [features] -default = ["client", "server"] +default = ["client", "server", "client-tls", "client-openssl"] client = [ - "hyper", "percent-encoding", "hyper-util/http1", "hyper-util/http2", "hyper-openssl", "hyper-tls", "native-tls", "openssl", "url" + "hyper", "percent-encoding", "hyper-util/http1", "hyper-util/http2", "url" +] +# TLS support using native-tls (macOS/Windows/iOS) or hyper-tls +client-tls = [ + "client", + "hyper-tls", "native-tls", + "swagger/tls" +] +# TLS support using OpenSSL (Linux and other platforms) +client-openssl = [ + "client", + "hyper-openssl", "openssl", + "swagger/tls" ] server = [ "serde_ignored", "hyper", "percent-encoding", "url", @@ -37,7 +49,7 @@ openssl = { version = "0.10", optional = true } async-trait = "0.1.88" chrono = { version = "0.4", features = ["serde"] } futures = "0.3" -swagger = { version = "7.0.0", features = ["serdejson", "server", "client", "tls"] } +swagger = { version = "7.0.0", features = ["serdejson", "server", "client"] } headers = "0.4.0" log = "0.4.27" diff --git a/samples/server/petstore/rust-server/output/ops-v3/README.md b/samples/server/petstore/rust-server/output/ops-v3/README.md index 24c642ae0416..772bc20930ee 100644 --- a/samples/server/petstore/rust-server/output/ops-v3/README.md +++ b/samples/server/petstore/rust-server/output/ops-v3/README.md @@ -145,6 +145,12 @@ The generated library has a few optional features that can be activated through * `client` * This defaults to enabled and creates the basic skeleton of a client implementation based on hyper * The constructed client implements the API trait by making remote API call. +* `client-tls` + * This defaults to enabled and provides HTTPS support using native-tls (macOS/Windows/iOS). + * Enable this feature for TLS support on Apple and Windows platforms. +* `client-openssl` + * This defaults to enabled and provides HTTPS support using OpenSSL (Linux and other platforms). + * Enable this feature for TLS support on Linux and other Unix-like platforms. * `conversions` * This defaults to disabled and creates extra derives on models to allow "transmogrification" between objects of structurally similar types. * `cli` @@ -153,6 +159,22 @@ The generated library has a few optional features that can be activated through * This defaults to disabled and allows JSON Schema validation of received data using `MakeService::set_validation` or `Service::set_validation`. * Note, enabling validation will have a performance penalty, especially if the API heavily uses regex based checks. +### Minimal dependencies (no TLS) + +If you only need HTTP support and want to minimize dependencies (e.g., to avoid OpenSSL build requirements), you can disable the default TLS features: + +```toml +[dependencies] +ops-v3 = { version = "0.0.1", default-features = false, features = ["server"] } +``` + +Or for client-only without TLS: + +```toml +[dependencies] +ops-v3 = { version = "0.0.1", default-features = false, features = ["client"] } +``` + See https://doc.rust-lang.org/cargo/reference/manifest.html#the-features-section for how to use features in your `Cargo.toml`. ## Documentation for API Endpoints diff --git a/samples/server/petstore/rust-server/output/ops-v3/src/client/mod.rs b/samples/server/petstore/rust-server/output/ops-v3/src/client/mod.rs index 90bfb329b0d0..6a42d99ca018 100644 --- a/samples/server/petstore/rust-server/output/ops-v3/src/client/mod.rs +++ b/samples/server/petstore/rust-server/output/ops-v3/src/client/mod.rs @@ -192,6 +192,10 @@ impl Client< #[derive(Debug, Clone)] pub enum HyperClient { Http(hyper_util::client::legacy::Client>), + #[cfg(any( + all(feature = "client-tls", any(target_os = "macos", target_os = "windows", target_os = "ios")), + all(feature = "client-openssl", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))) + ))] Https(hyper_util::client::legacy::Client>), } @@ -203,7 +207,11 @@ impl Service>> for HyperClient { fn call(&self, req: Request>) -> Self::Future { match self { HyperClient::Http(client) => client.request(req), - HyperClient::Https(client) => client.request(req) + #[cfg(any( + all(feature = "client-tls", any(target_os = "macos", target_os = "windows", target_os = "ios")), + all(feature = "client-openssl", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))) + ))] + HyperClient::Https(client) => client.request(req), } } } @@ -229,12 +237,23 @@ impl Client, C> where "http" => { HyperClient::Http(hyper_util::client::legacy::Client::builder(hyper_util::rt::TokioExecutor::new()).build(connector.build())) }, + #[cfg(any( + all(feature = "client-tls", any(target_os = "macos", target_os = "windows", target_os = "ios")), + all(feature = "client-openssl", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))) + ))] "https" => { let connector = connector.https() .build() .map_err(ClientInitError::SslError)?; HyperClient::Https(hyper_util::client::legacy::Client::builder(hyper_util::rt::TokioExecutor::new()).build(connector)) }, + #[cfg(not(any( + all(feature = "client-tls", any(target_os = "macos", target_os = "windows", target_os = "ios")), + all(feature = "client-openssl", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))) + )))] + "https" => { + return Err(ClientInitError::TlsNotEnabled); + }, _ => { return Err(ClientInitError::InvalidScheme); } @@ -277,12 +296,16 @@ impl Client< } } -#[cfg(any(target_os = "macos", target_os = "windows", target_os = "ios"))] +#[cfg(all(feature = "client-tls", any(target_os = "macos", target_os = "windows", target_os = "ios")))] type HttpsConnector = hyper_tls::HttpsConnector; -#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))] +#[cfg(all(feature = "client-openssl", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))))] type HttpsConnector = hyper_openssl::client::legacy::HttpsConnector; +#[cfg(any( + all(feature = "client-tls", any(target_os = "macos", target_os = "windows", target_os = "ios")), + all(feature = "client-openssl", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))) +))] impl Client< DropContextService< hyper_util::service::TowerToHyperService< @@ -315,7 +338,7 @@ impl Client< /// # Arguments /// * `base_path` - base path of the client API, i.e. "" /// * `ca_certificate` - Path to CA certificate used to authenticate the server - #[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))] + #[cfg(all(feature = "client-openssl", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))))] pub fn try_new_https_pinned( base_path: &str, ca_certificate: CA, @@ -338,7 +361,7 @@ impl Client< /// * `ca_certificate` - Path to CA certificate used to authenticate the server /// * `client_key` - Path to the client private key /// * `client_certificate` - Path to the client's public certificate associated with the private key - #[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))] + #[cfg(all(feature = "client-openssl", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))))] pub fn try_new_https_mutual( base_path: &str, ca_certificate: CA, @@ -396,12 +419,15 @@ pub enum ClientInitError { /// Missing Hostname MissingHost, + /// HTTPS requested but TLS features not enabled + TlsNotEnabled, + /// SSL Connection Error - #[cfg(any(target_os = "macos", target_os = "windows", target_os = "ios"))] + #[cfg(all(feature = "client-tls", any(target_os = "macos", target_os = "windows", target_os = "ios")))] SslError(native_tls::Error), /// SSL Connection Error - #[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))] + #[cfg(all(feature = "client-openssl", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))))] SslError(openssl::error::ErrorStack), } diff --git a/samples/server/petstore/rust-server/output/petstore-with-fake-endpoints-models-for-testing/Cargo.toml b/samples/server/petstore/rust-server/output/petstore-with-fake-endpoints-models-for-testing/Cargo.toml index afb7740ea680..37c3337fab54 100644 --- a/samples/server/petstore/rust-server/output/petstore-with-fake-endpoints-models-for-testing/Cargo.toml +++ b/samples/server/petstore/rust-server/output/petstore-with-fake-endpoints-models-for-testing/Cargo.toml @@ -8,11 +8,23 @@ edition = "2018" publish = ["crates-io"] [features] -default = ["client", "server"] +default = ["client", "server", "client-tls", "client-openssl"] client = [ "multipart", "multipart/client", "swagger/multipart_form", "serde_urlencoded", - "hyper", "percent-encoding", "hyper-util/http1", "hyper-util/http2", "hyper-openssl", "hyper-tls", "native-tls", "openssl", "url" + "hyper", "percent-encoding", "hyper-util/http1", "hyper-util/http2", "url" +] +# TLS support using native-tls (macOS/Windows/iOS) or hyper-tls +client-tls = [ + "client", + "hyper-tls", "native-tls", + "swagger/tls" +] +# TLS support using OpenSSL (Linux and other platforms) +client-openssl = [ + "client", + "hyper-openssl", "openssl", + "swagger/tls" ] server = [ "multipart", "multipart/server", "swagger/multipart_form", @@ -41,7 +53,7 @@ openssl = { version = "0.10", optional = true } async-trait = "0.1.88" chrono = { version = "0.4", features = ["serde"] } futures = "0.3" -swagger = { version = "7.0.0", features = ["serdejson", "server", "client", "tls"] } +swagger = { version = "7.0.0", features = ["serdejson", "server", "client"] } headers = "0.4.0" log = "0.4.27" diff --git a/samples/server/petstore/rust-server/output/petstore-with-fake-endpoints-models-for-testing/README.md b/samples/server/petstore/rust-server/output/petstore-with-fake-endpoints-models-for-testing/README.md index 12121f696964..0a6efedd1408 100644 --- a/samples/server/petstore/rust-server/output/petstore-with-fake-endpoints-models-for-testing/README.md +++ b/samples/server/petstore/rust-server/output/petstore-with-fake-endpoints-models-for-testing/README.md @@ -133,6 +133,12 @@ The generated library has a few optional features that can be activated through * `client` * This defaults to enabled and creates the basic skeleton of a client implementation based on hyper * The constructed client implements the API trait by making remote API call. +* `client-tls` + * This defaults to enabled and provides HTTPS support using native-tls (macOS/Windows/iOS). + * Enable this feature for TLS support on Apple and Windows platforms. +* `client-openssl` + * This defaults to enabled and provides HTTPS support using OpenSSL (Linux and other platforms). + * Enable this feature for TLS support on Linux and other Unix-like platforms. * `conversions` * This defaults to disabled and creates extra derives on models to allow "transmogrification" between objects of structurally similar types. * `cli` @@ -141,6 +147,22 @@ The generated library has a few optional features that can be activated through * This defaults to disabled and allows JSON Schema validation of received data using `MakeService::set_validation` or `Service::set_validation`. * Note, enabling validation will have a performance penalty, especially if the API heavily uses regex based checks. +### Minimal dependencies (no TLS) + +If you only need HTTP support and want to minimize dependencies (e.g., to avoid OpenSSL build requirements), you can disable the default TLS features: + +```toml +[dependencies] +petstore-with-fake-endpoints-models-for-testing = { version = "1.0.0", default-features = false, features = ["server"] } +``` + +Or for client-only without TLS: + +```toml +[dependencies] +petstore-with-fake-endpoints-models-for-testing = { version = "1.0.0", default-features = false, features = ["client"] } +``` + See https://doc.rust-lang.org/cargo/reference/manifest.html#the-features-section for how to use features in your `Cargo.toml`. ## Documentation for API Endpoints diff --git a/samples/server/petstore/rust-server/output/petstore-with-fake-endpoints-models-for-testing/src/client/mod.rs b/samples/server/petstore/rust-server/output/petstore-with-fake-endpoints-models-for-testing/src/client/mod.rs index 93a264aefb97..277243480ae9 100644 --- a/samples/server/petstore/rust-server/output/petstore-with-fake-endpoints-models-for-testing/src/client/mod.rs +++ b/samples/server/petstore/rust-server/output/petstore-with-fake-endpoints-models-for-testing/src/client/mod.rs @@ -193,6 +193,10 @@ impl Client< #[derive(Debug, Clone)] pub enum HyperClient { Http(hyper_util::client::legacy::Client>), + #[cfg(any( + all(feature = "client-tls", any(target_os = "macos", target_os = "windows", target_os = "ios")), + all(feature = "client-openssl", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))) + ))] Https(hyper_util::client::legacy::Client>), } @@ -204,7 +208,11 @@ impl Service>> for HyperClient { fn call(&self, req: Request>) -> Self::Future { match self { HyperClient::Http(client) => client.request(req), - HyperClient::Https(client) => client.request(req) + #[cfg(any( + all(feature = "client-tls", any(target_os = "macos", target_os = "windows", target_os = "ios")), + all(feature = "client-openssl", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))) + ))] + HyperClient::Https(client) => client.request(req), } } } @@ -230,12 +238,23 @@ impl Client, C> where "http" => { HyperClient::Http(hyper_util::client::legacy::Client::builder(hyper_util::rt::TokioExecutor::new()).build(connector.build())) }, + #[cfg(any( + all(feature = "client-tls", any(target_os = "macos", target_os = "windows", target_os = "ios")), + all(feature = "client-openssl", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))) + ))] "https" => { let connector = connector.https() .build() .map_err(ClientInitError::SslError)?; HyperClient::Https(hyper_util::client::legacy::Client::builder(hyper_util::rt::TokioExecutor::new()).build(connector)) }, + #[cfg(not(any( + all(feature = "client-tls", any(target_os = "macos", target_os = "windows", target_os = "ios")), + all(feature = "client-openssl", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))) + )))] + "https" => { + return Err(ClientInitError::TlsNotEnabled); + }, _ => { return Err(ClientInitError::InvalidScheme); } @@ -278,12 +297,16 @@ impl Client< } } -#[cfg(any(target_os = "macos", target_os = "windows", target_os = "ios"))] +#[cfg(all(feature = "client-tls", any(target_os = "macos", target_os = "windows", target_os = "ios")))] type HttpsConnector = hyper_tls::HttpsConnector; -#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))] +#[cfg(all(feature = "client-openssl", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))))] type HttpsConnector = hyper_openssl::client::legacy::HttpsConnector; +#[cfg(any( + all(feature = "client-tls", any(target_os = "macos", target_os = "windows", target_os = "ios")), + all(feature = "client-openssl", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))) +))] impl Client< DropContextService< hyper_util::service::TowerToHyperService< @@ -316,7 +339,7 @@ impl Client< /// # Arguments /// * `base_path` - base path of the client API, i.e. "" /// * `ca_certificate` - Path to CA certificate used to authenticate the server - #[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))] + #[cfg(all(feature = "client-openssl", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))))] pub fn try_new_https_pinned( base_path: &str, ca_certificate: CA, @@ -339,7 +362,7 @@ impl Client< /// * `ca_certificate` - Path to CA certificate used to authenticate the server /// * `client_key` - Path to the client private key /// * `client_certificate` - Path to the client's public certificate associated with the private key - #[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))] + #[cfg(all(feature = "client-openssl", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))))] pub fn try_new_https_mutual( base_path: &str, ca_certificate: CA, @@ -397,12 +420,15 @@ pub enum ClientInitError { /// Missing Hostname MissingHost, + /// HTTPS requested but TLS features not enabled + TlsNotEnabled, + /// SSL Connection Error - #[cfg(any(target_os = "macos", target_os = "windows", target_os = "ios"))] + #[cfg(all(feature = "client-tls", any(target_os = "macos", target_os = "windows", target_os = "ios")))] SslError(native_tls::Error), /// SSL Connection Error - #[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))] + #[cfg(all(feature = "client-openssl", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))))] SslError(openssl::error::ErrorStack), } diff --git a/samples/server/petstore/rust-server/output/ping-bearer-auth/Cargo.toml b/samples/server/petstore/rust-server/output/ping-bearer-auth/Cargo.toml index 36d6fcdc04b7..d3e60a48887c 100644 --- a/samples/server/petstore/rust-server/output/ping-bearer-auth/Cargo.toml +++ b/samples/server/petstore/rust-server/output/ping-bearer-auth/Cargo.toml @@ -8,9 +8,21 @@ license = "Unlicense" edition = "2018" [features] -default = ["client", "server"] +default = ["client", "server", "client-tls", "client-openssl"] client = [ - "hyper", "percent-encoding", "hyper-util/http1", "hyper-util/http2", "hyper-openssl", "hyper-tls", "native-tls", "openssl", "url" + "hyper", "percent-encoding", "hyper-util/http1", "hyper-util/http2", "url" +] +# TLS support using native-tls (macOS/Windows/iOS) or hyper-tls +client-tls = [ + "client", + "hyper-tls", "native-tls", + "swagger/tls" +] +# TLS support using OpenSSL (Linux and other platforms) +client-openssl = [ + "client", + "hyper-openssl", "openssl", + "swagger/tls" ] server = [ "serde_ignored", "hyper", "percent-encoding", "url", @@ -37,7 +49,7 @@ openssl = { version = "0.10", optional = true } async-trait = "0.1.88" chrono = { version = "0.4", features = ["serde"] } futures = "0.3" -swagger = { version = "7.0.0", features = ["serdejson", "server", "client", "tls"] } +swagger = { version = "7.0.0", features = ["serdejson", "server", "client"] } headers = "0.4.0" log = "0.4.27" diff --git a/samples/server/petstore/rust-server/output/ping-bearer-auth/README.md b/samples/server/petstore/rust-server/output/ping-bearer-auth/README.md index d6d9b8de49b6..d7ab962954b8 100644 --- a/samples/server/petstore/rust-server/output/ping-bearer-auth/README.md +++ b/samples/server/petstore/rust-server/output/ping-bearer-auth/README.md @@ -109,6 +109,12 @@ The generated library has a few optional features that can be activated through * `client` * This defaults to enabled and creates the basic skeleton of a client implementation based on hyper * The constructed client implements the API trait by making remote API call. +* `client-tls` + * This defaults to enabled and provides HTTPS support using native-tls (macOS/Windows/iOS). + * Enable this feature for TLS support on Apple and Windows platforms. +* `client-openssl` + * This defaults to enabled and provides HTTPS support using OpenSSL (Linux and other platforms). + * Enable this feature for TLS support on Linux and other Unix-like platforms. * `conversions` * This defaults to disabled and creates extra derives on models to allow "transmogrification" between objects of structurally similar types. * `cli` @@ -117,6 +123,22 @@ The generated library has a few optional features that can be activated through * This defaults to disabled and allows JSON Schema validation of received data using `MakeService::set_validation` or `Service::set_validation`. * Note, enabling validation will have a performance penalty, especially if the API heavily uses regex based checks. +### Minimal dependencies (no TLS) + +If you only need HTTP support and want to minimize dependencies (e.g., to avoid OpenSSL build requirements), you can disable the default TLS features: + +```toml +[dependencies] +ping-bearer-auth = { version = "1.0.0", default-features = false, features = ["server"] } +``` + +Or for client-only without TLS: + +```toml +[dependencies] +ping-bearer-auth = { version = "1.0.0", default-features = false, features = ["client"] } +``` + See https://doc.rust-lang.org/cargo/reference/manifest.html#the-features-section for how to use features in your `Cargo.toml`. ## Documentation for API Endpoints diff --git a/samples/server/petstore/rust-server/output/ping-bearer-auth/src/client/mod.rs b/samples/server/petstore/rust-server/output/ping-bearer-auth/src/client/mod.rs index 7918d022c501..22acaaaff83f 100644 --- a/samples/server/petstore/rust-server/output/ping-bearer-auth/src/client/mod.rs +++ b/samples/server/petstore/rust-server/output/ping-bearer-auth/src/client/mod.rs @@ -156,6 +156,10 @@ impl Client< #[derive(Debug, Clone)] pub enum HyperClient { Http(hyper_util::client::legacy::Client>), + #[cfg(any( + all(feature = "client-tls", any(target_os = "macos", target_os = "windows", target_os = "ios")), + all(feature = "client-openssl", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))) + ))] Https(hyper_util::client::legacy::Client>), } @@ -167,7 +171,11 @@ impl Service>> for HyperClient { fn call(&self, req: Request>) -> Self::Future { match self { HyperClient::Http(client) => client.request(req), - HyperClient::Https(client) => client.request(req) + #[cfg(any( + all(feature = "client-tls", any(target_os = "macos", target_os = "windows", target_os = "ios")), + all(feature = "client-openssl", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))) + ))] + HyperClient::Https(client) => client.request(req), } } } @@ -193,12 +201,23 @@ impl Client, C> where "http" => { HyperClient::Http(hyper_util::client::legacy::Client::builder(hyper_util::rt::TokioExecutor::new()).build(connector.build())) }, + #[cfg(any( + all(feature = "client-tls", any(target_os = "macos", target_os = "windows", target_os = "ios")), + all(feature = "client-openssl", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))) + ))] "https" => { let connector = connector.https() .build() .map_err(ClientInitError::SslError)?; HyperClient::Https(hyper_util::client::legacy::Client::builder(hyper_util::rt::TokioExecutor::new()).build(connector)) }, + #[cfg(not(any( + all(feature = "client-tls", any(target_os = "macos", target_os = "windows", target_os = "ios")), + all(feature = "client-openssl", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))) + )))] + "https" => { + return Err(ClientInitError::TlsNotEnabled); + }, _ => { return Err(ClientInitError::InvalidScheme); } @@ -241,12 +260,16 @@ impl Client< } } -#[cfg(any(target_os = "macos", target_os = "windows", target_os = "ios"))] +#[cfg(all(feature = "client-tls", any(target_os = "macos", target_os = "windows", target_os = "ios")))] type HttpsConnector = hyper_tls::HttpsConnector; -#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))] +#[cfg(all(feature = "client-openssl", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))))] type HttpsConnector = hyper_openssl::client::legacy::HttpsConnector; +#[cfg(any( + all(feature = "client-tls", any(target_os = "macos", target_os = "windows", target_os = "ios")), + all(feature = "client-openssl", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))) +))] impl Client< DropContextService< hyper_util::service::TowerToHyperService< @@ -279,7 +302,7 @@ impl Client< /// # Arguments /// * `base_path` - base path of the client API, i.e. "" /// * `ca_certificate` - Path to CA certificate used to authenticate the server - #[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))] + #[cfg(all(feature = "client-openssl", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))))] pub fn try_new_https_pinned( base_path: &str, ca_certificate: CA, @@ -302,7 +325,7 @@ impl Client< /// * `ca_certificate` - Path to CA certificate used to authenticate the server /// * `client_key` - Path to the client private key /// * `client_certificate` - Path to the client's public certificate associated with the private key - #[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))] + #[cfg(all(feature = "client-openssl", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))))] pub fn try_new_https_mutual( base_path: &str, ca_certificate: CA, @@ -360,12 +383,15 @@ pub enum ClientInitError { /// Missing Hostname MissingHost, + /// HTTPS requested but TLS features not enabled + TlsNotEnabled, + /// SSL Connection Error - #[cfg(any(target_os = "macos", target_os = "windows", target_os = "ios"))] + #[cfg(all(feature = "client-tls", any(target_os = "macos", target_os = "windows", target_os = "ios")))] SslError(native_tls::Error), /// SSL Connection Error - #[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))] + #[cfg(all(feature = "client-openssl", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))))] SslError(openssl::error::ErrorStack), } diff --git a/samples/server/petstore/rust-server/output/rust-server-test/Cargo.toml b/samples/server/petstore/rust-server/output/rust-server-test/Cargo.toml index c5bde100d3dc..6886c7dd31b5 100644 --- a/samples/server/petstore/rust-server/output/rust-server-test/Cargo.toml +++ b/samples/server/petstore/rust-server/output/rust-server-test/Cargo.toml @@ -8,9 +8,21 @@ license = "Unlicense" edition = "2018" [features] -default = ["client", "server"] +default = ["client", "server", "client-tls", "client-openssl"] client = [ - "hyper", "percent-encoding", "hyper-util/http1", "hyper-util/http2", "hyper-openssl", "hyper-tls", "native-tls", "openssl", "url" + "hyper", "percent-encoding", "hyper-util/http1", "hyper-util/http2", "url" +] +# TLS support using native-tls (macOS/Windows/iOS) or hyper-tls +client-tls = [ + "client", + "hyper-tls", "native-tls", + "swagger/tls" +] +# TLS support using OpenSSL (Linux and other platforms) +client-openssl = [ + "client", + "hyper-openssl", "openssl", + "swagger/tls" ] server = [ "serde_ignored", "hyper", "percent-encoding", "url", @@ -37,7 +49,7 @@ openssl = { version = "0.10", optional = true } async-trait = "0.1.88" chrono = { version = "0.4", features = ["serde"] } futures = "0.3" -swagger = { version = "7.0.0", features = ["serdejson", "server", "client", "tls"] } +swagger = { version = "7.0.0", features = ["serdejson", "server", "client"] } headers = "0.4.0" log = "0.4.27" diff --git a/samples/server/petstore/rust-server/output/rust-server-test/README.md b/samples/server/petstore/rust-server/output/rust-server-test/README.md index 05ed29e21a4c..c7e3e87a5b13 100644 --- a/samples/server/petstore/rust-server/output/rust-server-test/README.md +++ b/samples/server/petstore/rust-server/output/rust-server-test/README.md @@ -115,6 +115,12 @@ The generated library has a few optional features that can be activated through * `client` * This defaults to enabled and creates the basic skeleton of a client implementation based on hyper * The constructed client implements the API trait by making remote API call. +* `client-tls` + * This defaults to enabled and provides HTTPS support using native-tls (macOS/Windows/iOS). + * Enable this feature for TLS support on Apple and Windows platforms. +* `client-openssl` + * This defaults to enabled and provides HTTPS support using OpenSSL (Linux and other platforms). + * Enable this feature for TLS support on Linux and other Unix-like platforms. * `conversions` * This defaults to disabled and creates extra derives on models to allow "transmogrification" between objects of structurally similar types. * `cli` @@ -123,6 +129,22 @@ The generated library has a few optional features that can be activated through * This defaults to disabled and allows JSON Schema validation of received data using `MakeService::set_validation` or `Service::set_validation`. * Note, enabling validation will have a performance penalty, especially if the API heavily uses regex based checks. +### Minimal dependencies (no TLS) + +If you only need HTTP support and want to minimize dependencies (e.g., to avoid OpenSSL build requirements), you can disable the default TLS features: + +```toml +[dependencies] +rust-server-test = { version = "2.3.4", default-features = false, features = ["server"] } +``` + +Or for client-only without TLS: + +```toml +[dependencies] +rust-server-test = { version = "2.3.4", default-features = false, features = ["client"] } +``` + See https://doc.rust-lang.org/cargo/reference/manifest.html#the-features-section for how to use features in your `Cargo.toml`. ## Documentation for API Endpoints diff --git a/samples/server/petstore/rust-server/output/rust-server-test/src/client/mod.rs b/samples/server/petstore/rust-server/output/rust-server-test/src/client/mod.rs index 9c04dae37601..b30e11ef7710 100644 --- a/samples/server/petstore/rust-server/output/rust-server-test/src/client/mod.rs +++ b/samples/server/petstore/rust-server/output/rust-server-test/src/client/mod.rs @@ -164,6 +164,10 @@ impl Client< #[derive(Debug, Clone)] pub enum HyperClient { Http(hyper_util::client::legacy::Client>), + #[cfg(any( + all(feature = "client-tls", any(target_os = "macos", target_os = "windows", target_os = "ios")), + all(feature = "client-openssl", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))) + ))] Https(hyper_util::client::legacy::Client>), } @@ -175,7 +179,11 @@ impl Service>> for HyperClient { fn call(&self, req: Request>) -> Self::Future { match self { HyperClient::Http(client) => client.request(req), - HyperClient::Https(client) => client.request(req) + #[cfg(any( + all(feature = "client-tls", any(target_os = "macos", target_os = "windows", target_os = "ios")), + all(feature = "client-openssl", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))) + ))] + HyperClient::Https(client) => client.request(req), } } } @@ -201,12 +209,23 @@ impl Client, C> where "http" => { HyperClient::Http(hyper_util::client::legacy::Client::builder(hyper_util::rt::TokioExecutor::new()).build(connector.build())) }, + #[cfg(any( + all(feature = "client-tls", any(target_os = "macos", target_os = "windows", target_os = "ios")), + all(feature = "client-openssl", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))) + ))] "https" => { let connector = connector.https() .build() .map_err(ClientInitError::SslError)?; HyperClient::Https(hyper_util::client::legacy::Client::builder(hyper_util::rt::TokioExecutor::new()).build(connector)) }, + #[cfg(not(any( + all(feature = "client-tls", any(target_os = "macos", target_os = "windows", target_os = "ios")), + all(feature = "client-openssl", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))) + )))] + "https" => { + return Err(ClientInitError::TlsNotEnabled); + }, _ => { return Err(ClientInitError::InvalidScheme); } @@ -249,12 +268,16 @@ impl Client< } } -#[cfg(any(target_os = "macos", target_os = "windows", target_os = "ios"))] +#[cfg(all(feature = "client-tls", any(target_os = "macos", target_os = "windows", target_os = "ios")))] type HttpsConnector = hyper_tls::HttpsConnector; -#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))] +#[cfg(all(feature = "client-openssl", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))))] type HttpsConnector = hyper_openssl::client::legacy::HttpsConnector; +#[cfg(any( + all(feature = "client-tls", any(target_os = "macos", target_os = "windows", target_os = "ios")), + all(feature = "client-openssl", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))) +))] impl Client< DropContextService< hyper_util::service::TowerToHyperService< @@ -287,7 +310,7 @@ impl Client< /// # Arguments /// * `base_path` - base path of the client API, i.e. "" /// * `ca_certificate` - Path to CA certificate used to authenticate the server - #[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))] + #[cfg(all(feature = "client-openssl", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))))] pub fn try_new_https_pinned( base_path: &str, ca_certificate: CA, @@ -310,7 +333,7 @@ impl Client< /// * `ca_certificate` - Path to CA certificate used to authenticate the server /// * `client_key` - Path to the client private key /// * `client_certificate` - Path to the client's public certificate associated with the private key - #[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))] + #[cfg(all(feature = "client-openssl", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))))] pub fn try_new_https_mutual( base_path: &str, ca_certificate: CA, @@ -368,12 +391,15 @@ pub enum ClientInitError { /// Missing Hostname MissingHost, + /// HTTPS requested but TLS features not enabled + TlsNotEnabled, + /// SSL Connection Error - #[cfg(any(target_os = "macos", target_os = "windows", target_os = "ios"))] + #[cfg(all(feature = "client-tls", any(target_os = "macos", target_os = "windows", target_os = "ios")))] SslError(native_tls::Error), /// SSL Connection Error - #[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))] + #[cfg(all(feature = "client-openssl", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))))] SslError(openssl::error::ErrorStack), } From a8e6ee2b32e08195813277c87137c180753c3052 Mon Sep 17 00:00:00 2001 From: David Steele Date: Tue, 27 Jan 2026 10:11:58 +0000 Subject: [PATCH 2/2] feat: Switch default features so a user must select SSL backend --- .github/workflows/samples-rust-server.yaml | 6 +- .../main/resources/rust-server/Cargo.mustache | 39 +++++----- .../resources/rust-server/README.mustache | 26 ++++--- .../resources/rust-server/bin-cli.mustache | 18 +++-- .../resources/rust-server/client-mod.mustache | 73 ++++++++++--------- .../rust-server/example-client-main.mustache | 38 +++++++--- .../rust-server/server-callbacks.mustache | 19 ++--- .../output/multipart-v3/Cargo.toml | 38 +++++----- .../rust-server/output/multipart-v3/README.md | 26 ++++--- .../output/multipart-v3/bin/cli.rs | 18 +++-- .../multipart-v3/examples/client/main.rs | 38 +++++++--- .../output/multipart-v3/src/client/mod.rs | 73 ++++++++++--------- .../output/no-example-v3/Cargo.toml | 38 +++++----- .../output/no-example-v3/README.md | 26 ++++--- .../output/no-example-v3/bin/cli.rs | 18 +++-- .../no-example-v3/examples/client/main.rs | 38 +++++++--- .../output/no-example-v3/src/client/mod.rs | 73 ++++++++++--------- .../rust-server/output/openapi-v3/Cargo.toml | 39 +++++----- .../rust-server/output/openapi-v3/README.md | 26 ++++--- .../rust-server/output/openapi-v3/bin/cli.rs | 18 +++-- .../output/openapi-v3/examples/client/main.rs | 38 +++++++--- .../output/openapi-v3/src/client/mod.rs | 73 ++++++++++--------- .../output/openapi-v3/src/server/callbacks.rs | 19 ++--- .../rust-server/output/ops-v3/Cargo.toml | 38 +++++----- .../rust-server/output/ops-v3/README.md | 26 ++++--- .../rust-server/output/ops-v3/bin/cli.rs | 18 +++-- .../output/ops-v3/examples/client/main.rs | 38 +++++++--- .../output/ops-v3/src/client/mod.rs | 73 ++++++++++--------- .../Cargo.toml | 38 +++++----- .../README.md | 26 ++++--- .../bin/cli.rs | 18 +++-- .../examples/client/main.rs | 38 +++++++--- .../src/client/mod.rs | 73 ++++++++++--------- .../output/ping-bearer-auth/Cargo.toml | 38 +++++----- .../output/ping-bearer-auth/README.md | 26 ++++--- .../output/ping-bearer-auth/bin/cli.rs | 18 +++-- .../ping-bearer-auth/examples/client/main.rs | 38 +++++++--- .../output/ping-bearer-auth/src/client/mod.rs | 73 ++++++++++--------- .../output/rust-server-test/Cargo.toml | 38 +++++----- .../output/rust-server-test/README.md | 26 ++++--- .../output/rust-server-test/bin/cli.rs | 18 +++-- .../rust-server-test/examples/client/main.rs | 38 +++++++--- .../output/rust-server-test/src/client/mod.rs | 73 ++++++++++--------- 43 files changed, 916 insertions(+), 674 deletions(-) diff --git a/.github/workflows/samples-rust-server.yaml b/.github/workflows/samples-rust-server.yaml index 249ac5b21883..cc0574614a3c 100644 --- a/.github/workflows/samples-rust-server.yaml +++ b/.github/workflows/samples-rust-server.yaml @@ -64,11 +64,13 @@ jobs: if cargo read-manifest | grep -q '"validate"'; then cargo build --features validate --all-targets fi - # Client without TLS (HTTP-only) + # Test TLS features if they exist if cargo read-manifest | grep -q '"client-tls"'; then + # Client without TLS (HTTP-only) cargo build --no-default-features --features=client --lib - # Client with selective TLS backend + # Client with TLS (using native-tls) cargo build --no-default-features --features=client,client-tls --lib + # Server without TLS cargo build --no-default-features --features=server --lib fi cargo fmt diff --git a/modules/openapi-generator/src/main/resources/rust-server/Cargo.mustache b/modules/openapi-generator/src/main/resources/rust-server/Cargo.mustache index 4e62bb6a4a5d..519c9dcfe393 100644 --- a/modules/openapi-generator/src/main/resources/rust-server/Cargo.mustache +++ b/modules/openapi-generator/src/main/resources/rust-server/Cargo.mustache @@ -32,7 +32,7 @@ homepage = "{{.}}" {{/homePageUrl}} [features] -default = ["client", "server", "client-tls", "client-openssl"] +default = ["client", "server"] client = [ {{#apiUsesMultipartFormData}} "multipart", "multipart/client", "swagger/multipart_form", @@ -49,16 +49,16 @@ client = [ {{! Anything added to the list below, should probably be added to the callbacks list below }} "hyper", "percent-encoding", "hyper-util/http1", "hyper-util/http2", "url" ] -# TLS support using native-tls (macOS/Windows/iOS) or hyper-tls +# TLS support - automatically selects backend based on target OS: +# - macOS/Windows/iOS: native-tls via hyper-tls +# - Other platforms: OpenSSL via hyper-openssl +# Dependencies are in target-specific sections below client-tls = [ "client", - "hyper-tls", "native-tls", - "swagger/tls" -] -# TLS support using OpenSSL (Linux and other platforms) -client-openssl = [ - "client", - "hyper-openssl", "openssl", + "dep:native-tls", + "dep:hyper-tls", + "dep:openssl", + "dep:hyper-openssl", "swagger/tls" ] server = [ @@ -69,7 +69,6 @@ server = [ "mime_multipart", "swagger/multipart_related", {{/apiUsesMultipartRelated}} {{#hasCallbacks}} - "client-tls", {{/hasCallbacks}} {{! Anything added to the list below, should probably be added to the callbacks list above }} "serde_ignored", "hyper", "percent-encoding", "url", @@ -86,14 +85,6 @@ conversion = ["frunk", "frunk_derives", "frunk_core", "frunk-enum-core", "frunk- mock = ["mockall"] validate = [{{^apiUsesByteArray}}"regex",{{/apiUsesByteArray}} "serde_valid", "swagger/serdevalid"] -[target.'cfg(any(target_os = "macos", target_os = "windows", target_os = "ios"))'.dependencies] -native-tls = { version = "0.2", optional = true } -hyper-tls = { version = "0.6", optional = true } - -[target.'cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))'.dependencies] -hyper-openssl = { version = "0.10", optional = true } -openssl = { version = "0.10", optional = true } - [dependencies] # Common async-trait = "0.1.88" @@ -145,6 +136,12 @@ serde_urlencoded = { version = "0.7.1", optional = true } {{/usesUrlEncodedForm}} tower-service = "0.3.3" +# TLS support - all listed here, actual usage determined by cfg attributes in code +native-tls = { version = "0.2", optional = true } +hyper-tls = { version = "0.6", optional = true } +openssl = { version = "0.10", optional = true } +hyper-openssl = { version = "0.10", optional = true } + # Server, and client callback-specific {{^apiUsesByteArray}} lazy_static = { version = "1.5", optional = true } @@ -175,15 +172,13 @@ clap = "4.5" env_logger = "0.11" tokio = { version = "1.49", features = ["full"] } native-tls = "0.2" +openssl = "0.10" +tokio-openssl = "0.6" pin-project = "1.1.10" # Bearer authentication, used in examples jsonwebtoken = {version = "10.0.0", features = ["rust_crypto"]} -[target.'cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))'.dev-dependencies] -tokio-openssl = "0.6" -openssl = "0.10" - [[example]] name = "client" required-features = ["client"] diff --git a/modules/openapi-generator/src/main/resources/rust-server/README.mustache b/modules/openapi-generator/src/main/resources/rust-server/README.mustache index ada4f15edf7e..19a8ba411e06 100644 --- a/modules/openapi-generator/src/main/resources/rust-server/README.mustache +++ b/modules/openapi-generator/src/main/resources/rust-server/README.mustache @@ -127,11 +127,10 @@ The generated library has a few optional features that can be activated through * This defaults to enabled and creates the basic skeleton of a client implementation based on hyper * The constructed client implements the API trait by making remote API call. * `client-tls` - * This defaults to enabled and provides HTTPS support using native-tls (macOS/Windows/iOS). - * Enable this feature for TLS support on Apple and Windows platforms. -* `client-openssl` - * This defaults to enabled and provides HTTPS support using OpenSSL (Linux and other platforms). - * Enable this feature for TLS support on Linux and other Unix-like platforms. + * Optional feature that provides HTTPS support with automatic TLS backend selection: + - macOS/Windows/iOS: native-tls + hyper-tls + - Linux/Unix/others: OpenSSL + hyper-openssl + * Not enabled by default to minimize dependencies. * `conversions` * This defaults to disabled and creates extra derives on models to allow "transmogrification" between objects of structurally similar types. * `cli` @@ -140,22 +139,27 @@ The generated library has a few optional features that can be activated through * This defaults to disabled and allows JSON Schema validation of received data using `MakeService::set_validation` or `Service::set_validation`. * Note, enabling validation will have a performance penalty, especially if the API heavily uses regex based checks. -### Minimal dependencies (no TLS) +### Enabling HTTPS/TLS Support -If you only need HTTP support and want to minimize dependencies (e.g., to avoid OpenSSL build requirements), you can disable the default TLS features: +By default, only HTTP support is included. To enable HTTPS, add the `client-tls` feature: ```toml [dependencies] -{{{packageName}}} = { version = "{{{packageVersion}}}", default-features = false, features = ["server"] } +{{{packageName}}} = { version = "{{{packageVersion}}}", features = ["client-tls"] } ``` -Or for client-only without TLS: - +**For server with callbacks that need HTTPS:** ```toml [dependencies] -{{{packageName}}} = { version = "{{{packageVersion}}}", default-features = false, features = ["client"] } +{{{packageName}}} = { version = "{{{packageVersion}}}", features = ["server", "client-tls"] } ``` +The TLS backend is automatically selected based on your target platform: +- **macOS, Windows, iOS**: Uses `native-tls` (system TLS libraries) +- **Linux, Unix, other platforms**: Uses `openssl` + +This ensures the best compatibility and native integration on each platform. + See https://doc.rust-lang.org/cargo/reference/manifest.html#the-features-section for how to use features in your `Cargo.toml`. ## Documentation for API Endpoints diff --git a/modules/openapi-generator/src/main/resources/rust-server/bin-cli.mustache b/modules/openapi-generator/src/main/resources/rust-server/bin-cli.mustache index cdb4bc65d9a1..994bcd4109e9 100644 --- a/modules/openapi-generator/src/main/resources/rust-server/bin-cli.mustache +++ b/modules/openapi-generator/src/main/resources/rust-server/bin-cli.mustache @@ -45,17 +45,17 @@ struct Cli { server_address: String, /// Path to the client private key if using client-side TLS authentication - #[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))] + #[cfg(all(feature = "client-tls", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))))] #[clap(long, requires_all(&["client_certificate", "server_certificate"]))] client_key: Option, /// Path to the client's public certificate associated with the private key - #[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))] + #[cfg(all(feature = "client-tls", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))))] #[clap(long, requires_all(&["client_key", "server_certificate"]))] client_certificate: Option, /// Path to CA certificate used to authenticate the server - #[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))] + #[cfg(all(feature = "client-tls", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))))] #[clap(long)] server_certificate: Option, @@ -130,7 +130,8 @@ enum Operation { {{/apiInfo}} } -#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))] +// On Linux/Unix with OpenSSL (client-tls feature), support certificate pinning and mutual TLS +#[cfg(all(feature = "client-tls", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))))] fn create_client(args: &Cli, context: ClientContext) -> Result>> { if args.client_certificate.is_some() { debug!("Using mutual TLS"); @@ -156,8 +157,15 @@ fn create_client(args: &Cli, context: ClientContext) -> Result Result>> { + // Client::try_new() automatically detects the URL scheme (http:// or https://) + // and creates the appropriate client. + // Note: Certificate pinning and mutual TLS are only available on Linux/Unix with OpenSSL let client = Client::try_new(&args.server_address).context("Failed to create HTTP(S) client")?; Ok(Box::new(client.with_context(context))) diff --git a/modules/openapi-generator/src/main/resources/rust-server/client-mod.mustache b/modules/openapi-generator/src/main/resources/rust-server/client-mod.mustache index bce3dba34a7e..38c9ca3695d1 100644 --- a/modules/openapi-generator/src/main/resources/rust-server/client-mod.mustache +++ b/modules/openapi-generator/src/main/resources/rust-server/client-mod.mustache @@ -118,14 +118,17 @@ impl Client< } } +#[cfg(all(feature = "client-tls", any(target_os = "macos", target_os = "windows", target_os = "ios")))] +type HyperHttpsConnector = hyper_tls::HttpsConnector; + +#[cfg(all(feature = "client-tls", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))))] +type HyperHttpsConnector = hyper_openssl::client::legacy::HttpsConnector; + #[derive(Debug, Clone)] pub enum HyperClient { Http(hyper_util::client::legacy::Client>), - #[cfg(any( - all(feature = "client-tls", any(target_os = "macos", target_os = "windows", target_os = "ios")), - all(feature = "client-openssl", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))) - ))] - Https(hyper_util::client::legacy::Client>), + #[cfg(feature = "client-tls")] + Https(hyper_util::client::legacy::Client>), } impl Service>> for HyperClient { @@ -136,10 +139,7 @@ impl Service>> for HyperClient { fn call(&self, req: Request>) -> Self::Future { match self { HyperClient::Http(client) => client.request(req), - #[cfg(any( - all(feature = "client-tls", any(target_os = "macos", target_os = "windows", target_os = "ios")), - all(feature = "client-openssl", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))) - ))] + #[cfg(feature = "client-tls")] HyperClient::Https(client) => client.request(req), } } @@ -166,20 +166,15 @@ impl Client, C> where "http" => { HyperClient::Http(hyper_util::client::legacy::Client::builder(hyper_util::rt::TokioExecutor::new()).build(connector.build())) }, - #[cfg(any( - all(feature = "client-tls", any(target_os = "macos", target_os = "windows", target_os = "ios")), - all(feature = "client-openssl", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))) - ))] + #[cfg(feature = "client-tls")] "https" => { - let connector = connector.https() - .build() - .map_err(ClientInitError::SslError)?; - HyperClient::Https(hyper_util::client::legacy::Client::builder(hyper_util::rt::TokioExecutor::new()).build(connector)) + let https_connector = connector + .https() + .build() + .map_err(ClientInitError::SslError)?; + HyperClient::Https(hyper_util::client::legacy::Client::builder(hyper_util::rt::TokioExecutor::new()).build(https_connector)) }, - #[cfg(not(any( - all(feature = "client-tls", any(target_os = "macos", target_os = "windows", target_os = "ios")), - all(feature = "client-openssl", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))) - )))] + #[cfg(not(feature = "client-tls"))] "https" => { return Err(ClientInitError::TlsNotEnabled); }, @@ -228,13 +223,10 @@ impl Client< #[cfg(all(feature = "client-tls", any(target_os = "macos", target_os = "windows", target_os = "ios")))] type HttpsConnector = hyper_tls::HttpsConnector; -#[cfg(all(feature = "client-openssl", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))))] +#[cfg(all(feature = "client-tls", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))))] type HttpsConnector = hyper_openssl::client::legacy::HttpsConnector; -#[cfg(any( - all(feature = "client-tls", any(target_os = "macos", target_os = "windows", target_os = "ios")), - all(feature = "client-openssl", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))) -))] +#[cfg(feature = "client-tls")] impl Client< DropContextService< hyper_util::service::TowerToHyperService< @@ -249,10 +241,25 @@ impl Client< > where C: Clone + Send + Sync + 'static { - /// Create a client with a TLS connection to the server + /// Create a client with a TLS connection to the server using native-tls. /// /// # Arguments - /// * `base_path` - base path of the client API, i.e. "" + /// * `base_path` - base path of the client API, i.e. "" + #[cfg(any(target_os = "macos", target_os = "windows", target_os = "ios"))] + pub fn try_new_https(base_path: &str) -> Result + { + let https_connector = Connector::builder() + .https() + .build() + .map_err(ClientInitError::SslError)?; + Self::try_new_with_connector(base_path, Some("https"), https_connector) + } + + /// Create a client with a TLS connection to the server using OpenSSL. + /// + /// # Arguments + /// * `base_path` - base path of the client API, i.e. "" + #[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))] pub fn try_new_https(base_path: &str) -> Result { let https_connector = Connector::builder() @@ -265,9 +272,9 @@ impl Client< /// Create a client with a TLS connection to the server using a pinned certificate /// /// # Arguments - /// * `base_path` - base path of the client API, i.e. "" + /// * `base_path` - base path of the client API, i.e. "" /// * `ca_certificate` - Path to CA certificate used to authenticate the server - #[cfg(all(feature = "client-openssl", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))))] + #[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))] pub fn try_new_https_pinned( base_path: &str, ca_certificate: CA, @@ -286,11 +293,11 @@ impl Client< /// Create a client with a mutually authenticated TLS connection to the server. /// /// # Arguments - /// * `base_path` - base path of the client API, i.e. "" + /// * `base_path` - base path of the client API, i.e. "" /// * `ca_certificate` - Path to CA certificate used to authenticate the server /// * `client_key` - Path to the client private key /// * `client_certificate` - Path to the client's public certificate associated with the private key - #[cfg(all(feature = "client-openssl", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))))] + #[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))] pub fn try_new_https_mutual( base_path: &str, ca_certificate: CA, @@ -356,7 +363,7 @@ pub enum ClientInitError { SslError(native_tls::Error), /// SSL Connection Error - #[cfg(all(feature = "client-openssl", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))))] + #[cfg(all(feature = "client-tls", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))))] SslError(openssl::error::ErrorStack), } diff --git a/modules/openapi-generator/src/main/resources/rust-server/example-client-main.mustache b/modules/openapi-generator/src/main/resources/rust-server/example-client-main.mustache index ba2e7a3b510a..4c8c9753768b 100644 --- a/modules/openapi-generator/src/main/resources/rust-server/example-client-main.mustache +++ b/modules/openapi-generator/src/main/resources/rust-server/example-client-main.mustache @@ -115,17 +115,33 @@ fn main() { let context: ClientContext = swagger::make_context!(ContextBuilder, EmptyContext, auth_data, XSpanIdString::default()); - let mut client : Box> = if is_https { - // Using Simple HTTPS - let client = Box::new(Client::try_new_https(&base_url) - .expect("Failed to create HTTPS client")); - Box::new(client.with_context(context)) - } else { - // Using HTTP - let client = Box::new(Client::try_new_http( - &base_url) - .expect("Failed to create HTTP client")); - Box::new(client.with_context(context)) + let mut client : Box> = { + #[cfg(feature = "client-tls")] + { + if is_https { + // Using HTTPS with native-tls + let client = Box::new(Client::try_new_https(&base_url) + .expect("Failed to create HTTPS client")); + Box::new(client.with_context(context)) + } else { + // Using HTTP + let client = Box::new(Client::try_new_http(&base_url) + .expect("Failed to create HTTP client")); + Box::new(client.with_context(context)) + } + } + + #[cfg(not(feature = "client-tls"))] + { + if is_https { + panic!("HTTPS requested but TLS support not enabled. \ + Enable the 'client-tls' feature to use HTTPS."); + } + // Using HTTP only + let client = Box::new(Client::try_new_http(&base_url) + .expect("Failed to create HTTP client")); + Box::new(client.with_context(context)) + } }; let mut rt = tokio::runtime::Runtime::new().unwrap(); diff --git a/modules/openapi-generator/src/main/resources/rust-server/server-callbacks.mustache b/modules/openapi-generator/src/main/resources/rust-server/server-callbacks.mustache index fa86bd6dff9d..bee6406d981c 100644 --- a/modules/openapi-generator/src/main/resources/rust-server/server-callbacks.mustache +++ b/modules/openapi-generator/src/main/resources/rust-server/server-callbacks.mustache @@ -100,26 +100,23 @@ impl Client; -#[cfg(all(feature = "client-openssl", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))))] +#[cfg(all(feature = "client-tls", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))))] type HttpsConnector = hyper_openssl::client::legacy::HttpsConnector; -#[cfg(any( - all(feature = "client-tls", any(target_os = "macos", target_os = "windows", target_os = "ios")), - all(feature = "client-openssl", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))) -))] +#[cfg(feature = "client-tls")] impl Client>>, C>, C> where C: Clone + Send + Sync + 'static { - /// Create a client with a TLS connection to the server. - #[cfg(all(feature = "client-tls", any(target_os = "macos", target_os = "windows", target_os = "ios")))] + /// Create a client with a TLS connection to the server using native-tls. + #[cfg(any(target_os = "macos", target_os = "windows", target_os = "ios"))] pub fn new_https() -> Result { let https_connector = Connector::builder().https().build()?; Ok(Self::new_with_connector(https_connector)) } - /// Create a client with a TLS connection to the server. - #[cfg(all(feature = "client-openssl", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))))] + /// Create a client with a TLS connection to the server using OpenSSL. + #[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))] pub fn new_https() -> Result { let https_connector = Connector::builder().https().build()?; @@ -130,7 +127,7 @@ impl Client( ca_certificate: CA, ) -> Result where @@ -149,7 +146,7 @@ impl Client( ca_certificate: CA, client_key: K, diff --git a/samples/server/petstore/rust-server/output/multipart-v3/Cargo.toml b/samples/server/petstore/rust-server/output/multipart-v3/Cargo.toml index 1623d54231a4..4dc568ed1840 100644 --- a/samples/server/petstore/rust-server/output/multipart-v3/Cargo.toml +++ b/samples/server/petstore/rust-server/output/multipart-v3/Cargo.toml @@ -8,22 +8,22 @@ license = "Unlicense" edition = "2018" [features] -default = ["client", "server", "client-tls", "client-openssl"] +default = ["client", "server"] client = [ "multipart", "multipart/client", "swagger/multipart_form", "mime_multipart", "swagger/multipart_related", "hyper", "percent-encoding", "hyper-util/http1", "hyper-util/http2", "url" ] -# TLS support using native-tls (macOS/Windows/iOS) or hyper-tls +# TLS support - automatically selects backend based on target OS: +# - macOS/Windows/iOS: native-tls via hyper-tls +# - Other platforms: OpenSSL via hyper-openssl +# Dependencies are in target-specific sections below client-tls = [ "client", - "hyper-tls", "native-tls", - "swagger/tls" -] -# TLS support using OpenSSL (Linux and other platforms) -client-openssl = [ - "client", - "hyper-openssl", "openssl", + "dep:native-tls", + "dep:hyper-tls", + "dep:openssl", + "dep:hyper-openssl", "swagger/tls" ] server = [ @@ -40,14 +40,6 @@ conversion = ["frunk", "frunk_derives", "frunk_core", "frunk-enum-core", "frunk- mock = ["mockall"] validate = [ "serde_valid", "swagger/serdevalid"] -[target.'cfg(any(target_os = "macos", target_os = "windows", target_os = "ios"))'.dependencies] -native-tls = { version = "0.2", optional = true } -hyper-tls = { version = "0.6", optional = true } - -[target.'cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))'.dependencies] -hyper-openssl = { version = "0.10", optional = true } -openssl = { version = "0.10", optional = true } - [dependencies] # Common async-trait = "0.1.88" @@ -84,6 +76,12 @@ url = { version = "2.5", optional = true } # Client-specific tower-service = "0.3.3" +# TLS support - all listed here, actual usage determined by cfg attributes in code +native-tls = { version = "0.2", optional = true } +hyper-tls = { version = "0.6", optional = true } +openssl = { version = "0.10", optional = true } +hyper-openssl = { version = "0.10", optional = true } + # Server, and client callback-specific percent-encoding = { version = "2.3.2", optional = true } @@ -107,15 +105,13 @@ clap = "4.5" env_logger = "0.11" tokio = { version = "1.49", features = ["full"] } native-tls = "0.2" +openssl = "0.10" +tokio-openssl = "0.6" pin-project = "1.1.10" # Bearer authentication, used in examples jsonwebtoken = {version = "10.0.0", features = ["rust_crypto"]} -[target.'cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))'.dev-dependencies] -tokio-openssl = "0.6" -openssl = "0.10" - [[example]] name = "client" required-features = ["client"] diff --git a/samples/server/petstore/rust-server/output/multipart-v3/README.md b/samples/server/petstore/rust-server/output/multipart-v3/README.md index 52645b8ce0d7..766b5cd79b2d 100644 --- a/samples/server/petstore/rust-server/output/multipart-v3/README.md +++ b/samples/server/petstore/rust-server/output/multipart-v3/README.md @@ -112,11 +112,10 @@ The generated library has a few optional features that can be activated through * This defaults to enabled and creates the basic skeleton of a client implementation based on hyper * The constructed client implements the API trait by making remote API call. * `client-tls` - * This defaults to enabled and provides HTTPS support using native-tls (macOS/Windows/iOS). - * Enable this feature for TLS support on Apple and Windows platforms. -* `client-openssl` - * This defaults to enabled and provides HTTPS support using OpenSSL (Linux and other platforms). - * Enable this feature for TLS support on Linux and other Unix-like platforms. + * Optional feature that provides HTTPS support with automatic TLS backend selection: + - macOS/Windows/iOS: native-tls + hyper-tls + - Linux/Unix/others: OpenSSL + hyper-openssl + * Not enabled by default to minimize dependencies. * `conversions` * This defaults to disabled and creates extra derives on models to allow "transmogrification" between objects of structurally similar types. * `cli` @@ -125,22 +124,27 @@ The generated library has a few optional features that can be activated through * This defaults to disabled and allows JSON Schema validation of received data using `MakeService::set_validation` or `Service::set_validation`. * Note, enabling validation will have a performance penalty, especially if the API heavily uses regex based checks. -### Minimal dependencies (no TLS) +### Enabling HTTPS/TLS Support -If you only need HTTP support and want to minimize dependencies (e.g., to avoid OpenSSL build requirements), you can disable the default TLS features: +By default, only HTTP support is included. To enable HTTPS, add the `client-tls` feature: ```toml [dependencies] -multipart-v3 = { version = "1.0.7", default-features = false, features = ["server"] } +multipart-v3 = { version = "1.0.7", features = ["client-tls"] } ``` -Or for client-only without TLS: - +**For server with callbacks that need HTTPS:** ```toml [dependencies] -multipart-v3 = { version = "1.0.7", default-features = false, features = ["client"] } +multipart-v3 = { version = "1.0.7", features = ["server", "client-tls"] } ``` +The TLS backend is automatically selected based on your target platform: +- **macOS, Windows, iOS**: Uses `native-tls` (system TLS libraries) +- **Linux, Unix, other platforms**: Uses `openssl` + +This ensures the best compatibility and native integration on each platform. + See https://doc.rust-lang.org/cargo/reference/manifest.html#the-features-section for how to use features in your `Cargo.toml`. ## Documentation for API Endpoints diff --git a/samples/server/petstore/rust-server/output/multipart-v3/bin/cli.rs b/samples/server/petstore/rust-server/output/multipart-v3/bin/cli.rs index ff6a1dfd926d..df88edb242f5 100644 --- a/samples/server/petstore/rust-server/output/multipart-v3/bin/cli.rs +++ b/samples/server/petstore/rust-server/output/multipart-v3/bin/cli.rs @@ -35,17 +35,17 @@ struct Cli { server_address: String, /// Path to the client private key if using client-side TLS authentication - #[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))] + #[cfg(all(feature = "client-tls", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))))] #[clap(long, requires_all(&["client_certificate", "server_certificate"]))] client_key: Option, /// Path to the client's public certificate associated with the private key - #[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))] + #[cfg(all(feature = "client-tls", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))))] #[clap(long, requires_all(&["client_key", "server_certificate"]))] client_certificate: Option, /// Path to CA certificate used to authenticate the server - #[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))] + #[cfg(all(feature = "client-tls", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))))] #[clap(long)] server_certificate: Option, @@ -83,7 +83,8 @@ enum Operation { }, } -#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))] +// On Linux/Unix with OpenSSL (client-tls feature), support certificate pinning and mutual TLS +#[cfg(all(feature = "client-tls", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))))] fn create_client(args: &Cli, context: ClientContext) -> Result>> { if args.client_certificate.is_some() { debug!("Using mutual TLS"); @@ -109,8 +110,15 @@ fn create_client(args: &Cli, context: ClientContext) -> Result Result>> { + // Client::try_new() automatically detects the URL scheme (http:// or https://) + // and creates the appropriate client. + // Note: Certificate pinning and mutual TLS are only available on Linux/Unix with OpenSSL let client = Client::try_new(&args.server_address).context("Failed to create HTTP(S) client")?; Ok(Box::new(client.with_context(context))) diff --git a/samples/server/petstore/rust-server/output/multipart-v3/examples/client/main.rs b/samples/server/petstore/rust-server/output/multipart-v3/examples/client/main.rs index bfec3bbe3bef..7aecfea8630f 100644 --- a/samples/server/petstore/rust-server/output/multipart-v3/examples/client/main.rs +++ b/samples/server/petstore/rust-server/output/multipart-v3/examples/client/main.rs @@ -89,17 +89,33 @@ fn main() { let context: ClientContext = swagger::make_context!(ContextBuilder, EmptyContext, auth_data, XSpanIdString::default()); - let mut client : Box> = if is_https { - // Using Simple HTTPS - let client = Box::new(Client::try_new_https(&base_url) - .expect("Failed to create HTTPS client")); - Box::new(client.with_context(context)) - } else { - // Using HTTP - let client = Box::new(Client::try_new_http( - &base_url) - .expect("Failed to create HTTP client")); - Box::new(client.with_context(context)) + let mut client : Box> = { + #[cfg(feature = "client-tls")] + { + if is_https { + // Using HTTPS with native-tls + let client = Box::new(Client::try_new_https(&base_url) + .expect("Failed to create HTTPS client")); + Box::new(client.with_context(context)) + } else { + // Using HTTP + let client = Box::new(Client::try_new_http(&base_url) + .expect("Failed to create HTTP client")); + Box::new(client.with_context(context)) + } + } + + #[cfg(not(feature = "client-tls"))] + { + if is_https { + panic!("HTTPS requested but TLS support not enabled. \ + Enable the 'client-tls' feature to use HTTPS."); + } + // Using HTTP only + let client = Box::new(Client::try_new_http(&base_url) + .expect("Failed to create HTTP client")); + Box::new(client.with_context(context)) + } }; let mut rt = tokio::runtime::Runtime::new().unwrap(); diff --git a/samples/server/petstore/rust-server/output/multipart-v3/src/client/mod.rs b/samples/server/petstore/rust-server/output/multipart-v3/src/client/mod.rs index 22c64952d214..9d334d228e1e 100644 --- a/samples/server/petstore/rust-server/output/multipart-v3/src/client/mod.rs +++ b/samples/server/petstore/rust-server/output/multipart-v3/src/client/mod.rs @@ -160,14 +160,17 @@ impl Client< } } +#[cfg(all(feature = "client-tls", any(target_os = "macos", target_os = "windows", target_os = "ios")))] +type HyperHttpsConnector = hyper_tls::HttpsConnector; + +#[cfg(all(feature = "client-tls", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))))] +type HyperHttpsConnector = hyper_openssl::client::legacy::HttpsConnector; + #[derive(Debug, Clone)] pub enum HyperClient { Http(hyper_util::client::legacy::Client>), - #[cfg(any( - all(feature = "client-tls", any(target_os = "macos", target_os = "windows", target_os = "ios")), - all(feature = "client-openssl", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))) - ))] - Https(hyper_util::client::legacy::Client>), + #[cfg(feature = "client-tls")] + Https(hyper_util::client::legacy::Client>), } impl Service>> for HyperClient { @@ -178,10 +181,7 @@ impl Service>> for HyperClient { fn call(&self, req: Request>) -> Self::Future { match self { HyperClient::Http(client) => client.request(req), - #[cfg(any( - all(feature = "client-tls", any(target_os = "macos", target_os = "windows", target_os = "ios")), - all(feature = "client-openssl", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))) - ))] + #[cfg(feature = "client-tls")] HyperClient::Https(client) => client.request(req), } } @@ -208,20 +208,15 @@ impl Client, C> where "http" => { HyperClient::Http(hyper_util::client::legacy::Client::builder(hyper_util::rt::TokioExecutor::new()).build(connector.build())) }, - #[cfg(any( - all(feature = "client-tls", any(target_os = "macos", target_os = "windows", target_os = "ios")), - all(feature = "client-openssl", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))) - ))] + #[cfg(feature = "client-tls")] "https" => { - let connector = connector.https() - .build() - .map_err(ClientInitError::SslError)?; - HyperClient::Https(hyper_util::client::legacy::Client::builder(hyper_util::rt::TokioExecutor::new()).build(connector)) + let https_connector = connector + .https() + .build() + .map_err(ClientInitError::SslError)?; + HyperClient::Https(hyper_util::client::legacy::Client::builder(hyper_util::rt::TokioExecutor::new()).build(https_connector)) }, - #[cfg(not(any( - all(feature = "client-tls", any(target_os = "macos", target_os = "windows", target_os = "ios")), - all(feature = "client-openssl", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))) - )))] + #[cfg(not(feature = "client-tls"))] "https" => { return Err(ClientInitError::TlsNotEnabled); }, @@ -270,13 +265,10 @@ impl Client< #[cfg(all(feature = "client-tls", any(target_os = "macos", target_os = "windows", target_os = "ios")))] type HttpsConnector = hyper_tls::HttpsConnector; -#[cfg(all(feature = "client-openssl", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))))] +#[cfg(all(feature = "client-tls", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))))] type HttpsConnector = hyper_openssl::client::legacy::HttpsConnector; -#[cfg(any( - all(feature = "client-tls", any(target_os = "macos", target_os = "windows", target_os = "ios")), - all(feature = "client-openssl", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))) -))] +#[cfg(feature = "client-tls")] impl Client< DropContextService< hyper_util::service::TowerToHyperService< @@ -291,10 +283,25 @@ impl Client< > where C: Clone + Send + Sync + 'static { - /// Create a client with a TLS connection to the server + /// Create a client with a TLS connection to the server using native-tls. /// /// # Arguments - /// * `base_path` - base path of the client API, i.e. "" + /// * `base_path` - base path of the client API, i.e. "" + #[cfg(any(target_os = "macos", target_os = "windows", target_os = "ios"))] + pub fn try_new_https(base_path: &str) -> Result + { + let https_connector = Connector::builder() + .https() + .build() + .map_err(ClientInitError::SslError)?; + Self::try_new_with_connector(base_path, Some("https"), https_connector) + } + + /// Create a client with a TLS connection to the server using OpenSSL. + /// + /// # Arguments + /// * `base_path` - base path of the client API, i.e. "" + #[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))] pub fn try_new_https(base_path: &str) -> Result { let https_connector = Connector::builder() @@ -307,9 +314,9 @@ impl Client< /// Create a client with a TLS connection to the server using a pinned certificate /// /// # Arguments - /// * `base_path` - base path of the client API, i.e. "" + /// * `base_path` - base path of the client API, i.e. "" /// * `ca_certificate` - Path to CA certificate used to authenticate the server - #[cfg(all(feature = "client-openssl", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))))] + #[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))] pub fn try_new_https_pinned( base_path: &str, ca_certificate: CA, @@ -328,11 +335,11 @@ impl Client< /// Create a client with a mutually authenticated TLS connection to the server. /// /// # Arguments - /// * `base_path` - base path of the client API, i.e. "" + /// * `base_path` - base path of the client API, i.e. "" /// * `ca_certificate` - Path to CA certificate used to authenticate the server /// * `client_key` - Path to the client private key /// * `client_certificate` - Path to the client's public certificate associated with the private key - #[cfg(all(feature = "client-openssl", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))))] + #[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))] pub fn try_new_https_mutual( base_path: &str, ca_certificate: CA, @@ -398,7 +405,7 @@ pub enum ClientInitError { SslError(native_tls::Error), /// SSL Connection Error - #[cfg(all(feature = "client-openssl", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))))] + #[cfg(all(feature = "client-tls", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))))] SslError(openssl::error::ErrorStack), } diff --git a/samples/server/petstore/rust-server/output/no-example-v3/Cargo.toml b/samples/server/petstore/rust-server/output/no-example-v3/Cargo.toml index 8612179de3d8..338e93e4fe19 100644 --- a/samples/server/petstore/rust-server/output/no-example-v3/Cargo.toml +++ b/samples/server/petstore/rust-server/output/no-example-v3/Cargo.toml @@ -8,20 +8,20 @@ license = "Unlicense" edition = "2018" [features] -default = ["client", "server", "client-tls", "client-openssl"] +default = ["client", "server"] client = [ "hyper", "percent-encoding", "hyper-util/http1", "hyper-util/http2", "url" ] -# TLS support using native-tls (macOS/Windows/iOS) or hyper-tls +# TLS support - automatically selects backend based on target OS: +# - macOS/Windows/iOS: native-tls via hyper-tls +# - Other platforms: OpenSSL via hyper-openssl +# Dependencies are in target-specific sections below client-tls = [ "client", - "hyper-tls", "native-tls", - "swagger/tls" -] -# TLS support using OpenSSL (Linux and other platforms) -client-openssl = [ - "client", - "hyper-openssl", "openssl", + "dep:native-tls", + "dep:hyper-tls", + "dep:openssl", + "dep:hyper-openssl", "swagger/tls" ] server = [ @@ -36,14 +36,6 @@ conversion = ["frunk", "frunk_derives", "frunk_core", "frunk-enum-core", "frunk- mock = ["mockall"] validate = ["regex", "serde_valid", "swagger/serdevalid"] -[target.'cfg(any(target_os = "macos", target_os = "windows", target_os = "ios"))'.dependencies] -native-tls = { version = "0.2", optional = true } -hyper-tls = { version = "0.6", optional = true } - -[target.'cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))'.dependencies] -hyper-openssl = { version = "0.10", optional = true } -openssl = { version = "0.10", optional = true } - [dependencies] # Common async-trait = "0.1.88" @@ -76,6 +68,12 @@ url = { version = "2.5", optional = true } # Client-specific tower-service = "0.3.3" +# TLS support - all listed here, actual usage determined by cfg attributes in code +native-tls = { version = "0.2", optional = true } +hyper-tls = { version = "0.6", optional = true } +openssl = { version = "0.10", optional = true } +hyper-openssl = { version = "0.10", optional = true } + # Server, and client callback-specific lazy_static = { version = "1.5", optional = true } regex = { version = "1.12", optional = true } @@ -101,15 +99,13 @@ clap = "4.5" env_logger = "0.11" tokio = { version = "1.49", features = ["full"] } native-tls = "0.2" +openssl = "0.10" +tokio-openssl = "0.6" pin-project = "1.1.10" # Bearer authentication, used in examples jsonwebtoken = {version = "10.0.0", features = ["rust_crypto"]} -[target.'cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))'.dev-dependencies] -tokio-openssl = "0.6" -openssl = "0.10" - [[example]] name = "client" required-features = ["client"] diff --git a/samples/server/petstore/rust-server/output/no-example-v3/README.md b/samples/server/petstore/rust-server/output/no-example-v3/README.md index d4fa89969ac6..e7896ae6dbcf 100644 --- a/samples/server/petstore/rust-server/output/no-example-v3/README.md +++ b/samples/server/petstore/rust-server/output/no-example-v3/README.md @@ -109,11 +109,10 @@ The generated library has a few optional features that can be activated through * This defaults to enabled and creates the basic skeleton of a client implementation based on hyper * The constructed client implements the API trait by making remote API call. * `client-tls` - * This defaults to enabled and provides HTTPS support using native-tls (macOS/Windows/iOS). - * Enable this feature for TLS support on Apple and Windows platforms. -* `client-openssl` - * This defaults to enabled and provides HTTPS support using OpenSSL (Linux and other platforms). - * Enable this feature for TLS support on Linux and other Unix-like platforms. + * Optional feature that provides HTTPS support with automatic TLS backend selection: + - macOS/Windows/iOS: native-tls + hyper-tls + - Linux/Unix/others: OpenSSL + hyper-openssl + * Not enabled by default to minimize dependencies. * `conversions` * This defaults to disabled and creates extra derives on models to allow "transmogrification" between objects of structurally similar types. * `cli` @@ -122,22 +121,27 @@ The generated library has a few optional features that can be activated through * This defaults to disabled and allows JSON Schema validation of received data using `MakeService::set_validation` or `Service::set_validation`. * Note, enabling validation will have a performance penalty, especially if the API heavily uses regex based checks. -### Minimal dependencies (no TLS) +### Enabling HTTPS/TLS Support -If you only need HTTP support and want to minimize dependencies (e.g., to avoid OpenSSL build requirements), you can disable the default TLS features: +By default, only HTTP support is included. To enable HTTPS, add the `client-tls` feature: ```toml [dependencies] -no-example-v3 = { version = "0.0.1", default-features = false, features = ["server"] } +no-example-v3 = { version = "0.0.1", features = ["client-tls"] } ``` -Or for client-only without TLS: - +**For server with callbacks that need HTTPS:** ```toml [dependencies] -no-example-v3 = { version = "0.0.1", default-features = false, features = ["client"] } +no-example-v3 = { version = "0.0.1", features = ["server", "client-tls"] } ``` +The TLS backend is automatically selected based on your target platform: +- **macOS, Windows, iOS**: Uses `native-tls` (system TLS libraries) +- **Linux, Unix, other platforms**: Uses `openssl` + +This ensures the best compatibility and native integration on each platform. + See https://doc.rust-lang.org/cargo/reference/manifest.html#the-features-section for how to use features in your `Cargo.toml`. ## Documentation for API Endpoints diff --git a/samples/server/petstore/rust-server/output/no-example-v3/bin/cli.rs b/samples/server/petstore/rust-server/output/no-example-v3/bin/cli.rs index cb16d5acbd2d..df0d77c500e3 100644 --- a/samples/server/petstore/rust-server/output/no-example-v3/bin/cli.rs +++ b/samples/server/petstore/rust-server/output/no-example-v3/bin/cli.rs @@ -33,17 +33,17 @@ struct Cli { server_address: String, /// Path to the client private key if using client-side TLS authentication - #[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))] + #[cfg(all(feature = "client-tls", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))))] #[clap(long, requires_all(&["client_certificate", "server_certificate"]))] client_key: Option, /// Path to the client's public certificate associated with the private key - #[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))] + #[cfg(all(feature = "client-tls", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))))] #[clap(long, requires_all(&["client_key", "server_certificate"]))] client_certificate: Option, /// Path to CA certificate used to authenticate the server - #[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))] + #[cfg(all(feature = "client-tls", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))))] #[clap(long)] server_certificate: Option, @@ -63,7 +63,8 @@ enum Operation { }, } -#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))] +// On Linux/Unix with OpenSSL (client-tls feature), support certificate pinning and mutual TLS +#[cfg(all(feature = "client-tls", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))))] fn create_client(args: &Cli, context: ClientContext) -> Result>> { if args.client_certificate.is_some() { debug!("Using mutual TLS"); @@ -89,8 +90,15 @@ fn create_client(args: &Cli, context: ClientContext) -> Result Result>> { + // Client::try_new() automatically detects the URL scheme (http:// or https://) + // and creates the appropriate client. + // Note: Certificate pinning and mutual TLS are only available on Linux/Unix with OpenSSL let client = Client::try_new(&args.server_address).context("Failed to create HTTP(S) client")?; Ok(Box::new(client.with_context(context))) diff --git a/samples/server/petstore/rust-server/output/no-example-v3/examples/client/main.rs b/samples/server/petstore/rust-server/output/no-example-v3/examples/client/main.rs index 5d41ca4cb1f5..dcb6aa15a9ca 100644 --- a/samples/server/petstore/rust-server/output/no-example-v3/examples/client/main.rs +++ b/samples/server/petstore/rust-server/output/no-example-v3/examples/client/main.rs @@ -85,17 +85,33 @@ fn main() { let context: ClientContext = swagger::make_context!(ContextBuilder, EmptyContext, auth_data, XSpanIdString::default()); - let mut client : Box> = if is_https { - // Using Simple HTTPS - let client = Box::new(Client::try_new_https(&base_url) - .expect("Failed to create HTTPS client")); - Box::new(client.with_context(context)) - } else { - // Using HTTP - let client = Box::new(Client::try_new_http( - &base_url) - .expect("Failed to create HTTP client")); - Box::new(client.with_context(context)) + let mut client : Box> = { + #[cfg(feature = "client-tls")] + { + if is_https { + // Using HTTPS with native-tls + let client = Box::new(Client::try_new_https(&base_url) + .expect("Failed to create HTTPS client")); + Box::new(client.with_context(context)) + } else { + // Using HTTP + let client = Box::new(Client::try_new_http(&base_url) + .expect("Failed to create HTTP client")); + Box::new(client.with_context(context)) + } + } + + #[cfg(not(feature = "client-tls"))] + { + if is_https { + panic!("HTTPS requested but TLS support not enabled. \ + Enable the 'client-tls' feature to use HTTPS."); + } + // Using HTTP only + let client = Box::new(Client::try_new_http(&base_url) + .expect("Failed to create HTTP client")); + Box::new(client.with_context(context)) + } }; let mut rt = tokio::runtime::Runtime::new().unwrap(); diff --git a/samples/server/petstore/rust-server/output/no-example-v3/src/client/mod.rs b/samples/server/petstore/rust-server/output/no-example-v3/src/client/mod.rs index 55499e91acff..6af5a49082e8 100644 --- a/samples/server/petstore/rust-server/output/no-example-v3/src/client/mod.rs +++ b/samples/server/petstore/rust-server/output/no-example-v3/src/client/mod.rs @@ -153,14 +153,17 @@ impl Client< } } +#[cfg(all(feature = "client-tls", any(target_os = "macos", target_os = "windows", target_os = "ios")))] +type HyperHttpsConnector = hyper_tls::HttpsConnector; + +#[cfg(all(feature = "client-tls", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))))] +type HyperHttpsConnector = hyper_openssl::client::legacy::HttpsConnector; + #[derive(Debug, Clone)] pub enum HyperClient { Http(hyper_util::client::legacy::Client>), - #[cfg(any( - all(feature = "client-tls", any(target_os = "macos", target_os = "windows", target_os = "ios")), - all(feature = "client-openssl", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))) - ))] - Https(hyper_util::client::legacy::Client>), + #[cfg(feature = "client-tls")] + Https(hyper_util::client::legacy::Client>), } impl Service>> for HyperClient { @@ -171,10 +174,7 @@ impl Service>> for HyperClient { fn call(&self, req: Request>) -> Self::Future { match self { HyperClient::Http(client) => client.request(req), - #[cfg(any( - all(feature = "client-tls", any(target_os = "macos", target_os = "windows", target_os = "ios")), - all(feature = "client-openssl", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))) - ))] + #[cfg(feature = "client-tls")] HyperClient::Https(client) => client.request(req), } } @@ -201,20 +201,15 @@ impl Client, C> where "http" => { HyperClient::Http(hyper_util::client::legacy::Client::builder(hyper_util::rt::TokioExecutor::new()).build(connector.build())) }, - #[cfg(any( - all(feature = "client-tls", any(target_os = "macos", target_os = "windows", target_os = "ios")), - all(feature = "client-openssl", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))) - ))] + #[cfg(feature = "client-tls")] "https" => { - let connector = connector.https() - .build() - .map_err(ClientInitError::SslError)?; - HyperClient::Https(hyper_util::client::legacy::Client::builder(hyper_util::rt::TokioExecutor::new()).build(connector)) + let https_connector = connector + .https() + .build() + .map_err(ClientInitError::SslError)?; + HyperClient::Https(hyper_util::client::legacy::Client::builder(hyper_util::rt::TokioExecutor::new()).build(https_connector)) }, - #[cfg(not(any( - all(feature = "client-tls", any(target_os = "macos", target_os = "windows", target_os = "ios")), - all(feature = "client-openssl", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))) - )))] + #[cfg(not(feature = "client-tls"))] "https" => { return Err(ClientInitError::TlsNotEnabled); }, @@ -263,13 +258,10 @@ impl Client< #[cfg(all(feature = "client-tls", any(target_os = "macos", target_os = "windows", target_os = "ios")))] type HttpsConnector = hyper_tls::HttpsConnector; -#[cfg(all(feature = "client-openssl", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))))] +#[cfg(all(feature = "client-tls", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))))] type HttpsConnector = hyper_openssl::client::legacy::HttpsConnector; -#[cfg(any( - all(feature = "client-tls", any(target_os = "macos", target_os = "windows", target_os = "ios")), - all(feature = "client-openssl", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))) -))] +#[cfg(feature = "client-tls")] impl Client< DropContextService< hyper_util::service::TowerToHyperService< @@ -284,10 +276,25 @@ impl Client< > where C: Clone + Send + Sync + 'static { - /// Create a client with a TLS connection to the server + /// Create a client with a TLS connection to the server using native-tls. /// /// # Arguments - /// * `base_path` - base path of the client API, i.e. "" + /// * `base_path` - base path of the client API, i.e. "" + #[cfg(any(target_os = "macos", target_os = "windows", target_os = "ios"))] + pub fn try_new_https(base_path: &str) -> Result + { + let https_connector = Connector::builder() + .https() + .build() + .map_err(ClientInitError::SslError)?; + Self::try_new_with_connector(base_path, Some("https"), https_connector) + } + + /// Create a client with a TLS connection to the server using OpenSSL. + /// + /// # Arguments + /// * `base_path` - base path of the client API, i.e. "" + #[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))] pub fn try_new_https(base_path: &str) -> Result { let https_connector = Connector::builder() @@ -300,9 +307,9 @@ impl Client< /// Create a client with a TLS connection to the server using a pinned certificate /// /// # Arguments - /// * `base_path` - base path of the client API, i.e. "" + /// * `base_path` - base path of the client API, i.e. "" /// * `ca_certificate` - Path to CA certificate used to authenticate the server - #[cfg(all(feature = "client-openssl", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))))] + #[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))] pub fn try_new_https_pinned( base_path: &str, ca_certificate: CA, @@ -321,11 +328,11 @@ impl Client< /// Create a client with a mutually authenticated TLS connection to the server. /// /// # Arguments - /// * `base_path` - base path of the client API, i.e. "" + /// * `base_path` - base path of the client API, i.e. "" /// * `ca_certificate` - Path to CA certificate used to authenticate the server /// * `client_key` - Path to the client private key /// * `client_certificate` - Path to the client's public certificate associated with the private key - #[cfg(all(feature = "client-openssl", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))))] + #[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))] pub fn try_new_https_mutual( base_path: &str, ca_certificate: CA, @@ -391,7 +398,7 @@ pub enum ClientInitError { SslError(native_tls::Error), /// SSL Connection Error - #[cfg(all(feature = "client-openssl", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))))] + #[cfg(all(feature = "client-tls", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))))] SslError(openssl::error::ErrorStack), } diff --git a/samples/server/petstore/rust-server/output/openapi-v3/Cargo.toml b/samples/server/petstore/rust-server/output/openapi-v3/Cargo.toml index ad01f1106fa3..167e9c7caef3 100644 --- a/samples/server/petstore/rust-server/output/openapi-v3/Cargo.toml +++ b/samples/server/petstore/rust-server/output/openapi-v3/Cargo.toml @@ -8,26 +8,25 @@ license = "Unlicense" edition = "2018" [features] -default = ["client", "server", "client-tls", "client-openssl"] +default = ["client", "server"] client = [ "serde_urlencoded", "serde_ignored", "percent-encoding", "hyper", "percent-encoding", "hyper-util/http1", "hyper-util/http2", "url" ] -# TLS support using native-tls (macOS/Windows/iOS) or hyper-tls +# TLS support - automatically selects backend based on target OS: +# - macOS/Windows/iOS: native-tls via hyper-tls +# - Other platforms: OpenSSL via hyper-openssl +# Dependencies are in target-specific sections below client-tls = [ "client", - "hyper-tls", "native-tls", - "swagger/tls" -] -# TLS support using OpenSSL (Linux and other platforms) -client-openssl = [ - "client", - "hyper-openssl", "openssl", + "dep:native-tls", + "dep:hyper-tls", + "dep:openssl", + "dep:hyper-openssl", "swagger/tls" ] server = [ - "client-tls", "serde_ignored", "hyper", "percent-encoding", "url", ] @@ -39,14 +38,6 @@ conversion = ["frunk", "frunk_derives", "frunk_core", "frunk-enum-core", "frunk- mock = ["mockall"] validate = [ "serde_valid", "swagger/serdevalid"] -[target.'cfg(any(target_os = "macos", target_os = "windows", target_os = "ios"))'.dependencies] -native-tls = { version = "0.2", optional = true } -hyper-tls = { version = "0.6", optional = true } - -[target.'cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))'.dependencies] -hyper-openssl = { version = "0.10", optional = true } -openssl = { version = "0.10", optional = true } - [dependencies] # Common async-trait = "0.1.88" @@ -84,6 +75,12 @@ url = { version = "2.5", optional = true } serde_urlencoded = { version = "0.7.1", optional = true } tower-service = "0.3.3" +# TLS support - all listed here, actual usage determined by cfg attributes in code +native-tls = { version = "0.2", optional = true } +hyper-tls = { version = "0.6", optional = true } +openssl = { version = "0.10", optional = true } +hyper-openssl = { version = "0.10", optional = true } + # Server, and client callback-specific percent-encoding = { version = "2.3.2", optional = true } @@ -107,15 +104,13 @@ clap = "4.5" env_logger = "0.11" tokio = { version = "1.49", features = ["full"] } native-tls = "0.2" +openssl = "0.10" +tokio-openssl = "0.6" pin-project = "1.1.10" # Bearer authentication, used in examples jsonwebtoken = {version = "10.0.0", features = ["rust_crypto"]} -[target.'cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))'.dev-dependencies] -tokio-openssl = "0.6" -openssl = "0.10" - [[example]] name = "client" required-features = ["client"] diff --git a/samples/server/petstore/rust-server/output/openapi-v3/README.md b/samples/server/petstore/rust-server/output/openapi-v3/README.md index 0bf349381e3c..2728f483e178 100644 --- a/samples/server/petstore/rust-server/output/openapi-v3/README.md +++ b/samples/server/petstore/rust-server/output/openapi-v3/README.md @@ -140,11 +140,10 @@ The generated library has a few optional features that can be activated through * This defaults to enabled and creates the basic skeleton of a client implementation based on hyper * The constructed client implements the API trait by making remote API call. * `client-tls` - * This defaults to enabled and provides HTTPS support using native-tls (macOS/Windows/iOS). - * Enable this feature for TLS support on Apple and Windows platforms. -* `client-openssl` - * This defaults to enabled and provides HTTPS support using OpenSSL (Linux and other platforms). - * Enable this feature for TLS support on Linux and other Unix-like platforms. + * Optional feature that provides HTTPS support with automatic TLS backend selection: + - macOS/Windows/iOS: native-tls + hyper-tls + - Linux/Unix/others: OpenSSL + hyper-openssl + * Not enabled by default to minimize dependencies. * `conversions` * This defaults to disabled and creates extra derives on models to allow "transmogrification" between objects of structurally similar types. * `cli` @@ -153,22 +152,27 @@ The generated library has a few optional features that can be activated through * This defaults to disabled and allows JSON Schema validation of received data using `MakeService::set_validation` or `Service::set_validation`. * Note, enabling validation will have a performance penalty, especially if the API heavily uses regex based checks. -### Minimal dependencies (no TLS) +### Enabling HTTPS/TLS Support -If you only need HTTP support and want to minimize dependencies (e.g., to avoid OpenSSL build requirements), you can disable the default TLS features: +By default, only HTTP support is included. To enable HTTPS, add the `client-tls` feature: ```toml [dependencies] -openapi-v3 = { version = "1.0.7", default-features = false, features = ["server"] } +openapi-v3 = { version = "1.0.7", features = ["client-tls"] } ``` -Or for client-only without TLS: - +**For server with callbacks that need HTTPS:** ```toml [dependencies] -openapi-v3 = { version = "1.0.7", default-features = false, features = ["client"] } +openapi-v3 = { version = "1.0.7", features = ["server", "client-tls"] } ``` +The TLS backend is automatically selected based on your target platform: +- **macOS, Windows, iOS**: Uses `native-tls` (system TLS libraries) +- **Linux, Unix, other platforms**: Uses `openssl` + +This ensures the best compatibility and native integration on each platform. + See https://doc.rust-lang.org/cargo/reference/manifest.html#the-features-section for how to use features in your `Cargo.toml`. ## Documentation for API Endpoints diff --git a/samples/server/petstore/rust-server/output/openapi-v3/bin/cli.rs b/samples/server/petstore/rust-server/output/openapi-v3/bin/cli.rs index 30e4193c0756..271d62349e7f 100644 --- a/samples/server/petstore/rust-server/output/openapi-v3/bin/cli.rs +++ b/samples/server/petstore/rust-server/output/openapi-v3/bin/cli.rs @@ -63,17 +63,17 @@ struct Cli { server_address: String, /// Path to the client private key if using client-side TLS authentication - #[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))] + #[cfg(all(feature = "client-tls", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))))] #[clap(long, requires_all(&["client_certificate", "server_certificate"]))] client_key: Option, /// Path to the client's public certificate associated with the private key - #[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))] + #[cfg(all(feature = "client-tls", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))))] #[clap(long, requires_all(&["client_key", "server_certificate"]))] client_certificate: Option, /// Path to CA certificate used to authenticate the server - #[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))] + #[cfg(all(feature = "client-tls", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))))] #[clap(long)] server_certificate: Option, @@ -214,7 +214,8 @@ enum Operation { }, } -#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))] +// On Linux/Unix with OpenSSL (client-tls feature), support certificate pinning and mutual TLS +#[cfg(all(feature = "client-tls", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))))] fn create_client(args: &Cli, context: ClientContext) -> Result>> { if args.client_certificate.is_some() { debug!("Using mutual TLS"); @@ -240,8 +241,15 @@ fn create_client(args: &Cli, context: ClientContext) -> Result Result>> { + // Client::try_new() automatically detects the URL scheme (http:// or https://) + // and creates the appropriate client. + // Note: Certificate pinning and mutual TLS are only available on Linux/Unix with OpenSSL let client = Client::try_new(&args.server_address).context("Failed to create HTTP(S) client")?; Ok(Box::new(client.with_context(context))) diff --git a/samples/server/petstore/rust-server/output/openapi-v3/examples/client/main.rs b/samples/server/petstore/rust-server/output/openapi-v3/examples/client/main.rs index 1a59fa58bf15..1af6586cb53f 100644 --- a/samples/server/petstore/rust-server/output/openapi-v3/examples/client/main.rs +++ b/samples/server/petstore/rust-server/output/openapi-v3/examples/client/main.rs @@ -151,17 +151,33 @@ fn main() { let context: ClientContext = swagger::make_context!(ContextBuilder, EmptyContext, auth_data, XSpanIdString::default()); - let mut client : Box> = if is_https { - // Using Simple HTTPS - let client = Box::new(Client::try_new_https(&base_url) - .expect("Failed to create HTTPS client")); - Box::new(client.with_context(context)) - } else { - // Using HTTP - let client = Box::new(Client::try_new_http( - &base_url) - .expect("Failed to create HTTP client")); - Box::new(client.with_context(context)) + let mut client : Box> = { + #[cfg(feature = "client-tls")] + { + if is_https { + // Using HTTPS with native-tls + let client = Box::new(Client::try_new_https(&base_url) + .expect("Failed to create HTTPS client")); + Box::new(client.with_context(context)) + } else { + // Using HTTP + let client = Box::new(Client::try_new_http(&base_url) + .expect("Failed to create HTTP client")); + Box::new(client.with_context(context)) + } + } + + #[cfg(not(feature = "client-tls"))] + { + if is_https { + panic!("HTTPS requested but TLS support not enabled. \ + Enable the 'client-tls' feature to use HTTPS."); + } + // Using HTTP only + let client = Box::new(Client::try_new_http(&base_url) + .expect("Failed to create HTTP client")); + Box::new(client.with_context(context)) + } }; let mut rt = tokio::runtime::Runtime::new().unwrap(); diff --git a/samples/server/petstore/rust-server/output/openapi-v3/src/client/mod.rs b/samples/server/petstore/rust-server/output/openapi-v3/src/client/mod.rs index b34d569df2e1..c52cdaf4a90b 100644 --- a/samples/server/petstore/rust-server/output/openapi-v3/src/client/mod.rs +++ b/samples/server/petstore/rust-server/output/openapi-v3/src/client/mod.rs @@ -185,14 +185,17 @@ impl Client< } } +#[cfg(all(feature = "client-tls", any(target_os = "macos", target_os = "windows", target_os = "ios")))] +type HyperHttpsConnector = hyper_tls::HttpsConnector; + +#[cfg(all(feature = "client-tls", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))))] +type HyperHttpsConnector = hyper_openssl::client::legacy::HttpsConnector; + #[derive(Debug, Clone)] pub enum HyperClient { Http(hyper_util::client::legacy::Client>), - #[cfg(any( - all(feature = "client-tls", any(target_os = "macos", target_os = "windows", target_os = "ios")), - all(feature = "client-openssl", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))) - ))] - Https(hyper_util::client::legacy::Client>), + #[cfg(feature = "client-tls")] + Https(hyper_util::client::legacy::Client>), } impl Service>> for HyperClient { @@ -203,10 +206,7 @@ impl Service>> for HyperClient { fn call(&self, req: Request>) -> Self::Future { match self { HyperClient::Http(client) => client.request(req), - #[cfg(any( - all(feature = "client-tls", any(target_os = "macos", target_os = "windows", target_os = "ios")), - all(feature = "client-openssl", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))) - ))] + #[cfg(feature = "client-tls")] HyperClient::Https(client) => client.request(req), } } @@ -233,20 +233,15 @@ impl Client, C> where "http" => { HyperClient::Http(hyper_util::client::legacy::Client::builder(hyper_util::rt::TokioExecutor::new()).build(connector.build())) }, - #[cfg(any( - all(feature = "client-tls", any(target_os = "macos", target_os = "windows", target_os = "ios")), - all(feature = "client-openssl", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))) - ))] + #[cfg(feature = "client-tls")] "https" => { - let connector = connector.https() - .build() - .map_err(ClientInitError::SslError)?; - HyperClient::Https(hyper_util::client::legacy::Client::builder(hyper_util::rt::TokioExecutor::new()).build(connector)) + let https_connector = connector + .https() + .build() + .map_err(ClientInitError::SslError)?; + HyperClient::Https(hyper_util::client::legacy::Client::builder(hyper_util::rt::TokioExecutor::new()).build(https_connector)) }, - #[cfg(not(any( - all(feature = "client-tls", any(target_os = "macos", target_os = "windows", target_os = "ios")), - all(feature = "client-openssl", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))) - )))] + #[cfg(not(feature = "client-tls"))] "https" => { return Err(ClientInitError::TlsNotEnabled); }, @@ -295,13 +290,10 @@ impl Client< #[cfg(all(feature = "client-tls", any(target_os = "macos", target_os = "windows", target_os = "ios")))] type HttpsConnector = hyper_tls::HttpsConnector; -#[cfg(all(feature = "client-openssl", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))))] +#[cfg(all(feature = "client-tls", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))))] type HttpsConnector = hyper_openssl::client::legacy::HttpsConnector; -#[cfg(any( - all(feature = "client-tls", any(target_os = "macos", target_os = "windows", target_os = "ios")), - all(feature = "client-openssl", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))) -))] +#[cfg(feature = "client-tls")] impl Client< DropContextService< hyper_util::service::TowerToHyperService< @@ -316,10 +308,25 @@ impl Client< > where C: Clone + Send + Sync + 'static { - /// Create a client with a TLS connection to the server + /// Create a client with a TLS connection to the server using native-tls. /// /// # Arguments - /// * `base_path` - base path of the client API, i.e. "" + /// * `base_path` - base path of the client API, i.e. "" + #[cfg(any(target_os = "macos", target_os = "windows", target_os = "ios"))] + pub fn try_new_https(base_path: &str) -> Result + { + let https_connector = Connector::builder() + .https() + .build() + .map_err(ClientInitError::SslError)?; + Self::try_new_with_connector(base_path, Some("https"), https_connector) + } + + /// Create a client with a TLS connection to the server using OpenSSL. + /// + /// # Arguments + /// * `base_path` - base path of the client API, i.e. "" + #[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))] pub fn try_new_https(base_path: &str) -> Result { let https_connector = Connector::builder() @@ -332,9 +339,9 @@ impl Client< /// Create a client with a TLS connection to the server using a pinned certificate /// /// # Arguments - /// * `base_path` - base path of the client API, i.e. "" + /// * `base_path` - base path of the client API, i.e. "" /// * `ca_certificate` - Path to CA certificate used to authenticate the server - #[cfg(all(feature = "client-openssl", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))))] + #[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))] pub fn try_new_https_pinned( base_path: &str, ca_certificate: CA, @@ -353,11 +360,11 @@ impl Client< /// Create a client with a mutually authenticated TLS connection to the server. /// /// # Arguments - /// * `base_path` - base path of the client API, i.e. "" + /// * `base_path` - base path of the client API, i.e. "" /// * `ca_certificate` - Path to CA certificate used to authenticate the server /// * `client_key` - Path to the client private key /// * `client_certificate` - Path to the client's public certificate associated with the private key - #[cfg(all(feature = "client-openssl", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))))] + #[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))] pub fn try_new_https_mutual( base_path: &str, ca_certificate: CA, @@ -423,7 +430,7 @@ pub enum ClientInitError { SslError(native_tls::Error), /// SSL Connection Error - #[cfg(all(feature = "client-openssl", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))))] + #[cfg(all(feature = "client-tls", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))))] SslError(openssl::error::ErrorStack), } diff --git a/samples/server/petstore/rust-server/output/openapi-v3/src/server/callbacks.rs b/samples/server/petstore/rust-server/output/openapi-v3/src/server/callbacks.rs index 7146cc3f2529..9a0c9e74650a 100644 --- a/samples/server/petstore/rust-server/output/openapi-v3/src/server/callbacks.rs +++ b/samples/server/petstore/rust-server/output/openapi-v3/src/server/callbacks.rs @@ -126,26 +126,23 @@ impl Client; -#[cfg(all(feature = "client-openssl", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))))] +#[cfg(all(feature = "client-tls", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))))] type HttpsConnector = hyper_openssl::client::legacy::HttpsConnector; -#[cfg(any( - all(feature = "client-tls", any(target_os = "macos", target_os = "windows", target_os = "ios")), - all(feature = "client-openssl", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))) -))] +#[cfg(feature = "client-tls")] impl Client>>, C>, C> where C: Clone + Send + Sync + 'static { - /// Create a client with a TLS connection to the server. - #[cfg(all(feature = "client-tls", any(target_os = "macos", target_os = "windows", target_os = "ios")))] + /// Create a client with a TLS connection to the server using native-tls. + #[cfg(any(target_os = "macos", target_os = "windows", target_os = "ios"))] pub fn new_https() -> Result { let https_connector = Connector::builder().https().build()?; Ok(Self::new_with_connector(https_connector)) } - /// Create a client with a TLS connection to the server. - #[cfg(all(feature = "client-openssl", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))))] + /// Create a client with a TLS connection to the server using OpenSSL. + #[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))] pub fn new_https() -> Result { let https_connector = Connector::builder().https().build()?; @@ -156,7 +153,7 @@ impl Client( ca_certificate: CA, ) -> Result where @@ -175,7 +172,7 @@ impl Client( ca_certificate: CA, client_key: K, diff --git a/samples/server/petstore/rust-server/output/ops-v3/Cargo.toml b/samples/server/petstore/rust-server/output/ops-v3/Cargo.toml index d4d17bcca5a8..0f2226a37483 100644 --- a/samples/server/petstore/rust-server/output/ops-v3/Cargo.toml +++ b/samples/server/petstore/rust-server/output/ops-v3/Cargo.toml @@ -8,20 +8,20 @@ license = "Unlicense" edition = "2018" [features] -default = ["client", "server", "client-tls", "client-openssl"] +default = ["client", "server"] client = [ "hyper", "percent-encoding", "hyper-util/http1", "hyper-util/http2", "url" ] -# TLS support using native-tls (macOS/Windows/iOS) or hyper-tls +# TLS support - automatically selects backend based on target OS: +# - macOS/Windows/iOS: native-tls via hyper-tls +# - Other platforms: OpenSSL via hyper-openssl +# Dependencies are in target-specific sections below client-tls = [ "client", - "hyper-tls", "native-tls", - "swagger/tls" -] -# TLS support using OpenSSL (Linux and other platforms) -client-openssl = [ - "client", - "hyper-openssl", "openssl", + "dep:native-tls", + "dep:hyper-tls", + "dep:openssl", + "dep:hyper-openssl", "swagger/tls" ] server = [ @@ -36,14 +36,6 @@ conversion = ["frunk", "frunk_derives", "frunk_core", "frunk-enum-core", "frunk- mock = ["mockall"] validate = ["regex", "serde_valid", "swagger/serdevalid"] -[target.'cfg(any(target_os = "macos", target_os = "windows", target_os = "ios"))'.dependencies] -native-tls = { version = "0.2", optional = true } -hyper-tls = { version = "0.6", optional = true } - -[target.'cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))'.dependencies] -hyper-openssl = { version = "0.10", optional = true } -openssl = { version = "0.10", optional = true } - [dependencies] # Common async-trait = "0.1.88" @@ -76,6 +68,12 @@ url = { version = "2.5", optional = true } # Client-specific tower-service = "0.3.3" +# TLS support - all listed here, actual usage determined by cfg attributes in code +native-tls = { version = "0.2", optional = true } +hyper-tls = { version = "0.6", optional = true } +openssl = { version = "0.10", optional = true } +hyper-openssl = { version = "0.10", optional = true } + # Server, and client callback-specific lazy_static = { version = "1.5", optional = true } regex = { version = "1.12", optional = true } @@ -101,15 +99,13 @@ clap = "4.5" env_logger = "0.11" tokio = { version = "1.49", features = ["full"] } native-tls = "0.2" +openssl = "0.10" +tokio-openssl = "0.6" pin-project = "1.1.10" # Bearer authentication, used in examples jsonwebtoken = {version = "10.0.0", features = ["rust_crypto"]} -[target.'cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))'.dev-dependencies] -tokio-openssl = "0.6" -openssl = "0.10" - [[example]] name = "client" required-features = ["client"] diff --git a/samples/server/petstore/rust-server/output/ops-v3/README.md b/samples/server/petstore/rust-server/output/ops-v3/README.md index 772bc20930ee..247c2a315180 100644 --- a/samples/server/petstore/rust-server/output/ops-v3/README.md +++ b/samples/server/petstore/rust-server/output/ops-v3/README.md @@ -146,11 +146,10 @@ The generated library has a few optional features that can be activated through * This defaults to enabled and creates the basic skeleton of a client implementation based on hyper * The constructed client implements the API trait by making remote API call. * `client-tls` - * This defaults to enabled and provides HTTPS support using native-tls (macOS/Windows/iOS). - * Enable this feature for TLS support on Apple and Windows platforms. -* `client-openssl` - * This defaults to enabled and provides HTTPS support using OpenSSL (Linux and other platforms). - * Enable this feature for TLS support on Linux and other Unix-like platforms. + * Optional feature that provides HTTPS support with automatic TLS backend selection: + - macOS/Windows/iOS: native-tls + hyper-tls + - Linux/Unix/others: OpenSSL + hyper-openssl + * Not enabled by default to minimize dependencies. * `conversions` * This defaults to disabled and creates extra derives on models to allow "transmogrification" between objects of structurally similar types. * `cli` @@ -159,22 +158,27 @@ The generated library has a few optional features that can be activated through * This defaults to disabled and allows JSON Schema validation of received data using `MakeService::set_validation` or `Service::set_validation`. * Note, enabling validation will have a performance penalty, especially if the API heavily uses regex based checks. -### Minimal dependencies (no TLS) +### Enabling HTTPS/TLS Support -If you only need HTTP support and want to minimize dependencies (e.g., to avoid OpenSSL build requirements), you can disable the default TLS features: +By default, only HTTP support is included. To enable HTTPS, add the `client-tls` feature: ```toml [dependencies] -ops-v3 = { version = "0.0.1", default-features = false, features = ["server"] } +ops-v3 = { version = "0.0.1", features = ["client-tls"] } ``` -Or for client-only without TLS: - +**For server with callbacks that need HTTPS:** ```toml [dependencies] -ops-v3 = { version = "0.0.1", default-features = false, features = ["client"] } +ops-v3 = { version = "0.0.1", features = ["server", "client-tls"] } ``` +The TLS backend is automatically selected based on your target platform: +- **macOS, Windows, iOS**: Uses `native-tls` (system TLS libraries) +- **Linux, Unix, other platforms**: Uses `openssl` + +This ensures the best compatibility and native integration on each platform. + See https://doc.rust-lang.org/cargo/reference/manifest.html#the-features-section for how to use features in your `Cargo.toml`. ## Documentation for API Endpoints diff --git a/samples/server/petstore/rust-server/output/ops-v3/bin/cli.rs b/samples/server/petstore/rust-server/output/ops-v3/bin/cli.rs index 31a94281e196..372f0362e2bf 100644 --- a/samples/server/petstore/rust-server/output/ops-v3/bin/cli.rs +++ b/samples/server/petstore/rust-server/output/ops-v3/bin/cli.rs @@ -69,17 +69,17 @@ struct Cli { server_address: String, /// Path to the client private key if using client-side TLS authentication - #[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))] + #[cfg(all(feature = "client-tls", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))))] #[clap(long, requires_all(&["client_certificate", "server_certificate"]))] client_key: Option, /// Path to the client's public certificate associated with the private key - #[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))] + #[cfg(all(feature = "client-tls", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))))] #[clap(long, requires_all(&["client_key", "server_certificate"]))] client_certificate: Option, /// Path to CA certificate used to authenticate the server - #[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))] + #[cfg(all(feature = "client-tls", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))))] #[clap(long)] server_certificate: Option, @@ -169,7 +169,8 @@ enum Operation { }, } -#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))] +// On Linux/Unix with OpenSSL (client-tls feature), support certificate pinning and mutual TLS +#[cfg(all(feature = "client-tls", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))))] fn create_client(args: &Cli, context: ClientContext) -> Result>> { if args.client_certificate.is_some() { debug!("Using mutual TLS"); @@ -195,8 +196,15 @@ fn create_client(args: &Cli, context: ClientContext) -> Result Result>> { + // Client::try_new() automatically detects the URL scheme (http:// or https://) + // and creates the appropriate client. + // Note: Certificate pinning and mutual TLS are only available on Linux/Unix with OpenSSL let client = Client::try_new(&args.server_address).context("Failed to create HTTP(S) client")?; Ok(Box::new(client.with_context(context))) diff --git a/samples/server/petstore/rust-server/output/ops-v3/examples/client/main.rs b/samples/server/petstore/rust-server/output/ops-v3/examples/client/main.rs index b829ce8b9a4d..e9c51dfbcad6 100644 --- a/samples/server/petstore/rust-server/output/ops-v3/examples/client/main.rs +++ b/samples/server/petstore/rust-server/output/ops-v3/examples/client/main.rs @@ -157,17 +157,33 @@ fn main() { let context: ClientContext = swagger::make_context!(ContextBuilder, EmptyContext, auth_data, XSpanIdString::default()); - let mut client : Box> = if is_https { - // Using Simple HTTPS - let client = Box::new(Client::try_new_https(&base_url) - .expect("Failed to create HTTPS client")); - Box::new(client.with_context(context)) - } else { - // Using HTTP - let client = Box::new(Client::try_new_http( - &base_url) - .expect("Failed to create HTTP client")); - Box::new(client.with_context(context)) + let mut client : Box> = { + #[cfg(feature = "client-tls")] + { + if is_https { + // Using HTTPS with native-tls + let client = Box::new(Client::try_new_https(&base_url) + .expect("Failed to create HTTPS client")); + Box::new(client.with_context(context)) + } else { + // Using HTTP + let client = Box::new(Client::try_new_http(&base_url) + .expect("Failed to create HTTP client")); + Box::new(client.with_context(context)) + } + } + + #[cfg(not(feature = "client-tls"))] + { + if is_https { + panic!("HTTPS requested but TLS support not enabled. \ + Enable the 'client-tls' feature to use HTTPS."); + } + // Using HTTP only + let client = Box::new(Client::try_new_http(&base_url) + .expect("Failed to create HTTP client")); + Box::new(client.with_context(context)) + } }; let mut rt = tokio::runtime::Runtime::new().unwrap(); diff --git a/samples/server/petstore/rust-server/output/ops-v3/src/client/mod.rs b/samples/server/petstore/rust-server/output/ops-v3/src/client/mod.rs index 6a42d99ca018..17ef4ceafaec 100644 --- a/samples/server/petstore/rust-server/output/ops-v3/src/client/mod.rs +++ b/samples/server/petstore/rust-server/output/ops-v3/src/client/mod.rs @@ -189,14 +189,17 @@ impl Client< } } +#[cfg(all(feature = "client-tls", any(target_os = "macos", target_os = "windows", target_os = "ios")))] +type HyperHttpsConnector = hyper_tls::HttpsConnector; + +#[cfg(all(feature = "client-tls", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))))] +type HyperHttpsConnector = hyper_openssl::client::legacy::HttpsConnector; + #[derive(Debug, Clone)] pub enum HyperClient { Http(hyper_util::client::legacy::Client>), - #[cfg(any( - all(feature = "client-tls", any(target_os = "macos", target_os = "windows", target_os = "ios")), - all(feature = "client-openssl", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))) - ))] - Https(hyper_util::client::legacy::Client>), + #[cfg(feature = "client-tls")] + Https(hyper_util::client::legacy::Client>), } impl Service>> for HyperClient { @@ -207,10 +210,7 @@ impl Service>> for HyperClient { fn call(&self, req: Request>) -> Self::Future { match self { HyperClient::Http(client) => client.request(req), - #[cfg(any( - all(feature = "client-tls", any(target_os = "macos", target_os = "windows", target_os = "ios")), - all(feature = "client-openssl", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))) - ))] + #[cfg(feature = "client-tls")] HyperClient::Https(client) => client.request(req), } } @@ -237,20 +237,15 @@ impl Client, C> where "http" => { HyperClient::Http(hyper_util::client::legacy::Client::builder(hyper_util::rt::TokioExecutor::new()).build(connector.build())) }, - #[cfg(any( - all(feature = "client-tls", any(target_os = "macos", target_os = "windows", target_os = "ios")), - all(feature = "client-openssl", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))) - ))] + #[cfg(feature = "client-tls")] "https" => { - let connector = connector.https() - .build() - .map_err(ClientInitError::SslError)?; - HyperClient::Https(hyper_util::client::legacy::Client::builder(hyper_util::rt::TokioExecutor::new()).build(connector)) + let https_connector = connector + .https() + .build() + .map_err(ClientInitError::SslError)?; + HyperClient::Https(hyper_util::client::legacy::Client::builder(hyper_util::rt::TokioExecutor::new()).build(https_connector)) }, - #[cfg(not(any( - all(feature = "client-tls", any(target_os = "macos", target_os = "windows", target_os = "ios")), - all(feature = "client-openssl", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))) - )))] + #[cfg(not(feature = "client-tls"))] "https" => { return Err(ClientInitError::TlsNotEnabled); }, @@ -299,13 +294,10 @@ impl Client< #[cfg(all(feature = "client-tls", any(target_os = "macos", target_os = "windows", target_os = "ios")))] type HttpsConnector = hyper_tls::HttpsConnector; -#[cfg(all(feature = "client-openssl", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))))] +#[cfg(all(feature = "client-tls", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))))] type HttpsConnector = hyper_openssl::client::legacy::HttpsConnector; -#[cfg(any( - all(feature = "client-tls", any(target_os = "macos", target_os = "windows", target_os = "ios")), - all(feature = "client-openssl", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))) -))] +#[cfg(feature = "client-tls")] impl Client< DropContextService< hyper_util::service::TowerToHyperService< @@ -320,10 +312,25 @@ impl Client< > where C: Clone + Send + Sync + 'static { - /// Create a client with a TLS connection to the server + /// Create a client with a TLS connection to the server using native-tls. /// /// # Arguments - /// * `base_path` - base path of the client API, i.e. "" + /// * `base_path` - base path of the client API, i.e. "" + #[cfg(any(target_os = "macos", target_os = "windows", target_os = "ios"))] + pub fn try_new_https(base_path: &str) -> Result + { + let https_connector = Connector::builder() + .https() + .build() + .map_err(ClientInitError::SslError)?; + Self::try_new_with_connector(base_path, Some("https"), https_connector) + } + + /// Create a client with a TLS connection to the server using OpenSSL. + /// + /// # Arguments + /// * `base_path` - base path of the client API, i.e. "" + #[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))] pub fn try_new_https(base_path: &str) -> Result { let https_connector = Connector::builder() @@ -336,9 +343,9 @@ impl Client< /// Create a client with a TLS connection to the server using a pinned certificate /// /// # Arguments - /// * `base_path` - base path of the client API, i.e. "" + /// * `base_path` - base path of the client API, i.e. "" /// * `ca_certificate` - Path to CA certificate used to authenticate the server - #[cfg(all(feature = "client-openssl", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))))] + #[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))] pub fn try_new_https_pinned( base_path: &str, ca_certificate: CA, @@ -357,11 +364,11 @@ impl Client< /// Create a client with a mutually authenticated TLS connection to the server. /// /// # Arguments - /// * `base_path` - base path of the client API, i.e. "" + /// * `base_path` - base path of the client API, i.e. "" /// * `ca_certificate` - Path to CA certificate used to authenticate the server /// * `client_key` - Path to the client private key /// * `client_certificate` - Path to the client's public certificate associated with the private key - #[cfg(all(feature = "client-openssl", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))))] + #[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))] pub fn try_new_https_mutual( base_path: &str, ca_certificate: CA, @@ -427,7 +434,7 @@ pub enum ClientInitError { SslError(native_tls::Error), /// SSL Connection Error - #[cfg(all(feature = "client-openssl", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))))] + #[cfg(all(feature = "client-tls", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))))] SslError(openssl::error::ErrorStack), } diff --git a/samples/server/petstore/rust-server/output/petstore-with-fake-endpoints-models-for-testing/Cargo.toml b/samples/server/petstore/rust-server/output/petstore-with-fake-endpoints-models-for-testing/Cargo.toml index 37c3337fab54..948d4cbadde4 100644 --- a/samples/server/petstore/rust-server/output/petstore-with-fake-endpoints-models-for-testing/Cargo.toml +++ b/samples/server/petstore/rust-server/output/petstore-with-fake-endpoints-models-for-testing/Cargo.toml @@ -8,22 +8,22 @@ edition = "2018" publish = ["crates-io"] [features] -default = ["client", "server", "client-tls", "client-openssl"] +default = ["client", "server"] client = [ "multipart", "multipart/client", "swagger/multipart_form", "serde_urlencoded", "hyper", "percent-encoding", "hyper-util/http1", "hyper-util/http2", "url" ] -# TLS support using native-tls (macOS/Windows/iOS) or hyper-tls +# TLS support - automatically selects backend based on target OS: +# - macOS/Windows/iOS: native-tls via hyper-tls +# - Other platforms: OpenSSL via hyper-openssl +# Dependencies are in target-specific sections below client-tls = [ "client", - "hyper-tls", "native-tls", - "swagger/tls" -] -# TLS support using OpenSSL (Linux and other platforms) -client-openssl = [ - "client", - "hyper-openssl", "openssl", + "dep:native-tls", + "dep:hyper-tls", + "dep:openssl", + "dep:hyper-openssl", "swagger/tls" ] server = [ @@ -40,14 +40,6 @@ conversion = ["frunk", "frunk_derives", "frunk_core", "frunk-enum-core", "frunk- mock = ["mockall"] validate = [ "serde_valid", "swagger/serdevalid"] -[target.'cfg(any(target_os = "macos", target_os = "windows", target_os = "ios"))'.dependencies] -native-tls = { version = "0.2", optional = true } -hyper-tls = { version = "0.6", optional = true } - -[target.'cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))'.dependencies] -hyper-openssl = { version = "0.10", optional = true } -openssl = { version = "0.10", optional = true } - [dependencies] # Common async-trait = "0.1.88" @@ -86,6 +78,12 @@ url = { version = "2.5", optional = true } serde_urlencoded = { version = "0.7.1", optional = true } tower-service = "0.3.3" +# TLS support - all listed here, actual usage determined by cfg attributes in code +native-tls = { version = "0.2", optional = true } +hyper-tls = { version = "0.6", optional = true } +openssl = { version = "0.10", optional = true } +hyper-openssl = { version = "0.10", optional = true } + # Server, and client callback-specific percent-encoding = { version = "2.3.2", optional = true } @@ -110,15 +108,13 @@ clap = "4.5" env_logger = "0.11" tokio = { version = "1.49", features = ["full"] } native-tls = "0.2" +openssl = "0.10" +tokio-openssl = "0.6" pin-project = "1.1.10" # Bearer authentication, used in examples jsonwebtoken = {version = "10.0.0", features = ["rust_crypto"]} -[target.'cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))'.dev-dependencies] -tokio-openssl = "0.6" -openssl = "0.10" - [[example]] name = "client" required-features = ["client"] diff --git a/samples/server/petstore/rust-server/output/petstore-with-fake-endpoints-models-for-testing/README.md b/samples/server/petstore/rust-server/output/petstore-with-fake-endpoints-models-for-testing/README.md index 0a6efedd1408..d8e4ef54ccb6 100644 --- a/samples/server/petstore/rust-server/output/petstore-with-fake-endpoints-models-for-testing/README.md +++ b/samples/server/petstore/rust-server/output/petstore-with-fake-endpoints-models-for-testing/README.md @@ -134,11 +134,10 @@ The generated library has a few optional features that can be activated through * This defaults to enabled and creates the basic skeleton of a client implementation based on hyper * The constructed client implements the API trait by making remote API call. * `client-tls` - * This defaults to enabled and provides HTTPS support using native-tls (macOS/Windows/iOS). - * Enable this feature for TLS support on Apple and Windows platforms. -* `client-openssl` - * This defaults to enabled and provides HTTPS support using OpenSSL (Linux and other platforms). - * Enable this feature for TLS support on Linux and other Unix-like platforms. + * Optional feature that provides HTTPS support with automatic TLS backend selection: + - macOS/Windows/iOS: native-tls + hyper-tls + - Linux/Unix/others: OpenSSL + hyper-openssl + * Not enabled by default to minimize dependencies. * `conversions` * This defaults to disabled and creates extra derives on models to allow "transmogrification" between objects of structurally similar types. * `cli` @@ -147,22 +146,27 @@ The generated library has a few optional features that can be activated through * This defaults to disabled and allows JSON Schema validation of received data using `MakeService::set_validation` or `Service::set_validation`. * Note, enabling validation will have a performance penalty, especially if the API heavily uses regex based checks. -### Minimal dependencies (no TLS) +### Enabling HTTPS/TLS Support -If you only need HTTP support and want to minimize dependencies (e.g., to avoid OpenSSL build requirements), you can disable the default TLS features: +By default, only HTTP support is included. To enable HTTPS, add the `client-tls` feature: ```toml [dependencies] -petstore-with-fake-endpoints-models-for-testing = { version = "1.0.0", default-features = false, features = ["server"] } +petstore-with-fake-endpoints-models-for-testing = { version = "1.0.0", features = ["client-tls"] } ``` -Or for client-only without TLS: - +**For server with callbacks that need HTTPS:** ```toml [dependencies] -petstore-with-fake-endpoints-models-for-testing = { version = "1.0.0", default-features = false, features = ["client"] } +petstore-with-fake-endpoints-models-for-testing = { version = "1.0.0", features = ["server", "client-tls"] } ``` +The TLS backend is automatically selected based on your target platform: +- **macOS, Windows, iOS**: Uses `native-tls` (system TLS libraries) +- **Linux, Unix, other platforms**: Uses `openssl` + +This ensures the best compatibility and native integration on each platform. + See https://doc.rust-lang.org/cargo/reference/manifest.html#the-features-section for how to use features in your `Cargo.toml`. ## Documentation for API Endpoints diff --git a/samples/server/petstore/rust-server/output/petstore-with-fake-endpoints-models-for-testing/bin/cli.rs b/samples/server/petstore/rust-server/output/petstore-with-fake-endpoints-models-for-testing/bin/cli.rs index 5cc7d996d9a2..19a4a3ded863 100644 --- a/samples/server/petstore/rust-server/output/petstore-with-fake-endpoints-models-for-testing/bin/cli.rs +++ b/samples/server/petstore/rust-server/output/petstore-with-fake-endpoints-models-for-testing/bin/cli.rs @@ -68,17 +68,17 @@ struct Cli { server_address: String, /// Path to the client private key if using client-side TLS authentication - #[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))] + #[cfg(all(feature = "client-tls", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))))] #[clap(long, requires_all(&["client_certificate", "server_certificate"]))] client_key: Option, /// Path to the client's public certificate associated with the private key - #[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))] + #[cfg(all(feature = "client-tls", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))))] #[clap(long, requires_all(&["client_key", "server_certificate"]))] client_certificate: Option, /// Path to CA certificate used to authenticate the server - #[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))] + #[cfg(all(feature = "client-tls", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))))] #[clap(long)] server_certificate: Option, @@ -342,7 +342,8 @@ enum Operation { }, } -#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))] +// On Linux/Unix with OpenSSL (client-tls feature), support certificate pinning and mutual TLS +#[cfg(all(feature = "client-tls", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))))] fn create_client(args: &Cli, context: ClientContext) -> Result>> { if args.client_certificate.is_some() { debug!("Using mutual TLS"); @@ -368,8 +369,15 @@ fn create_client(args: &Cli, context: ClientContext) -> Result Result>> { + // Client::try_new() automatically detects the URL scheme (http:// or https://) + // and creates the appropriate client. + // Note: Certificate pinning and mutual TLS are only available on Linux/Unix with OpenSSL let client = Client::try_new(&args.server_address).context("Failed to create HTTP(S) client")?; Ok(Box::new(client.with_context(context))) diff --git a/samples/server/petstore/rust-server/output/petstore-with-fake-endpoints-models-for-testing/examples/client/main.rs b/samples/server/petstore/rust-server/output/petstore-with-fake-endpoints-models-for-testing/examples/client/main.rs index 8e8aa8e817cf..df4b9e3db338 100644 --- a/samples/server/petstore/rust-server/output/petstore-with-fake-endpoints-models-for-testing/examples/client/main.rs +++ b/samples/server/petstore/rust-server/output/petstore-with-fake-endpoints-models-for-testing/examples/client/main.rs @@ -156,17 +156,33 @@ fn main() { let context: ClientContext = swagger::make_context!(ContextBuilder, EmptyContext, auth_data, XSpanIdString::default()); - let mut client : Box> = if is_https { - // Using Simple HTTPS - let client = Box::new(Client::try_new_https(&base_url) - .expect("Failed to create HTTPS client")); - Box::new(client.with_context(context)) - } else { - // Using HTTP - let client = Box::new(Client::try_new_http( - &base_url) - .expect("Failed to create HTTP client")); - Box::new(client.with_context(context)) + let mut client : Box> = { + #[cfg(feature = "client-tls")] + { + if is_https { + // Using HTTPS with native-tls + let client = Box::new(Client::try_new_https(&base_url) + .expect("Failed to create HTTPS client")); + Box::new(client.with_context(context)) + } else { + // Using HTTP + let client = Box::new(Client::try_new_http(&base_url) + .expect("Failed to create HTTP client")); + Box::new(client.with_context(context)) + } + } + + #[cfg(not(feature = "client-tls"))] + { + if is_https { + panic!("HTTPS requested but TLS support not enabled. \ + Enable the 'client-tls' feature to use HTTPS."); + } + // Using HTTP only + let client = Box::new(Client::try_new_http(&base_url) + .expect("Failed to create HTTP client")); + Box::new(client.with_context(context)) + } }; let mut rt = tokio::runtime::Runtime::new().unwrap(); diff --git a/samples/server/petstore/rust-server/output/petstore-with-fake-endpoints-models-for-testing/src/client/mod.rs b/samples/server/petstore/rust-server/output/petstore-with-fake-endpoints-models-for-testing/src/client/mod.rs index 277243480ae9..f3d08aea796a 100644 --- a/samples/server/petstore/rust-server/output/petstore-with-fake-endpoints-models-for-testing/src/client/mod.rs +++ b/samples/server/petstore/rust-server/output/petstore-with-fake-endpoints-models-for-testing/src/client/mod.rs @@ -190,14 +190,17 @@ impl Client< } } +#[cfg(all(feature = "client-tls", any(target_os = "macos", target_os = "windows", target_os = "ios")))] +type HyperHttpsConnector = hyper_tls::HttpsConnector; + +#[cfg(all(feature = "client-tls", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))))] +type HyperHttpsConnector = hyper_openssl::client::legacy::HttpsConnector; + #[derive(Debug, Clone)] pub enum HyperClient { Http(hyper_util::client::legacy::Client>), - #[cfg(any( - all(feature = "client-tls", any(target_os = "macos", target_os = "windows", target_os = "ios")), - all(feature = "client-openssl", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))) - ))] - Https(hyper_util::client::legacy::Client>), + #[cfg(feature = "client-tls")] + Https(hyper_util::client::legacy::Client>), } impl Service>> for HyperClient { @@ -208,10 +211,7 @@ impl Service>> for HyperClient { fn call(&self, req: Request>) -> Self::Future { match self { HyperClient::Http(client) => client.request(req), - #[cfg(any( - all(feature = "client-tls", any(target_os = "macos", target_os = "windows", target_os = "ios")), - all(feature = "client-openssl", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))) - ))] + #[cfg(feature = "client-tls")] HyperClient::Https(client) => client.request(req), } } @@ -238,20 +238,15 @@ impl Client, C> where "http" => { HyperClient::Http(hyper_util::client::legacy::Client::builder(hyper_util::rt::TokioExecutor::new()).build(connector.build())) }, - #[cfg(any( - all(feature = "client-tls", any(target_os = "macos", target_os = "windows", target_os = "ios")), - all(feature = "client-openssl", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))) - ))] + #[cfg(feature = "client-tls")] "https" => { - let connector = connector.https() - .build() - .map_err(ClientInitError::SslError)?; - HyperClient::Https(hyper_util::client::legacy::Client::builder(hyper_util::rt::TokioExecutor::new()).build(connector)) + let https_connector = connector + .https() + .build() + .map_err(ClientInitError::SslError)?; + HyperClient::Https(hyper_util::client::legacy::Client::builder(hyper_util::rt::TokioExecutor::new()).build(https_connector)) }, - #[cfg(not(any( - all(feature = "client-tls", any(target_os = "macos", target_os = "windows", target_os = "ios")), - all(feature = "client-openssl", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))) - )))] + #[cfg(not(feature = "client-tls"))] "https" => { return Err(ClientInitError::TlsNotEnabled); }, @@ -300,13 +295,10 @@ impl Client< #[cfg(all(feature = "client-tls", any(target_os = "macos", target_os = "windows", target_os = "ios")))] type HttpsConnector = hyper_tls::HttpsConnector; -#[cfg(all(feature = "client-openssl", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))))] +#[cfg(all(feature = "client-tls", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))))] type HttpsConnector = hyper_openssl::client::legacy::HttpsConnector; -#[cfg(any( - all(feature = "client-tls", any(target_os = "macos", target_os = "windows", target_os = "ios")), - all(feature = "client-openssl", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))) -))] +#[cfg(feature = "client-tls")] impl Client< DropContextService< hyper_util::service::TowerToHyperService< @@ -321,10 +313,25 @@ impl Client< > where C: Clone + Send + Sync + 'static { - /// Create a client with a TLS connection to the server + /// Create a client with a TLS connection to the server using native-tls. /// /// # Arguments - /// * `base_path` - base path of the client API, i.e. "" + /// * `base_path` - base path of the client API, i.e. "" + #[cfg(any(target_os = "macos", target_os = "windows", target_os = "ios"))] + pub fn try_new_https(base_path: &str) -> Result + { + let https_connector = Connector::builder() + .https() + .build() + .map_err(ClientInitError::SslError)?; + Self::try_new_with_connector(base_path, Some("https"), https_connector) + } + + /// Create a client with a TLS connection to the server using OpenSSL. + /// + /// # Arguments + /// * `base_path` - base path of the client API, i.e. "" + #[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))] pub fn try_new_https(base_path: &str) -> Result { let https_connector = Connector::builder() @@ -337,9 +344,9 @@ impl Client< /// Create a client with a TLS connection to the server using a pinned certificate /// /// # Arguments - /// * `base_path` - base path of the client API, i.e. "" + /// * `base_path` - base path of the client API, i.e. "" /// * `ca_certificate` - Path to CA certificate used to authenticate the server - #[cfg(all(feature = "client-openssl", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))))] + #[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))] pub fn try_new_https_pinned( base_path: &str, ca_certificate: CA, @@ -358,11 +365,11 @@ impl Client< /// Create a client with a mutually authenticated TLS connection to the server. /// /// # Arguments - /// * `base_path` - base path of the client API, i.e. "" + /// * `base_path` - base path of the client API, i.e. "" /// * `ca_certificate` - Path to CA certificate used to authenticate the server /// * `client_key` - Path to the client private key /// * `client_certificate` - Path to the client's public certificate associated with the private key - #[cfg(all(feature = "client-openssl", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))))] + #[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))] pub fn try_new_https_mutual( base_path: &str, ca_certificate: CA, @@ -428,7 +435,7 @@ pub enum ClientInitError { SslError(native_tls::Error), /// SSL Connection Error - #[cfg(all(feature = "client-openssl", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))))] + #[cfg(all(feature = "client-tls", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))))] SslError(openssl::error::ErrorStack), } diff --git a/samples/server/petstore/rust-server/output/ping-bearer-auth/Cargo.toml b/samples/server/petstore/rust-server/output/ping-bearer-auth/Cargo.toml index d3e60a48887c..ec7d1bd02114 100644 --- a/samples/server/petstore/rust-server/output/ping-bearer-auth/Cargo.toml +++ b/samples/server/petstore/rust-server/output/ping-bearer-auth/Cargo.toml @@ -8,20 +8,20 @@ license = "Unlicense" edition = "2018" [features] -default = ["client", "server", "client-tls", "client-openssl"] +default = ["client", "server"] client = [ "hyper", "percent-encoding", "hyper-util/http1", "hyper-util/http2", "url" ] -# TLS support using native-tls (macOS/Windows/iOS) or hyper-tls +# TLS support - automatically selects backend based on target OS: +# - macOS/Windows/iOS: native-tls via hyper-tls +# - Other platforms: OpenSSL via hyper-openssl +# Dependencies are in target-specific sections below client-tls = [ "client", - "hyper-tls", "native-tls", - "swagger/tls" -] -# TLS support using OpenSSL (Linux and other platforms) -client-openssl = [ - "client", - "hyper-openssl", "openssl", + "dep:native-tls", + "dep:hyper-tls", + "dep:openssl", + "dep:hyper-openssl", "swagger/tls" ] server = [ @@ -36,14 +36,6 @@ conversion = ["frunk", "frunk_derives", "frunk_core", "frunk-enum-core", "frunk- mock = ["mockall"] validate = ["regex", "serde_valid", "swagger/serdevalid"] -[target.'cfg(any(target_os = "macos", target_os = "windows", target_os = "ios"))'.dependencies] -native-tls = { version = "0.2", optional = true } -hyper-tls = { version = "0.6", optional = true } - -[target.'cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))'.dependencies] -hyper-openssl = { version = "0.10", optional = true } -openssl = { version = "0.10", optional = true } - [dependencies] # Common async-trait = "0.1.88" @@ -76,6 +68,12 @@ url = { version = "2.5", optional = true } # Client-specific tower-service = "0.3.3" +# TLS support - all listed here, actual usage determined by cfg attributes in code +native-tls = { version = "0.2", optional = true } +hyper-tls = { version = "0.6", optional = true } +openssl = { version = "0.10", optional = true } +hyper-openssl = { version = "0.10", optional = true } + # Server, and client callback-specific lazy_static = { version = "1.5", optional = true } regex = { version = "1.12", optional = true } @@ -101,15 +99,13 @@ clap = "4.5" env_logger = "0.11" tokio = { version = "1.49", features = ["full"] } native-tls = "0.2" +openssl = "0.10" +tokio-openssl = "0.6" pin-project = "1.1.10" # Bearer authentication, used in examples jsonwebtoken = {version = "10.0.0", features = ["rust_crypto"]} -[target.'cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))'.dev-dependencies] -tokio-openssl = "0.6" -openssl = "0.10" - [[example]] name = "client" required-features = ["client"] diff --git a/samples/server/petstore/rust-server/output/ping-bearer-auth/README.md b/samples/server/petstore/rust-server/output/ping-bearer-auth/README.md index d7ab962954b8..0c451cb6f4ca 100644 --- a/samples/server/petstore/rust-server/output/ping-bearer-auth/README.md +++ b/samples/server/petstore/rust-server/output/ping-bearer-auth/README.md @@ -110,11 +110,10 @@ The generated library has a few optional features that can be activated through * This defaults to enabled and creates the basic skeleton of a client implementation based on hyper * The constructed client implements the API trait by making remote API call. * `client-tls` - * This defaults to enabled and provides HTTPS support using native-tls (macOS/Windows/iOS). - * Enable this feature for TLS support on Apple and Windows platforms. -* `client-openssl` - * This defaults to enabled and provides HTTPS support using OpenSSL (Linux and other platforms). - * Enable this feature for TLS support on Linux and other Unix-like platforms. + * Optional feature that provides HTTPS support with automatic TLS backend selection: + - macOS/Windows/iOS: native-tls + hyper-tls + - Linux/Unix/others: OpenSSL + hyper-openssl + * Not enabled by default to minimize dependencies. * `conversions` * This defaults to disabled and creates extra derives on models to allow "transmogrification" between objects of structurally similar types. * `cli` @@ -123,22 +122,27 @@ The generated library has a few optional features that can be activated through * This defaults to disabled and allows JSON Schema validation of received data using `MakeService::set_validation` or `Service::set_validation`. * Note, enabling validation will have a performance penalty, especially if the API heavily uses regex based checks. -### Minimal dependencies (no TLS) +### Enabling HTTPS/TLS Support -If you only need HTTP support and want to minimize dependencies (e.g., to avoid OpenSSL build requirements), you can disable the default TLS features: +By default, only HTTP support is included. To enable HTTPS, add the `client-tls` feature: ```toml [dependencies] -ping-bearer-auth = { version = "1.0.0", default-features = false, features = ["server"] } +ping-bearer-auth = { version = "1.0.0", features = ["client-tls"] } ``` -Or for client-only without TLS: - +**For server with callbacks that need HTTPS:** ```toml [dependencies] -ping-bearer-auth = { version = "1.0.0", default-features = false, features = ["client"] } +ping-bearer-auth = { version = "1.0.0", features = ["server", "client-tls"] } ``` +The TLS backend is automatically selected based on your target platform: +- **macOS, Windows, iOS**: Uses `native-tls` (system TLS libraries) +- **Linux, Unix, other platforms**: Uses `openssl` + +This ensures the best compatibility and native integration on each platform. + See https://doc.rust-lang.org/cargo/reference/manifest.html#the-features-section for how to use features in your `Cargo.toml`. ## Documentation for API Endpoints diff --git a/samples/server/petstore/rust-server/output/ping-bearer-auth/bin/cli.rs b/samples/server/petstore/rust-server/output/ping-bearer-auth/bin/cli.rs index 74286933066e..7256eebe98c3 100644 --- a/samples/server/petstore/rust-server/output/ping-bearer-auth/bin/cli.rs +++ b/samples/server/petstore/rust-server/output/ping-bearer-auth/bin/cli.rs @@ -33,17 +33,17 @@ struct Cli { server_address: String, /// Path to the client private key if using client-side TLS authentication - #[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))] + #[cfg(all(feature = "client-tls", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))))] #[clap(long, requires_all(&["client_certificate", "server_certificate"]))] client_key: Option, /// Path to the client's public certificate associated with the private key - #[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))] + #[cfg(all(feature = "client-tls", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))))] #[clap(long, requires_all(&["client_key", "server_certificate"]))] client_certificate: Option, /// Path to CA certificate used to authenticate the server - #[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))] + #[cfg(all(feature = "client-tls", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))))] #[clap(long)] server_certificate: Option, @@ -65,7 +65,8 @@ enum Operation { }, } -#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))] +// On Linux/Unix with OpenSSL (client-tls feature), support certificate pinning and mutual TLS +#[cfg(all(feature = "client-tls", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))))] fn create_client(args: &Cli, context: ClientContext) -> Result>> { if args.client_certificate.is_some() { debug!("Using mutual TLS"); @@ -91,8 +92,15 @@ fn create_client(args: &Cli, context: ClientContext) -> Result Result>> { + // Client::try_new() automatically detects the URL scheme (http:// or https://) + // and creates the appropriate client. + // Note: Certificate pinning and mutual TLS are only available on Linux/Unix with OpenSSL let client = Client::try_new(&args.server_address).context("Failed to create HTTP(S) client")?; Ok(Box::new(client.with_context(context))) diff --git a/samples/server/petstore/rust-server/output/ping-bearer-auth/examples/client/main.rs b/samples/server/petstore/rust-server/output/ping-bearer-auth/examples/client/main.rs index 24d1f7fe28ef..c7ff5d2f012e 100644 --- a/samples/server/petstore/rust-server/output/ping-bearer-auth/examples/client/main.rs +++ b/samples/server/petstore/rust-server/output/ping-bearer-auth/examples/client/main.rs @@ -85,17 +85,33 @@ fn main() { let context: ClientContext = swagger::make_context!(ContextBuilder, EmptyContext, auth_data, XSpanIdString::default()); - let mut client : Box> = if is_https { - // Using Simple HTTPS - let client = Box::new(Client::try_new_https(&base_url) - .expect("Failed to create HTTPS client")); - Box::new(client.with_context(context)) - } else { - // Using HTTP - let client = Box::new(Client::try_new_http( - &base_url) - .expect("Failed to create HTTP client")); - Box::new(client.with_context(context)) + let mut client : Box> = { + #[cfg(feature = "client-tls")] + { + if is_https { + // Using HTTPS with native-tls + let client = Box::new(Client::try_new_https(&base_url) + .expect("Failed to create HTTPS client")); + Box::new(client.with_context(context)) + } else { + // Using HTTP + let client = Box::new(Client::try_new_http(&base_url) + .expect("Failed to create HTTP client")); + Box::new(client.with_context(context)) + } + } + + #[cfg(not(feature = "client-tls"))] + { + if is_https { + panic!("HTTPS requested but TLS support not enabled. \ + Enable the 'client-tls' feature to use HTTPS."); + } + // Using HTTP only + let client = Box::new(Client::try_new_http(&base_url) + .expect("Failed to create HTTP client")); + Box::new(client.with_context(context)) + } }; let mut rt = tokio::runtime::Runtime::new().unwrap(); diff --git a/samples/server/petstore/rust-server/output/ping-bearer-auth/src/client/mod.rs b/samples/server/petstore/rust-server/output/ping-bearer-auth/src/client/mod.rs index 22acaaaff83f..ef7f47000c02 100644 --- a/samples/server/petstore/rust-server/output/ping-bearer-auth/src/client/mod.rs +++ b/samples/server/petstore/rust-server/output/ping-bearer-auth/src/client/mod.rs @@ -153,14 +153,17 @@ impl Client< } } +#[cfg(all(feature = "client-tls", any(target_os = "macos", target_os = "windows", target_os = "ios")))] +type HyperHttpsConnector = hyper_tls::HttpsConnector; + +#[cfg(all(feature = "client-tls", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))))] +type HyperHttpsConnector = hyper_openssl::client::legacy::HttpsConnector; + #[derive(Debug, Clone)] pub enum HyperClient { Http(hyper_util::client::legacy::Client>), - #[cfg(any( - all(feature = "client-tls", any(target_os = "macos", target_os = "windows", target_os = "ios")), - all(feature = "client-openssl", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))) - ))] - Https(hyper_util::client::legacy::Client>), + #[cfg(feature = "client-tls")] + Https(hyper_util::client::legacy::Client>), } impl Service>> for HyperClient { @@ -171,10 +174,7 @@ impl Service>> for HyperClient { fn call(&self, req: Request>) -> Self::Future { match self { HyperClient::Http(client) => client.request(req), - #[cfg(any( - all(feature = "client-tls", any(target_os = "macos", target_os = "windows", target_os = "ios")), - all(feature = "client-openssl", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))) - ))] + #[cfg(feature = "client-tls")] HyperClient::Https(client) => client.request(req), } } @@ -201,20 +201,15 @@ impl Client, C> where "http" => { HyperClient::Http(hyper_util::client::legacy::Client::builder(hyper_util::rt::TokioExecutor::new()).build(connector.build())) }, - #[cfg(any( - all(feature = "client-tls", any(target_os = "macos", target_os = "windows", target_os = "ios")), - all(feature = "client-openssl", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))) - ))] + #[cfg(feature = "client-tls")] "https" => { - let connector = connector.https() - .build() - .map_err(ClientInitError::SslError)?; - HyperClient::Https(hyper_util::client::legacy::Client::builder(hyper_util::rt::TokioExecutor::new()).build(connector)) + let https_connector = connector + .https() + .build() + .map_err(ClientInitError::SslError)?; + HyperClient::Https(hyper_util::client::legacy::Client::builder(hyper_util::rt::TokioExecutor::new()).build(https_connector)) }, - #[cfg(not(any( - all(feature = "client-tls", any(target_os = "macos", target_os = "windows", target_os = "ios")), - all(feature = "client-openssl", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))) - )))] + #[cfg(not(feature = "client-tls"))] "https" => { return Err(ClientInitError::TlsNotEnabled); }, @@ -263,13 +258,10 @@ impl Client< #[cfg(all(feature = "client-tls", any(target_os = "macos", target_os = "windows", target_os = "ios")))] type HttpsConnector = hyper_tls::HttpsConnector; -#[cfg(all(feature = "client-openssl", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))))] +#[cfg(all(feature = "client-tls", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))))] type HttpsConnector = hyper_openssl::client::legacy::HttpsConnector; -#[cfg(any( - all(feature = "client-tls", any(target_os = "macos", target_os = "windows", target_os = "ios")), - all(feature = "client-openssl", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))) -))] +#[cfg(feature = "client-tls")] impl Client< DropContextService< hyper_util::service::TowerToHyperService< @@ -284,10 +276,25 @@ impl Client< > where C: Clone + Send + Sync + 'static { - /// Create a client with a TLS connection to the server + /// Create a client with a TLS connection to the server using native-tls. /// /// # Arguments - /// * `base_path` - base path of the client API, i.e. "" + /// * `base_path` - base path of the client API, i.e. "" + #[cfg(any(target_os = "macos", target_os = "windows", target_os = "ios"))] + pub fn try_new_https(base_path: &str) -> Result + { + let https_connector = Connector::builder() + .https() + .build() + .map_err(ClientInitError::SslError)?; + Self::try_new_with_connector(base_path, Some("https"), https_connector) + } + + /// Create a client with a TLS connection to the server using OpenSSL. + /// + /// # Arguments + /// * `base_path` - base path of the client API, i.e. "" + #[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))] pub fn try_new_https(base_path: &str) -> Result { let https_connector = Connector::builder() @@ -300,9 +307,9 @@ impl Client< /// Create a client with a TLS connection to the server using a pinned certificate /// /// # Arguments - /// * `base_path` - base path of the client API, i.e. "" + /// * `base_path` - base path of the client API, i.e. "" /// * `ca_certificate` - Path to CA certificate used to authenticate the server - #[cfg(all(feature = "client-openssl", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))))] + #[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))] pub fn try_new_https_pinned( base_path: &str, ca_certificate: CA, @@ -321,11 +328,11 @@ impl Client< /// Create a client with a mutually authenticated TLS connection to the server. /// /// # Arguments - /// * `base_path` - base path of the client API, i.e. "" + /// * `base_path` - base path of the client API, i.e. "" /// * `ca_certificate` - Path to CA certificate used to authenticate the server /// * `client_key` - Path to the client private key /// * `client_certificate` - Path to the client's public certificate associated with the private key - #[cfg(all(feature = "client-openssl", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))))] + #[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))] pub fn try_new_https_mutual( base_path: &str, ca_certificate: CA, @@ -391,7 +398,7 @@ pub enum ClientInitError { SslError(native_tls::Error), /// SSL Connection Error - #[cfg(all(feature = "client-openssl", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))))] + #[cfg(all(feature = "client-tls", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))))] SslError(openssl::error::ErrorStack), } diff --git a/samples/server/petstore/rust-server/output/rust-server-test/Cargo.toml b/samples/server/petstore/rust-server/output/rust-server-test/Cargo.toml index 6886c7dd31b5..59bac87e1e51 100644 --- a/samples/server/petstore/rust-server/output/rust-server-test/Cargo.toml +++ b/samples/server/petstore/rust-server/output/rust-server-test/Cargo.toml @@ -8,20 +8,20 @@ license = "Unlicense" edition = "2018" [features] -default = ["client", "server", "client-tls", "client-openssl"] +default = ["client", "server"] client = [ "hyper", "percent-encoding", "hyper-util/http1", "hyper-util/http2", "url" ] -# TLS support using native-tls (macOS/Windows/iOS) or hyper-tls +# TLS support - automatically selects backend based on target OS: +# - macOS/Windows/iOS: native-tls via hyper-tls +# - Other platforms: OpenSSL via hyper-openssl +# Dependencies are in target-specific sections below client-tls = [ "client", - "hyper-tls", "native-tls", - "swagger/tls" -] -# TLS support using OpenSSL (Linux and other platforms) -client-openssl = [ - "client", - "hyper-openssl", "openssl", + "dep:native-tls", + "dep:hyper-tls", + "dep:openssl", + "dep:hyper-openssl", "swagger/tls" ] server = [ @@ -36,14 +36,6 @@ conversion = ["frunk", "frunk_derives", "frunk_core", "frunk-enum-core", "frunk- mock = ["mockall"] validate = ["regex", "serde_valid", "swagger/serdevalid"] -[target.'cfg(any(target_os = "macos", target_os = "windows", target_os = "ios"))'.dependencies] -native-tls = { version = "0.2", optional = true } -hyper-tls = { version = "0.6", optional = true } - -[target.'cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))'.dependencies] -hyper-openssl = { version = "0.10", optional = true } -openssl = { version = "0.10", optional = true } - [dependencies] # Common async-trait = "0.1.88" @@ -76,6 +68,12 @@ url = { version = "2.5", optional = true } # Client-specific tower-service = "0.3.3" +# TLS support - all listed here, actual usage determined by cfg attributes in code +native-tls = { version = "0.2", optional = true } +hyper-tls = { version = "0.6", optional = true } +openssl = { version = "0.10", optional = true } +hyper-openssl = { version = "0.10", optional = true } + # Server, and client callback-specific lazy_static = { version = "1.5", optional = true } regex = { version = "1.12", optional = true } @@ -101,15 +99,13 @@ clap = "4.5" env_logger = "0.11" tokio = { version = "1.49", features = ["full"] } native-tls = "0.2" +openssl = "0.10" +tokio-openssl = "0.6" pin-project = "1.1.10" # Bearer authentication, used in examples jsonwebtoken = {version = "10.0.0", features = ["rust_crypto"]} -[target.'cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))'.dev-dependencies] -tokio-openssl = "0.6" -openssl = "0.10" - [[example]] name = "client" required-features = ["client"] diff --git a/samples/server/petstore/rust-server/output/rust-server-test/README.md b/samples/server/petstore/rust-server/output/rust-server-test/README.md index c7e3e87a5b13..9fd06fad6f2e 100644 --- a/samples/server/petstore/rust-server/output/rust-server-test/README.md +++ b/samples/server/petstore/rust-server/output/rust-server-test/README.md @@ -116,11 +116,10 @@ The generated library has a few optional features that can be activated through * This defaults to enabled and creates the basic skeleton of a client implementation based on hyper * The constructed client implements the API trait by making remote API call. * `client-tls` - * This defaults to enabled and provides HTTPS support using native-tls (macOS/Windows/iOS). - * Enable this feature for TLS support on Apple and Windows platforms. -* `client-openssl` - * This defaults to enabled and provides HTTPS support using OpenSSL (Linux and other platforms). - * Enable this feature for TLS support on Linux and other Unix-like platforms. + * Optional feature that provides HTTPS support with automatic TLS backend selection: + - macOS/Windows/iOS: native-tls + hyper-tls + - Linux/Unix/others: OpenSSL + hyper-openssl + * Not enabled by default to minimize dependencies. * `conversions` * This defaults to disabled and creates extra derives on models to allow "transmogrification" between objects of structurally similar types. * `cli` @@ -129,22 +128,27 @@ The generated library has a few optional features that can be activated through * This defaults to disabled and allows JSON Schema validation of received data using `MakeService::set_validation` or `Service::set_validation`. * Note, enabling validation will have a performance penalty, especially if the API heavily uses regex based checks. -### Minimal dependencies (no TLS) +### Enabling HTTPS/TLS Support -If you only need HTTP support and want to minimize dependencies (e.g., to avoid OpenSSL build requirements), you can disable the default TLS features: +By default, only HTTP support is included. To enable HTTPS, add the `client-tls` feature: ```toml [dependencies] -rust-server-test = { version = "2.3.4", default-features = false, features = ["server"] } +rust-server-test = { version = "2.3.4", features = ["client-tls"] } ``` -Or for client-only without TLS: - +**For server with callbacks that need HTTPS:** ```toml [dependencies] -rust-server-test = { version = "2.3.4", default-features = false, features = ["client"] } +rust-server-test = { version = "2.3.4", features = ["server", "client-tls"] } ``` +The TLS backend is automatically selected based on your target platform: +- **macOS, Windows, iOS**: Uses `native-tls` (system TLS libraries) +- **Linux, Unix, other platforms**: Uses `openssl` + +This ensures the best compatibility and native integration on each platform. + See https://doc.rust-lang.org/cargo/reference/manifest.html#the-features-section for how to use features in your `Cargo.toml`. ## Documentation for API Endpoints diff --git a/samples/server/petstore/rust-server/output/rust-server-test/bin/cli.rs b/samples/server/petstore/rust-server/output/rust-server-test/bin/cli.rs index 01cbbf1f003a..b9446c60ed6b 100644 --- a/samples/server/petstore/rust-server/output/rust-server-test/bin/cli.rs +++ b/samples/server/petstore/rust-server/output/rust-server-test/bin/cli.rs @@ -41,17 +41,17 @@ struct Cli { server_address: String, /// Path to the client private key if using client-side TLS authentication - #[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))] + #[cfg(all(feature = "client-tls", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))))] #[clap(long, requires_all(&["client_certificate", "server_certificate"]))] client_key: Option, /// Path to the client's public certificate associated with the private key - #[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))] + #[cfg(all(feature = "client-tls", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))))] #[clap(long, requires_all(&["client_key", "server_certificate"]))] client_certificate: Option, /// Path to CA certificate used to authenticate the server - #[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))] + #[cfg(all(feature = "client-tls", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))))] #[clap(long)] server_certificate: Option, @@ -96,7 +96,8 @@ enum Operation { }, } -#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))] +// On Linux/Unix with OpenSSL (client-tls feature), support certificate pinning and mutual TLS +#[cfg(all(feature = "client-tls", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))))] fn create_client(args: &Cli, context: ClientContext) -> Result>> { if args.client_certificate.is_some() { debug!("Using mutual TLS"); @@ -122,8 +123,15 @@ fn create_client(args: &Cli, context: ClientContext) -> Result Result>> { + // Client::try_new() automatically detects the URL scheme (http:// or https://) + // and creates the appropriate client. + // Note: Certificate pinning and mutual TLS are only available on Linux/Unix with OpenSSL let client = Client::try_new(&args.server_address).context("Failed to create HTTP(S) client")?; Ok(Box::new(client.with_context(context))) diff --git a/samples/server/petstore/rust-server/output/rust-server-test/examples/client/main.rs b/samples/server/petstore/rust-server/output/rust-server-test/examples/client/main.rs index 63ebeec10bff..87e4794a994d 100644 --- a/samples/server/petstore/rust-server/output/rust-server-test/examples/client/main.rs +++ b/samples/server/petstore/rust-server/output/rust-server-test/examples/client/main.rs @@ -101,17 +101,33 @@ fn main() { let context: ClientContext = swagger::make_context!(ContextBuilder, EmptyContext, auth_data, XSpanIdString::default()); - let mut client : Box> = if is_https { - // Using Simple HTTPS - let client = Box::new(Client::try_new_https(&base_url) - .expect("Failed to create HTTPS client")); - Box::new(client.with_context(context)) - } else { - // Using HTTP - let client = Box::new(Client::try_new_http( - &base_url) - .expect("Failed to create HTTP client")); - Box::new(client.with_context(context)) + let mut client : Box> = { + #[cfg(feature = "client-tls")] + { + if is_https { + // Using HTTPS with native-tls + let client = Box::new(Client::try_new_https(&base_url) + .expect("Failed to create HTTPS client")); + Box::new(client.with_context(context)) + } else { + // Using HTTP + let client = Box::new(Client::try_new_http(&base_url) + .expect("Failed to create HTTP client")); + Box::new(client.with_context(context)) + } + } + + #[cfg(not(feature = "client-tls"))] + { + if is_https { + panic!("HTTPS requested but TLS support not enabled. \ + Enable the 'client-tls' feature to use HTTPS."); + } + // Using HTTP only + let client = Box::new(Client::try_new_http(&base_url) + .expect("Failed to create HTTP client")); + Box::new(client.with_context(context)) + } }; let mut rt = tokio::runtime::Runtime::new().unwrap(); diff --git a/samples/server/petstore/rust-server/output/rust-server-test/src/client/mod.rs b/samples/server/petstore/rust-server/output/rust-server-test/src/client/mod.rs index b30e11ef7710..e178ec17e05b 100644 --- a/samples/server/petstore/rust-server/output/rust-server-test/src/client/mod.rs +++ b/samples/server/petstore/rust-server/output/rust-server-test/src/client/mod.rs @@ -161,14 +161,17 @@ impl Client< } } +#[cfg(all(feature = "client-tls", any(target_os = "macos", target_os = "windows", target_os = "ios")))] +type HyperHttpsConnector = hyper_tls::HttpsConnector; + +#[cfg(all(feature = "client-tls", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))))] +type HyperHttpsConnector = hyper_openssl::client::legacy::HttpsConnector; + #[derive(Debug, Clone)] pub enum HyperClient { Http(hyper_util::client::legacy::Client>), - #[cfg(any( - all(feature = "client-tls", any(target_os = "macos", target_os = "windows", target_os = "ios")), - all(feature = "client-openssl", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))) - ))] - Https(hyper_util::client::legacy::Client>), + #[cfg(feature = "client-tls")] + Https(hyper_util::client::legacy::Client>), } impl Service>> for HyperClient { @@ -179,10 +182,7 @@ impl Service>> for HyperClient { fn call(&self, req: Request>) -> Self::Future { match self { HyperClient::Http(client) => client.request(req), - #[cfg(any( - all(feature = "client-tls", any(target_os = "macos", target_os = "windows", target_os = "ios")), - all(feature = "client-openssl", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))) - ))] + #[cfg(feature = "client-tls")] HyperClient::Https(client) => client.request(req), } } @@ -209,20 +209,15 @@ impl Client, C> where "http" => { HyperClient::Http(hyper_util::client::legacy::Client::builder(hyper_util::rt::TokioExecutor::new()).build(connector.build())) }, - #[cfg(any( - all(feature = "client-tls", any(target_os = "macos", target_os = "windows", target_os = "ios")), - all(feature = "client-openssl", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))) - ))] + #[cfg(feature = "client-tls")] "https" => { - let connector = connector.https() - .build() - .map_err(ClientInitError::SslError)?; - HyperClient::Https(hyper_util::client::legacy::Client::builder(hyper_util::rt::TokioExecutor::new()).build(connector)) + let https_connector = connector + .https() + .build() + .map_err(ClientInitError::SslError)?; + HyperClient::Https(hyper_util::client::legacy::Client::builder(hyper_util::rt::TokioExecutor::new()).build(https_connector)) }, - #[cfg(not(any( - all(feature = "client-tls", any(target_os = "macos", target_os = "windows", target_os = "ios")), - all(feature = "client-openssl", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))) - )))] + #[cfg(not(feature = "client-tls"))] "https" => { return Err(ClientInitError::TlsNotEnabled); }, @@ -271,13 +266,10 @@ impl Client< #[cfg(all(feature = "client-tls", any(target_os = "macos", target_os = "windows", target_os = "ios")))] type HttpsConnector = hyper_tls::HttpsConnector; -#[cfg(all(feature = "client-openssl", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))))] +#[cfg(all(feature = "client-tls", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))))] type HttpsConnector = hyper_openssl::client::legacy::HttpsConnector; -#[cfg(any( - all(feature = "client-tls", any(target_os = "macos", target_os = "windows", target_os = "ios")), - all(feature = "client-openssl", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))) -))] +#[cfg(feature = "client-tls")] impl Client< DropContextService< hyper_util::service::TowerToHyperService< @@ -292,10 +284,25 @@ impl Client< > where C: Clone + Send + Sync + 'static { - /// Create a client with a TLS connection to the server + /// Create a client with a TLS connection to the server using native-tls. /// /// # Arguments - /// * `base_path` - base path of the client API, i.e. "" + /// * `base_path` - base path of the client API, i.e. "" + #[cfg(any(target_os = "macos", target_os = "windows", target_os = "ios"))] + pub fn try_new_https(base_path: &str) -> Result + { + let https_connector = Connector::builder() + .https() + .build() + .map_err(ClientInitError::SslError)?; + Self::try_new_with_connector(base_path, Some("https"), https_connector) + } + + /// Create a client with a TLS connection to the server using OpenSSL. + /// + /// # Arguments + /// * `base_path` - base path of the client API, i.e. "" + #[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))] pub fn try_new_https(base_path: &str) -> Result { let https_connector = Connector::builder() @@ -308,9 +315,9 @@ impl Client< /// Create a client with a TLS connection to the server using a pinned certificate /// /// # Arguments - /// * `base_path` - base path of the client API, i.e. "" + /// * `base_path` - base path of the client API, i.e. "" /// * `ca_certificate` - Path to CA certificate used to authenticate the server - #[cfg(all(feature = "client-openssl", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))))] + #[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))] pub fn try_new_https_pinned( base_path: &str, ca_certificate: CA, @@ -329,11 +336,11 @@ impl Client< /// Create a client with a mutually authenticated TLS connection to the server. /// /// # Arguments - /// * `base_path` - base path of the client API, i.e. "" + /// * `base_path` - base path of the client API, i.e. "" /// * `ca_certificate` - Path to CA certificate used to authenticate the server /// * `client_key` - Path to the client private key /// * `client_certificate` - Path to the client's public certificate associated with the private key - #[cfg(all(feature = "client-openssl", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))))] + #[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))] pub fn try_new_https_mutual( base_path: &str, ca_certificate: CA, @@ -399,7 +406,7 @@ pub enum ClientInitError { SslError(native_tls::Error), /// SSL Connection Error - #[cfg(all(feature = "client-openssl", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))))] + #[cfg(all(feature = "client-tls", not(any(target_os = "macos", target_os = "windows", target_os = "ios"))))] SslError(openssl::error::ErrorStack), }