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

Side by Side Diff: chrome/browser/chromeos/gdata/operations_base.cc

Issue 10913184: Move basic operation components for Drive API to chrome/browser/google_apis directory (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Rebase Created 8 years, 3 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 | Annotate | Revision Log
OLDNEW
(Empty)
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
3 // found in the LICENSE file.
4
5 #include "chrome/browser/chromeos/gdata/operations_base.h"
6
7 #include "base/json/json_reader.h"
8 #include "base/metrics/histogram.h"
9 #include "base/string_number_conversions.h"
10 #include "base/stringprintf.h"
11 #include "base/values.h"
12 #include "chrome/browser/browser_process.h"
13 #include "chrome/common/net/url_util.h"
14 #include "content/public/browser/browser_thread.h"
15 #include "google_apis/gaia/gaia_urls.h"
16 #include "google_apis/gaia/google_service_auth_error.h"
17 #include "google_apis/gaia/oauth2_access_token_fetcher.h"
18 #include "net/base/load_flags.h"
19 #include "net/http/http_util.h"
20 #include "net/url_request/url_fetcher.h"
21 #include "net/url_request/url_request_status.h"
22
23 using content::BrowserThread;
24 using net::URLFetcher;
25
26 namespace {
27
28 // Used for success ratio histograms. 0 for failure, 1 for success,
29 // 2 for no connection (likely offline).
30 const int kSuccessRatioHistogramFailure = 0;
31 const int kSuccessRatioHistogramSuccess = 1;
32 const int kSuccessRatioHistogramNoConnection = 2;
33 const int kSuccessRatioHistogramMaxValue = 3; // The max value is exclusive.
34
35 // Template for optional OAuth2 authorization HTTP header.
36 const char kAuthorizationHeaderFormat[] = "Authorization: Bearer %s";
37 // Template for GData API version HTTP header.
38 const char kGDataVersionHeader[] = "GData-Version: 3.0";
39
40 // Maximum number of attempts for re-authentication per operation.
41 const int kMaxReAuthenticateAttemptsPerOperation = 1;
42
43 // Parse JSON string to base::Value object.
44 void ParseJsonOnBlockingPool(const std::string& data,
45 scoped_ptr<base::Value>* value) {
46 DCHECK(!BrowserThread::CurrentlyOn(BrowserThread::UI));
47
48 int error_code = -1;
49 std::string error_message;
50 value->reset(base::JSONReader::ReadAndReturnError(data,
51 base::JSON_PARSE_RFC,
52 &error_code,
53 &error_message));
54
55 if (!value->get()) {
56 LOG(ERROR) << "Error while parsing entry response: "
57 << error_message
58 << ", code: "
59 << error_code
60 << ", data:\n"
61 << data;
62 }
63 }
64
65 } // namespace
66
67 namespace gdata {
68
69 //================================ AuthOperation ===============================
70
71 AuthOperation::AuthOperation(OperationRegistry* registry,
72 const AuthStatusCallback& callback,
73 const std::vector<std::string>& scopes,
74 const std::string& refresh_token)
75 : OperationRegistry::Operation(registry),
76 refresh_token_(refresh_token),
77 callback_(callback),
78 scopes_(scopes) {
79 }
80
81 AuthOperation::~AuthOperation() {}
82
83 void AuthOperation::Start() {
84 DCHECK(!refresh_token_.empty());
85 oauth2_access_token_fetcher_.reset(new OAuth2AccessTokenFetcher(
86 this, g_browser_process->system_request_context()));
87 NotifyStart();
88 oauth2_access_token_fetcher_->Start(
89 GaiaUrls::GetInstance()->oauth2_chrome_client_id(),
90 GaiaUrls::GetInstance()->oauth2_chrome_client_secret(),
91 refresh_token_,
92 scopes_);
93 }
94
95 void AuthOperation::DoCancel() {
96 oauth2_access_token_fetcher_->CancelRequest();
97 if (!callback_.is_null())
98 callback_.Run(GDATA_CANCELLED, std::string());
99 }
100
101 // Callback for OAuth2AccessTokenFetcher on success. |access_token| is the token
102 // used to start fetching user data.
103 void AuthOperation::OnGetTokenSuccess(const std::string& access_token,
104 const base::Time& expiration_time) {
105 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
106
107 UMA_HISTOGRAM_ENUMERATION("GData.AuthSuccess",
108 kSuccessRatioHistogramSuccess,
109 kSuccessRatioHistogramMaxValue);
110
111 callback_.Run(HTTP_SUCCESS, access_token);
112 NotifyFinish(OperationRegistry::OPERATION_COMPLETED);
113 }
114
115 // Callback for OAuth2AccessTokenFetcher on failure.
116 void AuthOperation::OnGetTokenFailure(const GoogleServiceAuthError& error) {
117 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
118
119 LOG(WARNING) << "AuthOperation: token request using refresh token failed"
120 << error.ToString();
121
122 // There are many ways to fail, but if the failure is due to connection,
123 // it's likely that the device is off-line. We treat the error differently
124 // so that the file manager works while off-line.
125 if (error.state() == GoogleServiceAuthError::CONNECTION_FAILED) {
126 UMA_HISTOGRAM_ENUMERATION("GData.AuthSuccess",
127 kSuccessRatioHistogramNoConnection,
128 kSuccessRatioHistogramMaxValue);
129 callback_.Run(GDATA_NO_CONNECTION, std::string());
130 } else {
131 UMA_HISTOGRAM_ENUMERATION("GData.AuthSuccess",
132 kSuccessRatioHistogramFailure,
133 kSuccessRatioHistogramMaxValue);
134 callback_.Run(HTTP_UNAUTHORIZED, std::string());
135 }
136 NotifyFinish(OperationRegistry::OPERATION_FAILED);
137 }
138
139 //============================ UrlFetchOperationBase ===========================
140
141 UrlFetchOperationBase::UrlFetchOperationBase(OperationRegistry* registry)
142 : OperationRegistry::Operation(registry),
143 re_authenticate_count_(0),
144 save_temp_file_(false),
145 started_(false) {
146 }
147
148 UrlFetchOperationBase::UrlFetchOperationBase(
149 OperationRegistry* registry,
150 OperationRegistry::OperationType type,
151 const FilePath& path)
152 : OperationRegistry::Operation(registry, type, path),
153 re_authenticate_count_(0),
154 save_temp_file_(false) {
155 }
156
157 UrlFetchOperationBase::~UrlFetchOperationBase() {}
158
159 void UrlFetchOperationBase::Start(const std::string& auth_token) {
160 DCHECK(!auth_token.empty());
161
162 GURL url = GetURL();
163 DCHECK(!url.is_empty());
164 DVLOG(1) << "URL: " << url.spec();
165
166 url_fetcher_.reset(
167 URLFetcher::Create(url, GetRequestType(), this));
168 url_fetcher_->SetRequestContext(g_browser_process->system_request_context());
169 // Always set flags to neither send nor save cookies.
170 url_fetcher_->SetLoadFlags(
171 net::LOAD_DO_NOT_SEND_COOKIES | net::LOAD_DO_NOT_SAVE_COOKIES |
172 net::LOAD_DISABLE_CACHE);
173 if (save_temp_file_) {
174 url_fetcher_->SaveResponseToTemporaryFile(
175 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::FILE));
176 } else if (!output_file_path_.empty()) {
177 url_fetcher_->SaveResponseToFileAtPath(output_file_path_,
178 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::FILE));
179 }
180
181 // Add request headers.
182 // Note that SetExtraRequestHeaders clears the current headers and sets it
183 // to the passed-in headers, so calling it for each header will result in
184 // only the last header being set in request headers.
185 url_fetcher_->AddExtraRequestHeader(kGDataVersionHeader);
186 url_fetcher_->AddExtraRequestHeader(
187 base::StringPrintf(kAuthorizationHeaderFormat, auth_token.data()));
188 std::vector<std::string> headers = GetExtraRequestHeaders();
189 for (size_t i = 0; i < headers.size(); ++i) {
190 url_fetcher_->AddExtraRequestHeader(headers[i]);
191 DVLOG(1) << "Extra header: " << headers[i];
192 }
193
194 // Set upload data if available.
195 std::string upload_content_type;
196 std::string upload_content;
197 if (GetContentData(&upload_content_type, &upload_content)) {
198 url_fetcher_->SetUploadData(upload_content_type, upload_content);
199 }
200
201 // Register to operation registry.
202 NotifyStartToOperationRegistry();
203
204 url_fetcher_->Start();
205 started_ = true;
206 }
207
208 void UrlFetchOperationBase::SetReAuthenticateCallback(
209 const ReAuthenticateCallback& callback) {
210 DCHECK(re_authenticate_callback_.is_null());
211
212 re_authenticate_callback_ = callback;
213 }
214
215 URLFetcher::RequestType UrlFetchOperationBase::GetRequestType() const {
216 return URLFetcher::GET;
217 }
218
219 std::vector<std::string> UrlFetchOperationBase::GetExtraRequestHeaders() const {
220 return std::vector<std::string>();
221 }
222
223 bool UrlFetchOperationBase::GetContentData(std::string* upload_content_type,
224 std::string* upload_content) {
225 return false;
226 }
227
228 void UrlFetchOperationBase::DoCancel() {
229 url_fetcher_.reset(NULL);
230 RunCallbackOnPrematureFailure(GDATA_CANCELLED);
231 }
232
233 GDataErrorCode UrlFetchOperationBase::GetErrorCode(
234 const URLFetcher* source) const {
235 GDataErrorCode code = static_cast<GDataErrorCode>(source->GetResponseCode());
236 if (code == HTTP_SUCCESS && !source->GetStatus().is_success()) {
237 // If the HTTP response code is SUCCESS yet the URL request failed, it is
238 // likely that the failure is due to loss of connection.
239 code = GDATA_NO_CONNECTION;
240 }
241 return code;
242 }
243
244 void UrlFetchOperationBase::OnProcessURLFetchResultsComplete(bool result) {
245 if (result)
246 NotifySuccessToOperationRegistry();
247 else
248 NotifyFinish(OperationRegistry::OPERATION_FAILED);
249 }
250
251 void UrlFetchOperationBase::OnURLFetchComplete(const URLFetcher* source) {
252 GDataErrorCode code = GetErrorCode(source);
253 DVLOG(1) << "Response headers:\n" << GetResponseHeadersAsString(source);
254
255 if (code == HTTP_UNAUTHORIZED) {
256 if (!re_authenticate_callback_.is_null() &&
257 ++re_authenticate_count_ <= kMaxReAuthenticateAttemptsPerOperation) {
258 re_authenticate_callback_.Run(this);
259 return;
260 }
261
262 OnAuthFailed(code);
263 return;
264 }
265
266 // Overridden by each specialization
267 ProcessURLFetchResults(source);
268 }
269
270 void UrlFetchOperationBase::NotifySuccessToOperationRegistry() {
271 NotifyFinish(OperationRegistry::OPERATION_COMPLETED);
272 }
273
274 void UrlFetchOperationBase::NotifyStartToOperationRegistry() {
275 NotifyStart();
276 }
277
278 void UrlFetchOperationBase::OnAuthFailed(GDataErrorCode code) {
279 RunCallbackOnPrematureFailure(code);
280
281 // Notify authentication failed.
282 NotifyAuthFailed();
283
284 // Check if this failed before we even started fetching. If so, register
285 // for start so we can properly unregister with finish.
286 if (!started_)
287 NotifyStart();
288
289 // Note: NotifyFinish() must be invoked at the end, after all other callbacks
290 // and notifications. Once NotifyFinish() is called, the current instance of
291 // gdata operation will be deleted from the OperationRegistry and become
292 // invalid.
293 NotifyFinish(OperationRegistry::OPERATION_FAILED);
294 }
295
296 std::string UrlFetchOperationBase::GetResponseHeadersAsString(
297 const URLFetcher* url_fetcher) {
298 // net::HttpResponseHeaders::raw_headers(), as the name implies, stores
299 // all headers in their raw format, i.e each header is null-terminated.
300 // So logging raw_headers() only shows the first header, which is probably
301 // the status line. GetNormalizedHeaders, on the other hand, will show all
302 // the headers, one per line, which is probably what we want.
303 std::string headers;
304 // Check that response code indicates response headers are valid (i.e. not
305 // malformed) before we retrieve the headers.
306 if (url_fetcher->GetResponseCode() == URLFetcher::RESPONSE_CODE_INVALID) {
307 headers.assign("Response headers are malformed!!");
308 } else {
309 url_fetcher->GetResponseHeaders()->GetNormalizedHeaders(&headers);
310 }
311 return headers;
312 }
313
314 //============================ EntryActionOperation ============================
315
316 EntryActionOperation::EntryActionOperation(OperationRegistry* registry,
317 const EntryActionCallback& callback,
318 const GURL& document_url)
319 : UrlFetchOperationBase(registry),
320 callback_(callback),
321 document_url_(document_url) {
322 }
323
324 EntryActionOperation::~EntryActionOperation() {}
325
326 void EntryActionOperation::ProcessURLFetchResults(const URLFetcher* source) {
327 if (!callback_.is_null()) {
328 GDataErrorCode code = GetErrorCode(source);
329 callback_.Run(code, document_url_);
330 }
331 const bool success = true;
332 OnProcessURLFetchResultsComplete(success);
333 }
334
335 void EntryActionOperation::RunCallbackOnPrematureFailure(GDataErrorCode code) {
336 if (!callback_.is_null())
337 callback_.Run(code, document_url_);
338 }
339
340 //============================== GetDataOperation ==============================
341
342 GetDataOperation::GetDataOperation(OperationRegistry* registry,
343 const GetDataCallback& callback)
344 : UrlFetchOperationBase(registry),
345 callback_(callback),
346 ALLOW_THIS_IN_INITIALIZER_LIST(weak_ptr_factory_(this)) {
347 }
348
349 GetDataOperation::~GetDataOperation() {}
350
351 void GetDataOperation::ProcessURLFetchResults(const URLFetcher* source) {
352 std::string data;
353 source->GetResponseAsString(&data);
354 scoped_ptr<base::Value> root_value;
355 GDataErrorCode fetch_error_code = GetErrorCode(source);
356
357 switch (fetch_error_code) {
358 case HTTP_SUCCESS:
359 case HTTP_CREATED:
360 ParseResponse(fetch_error_code, data);
361 break;
362 default:
363 RunCallback(fetch_error_code, scoped_ptr<base::Value>());
364 const bool success = false;
365 OnProcessURLFetchResultsComplete(success);
366 break;
367 }
368 }
369
370 void GetDataOperation::RunCallbackOnPrematureFailure(
371 GDataErrorCode fetch_error_code) {
372 if (!callback_.is_null()) {
373 scoped_ptr<base::Value> root_value;
374 callback_.Run(fetch_error_code, root_value.Pass());
375 }
376 }
377
378 void GetDataOperation::ParseResponse(GDataErrorCode fetch_error_code,
379 const std::string& data) {
380 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
381
382 // Uses this hack to avoid deep-copy of json object because json might be so
383 // big. This pointer of scped_ptr is to ensure a deletion of the parsed json
384 // value object.
385 scoped_ptr<base::Value>* parsed_value = new scoped_ptr<base::Value>();
386
387 BrowserThread::PostBlockingPoolTaskAndReply(
388 FROM_HERE,
389 base::Bind(&ParseJsonOnBlockingPool,
390 data,
391 parsed_value),
392 base::Bind(&GetDataOperation::OnDataParsed,
393 weak_ptr_factory_.GetWeakPtr(),
394 fetch_error_code,
395 base::Owned(parsed_value)));
396 }
397
398 void GetDataOperation::OnDataParsed(
399 gdata::GDataErrorCode fetch_error_code,
400 scoped_ptr<base::Value>* value) {
401 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
402
403 bool success = true;
404 if (!value->get()) {
405 fetch_error_code = gdata::GDATA_PARSE_ERROR;
406 success = false;
407 }
408
409 // The ownership of the parsed json object is transfered to RunCallBack(),
410 // keeping the ownership of the |value| here.
411 RunCallback(fetch_error_code, value->Pass());
412
413 DCHECK(!value->get());
414
415 OnProcessURLFetchResultsComplete(success);
416 // |value| will be deleted after return because it is base::Owned()'d.
417 }
418
419 void GetDataOperation::RunCallback(GDataErrorCode fetch_error_code,
420 scoped_ptr<base::Value> value) {
421 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
422 if (!callback_.is_null())
423 callback_.Run(fetch_error_code, value.Pass());
424 }
425
426 } // namespace gdata
OLDNEW
« no previous file with comments | « chrome/browser/chromeos/gdata/operations_base.h ('k') | chrome/browser/chromeos/gdata/task_util.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698