Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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: 7 additions & 2 deletions contract-tests/client-contract-tests/src/entity_manager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -48,15 +48,20 @@ std::optional<std::string> EntityManager::create(ConfigParams const& in) {
endpoints.EventsBaseUrl(*in.serviceEndpoints->events);
}
}
auto& datasource = config_builder.DataSource();

if (in.streaming) {
if (in.streaming->baseUri) {
endpoints.StreamingBaseUrl(*in.streaming->baseUri);
}
if (in.streaming->initialRetryDelayMs) {
auto streaming = DataSourceBuilder::Streaming();
streaming.InitialReconnectDelay(
std::chrono::milliseconds(*in.streaming->initialRetryDelayMs));
datasource.Method(std::move(streaming));
}
}

auto& datasource = config_builder.DataSource();

if (in.polling) {
if (in.polling->baseUri) {
endpoints.PollingBaseUrl(*in.polling->baseUri);
Expand Down
17 changes: 9 additions & 8 deletions contract-tests/server-contract-tests/src/entity_manager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,7 @@ using namespace launchdarkly::server_side;

EntityManager::EntityManager(boost::asio::any_io_executor executor,
launchdarkly::Logger& logger)
:
counter_{0},
executor_{std::move(executor)},
logger_{logger} {}


: counter_{0}, executor_{std::move(executor)}, logger_{logger} {}

std::optional<std::string> EntityManager::create(ConfigParams const& in) {
std::string id = std::to_string(counter_++);
Expand Down Expand Up @@ -43,14 +38,20 @@ std::optional<std::string> EntityManager::create(ConfigParams const& in) {
}
}

auto& datasource = config_builder.DataSource();

if (in.streaming) {
if (in.streaming->baseUri) {
endpoints.StreamingBaseUrl(*in.streaming->baseUri);
}
if (in.streaming->initialRetryDelayMs) {
auto streaming = DataSourceBuilder::Streaming();
streaming.InitialReconnectDelay(
std::chrono::milliseconds(*in.streaming->initialRetryDelayMs));
datasource.Method(std::move(streaming));
}
}

auto& datasource = config_builder.DataSource();

if (in.polling) {
if (in.polling->baseUri) {
endpoints.PollingBaseUrl(*in.polling->baseUri);
Expand Down
6 changes: 0 additions & 6 deletions contract-tests/server-contract-tests/test-suppressions.txt
Original file line number Diff line number Diff line change
@@ -1,9 +1,3 @@
streaming/retry behavior/retry after IO error on reconnect
streaming/retry behavior/retry after recoverable HTTP error on reconnect/error 400
streaming/retry behavior/retry after recoverable HTTP error on reconnect/error 408
streaming/retry behavior/retry after recoverable HTTP error on reconnect/error 429
streaming/retry behavior/retry after recoverable HTTP error on reconnect/error 500
streaming/retry behavior/retry after recoverable HTTP error on reconnect/error 503
streaming/validation/drop and reconnect if stream event has malformed JSON/put event
streaming/validation/drop and reconnect if stream event has malformed JSON/patch event
streaming/validation/drop and reconnect if stream event has malformed JSON/delete event
Expand Down
6 changes: 3 additions & 3 deletions libs/client-sdk/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ For using LaunchDarkly in server-side C/C++ applications, refer to our [Server-S

LaunchDarkly overview
-------------------------
[LaunchDarkly](https://www.launchdarkly.com) is a feature management platform that serves over 100 billion feature flags
[LaunchDarkly](https://www.launchdarkly.com) is a feature management platform that serves trillions of feature flags
daily to help teams build better software, faster. [Get started](https://docs.launchdarkly.com/docs/getting-started)
using LaunchDarkly today!

Expand All @@ -36,7 +36,7 @@ installing and using the SDK.
### Incorporating the SDK

The SDK can be used via a C++ or C interface and can be incorporated via a static library or shared object. The static
library and shared object each have their on use cases and limitations.
library and shared object each have their own use cases and limitations.

The static library supports both the C++ and C interface. When using the static library, you should ensure that it is
compiled using a compatible configuration and toolchain. For instance, when using MSVC, it needs to be using the same
Expand Down Expand Up @@ -89,7 +89,7 @@ behave correctly.
Contributing
------------

We encourage pull requests and other contributions from the community. Check out
We encourage pull requests and other contributions from the community. Read
our [contributing guidelines](../../CONTRIBUTING.md) for instructions on how to contribute to this SDK.

About LaunchDarkly
Expand Down
4 changes: 3 additions & 1 deletion libs/client-sdk/src/data_sources/streaming_data_source.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,6 @@ void StreamingDataSource::Start() {
return;
}

// TODO: Initial reconnect delay. sc-204393
boost::urls::url url = uri_components.value();

if (data_source_config_.with_reasons) {
Expand All @@ -117,6 +116,9 @@ void StreamingDataSource::Start() {

client_builder.connect_timeout(http_config_.ConnectTimeout());

client_builder.initial_reconnect_delay(
streaming_config.initial_reconnect_delay);

for (auto const& header : http_config_.BaseHeaders()) {
client_builder.header(header.first, header.second);
}
Expand Down
4 changes: 3 additions & 1 deletion libs/server-sdk/src/data_sources/streaming_data_source.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,6 @@ void StreamingDataSource::Start() {
return;
}

// TODO: Initial reconnect delay. sc-204393
boost::urls::url url = uri_components.value();

auto client_builder = launchdarkly::sse::Builder(exec_, url.buffer());
Expand All @@ -93,6 +92,9 @@ void StreamingDataSource::Start() {

client_builder.connect_timeout(http_config_.ConnectTimeout());

client_builder.initial_reconnect_delay(
streaming_config_.initial_reconnect_delay);

for (auto const& header : http_config_.BaseHeaders()) {
client_builder.header(header.first, header.second);
}
Expand Down
9 changes: 9 additions & 0 deletions libs/server-sent-events/include/launchdarkly/sse/client.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,14 @@ class Builder {
*/
Builder& write_timeout(std::chrono::milliseconds timeout);

/**
* Specifies the initial delay before reconnection when backoff takes place
* due to an error on the connection.
* @param timeout
* @return Reference to this builder.
*/
Builder& initial_reconnect_delay(std::chrono::milliseconds delay);

/**
* Specify the method for the initial request. The default method is GET.
* @param verb The HTTP method.
Expand Down Expand Up @@ -138,6 +146,7 @@ class Builder {
std::optional<std::chrono::milliseconds> read_timeout_;
std::optional<std::chrono::milliseconds> write_timeout_;
std::optional<std::chrono::milliseconds> connect_timeout_;
std::optional<std::chrono::milliseconds> initial_reconnect_delay_;
LogCallback logging_cb_;
EventReceiver receiver_;
ErrorCallback error_cb_;
Expand Down
22 changes: 19 additions & 3 deletions libs/server-sent-events/src/client.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,13 @@ auto const kDefaultUserAgent = BOOST_BEAST_VERSION_STRING;
// Time duration used when no timeout is specified (1 year).
auto const kNoTimeout = std::chrono::hours(8760);

// Time duration that the backoff algorithm uses before initiating a new
// connection, the first time a failure is detected.
auto const kDefaultInitialReconnectDelay = std::chrono::seconds(1);

// Maximum duration between backoff attempts.
auto const kDefaultMaxBackoffDelay = std::chrono::seconds(30);

static boost::optional<net::ssl::context&> ToOptRef(
std::optional<net::ssl::context>& maybe_val) {
if (maybe_val) {
Expand All @@ -60,6 +67,7 @@ class FoxyClient : public Client,
std::optional<std::chrono::milliseconds> connect_timeout,
std::optional<std::chrono::milliseconds> read_timeout,
std::optional<std::chrono::milliseconds> write_timeout,
std::optional<std::chrono::milliseconds> initial_reconnect_delay,
Builder::EventReceiver receiver,
Builder::LogCallback logger,
Builder::ErrorCallback errors,
Expand All @@ -75,7 +83,9 @@ class FoxyClient : public Client,
launchdarkly::foxy::session_opts{
ToOptRef(ssl_context_),
connect_timeout.value_or(kNoTimeout)}),
backoff_(std::chrono::seconds(1), std::chrono::seconds(30)),
backoff_(
initial_reconnect_delay.value_or(kDefaultInitialReconnectDelay),
kDefaultMaxBackoffDelay),
last_event_id_(std::nullopt),
backoff_timer_(session_.get_executor()),
event_receiver_(std::move(receiver)),
Expand Down Expand Up @@ -347,6 +357,7 @@ Builder::Builder(net::any_io_executor ctx, std::string url)
read_timeout_{std::nullopt},
write_timeout_{std::nullopt},
connect_timeout_{std::nullopt},
initial_reconnect_delay_{std::nullopt},
logging_cb_([](auto msg) {}),
receiver_([](launchdarkly::sse::Event const&) {}),
error_cb_([](auto err) {}) {
Expand Down Expand Up @@ -382,6 +393,11 @@ Builder& Builder::write_timeout(std::chrono::milliseconds timeout) {
return *this;
}

Builder& Builder::initial_reconnect_delay(std::chrono::milliseconds delay) {
initial_reconnect_delay_ = delay;
return *this;
}

Builder& Builder::method(http::verb verb) {
request_.method(verb);
return *this;
Expand Down Expand Up @@ -441,8 +457,8 @@ std::shared_ptr<Client> Builder::build() {

return std::make_shared<FoxyClient>(
net::make_strand(executor_), request, host, service, connect_timeout_,
read_timeout_, write_timeout_, receiver_, logging_cb_, error_cb_,
std::move(ssl));
read_timeout_, write_timeout_, initial_reconnect_delay_, receiver_,
logging_cb_, error_cb_, std::move(ssl));
}

} // namespace launchdarkly::sse