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.
5 #include "google_apis/drive/base_requests.h"
7 #include "base/files/file_util.h"
8 #include "base/json/json_reader.h"
9 #include "base/json/json_writer.h"
10 #include "base/location.h"
11 #include "base/rand_util.h"
12 #include "base/sequenced_task_runner.h"
13 #include "base/strings/string_number_conversions.h"
14 #include "base/strings/stringprintf.h"
15 #include "base/task_runner_util.h"
16 #include "base/values.h"
17 #include "google_apis/drive/drive_api_parser.h"
18 #include "google_apis/drive/request_sender.h"
19 #include "google_apis/drive/request_util.h"
20 #include "google_apis/drive/task_util.h"
21 #include "google_apis/drive/time_util.h"
22 #include "net/base/elements_upload_data_stream.h"
23 #include "net/base/io_buffer.h"
24 #include "net/base/load_flags.h"
25 #include "net/base/net_errors.h"
26 #include "net/base/upload_bytes_element_reader.h"
27 #include "net/base/upload_data_stream.h"
28 #include "net/base/upload_element_reader.h"
29 #include "net/base/upload_file_element_reader.h"
30 #include "net/http/http_byte_range.h"
31 #include "net/http/http_response_headers.h"
32 #include "net/http/http_util.h"
33 #include "net/url_request/url_fetcher.h"
34 #include "net/url_request/url_request_status.h"
36 using net::URLFetcher
;
40 // Template for optional OAuth2 authorization HTTP header.
41 const char kAuthorizationHeaderFormat
[] = "Authorization: Bearer %s";
42 // Template for GData API version HTTP header.
43 const char kGDataVersionHeader
[] = "GData-Version: 3.0";
45 // Maximum number of attempts for re-authentication per request.
46 const int kMaxReAuthenticateAttemptsPerRequest
= 1;
48 // Template for initiate upload of both GData WAPI and Drive API v2.
49 const char kUploadContentType
[] = "X-Upload-Content-Type: ";
50 const char kUploadContentLength
[] = "X-Upload-Content-Length: ";
51 const char kUploadResponseLocation
[] = "location";
53 // Template for upload data range of both GData WAPI and Drive API v2.
54 const char kUploadContentRange
[] = "Content-Range: bytes ";
55 const char kUploadResponseRange
[] = "range";
57 // The prefix of multipart/related mime type.
58 const char kMultipartMimeTypePrefix
[] = "multipart/related; boundary=";
60 // Template for multipart request body.
61 const char kMessageFormatBeforeFile
[] =
62 "--%s\nContent-Type: %s\n\n%s\n--%s\nContent-Type: %s\n\n";
63 const char kMessageFormatAfterFile
[] = "\n--%s--";
65 // Characters to be used for multipart/related boundary.
66 const char kBoundaryCharacters
[] =
67 "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
68 // Size of multipart/related's boundary.
69 const char kBoundarySize
= 70;
71 // Parses JSON passed in |json| on |blocking_task_runner|. Runs |callback| on
72 // the calling thread when finished with either success or failure.
73 // The callback must not be null.
74 void ParseJsonOnBlockingPool(
75 base::TaskRunner
* blocking_task_runner
,
76 const std::string
& json
,
77 const base::Callback
<void(scoped_ptr
<base::Value
> value
)>& callback
) {
78 base::PostTaskAndReplyWithResult(
81 base::Bind(&google_apis::ParseJson
, json
),
85 // Returns response headers as a string. Returns a warning message if
86 // |url_fetcher| does not contain a valid response. Used only for debugging.
87 std::string
GetResponseHeadersAsString(const URLFetcher
* url_fetcher
) {
88 // net::HttpResponseHeaders::raw_headers(), as the name implies, stores
89 // all headers in their raw format, i.e each header is null-terminated.
90 // So logging raw_headers() only shows the first header, which is probably
91 // the status line. GetNormalizedHeaders, on the other hand, will show all
92 // the headers, one per line, which is probably what we want.
94 // Check that response code indicates response headers are valid (i.e. not
95 // malformed) before we retrieve the headers.
96 if (url_fetcher
->GetResponseCode() == URLFetcher::RESPONSE_CODE_INVALID
) {
97 headers
.assign("Response headers are malformed!!");
99 url_fetcher
->GetResponseHeaders()->GetNormalizedHeaders(&headers
);
104 bool IsSuccessfulResponseCode(int response_code
) {
105 return 200 <= response_code
&& response_code
<= 299;
108 // Creates metadata JSON string for multipart uploading.
109 // All the values are optional. If the value is empty or null, the value does
110 // not appear in the metadata.
111 std::string
CreateMultipartUploadMetadataJson(
112 const std::string
& title
,
113 const std::string
& parent_resource_id
,
114 const base::Time
& modified_date
,
115 const base::Time
& last_viewed_by_me_date
) {
116 base::DictionaryValue root
;
118 root
.SetString("title", title
);
121 if (!parent_resource_id
.empty()) {
122 scoped_ptr
<base::ListValue
> parents(new base::ListValue
);
124 google_apis::util::CreateParentValue(parent_resource_id
).release());
125 root
.Set("parents", parents
.release());
128 if (!modified_date
.is_null())
129 root
.SetString("modifiedDate",
130 google_apis::util::FormatTimeAsString(modified_date
));
132 if (!last_viewed_by_me_date
.is_null()) {
133 root
.SetString("lastViewedByMeDate", google_apis::util::FormatTimeAsString(
134 last_viewed_by_me_date
));
137 std::string json_string
;
138 base::JSONWriter::Write(&root
, &json_string
);
142 // Obtains the multipart body for the metadata string and file contents. If
143 // predetermined_boundary is empty, the function generates the boundary string.
144 bool GetMultipartContent(const std::string
& predetermined_boundary
,
145 const std::string
& metadata_json
,
146 const std::string
& content_type
,
147 const base::FilePath
& path
,
148 std::string
* upload_content_type
,
149 std::string
* upload_content_data
) {
150 std::string file_content
;
151 if (!ReadFileToString(path
, &file_content
))
154 std::string boundary
;
155 if (predetermined_boundary
.empty()) {
157 boundary
.resize(kBoundarySize
);
158 for (int i
= 0; i
< kBoundarySize
; ++i
) {
159 // Subtract 2 from the array size to exclude '\0', and to turn the size
160 // into the last index.
161 const int last_char_index
= arraysize(kBoundaryCharacters
) - 2;
162 boundary
[i
] = kBoundaryCharacters
[base::RandInt(0, last_char_index
)];
164 if (metadata_json
.find(boundary
, 0) == std::string::npos
&&
165 file_content
.find(boundary
, 0) == std::string::npos
) {
170 boundary
= predetermined_boundary
;
172 const std::string body_before_file
= base::StringPrintf(
173 kMessageFormatBeforeFile
, boundary
.c_str(), "application/json",
174 metadata_json
.c_str(), boundary
.c_str(), content_type
.c_str());
175 const std::string body_after_file
=
176 base::StringPrintf(kMessageFormatAfterFile
, boundary
.c_str());
178 *upload_content_type
= kMultipartMimeTypePrefix
+ boundary
;
179 *upload_content_data
= body_before_file
+ file_content
+ body_after_file
;
185 namespace google_apis
{
187 scoped_ptr
<base::Value
> ParseJson(const std::string
& json
) {
189 std::string error_message
;
190 scoped_ptr
<base::Value
> value(base::JSONReader::ReadAndReturnError(
191 json
, base::JSON_PARSE_RFC
, &error_code
, &error_message
));
194 std::string trimmed_json
;
195 if (json
.size() < 80) {
198 // Take the first 50 and the last 10 bytes.
199 trimmed_json
= base::StringPrintf(
201 json
.substr(0, 50).c_str(),
202 base::Uint64ToString(json
.size() - 60).c_str(),
203 json
.substr(json
.size() - 10).c_str());
205 LOG(WARNING
) << "Error while parsing entry response: " << error_message
206 << ", code: " << error_code
<< ", json:\n" << trimmed_json
;
211 //=========================== ResponseWriter ==================================
212 ResponseWriter::ResponseWriter(base::SequencedTaskRunner
* file_task_runner
,
213 const base::FilePath
& file_path
,
214 const GetContentCallback
& get_content_callback
)
215 : get_content_callback_(get_content_callback
),
216 weak_ptr_factory_(this) {
217 if (!file_path
.empty()) {
219 new net::URLFetcherFileWriter(file_task_runner
, file_path
));
223 ResponseWriter::~ResponseWriter() {
226 void ResponseWriter::DisownFile() {
227 DCHECK(file_writer_
);
228 file_writer_
->DisownFile();
231 int ResponseWriter::Initialize(const net::CompletionCallback
& callback
) {
233 return file_writer_
->Initialize(callback
);
239 int ResponseWriter::Write(net::IOBuffer
* buffer
,
241 const net::CompletionCallback
& callback
) {
242 if (!get_content_callback_
.is_null()) {
243 get_content_callback_
.Run(
245 make_scoped_ptr(new std::string(buffer
->data(), num_bytes
)));
249 const int result
= file_writer_
->Write(
251 base::Bind(&ResponseWriter::DidWrite
,
252 weak_ptr_factory_
.GetWeakPtr(),
253 make_scoped_refptr(buffer
), callback
));
254 if (result
!= net::ERR_IO_PENDING
)
255 DidWrite(buffer
, net::CompletionCallback(), result
);
259 data_
.append(buffer
->data(), num_bytes
);
263 int ResponseWriter::Finish(const net::CompletionCallback
& callback
) {
265 return file_writer_
->Finish(callback
);
270 void ResponseWriter::DidWrite(scoped_refptr
<net::IOBuffer
> buffer
,
271 const net::CompletionCallback
& callback
,
274 // Even if file_writer_ is used, append the data to |data_|, so that it can
275 // be used to get error information in case of server side errors.
276 // The size limit is to avoid consuming too much redundant memory.
277 const size_t kMaxStringSize
= 1024*1024;
278 if (data_
.size() < kMaxStringSize
) {
279 data_
.append(buffer
->data(), std::min(static_cast<size_t>(result
),
280 kMaxStringSize
- data_
.size()));
284 if (!callback
.is_null())
285 callback
.Run(result
);
288 //============================ UrlFetchRequestBase ===========================
290 UrlFetchRequestBase::UrlFetchRequestBase(RequestSender
* sender
)
291 : re_authenticate_count_(0),
292 response_writer_(NULL
),
294 error_code_(GDATA_OTHER_ERROR
),
295 weak_ptr_factory_(this) {
298 UrlFetchRequestBase::~UrlFetchRequestBase() {}
300 void UrlFetchRequestBase::Start(const std::string
& access_token
,
301 const std::string
& custom_user_agent
,
302 const ReAuthenticateCallback
& callback
) {
303 DCHECK(CalledOnValidThread());
304 DCHECK(!access_token
.empty());
305 DCHECK(!callback
.is_null());
306 DCHECK(re_authenticate_callback_
.is_null());
308 re_authenticate_callback_
= callback
;
311 if (url
.is_empty()) {
312 // Error is found on generating the url. Send the error message to the
313 // callback, and then return immediately without trying to connect
315 RunCallbackOnPrematureFailure(GDATA_OTHER_ERROR
);
318 DVLOG(1) << "URL: " << url
.spec();
320 URLFetcher::RequestType request_type
= GetRequestType();
321 url_fetcher_
.reset(URLFetcher::Create(url
, request_type
, this));
322 url_fetcher_
->SetRequestContext(sender_
->url_request_context_getter());
323 // Always set flags to neither send nor save cookies.
324 url_fetcher_
->SetLoadFlags(
325 net::LOAD_DO_NOT_SEND_COOKIES
| net::LOAD_DO_NOT_SAVE_COOKIES
|
326 net::LOAD_DISABLE_CACHE
);
328 base::FilePath output_file_path
;
329 GetContentCallback get_content_callback
;
330 GetOutputFilePath(&output_file_path
, &get_content_callback
);
331 if (!get_content_callback
.is_null())
332 get_content_callback
= CreateRelayCallback(get_content_callback
);
333 response_writer_
= new ResponseWriter(blocking_task_runner(),
335 get_content_callback
);
336 url_fetcher_
->SaveResponseWithWriter(
337 scoped_ptr
<net::URLFetcherResponseWriter
>(response_writer_
));
339 // Add request headers.
340 // Note that SetExtraRequestHeaders clears the current headers and sets it
341 // to the passed-in headers, so calling it for each header will result in
342 // only the last header being set in request headers.
343 if (!custom_user_agent
.empty())
344 url_fetcher_
->AddExtraRequestHeader("User-Agent: " + custom_user_agent
);
345 url_fetcher_
->AddExtraRequestHeader(kGDataVersionHeader
);
346 url_fetcher_
->AddExtraRequestHeader(
347 base::StringPrintf(kAuthorizationHeaderFormat
, access_token
.data()));
348 std::vector
<std::string
> headers
= GetExtraRequestHeaders();
349 for (size_t i
= 0; i
< headers
.size(); ++i
) {
350 url_fetcher_
->AddExtraRequestHeader(headers
[i
]);
351 DVLOG(1) << "Extra header: " << headers
[i
];
354 // Set upload data if available.
355 std::string upload_content_type
;
356 std::string upload_content
;
357 if (GetContentData(&upload_content_type
, &upload_content
)) {
358 url_fetcher_
->SetUploadData(upload_content_type
, upload_content
);
360 base::FilePath local_file_path
;
361 int64 range_offset
= 0;
362 int64 range_length
= 0;
363 if (GetContentFile(&local_file_path
, &range_offset
, &range_length
,
364 &upload_content_type
)) {
365 url_fetcher_
->SetUploadFilePath(
370 blocking_task_runner());
372 // Even if there is no content data, UrlFetcher requires to set empty
373 // upload data string for POST, PUT and PATCH methods, explicitly.
374 // It is because that most requests of those methods have non-empty
375 // body, and UrlFetcher checks whether it is actually not forgotten.
376 if (request_type
== URLFetcher::POST
||
377 request_type
== URLFetcher::PUT
||
378 request_type
== URLFetcher::PATCH
) {
379 // Set empty upload content-type and upload content, so that
380 // the request will have no "Content-type: " header and no content.
381 url_fetcher_
->SetUploadData(std::string(), std::string());
386 url_fetcher_
->Start();
389 URLFetcher::RequestType
UrlFetchRequestBase::GetRequestType() const {
390 return URLFetcher::GET
;
393 std::vector
<std::string
> UrlFetchRequestBase::GetExtraRequestHeaders() const {
394 return std::vector
<std::string
>();
397 bool UrlFetchRequestBase::GetContentData(std::string
* upload_content_type
,
398 std::string
* upload_content
) {
402 bool UrlFetchRequestBase::GetContentFile(base::FilePath
* local_file_path
,
405 std::string
* upload_content_type
) {
409 void UrlFetchRequestBase::GetOutputFilePath(
410 base::FilePath
* local_file_path
,
411 GetContentCallback
* get_content_callback
) {
414 void UrlFetchRequestBase::Cancel() {
415 response_writer_
= NULL
;
416 url_fetcher_
.reset(NULL
);
417 RunCallbackOnPrematureFailure(GDATA_CANCELLED
);
418 sender_
->RequestFinished(this);
421 GDataErrorCode
UrlFetchRequestBase::GetErrorCode() {
425 bool UrlFetchRequestBase::CalledOnValidThread() {
426 return thread_checker_
.CalledOnValidThread();
429 base::SequencedTaskRunner
* UrlFetchRequestBase::blocking_task_runner() const {
430 return sender_
->blocking_task_runner();
433 void UrlFetchRequestBase::OnProcessURLFetchResultsComplete() {
434 sender_
->RequestFinished(this);
437 void UrlFetchRequestBase::OnURLFetchComplete(const URLFetcher
* source
) {
438 DVLOG(1) << "Response headers:\n" << GetResponseHeadersAsString(source
);
440 // Determine error code.
441 error_code_
= static_cast<GDataErrorCode
>(source
->GetResponseCode());
442 if (!source
->GetStatus().is_success()) {
443 switch (source
->GetStatus().error()) {
444 case net::ERR_NETWORK_CHANGED
:
445 error_code_
= GDATA_NO_CONNECTION
;
448 error_code_
= GDATA_OTHER_ERROR
;
452 // The server may return detailed error status in JSON.
453 // See https://developers.google.com/drive/handle-errors
454 if (!IsSuccessfulResponseCode(error_code_
)) {
455 DVLOG(1) << response_writer_
->data();
457 const char kErrorKey
[] = "error";
458 const char kErrorErrorsKey
[] = "errors";
459 const char kErrorReasonKey
[] = "reason";
460 const char kErrorMessageKey
[] = "message";
461 const char kErrorReasonRateLimitExceeded
[] = "rateLimitExceeded";
462 const char kErrorReasonUserRateLimitExceeded
[] = "userRateLimitExceeded";
463 const char kErrorReasonQuotaExceeded
[] = "quotaExceeded";
465 scoped_ptr
<base::Value
> value(ParseJson(response_writer_
->data()));
466 base::DictionaryValue
* dictionary
= NULL
;
467 base::DictionaryValue
* error
= NULL
;
469 value
->GetAsDictionary(&dictionary
) &&
470 dictionary
->GetDictionaryWithoutPathExpansion(kErrorKey
, &error
)) {
471 // Get error message.
473 error
->GetStringWithoutPathExpansion(kErrorMessageKey
, &message
);
474 DLOG(ERROR
) << "code: " << error_code_
<< ", message: " << message
;
476 // Override the error code based on the reason of the first error.
477 base::ListValue
* errors
= NULL
;
478 base::DictionaryValue
* first_error
= NULL
;
479 if (error
->GetListWithoutPathExpansion(kErrorErrorsKey
, &errors
) &&
480 errors
->GetDictionary(0, &first_error
)) {
482 first_error
->GetStringWithoutPathExpansion(kErrorReasonKey
, &reason
);
483 if (reason
== kErrorReasonRateLimitExceeded
||
484 reason
== kErrorReasonUserRateLimitExceeded
)
485 error_code_
= HTTP_SERVICE_UNAVAILABLE
;
486 if (reason
== kErrorReasonQuotaExceeded
)
487 error_code_
= GDATA_NO_SPACE
;
492 // Handle authentication failure.
493 if (error_code_
== HTTP_UNAUTHORIZED
) {
494 if (++re_authenticate_count_
<= kMaxReAuthenticateAttemptsPerRequest
) {
495 // Reset re_authenticate_callback_ so Start() can be called again.
496 ReAuthenticateCallback callback
= re_authenticate_callback_
;
497 re_authenticate_callback_
.Reset();
502 OnAuthFailed(error_code_
);
506 // Overridden by each specialization
507 ProcessURLFetchResults(source
);
510 void UrlFetchRequestBase::OnAuthFailed(GDataErrorCode code
) {
511 RunCallbackOnPrematureFailure(code
);
512 sender_
->RequestFinished(this);
515 base::WeakPtr
<AuthenticatedRequestInterface
>
516 UrlFetchRequestBase::GetWeakPtr() {
517 return weak_ptr_factory_
.GetWeakPtr();
520 //============================ EntryActionRequest ============================
522 EntryActionRequest::EntryActionRequest(RequestSender
* sender
,
523 const EntryActionCallback
& callback
)
524 : UrlFetchRequestBase(sender
),
525 callback_(callback
) {
526 DCHECK(!callback_
.is_null());
529 EntryActionRequest::~EntryActionRequest() {}
531 void EntryActionRequest::ProcessURLFetchResults(const URLFetcher
* source
) {
532 callback_
.Run(GetErrorCode());
533 OnProcessURLFetchResultsComplete();
536 void EntryActionRequest::RunCallbackOnPrematureFailure(GDataErrorCode code
) {
540 //========================= InitiateUploadRequestBase ========================
542 InitiateUploadRequestBase::InitiateUploadRequestBase(
543 RequestSender
* sender
,
544 const InitiateUploadCallback
& callback
,
545 const std::string
& content_type
,
546 int64 content_length
)
547 : UrlFetchRequestBase(sender
),
549 content_type_(content_type
),
550 content_length_(content_length
) {
551 DCHECK(!callback_
.is_null());
552 DCHECK(!content_type_
.empty());
553 DCHECK_GE(content_length_
, 0);
556 InitiateUploadRequestBase::~InitiateUploadRequestBase() {}
558 void InitiateUploadRequestBase::ProcessURLFetchResults(
559 const URLFetcher
* source
) {
560 GDataErrorCode code
= GetErrorCode();
562 std::string upload_location
;
563 if (code
== HTTP_SUCCESS
) {
564 // Retrieve value of the first "Location" header.
565 source
->GetResponseHeaders()->EnumerateHeader(NULL
,
566 kUploadResponseLocation
,
570 callback_
.Run(code
, GURL(upload_location
));
571 OnProcessURLFetchResultsComplete();
574 void InitiateUploadRequestBase::RunCallbackOnPrematureFailure(
575 GDataErrorCode code
) {
576 callback_
.Run(code
, GURL());
579 std::vector
<std::string
>
580 InitiateUploadRequestBase::GetExtraRequestHeaders() const {
581 std::vector
<std::string
> headers
;
582 headers
.push_back(kUploadContentType
+ content_type_
);
584 kUploadContentLength
+ base::Int64ToString(content_length_
));
588 //============================ UploadRangeResponse =============================
590 UploadRangeResponse::UploadRangeResponse()
591 : code(HTTP_SUCCESS
),
592 start_position_received(0),
593 end_position_received(0) {
596 UploadRangeResponse::UploadRangeResponse(GDataErrorCode code
,
597 int64 start_position_received
,
598 int64 end_position_received
)
600 start_position_received(start_position_received
),
601 end_position_received(end_position_received
) {
604 UploadRangeResponse::~UploadRangeResponse() {
607 //========================== UploadRangeRequestBase ==========================
609 UploadRangeRequestBase::UploadRangeRequestBase(RequestSender
* sender
,
610 const GURL
& upload_url
)
611 : UrlFetchRequestBase(sender
),
612 upload_url_(upload_url
),
613 weak_ptr_factory_(this) {
616 UploadRangeRequestBase::~UploadRangeRequestBase() {}
618 GURL
UploadRangeRequestBase::GetURL() const {
619 // This is very tricky to get json from this request. To do that, &alt=json
620 // has to be appended not here but in InitiateUploadRequestBase::GetURL().
624 URLFetcher::RequestType
UploadRangeRequestBase::GetRequestType() const {
625 return URLFetcher::PUT
;
628 void UploadRangeRequestBase::ProcessURLFetchResults(
629 const URLFetcher
* source
) {
630 GDataErrorCode code
= GetErrorCode();
631 net::HttpResponseHeaders
* hdrs
= source
->GetResponseHeaders();
633 if (code
== HTTP_RESUME_INCOMPLETE
) {
634 // Retrieve value of the first "Range" header.
635 // The Range header is appeared only if there is at least one received
636 // byte. So, initialize the positions by 0 so that the [0,0) will be
637 // returned via the |callback_| for empty data case.
638 int64 start_position_received
= 0;
639 int64 end_position_received
= 0;
640 std::string range_received
;
641 hdrs
->EnumerateHeader(NULL
, kUploadResponseRange
, &range_received
);
642 if (!range_received
.empty()) { // Parse the range header.
643 std::vector
<net::HttpByteRange
> ranges
;
644 if (net::HttpUtil::ParseRangeHeader(range_received
, &ranges
) &&
646 // We only care about the first start-end pair in the range.
648 // Range header represents the range inclusively, while we are treating
649 // ranges exclusively (i.e., end_position_received should be one passed
650 // the last valid index). So "+ 1" is added.
651 start_position_received
= ranges
[0].first_byte_position();
652 end_position_received
= ranges
[0].last_byte_position() + 1;
655 // The Range header has the received data range, so the start position
656 // should be always 0.
657 DCHECK_EQ(start_position_received
, 0);
659 OnRangeRequestComplete(UploadRangeResponse(code
,
660 start_position_received
,
661 end_position_received
),
662 scoped_ptr
<base::Value
>());
664 OnProcessURLFetchResultsComplete();
665 } else if (code
== HTTP_CREATED
|| code
== HTTP_SUCCESS
) {
666 // The upload is successfully done. Parse the response which should be
667 // the entry's metadata.
668 ParseJsonOnBlockingPool(blocking_task_runner(),
669 response_writer()->data(),
670 base::Bind(&UploadRangeRequestBase::OnDataParsed
,
671 weak_ptr_factory_
.GetWeakPtr(),
674 // Failed to upload. Run callbacks to notify the error.
675 OnRangeRequestComplete(
676 UploadRangeResponse(code
, -1, -1), scoped_ptr
<base::Value
>());
677 OnProcessURLFetchResultsComplete();
681 void UploadRangeRequestBase::OnDataParsed(GDataErrorCode code
,
682 scoped_ptr
<base::Value
> value
) {
683 DCHECK(CalledOnValidThread());
684 DCHECK(code
== HTTP_CREATED
|| code
== HTTP_SUCCESS
);
686 OnRangeRequestComplete(UploadRangeResponse(code
, -1, -1), value
.Pass());
687 OnProcessURLFetchResultsComplete();
690 void UploadRangeRequestBase::RunCallbackOnPrematureFailure(
691 GDataErrorCode code
) {
692 OnRangeRequestComplete(
693 UploadRangeResponse(code
, 0, 0), scoped_ptr
<base::Value
>());
696 //========================== ResumeUploadRequestBase =========================
698 ResumeUploadRequestBase::ResumeUploadRequestBase(
699 RequestSender
* sender
,
700 const GURL
& upload_location
,
701 int64 start_position
,
703 int64 content_length
,
704 const std::string
& content_type
,
705 const base::FilePath
& local_file_path
)
706 : UploadRangeRequestBase(sender
, upload_location
),
707 start_position_(start_position
),
708 end_position_(end_position
),
709 content_length_(content_length
),
710 content_type_(content_type
),
711 local_file_path_(local_file_path
) {
712 DCHECK_LE(start_position_
, end_position_
);
715 ResumeUploadRequestBase::~ResumeUploadRequestBase() {}
717 std::vector
<std::string
>
718 ResumeUploadRequestBase::GetExtraRequestHeaders() const {
719 if (content_length_
== 0) {
720 // For uploading an empty document, just PUT an empty content.
721 DCHECK_EQ(start_position_
, 0);
722 DCHECK_EQ(end_position_
, 0);
723 return std::vector
<std::string
>();
726 // The header looks like
727 // Content-Range: bytes <start_position>-<end_position>/<content_length>
729 // Content-Range: bytes 7864320-8388607/13851821
730 // The header takes inclusive range, so we adjust by "end_position - 1".
731 DCHECK_GE(start_position_
, 0);
732 DCHECK_GT(end_position_
, 0);
733 DCHECK_GE(content_length_
, 0);
735 std::vector
<std::string
> headers
;
737 std::string(kUploadContentRange
) +
738 base::Int64ToString(start_position_
) + "-" +
739 base::Int64ToString(end_position_
- 1) + "/" +
740 base::Int64ToString(content_length_
));
744 bool ResumeUploadRequestBase::GetContentFile(
745 base::FilePath
* local_file_path
,
748 std::string
* upload_content_type
) {
749 if (start_position_
== end_position_
) {
754 *local_file_path
= local_file_path_
;
755 *range_offset
= start_position_
;
756 *range_length
= end_position_
- start_position_
;
757 *upload_content_type
= content_type_
;
761 //======================== GetUploadStatusRequestBase ========================
763 GetUploadStatusRequestBase::GetUploadStatusRequestBase(RequestSender
* sender
,
764 const GURL
& upload_url
,
765 int64 content_length
)
766 : UploadRangeRequestBase(sender
, upload_url
),
767 content_length_(content_length
) {}
769 GetUploadStatusRequestBase::~GetUploadStatusRequestBase() {}
771 std::vector
<std::string
>
772 GetUploadStatusRequestBase::GetExtraRequestHeaders() const {
773 // The header looks like
774 // Content-Range: bytes */<content_length>
776 // Content-Range: bytes */13851821
777 DCHECK_GE(content_length_
, 0);
779 std::vector
<std::string
> headers
;
781 std::string(kUploadContentRange
) + "*/" +
782 base::Int64ToString(content_length_
));
786 //========================= MultipartUploadRequestBase ========================
788 MultipartUploadRequestBase::MultipartUploadRequestBase(
789 RequestSender
* sender
,
790 const std::string
& title
,
791 const std::string
& parent_resource_id
,
792 const std::string
& content_type
,
793 int64 content_length
,
794 const base::Time
& modified_date
,
795 const base::Time
& last_viewed_by_me_date
,
796 const base::FilePath
& local_file_path
,
797 const FileResourceCallback
& callback
,
798 const ProgressCallback
& progress_callback
)
799 : UrlFetchRequestBase(sender
),
800 metadata_json_(CreateMultipartUploadMetadataJson(title
,
803 last_viewed_by_me_date
)),
804 content_type_(content_type
),
805 local_path_(local_file_path
),
806 has_modified_date_(!modified_date
.is_null()),
808 progress_callback_(progress_callback
),
809 weak_ptr_factory_(this) {
810 DCHECK(!content_type
.empty());
811 DCHECK_GE(content_length
, 0);
812 DCHECK(!local_file_path
.empty());
813 DCHECK(!callback
.is_null());
816 MultipartUploadRequestBase::~MultipartUploadRequestBase() {
819 void MultipartUploadRequestBase::Start(const std::string
& access_token
,
820 const std::string
& custom_user_agent
,
821 const ReAuthenticateCallback
& callback
) {
822 // If the request is cancelled, the request instance will be deleted in
823 // |UrlFetchRequestBase::Cancel| and OnPrepareUploadContent won't be called.
824 std::string
* const upload_content_type
= new std::string();
825 std::string
* const upload_content_data
= new std::string();
826 PostTaskAndReplyWithResult(
827 blocking_task_runner(), FROM_HERE
,
828 base::Bind(&GetMultipartContent
, boundary_
, metadata_json_
, content_type_
,
829 local_path_
, base::Unretained(upload_content_type
),
830 base::Unretained(upload_content_data
)),
831 base::Bind(&MultipartUploadRequestBase::OnPrepareUploadContent
,
832 weak_ptr_factory_
.GetWeakPtr(), access_token
,
833 custom_user_agent
, callback
, base::Owned(upload_content_type
),
834 base::Owned(upload_content_data
)));
837 void MultipartUploadRequestBase::OnPrepareUploadContent(
838 const std::string
& access_token
,
839 const std::string
& custom_user_agent
,
840 const ReAuthenticateCallback
& callback
,
841 std::string
* upload_content_type
,
842 std::string
* upload_content_data
,
845 RunCallbackOnPrematureFailure(GDATA_FILE_ERROR
);
848 upload_content_type_
.swap(*upload_content_type
);
849 upload_content_data_
.swap(*upload_content_data
);
850 UrlFetchRequestBase::Start(access_token
, custom_user_agent
, callback
);
853 void MultipartUploadRequestBase::SetBoundaryForTesting(
854 const std::string
& boundary
) {
855 boundary_
= boundary
;
858 bool MultipartUploadRequestBase::GetContentData(
859 std::string
* upload_content_type
,
860 std::string
* upload_content_data
) {
861 // TODO(hirono): Pass stream instead of actual data to reduce memory usage.
862 upload_content_type
->swap(upload_content_type_
);
863 upload_content_data
->swap(upload_content_data_
);
867 void MultipartUploadRequestBase::ProcessURLFetchResults(
868 const URLFetcher
* source
) {
869 // The upload is successfully done. Parse the response which should be
870 // the entry's metadata.
871 const GDataErrorCode code
= GetErrorCode();
872 if (code
== HTTP_CREATED
|| code
== HTTP_SUCCESS
) {
873 ParseJsonOnBlockingPool(
874 blocking_task_runner(), response_writer()->data(),
875 base::Bind(&MultipartUploadRequestBase::OnDataParsed
,
876 weak_ptr_factory_
.GetWeakPtr(), code
));
878 OnDataParsed(code
, scoped_ptr
<base::Value
>());
882 void MultipartUploadRequestBase::RunCallbackOnPrematureFailure(
883 GDataErrorCode code
) {
884 callback_
.Run(code
, scoped_ptr
<FileResource
>());
887 void MultipartUploadRequestBase::OnURLFetchUploadProgress(
888 const net::URLFetcher
* source
,
891 if (!progress_callback_
.is_null())
892 progress_callback_
.Run(current
, total
);
895 void MultipartUploadRequestBase::OnDataParsed(GDataErrorCode code
,
896 scoped_ptr
<base::Value
> value
) {
897 DCHECK(CalledOnValidThread());
899 callback_
.Run(code
, google_apis::FileResource::CreateFrom(*value
));
901 callback_
.Run(GDATA_PARSE_ERROR
, scoped_ptr
<FileResource
>());
902 OnProcessURLFetchResultsComplete();
905 //============================ DownloadFileRequestBase =========================
907 DownloadFileRequestBase::DownloadFileRequestBase(
908 RequestSender
* sender
,
909 const DownloadActionCallback
& download_action_callback
,
910 const GetContentCallback
& get_content_callback
,
911 const ProgressCallback
& progress_callback
,
912 const GURL
& download_url
,
913 const base::FilePath
& output_file_path
)
914 : UrlFetchRequestBase(sender
),
915 download_action_callback_(download_action_callback
),
916 get_content_callback_(get_content_callback
),
917 progress_callback_(progress_callback
),
918 download_url_(download_url
),
919 output_file_path_(output_file_path
) {
920 DCHECK(!download_action_callback_
.is_null());
921 DCHECK(!output_file_path_
.empty());
922 // get_content_callback may be null.
925 DownloadFileRequestBase::~DownloadFileRequestBase() {}
927 // Overridden from UrlFetchRequestBase.
928 GURL
DownloadFileRequestBase::GetURL() const {
929 return download_url_
;
932 void DownloadFileRequestBase::GetOutputFilePath(
933 base::FilePath
* local_file_path
,
934 GetContentCallback
* get_content_callback
) {
935 // Configure so that the downloaded content is saved to |output_file_path_|.
936 *local_file_path
= output_file_path_
;
937 *get_content_callback
= get_content_callback_
;
940 void DownloadFileRequestBase::OnURLFetchDownloadProgress(
941 const URLFetcher
* source
,
944 if (!progress_callback_
.is_null())
945 progress_callback_
.Run(current
, total
);
948 void DownloadFileRequestBase::ProcessURLFetchResults(const URLFetcher
* source
) {
949 GDataErrorCode code
= GetErrorCode();
951 // Take over the ownership of the the downloaded temp file.
952 base::FilePath temp_file
;
953 if (code
== HTTP_SUCCESS
) {
954 response_writer()->DisownFile();
955 temp_file
= output_file_path_
;
958 download_action_callback_
.Run(code
, temp_file
);
959 OnProcessURLFetchResultsComplete();
962 void DownloadFileRequestBase::RunCallbackOnPrematureFailure(
963 GDataErrorCode code
) {
964 download_action_callback_
.Run(code
, base::FilePath());
967 } // namespace google_apis