Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1584)

Side by Side Diff: components/ntp_snippets/ntp_snippets_fetcher.cc

Issue 1922083004: Allow fetching personalized snippets from ChromeReader. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Rebase update Created 4 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2016 The Chromium Authors. All rights reserved. 1 // Copyright 2016 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 "components/ntp_snippets/ntp_snippets_fetcher.h" 5 #include "components/ntp_snippets/ntp_snippets_fetcher.h"
6 6
7 #include <stdlib.h>
8
7 #include "base/files/file_path.h" 9 #include "base/files/file_path.h"
8 #include "base/files/file_util.h" 10 #include "base/files/file_util.h"
9 #include "base/metrics/sparse_histogram.h" 11 #include "base/metrics/sparse_histogram.h"
10 #include "base/path_service.h" 12 #include "base/path_service.h"
11 #include "base/strings/string_number_conversions.h" 13 #include "base/strings/string_number_conversions.h"
12 #include "base/strings/string_util.h" 14 #include "base/strings/string_util.h"
13 #include "base/strings/stringprintf.h" 15 #include "base/strings/stringprintf.h"
14 #include "components/data_use_measurement/core/data_use_user_data.h" 16 #include "components/data_use_measurement/core/data_use_user_data.h"
17 #include "components/ntp_snippets/ntp_snippets_constants.h"
18 #include "components/signin/core/browser/profile_oauth2_token_service.h"
19 #include "components/signin/core/browser/signin_manager.h"
20 #include "components/signin/core/browser/signin_manager_base.h"
21 #include "components/signin/core/browser/signin_tracker.h"
22 #include "components/variations/variations_associated_data.h"
15 #include "google_apis/google_api_keys.h" 23 #include "google_apis/google_api_keys.h"
16 #include "net/base/load_flags.h" 24 #include "net/base/load_flags.h"
17 #include "net/http/http_request_headers.h" 25 #include "net/http/http_request_headers.h"
18 #include "net/http/http_response_headers.h" 26 #include "net/http/http_response_headers.h"
19 #include "net/http/http_status_code.h" 27 #include "net/http/http_status_code.h"
20 #include "net/url_request/url_fetcher.h" 28 #include "net/url_request/url_fetcher.h"
29 #include "third_party/icu/source/common/unicode/uloc.h"
30 #include "third_party/icu/source/common/unicode/utypes.h"
21 31
22 using net::URLFetcher; 32 using net::URLFetcher;
23 using net::URLRequestContextGetter; 33 using net::URLRequestContextGetter;
24 using net::HttpRequestHeaders; 34 using net::HttpRequestHeaders;
25 using net::URLRequestStatus; 35 using net::URLRequestStatus;
26 36
27 namespace ntp_snippets { 37 namespace ntp_snippets {
28 38
29 namespace { 39 namespace {
30 40
41 const char kApiScope[] = "https://www.googleapis.com/auth/webhistory";
42
31 const char kStatusMessageURLRequestErrorFormat[] = "URLRequestStatus error %d"; 43 const char kStatusMessageURLRequestErrorFormat[] = "URLRequestStatus error %d";
32 const char kStatusMessageHTTPErrorFormat[] = "HTTP error %d"; 44 const char kStatusMessageHTTPErrorFormat[] = "HTTP error %d";
33 45
34 const char kContentSnippetsServerFormat[] = 46 const char kSnippetsServerAuthorized[] =
47 "https://chromereader-pa.googleapis.com/v1/fetch";
48 const char kSnippetsServerNonAuthorizedFormat[] =
35 "https://chromereader-pa.googleapis.com/v1/fetch?key=%s"; 49 "https://chromereader-pa.googleapis.com/v1/fetch?key=%s";
50 const char kAuthorizationRequestHeaderFormat[] = "Bearer %s";
51
52 // Variation parameter for the variant of fetching to use.
53 const char kVariantName[] = "fetching_variant";
54
55 // Constants listing possible values of the "variant" parameter.
56 const char kVariantRestrictedString[] = "restricted";
57 const char kVariantPersonalizedString[] = "personalized";
58 const char kVariantRestrictedPersonalizedString[] = "restricted_personalized";
36 59
37 const char kRequestParameterFormat[] = 60 const char kRequestParameterFormat[] =
38 "{" 61 "{"
39 " \"response_detail_level\": \"STANDARD\"," 62 " \"response_detail_level\": \"STANDARD\","
63 "%s"
40 " \"advanced_options\": {" 64 " \"advanced_options\": {"
41 " \"local_scoring_params\": {" 65 " \"local_scoring_params\": {"
42 " \"content_params\": {" 66 " \"content_params\": {"
43 " \"only_return_personalized_results\": false" 67 " \"only_return_personalized_results\": false"
68 "%s"
44 " }," 69 " },"
45 " \"content_restricts\": {" 70 " \"content_restricts\": {"
46 " \"type\": \"METADATA\"," 71 " \"type\": \"METADATA\","
47 " \"value\": \"TITLE\"" 72 " \"value\": \"TITLE\""
48 " }," 73 " },"
49 " \"content_restricts\": {" 74 " \"content_restricts\": {"
50 " \"type\": \"METADATA\"," 75 " \"type\": \"METADATA\","
51 " \"value\": \"SNIPPET\"" 76 " \"value\": \"SNIPPET\""
52 " }," 77 " },"
53 " \"content_restricts\": {" 78 " \"content_restricts\": {"
54 " \"type\": \"METADATA\"," 79 " \"type\": \"METADATA\","
55 " \"value\": \"THUMBNAIL\"" 80 " \"value\": \"THUMBNAIL\""
56 " }" 81 " }"
57 "%s" 82 "%s"
58 " }," 83 " },"
59 " \"global_scoring_params\": {" 84 " \"global_scoring_params\": {"
60 " \"num_to_return\": %i" 85 " \"num_to_return\": %i,"
86 " \"sort_type\": 1"
61 " }" 87 " }"
62 " }" 88 " }"
63 "}"; 89 "}";
64 90
91 const char kAuthorizationFormat[] = " \"obfuscated_gaia_id\": \"%s\",";
92 const char kUserSegmentFormat[] = " \"user_segment\": \"%s\"";
65 const char kHostRestrictFormat[] = 93 const char kHostRestrictFormat[] =
66 " ,\"content_selectors\": {" 94 " ,\"content_selectors\": {"
67 " \"type\": \"HOST_RESTRICT\"," 95 " \"type\": \"HOST_RESTRICT\","
68 " \"value\": \"%s\"" 96 " \"value\": \"%s\""
69 " }"; 97 " }";
70 98
71 } // namespace 99 } // namespace
72 100
73 NTPSnippetsFetcher::NTPSnippetsFetcher( 101 NTPSnippetsFetcher::NTPSnippetsFetcher(
102 SigninManagerBase* signin_manager,
103 OAuth2TokenService* token_service,
74 scoped_refptr<URLRequestContextGetter> url_request_context_getter, 104 scoped_refptr<URLRequestContextGetter> url_request_context_getter,
75 bool is_stable_channel) 105 bool is_stable_channel)
76 : url_request_context_getter_(url_request_context_getter), 106 : OAuth2TokenService::Consumer("NTP_snippets"),
77 is_stable_channel_(is_stable_channel) {} 107 url_request_context_getter_(url_request_context_getter),
108 signin_manager_(signin_manager),
109 token_service_(token_service),
110 waiting_for_refresh_token_(false),
111 is_stable_channel_(is_stable_channel) {
112 // Parse the variation parameters and set the defaults if missing.
113 std::string variant = variations::GetVariationParamValue(
114 ntp_snippets::kStudyName, kVariantName);
115 if (variant == kVariantRestrictedString)
116 variant_ = kRestricted;
117 else if (variant == kVariantPersonalizedString)
118 variant_ = kPersonalized;
119 else
120 variant_ = kRestrictedPersonalized;
121 }
78 122
79 NTPSnippetsFetcher::~NTPSnippetsFetcher() {} 123 NTPSnippetsFetcher::~NTPSnippetsFetcher() {
124 if (waiting_for_refresh_token_)
125 token_service_->RemoveObserver(this);
126 }
80 127
81 std::unique_ptr<NTPSnippetsFetcher::SnippetsAvailableCallbackList::Subscription> 128 std::unique_ptr<NTPSnippetsFetcher::SnippetsAvailableCallbackList::Subscription>
82 NTPSnippetsFetcher::AddCallback(const SnippetsAvailableCallback& callback) { 129 NTPSnippetsFetcher::AddCallback(const SnippetsAvailableCallback& callback) {
83 return callback_list_.Add(callback); 130 return callback_list_.Add(callback);
84 } 131 }
85 132
86 void NTPSnippetsFetcher::FetchSnippets(const std::set<std::string>& hosts, 133 void NTPSnippetsFetcher::FetchSnippets(const std::set<std::string>& hosts,
134 const std::string& language_code,
87 int count) { 135 int count) {
88 const std::string& key = is_stable_channel_ 136 hosts_ = hosts;
89 ? google_apis::GetAPIKey() 137
90 : google_apis::GetNonStableAPIKey(); 138 // Translate the BCP 47 |language_code| into a posix locale string
91 std::string url = 139 char locale[ULOC_FULLNAME_CAPACITY];
92 base::StringPrintf(kContentSnippetsServerFormat, key.c_str()); 140 UErrorCode error;
93 url_fetcher_ = URLFetcher::Create(GURL(url), URLFetcher::POST, this); 141 uloc_forLanguageTag(language_code.c_str(), locale, ULOC_FULLNAME_CAPACITY,
142 nullptr, &error);
143 DLOG_IF(WARNING, U_ZERO_ERROR != error) <<
144 "Error in translating language code to a locale string: " << error;
145 locale_ = locale;
146
147 count_ = count;
148
149 if (UseAuthentication()) {
150 if (signin_manager_->IsAuthenticated()) {
151 // Signed-in: get OAuth token --> fetch snippets.
152 StartTokenRequest();
153 } else if (signin_manager_->AuthInProgress()) {
154 // Currently signing in: wait for auth to finish (the refresh token) -->
155 // get OAuth token --> fetch snippets.
156 if (!waiting_for_refresh_token_) {
157 // Wait until we get a refresh token.
158 waiting_for_refresh_token_ = true;
159 token_service_->AddObserver(this);
160 }
161 }
162 } else {
163 // Not signed in: fetch snippets (without authentication).
164 FetchSnippetsNonAuthenticated();
165 }
166 }
167
168 void NTPSnippetsFetcher::FetchSnippetsImpl(const GURL& url,
169 const std::string& auth_header,
170 const std::string& request) {
171 url_fetcher_ = URLFetcher::Create(url, URLFetcher::POST, this);
172
94 url_fetcher_->SetRequestContext(url_request_context_getter_.get()); 173 url_fetcher_->SetRequestContext(url_request_context_getter_.get());
95 url_fetcher_->SetLoadFlags(net::LOAD_DO_NOT_SEND_COOKIES | 174 url_fetcher_->SetLoadFlags(net::LOAD_DO_NOT_SEND_COOKIES |
96 net::LOAD_DO_NOT_SAVE_COOKIES); 175 net::LOAD_DO_NOT_SAVE_COOKIES);
176
97 data_use_measurement::DataUseUserData::AttachToFetcher( 177 data_use_measurement::DataUseUserData::AttachToFetcher(
98 url_fetcher_.get(), data_use_measurement::DataUseUserData::NTP_SNIPPETS); 178 url_fetcher_.get(), data_use_measurement::DataUseUserData::NTP_SNIPPETS);
179
99 HttpRequestHeaders headers; 180 HttpRequestHeaders headers;
181 if (!auth_header.empty())
182 headers.SetHeader("Authorization", auth_header);
100 headers.SetHeader("Content-Type", "application/json; charset=UTF-8"); 183 headers.SetHeader("Content-Type", "application/json; charset=UTF-8");
101 url_fetcher_->SetExtraRequestHeaders(headers.ToString()); 184 url_fetcher_->SetExtraRequestHeaders(headers.ToString());
102 std::string host_restricts; 185 url_fetcher_->SetUploadData("application/json", request);
103 for (const std::string& host : hosts)
104 host_restricts += base::StringPrintf(kHostRestrictFormat, host.c_str());
105 url_fetcher_->SetUploadData("application/json",
106 base::StringPrintf(kRequestParameterFormat,
107 host_restricts.c_str(),
108 count));
109
110 // Fetchers are sometimes cancelled because a network change was detected. 186 // Fetchers are sometimes cancelled because a network change was detected.
111 url_fetcher_->SetAutomaticallyRetryOnNetworkChanges(3); 187 url_fetcher_->SetAutomaticallyRetryOnNetworkChanges(3);
112 // Try to make fetching the files bit more robust even with poor connection. 188 // Try to make fetching the files bit more robust even with poor connection.
113 url_fetcher_->SetMaxRetriesOn5xx(3); 189 url_fetcher_->SetMaxRetriesOn5xx(3);
114 url_fetcher_->Start(); 190 url_fetcher_->Start();
115 } 191 }
116 192
193 std::string NTPSnippetsFetcher::GetHostsRestricts() const {
194 std::string host_restricts;
195 if (variant_ == kRestricted || variant_ == kRestrictedPersonalized) {
196 for (const std::string& host : hosts_)
197 host_restricts += base::StringPrintf(kHostRestrictFormat, host.c_str());
198 }
199 return host_restricts;
200 }
201
202 bool NTPSnippetsFetcher::UseAuthentication() {
203 return (variant_ == kPersonalized ||
204 (variant_ == kRestrictedPersonalized && !hosts_.empty())) &&
205 (signin_manager_->IsAuthenticated() || signin_manager_->AuthInProgress());
206 }
207
208 void NTPSnippetsFetcher::FetchSnippetsNonAuthenticated() {
209 // When not providing OAuth token, we need to pass the Google API key
210 const std::string& key = is_stable_channel_
211 ? google_apis::GetAPIKey()
212 : google_apis::GetNonStableAPIKey();
213 GURL url(base::StringPrintf(kSnippetsServerNonAuthorizedFormat, key.c_str()));
214
215 FetchSnippetsImpl(url, std::string(),
216 base::StringPrintf(kRequestParameterFormat, "", "",
217 GetHostsRestricts().c_str(),
218 count_));
219 }
220
221 void NTPSnippetsFetcher::FetchSnippetsAuthenticated(
222 const std::string& account_id,
223 const std::string& oauth_access_token) {
224 std::string auth =
225 base::StringPrintf(kAuthorizationFormat, account_id.c_str());
226 std::string user_segment =
227 base::StringPrintf(kUserSegmentFormat, locale_.c_str());
228
229 FetchSnippetsImpl(GURL(kSnippetsServerAuthorized),
230 base::StringPrintf(kAuthorizationRequestHeaderFormat,
231 oauth_access_token.c_str()),
232 base::StringPrintf(kRequestParameterFormat,
233 auth.c_str(),
234 user_segment.c_str(),
235 GetHostsRestricts().c_str(),
236 count_));
237 }
238
239 void NTPSnippetsFetcher::StartTokenRequest() {
240 OAuth2TokenService::ScopeSet scopes;
241 scopes.insert(kApiScope);
242 oauth_request_ = token_service_->StartRequest(
243 signin_manager_->GetAuthenticatedAccountId(), scopes, this);
244 }
245
246 ////////////////////////////////////////////////////////////////////////////////
247 // OAuth2TokenService::Consumer overrides
248 void NTPSnippetsFetcher::OnGetTokenSuccess(
249 const OAuth2TokenService::Request* request,
250 const std::string& access_token,
251 const base::Time& expiration_time) {
252 oauth_request_.reset();
253
254 FetchSnippetsAuthenticated(request->GetAccountId(), access_token);
255 }
256
257 void NTPSnippetsFetcher::OnGetTokenFailure(
258 const OAuth2TokenService::Request* request,
259 const GoogleServiceAuthError& error) {
260 oauth_request_.reset();
261 DLOG(ERROR) << "Unable to get token: " << error.ToString()
262 << " - fetching the snippets without authentication.";
263
264 FetchSnippetsNonAuthenticated();
265 }
266
267 ////////////////////////////////////////////////////////////////////////////////
268 // OAuth2TokenService::Observer overrides
269 void NTPSnippetsFetcher::OnRefreshTokenAvailable(
270 const std::string& account_id) {
271 token_service_->RemoveObserver(this);
272 waiting_for_refresh_token_ = false;
273 StartTokenRequest();
274 }
275
117 //////////////////////////////////////////////////////////////////////////////// 276 ////////////////////////////////////////////////////////////////////////////////
118 // URLFetcherDelegate overrides 277 // URLFetcherDelegate overrides
119 void NTPSnippetsFetcher::OnURLFetchComplete(const URLFetcher* source) { 278 void NTPSnippetsFetcher::OnURLFetchComplete(const URLFetcher* source) {
120 DCHECK_EQ(url_fetcher_.get(), source); 279 DCHECK_EQ(url_fetcher_.get(), source);
121 280
122 std::string message; 281 std::string message;
123 const URLRequestStatus& status = source->GetStatus(); 282 const URLRequestStatus& status = source->GetStatus();
124 283
125 UMA_HISTOGRAM_SPARSE_SLOWLY( 284 UMA_HISTOGRAM_SPARSE_SLOWLY(
126 "NewTabPage.Snippets.FetchHttpResponseOrErrorCode", 285 "NewTabPage.Snippets.FetchHttpResponseOrErrorCode",
127 status.is_success() ? source->GetResponseCode() : status.error()); 286 status.is_success() ? source->GetResponseCode() : status.error());
128 287
129 if (!status.is_success()) { 288 if (!status.is_success()) {
130 message = base::StringPrintf(kStatusMessageURLRequestErrorFormat, 289 message = base::StringPrintf(kStatusMessageURLRequestErrorFormat,
131 status.error()); 290 status.error());
132 } else if (source->GetResponseCode() != net::HTTP_OK) { 291 } else if (source->GetResponseCode() != net::HTTP_OK) {
292 // TODO(jkrcal): https://crbug.com/609084
293 // We need to deal with the edge case again where the auth
294 // token expires just before we send the request (in which case we need to
295 // fetch a new auth token). We should extract that into a common class
296 // instead of adding it to every single class that uses auth tokens.
133 message = base::StringPrintf(kStatusMessageHTTPErrorFormat, 297 message = base::StringPrintf(kStatusMessageHTTPErrorFormat,
134 source->GetResponseCode()); 298 source->GetResponseCode());
135 } 299 }
136 300
137 std::string response; 301 std::string response;
138 if (!message.empty()) { 302 if (!message.empty()) {
303 std::string error_response;
304 source->GetResponseAsString(&error_response);
139 DLOG(WARNING) << message << " while trying to download " 305 DLOG(WARNING) << message << " while trying to download "
140 << source->GetURL().spec(); 306 << source->GetURL().spec() << ": "
307 << error_response;
Bernhard Bauer 2016/05/04 15:56:36 This seems like it should fit on the previous line
jkrcal 2016/05/09 11:58:06 Done.
141 308
142 } else { 309 } else {
143 bool stores_result_to_string = source->GetResponseAsString(&response); 310 bool stores_result_to_string = source->GetResponseAsString(&response);
144 DCHECK(stores_result_to_string); 311 DCHECK(stores_result_to_string);
145 } 312 }
146 313
147 callback_list_.Notify(response, message); 314 callback_list_.Notify(response, message);
148 } 315 }
149 316
150 } // namespace ntp_snippets 317 } // namespace ntp_snippets
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698