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 23 matching lines...) Expand all Loading... |
70 // has no significant state, -1 to never discard. | 73 // has no significant state, -1 to never discard. |
71 -1, | 74 -1, |
72 | 75 |
73 // Don't use initial delay unless the last request was an error. | 76 // Don't use initial delay unless the last request was an error. |
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; |
| 83 const int kMaxOAuth2Attempts = 3; |
80 | 84 |
81 const char kNotFromWebstoreInstallSource[] = "notfromwebstore"; | 85 const char kNotFromWebstoreInstallSource[] = "notfromwebstore"; |
82 const char kDefaultInstallSource[] = ""; | 86 const char kDefaultInstallSource[] = ""; |
83 | 87 |
84 #define RETRY_HISTOGRAM(name, retry_count, url) \ | 88 const char kGoogleDotCom[] = "google.com"; |
85 if ((url).DomainIs("google.com")) { \ | 89 const char kTokenServiceConsumerId[] = "extension_downloader"; |
86 UMA_HISTOGRAM_CUSTOM_COUNTS( \ | 90 const char kWebstoreOAuth2Scope[] = |
87 "Extensions." name "RetryCountGoogleUrl", retry_count, 1, \ | 91 "https://www.googleapis.com/auth/chromewebstore.readonly"; |
88 kMaxRetries, kMaxRetries+1); \ | 92 |
89 } else { \ | 93 #define RETRY_HISTOGRAM(name, retry_count, url) \ |
90 UMA_HISTOGRAM_CUSTOM_COUNTS( \ | 94 if ((url).DomainIs(kGoogleDotCom)) { \ |
91 "Extensions." name "RetryCountOtherUrl", retry_count, 1, \ | 95 UMA_HISTOGRAM_CUSTOM_COUNTS("Extensions." name "RetryCountGoogleUrl", \ |
92 kMaxRetries, kMaxRetries+1); \ | 96 retry_count, \ |
93 } | 97 1, \ |
| 98 kMaxRetries, \ |
| 99 kMaxRetries + 1); \ |
| 100 } else { \ |
| 101 UMA_HISTOGRAM_CUSTOM_COUNTS("Extensions." name "RetryCountOtherUrl", \ |
| 102 retry_count, \ |
| 103 1, \ |
| 104 kMaxRetries, \ |
| 105 kMaxRetries + 1); \ |
| 106 } |
94 | 107 |
95 bool ShouldRetryRequest(const net::URLRequestStatus& status, | 108 bool ShouldRetryRequest(const net::URLRequestStatus& status, |
96 int response_code) { | 109 int response_code) { |
97 // Retry if the response code is a server error, or the request failed because | 110 // Retry if the response code is a server error, or the request failed because |
98 // of network errors as opposed to file errors. | 111 // of network errors as opposed to file errors. |
99 return ((response_code >= 500 && status.is_success()) || | 112 return ((response_code >= 500 && status.is_success()) || |
100 status.status() == net::URLRequestStatus::FAILED); | 113 status.status() == net::URLRequestStatus::FAILED); |
101 } | 114 } |
102 | 115 |
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| | 116 // 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, | 117 // 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 | 118 // 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 | 119 // preserved as-is. Returns |false| if the user index exceeds a hard-coded |
132 // maximum. | 120 // maximum. |
133 bool IncrementAuthUserIndex(GURL* url) { | 121 bool IncrementAuthUserIndex(GURL* url) { |
134 int user_index = 0; | 122 int user_index = 0; |
135 std::string old_query = url->query(); | 123 std::string old_query = url->query(); |
136 std::vector<std::string> new_query_parts; | 124 std::vector<std::string> new_query_parts; |
137 url::Component query(0, old_query.length()); | 125 url::Component query(0, old_query.length()); |
(...skipping 21 matching lines...) Expand all Loading... |
159 } | 147 } |
160 | 148 |
161 } // namespace | 149 } // namespace |
162 | 150 |
163 UpdateDetails::UpdateDetails(const std::string& id, const Version& version) | 151 UpdateDetails::UpdateDetails(const std::string& id, const Version& version) |
164 : id(id), version(version) {} | 152 : id(id), version(version) {} |
165 | 153 |
166 UpdateDetails::~UpdateDetails() {} | 154 UpdateDetails::~UpdateDetails() {} |
167 | 155 |
168 ExtensionDownloader::ExtensionFetch::ExtensionFetch() | 156 ExtensionDownloader::ExtensionFetch::ExtensionFetch() |
169 : url(), is_protected(false) {} | 157 : url(), credentials(CREDENTIALS_NONE) { |
| 158 } |
170 | 159 |
171 ExtensionDownloader::ExtensionFetch::ExtensionFetch( | 160 ExtensionDownloader::ExtensionFetch::ExtensionFetch( |
172 const std::string& id, | 161 const std::string& id, |
173 const GURL& url, | 162 const GURL& url, |
174 const std::string& package_hash, | 163 const std::string& package_hash, |
175 const std::string& version, | 164 const std::string& version, |
176 const std::set<int>& request_ids) | 165 const std::set<int>& request_ids) |
177 : id(id), url(url), package_hash(package_hash), version(version), | 166 : id(id), |
178 request_ids(request_ids), is_protected(false) {} | 167 url(url), |
| 168 package_hash(package_hash), |
| 169 version(version), |
| 170 request_ids(request_ids), |
| 171 credentials(CREDENTIALS_NONE), |
| 172 oauth2_attempt_count(0) { |
| 173 } |
179 | 174 |
180 ExtensionDownloader::ExtensionFetch::~ExtensionFetch() {} | 175 ExtensionDownloader::ExtensionFetch::~ExtensionFetch() {} |
181 | 176 |
182 ExtensionDownloader::ExtensionDownloader( | 177 ExtensionDownloader::ExtensionDownloader( |
183 ExtensionDownloaderDelegate* delegate, | 178 ExtensionDownloaderDelegate* delegate, |
184 net::URLRequestContextGetter* request_context) | 179 net::URLRequestContextGetter* request_context, |
185 : delegate_(delegate), | 180 IdentityProvider* webstore_identity_provider) |
| 181 : OAuth2TokenService::Consumer(kTokenServiceConsumerId), |
| 182 delegate_(delegate), |
186 request_context_(request_context), | 183 request_context_(request_context), |
187 weak_ptr_factory_(this), | 184 weak_ptr_factory_(this), |
188 manifests_queue_(&kDefaultBackoffPolicy, | 185 manifests_queue_(&kDefaultBackoffPolicy, |
189 base::Bind(&ExtensionDownloader::CreateManifestFetcher, | 186 base::Bind(&ExtensionDownloader::CreateManifestFetcher, |
190 base::Unretained(this))), | 187 base::Unretained(this))), |
191 extensions_queue_(&kDefaultBackoffPolicy, | 188 extensions_queue_(&kDefaultBackoffPolicy, |
192 base::Bind(&ExtensionDownloader::CreateExtensionFetcher, | 189 base::Bind(&ExtensionDownloader::CreateExtensionFetcher, |
193 base::Unretained(this))), | 190 base::Unretained(this))), |
194 extension_cache_(NULL) { | 191 extension_cache_(NULL), |
| 192 identity_provider_(webstore_identity_provider) { |
195 DCHECK(delegate_); | 193 DCHECK(delegate_); |
196 DCHECK(request_context_); | 194 DCHECK(request_context_); |
197 } | 195 } |
198 | 196 |
199 ExtensionDownloader::~ExtensionDownloader() {} | 197 ExtensionDownloader::~ExtensionDownloader() {} |
200 | 198 |
201 bool ExtensionDownloader::AddExtension(const Extension& extension, | 199 bool ExtensionDownloader::AddExtension(const Extension& extension, |
202 int request_id) { | 200 int request_id) { |
203 // Skip extensions with empty update URLs converted from user | 201 // Skip extensions with empty update URLs converted from user |
204 // scripts. | 202 // scripts. |
(...skipping 94 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
299 if (extension_urls::IsWebstoreUpdateUrl(update_url) && | 297 if (extension_urls::IsWebstoreUpdateUrl(update_url) && |
300 !update_url.SchemeIsSecure()) | 298 !update_url.SchemeIsSecure()) |
301 update_url = extension_urls::GetWebstoreUpdateUrl(); | 299 update_url = extension_urls::GetWebstoreUpdateUrl(); |
302 | 300 |
303 // Skip extensions with empty IDs. | 301 // Skip extensions with empty IDs. |
304 if (id.empty()) { | 302 if (id.empty()) { |
305 LOG(WARNING) << "Found extension with empty ID"; | 303 LOG(WARNING) << "Found extension with empty ID"; |
306 return false; | 304 return false; |
307 } | 305 } |
308 | 306 |
309 if (update_url.DomainIs("google.com")) { | 307 if (update_url.DomainIs(kGoogleDotCom)) { |
310 url_stats_.google_url_count++; | 308 url_stats_.google_url_count++; |
311 } else if (update_url.is_empty()) { | 309 } else if (update_url.is_empty()) { |
312 url_stats_.no_url_count++; | 310 url_stats_.no_url_count++; |
313 // Fill in default update URL. | 311 // Fill in default update URL. |
314 update_url = extension_urls::GetWebstoreUpdateUrl(); | 312 update_url = extension_urls::GetWebstoreUpdateUrl(); |
315 } else { | 313 } else { |
316 url_stats_.other_url_count++; | 314 url_stats_.other_url_count++; |
317 } | 315 } |
318 | 316 |
319 switch (extension_type) { | 317 switch (extension_type) { |
(...skipping 253 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
573 } | 571 } |
574 } | 572 } |
575 scoped_ptr<ExtensionFetch> fetch(new ExtensionFetch( | 573 scoped_ptr<ExtensionFetch> fetch(new ExtensionFetch( |
576 update->extension_id, crx_url, update->package_hash, | 574 update->extension_id, crx_url, update->package_hash, |
577 update->version, fetch_data.request_ids())); | 575 update->version, fetch_data.request_ids())); |
578 FetchUpdatedExtension(fetch.Pass()); | 576 FetchUpdatedExtension(fetch.Pass()); |
579 } | 577 } |
580 | 578 |
581 // If the manifest response included a <daystart> element, we want to save | 579 // 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. | 580 // that value for any extensions which had sent a ping in the request. |
583 if (fetch_data.base_url().DomainIs("google.com") && | 581 if (fetch_data.base_url().DomainIs(kGoogleDotCom) && |
584 results->daystart_elapsed_seconds >= 0) { | 582 results->daystart_elapsed_seconds >= 0) { |
585 Time day_start = | 583 Time day_start = |
586 Time::Now() - TimeDelta::FromSeconds(results->daystart_elapsed_seconds); | 584 Time::Now() - TimeDelta::FromSeconds(results->daystart_elapsed_seconds); |
587 | 585 |
588 const std::set<std::string>& extension_ids = fetch_data.extension_ids(); | 586 const std::set<std::string>& extension_ids = fetch_data.extension_ids(); |
589 std::set<std::string>::const_iterator i; | 587 std::set<std::string>::const_iterator i; |
590 for (i = extension_ids.begin(); i != extension_ids.end(); i++) { | 588 for (i = extension_ids.begin(); i != extension_ids.end(); i++) { |
591 const std::string& id = *i; | 589 const std::string& id = *i; |
592 ExtensionDownloaderDelegate::PingResult& result = ping_results_[id]; | 590 ExtensionDownloaderDelegate::PingResult& result = ping_results_[id]; |
593 result.did_ping = fetch_data.DidPing(id, ManifestFetchData::ROLLCALL); | 591 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, | 713 const base::FilePath& crx_path, |
716 bool file_ownership_passed) { | 714 bool file_ownership_passed) { |
717 delegate_->OnExtensionDownloadFinished(fetch_data->id, crx_path, | 715 delegate_->OnExtensionDownloadFinished(fetch_data->id, crx_path, |
718 file_ownership_passed, fetch_data->url, fetch_data->version, | 716 file_ownership_passed, fetch_data->url, fetch_data->version, |
719 ping_results_[fetch_data->id], fetch_data->request_ids); | 717 ping_results_[fetch_data->id], fetch_data->request_ids); |
720 ping_results_.erase(fetch_data->id); | 718 ping_results_.erase(fetch_data->id); |
721 } | 719 } |
722 | 720 |
723 void ExtensionDownloader::CreateExtensionFetcher() { | 721 void ExtensionDownloader::CreateExtensionFetcher() { |
724 const ExtensionFetch* fetch = extensions_queue_.active_request(); | 722 const ExtensionFetch* fetch = extensions_queue_.active_request(); |
| 723 extension_fetcher_.reset(net::URLFetcher::Create( |
| 724 kExtensionFetcherId, fetch->url, net::URLFetcher::GET, this)); |
| 725 extension_fetcher_->SetRequestContext(request_context_); |
| 726 extension_fetcher_->SetAutomaticallyRetryOnNetworkChanges(3); |
| 727 |
725 int load_flags = net::LOAD_DISABLE_CACHE; | 728 int load_flags = net::LOAD_DISABLE_CACHE; |
726 if (!fetch->is_protected || !fetch->url.SchemeIs("https")) { | 729 bool is_secure = fetch->url.SchemeIsSecure(); |
| 730 if (fetch->credentials != ExtensionFetch::CREDENTIALS_COOKIES || !is_secure) { |
727 load_flags |= net::LOAD_DO_NOT_SEND_COOKIES | | 731 load_flags |= net::LOAD_DO_NOT_SEND_COOKIES | |
728 net::LOAD_DO_NOT_SAVE_COOKIES; | 732 net::LOAD_DO_NOT_SAVE_COOKIES; |
729 } | 733 } |
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); | 734 extension_fetcher_->SetLoadFlags(load_flags); |
734 extension_fetcher_->SetAutomaticallyRetryOnNetworkChanges(3); | 735 |
735 // Download CRX files to a temp file. The blacklist is small and will be | 736 // 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. | 737 // processed in memory, so it is fetched into a string. |
737 if (fetch->id != kBlacklistAppID) { | 738 if (fetch->id != kBlacklistAppID) { |
738 extension_fetcher_->SaveResponseToTemporaryFile( | 739 extension_fetcher_->SaveResponseToTemporaryFile( |
739 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::FILE)); | 740 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::FILE)); |
740 } | 741 } |
741 | 742 |
| 743 if (fetch->credentials == ExtensionFetch::CREDENTIALS_OAUTH2_TOKEN && |
| 744 is_secure) { |
| 745 if (access_token_.empty()) { |
| 746 // We should try OAuth2, but we have no token cached. This |
| 747 // ExtensionFetcher will be started once the token fetch is complete, |
| 748 // in either OnTokenFetchSuccess or OnTokenFetchFailure. |
| 749 DCHECK(identity_provider_); |
| 750 OAuth2TokenService::ScopeSet webstore_scopes; |
| 751 webstore_scopes.insert(kWebstoreOAuth2Scope); |
| 752 access_token_request_ = |
| 753 identity_provider_->GetTokenService()->StartRequest( |
| 754 identity_provider_->GetActiveAccountId(), |
| 755 webstore_scopes, |
| 756 this); |
| 757 return; |
| 758 } |
| 759 extension_fetcher_->AddExtraRequestHeader( |
| 760 base::StringPrintf("%s: Bearer %s", |
| 761 net::HttpRequestHeaders::kAuthorization, |
| 762 access_token_.c_str())); |
| 763 } |
| 764 |
742 VLOG(2) << "Starting fetch of " << fetch->url << " for " << fetch->id; | 765 VLOG(2) << "Starting fetch of " << fetch->url << " for " << fetch->id; |
743 | |
744 extension_fetcher_->Start(); | 766 extension_fetcher_->Start(); |
745 } | 767 } |
746 | 768 |
747 void ExtensionDownloader::OnCRXFetchComplete( | 769 void ExtensionDownloader::OnCRXFetchComplete( |
748 const net::URLFetcher* source, | 770 const net::URLFetcher* source, |
749 const GURL& url, | 771 const GURL& url, |
750 const net::URLRequestStatus& status, | 772 const net::URLRequestStatus& status, |
751 int response_code, | 773 int response_code, |
752 const base::TimeDelta& backoff_delay) { | 774 const base::TimeDelta& backoff_delay) { |
753 const std::string& id = extensions_queue_.active_request()->id; | 775 ExtensionFetch& active_request = *extensions_queue_.active_request(); |
| 776 const std::string& id = active_request.id; |
754 if (status.status() == net::URLRequestStatus::SUCCESS && | 777 if (status.status() == net::URLRequestStatus::SUCCESS && |
755 (response_code == 200 || url.SchemeIsFile())) { | 778 (response_code == 200 || url.SchemeIsFile())) { |
756 RETRY_HISTOGRAM("CrxFetchSuccess", | 779 RETRY_HISTOGRAM("CrxFetchSuccess", |
757 extensions_queue_.active_request_failure_count(), url); | 780 extensions_queue_.active_request_failure_count(), url); |
758 base::FilePath crx_path; | 781 base::FilePath crx_path; |
759 // Take ownership of the file at |crx_path|. | 782 // Take ownership of the file at |crx_path|. |
760 CHECK(source->GetResponseAsFilePath(true, &crx_path)); | 783 CHECK(source->GetResponseAsFilePath(true, &crx_path)); |
761 scoped_ptr<ExtensionFetch> fetch_data = | 784 scoped_ptr<ExtensionFetch> fetch_data = |
762 extensions_queue_.reset_active_request(); | 785 extensions_queue_.reset_active_request(); |
763 if (extension_cache_) { | 786 if (extension_cache_) { |
764 const std::string& version = fetch_data->version; | 787 const std::string& version = fetch_data->version; |
765 extension_cache_->PutExtension(id, crx_path, version, | 788 extension_cache_->PutExtension(id, crx_path, version, |
766 base::Bind(&ExtensionDownloader::NotifyDelegateDownloadFinished, | 789 base::Bind(&ExtensionDownloader::NotifyDelegateDownloadFinished, |
767 weak_ptr_factory_.GetWeakPtr(), | 790 weak_ptr_factory_.GetWeakPtr(), |
768 base::Passed(&fetch_data))); | 791 base::Passed(&fetch_data))); |
769 } else { | 792 } else { |
770 NotifyDelegateDownloadFinished(fetch_data.Pass(), crx_path, true); | 793 NotifyDelegateDownloadFinished(fetch_data.Pass(), crx_path, true); |
771 } | 794 } |
772 } else if (ShouldRetryRequestWithCookies( | 795 } else if (IterateFetchCredentialsAfterFailure( |
773 status, | 796 &active_request, |
774 response_code, | 797 status, |
775 extensions_queue_.active_request()->is_protected)) { | 798 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); | 799 extensions_queue_.RetryRequest(backoff_delay); |
785 } else { | 800 } else { |
786 const std::set<int>& request_ids = | 801 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]; | 802 const ExtensionDownloaderDelegate::PingResult& ping = ping_results_[id]; |
789 VLOG(1) << "Failed to fetch extension '" << url.possibly_invalid_spec() | 803 VLOG(1) << "Failed to fetch extension '" << url.possibly_invalid_spec() |
790 << "' response code:" << response_code; | 804 << "' response code:" << response_code; |
791 if (ShouldRetryRequest(status, response_code) && | 805 if (ShouldRetryRequest(status, response_code) && |
792 extensions_queue_.active_request_failure_count() < kMaxRetries) { | 806 extensions_queue_.active_request_failure_count() < kMaxRetries) { |
793 extensions_queue_.RetryRequest(backoff_delay); | 807 extensions_queue_.RetryRequest(backoff_delay); |
794 } else { | 808 } else { |
795 RETRY_HISTOGRAM("CrxFetchFailure", | 809 RETRY_HISTOGRAM("CrxFetchFailure", |
796 extensions_queue_.active_request_failure_count(), url); | 810 extensions_queue_.active_request_failure_count(), url); |
797 // status.error() is 0 (net::OK) or negative. (See net/base/net_errors.h) | 811 // status.error() is 0 (net::OK) or negative. (See net/base/net_errors.h) |
(...skipping 25 matching lines...) Expand all Loading... |
823 | 837 |
824 void ExtensionDownloader::NotifyUpdateFound(const std::string& id, | 838 void ExtensionDownloader::NotifyUpdateFound(const std::string& id, |
825 const std::string& version) { | 839 const std::string& version) { |
826 UpdateDetails updateInfo(id, Version(version)); | 840 UpdateDetails updateInfo(id, Version(version)); |
827 content::NotificationService::current()->Notify( | 841 content::NotificationService::current()->Notify( |
828 extensions::NOTIFICATION_EXTENSION_UPDATE_FOUND, | 842 extensions::NOTIFICATION_EXTENSION_UPDATE_FOUND, |
829 content::NotificationService::AllBrowserContextsAndSources(), | 843 content::NotificationService::AllBrowserContextsAndSources(), |
830 content::Details<UpdateDetails>(&updateInfo)); | 844 content::Details<UpdateDetails>(&updateInfo)); |
831 } | 845 } |
832 | 846 |
| 847 bool ExtensionDownloader::IterateFetchCredentialsAfterFailure( |
| 848 ExtensionFetch* fetch, |
| 849 const net::URLRequestStatus& status, |
| 850 int response_code) { |
| 851 bool auth_failure = status.status() == net::URLRequestStatus::CANCELED || |
| 852 (status.status() == net::URLRequestStatus::SUCCESS && |
| 853 (response_code == net::HTTP_UNAUTHORIZED || |
| 854 response_code == net::HTTP_FORBIDDEN)); |
| 855 if (!auth_failure) { |
| 856 return false; |
| 857 } |
| 858 // Here we decide what to do next if the server refused to authorize this |
| 859 // fetch. |
| 860 switch (fetch->credentials) { |
| 861 case ExtensionFetch::CREDENTIALS_NONE: |
| 862 if (fetch->url.DomainIs(kGoogleDotCom) && identity_provider_) { |
| 863 fetch->credentials = ExtensionFetch::CREDENTIALS_OAUTH2_TOKEN; |
| 864 } else { |
| 865 fetch->credentials = ExtensionFetch::CREDENTIALS_COOKIES; |
| 866 } |
| 867 return true; |
| 868 case ExtensionFetch::CREDENTIALS_OAUTH2_TOKEN: |
| 869 fetch->oauth2_attempt_count++; |
| 870 // OAuth2 may fail due to an expired access token, in which case we |
| 871 // should invalidate the token and try again. |
| 872 if (response_code == net::HTTP_UNAUTHORIZED && |
| 873 fetch->oauth2_attempt_count <= kMaxOAuth2Attempts) { |
| 874 DCHECK(identity_provider_ != NULL); |
| 875 OAuth2TokenService::ScopeSet webstore_scopes; |
| 876 webstore_scopes.insert(kWebstoreOAuth2Scope); |
| 877 identity_provider_->GetTokenService()->InvalidateToken( |
| 878 identity_provider_->GetActiveAccountId(), |
| 879 webstore_scopes, |
| 880 access_token_); |
| 881 access_token_.clear(); |
| 882 return true; |
| 883 } |
| 884 // Either there is no Gaia identity available, the active identity |
| 885 // doesn't have access to this resource, or the server keeps returning |
| 886 // 401s and we've retried too many times. Fall back on cookies. |
| 887 if (access_token_.empty() || |
| 888 response_code == net::HTTP_FORBIDDEN || |
| 889 fetch->oauth2_attempt_count > kMaxOAuth2Attempts) { |
| 890 fetch->credentials = ExtensionFetch::CREDENTIALS_COOKIES; |
| 891 return true; |
| 892 } |
| 893 // Something else is wrong. Time to give up. |
| 894 return false; |
| 895 case ExtensionFetch::CREDENTIALS_COOKIES: |
| 896 if (response_code == net::HTTP_FORBIDDEN) { |
| 897 // Try the next session identity, up to some maximum. |
| 898 return IncrementAuthUserIndex(&fetch->url); |
| 899 } |
| 900 return false; |
| 901 default: |
| 902 NOTREACHED(); |
| 903 } |
| 904 NOTREACHED(); |
| 905 return false; |
| 906 } |
| 907 |
| 908 void ExtensionDownloader::OnGetTokenSuccess( |
| 909 const OAuth2TokenService::Request* request, |
| 910 const std::string& access_token, |
| 911 const base::Time& expiration_time) { |
| 912 access_token_ = access_token; |
| 913 extension_fetcher_->AddExtraRequestHeader( |
| 914 base::StringPrintf("%s: Bearer %s", |
| 915 net::HttpRequestHeaders::kAuthorization, |
| 916 access_token_.c_str())); |
| 917 extension_fetcher_->Start(); |
| 918 } |
| 919 |
| 920 void ExtensionDownloader::OnGetTokenFailure( |
| 921 const OAuth2TokenService::Request* request, |
| 922 const GoogleServiceAuthError& error) { |
| 923 // If we fail to get an access token, kick the pending fetch and let it fall |
| 924 // back on cookies. |
| 925 extension_fetcher_->Start(); |
| 926 } |
| 927 |
833 } // namespace extensions | 928 } // namespace extensions |
OLD | NEW |