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

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

Powered by Google App Engine
This is Rietveld 408576698