2525#include < sstream>
2626#include < thread>
2727
28+ #include " olp/core/client/Condition.h"
29+ #include " olp/core/client/ErrorCode.h"
2830#include " olp/core/http/NetworkConstants.h"
31+ #include " olp/core/logging/Log.h"
2932#include " olp/core/utils/Url.h"
3033
3134namespace olp {
3235namespace client {
36+ constexpr auto kLogTag = " OlpClient" ;
37+
38+ static const auto kCancelledErrorResponse =
39+ http::NetworkResponse ()
40+ .WithStatus(static_cast <int >(http::ErrorCode::CANCELLED_ERROR))
41+ .WithError(" Operation Cancelled." );
42+
43+ static const auto kTimeoutErrorResponse =
44+ http::NetworkResponse ()
45+ .WithStatus(static_cast <int >(http::ErrorCode::TIMEOUT_ERROR))
46+ .WithError(" Network request timed out." );
47+
48+ HttpResponse ToHttpResponse (const http::NetworkResponse& response) {
49+ return {response.GetStatus (), response.GetError ()};
50+ }
3351
3452namespace {
3553bool CaseInsensitiveCompare (const std::string& str1, const std::string& str2) {
@@ -125,6 +143,61 @@ http::NetworkRequest::HttpVerb GetHttpVerb(const std::string& verb) {
125143 return http_verb;
126144}
127145
146+ HttpResponse SendRequest (const http::NetworkRequest& request,
147+ const olp::client::OlpClientSettings& settings,
148+ const olp::client::RetrySettings& retry_settings,
149+ client::CancellationContext context) {
150+ http::NetworkResponse network_response = kCancelledErrorResponse ;
151+ auto interest_flag = std::make_shared<std::atomic_bool>(true );
152+ Condition condition{};
153+ auto response_body = std::make_shared<std::stringstream>();
154+ http::SendOutcome outcome{http::ErrorCode::CANCELLED_ERROR};
155+
156+ context.ExecuteOrCancelled (
157+ [&]() {
158+ outcome = settings.network_request_handler ->Send (
159+ request, response_body,
160+ [&, interest_flag](http::NetworkResponse response) {
161+ if (interest_flag->exchange (false )) {
162+ network_response = std::move (response);
163+ condition.Notify ();
164+ }
165+ });
166+
167+ return CancellationToken ([&, interest_flag]() {
168+ if (interest_flag->exchange (false )) {
169+ settings.network_request_handler ->Cancel (outcome.GetRequestId ());
170+ network_response = kCancelledErrorResponse ;
171+ network_response.WithRequestId (outcome.GetRequestId ());
172+ condition.Notify ();
173+ }
174+ });
175+ },
176+ [&condition]() { condition.Notify (); });
177+
178+ if (!outcome.IsSuccessful ()) {
179+ return {static_cast <int >(outcome.GetErrorCode ()),
180+ ErrorCodeToString (outcome.GetErrorCode ())};
181+ }
182+
183+ if (!condition.Wait (std::chrono::seconds (retry_settings.timeout ))) {
184+ OLP_SDK_LOG_INFO_F (kLogTag , " Timeout" );
185+ context.CancelOperation ();
186+ return ToHttpResponse (kTimeoutErrorResponse );
187+ }
188+
189+ // If request was cancelled before execution, we reach here without
190+ // calling callback or cancellation token, meaning interest_flag is still
191+ // true.
192+ interest_flag->store (false );
193+ if (context.IsCancelled ()) {
194+ return ToHttpResponse (kCancelledErrorResponse );
195+ }
196+
197+ return {network_response.GetStatus (), std::move (*response_body)};
198+ }
199+
200+ bool StatusSuccess (int status) { return status >= 0 && status < 400 ; }
128201} // anonymous namespace
129202
130203OlpClient::OlpClient () {}
@@ -155,7 +228,8 @@ std::shared_ptr<http::NetworkRequest> OlpClient::CreateRequest(
155228 http::NetworkRequest::HttpVerb http_verb = GetHttpVerb (method);
156229 network_request->WithVerb (http_verb);
157230
158- if (settings_.authentication_settings ) {
231+ if (settings_.authentication_settings &&
232+ settings_.authentication_settings .get ().provider ) {
159233 std::string bearer = http::kBearer + std::string (" " ) +
160234 settings_.authentication_settings .get ().provider ();
161235 network_request->WithHeader (http::kAuthorizationHeader , bearer);
@@ -198,7 +272,7 @@ NetworkAsyncCallback GetRetryCallback(
198272 const std::weak_ptr<CancellationContext>& weak_cancel_context) {
199273 ++current_try;
200274 return [=](HttpResponse response) {
201- if (current_try >= settings.max_attempts ||
275+ if (current_try > settings.max_attempts ||
202276 !settings.retry_condition (response)) {
203277 callback (std::move (response));
204278 } else {
@@ -224,8 +298,11 @@ NetworkAsyncCallback GetRetryCallback(
224298 " Operation Cancelled." ));
225299 });
226300 } else {
227- callback (HttpResponse (static_cast <int >(http::ErrorCode::UNKNOWN_ERROR),
228- " Unknown error." ));
301+ // Last (and only) strong reference lives in cancellation
302+ // token, which is reset on CancelOperation.
303+ callback (
304+ HttpResponse (static_cast <int >(http::ErrorCode::CANCELLED_ERROR),
305+ " Operation Cancelled." ));
229306 }
230307 }
231308 };
@@ -286,5 +363,91 @@ CancellationToken OlpClient::CallApi(
286363 [cancel_context]() { cancel_context->CancelOperation (); });
287364}
288365
366+ HttpResponse OlpClient::CallApi (
367+ std::string path, std::string method,
368+ std::multimap<std::string, std::string> query_params,
369+ std::multimap<std::string, std::string> header_params,
370+ std::multimap<std::string, std::string> forms_params,
371+ std::shared_ptr<std::vector<unsigned char >> post_body,
372+ std::string content_type, CancellationContext context) const {
373+ http::NetworkRequest network_request (
374+ olp::utils::Url::Construct (base_url_, path, query_params));
375+
376+ network_request.WithVerb (GetHttpVerb (method));
377+
378+ if (settings_.authentication_settings &&
379+ settings_.authentication_settings .get ().provider ) {
380+ std::string bearer = http::kBearer + std::string (" " ) +
381+ settings_.authentication_settings .get ().provider ();
382+ network_request.WithHeader (http::kAuthorizationHeader , bearer);
383+ }
384+
385+ for (const auto & header : default_headers_) {
386+ network_request.WithHeader (header.first , header.second );
387+ }
388+
389+ std::string custom_user_agent;
390+ for (const auto & header : header_params) {
391+ // Merge all User-Agent headers into one header.
392+ // This is required for (at least) iOS network implementation,
393+ // which uses headers dictionary without any duplicates.
394+ // User agents entries are usually separated by a whitespace, e.g.
395+ // Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:47.0) Firefox/47.0
396+ if (CaseInsensitiveCompare (header.first , http::kUserAgentHeader )) {
397+ custom_user_agent += header.second + std::string (" " );
398+ } else {
399+ network_request.WithHeader (header.first , header.second );
400+ }
401+ }
402+
403+ custom_user_agent += http::kOlpSdkUserAgent ;
404+ network_request.WithHeader (http::kUserAgentHeader , custom_user_agent);
405+
406+ if (!content_type.empty ()) {
407+ network_request.WithHeader (http::kContentTypeHeader , content_type);
408+ }
409+
410+ network_request.WithBody (post_body);
411+
412+ const auto & retry_settings = settings_.retry_settings ;
413+ auto backdown_period = retry_settings.initial_backdown_period ;
414+
415+ olp::http::NetworkSettings network_settings;
416+
417+ network_settings.WithTransferTimeout (retry_settings.timeout )
418+ .WithConnectionTimeout (retry_settings.timeout );
419+
420+ network_settings.WithProxySettings (
421+ settings_.proxy_settings .value_or (olp::http::NetworkProxySettings ()));
422+
423+ network_request.WithSettings (std::move (network_settings));
424+
425+ auto response =
426+ SendRequest (network_request, settings_, retry_settings, context);
427+
428+ for (int i = 0 ; i < retry_settings.max_attempts && !context.IsCancelled ();
429+ i++) {
430+ if (StatusSuccess (response.status )) {
431+ return response;
432+ }
433+
434+ if (!retry_settings.retry_condition (response)) {
435+ return response;
436+ }
437+
438+ // do the periodical sleep and check for cancellation status in between.
439+ auto duration_to_sleep = backdown_period;
440+ while (duration_to_sleep > 0 && !context.IsCancelled ()) {
441+ auto sleep_ms = std::min (1000 , duration_to_sleep);
442+ std::this_thread::sleep_for (std::chrono::milliseconds (sleep_ms));
443+ duration_to_sleep -= sleep_ms;
444+ }
445+
446+ backdown_period = retry_settings.backdown_policy (backdown_period);
447+ response = SendRequest (network_request, settings_, retry_settings, context);
448+ }
449+
450+ return response;
451+ }
289452} // namespace client
290453} // namespace olp
0 commit comments