Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
a52b71d
feat!: Update StreamingDataSourceBuilder to require HttpTransport (#131)
keelerm84 Jan 16, 2026
a0528ba
feat!: Upgrade to hyper 1.0 (#135)
keelerm84 Jan 20, 2026
1a7b623
feat: Add HttpTransport trait abstraction (#136)
keelerm84 Jan 20, 2026
e984692
feat!: Migrate events to HttpTransport (#137)
keelerm84 Jan 20, 2026
92630fb
refactor: Migrate feature requester to HttpTransport (#138)
keelerm84 Jan 20, 2026
af7a9f6
chore: Update contract tests and add custom transport example (#139)
keelerm84 Jan 21, 2026
9461063
feat!: Remove deprecated initialized_async function (#140)
keelerm84 Jan 21, 2026
f26f27c
Merge branch 'main' into feat/3.x
keelerm84 Jan 22, 2026
85ad7b4
feat!: Allow filtering all_flags_state to mobile only (#142)
keelerm84 Jan 28, 2026
e00b82d
feat: Support synchronous event sending with new `flush_blocking` me…
keelerm84 Jan 29, 2026
e37dafd
Merge branch 'main' into feat/3.x
keelerm84 Feb 13, 2026
f5af46a
feat: Replace local http transport with launchdarkly-sdk-transport (#…
keelerm84 Feb 19, 2026
921a92d
feat: Add native-tls feature (#151)
keelerm84 Feb 23, 2026
15ec0ab
feat: Choose crypto library with crypto-(aws-lc-rs|openssl) features …
keelerm84 Feb 23, 2026
3813e05
chore: Support http-proxy contract-test capability (#153)
keelerm84 Feb 23, 2026
ddeb1e0
feat!: Enable event compression by default (#154)
keelerm84 Feb 23, 2026
c70c2a0
chore: Use published version of eventsource client
keelerm84 Feb 25, 2026
cb6d607
Merge branch 'main' into feat/3.x
keelerm84 Feb 26, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 0 additions & 9 deletions .github/actions/build-docs/action.yml

This file was deleted.

16 changes: 13 additions & 3 deletions .github/actions/ci/action.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
name: CI Workflow
description: 'Shared CI workflow.'
description: "Shared CI workflow."

inputs:
cargo-flags:
description: "Flags to pass to cargo commands."
required: false
default: ""
cargo-test-flags:
description: "Flags to pass to cargo test commands."
required: false
default: ""

runs:
using: composite
Expand All @@ -10,8 +20,8 @@ runs:

- name: Run tests
shell: bash
run: cargo test -p launchdarkly-server-sdk
run: cargo test ${{ inputs.cargo-flags }} ${{ inputs.cargo-test-flags }} -p launchdarkly-server-sdk

- name: Run clippy checks
shell: bash
run: cargo clippy -p launchdarkly-server-sdk -- -D warnings
run: cargo clippy ${{ inputs.cargo-flags }} -p launchdarkly-server-sdk -- -D warnings
12 changes: 6 additions & 6 deletions .github/actions/contract-tests/action.yml
Original file line number Diff line number Diff line change
@@ -1,23 +1,23 @@
name: Contract test runner
description: 'Reusable contract runner action'
description: "Reusable contract runner action"
inputs:
tls_feature:
description: 'Which TLS feature do you want to enable?'
cargo-flags:
description: "Flags to pass to cargo commands."
required: true
token:
description: 'GH Token used for retrieving SDK test harness.'
description: "GH Token used for retrieving SDK test harness."
required: true

runs:
using: composite
steps:
- name: Build contract tests
shell: bash
run: TLS_FEATURE="${{ inputs.tls_feature }}" make build-contract-tests
run: CARGO_FLAGS="${{ inputs.cargo-flags }}" make build-contract-tests

- name: Start contract test service
shell: bash
run: make start-contract-test-service-bg
run: CARGO_FLAGS="${{ inputs.cargo-flags }}" make start-contract-test-service-bg

- uses: launchdarkly/gh-actions/actions/contract-tests@contract-tests-v1.0.2
with:
Expand Down
57 changes: 49 additions & 8 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,34 @@ on:
jobs:
ci-build:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
features:
- name: "default (aws-lc-rs for crypto)"

- name: "no-features"
cargo-flags: "--no-default-features"
cargo-test-flags: "--lib"
skip_contract_tests: "true"

- name: "hyper"
cargo-flags: "--no-default-features --features hyper"
cargo-test-flags: "--lib"

- name: "hyper-rustls-native-roots"
cargo-flags: "--no-default-features --features hyper-rustls-native-roots"

- name: "hyper-rustls-webpki-roots"
cargo-flags: "--no-default-features --features hyper-rustls-webpki-roots"

- name: "native-tls"
cargo-flags: "--no-default-features --features native-tls"

- name: "openssl crypto"
cargo-flags: "--no-default-features --features hyper-rustls-native-roots,crypto-openssl"

name: CI (${{ matrix.features.name }})

steps:
- uses: actions/checkout@v4
Expand All @@ -26,20 +54,33 @@ jobs:
rustup component add rustfmt clippy

- uses: ./.github/actions/ci
with:
cargo-flags: ${{ matrix.features.cargo-flags }}
cargo-test-flags: ${{ matrix.features.cargo-test-flags }}

- name: "Run contract tests with hyper_rustls"
uses: ./.github/actions/contract-tests
- uses: ./.github/actions/contract-tests
if: ${{ matrix.features.skip_contract_tests != 'true' }}
with:
tls_feature: "rustls"
cargo-flags: ${{ matrix.features.cargo-flags }}
token: ${{ secrets.GITHUB_TOKEN }}

- name: "Run contract tests with hyper_tls"
uses: ./.github/actions/contract-tests
build-docs:
runs-on: ubuntu-latest
name: Build Documentation (all features)

steps:
- uses: actions/checkout@v4
with:
tls_feature: "tls"
token: ${{ secrets.GITHUB_TOKEN }}
fetch-depth: 0

- name: Setup rust tooling
run: rustup override set nightly

- name: Install cargo-docs-rs
run: cargo install cargo-docs-rs

- uses: ./.github/actions/build-docs
- name: Build documentation
run: cargo docs-rs -p launchdarkly-server-sdk

musl-build:
runs-on: ubuntu-latest
Expand Down
1 change: 0 additions & 1 deletion .github/workflows/manual-publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ jobs:
rustup component add rustfmt clippy
- uses: ./.github/actions/ci
- uses: ./.github/actions/build-docs

- uses: launchdarkly/gh-actions/actions/release-secrets@release-secrets-v1.2.0
name: "Get crates.io token"
Expand Down
6 changes: 3 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
TEMP_TEST_OUTPUT=/tmp/contract-test-service.log
TLS_FEATURE ?= rustls
CARGO_FLAGS ?= hyper-rustls-native-roots

build-contract-tests:
cargo build -p contract-tests --release --no-default-features --features "$(TLS_FEATURE)"
cargo build -p contract-tests --release $(CARGO_FLAGS)

start-contract-test-service: build-contract-tests
@./target/release/contract-tests

start-contract-test-service-bg:
@echo "Test service output will be captured in $(TEMP_TEST_OUTPUT)"
@make start-contract-test-service >$(TEMP_TEST_OUTPUT) 2>&1 &
@$(MAKE) start-contract-test-service >$(TEMP_TEST_OUTPUT) 2>&1 &

run-contract-tests:
@curl -s https://raw.githubusercontent.com/launchdarkly/sdk-test-harness/main/downloader/run.sh \
Expand Down
38 changes: 32 additions & 6 deletions contract-tests/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,18 +9,44 @@ license = "Apache-2.0"
actix = "0.13.0"
actix-web = "4.2.1"
env_logger = "0.10.0"
eventsource-client = { version = "0.17.0" }
launchdarkly-sdk-transport = { version = "0.1.0" }
log = "0.4.14"
launchdarkly-server-sdk = { path = "../launchdarkly-server-sdk/", default-features = false, features = ["event-compression"]}
serde = { version = "1.0.132", features = ["derive"] }
serde_json = "1.0.73"
futures = "0.3.12"
hyper = { version = "0.14.19", features = ["client"] }
hyper-rustls = { version = "0.24.1" , optional = true, features = ["http2"]}
hyper-tls = { version = "0.5.0", optional = true }
hyper-util = { version = "0.1", features = ["client-legacy", "http1", "http2", "tokio"] }
hyper-rustls = { version = "0.27", default-features = false, features = ["http1", "http2", "ring"], optional = true }
hyper-tls = { version = "0.6.0", optional = true }
reqwest = { version = "0.12.4", features = ["default", "blocking", "json"] }
async-mutex = "1.4.0"

[features]
default = ["rustls"]
rustls = ["hyper-rustls/http1", "hyper-rustls/http2", "launchdarkly-server-sdk/rustls"]
tls = ["hyper-tls"]
default = ["hyper", "crypto-aws-lc-rs"]

crypto-aws-lc-rs = ["launchdarkly-server-sdk/crypto-aws-lc-rs"]
crypto-openssl = ["launchdarkly-server-sdk/crypto-openssl"]

hyper = [
"launchdarkly-sdk-transport/hyper",
"eventsource-client/hyper"
]
hyper-rustls-native-roots = [
"hyper",
"dep:hyper-rustls",
"launchdarkly-sdk-transport/hyper-rustls-native-roots",
"eventsource-client/hyper-rustls-native-roots"
]
hyper-rustls-webpki-roots = [
"hyper",
"dep:hyper-rustls",
"launchdarkly-sdk-transport/hyper-rustls-webpki-roots",
"eventsource-client/hyper-rustls-webpki-roots"
]
native-tls = [
"hyper",
"dep:hyper-tls",
"launchdarkly-sdk-transport/native-tls",
"eventsource-client/native-tls"
]
45 changes: 33 additions & 12 deletions contract-tests/src/client_entity.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,15 @@ const DEFAULT_EVENTS_BASE_URL: &str = "https://events.launchdarkly.com";

use launchdarkly_server_sdk::{
ApplicationInfo, BuildError, Client, ConfigBuilder, Detail, EventProcessorBuilder,
FlagDetailConfig, FlagValue, NullEventProcessorBuilder, PollingDataSourceBuilder,
FlagDetailConfig, FlagFilter, FlagValue, NullEventProcessorBuilder, PollingDataSourceBuilder,
ServiceEndpointsBuilder, StreamingDataSourceBuilder,
};

#[cfg(any(feature = "crypto-aws-lc-rs", feature = "crypto-openssl"))]
use crate::command_params::SecureModeHashResponse;
use crate::command_params::{
ContextBuildParams, ContextConvertParams, ContextParam, ContextResponse,
MigrationOperationResponse, MigrationVariationResponse, SecureModeHashResponse,
MigrationOperationResponse, MigrationVariationResponse,
};
use crate::HttpsConnector;
use crate::{
Expand All @@ -37,6 +39,21 @@ impl ClientEntity {
create_instance_params: CreateInstanceParams,
connector: HttpsConnector,
) -> Result<Self, BuildError> {
let proxy = create_instance_params
.configuration
.proxy
.unwrap_or_default()
.http_proxy
.unwrap_or_default();
let mut transport_builder = launchdarkly_sdk_transport::HyperTransport::builder();
if !proxy.is_empty() {
transport_builder = transport_builder.proxy_url(proxy.clone());
}

// Create fresh transports for this client to avoid shared connection pool issues
let transport = transport_builder
.build_with_connector(connector.clone())
.map_err(|e| BuildError::InvalidConfig(e.to_string()))?;
let mut config_builder =
ConfigBuilder::new(&create_instance_params.configuration.credential);

Expand Down Expand Up @@ -79,7 +96,7 @@ impl ClientEntity {
if let Some(delay) = streaming.initial_retry_delay_ms {
streaming_builder.initial_reconnect_delay(Duration::from_millis(delay));
}
streaming_builder.https_connector(connector.clone());
streaming_builder.transport(transport.clone());

config_builder = config_builder.data_source(&streaming_builder);
} else if let Some(polling) = create_instance_params.configuration.polling {
Expand All @@ -91,15 +108,15 @@ impl ClientEntity {
if let Some(delay) = polling.poll_interval_ms {
polling_builder.poll_interval(Duration::from_millis(delay));
}
polling_builder.https_connector(connector.clone());
polling_builder.transport(transport.clone());

config_builder = config_builder.data_source(&polling_builder);
} else {
// If we didn't specify streaming or polling, we fall back to basic streaming. The only
// customization we provide is the https connector to support testing multiple
// connectors.
// customization we provide is the transport to support testing multiple
// transport implementations.
let mut streaming_builder = StreamingDataSourceBuilder::new();
streaming_builder.https_connector(connector.clone());
streaming_builder.transport(transport.clone());
config_builder = config_builder.data_source(&streaming_builder);
}

Expand All @@ -113,6 +130,7 @@ impl ClientEntity {
processor_builder.capacity(capacity);
}
processor_builder.all_attributes_private(events.all_attributes_private);
processor_builder.compress_events(false);
if let Some(e) = events.enable_gzip {
processor_builder.compress_events(e);
}
Expand All @@ -124,7 +142,7 @@ impl ClientEntity {
if let Some(attributes) = events.global_private_attributes {
processor_builder.private_attributes(attributes);
}
processor_builder.https_connector(connector.clone());
processor_builder.transport(transport);
processor_builder.omit_anonymous_contexts(events.omit_anonymous_contexts);

config_builder.event_processor(&processor_builder)
Expand Down Expand Up @@ -214,14 +232,17 @@ impl ClientEntity {
ContextResponse::from(Self::context_convert(params)),
)))
}
#[cfg(any(feature = "crypto-aws-lc-rs", feature = "crypto-openssl"))]
"secureModeHash" => {
let params = command
.secure_mode_hash
.ok_or("secureModeHash params should be set")?;
let hash = self
.client
.secure_mode_hash(&params.context)
.map_err(|e| e.to_string())?;
Ok(Some(CommandResponse::SecureModeHash(
SecureModeHashResponse {
result: self.client.secure_mode_hash(&params.context),
},
SecureModeHashResponse { result: hash },
)))
}
"migrationVariation" => {
Expand Down Expand Up @@ -551,7 +572,7 @@ impl ClientEntity {
}

if params.client_side_only {
config.client_side_only();
config.flag_filter(FlagFilter::CLIENT);
}

if params.details_only_for_tracked_flags {
Expand Down
4 changes: 4 additions & 0 deletions contract-tests/src/command_params.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ pub enum CommandResponse {
EvaluateFlag(EvaluateFlagResponse),
EvaluateAll(EvaluateAllFlagsResponse),
ContextBuildOrConvert(ContextResponse),
#[cfg(any(feature = "crypto-aws-lc-rs", feature = "crypto-openssl"))]
SecureModeHash(SecureModeHashResponse),
MigrationVariation(MigrationVariationResponse),
MigrationOperation(MigrationOperationResponse),
Expand All @@ -25,6 +26,7 @@ pub struct CommandParams {
pub identify_event: Option<IdentifyEventParams>,
pub context_build: Option<ContextBuildParams>,
pub context_convert: Option<ContextConvertParams>,
#[cfg(any(feature = "crypto-aws-lc-rs", feature = "crypto-openssl"))]
pub secure_mode_hash: Option<SecureModeHashParams>,
pub migration_variation: Option<MigrationVariationParams>,
pub migration_operation: Option<MigrationOperationParams>,
Expand Down Expand Up @@ -126,12 +128,14 @@ pub struct ContextConvertParams {
pub input: String,
}

#[cfg(any(feature = "crypto-aws-lc-rs", feature = "crypto-openssl"))]
#[derive(Deserialize, Debug)]
#[serde(rename_all = "camelCase")]
pub struct SecureModeHashParams {
pub context: Context,
}

#[cfg(any(feature = "crypto-aws-lc-rs", feature = "crypto-openssl"))]
#[derive(Serialize, Debug, Clone)]
#[serde(rename_all = "camelCase")]
pub struct SecureModeHashResponse {
Expand Down
Loading
Loading