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 DCHECK(!callback
.is_null());
392 FilesInsertRequest::~FilesInsertRequest() {}
394 net::URLFetcher::RequestType
FilesInsertRequest::GetRequestType() const {
395 return net::URLFetcher::POST
;
398 bool FilesInsertRequest::GetContentData(std::string
* upload_content_type
,
399 std::string
* upload_content
) {
400 *upload_content_type
= util::kContentTypeApplicationJson
;
402 base::DictionaryValue root
;
404 if (!last_viewed_by_me_date_
.is_null()) {
405 root
.SetString("lastViewedByMeDate",
406 util::FormatTimeAsString(last_viewed_by_me_date_
));
409 if (!mime_type_
.empty())
410 root
.SetString("mimeType", mime_type_
);
412 if (!modified_date_
.is_null())
413 root
.SetString("modifiedDate", util::FormatTimeAsString(modified_date_
));
415 if (!parents_
.empty()) {
416 base::ListValue
* parents_value
= new base::ListValue
;
417 for (size_t i
= 0; i
< parents_
.size(); ++i
) {
418 base::DictionaryValue
* parent
= new base::DictionaryValue
;
419 parent
->SetString("id", parents_
[i
]);
420 parents_value
->Append(parent
);
422 root
.Set("parents", parents_value
);
426 root
.SetString("title", title_
);
428 AttachProperties(properties_
, &root
);
429 base::JSONWriter::Write(root
, upload_content
);
431 DVLOG(1) << "FilesInsert data: " << *upload_content_type
<< ", ["
432 << *upload_content
<< "]";
436 GURL
FilesInsertRequest::GetURLInternal() const {
437 return url_generator_
.GetFilesInsertUrl();
440 //============================== FilesPatchRequest ============================
442 FilesPatchRequest::FilesPatchRequest(
443 RequestSender
* sender
,
444 const DriveApiUrlGenerator
& url_generator
,
445 const FileResourceCallback
& callback
)
446 : DriveApiDataRequest
<FileResource
>(sender
, callback
),
447 url_generator_(url_generator
),
448 set_modified_date_(false),
449 update_viewed_date_(true) {
450 DCHECK(!callback
.is_null());
453 FilesPatchRequest::~FilesPatchRequest() {}
455 net::URLFetcher::RequestType
FilesPatchRequest::GetRequestType() const {
456 return net::URLFetcher::PATCH
;
459 std::vector
<std::string
> FilesPatchRequest::GetExtraRequestHeaders() const {
460 std::vector
<std::string
> headers
;
461 headers
.push_back(util::kIfMatchAllHeader
);
465 GURL
FilesPatchRequest::GetURLInternal() const {
466 return url_generator_
.GetFilesPatchUrl(
467 file_id_
, set_modified_date_
, update_viewed_date_
);
470 bool FilesPatchRequest::GetContentData(std::string
* upload_content_type
,
471 std::string
* upload_content
) {
472 if (title_
.empty() &&
473 modified_date_
.is_null() &&
474 last_viewed_by_me_date_
.is_null() &&
478 *upload_content_type
= util::kContentTypeApplicationJson
;
480 base::DictionaryValue root
;
482 root
.SetString("title", title_
);
484 if (!modified_date_
.is_null())
485 root
.SetString("modifiedDate", util::FormatTimeAsString(modified_date_
));
487 if (!last_viewed_by_me_date_
.is_null()) {
488 root
.SetString("lastViewedByMeDate",
489 util::FormatTimeAsString(last_viewed_by_me_date_
));
492 if (!parents_
.empty()) {
493 base::ListValue
* parents_value
= new base::ListValue
;
494 for (size_t i
= 0; i
< parents_
.size(); ++i
) {
495 base::DictionaryValue
* parent
= new base::DictionaryValue
;
496 parent
->SetString("id", parents_
[i
]);
497 parents_value
->Append(parent
);
499 root
.Set("parents", parents_value
);
502 AttachProperties(properties_
, &root
);
503 base::JSONWriter::Write(root
, upload_content
);
505 DVLOG(1) << "FilesPatch data: " << *upload_content_type
<< ", ["
506 << *upload_content
<< "]";
510 //============================= FilesCopyRequest ==============================
512 FilesCopyRequest::FilesCopyRequest(
513 RequestSender
* sender
,
514 const DriveApiUrlGenerator
& url_generator
,
515 const FileResourceCallback
& callback
)
516 : DriveApiDataRequest
<FileResource
>(sender
, callback
),
517 url_generator_(url_generator
) {
518 DCHECK(!callback
.is_null());
521 FilesCopyRequest::~FilesCopyRequest() {
524 net::URLFetcher::RequestType
FilesCopyRequest::GetRequestType() const {
525 return net::URLFetcher::POST
;
528 GURL
FilesCopyRequest::GetURLInternal() const {
529 return url_generator_
.GetFilesCopyUrl(file_id_
);
532 bool FilesCopyRequest::GetContentData(std::string
* upload_content_type
,
533 std::string
* upload_content
) {
534 if (parents_
.empty() && title_
.empty())
537 *upload_content_type
= util::kContentTypeApplicationJson
;
539 base::DictionaryValue root
;
541 if (!modified_date_
.is_null())
542 root
.SetString("modifiedDate", util::FormatTimeAsString(modified_date_
));
544 if (!parents_
.empty()) {
545 base::ListValue
* parents_value
= new base::ListValue
;
546 for (size_t i
= 0; i
< parents_
.size(); ++i
) {
547 base::DictionaryValue
* parent
= new base::DictionaryValue
;
548 parent
->SetString("id", parents_
[i
]);
549 parents_value
->Append(parent
);
551 root
.Set("parents", parents_value
);
555 root
.SetString("title", title_
);
557 base::JSONWriter::Write(root
, upload_content
);
558 DVLOG(1) << "FilesCopy data: " << *upload_content_type
<< ", ["
559 << *upload_content
<< "]";
563 //============================= FilesListRequest =============================
565 FilesListRequest::FilesListRequest(
566 RequestSender
* sender
,
567 const DriveApiUrlGenerator
& url_generator
,
568 const FileListCallback
& callback
)
569 : DriveApiDataRequest
<FileList
>(sender
, callback
),
570 url_generator_(url_generator
),
572 DCHECK(!callback
.is_null());
575 FilesListRequest::~FilesListRequest() {}
577 GURL
FilesListRequest::GetURLInternal() const {
578 return url_generator_
.GetFilesListUrl(max_results_
, page_token_
, q_
);
581 //======================== FilesListNextPageRequest =========================
583 FilesListNextPageRequest::FilesListNextPageRequest(
584 RequestSender
* sender
,
585 const FileListCallback
& callback
)
586 : DriveApiDataRequest
<FileList
>(sender
, callback
) {
587 DCHECK(!callback
.is_null());
590 FilesListNextPageRequest::~FilesListNextPageRequest() {
593 GURL
FilesListNextPageRequest::GetURLInternal() const {
597 //============================ FilesDeleteRequest =============================
599 FilesDeleteRequest::FilesDeleteRequest(
600 RequestSender
* sender
,
601 const DriveApiUrlGenerator
& url_generator
,
602 const EntryActionCallback
& callback
)
603 : EntryActionRequest(sender
, callback
),
604 url_generator_(url_generator
) {
605 DCHECK(!callback
.is_null());
608 FilesDeleteRequest::~FilesDeleteRequest() {}
610 net::URLFetcher::RequestType
FilesDeleteRequest::GetRequestType() const {
611 return net::URLFetcher::DELETE_REQUEST
;
614 GURL
FilesDeleteRequest::GetURL() const {
615 return url_generator_
.GetFilesDeleteUrl(file_id_
);
618 std::vector
<std::string
> FilesDeleteRequest::GetExtraRequestHeaders() const {
619 std::vector
<std::string
> headers(
620 EntryActionRequest::GetExtraRequestHeaders());
621 headers
.push_back(util::GenerateIfMatchHeader(etag_
));
625 //============================ FilesTrashRequest =============================
627 FilesTrashRequest::FilesTrashRequest(
628 RequestSender
* sender
,
629 const DriveApiUrlGenerator
& url_generator
,
630 const FileResourceCallback
& callback
)
631 : DriveApiDataRequest
<FileResource
>(sender
, callback
),
632 url_generator_(url_generator
) {
633 DCHECK(!callback
.is_null());
636 FilesTrashRequest::~FilesTrashRequest() {}
638 net::URLFetcher::RequestType
FilesTrashRequest::GetRequestType() const {
639 return net::URLFetcher::POST
;
642 GURL
FilesTrashRequest::GetURLInternal() const {
643 return url_generator_
.GetFilesTrashUrl(file_id_
);
646 //============================== AboutGetRequest =============================
648 AboutGetRequest::AboutGetRequest(
649 RequestSender
* sender
,
650 const DriveApiUrlGenerator
& url_generator
,
651 const AboutResourceCallback
& callback
)
652 : DriveApiDataRequest
<AboutResource
>(sender
, callback
),
653 url_generator_(url_generator
) {
654 DCHECK(!callback
.is_null());
657 AboutGetRequest::~AboutGetRequest() {}
659 GURL
AboutGetRequest::GetURLInternal() const {
660 return url_generator_
.GetAboutGetUrl();
663 //============================ ChangesListRequest ===========================
665 ChangesListRequest::ChangesListRequest(
666 RequestSender
* sender
,
667 const DriveApiUrlGenerator
& url_generator
,
668 const ChangeListCallback
& callback
)
669 : DriveApiDataRequest
<ChangeList
>(sender
, callback
),
670 url_generator_(url_generator
),
671 include_deleted_(true),
673 start_change_id_(0) {
674 DCHECK(!callback
.is_null());
677 ChangesListRequest::~ChangesListRequest() {}
679 GURL
ChangesListRequest::GetURLInternal() const {
680 return url_generator_
.GetChangesListUrl(
681 include_deleted_
, max_results_
, page_token_
, start_change_id_
);
684 //======================== ChangesListNextPageRequest =========================
686 ChangesListNextPageRequest::ChangesListNextPageRequest(
687 RequestSender
* sender
,
688 const ChangeListCallback
& callback
)
689 : DriveApiDataRequest
<ChangeList
>(sender
, callback
) {
690 DCHECK(!callback
.is_null());
693 ChangesListNextPageRequest::~ChangesListNextPageRequest() {
696 GURL
ChangesListNextPageRequest::GetURLInternal() const {
700 //============================== AppsListRequest ===========================
702 AppsListRequest::AppsListRequest(
703 RequestSender
* sender
,
704 const DriveApiUrlGenerator
& url_generator
,
705 bool use_internal_endpoint
,
706 const AppListCallback
& callback
)
707 : DriveApiDataRequest
<AppList
>(sender
, callback
),
708 url_generator_(url_generator
),
709 use_internal_endpoint_(use_internal_endpoint
) {
710 DCHECK(!callback
.is_null());
713 AppsListRequest::~AppsListRequest() {}
715 GURL
AppsListRequest::GetURLInternal() const {
716 return url_generator_
.GetAppsListUrl(use_internal_endpoint_
);
719 //============================== AppsDeleteRequest ===========================
721 AppsDeleteRequest::AppsDeleteRequest(RequestSender
* sender
,
722 const DriveApiUrlGenerator
& url_generator
,
723 const EntryActionCallback
& callback
)
724 : EntryActionRequest(sender
, callback
),
725 url_generator_(url_generator
) {
726 DCHECK(!callback
.is_null());
729 AppsDeleteRequest::~AppsDeleteRequest() {}
731 net::URLFetcher::RequestType
AppsDeleteRequest::GetRequestType() const {
732 return net::URLFetcher::DELETE_REQUEST
;
735 GURL
AppsDeleteRequest::GetURL() const {
736 return url_generator_
.GetAppsDeleteUrl(app_id_
);
739 //========================== ChildrenInsertRequest ============================
741 ChildrenInsertRequest::ChildrenInsertRequest(
742 RequestSender
* sender
,
743 const DriveApiUrlGenerator
& url_generator
,
744 const EntryActionCallback
& callback
)
745 : EntryActionRequest(sender
, callback
),
746 url_generator_(url_generator
) {
747 DCHECK(!callback
.is_null());
750 ChildrenInsertRequest::~ChildrenInsertRequest() {}
752 net::URLFetcher::RequestType
ChildrenInsertRequest::GetRequestType() const {
753 return net::URLFetcher::POST
;
756 GURL
ChildrenInsertRequest::GetURL() const {
757 return url_generator_
.GetChildrenInsertUrl(folder_id_
);
760 bool ChildrenInsertRequest::GetContentData(std::string
* upload_content_type
,
761 std::string
* upload_content
) {
762 *upload_content_type
= util::kContentTypeApplicationJson
;
764 base::DictionaryValue root
;
765 root
.SetString("id", id_
);
767 base::JSONWriter::Write(root
, upload_content
);
768 DVLOG(1) << "InsertResource data: " << *upload_content_type
<< ", ["
769 << *upload_content
<< "]";
773 //========================== ChildrenDeleteRequest ============================
775 ChildrenDeleteRequest::ChildrenDeleteRequest(
776 RequestSender
* sender
,
777 const DriveApiUrlGenerator
& url_generator
,
778 const EntryActionCallback
& callback
)
779 : EntryActionRequest(sender
, callback
),
780 url_generator_(url_generator
) {
781 DCHECK(!callback
.is_null());
784 ChildrenDeleteRequest::~ChildrenDeleteRequest() {}
786 net::URLFetcher::RequestType
ChildrenDeleteRequest::GetRequestType() const {
787 return net::URLFetcher::DELETE_REQUEST
;
790 GURL
ChildrenDeleteRequest::GetURL() const {
791 return url_generator_
.GetChildrenDeleteUrl(child_id_
, folder_id_
);
794 //======================= InitiateUploadNewFileRequest =======================
796 InitiateUploadNewFileRequest::InitiateUploadNewFileRequest(
797 RequestSender
* sender
,
798 const DriveApiUrlGenerator
& url_generator
,
799 const std::string
& content_type
,
800 int64 content_length
,
801 const std::string
& parent_resource_id
,
802 const std::string
& title
,
803 const InitiateUploadCallback
& callback
)
804 : InitiateUploadRequestBase(sender
,
808 url_generator_(url_generator
),
809 parent_resource_id_(parent_resource_id
),
813 InitiateUploadNewFileRequest::~InitiateUploadNewFileRequest() {}
815 GURL
InitiateUploadNewFileRequest::GetURL() const {
816 return url_generator_
.GetInitiateUploadNewFileUrl(!modified_date_
.is_null());
819 net::URLFetcher::RequestType
820 InitiateUploadNewFileRequest::GetRequestType() const {
821 return net::URLFetcher::POST
;
824 bool InitiateUploadNewFileRequest::GetContentData(
825 std::string
* upload_content_type
,
826 std::string
* upload_content
) {
827 *upload_content_type
= util::kContentTypeApplicationJson
;
829 base::DictionaryValue root
;
830 root
.SetString("title", title_
);
833 scoped_ptr
<base::ListValue
> parents(new base::ListValue
);
834 parents
->Append(util::CreateParentValue(parent_resource_id_
).release());
835 root
.Set("parents", parents
.release());
837 if (!modified_date_
.is_null())
838 root
.SetString("modifiedDate", util::FormatTimeAsString(modified_date_
));
840 if (!last_viewed_by_me_date_
.is_null()) {
841 root
.SetString("lastViewedByMeDate",
842 util::FormatTimeAsString(last_viewed_by_me_date_
));
845 AttachProperties(properties_
, &root
);
846 base::JSONWriter::Write(root
, upload_content
);
848 DVLOG(1) << "InitiateUploadNewFile data: " << *upload_content_type
<< ", ["
849 << *upload_content
<< "]";
853 //===================== InitiateUploadExistingFileRequest ====================
855 InitiateUploadExistingFileRequest::InitiateUploadExistingFileRequest(
856 RequestSender
* sender
,
857 const DriveApiUrlGenerator
& url_generator
,
858 const std::string
& content_type
,
859 int64 content_length
,
860 const std::string
& resource_id
,
861 const std::string
& etag
,
862 const InitiateUploadCallback
& callback
)
863 : InitiateUploadRequestBase(sender
,
867 url_generator_(url_generator
),
868 resource_id_(resource_id
),
872 InitiateUploadExistingFileRequest::~InitiateUploadExistingFileRequest() {}
874 GURL
InitiateUploadExistingFileRequest::GetURL() const {
875 return url_generator_
.GetInitiateUploadExistingFileUrl(
876 resource_id_
, !modified_date_
.is_null());
879 net::URLFetcher::RequestType
880 InitiateUploadExistingFileRequest::GetRequestType() const {
881 return net::URLFetcher::PUT
;
884 std::vector
<std::string
>
885 InitiateUploadExistingFileRequest::GetExtraRequestHeaders() const {
886 std::vector
<std::string
> headers(
887 InitiateUploadRequestBase::GetExtraRequestHeaders());
888 headers
.push_back(util::GenerateIfMatchHeader(etag_
));
892 bool InitiateUploadExistingFileRequest::GetContentData(
893 std::string
* upload_content_type
,
894 std::string
* upload_content
) {
895 base::DictionaryValue root
;
896 if (!parent_resource_id_
.empty()) {
897 scoped_ptr
<base::ListValue
> parents(new base::ListValue
);
898 parents
->Append(util::CreateParentValue(parent_resource_id_
).release());
899 root
.Set("parents", parents
.release());
903 root
.SetString("title", title_
);
905 if (!modified_date_
.is_null())
906 root
.SetString("modifiedDate", util::FormatTimeAsString(modified_date_
));
908 if (!last_viewed_by_me_date_
.is_null()) {
909 root
.SetString("lastViewedByMeDate",
910 util::FormatTimeAsString(last_viewed_by_me_date_
));
913 AttachProperties(properties_
, &root
);
917 *upload_content_type
= util::kContentTypeApplicationJson
;
918 base::JSONWriter::Write(root
, upload_content
);
919 DVLOG(1) << "InitiateUploadExistingFile data: " << *upload_content_type
920 << ", [" << *upload_content
<< "]";
924 //============================ ResumeUploadRequest ===========================
926 ResumeUploadRequest::ResumeUploadRequest(
927 RequestSender
* sender
,
928 const GURL
& upload_location
,
929 int64 start_position
,
931 int64 content_length
,
932 const std::string
& content_type
,
933 const base::FilePath
& local_file_path
,
934 const UploadRangeCallback
& callback
,
935 const ProgressCallback
& progress_callback
)
936 : ResumeUploadRequestBase(sender
,
944 progress_callback_(progress_callback
) {
945 DCHECK(!callback_
.is_null());
948 ResumeUploadRequest::~ResumeUploadRequest() {}
950 void ResumeUploadRequest::OnRangeRequestComplete(
951 const UploadRangeResponse
& response
,
952 scoped_ptr
<base::Value
> value
) {
953 DCHECK(CalledOnValidThread());
954 ParseFileResourceWithUploadRangeAndRun(callback_
, response
, value
.Pass());
957 void ResumeUploadRequest::OnURLFetchUploadProgress(
958 const net::URLFetcher
* source
, int64 current
, int64 total
) {
959 if (!progress_callback_
.is_null())
960 progress_callback_
.Run(current
, total
);
963 //========================== GetUploadStatusRequest ==========================
965 GetUploadStatusRequest::GetUploadStatusRequest(
966 RequestSender
* sender
,
967 const GURL
& upload_url
,
968 int64 content_length
,
969 const UploadRangeCallback
& callback
)
970 : GetUploadStatusRequestBase(sender
,
973 callback_(callback
) {
974 DCHECK(!callback
.is_null());
977 GetUploadStatusRequest::~GetUploadStatusRequest() {}
979 void GetUploadStatusRequest::OnRangeRequestComplete(
980 const UploadRangeResponse
& response
,
981 scoped_ptr
<base::Value
> value
) {
982 DCHECK(CalledOnValidThread());
983 ParseFileResourceWithUploadRangeAndRun(callback_
, response
, value
.Pass());
986 //======================= MultipartUploadNewFileDelegate =======================
988 MultipartUploadNewFileDelegate::MultipartUploadNewFileDelegate(
989 base::SequencedTaskRunner
* task_runner
,
990 const std::string
& title
,
991 const std::string
& parent_resource_id
,
992 const std::string
& content_type
,
993 int64 content_length
,
994 const base::Time
& modified_date
,
995 const base::Time
& last_viewed_by_me_date
,
996 const base::FilePath
& local_file_path
,
997 const Properties
& properties
,
998 const DriveApiUrlGenerator
& url_generator
,
999 const FileResourceCallback
& callback
,
1000 const ProgressCallback
& progress_callback
)
1001 : MultipartUploadRequestBase(
1003 CreateMultipartUploadMetadataJson(title
,
1006 last_viewed_by_me_date
,
1013 has_modified_date_(!modified_date
.is_null()),
1014 url_generator_(url_generator
) {
1017 MultipartUploadNewFileDelegate::~MultipartUploadNewFileDelegate() {
1020 GURL
MultipartUploadNewFileDelegate::GetURL() const {
1021 return url_generator_
.GetMultipartUploadNewFileUrl(has_modified_date_
);
1024 net::URLFetcher::RequestType
MultipartUploadNewFileDelegate::GetRequestType()
1026 return net::URLFetcher::POST
;
1029 //====================== MultipartUploadExistingFileDelegate ===================
1031 MultipartUploadExistingFileDelegate::MultipartUploadExistingFileDelegate(
1032 base::SequencedTaskRunner
* task_runner
,
1033 const std::string
& title
,
1034 const std::string
& resource_id
,
1035 const std::string
& parent_resource_id
,
1036 const std::string
& content_type
,
1037 int64 content_length
,
1038 const base::Time
& modified_date
,
1039 const base::Time
& last_viewed_by_me_date
,
1040 const base::FilePath
& local_file_path
,
1041 const std::string
& etag
,
1042 const Properties
& properties
,
1043 const DriveApiUrlGenerator
& url_generator
,
1044 const FileResourceCallback
& callback
,
1045 const ProgressCallback
& progress_callback
)
1046 : MultipartUploadRequestBase(
1048 CreateMultipartUploadMetadataJson(title
,
1051 last_viewed_by_me_date
,
1058 resource_id_(resource_id
),
1060 has_modified_date_(!modified_date
.is_null()),
1061 url_generator_(url_generator
) {
1064 MultipartUploadExistingFileDelegate::~MultipartUploadExistingFileDelegate() {
1067 std::vector
<std::string
>
1068 MultipartUploadExistingFileDelegate::GetExtraRequestHeaders() const {
1069 std::vector
<std::string
> headers(
1070 MultipartUploadRequestBase::GetExtraRequestHeaders());
1071 headers
.push_back(util::GenerateIfMatchHeader(etag_
));
1075 GURL
MultipartUploadExistingFileDelegate::GetURL() const {
1076 return url_generator_
.GetMultipartUploadExistingFileUrl(resource_id_
,
1077 has_modified_date_
);
1080 net::URLFetcher::RequestType
1081 MultipartUploadExistingFileDelegate::GetRequestType() const {
1082 return net::URLFetcher::PUT
;
1085 //========================== DownloadFileRequest ==========================
1087 DownloadFileRequest::DownloadFileRequest(
1088 RequestSender
* sender
,
1089 const DriveApiUrlGenerator
& url_generator
,
1090 const std::string
& resource_id
,
1091 const base::FilePath
& output_file_path
,
1092 const DownloadActionCallback
& download_action_callback
,
1093 const GetContentCallback
& get_content_callback
,
1094 const ProgressCallback
& progress_callback
)
1095 : DownloadFileRequestBase(
1097 download_action_callback
,
1098 get_content_callback
,
1100 url_generator
.GenerateDownloadFileUrl(resource_id
),
1104 DownloadFileRequest::~DownloadFileRequest() {
1107 //========================== PermissionsInsertRequest ==========================
1109 PermissionsInsertRequest::PermissionsInsertRequest(
1110 RequestSender
* sender
,
1111 const DriveApiUrlGenerator
& url_generator
,
1112 const EntryActionCallback
& callback
)
1113 : EntryActionRequest(sender
, callback
),
1114 url_generator_(url_generator
),
1115 type_(PERMISSION_TYPE_USER
),
1116 role_(PERMISSION_ROLE_READER
) {
1119 PermissionsInsertRequest::~PermissionsInsertRequest() {
1122 GURL
PermissionsInsertRequest::GetURL() const {
1123 return url_generator_
.GetPermissionsInsertUrl(id_
);
1126 net::URLFetcher::RequestType
1127 PermissionsInsertRequest::GetRequestType() const {
1128 return net::URLFetcher::POST
;
1131 bool PermissionsInsertRequest::GetContentData(std::string
* upload_content_type
,
1132 std::string
* upload_content
) {
1133 *upload_content_type
= util::kContentTypeApplicationJson
;
1135 base::DictionaryValue root
;
1137 case PERMISSION_TYPE_ANYONE
:
1138 root
.SetString("type", "anyone");
1140 case PERMISSION_TYPE_DOMAIN
:
1141 root
.SetString("type", "domain");
1143 case PERMISSION_TYPE_GROUP
:
1144 root
.SetString("type", "group");
1146 case PERMISSION_TYPE_USER
:
1147 root
.SetString("type", "user");
1151 case PERMISSION_ROLE_OWNER
:
1152 root
.SetString("role", "owner");
1154 case PERMISSION_ROLE_READER
:
1155 root
.SetString("role", "reader");
1157 case PERMISSION_ROLE_WRITER
:
1158 root
.SetString("role", "writer");
1160 case PERMISSION_ROLE_COMMENTER
:
1161 root
.SetString("role", "reader");
1163 base::ListValue
* list
= new base::ListValue
;
1164 list
->AppendString("commenter");
1165 root
.Set("additionalRoles", list
);
1169 root
.SetString("value", value_
);
1170 base::JSONWriter::Write(root
, upload_content
);
1174 //======================= SingleBatchableDelegateRequest =======================
1176 SingleBatchableDelegateRequest::SingleBatchableDelegateRequest(
1177 RequestSender
* sender
,
1178 BatchableDelegate
* delegate
)
1179 : UrlFetchRequestBase(sender
),
1180 delegate_(delegate
),
1181 weak_ptr_factory_(this) {
1184 SingleBatchableDelegateRequest::~SingleBatchableDelegateRequest() {
1187 GURL
SingleBatchableDelegateRequest::GetURL() const {
1188 return delegate_
->GetURL();
1191 net::URLFetcher::RequestType
SingleBatchableDelegateRequest::GetRequestType()
1193 return delegate_
->GetRequestType();
1196 std::vector
<std::string
>
1197 SingleBatchableDelegateRequest::GetExtraRequestHeaders() const {
1198 return delegate_
->GetExtraRequestHeaders();
1201 void SingleBatchableDelegateRequest::Prepare(const PrepareCallback
& callback
) {
1202 delegate_
->Prepare(callback
);
1205 bool SingleBatchableDelegateRequest::GetContentData(
1206 std::string
* upload_content_type
,
1207 std::string
* upload_content
) {
1208 return delegate_
->GetContentData(upload_content_type
, upload_content
);
1211 void SingleBatchableDelegateRequest::ProcessURLFetchResults(
1212 const net::URLFetcher
* source
) {
1213 delegate_
->NotifyResult(
1214 GetErrorCode(), response_writer()->data(),
1216 &SingleBatchableDelegateRequest::OnProcessURLFetchResultsComplete
,
1217 weak_ptr_factory_
.GetWeakPtr()));
1220 void SingleBatchableDelegateRequest::RunCallbackOnPrematureFailure(
1221 DriveApiErrorCode code
) {
1222 delegate_
->NotifyError(code
);
1225 void SingleBatchableDelegateRequest::OnURLFetchUploadProgress(
1226 const net::URLFetcher
* source
,
1229 delegate_
->NotifyUploadProgress(source
, current
, total
);
1232 //========================== BatchUploadRequest ==========================
1234 BatchUploadChildEntry::BatchUploadChildEntry(BatchableDelegate
* request
)
1235 : request(request
), prepared(false), data_offset(0), data_size(0) {
1238 BatchUploadChildEntry::~BatchUploadChildEntry() {
1241 BatchUploadRequest::BatchUploadRequest(
1242 RequestSender
* sender
,
1243 const DriveApiUrlGenerator
& url_generator
)
1244 : UrlFetchRequestBase(sender
),
1246 url_generator_(url_generator
),
1248 last_progress_value_(0),
1249 weak_ptr_factory_(this) {
1252 BatchUploadRequest::~BatchUploadRequest() {
1255 void BatchUploadRequest::SetBoundaryForTesting(const std::string
& boundary
) {
1256 boundary_
= boundary
;
1259 void BatchUploadRequest::AddRequest(BatchableDelegate
* request
) {
1260 DCHECK(CalledOnValidThread());
1262 DCHECK(GetChildEntry(request
) == child_requests_
.end());
1263 DCHECK(!committed_
);
1264 child_requests_
.push_back(new BatchUploadChildEntry(request
));
1265 request
->Prepare(base::Bind(&BatchUploadRequest::OnChildRequestPrepared
,
1266 weak_ptr_factory_
.GetWeakPtr(), request
));
1269 void BatchUploadRequest::OnChildRequestPrepared(RequestID request_id
,
1270 DriveApiErrorCode result
) {
1271 DCHECK(CalledOnValidThread());
1272 auto const child
= GetChildEntry(request_id
);
1273 DCHECK(child
!= child_requests_
.end());
1274 if (IsSuccessfulDriveApiErrorCode(result
)) {
1275 (*child
)->prepared
= true;
1277 (*child
)->request
->NotifyError(result
);
1278 child_requests_
.erase(child
);
1280 MayCompletePrepare();
1283 void BatchUploadRequest::Commit() {
1284 DCHECK(CalledOnValidThread());
1285 DCHECK(!committed_
);
1286 if (child_requests_
.empty()) {
1290 MayCompletePrepare();
1294 void BatchUploadRequest::Prepare(const PrepareCallback
& callback
) {
1295 DCHECK(CalledOnValidThread());
1296 DCHECK(!callback
.is_null());
1297 prepare_callback_
= callback
;
1298 MayCompletePrepare();
1301 void BatchUploadRequest::Cancel() {
1302 child_requests_
.clear();
1303 UrlFetchRequestBase::Cancel();
1306 // Obtains corresponding child entry of |request_id|. Returns NULL if the
1307 // entry is not found.
1308 ScopedVector
<BatchUploadChildEntry
>::iterator
BatchUploadRequest::GetChildEntry(
1309 RequestID request_id
) {
1310 for (auto it
= child_requests_
.begin(); it
!= child_requests_
.end(); ++it
) {
1311 if ((*it
)->request
.get() == request_id
)
1314 return child_requests_
.end();
1317 void BatchUploadRequest::MayCompletePrepare() {
1318 if (!committed_
|| prepare_callback_
.is_null())
1320 for (const auto& child
: child_requests_
) {
1321 if (!child
->prepared
)
1325 // Build multipart body here.
1326 int64 total_size
= 0;
1327 std::vector
<ContentTypeAndData
> parts
;
1328 for (auto& child
: child_requests_
) {
1331 const bool result
= child
->request
->GetContentData(&type
, &data
);
1332 // Upload request must have content data.
1335 const GURL url
= child
->request
->GetURL();
1337 switch (child
->request
->GetRequestType()) {
1338 case net::URLFetcher::POST
:
1341 case net::URLFetcher::PUT
:
1348 const std::string header
= base::StringPrintf(
1349 kBatchUploadRequestFormat
, method
.c_str(), url
.path().c_str(),
1350 url_generator_
.GetBatchUploadUrl().host().c_str(), type
.c_str());
1352 child
->data_offset
= header
.size();
1353 child
->data_size
= data
.size();
1354 total_size
+= data
.size();
1356 parts
.push_back(ContentTypeAndData());
1357 parts
.back().type
= kHttpContentType
;
1358 parts
.back().data
= header
;
1359 parts
.back().data
.append(data
);
1362 UMA_HISTOGRAM_COUNTS_100(kUMADriveTotalFileCountInBatchUpload
, parts
.size());
1363 UMA_HISTOGRAM_MEMORY_KB(kUMADriveTotalFileSizeInBatchUpload
,
1366 std::vector
<uint64
> part_data_offset
;
1367 GenerateMultipartBody(MULTIPART_MIXED
, boundary_
, parts
, &upload_content_
,
1369 DCHECK(part_data_offset
.size() == child_requests_
.size());
1370 for (size_t i
= 0; i
< child_requests_
.size(); ++i
) {
1371 child_requests_
[i
]->data_offset
+= part_data_offset
[i
];
1373 prepare_callback_
.Run(HTTP_SUCCESS
);
1376 bool BatchUploadRequest::GetContentData(std::string
* upload_content_type
,
1377 std::string
* upload_content_data
) {
1378 upload_content_type
->assign(upload_content_
.type
);
1379 upload_content_data
->assign(upload_content_
.data
);
1383 base::WeakPtr
<BatchUploadRequest
>
1384 BatchUploadRequest::GetWeakPtrAsBatchUploadRequest() {
1385 return weak_ptr_factory_
.GetWeakPtr();
1388 GURL
BatchUploadRequest::GetURL() const {
1389 return url_generator_
.GetBatchUploadUrl();
1392 net::URLFetcher::RequestType
BatchUploadRequest::GetRequestType() const {
1393 return net::URLFetcher::PUT
;
1396 std::vector
<std::string
> BatchUploadRequest::GetExtraRequestHeaders() const {
1397 std::vector
<std::string
> headers
;
1398 headers
.push_back(kBatchUploadHeader
);
1402 void BatchUploadRequest::ProcessURLFetchResults(const net::URLFetcher
* source
) {
1403 UMA_HISTOGRAM_SPARSE_SLOWLY(kUMADriveBatchUploadResponseCode
, GetErrorCode());
1405 if (!IsSuccessfulDriveApiErrorCode(GetErrorCode())) {
1406 RunCallbackOnPrematureFailure(GetErrorCode());
1407 sender_
->RequestFinished(this);
1411 std::string content_type
;
1412 source
->GetResponseHeaders()->EnumerateHeader(
1413 /* need only first header */ NULL
, "Content-Type", &content_type
);
1415 std::vector
<MultipartHttpResponse
> parts
;
1416 if (!ParseMultipartResponse(content_type
, response_writer()->data(),
1418 child_requests_
.size() != parts
.size()) {
1419 RunCallbackOnPrematureFailure(DRIVE_PARSE_ERROR
);
1420 sender_
->RequestFinished(this);
1424 for (size_t i
= 0; i
< parts
.size(); ++i
) {
1425 BatchableDelegate
* const delegate
= child_requests_
[i
]->request
.get();
1426 // Pass onwership of |delegate| so that child_requests_.clear() won't
1427 // kill the delegate. It has to be deleted after the notification.
1428 delegate
->NotifyResult(
1429 parts
[i
].code
, parts
[i
].body
,
1430 base::Bind(&EmptyClosure
, base::Passed(&child_requests_
[i
]->request
)));
1432 child_requests_
.clear();
1434 sender_
->RequestFinished(this);
1437 void BatchUploadRequest::RunCallbackOnPrematureFailure(DriveApiErrorCode code
) {
1438 for (auto child
: child_requests_
) {
1439 child
->request
->NotifyError(code
);
1441 child_requests_
.clear();
1444 void BatchUploadRequest::OnURLFetchUploadProgress(const net::URLFetcher
* source
,
1447 for (auto child
: child_requests_
) {
1448 if (child
->data_offset
<= current
&&
1449 current
<= child
->data_offset
+ child
->data_size
) {
1450 child
->request
->NotifyUploadProgress(source
, current
- child
->data_offset
,
1452 } else if (last_progress_value_
< child
->data_offset
+ child
->data_size
&&
1453 child
->data_offset
+ child
->data_size
< current
) {
1454 child
->request
->NotifyUploadProgress(source
, child
->data_size
,
1458 last_progress_value_
= current
;
1460 } // namespace drive
1461 } // namespace google_apis