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/drive_api_requests.h"
8 #include "base/callback.h"
9 #include "base/json/json_writer.h"
10 #include "base/location.h"
11 #include "base/metrics/histogram_macros.h"
12 #include "base/metrics/sparse_histogram.h"
13 #include "base/sequenced_task_runner.h"
14 #include "base/strings/string_number_conversions.h"
15 #include "base/strings/string_piece.h"
16 #include "base/strings/stringprintf.h"
17 #include "base/task_runner_util.h"
18 #include "base/values.h"
19 #include "google_apis/drive/request_sender.h"
20 #include "google_apis/drive/request_util.h"
21 #include "google_apis/drive/time_util.h"
22 #include "net/base/url_util.h"
23 #include "net/http/http_response_headers.h"
25 namespace google_apis
{
29 // Format of one request in batch uploading request.
30 const char kBatchUploadRequestFormat
[] =
33 "X-Goog-Upload-Protocol: multipart\n"
37 // Request header for specifying batch upload.
38 const char kBatchUploadHeader
[] = "X-Goog-Upload-Protocol: batch";
40 // Content type of HTTP request.
41 const char kHttpContentType
[] = "application/http";
43 // Break line in HTTP message.
44 const char kHttpBr
[] = "\r\n";
46 // Mime type of multipart mixed.
47 const char kMultipartMixedMimeTypePrefix
[] = "multipart/mixed; boundary=";
50 const char kUMADriveBatchUploadResponseCode
[] = "Drive.BatchUploadResponseCode";
51 const char kUMADriveTotalFileCountInBatchUpload
[] =
52 "Drive.TotalFileCountInBatchUpload";
53 const char kUMADriveTotalFileSizeInBatchUpload
[] =
54 "Drive.TotalFileSizeInBatchUpload";
56 // Parses the JSON value to FileResource instance and runs |callback| on the
57 // UI thread once parsing is done.
58 // This is customized version of ParseJsonAndRun defined above to adapt the
59 // remaining response type.
60 void ParseFileResourceWithUploadRangeAndRun(const UploadRangeCallback
& callback
,
61 const UploadRangeResponse
& response
,
62 scoped_ptr
<base::Value
> value
) {
63 DCHECK(!callback
.is_null());
65 scoped_ptr
<FileResource
> file_resource
;
67 file_resource
= FileResource::CreateFrom(*value
);
70 UploadRangeResponse(DRIVE_PARSE_ERROR
,
71 response
.start_position_received
,
72 response
.end_position_received
),
73 scoped_ptr
<FileResource
>());
78 callback
.Run(response
, file_resource
.Pass());
81 // Attaches |properties| to the |request_body| if |properties| is not empty.
82 // |request_body| must not be NULL.
83 void AttachProperties(const Properties
& properties
,
84 base::DictionaryValue
* request_body
) {
86 if (properties
.empty())
89 base::ListValue
* const properties_value
= new base::ListValue
;
90 for (const auto& property
: properties
) {
91 base::DictionaryValue
* const property_value
= new base::DictionaryValue
;
92 std::string visibility_as_string
;
93 switch (property
.visibility()) {
94 case Property::VISIBILITY_PRIVATE
:
95 visibility_as_string
= "PRIVATE";
97 case Property::VISIBILITY_PUBLIC
:
98 visibility_as_string
= "PUBLIC";
101 property_value
->SetString("visibility", visibility_as_string
);
102 property_value
->SetString("key", property
.key());
103 property_value
->SetString("value", property
.value());
104 properties_value
->Append(property_value
);
106 request_body
->Set("properties", properties_value
);
109 // Creates metadata JSON string for multipart uploading.
110 // All the values are optional. If the value is empty or null, the value does
111 // not appear in the metadata.
112 std::string
CreateMultipartUploadMetadataJson(
113 const std::string
& title
,
114 const std::string
& parent_resource_id
,
115 const base::Time
& modified_date
,
116 const base::Time
& last_viewed_by_me_date
,
117 const Properties
& properties
) {
118 base::DictionaryValue root
;
120 root
.SetString("title", title
);
123 if (!parent_resource_id
.empty()) {
124 scoped_ptr
<base::ListValue
> parents(new base::ListValue
);
126 google_apis::util::CreateParentValue(parent_resource_id
).release());
127 root
.Set("parents", parents
.release());
130 if (!modified_date
.is_null()) {
131 root
.SetString("modifiedDate",
132 google_apis::util::FormatTimeAsString(modified_date
));
135 if (!last_viewed_by_me_date
.is_null()) {
136 root
.SetString("lastViewedByMeDate", google_apis::util::FormatTimeAsString(
137 last_viewed_by_me_date
));
140 AttachProperties(properties
, &root
);
141 std::string json_string
;
142 base::JSONWriter::Write(root
, &json_string
);
146 // Splits |string| into lines by |kHttpBr|.
147 // Each line does not include |kHttpBr|.
148 void SplitIntoLines(const std::string
& string
,
149 std::vector
<base::StringPiece
>* output
) {
150 const size_t br_size
= std::string(kHttpBr
).size();
151 std::string::const_iterator it
= string
.begin();
152 std::vector
<base::StringPiece
> lines
;
154 const std::string::const_iterator next_pos
=
155 std::search(it
, string
.end(), kHttpBr
, kHttpBr
+ br_size
);
156 lines
.push_back(base::StringPiece(it
, next_pos
));
157 if (next_pos
== string
.end())
159 it
= next_pos
+ br_size
;
164 // Remove transport padding (spaces and tabs at the end of line) from |piece|.
165 base::StringPiece
TrimTransportPadding(const base::StringPiece
& piece
) {
166 size_t trim_size
= 0;
167 while (trim_size
< piece
.size() &&
168 (piece
[piece
.size() - 1 - trim_size
] == ' ' ||
169 piece
[piece
.size() - 1 - trim_size
] == '\t')) {
172 return piece
.substr(0, piece
.size() - trim_size
);
175 void EmptyClosure(scoped_ptr
<BatchableDelegate
>) {
180 MultipartHttpResponse::MultipartHttpResponse() : code(HTTP_SUCCESS
) {
183 MultipartHttpResponse::~MultipartHttpResponse() {
186 // The |response| must be multipart/mixed format that contains child HTTP
187 // response of drive batch request.
188 // https://www.ietf.org/rfc/rfc2046.txt
192 // Content-type: application/http
195 // Header of child response
197 // Body of child response
199 // Content-type: application/http
201 // HTTP/1.1 404 Not Found
202 // Header of child response
204 // Body of child response
206 bool ParseMultipartResponse(const std::string
& content_type
,
207 const std::string
& response
,
208 std::vector
<MultipartHttpResponse
>* parts
) {
209 if (response
.empty())
212 base::StringPiece
content_type_piece(content_type
);
213 if (!content_type_piece
.starts_with(kMultipartMixedMimeTypePrefix
)) {
216 content_type_piece
.remove_prefix(
217 base::StringPiece(kMultipartMixedMimeTypePrefix
).size());
219 if (content_type_piece
.empty())
221 if (content_type_piece
[0] == '"') {
222 if (content_type_piece
.size() <= 2 ||
223 content_type_piece
[content_type_piece
.size() - 1] != '"') {
227 content_type_piece
.substr(1, content_type_piece
.size() - 2);
230 std::string boundary
;
231 content_type_piece
.CopyToString(&boundary
);
232 const std::string header
= "--" + boundary
;
233 const std::string terminator
= "--" + boundary
+ "--";
235 std::vector
<base::StringPiece
> lines
;
236 SplitIntoLines(response
, &lines
);
241 STATE_PART_HTTP_STATUS_LINE
,
242 STATE_PART_HTTP_HEADER
,
244 } state
= STATE_START
;
246 const std::string kHttpStatusPrefix
= "HTTP/1.1 ";
247 std::vector
<MultipartHttpResponse
> responses
;
248 DriveApiErrorCode code
= DRIVE_PARSE_ERROR
;
250 for (const auto& line
: lines
) {
251 if (state
== STATE_PART_HEADER
&& line
.empty()) {
252 state
= STATE_PART_HTTP_STATUS_LINE
;
256 if (state
== STATE_PART_HTTP_STATUS_LINE
) {
257 if (line
.starts_with(kHttpStatusPrefix
)) {
260 line
.substr(base::StringPiece(kHttpStatusPrefix
).size()),
263 code
= static_cast<DriveApiErrorCode
>(int_code
);
265 code
= DRIVE_PARSE_ERROR
;
267 code
= DRIVE_PARSE_ERROR
;
269 state
= STATE_PART_HTTP_HEADER
;
273 if (state
== STATE_PART_HTTP_HEADER
&& line
.empty()) {
274 state
= STATE_PART_HTTP_BODY
;
278 const base::StringPiece chopped_line
= TrimTransportPadding(line
);
279 const bool is_new_part
= chopped_line
== header
;
280 const bool was_last_part
= chopped_line
== terminator
;
281 if (is_new_part
|| was_last_part
) {
285 case STATE_PART_HEADER
:
286 case STATE_PART_HTTP_STATUS_LINE
:
287 responses
.push_back(MultipartHttpResponse());
288 responses
.back().code
= DRIVE_PARSE_ERROR
;
290 case STATE_PART_HTTP_HEADER
:
291 responses
.push_back(MultipartHttpResponse());
292 responses
.back().code
= code
;
294 case STATE_PART_HTTP_BODY
:
295 // Drop the last kHttpBr.
297 body
.resize(body
.size() - 2);
298 responses
.push_back(MultipartHttpResponse());
299 responses
.back().code
= code
;
300 responses
.back().body
.swap(body
);
304 state
= STATE_PART_HEADER
;
307 } else if (state
== STATE_PART_HTTP_BODY
) {
308 line
.AppendToString(&body
);
309 body
.append(kHttpBr
);
313 parts
->swap(responses
);
317 Property::Property() : visibility_(VISIBILITY_PRIVATE
) {
320 Property::~Property() {
323 //============================ DriveApiPartialFieldRequest ====================
325 DriveApiPartialFieldRequest::DriveApiPartialFieldRequest(
326 RequestSender
* sender
) : UrlFetchRequestBase(sender
) {
329 DriveApiPartialFieldRequest::~DriveApiPartialFieldRequest() {
332 GURL
DriveApiPartialFieldRequest::GetURL() const {
333 GURL url
= GetURLInternal();
334 if (!fields_
.empty())
335 url
= net::AppendOrReplaceQueryParameter(url
, "fields", fields_
);
339 //=============================== FilesGetRequest =============================
341 FilesGetRequest::FilesGetRequest(
342 RequestSender
* sender
,
343 const DriveApiUrlGenerator
& url_generator
,
344 bool use_internal_endpoint
,
345 const FileResourceCallback
& callback
)
346 : DriveApiDataRequest
<FileResource
>(sender
, callback
),
347 url_generator_(url_generator
),
348 use_internal_endpoint_(use_internal_endpoint
) {
349 DCHECK(!callback
.is_null());
352 FilesGetRequest::~FilesGetRequest() {}
354 GURL
FilesGetRequest::GetURLInternal() const {
355 return url_generator_
.GetFilesGetUrl(file_id_
,
356 use_internal_endpoint_
,
360 //============================ FilesAuthorizeRequest ===========================
362 FilesAuthorizeRequest::FilesAuthorizeRequest(
363 RequestSender
* sender
,
364 const DriveApiUrlGenerator
& url_generator
,
365 const FileResourceCallback
& callback
)
366 : DriveApiDataRequest
<FileResource
>(sender
, callback
),
367 url_generator_(url_generator
) {
368 DCHECK(!callback
.is_null());
371 FilesAuthorizeRequest::~FilesAuthorizeRequest() {}
373 net::URLFetcher::RequestType
FilesAuthorizeRequest::GetRequestType() const {
374 return net::URLFetcher::POST
;
377 GURL
FilesAuthorizeRequest::GetURLInternal() const {
378 return url_generator_
.GetFilesAuthorizeUrl(file_id_
, app_id_
);
381 //============================ FilesInsertRequest ============================
383 FilesInsertRequest::FilesInsertRequest(
384 RequestSender
* sender
,
385 const DriveApiUrlGenerator
& url_generator
,
386 const FileResourceCallback
& callback
)
387 : DriveApiDataRequest
<FileResource
>(sender
, callback
),
388 url_generator_(url_generator
),
389 visibility_(FILE_VISIBILITY_DEFAULT
) {
390 DCHECK(!callback
.is_null());
393 FilesInsertRequest::~FilesInsertRequest() {}
395 net::URLFetcher::RequestType
FilesInsertRequest::GetRequestType() const {
396 return net::URLFetcher::POST
;
399 bool FilesInsertRequest::GetContentData(std::string
* upload_content_type
,
400 std::string
* upload_content
) {
401 *upload_content_type
= util::kContentTypeApplicationJson
;
403 base::DictionaryValue root
;
405 if (!last_viewed_by_me_date_
.is_null()) {
406 root
.SetString("lastViewedByMeDate",
407 util::FormatTimeAsString(last_viewed_by_me_date_
));
410 if (!mime_type_
.empty())
411 root
.SetString("mimeType", mime_type_
);
413 if (!modified_date_
.is_null())
414 root
.SetString("modifiedDate", util::FormatTimeAsString(modified_date_
));
416 if (!parents_
.empty()) {
417 base::ListValue
* parents_value
= new base::ListValue
;
418 for (size_t i
= 0; i
< parents_
.size(); ++i
) {
419 base::DictionaryValue
* parent
= new base::DictionaryValue
;
420 parent
->SetString("id", parents_
[i
]);
421 parents_value
->Append(parent
);
423 root
.Set("parents", parents_value
);
427 root
.SetString("title", title_
);
429 AttachProperties(properties_
, &root
);
430 base::JSONWriter::Write(root
, upload_content
);
432 DVLOG(1) << "FilesInsert data: " << *upload_content_type
<< ", ["
433 << *upload_content
<< "]";
437 GURL
FilesInsertRequest::GetURLInternal() const {
438 return url_generator_
.GetFilesInsertUrl(
439 visibility_
== FILE_VISIBILITY_PRIVATE
? "PRIVATE" : "");
442 //============================== FilesPatchRequest ============================
444 FilesPatchRequest::FilesPatchRequest(
445 RequestSender
* sender
,
446 const DriveApiUrlGenerator
& url_generator
,
447 const FileResourceCallback
& callback
)
448 : DriveApiDataRequest
<FileResource
>(sender
, callback
),
449 url_generator_(url_generator
),
450 set_modified_date_(false),
451 update_viewed_date_(true) {
452 DCHECK(!callback
.is_null());
455 FilesPatchRequest::~FilesPatchRequest() {}
457 net::URLFetcher::RequestType
FilesPatchRequest::GetRequestType() const {
458 return net::URLFetcher::PATCH
;
461 std::vector
<std::string
> FilesPatchRequest::GetExtraRequestHeaders() const {
462 std::vector
<std::string
> headers
;
463 headers
.push_back(util::kIfMatchAllHeader
);
467 GURL
FilesPatchRequest::GetURLInternal() const {
468 return url_generator_
.GetFilesPatchUrl(
469 file_id_
, set_modified_date_
, update_viewed_date_
);
472 bool FilesPatchRequest::GetContentData(std::string
* upload_content_type
,
473 std::string
* upload_content
) {
474 if (title_
.empty() &&
475 modified_date_
.is_null() &&
476 last_viewed_by_me_date_
.is_null() &&
480 *upload_content_type
= util::kContentTypeApplicationJson
;
482 base::DictionaryValue root
;
484 root
.SetString("title", title_
);
486 if (!modified_date_
.is_null())
487 root
.SetString("modifiedDate", util::FormatTimeAsString(modified_date_
));
489 if (!last_viewed_by_me_date_
.is_null()) {
490 root
.SetString("lastViewedByMeDate",
491 util::FormatTimeAsString(last_viewed_by_me_date_
));
494 if (!parents_
.empty()) {
495 base::ListValue
* parents_value
= new base::ListValue
;
496 for (size_t i
= 0; i
< parents_
.size(); ++i
) {
497 base::DictionaryValue
* parent
= new base::DictionaryValue
;
498 parent
->SetString("id", parents_
[i
]);
499 parents_value
->Append(parent
);
501 root
.Set("parents", parents_value
);
504 AttachProperties(properties_
, &root
);
505 base::JSONWriter::Write(root
, upload_content
);
507 DVLOG(1) << "FilesPatch data: " << *upload_content_type
<< ", ["
508 << *upload_content
<< "]";
512 //============================= FilesCopyRequest ==============================
514 FilesCopyRequest::FilesCopyRequest(
515 RequestSender
* sender
,
516 const DriveApiUrlGenerator
& url_generator
,
517 const FileResourceCallback
& callback
)
518 : DriveApiDataRequest
<FileResource
>(sender
, callback
),
519 url_generator_(url_generator
),
520 visibility_(FILE_VISIBILITY_DEFAULT
) {
521 DCHECK(!callback
.is_null());
524 FilesCopyRequest::~FilesCopyRequest() {
527 net::URLFetcher::RequestType
FilesCopyRequest::GetRequestType() const {
528 return net::URLFetcher::POST
;
531 GURL
FilesCopyRequest::GetURLInternal() const {
532 return url_generator_
.GetFilesCopyUrl(
533 file_id_
, visibility_
== FILE_VISIBILITY_PRIVATE
? "PRIVATE" : "");
536 bool FilesCopyRequest::GetContentData(std::string
* upload_content_type
,
537 std::string
* upload_content
) {
538 if (parents_
.empty() && title_
.empty())
541 *upload_content_type
= util::kContentTypeApplicationJson
;
543 base::DictionaryValue root
;
545 if (!modified_date_
.is_null())
546 root
.SetString("modifiedDate", util::FormatTimeAsString(modified_date_
));
548 if (!parents_
.empty()) {
549 base::ListValue
* parents_value
= new base::ListValue
;
550 for (size_t i
= 0; i
< parents_
.size(); ++i
) {
551 base::DictionaryValue
* parent
= new base::DictionaryValue
;
552 parent
->SetString("id", parents_
[i
]);
553 parents_value
->Append(parent
);
555 root
.Set("parents", parents_value
);
559 root
.SetString("title", title_
);
561 base::JSONWriter::Write(root
, upload_content
);
562 DVLOG(1) << "FilesCopy data: " << *upload_content_type
<< ", ["
563 << *upload_content
<< "]";
567 //============================= FilesListRequest =============================
569 FilesListRequest::FilesListRequest(
570 RequestSender
* sender
,
571 const DriveApiUrlGenerator
& url_generator
,
572 const FileListCallback
& callback
)
573 : DriveApiDataRequest
<FileList
>(sender
, callback
),
574 url_generator_(url_generator
),
576 DCHECK(!callback
.is_null());
579 FilesListRequest::~FilesListRequest() {}
581 GURL
FilesListRequest::GetURLInternal() const {
582 return url_generator_
.GetFilesListUrl(max_results_
, page_token_
, q_
);
585 //======================== FilesListNextPageRequest =========================
587 FilesListNextPageRequest::FilesListNextPageRequest(
588 RequestSender
* sender
,
589 const FileListCallback
& callback
)
590 : DriveApiDataRequest
<FileList
>(sender
, callback
) {
591 DCHECK(!callback
.is_null());
594 FilesListNextPageRequest::~FilesListNextPageRequest() {
597 GURL
FilesListNextPageRequest::GetURLInternal() const {
601 //============================ FilesDeleteRequest =============================
603 FilesDeleteRequest::FilesDeleteRequest(
604 RequestSender
* sender
,
605 const DriveApiUrlGenerator
& url_generator
,
606 const EntryActionCallback
& callback
)
607 : EntryActionRequest(sender
, callback
),
608 url_generator_(url_generator
) {
609 DCHECK(!callback
.is_null());
612 FilesDeleteRequest::~FilesDeleteRequest() {}
614 net::URLFetcher::RequestType
FilesDeleteRequest::GetRequestType() const {
615 return net::URLFetcher::DELETE_REQUEST
;
618 GURL
FilesDeleteRequest::GetURL() const {
619 return url_generator_
.GetFilesDeleteUrl(file_id_
);
622 std::vector
<std::string
> FilesDeleteRequest::GetExtraRequestHeaders() const {
623 std::vector
<std::string
> headers(
624 EntryActionRequest::GetExtraRequestHeaders());
625 headers
.push_back(util::GenerateIfMatchHeader(etag_
));
629 //============================ FilesTrashRequest =============================
631 FilesTrashRequest::FilesTrashRequest(
632 RequestSender
* sender
,
633 const DriveApiUrlGenerator
& url_generator
,
634 const FileResourceCallback
& callback
)
635 : DriveApiDataRequest
<FileResource
>(sender
, callback
),
636 url_generator_(url_generator
) {
637 DCHECK(!callback
.is_null());
640 FilesTrashRequest::~FilesTrashRequest() {}
642 net::URLFetcher::RequestType
FilesTrashRequest::GetRequestType() const {
643 return net::URLFetcher::POST
;
646 GURL
FilesTrashRequest::GetURLInternal() const {
647 return url_generator_
.GetFilesTrashUrl(file_id_
);
650 //============================== AboutGetRequest =============================
652 AboutGetRequest::AboutGetRequest(
653 RequestSender
* sender
,
654 const DriveApiUrlGenerator
& url_generator
,
655 const AboutResourceCallback
& callback
)
656 : DriveApiDataRequest
<AboutResource
>(sender
, callback
),
657 url_generator_(url_generator
) {
658 DCHECK(!callback
.is_null());
661 AboutGetRequest::~AboutGetRequest() {}
663 GURL
AboutGetRequest::GetURLInternal() const {
664 return url_generator_
.GetAboutGetUrl();
667 //============================ ChangesListRequest ===========================
669 ChangesListRequest::ChangesListRequest(
670 RequestSender
* sender
,
671 const DriveApiUrlGenerator
& url_generator
,
672 const ChangeListCallback
& callback
)
673 : DriveApiDataRequest
<ChangeList
>(sender
, callback
),
674 url_generator_(url_generator
),
675 include_deleted_(true),
677 start_change_id_(0) {
678 DCHECK(!callback
.is_null());
681 ChangesListRequest::~ChangesListRequest() {}
683 GURL
ChangesListRequest::GetURLInternal() const {
684 return url_generator_
.GetChangesListUrl(
685 include_deleted_
, max_results_
, page_token_
, start_change_id_
);
688 //======================== ChangesListNextPageRequest =========================
690 ChangesListNextPageRequest::ChangesListNextPageRequest(
691 RequestSender
* sender
,
692 const ChangeListCallback
& callback
)
693 : DriveApiDataRequest
<ChangeList
>(sender
, callback
) {
694 DCHECK(!callback
.is_null());
697 ChangesListNextPageRequest::~ChangesListNextPageRequest() {
700 GURL
ChangesListNextPageRequest::GetURLInternal() const {
704 //============================== AppsListRequest ===========================
706 AppsListRequest::AppsListRequest(
707 RequestSender
* sender
,
708 const DriveApiUrlGenerator
& url_generator
,
709 bool use_internal_endpoint
,
710 const AppListCallback
& callback
)
711 : DriveApiDataRequest
<AppList
>(sender
, callback
),
712 url_generator_(url_generator
),
713 use_internal_endpoint_(use_internal_endpoint
) {
714 DCHECK(!callback
.is_null());
717 AppsListRequest::~AppsListRequest() {}
719 GURL
AppsListRequest::GetURLInternal() const {
720 return url_generator_
.GetAppsListUrl(use_internal_endpoint_
);
723 //============================== AppsDeleteRequest ===========================
725 AppsDeleteRequest::AppsDeleteRequest(RequestSender
* sender
,
726 const DriveApiUrlGenerator
& url_generator
,
727 const EntryActionCallback
& callback
)
728 : EntryActionRequest(sender
, callback
),
729 url_generator_(url_generator
) {
730 DCHECK(!callback
.is_null());
733 AppsDeleteRequest::~AppsDeleteRequest() {}
735 net::URLFetcher::RequestType
AppsDeleteRequest::GetRequestType() const {
736 return net::URLFetcher::DELETE_REQUEST
;
739 GURL
AppsDeleteRequest::GetURL() const {
740 return url_generator_
.GetAppsDeleteUrl(app_id_
);
743 //========================== ChildrenInsertRequest ============================
745 ChildrenInsertRequest::ChildrenInsertRequest(
746 RequestSender
* sender
,
747 const DriveApiUrlGenerator
& url_generator
,
748 const EntryActionCallback
& callback
)
749 : EntryActionRequest(sender
, callback
),
750 url_generator_(url_generator
) {
751 DCHECK(!callback
.is_null());
754 ChildrenInsertRequest::~ChildrenInsertRequest() {}
756 net::URLFetcher::RequestType
ChildrenInsertRequest::GetRequestType() const {
757 return net::URLFetcher::POST
;
760 GURL
ChildrenInsertRequest::GetURL() const {
761 return url_generator_
.GetChildrenInsertUrl(folder_id_
);
764 bool ChildrenInsertRequest::GetContentData(std::string
* upload_content_type
,
765 std::string
* upload_content
) {
766 *upload_content_type
= util::kContentTypeApplicationJson
;
768 base::DictionaryValue root
;
769 root
.SetString("id", id_
);
771 base::JSONWriter::Write(root
, upload_content
);
772 DVLOG(1) << "InsertResource data: " << *upload_content_type
<< ", ["
773 << *upload_content
<< "]";
777 //========================== ChildrenDeleteRequest ============================
779 ChildrenDeleteRequest::ChildrenDeleteRequest(
780 RequestSender
* sender
,
781 const DriveApiUrlGenerator
& url_generator
,
782 const EntryActionCallback
& callback
)
783 : EntryActionRequest(sender
, callback
),
784 url_generator_(url_generator
) {
785 DCHECK(!callback
.is_null());
788 ChildrenDeleteRequest::~ChildrenDeleteRequest() {}
790 net::URLFetcher::RequestType
ChildrenDeleteRequest::GetRequestType() const {
791 return net::URLFetcher::DELETE_REQUEST
;
794 GURL
ChildrenDeleteRequest::GetURL() const {
795 return url_generator_
.GetChildrenDeleteUrl(child_id_
, folder_id_
);
798 //======================= InitiateUploadNewFileRequest =======================
800 InitiateUploadNewFileRequest::InitiateUploadNewFileRequest(
801 RequestSender
* sender
,
802 const DriveApiUrlGenerator
& url_generator
,
803 const std::string
& content_type
,
804 int64 content_length
,
805 const std::string
& parent_resource_id
,
806 const std::string
& title
,
807 const InitiateUploadCallback
& callback
)
808 : InitiateUploadRequestBase(sender
,
812 url_generator_(url_generator
),
813 parent_resource_id_(parent_resource_id
),
817 InitiateUploadNewFileRequest::~InitiateUploadNewFileRequest() {}
819 GURL
InitiateUploadNewFileRequest::GetURL() const {
820 return url_generator_
.GetInitiateUploadNewFileUrl(!modified_date_
.is_null());
823 net::URLFetcher::RequestType
824 InitiateUploadNewFileRequest::GetRequestType() const {
825 return net::URLFetcher::POST
;
828 bool InitiateUploadNewFileRequest::GetContentData(
829 std::string
* upload_content_type
,
830 std::string
* upload_content
) {
831 *upload_content_type
= util::kContentTypeApplicationJson
;
833 base::DictionaryValue root
;
834 root
.SetString("title", title_
);
837 scoped_ptr
<base::ListValue
> parents(new base::ListValue
);
838 parents
->Append(util::CreateParentValue(parent_resource_id_
).release());
839 root
.Set("parents", parents
.release());
841 if (!modified_date_
.is_null())
842 root
.SetString("modifiedDate", util::FormatTimeAsString(modified_date_
));
844 if (!last_viewed_by_me_date_
.is_null()) {
845 root
.SetString("lastViewedByMeDate",
846 util::FormatTimeAsString(last_viewed_by_me_date_
));
849 AttachProperties(properties_
, &root
);
850 base::JSONWriter::Write(root
, upload_content
);
852 DVLOG(1) << "InitiateUploadNewFile data: " << *upload_content_type
<< ", ["
853 << *upload_content
<< "]";
857 //===================== InitiateUploadExistingFileRequest ====================
859 InitiateUploadExistingFileRequest::InitiateUploadExistingFileRequest(
860 RequestSender
* sender
,
861 const DriveApiUrlGenerator
& url_generator
,
862 const std::string
& content_type
,
863 int64 content_length
,
864 const std::string
& resource_id
,
865 const std::string
& etag
,
866 const InitiateUploadCallback
& callback
)
867 : InitiateUploadRequestBase(sender
,
871 url_generator_(url_generator
),
872 resource_id_(resource_id
),
876 InitiateUploadExistingFileRequest::~InitiateUploadExistingFileRequest() {}
878 GURL
InitiateUploadExistingFileRequest::GetURL() const {
879 return url_generator_
.GetInitiateUploadExistingFileUrl(
880 resource_id_
, !modified_date_
.is_null());
883 net::URLFetcher::RequestType
884 InitiateUploadExistingFileRequest::GetRequestType() const {
885 return net::URLFetcher::PUT
;
888 std::vector
<std::string
>
889 InitiateUploadExistingFileRequest::GetExtraRequestHeaders() const {
890 std::vector
<std::string
> headers(
891 InitiateUploadRequestBase::GetExtraRequestHeaders());
892 headers
.push_back(util::GenerateIfMatchHeader(etag_
));
896 bool InitiateUploadExistingFileRequest::GetContentData(
897 std::string
* upload_content_type
,
898 std::string
* upload_content
) {
899 base::DictionaryValue root
;
900 if (!parent_resource_id_
.empty()) {
901 scoped_ptr
<base::ListValue
> parents(new base::ListValue
);
902 parents
->Append(util::CreateParentValue(parent_resource_id_
).release());
903 root
.Set("parents", parents
.release());
907 root
.SetString("title", title_
);
909 if (!modified_date_
.is_null())
910 root
.SetString("modifiedDate", util::FormatTimeAsString(modified_date_
));
912 if (!last_viewed_by_me_date_
.is_null()) {
913 root
.SetString("lastViewedByMeDate",
914 util::FormatTimeAsString(last_viewed_by_me_date_
));
917 AttachProperties(properties_
, &root
);
921 *upload_content_type
= util::kContentTypeApplicationJson
;
922 base::JSONWriter::Write(root
, upload_content
);
923 DVLOG(1) << "InitiateUploadExistingFile data: " << *upload_content_type
924 << ", [" << *upload_content
<< "]";
928 //============================ ResumeUploadRequest ===========================
930 ResumeUploadRequest::ResumeUploadRequest(
931 RequestSender
* sender
,
932 const GURL
& upload_location
,
933 int64 start_position
,
935 int64 content_length
,
936 const std::string
& content_type
,
937 const base::FilePath
& local_file_path
,
938 const UploadRangeCallback
& callback
,
939 const ProgressCallback
& progress_callback
)
940 : ResumeUploadRequestBase(sender
,
948 progress_callback_(progress_callback
) {
949 DCHECK(!callback_
.is_null());
952 ResumeUploadRequest::~ResumeUploadRequest() {}
954 void ResumeUploadRequest::OnRangeRequestComplete(
955 const UploadRangeResponse
& response
,
956 scoped_ptr
<base::Value
> value
) {
957 DCHECK(CalledOnValidThread());
958 ParseFileResourceWithUploadRangeAndRun(callback_
, response
, value
.Pass());
961 void ResumeUploadRequest::OnURLFetchUploadProgress(
962 const net::URLFetcher
* source
, int64 current
, int64 total
) {
963 if (!progress_callback_
.is_null())
964 progress_callback_
.Run(current
, total
);
967 //========================== GetUploadStatusRequest ==========================
969 GetUploadStatusRequest::GetUploadStatusRequest(
970 RequestSender
* sender
,
971 const GURL
& upload_url
,
972 int64 content_length
,
973 const UploadRangeCallback
& callback
)
974 : GetUploadStatusRequestBase(sender
,
977 callback_(callback
) {
978 DCHECK(!callback
.is_null());
981 GetUploadStatusRequest::~GetUploadStatusRequest() {}
983 void GetUploadStatusRequest::OnRangeRequestComplete(
984 const UploadRangeResponse
& response
,
985 scoped_ptr
<base::Value
> value
) {
986 DCHECK(CalledOnValidThread());
987 ParseFileResourceWithUploadRangeAndRun(callback_
, response
, value
.Pass());
990 //======================= MultipartUploadNewFileDelegate =======================
992 MultipartUploadNewFileDelegate::MultipartUploadNewFileDelegate(
993 base::SequencedTaskRunner
* task_runner
,
994 const std::string
& title
,
995 const std::string
& parent_resource_id
,
996 const std::string
& content_type
,
997 int64 content_length
,
998 const base::Time
& modified_date
,
999 const base::Time
& last_viewed_by_me_date
,
1000 const base::FilePath
& local_file_path
,
1001 const Properties
& properties
,
1002 const DriveApiUrlGenerator
& url_generator
,
1003 const FileResourceCallback
& callback
,
1004 const ProgressCallback
& progress_callback
)
1005 : MultipartUploadRequestBase(
1007 CreateMultipartUploadMetadataJson(title
,
1010 last_viewed_by_me_date
,
1017 has_modified_date_(!modified_date
.is_null()),
1018 url_generator_(url_generator
) {
1021 MultipartUploadNewFileDelegate::~MultipartUploadNewFileDelegate() {
1024 GURL
MultipartUploadNewFileDelegate::GetURL() const {
1025 return url_generator_
.GetMultipartUploadNewFileUrl(has_modified_date_
);
1028 net::URLFetcher::RequestType
MultipartUploadNewFileDelegate::GetRequestType()
1030 return net::URLFetcher::POST
;
1033 //====================== MultipartUploadExistingFileDelegate ===================
1035 MultipartUploadExistingFileDelegate::MultipartUploadExistingFileDelegate(
1036 base::SequencedTaskRunner
* task_runner
,
1037 const std::string
& title
,
1038 const std::string
& resource_id
,
1039 const std::string
& parent_resource_id
,
1040 const std::string
& content_type
,
1041 int64 content_length
,
1042 const base::Time
& modified_date
,
1043 const base::Time
& last_viewed_by_me_date
,
1044 const base::FilePath
& local_file_path
,
1045 const std::string
& etag
,
1046 const Properties
& properties
,
1047 const DriveApiUrlGenerator
& url_generator
,
1048 const FileResourceCallback
& callback
,
1049 const ProgressCallback
& progress_callback
)
1050 : MultipartUploadRequestBase(
1052 CreateMultipartUploadMetadataJson(title
,
1055 last_viewed_by_me_date
,
1062 resource_id_(resource_id
),
1064 has_modified_date_(!modified_date
.is_null()),
1065 url_generator_(url_generator
) {
1068 MultipartUploadExistingFileDelegate::~MultipartUploadExistingFileDelegate() {
1071 std::vector
<std::string
>
1072 MultipartUploadExistingFileDelegate::GetExtraRequestHeaders() const {
1073 std::vector
<std::string
> headers(
1074 MultipartUploadRequestBase::GetExtraRequestHeaders());
1075 headers
.push_back(util::GenerateIfMatchHeader(etag_
));
1079 GURL
MultipartUploadExistingFileDelegate::GetURL() const {
1080 return url_generator_
.GetMultipartUploadExistingFileUrl(resource_id_
,
1081 has_modified_date_
);
1084 net::URLFetcher::RequestType
1085 MultipartUploadExistingFileDelegate::GetRequestType() const {
1086 return net::URLFetcher::PUT
;
1089 //========================== DownloadFileRequest ==========================
1091 DownloadFileRequest::DownloadFileRequest(
1092 RequestSender
* sender
,
1093 const DriveApiUrlGenerator
& url_generator
,
1094 const std::string
& resource_id
,
1095 const base::FilePath
& output_file_path
,
1096 const DownloadActionCallback
& download_action_callback
,
1097 const GetContentCallback
& get_content_callback
,
1098 const ProgressCallback
& progress_callback
)
1099 : DownloadFileRequestBase(
1101 download_action_callback
,
1102 get_content_callback
,
1104 url_generator
.GenerateDownloadFileUrl(resource_id
),
1108 DownloadFileRequest::~DownloadFileRequest() {
1111 //========================== PermissionsInsertRequest ==========================
1113 PermissionsInsertRequest::PermissionsInsertRequest(
1114 RequestSender
* sender
,
1115 const DriveApiUrlGenerator
& url_generator
,
1116 const EntryActionCallback
& callback
)
1117 : EntryActionRequest(sender
, callback
),
1118 url_generator_(url_generator
),
1119 type_(PERMISSION_TYPE_USER
),
1120 role_(PERMISSION_ROLE_READER
) {
1123 PermissionsInsertRequest::~PermissionsInsertRequest() {
1126 GURL
PermissionsInsertRequest::GetURL() const {
1127 return url_generator_
.GetPermissionsInsertUrl(id_
);
1130 net::URLFetcher::RequestType
1131 PermissionsInsertRequest::GetRequestType() const {
1132 return net::URLFetcher::POST
;
1135 bool PermissionsInsertRequest::GetContentData(std::string
* upload_content_type
,
1136 std::string
* upload_content
) {
1137 *upload_content_type
= util::kContentTypeApplicationJson
;
1139 base::DictionaryValue root
;
1141 case PERMISSION_TYPE_ANYONE
:
1142 root
.SetString("type", "anyone");
1144 case PERMISSION_TYPE_DOMAIN
:
1145 root
.SetString("type", "domain");
1147 case PERMISSION_TYPE_GROUP
:
1148 root
.SetString("type", "group");
1150 case PERMISSION_TYPE_USER
:
1151 root
.SetString("type", "user");
1155 case PERMISSION_ROLE_OWNER
:
1156 root
.SetString("role", "owner");
1158 case PERMISSION_ROLE_READER
:
1159 root
.SetString("role", "reader");
1161 case PERMISSION_ROLE_WRITER
:
1162 root
.SetString("role", "writer");
1164 case PERMISSION_ROLE_COMMENTER
:
1165 root
.SetString("role", "reader");
1167 base::ListValue
* list
= new base::ListValue
;
1168 list
->AppendString("commenter");
1169 root
.Set("additionalRoles", list
);
1173 root
.SetString("value", value_
);
1174 base::JSONWriter::Write(root
, upload_content
);
1178 //======================= SingleBatchableDelegateRequest =======================
1180 SingleBatchableDelegateRequest::SingleBatchableDelegateRequest(
1181 RequestSender
* sender
,
1182 BatchableDelegate
* delegate
)
1183 : UrlFetchRequestBase(sender
),
1184 delegate_(delegate
),
1185 weak_ptr_factory_(this) {
1188 SingleBatchableDelegateRequest::~SingleBatchableDelegateRequest() {
1191 GURL
SingleBatchableDelegateRequest::GetURL() const {
1192 return delegate_
->GetURL();
1195 net::URLFetcher::RequestType
SingleBatchableDelegateRequest::GetRequestType()
1197 return delegate_
->GetRequestType();
1200 std::vector
<std::string
>
1201 SingleBatchableDelegateRequest::GetExtraRequestHeaders() const {
1202 return delegate_
->GetExtraRequestHeaders();
1205 void SingleBatchableDelegateRequest::Prepare(const PrepareCallback
& callback
) {
1206 delegate_
->Prepare(callback
);
1209 bool SingleBatchableDelegateRequest::GetContentData(
1210 std::string
* upload_content_type
,
1211 std::string
* upload_content
) {
1212 return delegate_
->GetContentData(upload_content_type
, upload_content
);
1215 void SingleBatchableDelegateRequest::ProcessURLFetchResults(
1216 const net::URLFetcher
* source
) {
1217 delegate_
->NotifyResult(
1218 GetErrorCode(), response_writer()->data(),
1220 &SingleBatchableDelegateRequest::OnProcessURLFetchResultsComplete
,
1221 weak_ptr_factory_
.GetWeakPtr()));
1224 void SingleBatchableDelegateRequest::RunCallbackOnPrematureFailure(
1225 DriveApiErrorCode code
) {
1226 delegate_
->NotifyError(code
);
1229 void SingleBatchableDelegateRequest::OnURLFetchUploadProgress(
1230 const net::URLFetcher
* source
,
1233 delegate_
->NotifyUploadProgress(source
, current
, total
);
1236 //========================== BatchUploadRequest ==========================
1238 BatchUploadChildEntry::BatchUploadChildEntry(BatchableDelegate
* request
)
1239 : request(request
), prepared(false), data_offset(0), data_size(0) {
1242 BatchUploadChildEntry::~BatchUploadChildEntry() {
1245 BatchUploadRequest::BatchUploadRequest(
1246 RequestSender
* sender
,
1247 const DriveApiUrlGenerator
& url_generator
)
1248 : UrlFetchRequestBase(sender
),
1250 url_generator_(url_generator
),
1252 last_progress_value_(0),
1253 weak_ptr_factory_(this) {
1256 BatchUploadRequest::~BatchUploadRequest() {
1259 void BatchUploadRequest::SetBoundaryForTesting(const std::string
& boundary
) {
1260 boundary_
= boundary
;
1263 void BatchUploadRequest::AddRequest(BatchableDelegate
* request
) {
1264 DCHECK(CalledOnValidThread());
1266 DCHECK(GetChildEntry(request
) == child_requests_
.end());
1267 DCHECK(!committed_
);
1268 child_requests_
.push_back(new BatchUploadChildEntry(request
));
1269 request
->Prepare(base::Bind(&BatchUploadRequest::OnChildRequestPrepared
,
1270 weak_ptr_factory_
.GetWeakPtr(), request
));
1273 void BatchUploadRequest::OnChildRequestPrepared(RequestID request_id
,
1274 DriveApiErrorCode result
) {
1275 DCHECK(CalledOnValidThread());
1276 auto const child
= GetChildEntry(request_id
);
1277 DCHECK(child
!= child_requests_
.end());
1278 if (IsSuccessfulDriveApiErrorCode(result
)) {
1279 (*child
)->prepared
= true;
1281 (*child
)->request
->NotifyError(result
);
1282 child_requests_
.erase(child
);
1284 MayCompletePrepare();
1287 void BatchUploadRequest::Commit() {
1288 DCHECK(CalledOnValidThread());
1289 DCHECK(!committed_
);
1290 if (child_requests_
.empty()) {
1294 MayCompletePrepare();
1298 void BatchUploadRequest::Prepare(const PrepareCallback
& callback
) {
1299 DCHECK(CalledOnValidThread());
1300 DCHECK(!callback
.is_null());
1301 prepare_callback_
= callback
;
1302 MayCompletePrepare();
1305 void BatchUploadRequest::Cancel() {
1306 child_requests_
.clear();
1307 UrlFetchRequestBase::Cancel();
1310 // Obtains corresponding child entry of |request_id|. Returns NULL if the
1311 // entry is not found.
1312 ScopedVector
<BatchUploadChildEntry
>::iterator
BatchUploadRequest::GetChildEntry(
1313 RequestID request_id
) {
1314 for (auto it
= child_requests_
.begin(); it
!= child_requests_
.end(); ++it
) {
1315 if ((*it
)->request
.get() == request_id
)
1318 return child_requests_
.end();
1321 void BatchUploadRequest::MayCompletePrepare() {
1322 if (!committed_
|| prepare_callback_
.is_null())
1324 for (const auto& child
: child_requests_
) {
1325 if (!child
->prepared
)
1329 // Build multipart body here.
1330 int64 total_size
= 0;
1331 std::vector
<ContentTypeAndData
> parts
;
1332 for (auto& child
: child_requests_
) {
1335 const bool result
= child
->request
->GetContentData(&type
, &data
);
1336 // Upload request must have content data.
1339 const GURL url
= child
->request
->GetURL();
1341 switch (child
->request
->GetRequestType()) {
1342 case net::URLFetcher::POST
:
1345 case net::URLFetcher::PUT
:
1352 const std::string header
= base::StringPrintf(
1353 kBatchUploadRequestFormat
, method
.c_str(), url
.path().c_str(),
1354 url_generator_
.GetBatchUploadUrl().host().c_str(), type
.c_str());
1356 child
->data_offset
= header
.size();
1357 child
->data_size
= data
.size();
1358 total_size
+= data
.size();
1360 parts
.push_back(ContentTypeAndData());
1361 parts
.back().type
= kHttpContentType
;
1362 parts
.back().data
= header
;
1363 parts
.back().data
.append(data
);
1366 UMA_HISTOGRAM_COUNTS_100(kUMADriveTotalFileCountInBatchUpload
, parts
.size());
1367 UMA_HISTOGRAM_MEMORY_KB(kUMADriveTotalFileSizeInBatchUpload
,
1370 std::vector
<uint64
> part_data_offset
;
1371 GenerateMultipartBody(MULTIPART_MIXED
, boundary_
, parts
, &upload_content_
,
1373 DCHECK(part_data_offset
.size() == child_requests_
.size());
1374 for (size_t i
= 0; i
< child_requests_
.size(); ++i
) {
1375 child_requests_
[i
]->data_offset
+= part_data_offset
[i
];
1377 prepare_callback_
.Run(HTTP_SUCCESS
);
1380 bool BatchUploadRequest::GetContentData(std::string
* upload_content_type
,
1381 std::string
* upload_content_data
) {
1382 upload_content_type
->assign(upload_content_
.type
);
1383 upload_content_data
->assign(upload_content_
.data
);
1387 base::WeakPtr
<BatchUploadRequest
>
1388 BatchUploadRequest::GetWeakPtrAsBatchUploadRequest() {
1389 return weak_ptr_factory_
.GetWeakPtr();
1392 GURL
BatchUploadRequest::GetURL() const {
1393 return url_generator_
.GetBatchUploadUrl();
1396 net::URLFetcher::RequestType
BatchUploadRequest::GetRequestType() const {
1397 return net::URLFetcher::PUT
;
1400 std::vector
<std::string
> BatchUploadRequest::GetExtraRequestHeaders() const {
1401 std::vector
<std::string
> headers
;
1402 headers
.push_back(kBatchUploadHeader
);
1406 void BatchUploadRequest::ProcessURLFetchResults(const net::URLFetcher
* source
) {
1407 UMA_HISTOGRAM_SPARSE_SLOWLY(kUMADriveBatchUploadResponseCode
, GetErrorCode());
1409 if (!IsSuccessfulDriveApiErrorCode(GetErrorCode())) {
1410 RunCallbackOnPrematureFailure(GetErrorCode());
1411 sender_
->RequestFinished(this);
1415 std::string content_type
;
1416 source
->GetResponseHeaders()->EnumerateHeader(
1417 /* need only first header */ NULL
, "Content-Type", &content_type
);
1419 std::vector
<MultipartHttpResponse
> parts
;
1420 if (!ParseMultipartResponse(content_type
, response_writer()->data(),
1422 child_requests_
.size() != parts
.size()) {
1423 RunCallbackOnPrematureFailure(DRIVE_PARSE_ERROR
);
1424 sender_
->RequestFinished(this);
1428 for (size_t i
= 0; i
< parts
.size(); ++i
) {
1429 BatchableDelegate
* const delegate
= child_requests_
[i
]->request
.get();
1430 // Pass onwership of |delegate| so that child_requests_.clear() won't
1431 // kill the delegate. It has to be deleted after the notification.
1432 delegate
->NotifyResult(
1433 parts
[i
].code
, parts
[i
].body
,
1434 base::Bind(&EmptyClosure
, base::Passed(&child_requests_
[i
]->request
)));
1436 child_requests_
.clear();
1438 sender_
->RequestFinished(this);
1441 void BatchUploadRequest::RunCallbackOnPrematureFailure(DriveApiErrorCode code
) {
1442 for (auto child
: child_requests_
) {
1443 child
->request
->NotifyError(code
);
1445 child_requests_
.clear();
1448 void BatchUploadRequest::OnURLFetchUploadProgress(const net::URLFetcher
* source
,
1451 for (auto child
: child_requests_
) {
1452 if (child
->data_offset
<= current
&&
1453 current
<= child
->data_offset
+ child
->data_size
) {
1454 child
->request
->NotifyUploadProgress(source
, current
- child
->data_offset
,
1456 } else if (last_progress_value_
< child
->data_offset
+ child
->data_size
&&
1457 child
->data_offset
+ child
->data_size
< current
) {
1458 child
->request
->NotifyUploadProgress(source
, child
->data_size
,
1462 last_progress_value_
= current
;
1464 } // namespace drive
1465 } // namespace google_apis