Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "chrome/browser/extensions/updater/extension_downloader.h" | 5 #include "chrome/browser/extensions/updater/extension_downloader.h" |
| 6 | 6 |
| 7 #include <utility> | 7 #include <utility> |
| 8 | 8 |
| 9 #include "base/bind.h" | 9 #include "base/bind.h" |
| 10 #include "base/command_line.h" | 10 #include "base/command_line.h" |
| (...skipping 13 matching lines...) Expand all Loading... | |
| 24 #include "chrome/browser/extensions/updater/request_queue_impl.h" | 24 #include "chrome/browser/extensions/updater/request_queue_impl.h" |
| 25 #include "chrome/browser/extensions/updater/safe_manifest_parser.h" | 25 #include "chrome/browser/extensions/updater/safe_manifest_parser.h" |
| 26 #include "chrome/browser/metrics/chrome_metrics_service_accessor.h" | 26 #include "chrome/browser/metrics/chrome_metrics_service_accessor.h" |
| 27 #include "chrome/common/chrome_switches.h" | 27 #include "chrome/common/chrome_switches.h" |
| 28 #include "chrome/common/chrome_version_info.h" | 28 #include "chrome/common/chrome_version_info.h" |
| 29 #include "chrome/common/extensions/extension_constants.h" | 29 #include "chrome/common/extensions/extension_constants.h" |
| 30 #include "chrome/common/extensions/manifest_url_handler.h" | 30 #include "chrome/common/extensions/manifest_url_handler.h" |
| 31 #include "content/public/browser/browser_thread.h" | 31 #include "content/public/browser/browser_thread.h" |
| 32 #include "content/public/browser/notification_details.h" | 32 #include "content/public/browser/notification_details.h" |
| 33 #include "content/public/browser/notification_service.h" | 33 #include "content/public/browser/notification_service.h" |
| 34 #include "google_apis/gaia/identity_provider.h" | |
| 34 #include "net/base/backoff_entry.h" | 35 #include "net/base/backoff_entry.h" |
| 35 #include "net/base/load_flags.h" | 36 #include "net/base/load_flags.h" |
| 36 #include "net/base/net_errors.h" | 37 #include "net/base/net_errors.h" |
| 38 #include "net/http/http_request_headers.h" | |
| 39 #include "net/http/http_status_code.h" | |
| 37 #include "net/url_request/url_fetcher.h" | 40 #include "net/url_request/url_fetcher.h" |
| 38 #include "net/url_request/url_request_context_getter.h" | 41 #include "net/url_request/url_request_context_getter.h" |
| 39 #include "net/url_request/url_request_status.h" | 42 #include "net/url_request/url_request_status.h" |
| 40 | 43 |
| 41 using base::Time; | 44 using base::Time; |
| 42 using base::TimeDelta; | 45 using base::TimeDelta; |
| 43 using content::BrowserThread; | 46 using content::BrowserThread; |
| 44 | 47 |
| 45 namespace extensions { | 48 namespace extensions { |
| 46 | 49 |
| (...skipping 27 matching lines...) Expand all Loading... | |
| 74 false, | 77 false, |
| 75 }; | 78 }; |
| 76 | 79 |
| 77 const char kAuthUserQueryKey[] = "authuser"; | 80 const char kAuthUserQueryKey[] = "authuser"; |
| 78 | 81 |
| 79 const int kMaxAuthUserValue = 10; | 82 const int kMaxAuthUserValue = 10; |
| 80 | 83 |
| 81 const char kNotFromWebstoreInstallSource[] = "notfromwebstore"; | 84 const char kNotFromWebstoreInstallSource[] = "notfromwebstore"; |
| 82 const char kDefaultInstallSource[] = ""; | 85 const char kDefaultInstallSource[] = ""; |
| 83 | 86 |
| 84 #define RETRY_HISTOGRAM(name, retry_count, url) \ | 87 const char kGoogleDotCom[] = "google.com"; |
| 85 if ((url).DomainIs("google.com")) { \ | 88 const char kTokenServiceConsumerId[] = "extension_downloader"; |
| 86 UMA_HISTOGRAM_CUSTOM_COUNTS( \ | 89 const char kWebstoreOAuth2Scope[] = |
| 87 "Extensions." name "RetryCountGoogleUrl", retry_count, 1, \ | 90 "https://www.googleapis.com/auth/chromewebstore.readonly"; |
| 88 kMaxRetries, kMaxRetries+1); \ | 91 |
| 89 } else { \ | 92 #define RETRY_HISTOGRAM(name, retry_count, url) \ |
| 90 UMA_HISTOGRAM_CUSTOM_COUNTS( \ | 93 if ((url).DomainIs(kGoogleDotCom)) { \ |
| 91 "Extensions." name "RetryCountOtherUrl", retry_count, 1, \ | 94 UMA_HISTOGRAM_CUSTOM_COUNTS("Extensions." name "RetryCountGoogleUrl", \ |
| 92 kMaxRetries, kMaxRetries+1); \ | 95 retry_count, \ |
| 93 } | 96 1, \ |
| 97 kMaxRetries, \ | |
| 98 kMaxRetries + 1); \ | |
| 99 } else { \ | |
| 100 UMA_HISTOGRAM_CUSTOM_COUNTS("Extensions." name "RetryCountOtherUrl", \ | |
| 101 retry_count, \ | |
| 102 1, \ | |
| 103 kMaxRetries, \ | |
| 104 kMaxRetries + 1); \ | |
| 105 } | |
| 94 | 106 |
| 95 bool ShouldRetryRequest(const net::URLRequestStatus& status, | 107 bool ShouldRetryRequest(const net::URLRequestStatus& status, |
| 96 int response_code) { | 108 int response_code) { |
| 97 // Retry if the response code is a server error, or the request failed because | 109 // Retry if the response code is a server error, or the request failed because |
| 98 // of network errors as opposed to file errors. | 110 // of network errors as opposed to file errors. |
| 99 return ((response_code >= 500 && status.is_success()) || | 111 return ((response_code >= 500 && status.is_success()) || |
| 100 status.status() == net::URLRequestStatus::FAILED); | 112 status.status() == net::URLRequestStatus::FAILED); |
| 101 } | 113 } |
| 102 | 114 |
| 103 bool ShouldRetryRequestWithCookies(const net::URLRequestStatus& status, | |
| 104 int response_code, | |
| 105 bool included_cookies) { | |
| 106 if (included_cookies) | |
| 107 return false; | |
| 108 | |
| 109 if (status.status() == net::URLRequestStatus::CANCELED) | |
| 110 return true; | |
| 111 | |
| 112 // Retry if a 401 or 403 is received. | |
| 113 return (status.status() == net::URLRequestStatus::SUCCESS && | |
| 114 (response_code == 401 || response_code == 403)); | |
| 115 } | |
| 116 | |
| 117 bool ShouldRetryRequestWithNextUser(const net::URLRequestStatus& status, | |
| 118 int response_code, | |
| 119 bool included_cookies) { | |
| 120 // Retry if a 403 is received in response to a request including cookies. | |
| 121 // Note that receiving a 401 in response to a request which included cookies | |
| 122 // should indicate that the |authuser| index was out of bounds for the profile | |
| 123 // and therefore Chrome should NOT retry with another index. | |
| 124 return (status.status() == net::URLRequestStatus::SUCCESS && | |
| 125 response_code == 403 && included_cookies); | |
| 126 } | |
| 127 | |
| 128 // This parses and updates a URL query such that the value of the |authuser| | 115 // This parses and updates a URL query such that the value of the |authuser| |
| 129 // query parameter is incremented by 1. If parameter was not present in the URL, | 116 // query parameter is incremented by 1. If parameter was not present in the URL, |
| 130 // it will be added with a value of 1. All other query keys and values are | 117 // it will be added with a value of 1. All other query keys and values are |
| 131 // preserved as-is. Returns |false| if the user index exceeds a hard-coded | 118 // preserved as-is. Returns |false| if the user index exceeds a hard-coded |
| 132 // maximum. | 119 // maximum. |
| 133 bool IncrementAuthUserIndex(GURL* url) { | 120 bool IncrementAuthUserIndex(GURL* url) { |
| 134 int user_index = 0; | 121 int user_index = 0; |
| 135 std::string old_query = url->query(); | 122 std::string old_query = url->query(); |
| 136 std::vector<std::string> new_query_parts; | 123 std::vector<std::string> new_query_parts; |
| 137 url::Component query(0, old_query.length()); | 124 url::Component query(0, old_query.length()); |
| (...skipping 21 matching lines...) Expand all Loading... | |
| 159 } | 146 } |
| 160 | 147 |
| 161 } // namespace | 148 } // namespace |
| 162 | 149 |
| 163 UpdateDetails::UpdateDetails(const std::string& id, const Version& version) | 150 UpdateDetails::UpdateDetails(const std::string& id, const Version& version) |
| 164 : id(id), version(version) {} | 151 : id(id), version(version) {} |
| 165 | 152 |
| 166 UpdateDetails::~UpdateDetails() {} | 153 UpdateDetails::~UpdateDetails() {} |
| 167 | 154 |
| 168 ExtensionDownloader::ExtensionFetch::ExtensionFetch() | 155 ExtensionDownloader::ExtensionFetch::ExtensionFetch() |
| 169 : url(), is_protected(false) {} | 156 : url(), credentials(CREDENTIALS_NONE) { |
| 157 } | |
| 170 | 158 |
| 171 ExtensionDownloader::ExtensionFetch::ExtensionFetch( | 159 ExtensionDownloader::ExtensionFetch::ExtensionFetch( |
| 172 const std::string& id, | 160 const std::string& id, |
| 173 const GURL& url, | 161 const GURL& url, |
| 174 const std::string& package_hash, | 162 const std::string& package_hash, |
| 175 const std::string& version, | 163 const std::string& version, |
| 176 const std::set<int>& request_ids) | 164 const std::set<int>& request_ids) |
| 177 : id(id), url(url), package_hash(package_hash), version(version), | 165 : id(id), |
| 178 request_ids(request_ids), is_protected(false) {} | 166 url(url), |
| 167 package_hash(package_hash), | |
| 168 version(version), | |
| 169 request_ids(request_ids), | |
| 170 credentials(CREDENTIALS_NONE) { | |
| 171 } | |
| 179 | 172 |
| 180 ExtensionDownloader::ExtensionFetch::~ExtensionFetch() {} | 173 ExtensionDownloader::ExtensionFetch::~ExtensionFetch() {} |
| 181 | 174 |
| 182 ExtensionDownloader::ExtensionDownloader( | 175 ExtensionDownloader::ExtensionDownloader( |
| 183 ExtensionDownloaderDelegate* delegate, | 176 ExtensionDownloaderDelegate* delegate, |
| 184 net::URLRequestContextGetter* request_context) | 177 net::URLRequestContextGetter* request_context, |
| 185 : delegate_(delegate), | 178 IdentityProvider* webstore_identity_provider) |
| 179 : OAuth2TokenService::Consumer(kTokenServiceConsumerId), | |
| 180 delegate_(delegate), | |
| 186 request_context_(request_context), | 181 request_context_(request_context), |
| 187 weak_ptr_factory_(this), | 182 weak_ptr_factory_(this), |
| 188 manifests_queue_(&kDefaultBackoffPolicy, | 183 manifests_queue_(&kDefaultBackoffPolicy, |
| 189 base::Bind(&ExtensionDownloader::CreateManifestFetcher, | 184 base::Bind(&ExtensionDownloader::CreateManifestFetcher, |
| 190 base::Unretained(this))), | 185 base::Unretained(this))), |
| 191 extensions_queue_(&kDefaultBackoffPolicy, | 186 extensions_queue_(&kDefaultBackoffPolicy, |
| 192 base::Bind(&ExtensionDownloader::CreateExtensionFetcher, | 187 base::Bind(&ExtensionDownloader::CreateExtensionFetcher, |
| 193 base::Unretained(this))), | 188 base::Unretained(this))), |
| 194 extension_cache_(NULL) { | 189 extension_cache_(NULL), |
| 190 identity_provider_(webstore_identity_provider) { | |
| 195 DCHECK(delegate_); | 191 DCHECK(delegate_); |
| 196 DCHECK(request_context_); | 192 DCHECK(request_context_); |
| 197 } | 193 } |
| 198 | 194 |
| 199 ExtensionDownloader::~ExtensionDownloader() {} | 195 ExtensionDownloader::~ExtensionDownloader() {} |
| 200 | 196 |
| 201 bool ExtensionDownloader::AddExtension(const Extension& extension, | 197 bool ExtensionDownloader::AddExtension(const Extension& extension, |
| 202 int request_id) { | 198 int request_id) { |
| 203 // Skip extensions with empty update URLs converted from user | 199 // Skip extensions with empty update URLs converted from user |
| 204 // scripts. | 200 // scripts. |
| (...skipping 94 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 299 if (extension_urls::IsWebstoreUpdateUrl(update_url) && | 295 if (extension_urls::IsWebstoreUpdateUrl(update_url) && |
| 300 !update_url.SchemeIsSecure()) | 296 !update_url.SchemeIsSecure()) |
| 301 update_url = extension_urls::GetWebstoreUpdateUrl(); | 297 update_url = extension_urls::GetWebstoreUpdateUrl(); |
| 302 | 298 |
| 303 // Skip extensions with empty IDs. | 299 // Skip extensions with empty IDs. |
| 304 if (id.empty()) { | 300 if (id.empty()) { |
| 305 LOG(WARNING) << "Found extension with empty ID"; | 301 LOG(WARNING) << "Found extension with empty ID"; |
| 306 return false; | 302 return false; |
| 307 } | 303 } |
| 308 | 304 |
| 309 if (update_url.DomainIs("google.com")) { | 305 if (update_url.DomainIs(kGoogleDotCom)) { |
| 310 url_stats_.google_url_count++; | 306 url_stats_.google_url_count++; |
| 311 } else if (update_url.is_empty()) { | 307 } else if (update_url.is_empty()) { |
| 312 url_stats_.no_url_count++; | 308 url_stats_.no_url_count++; |
| 313 // Fill in default update URL. | 309 // Fill in default update URL. |
| 314 update_url = extension_urls::GetWebstoreUpdateUrl(); | 310 update_url = extension_urls::GetWebstoreUpdateUrl(); |
| 315 } else { | 311 } else { |
| 316 url_stats_.other_url_count++; | 312 url_stats_.other_url_count++; |
| 317 } | 313 } |
| 318 | 314 |
| 319 switch (extension_type) { | 315 switch (extension_type) { |
| (...skipping 253 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 573 } | 569 } |
| 574 } | 570 } |
| 575 scoped_ptr<ExtensionFetch> fetch(new ExtensionFetch( | 571 scoped_ptr<ExtensionFetch> fetch(new ExtensionFetch( |
| 576 update->extension_id, crx_url, update->package_hash, | 572 update->extension_id, crx_url, update->package_hash, |
| 577 update->version, fetch_data.request_ids())); | 573 update->version, fetch_data.request_ids())); |
| 578 FetchUpdatedExtension(fetch.Pass()); | 574 FetchUpdatedExtension(fetch.Pass()); |
| 579 } | 575 } |
| 580 | 576 |
| 581 // If the manifest response included a <daystart> element, we want to save | 577 // If the manifest response included a <daystart> element, we want to save |
| 582 // that value for any extensions which had sent a ping in the request. | 578 // that value for any extensions which had sent a ping in the request. |
| 583 if (fetch_data.base_url().DomainIs("google.com") && | 579 if (fetch_data.base_url().DomainIs(kGoogleDotCom) && |
| 584 results->daystart_elapsed_seconds >= 0) { | 580 results->daystart_elapsed_seconds >= 0) { |
| 585 Time day_start = | 581 Time day_start = |
| 586 Time::Now() - TimeDelta::FromSeconds(results->daystart_elapsed_seconds); | 582 Time::Now() - TimeDelta::FromSeconds(results->daystart_elapsed_seconds); |
| 587 | 583 |
| 588 const std::set<std::string>& extension_ids = fetch_data.extension_ids(); | 584 const std::set<std::string>& extension_ids = fetch_data.extension_ids(); |
| 589 std::set<std::string>::const_iterator i; | 585 std::set<std::string>::const_iterator i; |
| 590 for (i = extension_ids.begin(); i != extension_ids.end(); i++) { | 586 for (i = extension_ids.begin(); i != extension_ids.end(); i++) { |
| 591 const std::string& id = *i; | 587 const std::string& id = *i; |
| 592 ExtensionDownloaderDelegate::PingResult& result = ping_results_[id]; | 588 ExtensionDownloaderDelegate::PingResult& result = ping_results_[id]; |
| 593 result.did_ping = fetch_data.DidPing(id, ManifestFetchData::ROLLCALL); | 589 result.did_ping = fetch_data.DidPing(id, ManifestFetchData::ROLLCALL); |
| (...skipping 121 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 715 const base::FilePath& crx_path, | 711 const base::FilePath& crx_path, |
| 716 bool file_ownership_passed) { | 712 bool file_ownership_passed) { |
| 717 delegate_->OnExtensionDownloadFinished(fetch_data->id, crx_path, | 713 delegate_->OnExtensionDownloadFinished(fetch_data->id, crx_path, |
| 718 file_ownership_passed, fetch_data->url, fetch_data->version, | 714 file_ownership_passed, fetch_data->url, fetch_data->version, |
| 719 ping_results_[fetch_data->id], fetch_data->request_ids); | 715 ping_results_[fetch_data->id], fetch_data->request_ids); |
| 720 ping_results_.erase(fetch_data->id); | 716 ping_results_.erase(fetch_data->id); |
| 721 } | 717 } |
| 722 | 718 |
| 723 void ExtensionDownloader::CreateExtensionFetcher() { | 719 void ExtensionDownloader::CreateExtensionFetcher() { |
| 724 const ExtensionFetch* fetch = extensions_queue_.active_request(); | 720 const ExtensionFetch* fetch = extensions_queue_.active_request(); |
| 721 extension_fetcher_.reset(net::URLFetcher::Create( | |
| 722 kExtensionFetcherId, fetch->url, net::URLFetcher::GET, this)); | |
| 723 extension_fetcher_->SetRequestContext(request_context_); | |
| 724 extension_fetcher_->SetAutomaticallyRetryOnNetworkChanges(3); | |
| 725 | |
| 725 int load_flags = net::LOAD_DISABLE_CACHE; | 726 int load_flags = net::LOAD_DISABLE_CACHE; |
| 726 if (!fetch->is_protected || !fetch->url.SchemeIs("https")) { | 727 bool is_secure = fetch->url.SchemeIsSecure(); |
| 728 if (fetch->credentials != ExtensionFetch::CREDENTIALS_COOKIES || !is_secure) { | |
| 727 load_flags |= net::LOAD_DO_NOT_SEND_COOKIES | | 729 load_flags |= net::LOAD_DO_NOT_SEND_COOKIES | |
| 728 net::LOAD_DO_NOT_SAVE_COOKIES; | 730 net::LOAD_DO_NOT_SAVE_COOKIES; |
| 729 } | 731 } |
| 730 extension_fetcher_.reset(net::URLFetcher::Create( | |
| 731 kExtensionFetcherId, fetch->url, net::URLFetcher::GET, this)); | |
| 732 extension_fetcher_->SetRequestContext(request_context_); | |
| 733 extension_fetcher_->SetLoadFlags(load_flags); | 732 extension_fetcher_->SetLoadFlags(load_flags); |
| 734 extension_fetcher_->SetAutomaticallyRetryOnNetworkChanges(3); | 733 |
| 735 // Download CRX files to a temp file. The blacklist is small and will be | 734 // Download CRX files to a temp file. The blacklist is small and will be |
| 736 // processed in memory, so it is fetched into a string. | 735 // processed in memory, so it is fetched into a string. |
| 737 if (fetch->id != kBlacklistAppID) { | 736 if (fetch->id != kBlacklistAppID) { |
| 738 extension_fetcher_->SaveResponseToTemporaryFile( | 737 extension_fetcher_->SaveResponseToTemporaryFile( |
| 739 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::FILE)); | 738 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::FILE)); |
| 740 } | 739 } |
| 741 | 740 |
| 741 if (fetch->credentials == ExtensionFetch::CREDENTIALS_OAUTH2_TOKEN && | |
| 742 is_secure) { | |
| 743 if (access_token_.empty()) { | |
| 744 // We should try OAuth2, but we have no token cached. This | |
| 745 // ExtensionFetcher will be started once a token fetch is complete. | |
| 746 DCHECK(identity_provider_); | |
| 747 OAuth2TokenService::ScopeSet webstore_scopes; | |
| 748 webstore_scopes.insert(kWebstoreOAuth2Scope); | |
| 749 access_token_request_ = | |
| 750 identity_provider_->GetTokenService()->StartRequest( | |
| 751 identity_provider_->GetActiveAccountId(), | |
| 752 webstore_scopes, | |
| 753 this); | |
|
asargent_no_longer_on_chrome
2014/08/07 17:09:12
It might be helpful to future readers to add a com
Ken Rockot(use gerrit already)
2014/08/07 19:50:47
Added some clarity to the existing comment
| |
| 754 return; | |
| 755 } | |
| 756 extension_fetcher_->AddExtraRequestHeader( | |
| 757 base::StringPrintf("%s: Bearer %s", | |
| 758 net::HttpRequestHeaders::kAuthorization, | |
| 759 access_token_.c_str())); | |
|
asargent_no_longer_on_chrome
2014/08/07 17:09:12
Would it be worth adding a kAuthorizationBearer co
Ken Rockot(use gerrit already)
2014/08/07 19:50:47
Bearer tokens are (unlike the Authorization header
| |
| 760 } | |
| 761 | |
| 742 VLOG(2) << "Starting fetch of " << fetch->url << " for " << fetch->id; | 762 VLOG(2) << "Starting fetch of " << fetch->url << " for " << fetch->id; |
| 743 | |
| 744 extension_fetcher_->Start(); | 763 extension_fetcher_->Start(); |
| 745 } | 764 } |
| 746 | 765 |
| 747 void ExtensionDownloader::OnCRXFetchComplete( | 766 void ExtensionDownloader::OnCRXFetchComplete( |
| 748 const net::URLFetcher* source, | 767 const net::URLFetcher* source, |
| 749 const GURL& url, | 768 const GURL& url, |
| 750 const net::URLRequestStatus& status, | 769 const net::URLRequestStatus& status, |
| 751 int response_code, | 770 int response_code, |
| 752 const base::TimeDelta& backoff_delay) { | 771 const base::TimeDelta& backoff_delay) { |
| 753 const std::string& id = extensions_queue_.active_request()->id; | 772 ExtensionFetch& active_request = *extensions_queue_.active_request(); |
| 773 const std::string& id = active_request.id; | |
| 754 if (status.status() == net::URLRequestStatus::SUCCESS && | 774 if (status.status() == net::URLRequestStatus::SUCCESS && |
| 755 (response_code == 200 || url.SchemeIsFile())) { | 775 (response_code == 200 || url.SchemeIsFile())) { |
| 756 RETRY_HISTOGRAM("CrxFetchSuccess", | 776 RETRY_HISTOGRAM("CrxFetchSuccess", |
| 757 extensions_queue_.active_request_failure_count(), url); | 777 extensions_queue_.active_request_failure_count(), url); |
| 758 base::FilePath crx_path; | 778 base::FilePath crx_path; |
| 759 // Take ownership of the file at |crx_path|. | 779 // Take ownership of the file at |crx_path|. |
| 760 CHECK(source->GetResponseAsFilePath(true, &crx_path)); | 780 CHECK(source->GetResponseAsFilePath(true, &crx_path)); |
| 761 scoped_ptr<ExtensionFetch> fetch_data = | 781 scoped_ptr<ExtensionFetch> fetch_data = |
| 762 extensions_queue_.reset_active_request(); | 782 extensions_queue_.reset_active_request(); |
| 763 if (extension_cache_) { | 783 if (extension_cache_) { |
| 764 const std::string& version = fetch_data->version; | 784 const std::string& version = fetch_data->version; |
| 765 extension_cache_->PutExtension(id, crx_path, version, | 785 extension_cache_->PutExtension(id, crx_path, version, |
| 766 base::Bind(&ExtensionDownloader::NotifyDelegateDownloadFinished, | 786 base::Bind(&ExtensionDownloader::NotifyDelegateDownloadFinished, |
| 767 weak_ptr_factory_.GetWeakPtr(), | 787 weak_ptr_factory_.GetWeakPtr(), |
| 768 base::Passed(&fetch_data))); | 788 base::Passed(&fetch_data))); |
| 769 } else { | 789 } else { |
| 770 NotifyDelegateDownloadFinished(fetch_data.Pass(), crx_path, true); | 790 NotifyDelegateDownloadFinished(fetch_data.Pass(), crx_path, true); |
| 771 } | 791 } |
| 772 } else if (ShouldRetryRequestWithCookies( | 792 } else if (IterateFetchCredentialsAfterFailure( |
| 773 status, | 793 &active_request, |
| 774 response_code, | 794 status, |
| 775 extensions_queue_.active_request()->is_protected)) { | 795 response_code)) { |
| 776 // Requeue the fetch with |is_protected| set, enabling cookies. | |
| 777 extensions_queue_.active_request()->is_protected = true; | |
| 778 extensions_queue_.RetryRequest(backoff_delay); | |
| 779 } else if (ShouldRetryRequestWithNextUser( | |
| 780 status, | |
| 781 response_code, | |
| 782 extensions_queue_.active_request()->is_protected) && | |
| 783 IncrementAuthUserIndex(&extensions_queue_.active_request()->url)) { | |
| 784 extensions_queue_.RetryRequest(backoff_delay); | 796 extensions_queue_.RetryRequest(backoff_delay); |
| 785 } else { | 797 } else { |
| 786 const std::set<int>& request_ids = | 798 const std::set<int>& request_ids = active_request.request_ids; |
| 787 extensions_queue_.active_request()->request_ids; | |
| 788 const ExtensionDownloaderDelegate::PingResult& ping = ping_results_[id]; | 799 const ExtensionDownloaderDelegate::PingResult& ping = ping_results_[id]; |
| 789 VLOG(1) << "Failed to fetch extension '" << url.possibly_invalid_spec() | 800 VLOG(1) << "Failed to fetch extension '" << url.possibly_invalid_spec() |
| 790 << "' response code:" << response_code; | 801 << "' response code:" << response_code; |
| 791 if (ShouldRetryRequest(status, response_code) && | 802 if (ShouldRetryRequest(status, response_code) && |
| 792 extensions_queue_.active_request_failure_count() < kMaxRetries) { | 803 extensions_queue_.active_request_failure_count() < kMaxRetries) { |
| 793 extensions_queue_.RetryRequest(backoff_delay); | 804 extensions_queue_.RetryRequest(backoff_delay); |
| 794 } else { | 805 } else { |
| 795 RETRY_HISTOGRAM("CrxFetchFailure", | 806 RETRY_HISTOGRAM("CrxFetchFailure", |
| 796 extensions_queue_.active_request_failure_count(), url); | 807 extensions_queue_.active_request_failure_count(), url); |
| 797 // status.error() is 0 (net::OK) or negative. (See net/base/net_errors.h) | 808 // status.error() is 0 (net::OK) or negative. (See net/base/net_errors.h) |
| (...skipping 25 matching lines...) Expand all Loading... | |
| 823 | 834 |
| 824 void ExtensionDownloader::NotifyUpdateFound(const std::string& id, | 835 void ExtensionDownloader::NotifyUpdateFound(const std::string& id, |
| 825 const std::string& version) { | 836 const std::string& version) { |
| 826 UpdateDetails updateInfo(id, Version(version)); | 837 UpdateDetails updateInfo(id, Version(version)); |
| 827 content::NotificationService::current()->Notify( | 838 content::NotificationService::current()->Notify( |
| 828 extensions::NOTIFICATION_EXTENSION_UPDATE_FOUND, | 839 extensions::NOTIFICATION_EXTENSION_UPDATE_FOUND, |
| 829 content::NotificationService::AllBrowserContextsAndSources(), | 840 content::NotificationService::AllBrowserContextsAndSources(), |
| 830 content::Details<UpdateDetails>(&updateInfo)); | 841 content::Details<UpdateDetails>(&updateInfo)); |
| 831 } | 842 } |
| 832 | 843 |
| 844 bool ExtensionDownloader::IterateFetchCredentialsAfterFailure( | |
| 845 ExtensionFetch* fetch, | |
| 846 const net::URLRequestStatus& status, | |
| 847 int response_code) { | |
| 848 bool auth_failure = status.status() == net::URLRequestStatus::CANCELED || | |
| 849 (status.status() == net::URLRequestStatus::SUCCESS && | |
| 850 (response_code == net::HTTP_UNAUTHORIZED || | |
| 851 response_code == net::HTTP_FORBIDDEN)); | |
| 852 if (!auth_failure) { | |
| 853 return false; | |
| 854 } | |
| 855 // Here we decide what to do next if the server refused to authorize this | |
| 856 // fetch. | |
| 857 switch (fetch->credentials) { | |
| 858 case ExtensionFetch::CREDENTIALS_NONE: | |
| 859 if (fetch->url.DomainIs(kGoogleDotCom) && identity_provider_) { | |
| 860 fetch->credentials = ExtensionFetch::CREDENTIALS_OAUTH2_TOKEN; | |
| 861 } else { | |
| 862 fetch->credentials = ExtensionFetch::CREDENTIALS_COOKIES; | |
| 863 } | |
| 864 return true; | |
| 865 case ExtensionFetch::CREDENTIALS_OAUTH2_TOKEN: | |
| 866 // OAuth2 may fail due to an expired access token, in which case we | |
| 867 // should invalidate the token and try again. | |
| 868 if (response_code == net::HTTP_UNAUTHORIZED) { | |
| 869 DCHECK(identity_provider_ != NULL); | |
| 870 OAuth2TokenService::ScopeSet webstore_scopes; | |
| 871 webstore_scopes.insert(kWebstoreOAuth2Scope); | |
| 872 identity_provider_->GetTokenService()->InvalidateToken( | |
| 873 identity_provider_->GetActiveAccountId(), | |
| 874 webstore_scopes, | |
| 875 access_token_); | |
| 876 access_token_.clear(); | |
|
asargent_no_longer_on_chrome
2014/08/07 17:09:12
Can a misbehaving server cause us to fall into an
Ken Rockot(use gerrit already)
2014/08/07 19:50:48
Ah yes. Good catch. Added an attempt counter to th
| |
| 877 return true; | |
| 878 } | |
| 879 // Either there is no Gaia identity available or the active identity | |
| 880 // doesn't have access to this resource. Fall back on cookies. | |
| 881 if (access_token_.empty() || | |
| 882 response_code == net::HTTP_FORBIDDEN) { | |
| 883 fetch->credentials = ExtensionFetch::CREDENTIALS_COOKIES; | |
| 884 return true; | |
| 885 } | |
| 886 return false; | |
| 887 case ExtensionFetch::CREDENTIALS_COOKIES: | |
| 888 if (response_code == net::HTTP_FORBIDDEN) { | |
| 889 // Try the next session identity, up to some maximum. | |
| 890 return IncrementAuthUserIndex(&fetch->url); | |
| 891 } | |
| 892 return false; | |
| 893 default: | |
| 894 NOTREACHED(); | |
| 895 } | |
| 896 NOTREACHED(); | |
| 897 } | |
| 898 | |
| 899 void ExtensionDownloader::OnGetTokenSuccess( | |
| 900 const OAuth2TokenService::Request* request, | |
| 901 const std::string& access_token, | |
| 902 const base::Time& expiration_time) { | |
| 903 access_token_ = access_token; | |
| 904 extension_fetcher_->AddExtraRequestHeader( | |
| 905 base::StringPrintf("%s: Bearer %s", | |
| 906 net::HttpRequestHeaders::kAuthorization, | |
| 907 access_token_.c_str())); | |
| 908 extension_fetcher_->Start(); | |
| 909 } | |
| 910 | |
| 911 void ExtensionDownloader::OnGetTokenFailure( | |
| 912 const OAuth2TokenService::Request* request, | |
| 913 const GoogleServiceAuthError& error) { | |
| 914 // If we fail to get an access token, kick the pending fetch and let it fall | |
| 915 // back on cookies. | |
| 916 extension_fetcher_->Start(); | |
| 917 } | |
| 918 | |
| 833 } // namespace extensions | 919 } // namespace extensions |
| OLD | NEW |