Skip to content

Commit cd9e504

Browse files
committed
fix: don't crash on platforms without en_US.utf8 locale when parsing date headers
1 parent e852966 commit cd9e504

File tree

5 files changed

+41
-6
lines changed

5 files changed

+41
-6
lines changed

libs/internal/include/launchdarkly/events/parse_date_header.hpp

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,17 @@
88
namespace launchdarkly::events {
99

1010
template <typename Clock>
11+
1112
static std::optional<typename Clock::time_point> ParseDateHeader(
12-
std::string const& datetime) {
13+
std::string const& datetime,
14+
std::locale const& locale) {
1315
// The following comments may not be entirely accurate.
1416
// TODO: There must be a better way.
1517

1618
std::tm gmt_tm = {};
19+
1720
std::istringstream string_stream(datetime);
18-
string_stream.imbue(std::locale("en_US.utf-8"));
21+
string_stream.imbue(locale);
1922
string_stream >> std::get_time(&gmt_tm, "%a, %d %b %Y %H:%M:%S GMT");
2023
if (string_stream.fail()) {
2124
return std::nullopt;

libs/internal/include/launchdarkly/events/request_worker.hpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,7 @@ class RequestWorker {
9999
RequestWorker(boost::asio::any_io_executor io,
100100
std::chrono::milliseconds retry_after,
101101
std::size_t id,
102+
std::optional<std::locale> date_header_locale,
102103
Logger& logger);
103104

104105
/**
@@ -163,6 +164,10 @@ class RequestWorker {
163164
/* Tag used in logs. */
164165
std::string tag_;
165166

167+
/* The en_US locale is used to parse the Date header from HTTP responses.
168+
* On some platforms, this may not be available hence the optional. */
169+
std::optional<std::locale> date_header_locale_;
170+
166171
Logger& logger_;
167172

168173
void OnDeliveryAttempt(network::HttpResult request, ResultCallback cb);

libs/internal/include/launchdarkly/events/worker_pool.hpp

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,6 @@ namespace launchdarkly::events {
2424
*/
2525
class WorkerPool {
2626
public:
27-
using ServerTimeCallback =
28-
std::function<void(std::chrono::system_clock::time_point)>;
2927
/**
3028
* Constructs a new WorkerPool.
3129
* @param io The executor used for all workers.

libs/internal/src/events/request_worker.cpp

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,15 @@ namespace launchdarkly::events {
66
RequestWorker::RequestWorker(boost::asio::any_io_executor io,
77
std::chrono::milliseconds retry_after,
88
std::size_t id,
9+
std::optional<std::locale> date_header_locale,
910
Logger& logger)
1011
: timer_(io),
1112
retry_delay_(retry_after),
1213
state_(State::Idle),
1314
requester_(timer_.get_executor()),
1415
batch_(std::nullopt),
1516
tag_("flush-worker[" + std::to_string(id) + "]: "),
17+
date_header_locale_(std::move(date_header_locale)),
1618
logger_(logger) {}
1719

1820
bool RequestWorker::Available() const {
@@ -81,11 +83,15 @@ void RequestWorker::OnDeliveryAttempt(network::HttpResult result,
8183
batch_.reset();
8284
break;
8385
case Action::ParseDateAndReset: {
86+
if (!date_header_locale_) {
87+
batch_.reset();
88+
break;
89+
}
8490
auto headers = result.Headers();
8591
if (auto date = headers.find("Date"); date != headers.end()) {
8692
if (auto server_time =
8793
ParseDateHeader<std::chrono::system_clock>(
88-
date->second)) {
94+
date->second, *date_header_locale_)) {
8995
callback(batch_->Count(), *server_time);
9096
}
9197
}

libs/internal/src/events/worker_pool.cpp

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,37 @@
55

66
namespace launchdarkly::events {
77

8+
std::optional<std::locale> GetLocale(std::string const& locale,
9+
std::string const& tag,
10+
Logger& logger) {
11+
try {
12+
return std::locale(locale);
13+
} catch (std::runtime_error const& err) {
14+
LD_LOG(logger, LogLevel::kWarn)
15+
<< tag << " couldn't load " << locale
16+
<< " locale. If debug events are enabled, they may be emitted for "
17+
"longer than expected";
18+
return std::nullopt;
19+
}
20+
}
21+
822
WorkerPool::WorkerPool(boost::asio::any_io_executor io,
923
std::size_t pool_size,
1024
std::chrono::milliseconds delivery_retry_delay,
1125
Logger& logger)
1226
: io_(io), workers_() {
27+
// The en_US.utf-8 locale is used whenever a date is parsed from the HTTP
28+
// headers returned by the event-delivery endpoints. If the locale is
29+
// unavailable, then the workers will skip the parsing step.
30+
//
31+
// This may result in debug events being emitted for longer than expected
32+
// if the host's time is way out of sync.
33+
std::optional<std::locale> date_header_locale =
34+
GetLocale("en_US.utf-8", "event-processor", logger);
35+
1336
for (std::size_t i = 0; i < pool_size; i++) {
1437
workers_.emplace_back(std::make_unique<RequestWorker>(
15-
io_, delivery_retry_delay, i, logger));
38+
io_, delivery_retry_delay, i, date_header_locale, logger));
1639
}
1740
}
1841

0 commit comments

Comments
 (0)