Index: chrome/browser/extensions/updater/extension_downloader.cc |
diff --git a/chrome/browser/extensions/updater/extension_downloader.cc b/chrome/browser/extensions/updater/extension_downloader.cc |
index 8a2177be92bdb7feed14220066931ad2879bddfa..7cad3ea278301d8fbf2d3c610eaeb3d153e18212 100644 |
--- a/chrome/browser/extensions/updater/extension_downloader.cc |
+++ b/chrome/browser/extensions/updater/extension_downloader.cc |
@@ -16,7 +16,9 @@ |
#include "base/metrics/sparse_histogram.h" |
#include "base/platform_file.h" |
#include "base/stl_util.h" |
+#include "base/strings/string_number_conversions.h" |
#include "base/strings/string_util.h" |
+#include "base/strings/stringprintf.h" |
#include "base/time/time.h" |
#include "base/version.h" |
#include "chrome/browser/chrome_notification_types.h" |
@@ -74,6 +76,10 @@ const net::BackoffEntry::Policy kDefaultBackoffPolicy = { |
false, |
}; |
+const char kAuthUserQueryKey[] = "authuser"; |
+ |
+const int kMaxAuthUserValue = 10; |
+ |
const char kNotFromWebstoreInstallSource[] = "notfromwebstore"; |
const char kDefaultInstallSource[] = ""; |
@@ -92,8 +98,66 @@ bool ShouldRetryRequest(const net::URLRequestStatus& status, |
int response_code) { |
// Retry if the response code is a server error, or the request failed because |
// of network errors as opposed to file errors. |
- return (response_code >= 500 && status.is_success()) || |
- status.status() == net::URLRequestStatus::FAILED; |
+ return ((response_code >= 500 && status.is_success()) || |
+ status.status() == net::URLRequestStatus::FAILED); |
+} |
+ |
+bool ShouldRetryRequestWithCookies(const net::URLRequestStatus& status, |
+ int response_code, |
+ bool included_cookies) { |
+ if (included_cookies) |
+ return false; |
+ |
+ if (status.status() == net::URLRequestStatus::CANCELED) |
+ return true; |
+ |
+ // Retry if a 401 or 403 is received. |
+ return (status.status() == net::URLRequestStatus::SUCCESS && |
+ (response_code == 401 || response_code == 403)); |
+} |
+ |
+bool ShouldRetryRequestWithNextUser(const net::URLRequestStatus& status, |
+ int response_code, |
+ bool included_cookies) { |
+ // Retry if a 403 is received in response to a request including cookies. |
+ // Note that receiving a 401 in response to a request which included cookies |
+ // should indicate that the |authuser| index was out of bounds for the profile |
+ // and therefore Chrome should NOT retry with another index. |
+ return (status.status() == net::URLRequestStatus::SUCCESS && |
+ response_code == 403 && included_cookies); |
+} |
+ |
+// This parses and updates a URL query such that the value of the |authuser| |
+// query parameter is incremented by 1. If parameter was not present in the URL, |
+// it will be added with a value of 1. All other query keys and values are |
+// preserved as-is. Returns |false| if the user index exceeds a hard-coded |
+// maximum. |
+bool IncrementAuthUserIndex(GURL* url) { |
+ int user_index = 0; |
+ std::string old_query = url->query(); |
+ std::vector<std::string> new_query_parts; |
+ url::Component query(0, old_query.length()); |
+ url::Component key, value; |
+ while (url::ExtractQueryKeyValue(old_query.c_str(), &query, &key, &value)) { |
+ std::string key_string = old_query.substr(key.begin, key.len); |
+ std::string value_string = old_query.substr(value.begin, value.len); |
+ if (key_string == kAuthUserQueryKey) { |
+ base::StringToInt(value_string, &user_index); |
+ } else { |
+ new_query_parts.push_back(base::StringPrintf( |
+ "%s=%s", key_string.c_str(), value_string.c_str())); |
+ } |
+ } |
+ if (user_index >= kMaxAuthUserValue) |
+ return false; |
+ new_query_parts.push_back( |
+ base::StringPrintf("%s=%d", kAuthUserQueryKey, user_index + 1)); |
+ std::string new_query_string = JoinString(new_query_parts, '&'); |
+ url::Component new_query(0, new_query_string.size()); |
+ url::Replacements<char> replacements; |
+ replacements.SetQuery(new_query_string.c_str(), new_query); |
+ *url = url->ReplaceComponents(replacements); |
+ return true; |
} |
} // namespace |
@@ -707,17 +771,23 @@ void ExtensionDownloader::OnCRXFetchComplete( |
} else { |
NotifyDelegateDownloadFinished(fetch_data.Pass(), crx_path, true); |
} |
- } else if (status.status() == net::URLRequestStatus::SUCCESS && |
- (response_code == 401 || response_code == 403) && |
- !extensions_queue_.active_request()->is_protected) { |
- // On 401 or 403, requeue this fetch with cookies enabled. |
+ } else if (ShouldRetryRequestWithCookies( |
+ status, |
+ response_code, |
+ extensions_queue_.active_request()->is_protected)) { |
+ // Requeue the fetch with |is_protected| set, enabling cookies. |
extensions_queue_.active_request()->is_protected = true; |
extensions_queue_.RetryRequest(backoff_delay); |
+ } else if (ShouldRetryRequestWithNextUser( |
+ status, |
+ response_code, |
+ extensions_queue_.active_request()->is_protected) && |
+ IncrementAuthUserIndex(&extensions_queue_.active_request()->url)) { |
+ extensions_queue_.RetryRequest(backoff_delay); |
} else { |
const std::set<int>& request_ids = |
extensions_queue_.active_request()->request_ids; |
const ExtensionDownloaderDelegate::PingResult& ping = ping_results_[id]; |
- |
VLOG(1) << "Failed to fetch extension '" << url.possibly_invalid_spec() |
<< "' response code:" << response_code; |
if (ShouldRetryRequest(status, response_code) && |