OLD | NEW |
| (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/google_apis/base_operations.h" | |
6 | |
7 #include "base/json/json_reader.h" | |
8 #include "base/stringprintf.h" | |
9 #include "base/strings/string_number_conversions.h" | |
10 #include "base/task_runner_util.h" | |
11 #include "base/threading/sequenced_worker_pool.h" | |
12 #include "base/values.h" | |
13 #include "chrome/browser/google_apis/operation_runner.h" | |
14 #include "content/public/browser/browser_thread.h" | |
15 #include "net/base/load_flags.h" | |
16 #include "net/base/net_errors.h" | |
17 #include "net/http/http_byte_range.h" | |
18 #include "net/http/http_response_headers.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 // Template for optional OAuth2 authorization HTTP header. | |
29 const char kAuthorizationHeaderFormat[] = "Authorization: Bearer %s"; | |
30 // Template for GData API version HTTP header. | |
31 const char kGDataVersionHeader[] = "GData-Version: 3.0"; | |
32 | |
33 // Maximum number of attempts for re-authentication per operation. | |
34 const int kMaxReAuthenticateAttemptsPerOperation = 1; | |
35 | |
36 // Template for initiate upload of both GData WAPI and Drive API v2. | |
37 const char kUploadContentType[] = "X-Upload-Content-Type: "; | |
38 const char kUploadContentLength[] = "X-Upload-Content-Length: "; | |
39 const char kUploadResponseLocation[] = "location"; | |
40 | |
41 // Template for upload data range of both GData WAPI and Drive API v2. | |
42 const char kUploadContentRange[] = "Content-Range: bytes "; | |
43 const char kUploadResponseRange[] = "range"; | |
44 | |
45 // Parse JSON string to base::Value object. | |
46 scoped_ptr<base::Value> ParseJsonOnBlockingPool(const std::string& json) { | |
47 DCHECK(!BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
48 | |
49 int error_code = -1; | |
50 std::string error_message; | |
51 scoped_ptr<base::Value> value(base::JSONReader::ReadAndReturnError( | |
52 json, base::JSON_PARSE_RFC, &error_code, &error_message)); | |
53 | |
54 if (!value.get()) { | |
55 LOG(ERROR) << "Error while parsing entry response: " << error_message | |
56 << ", code: " << error_code << ", json:\n" << json; | |
57 } | |
58 return value.Pass(); | |
59 } | |
60 | |
61 // Returns response headers as a string. Returns a warning message if | |
62 // |url_fetcher| does not contain a valid response. Used only for debugging. | |
63 std::string GetResponseHeadersAsString( | |
64 const URLFetcher* url_fetcher) { | |
65 // net::HttpResponseHeaders::raw_headers(), as the name implies, stores | |
66 // all headers in their raw format, i.e each header is null-terminated. | |
67 // So logging raw_headers() only shows the first header, which is probably | |
68 // the status line. GetNormalizedHeaders, on the other hand, will show all | |
69 // the headers, one per line, which is probably what we want. | |
70 std::string headers; | |
71 // Check that response code indicates response headers are valid (i.e. not | |
72 // malformed) before we retrieve the headers. | |
73 if (url_fetcher->GetResponseCode() == URLFetcher::RESPONSE_CODE_INVALID) { | |
74 headers.assign("Response headers are malformed!!"); | |
75 } else { | |
76 url_fetcher->GetResponseHeaders()->GetNormalizedHeaders(&headers); | |
77 } | |
78 return headers; | |
79 } | |
80 | |
81 } // namespace | |
82 | |
83 namespace google_apis { | |
84 | |
85 void ParseJson(const std::string& json, const ParseJsonCallback& callback) { | |
86 base::PostTaskAndReplyWithResult( | |
87 BrowserThread::GetBlockingPool(), | |
88 FROM_HERE, | |
89 base::Bind(&ParseJsonOnBlockingPool, json), | |
90 callback); | |
91 } | |
92 | |
93 //============================ UrlFetchOperationBase =========================== | |
94 | |
95 UrlFetchOperationBase::UrlFetchOperationBase( | |
96 OperationRunner* runner, | |
97 net::URLRequestContextGetter* url_request_context_getter) | |
98 : OperationRegistry::Operation(runner->operation_registry()), | |
99 url_request_context_getter_(url_request_context_getter), | |
100 re_authenticate_count_(0), | |
101 started_(false), | |
102 save_temp_file_(false), | |
103 weak_ptr_factory_(this) { | |
104 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
105 } | |
106 | |
107 UrlFetchOperationBase::UrlFetchOperationBase( | |
108 OperationRunner* runner, | |
109 net::URLRequestContextGetter* url_request_context_getter, | |
110 const base::FilePath& path) | |
111 : OperationRegistry::Operation(runner->operation_registry(), path), | |
112 url_request_context_getter_(url_request_context_getter), | |
113 re_authenticate_count_(0), | |
114 started_(false), | |
115 save_temp_file_(false), | |
116 weak_ptr_factory_(this) { | |
117 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
118 } | |
119 | |
120 UrlFetchOperationBase::~UrlFetchOperationBase() {} | |
121 | |
122 void UrlFetchOperationBase::Start(const std::string& access_token, | |
123 const std::string& custom_user_agent, | |
124 const ReAuthenticateCallback& callback) { | |
125 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
126 DCHECK(url_request_context_getter_); | |
127 DCHECK(!access_token.empty()); | |
128 DCHECK(!callback.is_null()); | |
129 DCHECK(re_authenticate_callback_.is_null()); | |
130 | |
131 re_authenticate_callback_ = callback; | |
132 | |
133 GURL url = GetURL(); | |
134 if (url.is_empty()) { | |
135 // Error is found on generating the url. Send the error message to the | |
136 // callback, and then return immediately without trying to connect | |
137 // to the server. | |
138 RunCallbackOnPrematureFailure(GDATA_OTHER_ERROR); | |
139 return; | |
140 } | |
141 DVLOG(1) << "URL: " << url.spec(); | |
142 | |
143 URLFetcher::RequestType request_type = GetRequestType(); | |
144 url_fetcher_.reset( | |
145 URLFetcher::Create(url, request_type, this)); | |
146 url_fetcher_->SetRequestContext(url_request_context_getter_); | |
147 // Always set flags to neither send nor save cookies. | |
148 url_fetcher_->SetLoadFlags( | |
149 net::LOAD_DO_NOT_SEND_COOKIES | net::LOAD_DO_NOT_SAVE_COOKIES | | |
150 net::LOAD_DISABLE_CACHE); | |
151 if (save_temp_file_) { | |
152 url_fetcher_->SaveResponseToTemporaryFile( | |
153 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::FILE)); | |
154 } else if (!output_file_path_.empty()) { | |
155 url_fetcher_->SaveResponseToFileAtPath(output_file_path_, | |
156 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::FILE)); | |
157 } | |
158 | |
159 // Add request headers. | |
160 // Note that SetExtraRequestHeaders clears the current headers and sets it | |
161 // to the passed-in headers, so calling it for each header will result in | |
162 // only the last header being set in request headers. | |
163 if (!custom_user_agent.empty()) | |
164 url_fetcher_->AddExtraRequestHeader("User-Agent: " + custom_user_agent); | |
165 url_fetcher_->AddExtraRequestHeader(kGDataVersionHeader); | |
166 url_fetcher_->AddExtraRequestHeader( | |
167 base::StringPrintf(kAuthorizationHeaderFormat, access_token.data())); | |
168 std::vector<std::string> headers = GetExtraRequestHeaders(); | |
169 for (size_t i = 0; i < headers.size(); ++i) { | |
170 url_fetcher_->AddExtraRequestHeader(headers[i]); | |
171 DVLOG(1) << "Extra header: " << headers[i]; | |
172 } | |
173 | |
174 // Set upload data if available. | |
175 std::string upload_content_type; | |
176 std::string upload_content; | |
177 if (GetContentData(&upload_content_type, &upload_content)) { | |
178 url_fetcher_->SetUploadData(upload_content_type, upload_content); | |
179 } else { | |
180 base::FilePath local_file_path; | |
181 int64 range_offset = 0; | |
182 int64 range_length = 0; | |
183 if (GetContentFile(&local_file_path, &range_offset, &range_length, | |
184 &upload_content_type)) { | |
185 url_fetcher_->SetUploadFilePath( | |
186 upload_content_type, | |
187 local_file_path, | |
188 range_offset, | |
189 range_length, | |
190 BrowserThread::GetBlockingPool()); | |
191 } else { | |
192 // Even if there is no content data, UrlFetcher requires to set empty | |
193 // upload data string for POST, PUT and PATCH methods, explicitly. | |
194 // It is because that most requests of those methods have non-empty | |
195 // body, and UrlFetcher checks whether it is actually not forgotten. | |
196 if (request_type == URLFetcher::POST || | |
197 request_type == URLFetcher::PUT || | |
198 request_type == URLFetcher::PATCH) { | |
199 // Set empty upload content-type and upload content, so that | |
200 // the request will have no "Content-type: " header and no content. | |
201 url_fetcher_->SetUploadData(std::string(), std::string()); | |
202 } | |
203 } | |
204 } | |
205 | |
206 // Register to operation registry. | |
207 NotifyStartToOperationRegistry(); | |
208 | |
209 url_fetcher_->Start(); | |
210 started_ = true; | |
211 } | |
212 | |
213 URLFetcher::RequestType UrlFetchOperationBase::GetRequestType() const { | |
214 return URLFetcher::GET; | |
215 } | |
216 | |
217 std::vector<std::string> UrlFetchOperationBase::GetExtraRequestHeaders() const { | |
218 return std::vector<std::string>(); | |
219 } | |
220 | |
221 bool UrlFetchOperationBase::GetContentData(std::string* upload_content_type, | |
222 std::string* upload_content) { | |
223 return false; | |
224 } | |
225 | |
226 bool UrlFetchOperationBase::GetContentFile(base::FilePath* local_file_path, | |
227 int64* range_offset, | |
228 int64* range_length, | |
229 std::string* upload_content_type) { | |
230 return false; | |
231 } | |
232 | |
233 void UrlFetchOperationBase::DoCancel() { | |
234 url_fetcher_.reset(NULL); | |
235 RunCallbackOnPrematureFailure(GDATA_CANCELLED); | |
236 } | |
237 | |
238 // static | |
239 GDataErrorCode UrlFetchOperationBase::GetErrorCode(const URLFetcher* source) { | |
240 GDataErrorCode code = static_cast<GDataErrorCode>(source->GetResponseCode()); | |
241 if (!source->GetStatus().is_success()) { | |
242 switch (source->GetStatus().error()) { | |
243 case net::ERR_NETWORK_CHANGED: | |
244 code = GDATA_NO_CONNECTION; | |
245 break; | |
246 default: | |
247 code = GDATA_OTHER_ERROR; | |
248 } | |
249 } | |
250 return code; | |
251 } | |
252 | |
253 void UrlFetchOperationBase::OnProcessURLFetchResultsComplete(bool result) { | |
254 if (result) | |
255 NotifySuccessToOperationRegistry(); | |
256 else | |
257 NotifyFinish(OPERATION_FAILED); | |
258 } | |
259 | |
260 void UrlFetchOperationBase::OnURLFetchComplete(const URLFetcher* source) { | |
261 GDataErrorCode code = GetErrorCode(source); | |
262 DVLOG(1) << "Response headers:\n" << GetResponseHeadersAsString(source); | |
263 | |
264 if (code == HTTP_UNAUTHORIZED) { | |
265 if (++re_authenticate_count_ <= kMaxReAuthenticateAttemptsPerOperation) { | |
266 // Reset re_authenticate_callback_ so Start() can be called again. | |
267 ReAuthenticateCallback callback = re_authenticate_callback_; | |
268 re_authenticate_callback_.Reset(); | |
269 callback.Run(this); | |
270 return; | |
271 } | |
272 | |
273 OnAuthFailed(code); | |
274 return; | |
275 } | |
276 | |
277 // Overridden by each specialization | |
278 ProcessURLFetchResults(source); | |
279 } | |
280 | |
281 void UrlFetchOperationBase::NotifySuccessToOperationRegistry() { | |
282 NotifyFinish(OPERATION_COMPLETED); | |
283 } | |
284 | |
285 void UrlFetchOperationBase::NotifyStartToOperationRegistry() { | |
286 NotifyStart(); | |
287 } | |
288 | |
289 void UrlFetchOperationBase::OnAuthFailed(GDataErrorCode code) { | |
290 RunCallbackOnPrematureFailure(code); | |
291 | |
292 // Check if this failed before we even started fetching. If so, register | |
293 // for start so we can properly unregister with finish. | |
294 if (!started_) | |
295 NotifyStart(); | |
296 | |
297 // Note: NotifyFinish() must be invoked at the end, after all other callbacks | |
298 // and notifications. Once NotifyFinish() is called, the current instance of | |
299 // gdata operation will be deleted from the OperationRegistry and become | |
300 // invalid. | |
301 NotifyFinish(OPERATION_FAILED); | |
302 } | |
303 | |
304 base::WeakPtr<AuthenticatedOperationInterface> | |
305 UrlFetchOperationBase::GetWeakPtr() { | |
306 return weak_ptr_factory_.GetWeakPtr(); | |
307 } | |
308 | |
309 //============================ EntryActionOperation ============================ | |
310 | |
311 EntryActionOperation::EntryActionOperation( | |
312 OperationRunner* runner, | |
313 net::URLRequestContextGetter* url_request_context_getter, | |
314 const EntryActionCallback& callback) | |
315 : UrlFetchOperationBase(runner, url_request_context_getter), | |
316 callback_(callback) { | |
317 DCHECK(!callback_.is_null()); | |
318 } | |
319 | |
320 EntryActionOperation::~EntryActionOperation() {} | |
321 | |
322 void EntryActionOperation::ProcessURLFetchResults(const URLFetcher* source) { | |
323 GDataErrorCode code = GetErrorCode(source); | |
324 callback_.Run(code); | |
325 const bool success = true; | |
326 OnProcessURLFetchResultsComplete(success); | |
327 } | |
328 | |
329 void EntryActionOperation::RunCallbackOnPrematureFailure(GDataErrorCode code) { | |
330 callback_.Run(code); | |
331 } | |
332 | |
333 //============================== GetDataOperation ============================== | |
334 | |
335 GetDataOperation::GetDataOperation( | |
336 OperationRunner* runner, | |
337 net::URLRequestContextGetter* url_request_context_getter, | |
338 const GetDataCallback& callback) | |
339 : UrlFetchOperationBase(runner, url_request_context_getter), | |
340 callback_(callback), | |
341 weak_ptr_factory_(this) { | |
342 DCHECK(!callback_.is_null()); | |
343 } | |
344 | |
345 GetDataOperation::~GetDataOperation() {} | |
346 | |
347 void GetDataOperation::ParseResponse(GDataErrorCode fetch_error_code, | |
348 const std::string& data) { | |
349 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
350 | |
351 VLOG(1) << "JSON received from " << GetURL().spec() << ": " | |
352 << data.size() << " bytes"; | |
353 ParseJson(data, | |
354 base::Bind(&GetDataOperation::OnDataParsed, | |
355 weak_ptr_factory_.GetWeakPtr(), | |
356 fetch_error_code)); | |
357 } | |
358 | |
359 void GetDataOperation::ProcessURLFetchResults(const URLFetcher* source) { | |
360 std::string data; | |
361 source->GetResponseAsString(&data); | |
362 scoped_ptr<base::Value> root_value; | |
363 GDataErrorCode fetch_error_code = GetErrorCode(source); | |
364 | |
365 switch (fetch_error_code) { | |
366 case HTTP_SUCCESS: | |
367 case HTTP_CREATED: | |
368 ParseResponse(fetch_error_code, data); | |
369 break; | |
370 default: | |
371 RunCallbackOnPrematureFailure(fetch_error_code); | |
372 const bool success = false; | |
373 OnProcessURLFetchResultsComplete(success); | |
374 break; | |
375 } | |
376 } | |
377 | |
378 void GetDataOperation::RunCallbackOnPrematureFailure( | |
379 GDataErrorCode fetch_error_code) { | |
380 callback_.Run(fetch_error_code, scoped_ptr<base::Value>()); | |
381 } | |
382 | |
383 void GetDataOperation::OnDataParsed( | |
384 GDataErrorCode fetch_error_code, | |
385 scoped_ptr<base::Value> value) { | |
386 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
387 | |
388 bool success = true; | |
389 if (!value.get()) { | |
390 fetch_error_code = GDATA_PARSE_ERROR; | |
391 success = false; | |
392 } | |
393 | |
394 RunCallbackOnSuccess(fetch_error_code, value.Pass()); | |
395 | |
396 DCHECK(!value.get()); | |
397 OnProcessURLFetchResultsComplete(success); | |
398 } | |
399 | |
400 void GetDataOperation::RunCallbackOnSuccess(GDataErrorCode fetch_error_code, | |
401 scoped_ptr<base::Value> value) { | |
402 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
403 callback_.Run(fetch_error_code, value.Pass()); | |
404 } | |
405 | |
406 //========================= InitiateUploadOperationBase ======================== | |
407 | |
408 InitiateUploadOperationBase::InitiateUploadOperationBase( | |
409 OperationRunner* runner, | |
410 net::URLRequestContextGetter* url_request_context_getter, | |
411 const InitiateUploadCallback& callback, | |
412 const base::FilePath& drive_file_path, | |
413 const std::string& content_type, | |
414 int64 content_length) | |
415 : UrlFetchOperationBase(runner, | |
416 url_request_context_getter, | |
417 drive_file_path), | |
418 callback_(callback), | |
419 drive_file_path_(drive_file_path), | |
420 content_type_(content_type), | |
421 content_length_(content_length) { | |
422 DCHECK(!callback_.is_null()); | |
423 DCHECK(!content_type_.empty()); | |
424 DCHECK_GE(content_length_, 0); | |
425 } | |
426 | |
427 InitiateUploadOperationBase::~InitiateUploadOperationBase() {} | |
428 | |
429 void InitiateUploadOperationBase::ProcessURLFetchResults( | |
430 const URLFetcher* source) { | |
431 GDataErrorCode code = GetErrorCode(source); | |
432 | |
433 std::string upload_location; | |
434 if (code == HTTP_SUCCESS) { | |
435 // Retrieve value of the first "Location" header. | |
436 source->GetResponseHeaders()->EnumerateHeader(NULL, | |
437 kUploadResponseLocation, | |
438 &upload_location); | |
439 } | |
440 VLOG(1) << "Got response for [" << drive_file_path_.value() | |
441 << "]: code=" << code | |
442 << ", location=[" << upload_location << "]"; | |
443 | |
444 callback_.Run(code, GURL(upload_location)); | |
445 OnProcessURLFetchResultsComplete(code == HTTP_SUCCESS); | |
446 } | |
447 | |
448 void InitiateUploadOperationBase::NotifySuccessToOperationRegistry() { | |
449 NotifySuspend(); | |
450 } | |
451 | |
452 void InitiateUploadOperationBase::RunCallbackOnPrematureFailure( | |
453 GDataErrorCode code) { | |
454 callback_.Run(code, GURL()); | |
455 } | |
456 | |
457 std::vector<std::string> | |
458 InitiateUploadOperationBase::GetExtraRequestHeaders() const { | |
459 std::vector<std::string> headers; | |
460 headers.push_back(kUploadContentType + content_type_); | |
461 headers.push_back( | |
462 kUploadContentLength + base::Int64ToString(content_length_)); | |
463 return headers; | |
464 } | |
465 | |
466 //============================ UploadRangeResponse ============================= | |
467 | |
468 UploadRangeResponse::UploadRangeResponse() | |
469 : code(HTTP_SUCCESS), | |
470 start_position_received(0), | |
471 end_position_received(0) { | |
472 } | |
473 | |
474 UploadRangeResponse::UploadRangeResponse(GDataErrorCode code, | |
475 int64 start_position_received, | |
476 int64 end_position_received) | |
477 : code(code), | |
478 start_position_received(start_position_received), | |
479 end_position_received(end_position_received) { | |
480 } | |
481 | |
482 UploadRangeResponse::~UploadRangeResponse() { | |
483 } | |
484 | |
485 //========================== UploadRangeOperationBase ========================== | |
486 | |
487 UploadRangeOperationBase::UploadRangeOperationBase( | |
488 OperationRunner* runner, | |
489 net::URLRequestContextGetter* url_request_context_getter, | |
490 const base::FilePath& drive_file_path, | |
491 const GURL& upload_url) | |
492 : UrlFetchOperationBase(runner, | |
493 url_request_context_getter, | |
494 drive_file_path), | |
495 drive_file_path_(drive_file_path), | |
496 upload_url_(upload_url), | |
497 last_chunk_completed_(false), | |
498 weak_ptr_factory_(this) { | |
499 } | |
500 | |
501 UploadRangeOperationBase::~UploadRangeOperationBase() {} | |
502 | |
503 GURL UploadRangeOperationBase::GetURL() const { | |
504 // This is very tricky to get json from this operation. To do that, &alt=json | |
505 // has to be appended not here but in InitiateUploadOperation::GetURL(). | |
506 return upload_url_; | |
507 } | |
508 | |
509 URLFetcher::RequestType UploadRangeOperationBase::GetRequestType() const { | |
510 return URLFetcher::PUT; | |
511 } | |
512 | |
513 void UploadRangeOperationBase::ProcessURLFetchResults( | |
514 const URLFetcher* source) { | |
515 GDataErrorCode code = GetErrorCode(source); | |
516 net::HttpResponseHeaders* hdrs = source->GetResponseHeaders(); | |
517 | |
518 if (code == HTTP_RESUME_INCOMPLETE) { | |
519 // Retrieve value of the first "Range" header. | |
520 // The Range header is appeared only if there is at least one received | |
521 // byte. So, initialize the positions by 0 so that the [0,0) will be | |
522 // returned via the |callback_| for empty data case. | |
523 int64 start_position_received = 0; | |
524 int64 end_position_received = 0; | |
525 std::string range_received; | |
526 hdrs->EnumerateHeader(NULL, kUploadResponseRange, &range_received); | |
527 if (!range_received.empty()) { // Parse the range header. | |
528 std::vector<net::HttpByteRange> ranges; | |
529 if (net::HttpUtil::ParseRangeHeader(range_received, &ranges) && | |
530 !ranges.empty() ) { | |
531 // We only care about the first start-end pair in the range. | |
532 // | |
533 // Range header represents the range inclusively, while we are treating | |
534 // ranges exclusively (i.e., end_position_received should be one passed | |
535 // the last valid index). So "+ 1" is added. | |
536 start_position_received = ranges[0].first_byte_position(); | |
537 end_position_received = ranges[0].last_byte_position() + 1; | |
538 } | |
539 } | |
540 // The Range header has the received data range, so the start position | |
541 // should be always 0. | |
542 DCHECK_EQ(start_position_received, 0); | |
543 DVLOG(1) << "Got response for [" << drive_file_path_.value() | |
544 << "]: code=" << code | |
545 << ", range_hdr=[" << range_received | |
546 << "], range_parsed=" << start_position_received | |
547 << "," << end_position_received; | |
548 | |
549 OnRangeOperationComplete(UploadRangeResponse(code, | |
550 start_position_received, | |
551 end_position_received), | |
552 scoped_ptr<base::Value>()); | |
553 | |
554 OnProcessURLFetchResultsComplete(true); | |
555 } else { | |
556 // There might be explanation of unexpected error code in response. | |
557 std::string response_content; | |
558 source->GetResponseAsString(&response_content); | |
559 DVLOG(1) << "Got response for [" << drive_file_path_.value() | |
560 << "]: code=" << code | |
561 << ", content=[\n" << response_content << "\n]"; | |
562 | |
563 ParseJson(response_content, | |
564 base::Bind(&UploadRangeOperationBase::OnDataParsed, | |
565 weak_ptr_factory_.GetWeakPtr(), | |
566 code)); | |
567 } | |
568 } | |
569 | |
570 void UploadRangeOperationBase::OnDataParsed(GDataErrorCode code, | |
571 scoped_ptr<base::Value> value) { | |
572 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
573 | |
574 // For a new file, HTTP_CREATED is returned. | |
575 // For an existing file, HTTP_SUCCESS is returned. | |
576 if (code == HTTP_CREATED || code == HTTP_SUCCESS) | |
577 last_chunk_completed_ = true; | |
578 | |
579 OnRangeOperationComplete(UploadRangeResponse(code, -1, -1), value.Pass()); | |
580 OnProcessURLFetchResultsComplete(last_chunk_completed_); | |
581 } | |
582 | |
583 void UploadRangeOperationBase::NotifySuccessToOperationRegistry() { | |
584 if (last_chunk_completed_) | |
585 NotifyFinish(OPERATION_COMPLETED); | |
586 else | |
587 NotifySuspend(); | |
588 } | |
589 | |
590 void UploadRangeOperationBase::RunCallbackOnPrematureFailure( | |
591 GDataErrorCode code) { | |
592 OnRangeOperationComplete( | |
593 UploadRangeResponse(code, 0, 0), scoped_ptr<base::Value>()); | |
594 } | |
595 | |
596 //========================== ResumeUploadOperationBase ========================= | |
597 | |
598 ResumeUploadOperationBase::ResumeUploadOperationBase( | |
599 OperationRunner* runner, | |
600 net::URLRequestContextGetter* url_request_context_getter, | |
601 const base::FilePath& drive_file_path, | |
602 const GURL& upload_location, | |
603 int64 start_position, | |
604 int64 end_position, | |
605 int64 content_length, | |
606 const std::string& content_type, | |
607 const base::FilePath& local_file_path) | |
608 : UploadRangeOperationBase(runner, | |
609 url_request_context_getter, | |
610 drive_file_path, | |
611 upload_location), | |
612 start_position_(start_position), | |
613 end_position_(end_position), | |
614 content_length_(content_length), | |
615 content_type_(content_type), | |
616 local_file_path_(local_file_path) { | |
617 DCHECK_LE(start_position_, end_position_); | |
618 } | |
619 | |
620 ResumeUploadOperationBase::~ResumeUploadOperationBase() {} | |
621 | |
622 std::vector<std::string> | |
623 ResumeUploadOperationBase::GetExtraRequestHeaders() const { | |
624 if (content_length_ == 0) { | |
625 // For uploading an empty document, just PUT an empty content. | |
626 DCHECK_EQ(start_position_, 0); | |
627 DCHECK_EQ(end_position_, 0); | |
628 return std::vector<std::string>(); | |
629 } | |
630 | |
631 // The header looks like | |
632 // Content-Range: bytes <start_position>-<end_position>/<content_length> | |
633 // for example: | |
634 // Content-Range: bytes 7864320-8388607/13851821 | |
635 // The header takes inclusive range, so we adjust by "end_position - 1". | |
636 DCHECK_GE(start_position_, 0); | |
637 DCHECK_GT(end_position_, 0); | |
638 DCHECK_GE(content_length_, 0); | |
639 | |
640 std::vector<std::string> headers; | |
641 headers.push_back( | |
642 std::string(kUploadContentRange) + | |
643 base::Int64ToString(start_position_) + "-" + | |
644 base::Int64ToString(end_position_ - 1) + "/" + | |
645 base::Int64ToString(content_length_)); | |
646 return headers; | |
647 } | |
648 | |
649 bool ResumeUploadOperationBase::GetContentFile( | |
650 base::FilePath* local_file_path, | |
651 int64* range_offset, | |
652 int64* range_length, | |
653 std::string* upload_content_type) { | |
654 if (start_position_ == end_position_) { | |
655 // No content data. | |
656 return false; | |
657 } | |
658 | |
659 *local_file_path = local_file_path_; | |
660 *range_offset = start_position_; | |
661 *range_length = end_position_ - start_position_; | |
662 *upload_content_type = content_type_; | |
663 return true; | |
664 } | |
665 | |
666 void ResumeUploadOperationBase::NotifyStartToOperationRegistry() { | |
667 NotifyResume(); | |
668 } | |
669 | |
670 //======================== GetUploadStatusOperationBase ======================== | |
671 | |
672 GetUploadStatusOperationBase::GetUploadStatusOperationBase( | |
673 OperationRunner* runner, | |
674 net::URLRequestContextGetter* url_request_context_getter, | |
675 const base::FilePath& drive_file_path, | |
676 const GURL& upload_url, | |
677 int64 content_length) | |
678 : UploadRangeOperationBase(runner, | |
679 url_request_context_getter, | |
680 drive_file_path, | |
681 upload_url), | |
682 content_length_(content_length) {} | |
683 | |
684 GetUploadStatusOperationBase::~GetUploadStatusOperationBase() {} | |
685 | |
686 std::vector<std::string> | |
687 GetUploadStatusOperationBase::GetExtraRequestHeaders() const { | |
688 // The header looks like | |
689 // Content-Range: bytes */<content_length> | |
690 // for example: | |
691 // Content-Range: bytes */13851821 | |
692 DCHECK_GE(content_length_, 0); | |
693 | |
694 std::vector<std::string> headers; | |
695 headers.push_back( | |
696 std::string(kUploadContentRange) + "*/" + | |
697 base::Int64ToString(content_length_)); | |
698 return headers; | |
699 } | |
700 | |
701 //============================ DownloadFileOperation =========================== | |
702 | |
703 DownloadFileOperation::DownloadFileOperation( | |
704 OperationRunner* runner, | |
705 net::URLRequestContextGetter* url_request_context_getter, | |
706 const DownloadActionCallback& download_action_callback, | |
707 const GetContentCallback& get_content_callback, | |
708 const ProgressCallback& progress_callback, | |
709 const GURL& download_url, | |
710 const base::FilePath& drive_file_path, | |
711 const base::FilePath& output_file_path) | |
712 : UrlFetchOperationBase(runner, | |
713 url_request_context_getter, | |
714 drive_file_path), | |
715 download_action_callback_(download_action_callback), | |
716 get_content_callback_(get_content_callback), | |
717 progress_callback_(progress_callback), | |
718 download_url_(download_url) { | |
719 DCHECK(!download_action_callback_.is_null()); | |
720 // get_content_callback may be null. | |
721 | |
722 // Make sure we download the content into a temp file. | |
723 if (output_file_path.empty()) | |
724 set_save_temp_file(true); | |
725 else | |
726 set_output_file_path(output_file_path); | |
727 } | |
728 | |
729 DownloadFileOperation::~DownloadFileOperation() {} | |
730 | |
731 // Overridden from UrlFetchOperationBase. | |
732 GURL DownloadFileOperation::GetURL() const { | |
733 return download_url_; | |
734 } | |
735 | |
736 void DownloadFileOperation::OnURLFetchDownloadProgress(const URLFetcher* source, | |
737 int64 current, | |
738 int64 total) { | |
739 if (!progress_callback_.is_null()) | |
740 progress_callback_.Run(current, total); | |
741 } | |
742 | |
743 bool DownloadFileOperation::ShouldSendDownloadData() { | |
744 return !get_content_callback_.is_null(); | |
745 } | |
746 | |
747 void DownloadFileOperation::OnURLFetchDownloadData( | |
748 const URLFetcher* source, | |
749 scoped_ptr<std::string> download_data) { | |
750 if (!get_content_callback_.is_null()) | |
751 get_content_callback_.Run(HTTP_SUCCESS, download_data.Pass()); | |
752 } | |
753 | |
754 void DownloadFileOperation::ProcessURLFetchResults(const URLFetcher* source) { | |
755 GDataErrorCode code = GetErrorCode(source); | |
756 | |
757 // Take over the ownership of the the downloaded temp file. | |
758 base::FilePath temp_file; | |
759 if (code == HTTP_SUCCESS && | |
760 !source->GetResponseAsFilePath(true, // take_ownership | |
761 &temp_file)) { | |
762 code = GDATA_FILE_ERROR; | |
763 } | |
764 | |
765 download_action_callback_.Run(code, temp_file); | |
766 OnProcessURLFetchResultsComplete(code == HTTP_SUCCESS); | |
767 } | |
768 | |
769 void DownloadFileOperation::RunCallbackOnPrematureFailure(GDataErrorCode code) { | |
770 download_action_callback_.Run(code, base::FilePath()); | |
771 } | |
772 | |
773 } // namespace google_apis | |
OLD | NEW |