libweave: Retry failed RefreshAccessToken requests
During network outage, failed request to refresh access token may
result in failed XMPP connection which will not be re-established
until the next scheduled GCD server communication which may not happen
for as long as 30 minutes.
Added automatic retries with exponential backoff if the failure is
due to network connectivity.
BUG=brillo:1264
TEST=`FEATURES=test emerge-link buffet`
Change-Id: Ifacbba2de036e656fe92c319b3032c32045f572c
Reviewed-on: https://chromium-review.googlesource.com/288700
Trybot-Ready: Alex Vakulenko <avakulenko@chromium.org>
Tested-by: Alex Vakulenko <avakulenko@chromium.org>
Reviewed-by: Vitaly Buka <vitalybuka@chromium.org>
Commit-Queue: Alex Vakulenko <avakulenko@chromium.org>
diff --git a/libweave/src/device_registration_info.cc b/libweave/src/device_registration_info.cc
index 9f97900..500761c 100644
--- a/libweave/src/device_registration_info.cc
+++ b/libweave/src/device_registration_info.cc
@@ -131,6 +131,8 @@
cloud_backoff_policy_->always_use_initial_delay = false;
cloud_backoff_entry_.reset(
new chromeos::BackoffEntry{cloud_backoff_policy_.get()});
+ oauth2_backoff_entry_.reset(
+ new chromeos::BackoffEntry{cloud_backoff_policy_.get()});
command_manager_->AddOnCommandDefChanged(
base::Bind(&DeviceRegistrationInfo::OnCommandDefsChanged,
@@ -244,18 +246,26 @@
return;
}
- // Make a shared pointer to |error_callback| since we are going to share this
- // callback with both success and error callbacks for PostFormData() and if
- // |error_callback| has any move-only types, one of the copies will be bad.
+ if (oauth2_backoff_entry_->ShouldRejectRequest()) {
+ VLOG(1) << "RefreshToken request delayed for "
+ << oauth2_backoff_entry_->GetTimeUntilRelease()
+ << " due to backoff policy";
+ task_runner_->PostDelayedTask(
+ FROM_HERE, base::Bind(&DeviceRegistrationInfo::RefreshAccessToken,
+ AsWeakPtr(), success_callback, error_callback),
+ oauth2_backoff_entry_->GetTimeUntilRelease());
+ return;
+ }
+
+ // Make a shared pointer to |success_callback| and |error_callback| since we
+ // are going to share these callbacks with both success and error callbacks
+ // for PostFormData() and if the callbacks have any move-only types,
+ // one of the copies will be bad.
+ auto shared_success_callback =
+ std::make_shared<base::Closure>(success_callback);
auto shared_error_callback =
std::make_shared<CloudRequestErrorCallback>(error_callback);
- auto on_refresh_error = [shared_error_callback](
- chromeos::http::RequestID id, const chromeos::Error* error) {
- VLOG(1) << "Refresh access token request with ID " << id << " failed";
- shared_error_callback->Run(error);
- };
-
chromeos::http::FormFieldList form_data{
{"refresh_token", config_->refresh_token()},
{"client_id", config_->client_id()},
@@ -266,19 +276,22 @@
chromeos::http::RequestID request_id = chromeos::http::PostFormData(
GetOAuthURL("token"), form_data, {}, transport_,
base::Bind(&DeviceRegistrationInfo::OnRefreshAccessTokenSuccess,
- weak_factory_.GetWeakPtr(), success_callback,
+ weak_factory_.GetWeakPtr(), shared_success_callback,
shared_error_callback),
- base::Bind(on_refresh_error));
+ base::Bind(&DeviceRegistrationInfo::OnRefreshAccessTokenError,
+ weak_factory_.GetWeakPtr(), shared_success_callback,
+ shared_error_callback));
VLOG(1) << "Refresh access token request dispatched. Request ID = "
<< request_id;
}
void DeviceRegistrationInfo::OnRefreshAccessTokenSuccess(
- const base::Closure& success_callback,
+ const std::shared_ptr<base::Closure>& success_callback,
const std::shared_ptr<CloudRequestErrorCallback>& error_callback,
chromeos::http::RequestID id,
std::unique_ptr<chromeos::http::Response> response) {
VLOG(1) << "Refresh access token request with ID " << id << " completed";
+ oauth2_backoff_entry_->InformOfRequest(true);
chromeos::ErrorPtr error;
auto json = ParseOAuthResponse(response.get(), &error);
if (!json) {
@@ -308,7 +321,17 @@
// Now that we have a new access token, retry the connection.
StartNotificationChannel();
}
- success_callback.Run();
+ success_callback->Run();
+}
+
+void DeviceRegistrationInfo::OnRefreshAccessTokenError(
+ const std::shared_ptr<base::Closure>& success_callback,
+ const std::shared_ptr<CloudRequestErrorCallback>& error_callback,
+ chromeos::http::RequestID id,
+ const chromeos::Error* error) {
+ VLOG(1) << "Refresh access token request with ID " << id << " failed";
+ oauth2_backoff_entry_->InformOfRequest(false);
+ RefreshAccessToken(*success_callback, *error_callback);
}
void DeviceRegistrationInfo::StartNotificationChannel() {
diff --git a/libweave/src/device_registration_info.h b/libweave/src/device_registration_info.h
index 4ea7e00..27289bd 100644
--- a/libweave/src/device_registration_info.h
+++ b/libweave/src/device_registration_info.h
@@ -174,12 +174,17 @@
void RefreshAccessToken(const base::Closure& success_callback,
const CloudRequestErrorCallback& error_callback);
- // Success callback for RefreshAccessToken().
+ // Callbacks for RefreshAccessToken().
void OnRefreshAccessTokenSuccess(
- const base::Closure& success_callback,
+ const std::shared_ptr<base::Closure>& success_callback,
const std::shared_ptr<CloudRequestErrorCallback>& error_callback,
chromeos::http::RequestID id,
std::unique_ptr<chromeos::http::Response> response);
+ void OnRefreshAccessTokenError(
+ const std::shared_ptr<base::Closure>& success_callback,
+ const std::shared_ptr<CloudRequestErrorCallback>& error_callback,
+ chromeos::http::RequestID id,
+ const chromeos::Error* error);
// Parse the OAuth response, and sets registration status to
// kInvalidCredentials if our registration is no longer valid.
@@ -315,6 +320,7 @@
// Backoff manager for DoCloudRequest() method.
std::unique_ptr<chromeos::BackoffEntry::Policy> cloud_backoff_policy_;
std::unique_ptr<chromeos::BackoffEntry> cloud_backoff_entry_;
+ std::unique_ptr<chromeos::BackoffEntry> oauth2_backoff_entry_;
// Flag set to true while a device state update patch request is in flight
// to the cloud server.