diff --git a/.github/actions/contract-tests/action.yml b/.github/actions/contract-tests/action.yml index a4f9438..3875477 100644 --- a/.github/actions/contract-tests/action.yml +++ b/.github/actions/contract-tests/action.yml @@ -1,8 +1,8 @@ name: Contract test runner description: 'Reusable contract runner action' inputs: - tls_feature: - description: 'Which TLS feature do you want to enable?' + features: + description: 'Which features should be enabled during build?' required: true token: description: 'GH Token used for retrieving SDK test harness.' @@ -13,7 +13,7 @@ runs: steps: - name: Build contract tests shell: bash - run: TLS_FEATURE="${{ inputs.tls_feature }}" make build-contract-tests + run: FEATURES="${{ inputs.features }}" make build-contract-tests - name: Start contract test service shell: bash diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 57d7cca..6ad9c5d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -25,16 +25,22 @@ jobs: - uses: ./.github/actions/ci - - name: 'Run contract tests with hyper_rustls' + - name: 'Run contract tests with hyper_rustls and native cert store' uses: ./.github/actions/contract-tests with: - tls_feature: 'rustls' + features: 'rustls' + token: ${{ secrets.GITHUB_TOKEN }} + + - name: 'Run contract tests with hyper_rustls and WebPKI cert store' + uses: ./.github/actions/contract-tests + with: + features: 'rustls,webpki-roots' token: ${{ secrets.GITHUB_TOKEN }} - name: 'Run contract tests with hyper_tls' uses: ./.github/actions/contract-tests with: - tls_feature: 'tls' + features: 'tls' token: ${{ secrets.GITHUB_TOKEN }} - uses: ./.github/actions/build-docs diff --git a/.gitignore b/.gitignore index d889fbf..45795ca 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ **/*.rs.bk Cargo.lock .idea +.vscode \ No newline at end of file diff --git a/Makefile b/Makefile index 3da13d4..b6b1a7a 100644 --- a/Makefile +++ b/Makefile @@ -1,8 +1,8 @@ TEMP_TEST_OUTPUT=/tmp/contract-test-service.log -TLS_FEATURE ?= rustls +FEATURES ?= rustls build-contract-tests: - cargo build -p contract-tests --release --no-default-features --features "$(TLS_FEATURE)" + cargo build -p contract-tests --release --no-default-features --features "$(FEATURES)" start-contract-test-service: build-contract-tests @./target/release/contract-tests diff --git a/launchdarkly-server-sdk/Cargo.toml b/launchdarkly-server-sdk/Cargo.toml index f399b43..742e566 100644 --- a/launchdarkly-server-sdk/Cargo.toml +++ b/launchdarkly-server-sdk/Cargo.toml @@ -4,14 +4,17 @@ description = "LaunchDarkly Server-Side SDK" version = "2.4.1" authors = ["LaunchDarkly"] edition = "2021" -rust-version = "1.74.0" # MSRV +rust-version = "1.74.0" # MSRV license = "Apache-2.0" homepage = "https://docs.launchdarkly.com/sdk/server-side/rust" repository = "https://github.com/launchdarkly/rust-server-sdk" -keywords = ["launchdarkly", "launchdarkly-sdk", "feature-flags", "feature-toggles"] -exclude = [ - "coverage.sh" +keywords = [ + "launchdarkly", + "launchdarkly-sdk", + "feature-flags", + "feature-toggles", ] +exclude = ["coverage.sh"] [package.metadata.docs.rs] features = ["event-compression"] @@ -34,16 +37,18 @@ tokio = { version = "1.17.0", features = ["rt-multi-thread"] } parking_lot = "0.12.0" tokio-stream = { version = "0.1.8", features = ["sync"] } moka = { version = "0.12.1", features = ["sync"] } -uuid = {version = "1.2.2", features = ["v4"] } +uuid = { version = "1.2.2", features = ["v4"] } hyper = { version = "0.14.19", features = ["client", "http1", "http2", "tcp"] } -hyper-rustls = { version = "0.24.1" , optional = true} +hyper-rustls = { version = "0.24.1", optional = true } rand = "0.8" flate2 = { version = "1.0.35", optional = true } [dev-dependencies] maplit = "1.0.1" env_logger = "0.10.0" -serde_json = { version = "1.0.73", features = ["preserve_order"] } # for deterministic JSON testing +serde_json = { version = "1.0.73", features = [ + "preserve_order", +] } # for deterministic JSON testing tokio = { version = "1.17.0", features = ["macros", "time"] } test-case = "3.2.1" mockito = "1.2.0" @@ -53,7 +58,12 @@ reqwest = { version = "0.12.4", features = ["json"] } [features] default = ["rustls"] -rustls = ["hyper-rustls/http1", "hyper-rustls/http2", "eventsource-client/rustls"] +rustls = [ + "hyper-rustls/http1", + "hyper-rustls/http2", + "eventsource-client/rustls", +] +webpki-roots = ["hyper-rustls/webpki-roots"] event-compression = ["flate2"] [[example]] diff --git a/launchdarkly-server-sdk/src/data_source_builders.rs b/launchdarkly-server-sdk/src/data_source_builders.rs index ec32390..d7512b9 100644 --- a/launchdarkly-server-sdk/src/data_source_builders.rs +++ b/launchdarkly-server-sdk/src/data_source_builders.rs @@ -1,9 +1,8 @@ use super::service_endpoints; use crate::data_source::{DataSource, NullDataSource, PollingDataSource, StreamingDataSource}; use crate::feature_requester_builders::{FeatureRequesterFactory, HyperFeatureRequesterBuilder}; +use crate::https_connector::create_https_connector; use hyper::{client::connect::Connection, service::Service, Uri}; -#[cfg(feature = "rustls")] -use hyper_rustls::HttpsConnectorBuilder; use std::sync::{Arc, Mutex}; use std::time::Duration; use thiserror::Error; @@ -102,12 +101,7 @@ where let data_source_result = match &self.connector { #[cfg(feature = "rustls")] None => { - let connector = HttpsConnectorBuilder::new() - .with_native_roots() - .https_or_http() - .enable_http1() - .enable_http2() - .build(); + let connector = create_https_connector(); Ok(StreamingDataSource::new( endpoints.streaming_base_url(), sdk_key, @@ -272,13 +266,7 @@ where match &self.connector { #[cfg(feature = "rustls")] None => { - let connector = HttpsConnectorBuilder::new() - .with_native_roots() - .https_or_http() - .enable_http1() - .enable_http2() - .build(); - + let connector = create_https_connector(); Ok(Box::new(HyperFeatureRequesterBuilder::new( endpoints.polling_base_url(), sdk_key, diff --git a/launchdarkly-server-sdk/src/events/processor_builders.rs b/launchdarkly-server-sdk/src/events/processor_builders.rs index 75b7e8e..38fce83 100644 --- a/launchdarkly-server-sdk/src/events/processor_builders.rs +++ b/launchdarkly-server-sdk/src/events/processor_builders.rs @@ -7,13 +7,12 @@ use std::time::Duration; use hyper::client::connect::Connection; use hyper::service::Service; use hyper::Uri; -#[cfg(feature = "rustls")] -use hyper_rustls::HttpsConnectorBuilder; use launchdarkly_server_sdk_evaluation::Reference; use thiserror::Error; use tokio::io::{AsyncRead, AsyncWrite}; use crate::events::sender::HyperEventSender; +use crate::https_connector::create_https_connector; use crate::{service_endpoints, LAUNCHDARKLY_TAGS_HEADER}; use super::processor::{ @@ -124,13 +123,7 @@ where } else { #[cfg(feature = "rustls")] { - let connector = HttpsConnectorBuilder::new() - .with_native_roots() - .https_or_http() - .enable_http1() - .enable_http2() - .build(); - + let connector = create_https_connector(); Ok(Arc::new(HyperEventSender::new( connector, hyper::Uri::from_str(url_string.as_str()).unwrap(), diff --git a/launchdarkly-server-sdk/src/https_connector.rs b/launchdarkly-server-sdk/src/https_connector.rs new file mode 100644 index 0000000..e4413a2 --- /dev/null +++ b/launchdarkly-server-sdk/src/https_connector.rs @@ -0,0 +1,34 @@ +use hyper::client::HttpConnector; +use hyper_rustls::builderstates::WantsSchemes; +use hyper_rustls::HttpsConnector; +use hyper_rustls::HttpsConnectorBuilder; + +// Creates an HTTPS connector for secure HTTP requests. +// +// This function configures and returns a connector that provides HTTPS capabilities to +// HTTP client implementations. +// +// # Features +// +// By default, this function uses the system's native certificate store for certificate +// verification. However, if the `webpki-roots` feature is enabled, it will use the +// WebPKI library instead. This is useful in environments where the system's certificate +// store is not available or not reliable. +// +pub fn create_https_connector() -> HttpsConnector { + builder() + .https_or_http() + .enable_http1() + .enable_http2() + .build() +} + +#[cfg(feature = "webpki-roots")] +fn builder() -> HttpsConnectorBuilder { + HttpsConnectorBuilder::new().with_webpki_roots() +} + +#[cfg(not(feature = "webpki-roots"))] +fn builder() -> HttpsConnectorBuilder { + HttpsConnectorBuilder::new().with_native_roots() +} diff --git a/launchdarkly-server-sdk/src/lib.rs b/launchdarkly-server-sdk/src/lib.rs index 06c791f..376896f 100644 --- a/launchdarkly-server-sdk/src/lib.rs +++ b/launchdarkly-server-sdk/src/lib.rs @@ -60,6 +60,7 @@ mod evaluation; mod events; mod feature_requester; mod feature_requester_builders; +mod https_connector; mod migrations; mod reqwest; mod sampler;