44#include < launchdarkly/network/http_error_messages.hpp>
55#include < launchdarkly/server_side/config/builders/all_builders.hpp>
66
7+ #include < boost/algorithm/string/predicate.hpp>
78#include < boost/json.hpp>
89#include < boost/url/parse.hpp>
910#include < boost/url/url.hpp>
1011
1112namespace launchdarkly ::server_side::data_systems {
1213
1314static char const * const kFDv2PollPath = " /sdk/poll" ;
15+ static char const * const kFDv1FallbackHeader = " X-LD-FD-Fallback" ;
1416
1517static char const * const kErrorParsingBody =
1618 " Could not parse FDv2 polling response" ;
@@ -32,6 +34,12 @@ static ErrorInfo MakeError(ErrorKind kind,
3234 std::chrono::system_clock::now ()};
3335}
3436
37+ static bool ReadFDv1FallbackDirective (
38+ network::HttpResult::HeadersType const & headers) {
39+ auto const it = headers.find (kFDv1FallbackHeader );
40+ return it != headers.end () && boost::iequals (it->second , " true" );
41+ }
42+
3543network::HttpRequest MakeFDv2PollRequest (
3644 config::built::ServiceEndpoints const & endpoints,
3745 config::built::HttpProperties const & http_properties,
@@ -88,15 +96,13 @@ static FDv2SourceResult ParseFDv2PollEvents(
8896 auto typed = TranslateChangeSet (*change_set, logger);
8997 if (!typed) {
9098 return FDv2SourceResult{FDv2SourceResult::Interrupted{
91- MakeError (ErrorKind::kInvalidData , 0 , kErrorTranslation ),
92- false }};
99+ MakeError (ErrorKind::kInvalidData , 0 , kErrorTranslation )}};
93100 }
94101 return FDv2SourceResult{
95- FDv2SourceResult::ChangeSet{std::move (*typed), false }};
102+ FDv2SourceResult::ChangeSet{std::move (*typed)}};
96103 }
97104 if (auto * goodbye = std::get_if<Goodbye>(&result)) {
98- return FDv2SourceResult{
99- FDv2SourceResult::Goodbye{goodbye->reason , false }};
105+ return FDv2SourceResult{FDv2SourceResult::Goodbye{goodbye->reason }};
100106 }
101107 if (auto * error = std::get_if<FDv2ProtocolHandler::Error>(&result)) {
102108 if (error->kind == FDv2ProtocolHandler::Error::Kind::kServerError ) {
@@ -107,16 +113,15 @@ static FDv2SourceResult ParseFDv2PollEvents(
107113 id.value_or (" " ) + " ' with reason: '" + error->message +
108114 " '. Automatic retry will occur." ;
109115 return FDv2SourceResult{FDv2SourceResult::Interrupted{
110- MakeError (ErrorKind::kErrorResponse , 0 , std::move (msg)),
111- false }};
116+ MakeError (ErrorKind::kErrorResponse , 0 , std::move (msg))}};
112117 }
113118 return FDv2SourceResult{FDv2SourceResult::Interrupted{
114- MakeError (ErrorKind::kInvalidData , 0 , error->message ), false }};
119+ MakeError (ErrorKind::kInvalidData , 0 , error->message )}};
115120 }
116121 }
117122
118123 return FDv2SourceResult{FDv2SourceResult::Interrupted{
119- MakeError (ErrorKind::kInvalidData , 0 , kErrorIncompletePayload ), false }};
124+ MakeError (ErrorKind::kInvalidData , 0 , kErrorIncompletePayload )}};
120125}
121126
122127static FDv2SourceResult ParseFDv2PollResponse (
@@ -127,25 +132,25 @@ static FDv2SourceResult ParseFDv2PollResponse(
127132 auto parsed = boost::json::parse (body, ec);
128133 if (ec) {
129134 return FDv2SourceResult{FDv2SourceResult::Interrupted{
130- MakeError (ErrorKind::kInvalidData , 0 , kErrorParsingBody ), false }};
135+ MakeError (ErrorKind::kInvalidData , 0 , kErrorParsingBody )}};
131136 }
132137
133138 auto const * obj = parsed.if_object ();
134139 if (!obj) {
135140 return FDv2SourceResult{FDv2SourceResult::Interrupted{
136- MakeError (ErrorKind::kInvalidData , 0 , kErrorParsingBody ), false }};
141+ MakeError (ErrorKind::kInvalidData , 0 , kErrorParsingBody )}};
137142 }
138143
139144 auto const * events_val = obj->if_contains (" events" );
140145 if (!events_val) {
141146 return FDv2SourceResult{FDv2SourceResult::Interrupted{
142- MakeError (ErrorKind::kInvalidData , 0 , kErrorMissingEvents ), false }};
147+ MakeError (ErrorKind::kInvalidData , 0 , kErrorMissingEvents )}};
143148 }
144149
145150 auto const * events_arr = events_val->if_array ();
146151 if (!events_arr) {
147152 return FDv2SourceResult{FDv2SourceResult::Interrupted{
148- MakeError (ErrorKind::kInvalidData , 0 , kErrorMissingEvents ), false }};
153+ MakeError (ErrorKind::kInvalidData , 0 , kErrorMissingEvents )}};
149154 }
150155
151156 return ParseFDv2PollEvents (*events_arr, protocol_handler, logger);
@@ -161,24 +166,28 @@ data_interfaces::FDv2SourceResult HandleFDv2PollResponse(
161166 std::string error_msg = msg.has_value () ? *msg : " unknown error" ;
162167 LD_LOG (logger, LogLevel::kWarn ) << identity << " : " << error_msg;
163168 return FDv2SourceResult{FDv2SourceResult::Interrupted{
164- MakeError (ErrorKind::kNetworkError , 0 , std::move (error_msg)),
165- false }};
169+ MakeError (ErrorKind::kNetworkError , 0 , std::move (error_msg))}};
166170 }
167171
172+ bool const fdv1_fallback = ReadFDv1FallbackDirective (res.Headers ());
173+
168174 if (res.Status () == 304 ) {
169- return FDv2SourceResult{FDv2SourceResult::ChangeSet{
170- data_model::ChangeSet<data_interfaces::ChangeSetData>{
171- data_model::ChangeSetType::kNone , {}, data_model::Selector{}},
172- false }};
175+ return FDv2SourceResult{
176+ FDv2SourceResult::ChangeSet{
177+ data_model::ChangeSet<data_interfaces::ChangeSetData>{
178+ data_model::ChangeSetType::kNone ,
179+ {},
180+ data_model::Selector{}}},
181+ fdv1_fallback};
173182 }
174183
175184 if (res.Status () == 200 ) {
176185 auto const & body = res.Body ();
177186 if (!body) {
178- return FDv2SourceResult{FDv2SourceResult::Interrupted{
179- MakeError ( ErrorKind::kInvalidData , 0 ,
180- " polling response contained no body" ),
181- false } };
187+ return FDv2SourceResult{FDv2SourceResult::Interrupted{MakeError (
188+ ErrorKind::kInvalidData , 0 ,
189+ " polling response contained no body" )} ,
190+ fdv1_fallback };
182191 }
183192
184193 auto result = ParseFDv2PollResponse (*body, protocol_handler, logger);
@@ -192,24 +201,27 @@ data_interfaces::FDv2SourceResult HandleFDv2PollResponse(
192201 << identity << " : " << interrupted->error .Message ();
193202 }
194203 }
204+ result.fdv1_fallback = fdv1_fallback;
195205 return result;
196206 }
197207
198208 if (network::IsRecoverableStatus (res.Status ())) {
199209 std::string msg = network::ErrorForStatusCode (
200210 res.Status (), " FDv2 polling request" , " will retry" );
201211 LD_LOG (logger, LogLevel::kWarn ) << identity << " : " << msg;
202- return FDv2SourceResult{FDv2SourceResult::Interrupted{
203- MakeError (ErrorKind::kErrorResponse , res.Status (), std::move (msg)),
204- false }};
212+ return FDv2SourceResult{
213+ FDv2SourceResult::Interrupted{MakeError (
214+ ErrorKind::kErrorResponse , res.Status (), std::move (msg))},
215+ fdv1_fallback};
205216 }
206217
207218 std::string msg = network::ErrorForStatusCode (
208219 res.Status (), " FDv2 polling request" , std::nullopt );
209220 LD_LOG (logger, LogLevel::kError ) << identity << " : " << msg;
210- return FDv2SourceResult{FDv2SourceResult::TerminalError{
211- MakeError (ErrorKind::kErrorResponse , res.Status (), std::move (msg)),
212- false }};
221+ return FDv2SourceResult{
222+ FDv2SourceResult::TerminalError{
223+ MakeError (ErrorKind::kErrorResponse , res.Status (), std::move (msg))},
224+ fdv1_fallback};
213225}
214226
215227} // namespace launchdarkly::server_side::data_systems
0 commit comments