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/sequenced_task_runner.h"
12 #include "base/strings/string_number_conversions.h"
13 #include "base/strings/string_piece.h"
14 #include "base/strings/stringprintf.h"
15 #include "base/task_runner_util.h"
16 #include "base/values.h"
17 #include "google_apis/drive/request_sender.h"
18 #include "google_apis/drive/request_util.h"
19 #include "google_apis/drive/time_util.h"
20 #include "net/base/url_util.h"
21 #include "net/http/http_response_headers.h"
23 namespace google_apis
{
27 // Format of one request in batch uploading request.
28 const char kBatchUploadRequestFormat
[] =
31 "X-Goog-Upload-Protocol: multipart\n"
35 // Request header for specifying batch upload.
36 const char kBatchUploadHeader
[] = "X-Goog-Upload-Protocol: batch";
38 // Content type of HTTP request.
39 const char kHttpContentType
[] = "application/http";
41 // Break line in HTTP message.
42 const char kHttpBr
[] = "\r\n";
44 // Mime type of multipart mixed.
45 const char kMultipartMixedMimeTypePrefix
[] = "multipart/mixed; boundary=";
47 // Parses the JSON value to FileResource instance and runs |callback| on the
48 // UI thread once parsing is done.
49 // This is customized version of ParseJsonAndRun defined above to adapt the
50 // remaining response type.
51 void ParseFileResourceWithUploadRangeAndRun(const UploadRangeCallback
& callback
,
52 const UploadRangeResponse
& response
,
53 scoped_ptr
<base::Value
> value
) {
54 DCHECK(!callback
.is_null());
56 scoped_ptr
<FileResource
> file_resource
;
58 file_resource
= FileResource::CreateFrom(*value
);
61 UploadRangeResponse(DRIVE_PARSE_ERROR
,
62 response
.start_position_received
,
63 response
.end_position_received
),
64 scoped_ptr
<FileResource
>());
69 callback
.Run(response
, file_resource
.Pass());
72 // Attaches |properties| to the |request_body| if |properties| is not empty.
73 // |request_body| must not be NULL.
74 void AttachProperties(const Properties
& properties
,
75 base::DictionaryValue
* request_body
) {
77 if (properties
.empty())
80 base::ListValue
* const properties_value
= new base::ListValue
;
81 for (const auto& property
: properties
) {
82 base::DictionaryValue
* const property_value
= new base::DictionaryValue
;
83 std::string visibility_as_string
;
84 switch (property
.visibility()) {
85 case Property::VISIBILITY_PRIVATE
:
86 visibility_as_string
= "PRIVATE";
88 case Property::VISIBILITY_PUBLIC
:
89 visibility_as_string
= "PUBLIC";
92 property_value
->SetString("visibility", visibility_as_string
);
93 property_value
->SetString("key", property
.key());
94 property_value
->SetString("value", property
.value());
95 properties_value
->Append(property_value
);
97 request_body
->Set("properties", properties_value
);
100 // Creates metadata JSON string for multipart uploading.
101 // All the values are optional. If the value is empty or null, the value does
102 // not appear in the metadata.
103 std::string
CreateMultipartUploadMetadataJson(
104 const std::string
& title
,
105 const std::string
& parent_resource_id
,
106 const base::Time
& modified_date
,
107 const base::Time
& last_viewed_by_me_date
,
108 const Properties
& properties
) {
109 base::DictionaryValue root
;
111 root
.SetString("title", title
);
114 if (!parent_resource_id
.empty()) {
115 scoped_ptr
<base::ListValue
> parents(new base::ListValue
);
117 google_apis::util::CreateParentValue(parent_resource_id
).release());
118 root
.Set("parents", parents
.release());
121 if (!modified_date
.is_null()) {
122 root
.SetString("modifiedDate",
123 google_apis::util::FormatTimeAsString(modified_date
));
126 if (!last_viewed_by_me_date
.is_null()) {
127 root
.SetString("lastViewedByMeDate", google_apis::util::FormatTimeAsString(
128 last_viewed_by_me_date
));
131 AttachProperties(properties
, &root
);
132 std::string json_string
;
133 base::JSONWriter::Write(root
, &json_string
);
137 // Splits |string| into lines by |kHttpBr|.
138 // Each line does not include |kHttpBr|.
139 void SplitIntoLines(const std::string
& string
,
140 std::vector
<base::StringPiece
>* output
) {
141 const size_t br_size
= std::string(kHttpBr
).size();
142 std::string::const_iterator it
= string
.begin();
143 std::vector
<base::StringPiece
> lines
;
145 const std::string::const_iterator next_pos
=
146 std::search(it
, string
.end(), kHttpBr
, kHttpBr
+ br_size
);
147 lines
.push_back(base::StringPiece(it
, next_pos
));
148 if (next_pos
== string
.end())
150 it
= next_pos
+ br_size
;
155 // Remove transport padding (spaces and tabs at the end of line) from |piece|.
156 base::StringPiece
TrimTransportPadding(const base::StringPiece
& piece
) {
157 size_t trim_size
= 0;
158 while (trim_size
< piece
.size() &&
159 (piece
[piece
.size() - 1 - trim_size
] == ' ' ||
160 piece
[piece
.size() - 1 - trim_size
] == '\t')) {
163 return piece
.substr(0, piece
.size() - trim_size
);
166 void EmptyClosure(scoped_ptr
<BatchableDelegate
>) {
171 MultipartHttpResponse::MultipartHttpResponse() : code(HTTP_SUCCESS
) {
174 MultipartHttpResponse::~MultipartHttpResponse() {
177 // The |response| must be multipart/mixed format that contains child HTTP
178 // response of drive batch request.
179 // https://www.ietf.org/rfc/rfc2046.txt
183 // Content-type: application/http
186 // Header of child response
188 // Body of child response
190 // Content-type: application/http
192 // HTTP/1.1 404 Not Found
193 // Header of child response
195 // Body of child response
197 bool ParseMultipartResponse(const std::string
& content_type
,
198 const std::string
& response
,
199 std::vector
<MultipartHttpResponse
>* parts
) {
200 if (response
.empty())
203 base::StringPiece
content_type_piece(content_type
);
204 if (!content_type_piece
.starts_with(kMultipartMixedMimeTypePrefix
)) {
207 content_type_piece
.remove_prefix(
208 base::StringPiece(kMultipartMixedMimeTypePrefix
).size());
210 if (content_type_piece
.empty())
212 if (content_type_piece
[0] == '"') {
213 if (content_type_piece
.size() <= 2 ||
214 content_type_piece
[content_type_piece
.size() - 1] != '"') {
218 content_type_piece
.substr(1, content_type_piece
.size() - 2);
221 std::string boundary
;
222 content_type_piece
.CopyToString(&boundary
);
223 const std::string header
= "--" + boundary
;
224 const std::string terminator
= "--" + boundary
+ "--";
226 std::vector
<base::StringPiece
> lines
;
227 SplitIntoLines(response
, &lines
);
232 STATE_PART_HTTP_STATUS_LINE
,
233 STATE_PART_HTTP_HEADER
,
235 } state
= STATE_START
;
237 const std::string kHttpStatusPrefix
= "HTTP/1.1 ";
238 std::vector
<MultipartHttpResponse
> responses
;
239 DriveApiErrorCode code
= DRIVE_PARSE_ERROR
;
241 for (const auto& line
: lines
) {
242 if (state
== STATE_PART_HEADER
&& line
.empty()) {
243 state
= STATE_PART_HTTP_STATUS_LINE
;
247 if (state
== STATE_PART_HTTP_STATUS_LINE
) {
248 if (line
.starts_with(kHttpStatusPrefix
)) {
251 line
.substr(base::StringPiece(kHttpStatusPrefix
).size()),
254 code
= static_cast<DriveApiErrorCode
>(int_code
);
256 code
= DRIVE_PARSE_ERROR
;
258 code
= DRIVE_PARSE_ERROR
;
260 state
= STATE_PART_HTTP_HEADER
;
264 if (state
== STATE_PART_HTTP_HEADER
&& line
.empty()) {
265 state
= STATE_PART_HTTP_BODY
;
269 const base::StringPiece chopped_line
= TrimTransportPadding(line
);
270 const bool is_new_part
= chopped_line
== header
;
271 const bool was_last_part
= chopped_line
== terminator
;
272 if (is_new_part
|| was_last_part
) {
276 case STATE_PART_HEADER
:
277 case STATE_PART_HTTP_STATUS_LINE
:
278 responses
.push_back(MultipartHttpResponse());
279 responses
.back().code
= DRIVE_PARSE_ERROR
;
281 case STATE_PART_HTTP_HEADER
:
282 responses
.push_back(MultipartHttpResponse());
283 responses
.back().code
= code
;
285 case STATE_PART_HTTP_BODY
:
286 // Drop the last kHttpBr.
288 body
.resize(body
.size() - 2);
289 responses
.push_back(MultipartHttpResponse());
290 responses
.back().code
= code
;
291 responses
.back().body
.swap(body
);
295 state
= STATE_PART_HEADER
;
298 } else if (state
== STATE_PART_HTTP_BODY
) {
299 line
.AppendToString(&body
);
300 body
.append(kHttpBr
);
304 parts
->swap(responses
);
308 Property::Property() : visibility_(VISIBILITY_PRIVATE
) {
311 Property::~Property() {
314 //============================ DriveApiPartialFieldRequest ====================
316 DriveApiPartialFieldRequest::DriveApiPartialFieldRequest(
317 RequestSender
* sender
) : UrlFetchRequestBase(sender
) {
320 DriveApiPartialFieldRequest::~DriveApiPartialFieldRequest() {
323 GURL
DriveApiPartialFieldRequest::GetURL() const {
324 GURL url
= GetURLInternal();
325 if (!fields_
.empty())
326 url
= net::AppendOrReplaceQueryParameter(url
, "fields", fields_
);
330 //=============================== FilesGetRequest =============================
332 FilesGetRequest::FilesGetRequest(
333 RequestSender
* sender
,
334 const DriveApiUrlGenerator
& url_generator
,
335 bool use_internal_endpoint
,
336 const FileResourceCallback
& callback
)
337 : DriveApiDataRequest
<FileResource
>(sender
, callback
),
338 url_generator_(url_generator
),
339 use_internal_endpoint_(use_internal_endpoint
) {
340 DCHECK(!callback
.is_null());
343 FilesGetRequest::~FilesGetRequest() {}
345 GURL
FilesGetRequest::GetURLInternal() const {
346 return url_generator_
.GetFilesGetUrl(file_id_
,
347 use_internal_endpoint_
,
351 //============================ FilesAuthorizeRequest ===========================
353 FilesAuthorizeRequest::FilesAuthorizeRequest(
354 RequestSender
* sender
,
355 const DriveApiUrlGenerator
& url_generator
,
356 const FileResourceCallback
& callback
)
357 : DriveApiDataRequest
<FileResource
>(sender
, callback
),
358 url_generator_(url_generator
) {
359 DCHECK(!callback
.is_null());
362 FilesAuthorizeRequest::~FilesAuthorizeRequest() {}
364 net::URLFetcher::RequestType
FilesAuthorizeRequest::GetRequestType() const {
365 return net::URLFetcher::POST
;
368 GURL
FilesAuthorizeRequest::GetURLInternal() const {
369 return url_generator_
.GetFilesAuthorizeUrl(file_id_
, app_id_
);
372 //============================ FilesInsertRequest ============================
374 FilesInsertRequest::FilesInsertRequest(
375 RequestSender
* sender
,
376 const DriveApiUrlGenerator
& url_generator
,
377 const FileResourceCallback
& callback
)
378 : DriveApiDataRequest
<FileResource
>(sender
, callback
),
379 url_generator_(url_generator
) {
380 DCHECK(!callback
.is_null());
383 FilesInsertRequest::~FilesInsertRequest() {}
385 net::URLFetcher::RequestType
FilesInsertRequest::GetRequestType() const {
386 return net::URLFetcher::POST
;
389 bool FilesInsertRequest::GetContentData(std::string
* upload_content_type
,
390 std::string
* upload_content
) {
391 *upload_content_type
= util::kContentTypeApplicationJson
;
393 base::DictionaryValue root
;
395 if (!last_viewed_by_me_date_
.is_null()) {
396 root
.SetString("lastViewedByMeDate",
397 util::FormatTimeAsString(last_viewed_by_me_date_
));
400 if (!mime_type_
.empty())
401 root
.SetString("mimeType", mime_type_
);
403 if (!modified_date_
.is_null())
404 root
.SetString("modifiedDate", util::FormatTimeAsString(modified_date_
));
406 if (!parents_
.empty()) {
407 base::ListValue
* parents_value
= new base::ListValue
;
408 for (size_t i
= 0; i
< parents_
.size(); ++i
) {
409 base::DictionaryValue
* parent
= new base::DictionaryValue
;
410 parent
->SetString("id", parents_
[i
]);
411 parents_value
->Append(parent
);
413 root
.Set("parents", parents_value
);
417 root
.SetString("title", title_
);
419 AttachProperties(properties_
, &root
);
420 base::JSONWriter::Write(root
, upload_content
);
422 DVLOG(1) << "FilesInsert data: " << *upload_content_type
<< ", ["
423 << *upload_content
<< "]";
427 GURL
FilesInsertRequest::GetURLInternal() const {
428 return url_generator_
.GetFilesInsertUrl();
431 //============================== FilesPatchRequest ============================
433 FilesPatchRequest::FilesPatchRequest(
434 RequestSender
* sender
,
435 const DriveApiUrlGenerator
& url_generator
,
436 const FileResourceCallback
& callback
)
437 : DriveApiDataRequest
<FileResource
>(sender
, callback
),
438 url_generator_(url_generator
),
439 set_modified_date_(false),
440 update_viewed_date_(true) {
441 DCHECK(!callback
.is_null());
444 FilesPatchRequest::~FilesPatchRequest() {}
446 net::URLFetcher::RequestType
FilesPatchRequest::GetRequestType() const {
447 return net::URLFetcher::PATCH
;
450 std::vector
<std::string
> FilesPatchRequest::GetExtraRequestHeaders() const {
451 std::vector
<std::string
> headers
;
452 headers
.push_back(util::kIfMatchAllHeader
);
456 GURL
FilesPatchRequest::GetURLInternal() const {
457 return url_generator_
.GetFilesPatchUrl(
458 file_id_
, set_modified_date_
, update_viewed_date_
);
461 bool FilesPatchRequest::GetContentData(std::string
* upload_content_type
,
462 std::string
* upload_content
) {
463 if (title_
.empty() &&
464 modified_date_
.is_null() &&
465 last_viewed_by_me_date_
.is_null() &&
469 *upload_content_type
= util::kContentTypeApplicationJson
;
471 base::DictionaryValue root
;
473 root
.SetString("title", title_
);
475 if (!modified_date_
.is_null())
476 root
.SetString("modifiedDate", util::FormatTimeAsString(modified_date_
));
478 if (!last_viewed_by_me_date_
.is_null()) {
479 root
.SetString("lastViewedByMeDate",
480 util::FormatTimeAsString(last_viewed_by_me_date_
));
483 if (!parents_
.empty()) {
484 base::ListValue
* parents_value
= new base::ListValue
;
485 for (size_t i
= 0; i
< parents_
.size(); ++i
) {
486 base::DictionaryValue
* parent
= new base::DictionaryValue
;
487 parent
->SetString("id", parents_
[i
]);
488 parents_value
->Append(parent
);
490 root
.Set("parents", parents_value
);
493 AttachProperties(properties_
, &root
);
494 base::JSONWriter::Write(root
, upload_content
);
496 DVLOG(1) << "FilesPatch data: " << *upload_content_type
<< ", ["
497 << *upload_content
<< "]";
501 //============================= FilesCopyRequest ==============================
503 FilesCopyRequest::FilesCopyRequest(
504 RequestSender
* sender
,
505 const DriveApiUrlGenerator
& url_generator
,
506 const FileResourceCallback
& callback
)
507 : DriveApiDataRequest
<FileResource
>(sender
, callback
),
508 url_generator_(url_generator
) {
509 DCHECK(!callback
.is_null());
512 FilesCopyRequest::~FilesCopyRequest() {
515 net::URLFetcher::RequestType
FilesCopyRequest::GetRequestType() const {
516 return net::URLFetcher::POST
;
519 GURL
FilesCopyRequest::GetURLInternal() const {
520 return url_generator_
.GetFilesCopyUrl(file_id_
);
523 bool FilesCopyRequest::GetContentData(std::string
* upload_content_type
,
524 std::string
* upload_content
) {
525 if (parents_
.empty() && title_
.empty())
528 *upload_content_type
= util::kContentTypeApplicationJson
;
530 base::DictionaryValue root
;
532 if (!modified_date_
.is_null())
533 root
.SetString("modifiedDate", util::FormatTimeAsString(modified_date_
));
535 if (!parents_
.empty()) {
536 base::ListValue
* parents_value
= new base::ListValue
;
537 for (size_t i
= 0; i
< parents_
.size(); ++i
) {
538 base::DictionaryValue
* parent
= new base::DictionaryValue
;
539 parent
->SetString("id", parents_
[i
]);
540 parents_value
->Append(parent
);
542 root
.Set("parents", parents_value
);
546 root
.SetString("title", title_
);
548 base::JSONWriter::Write(root
, upload_content
);
549 DVLOG(1) << "FilesCopy data: " << *upload_content_type
<< ", ["
550 << *upload_content
<< "]";
554 //============================= FilesListRequest =============================
556 FilesListRequest::FilesListRequest(
557 RequestSender
* sender
,
558 const DriveApiUrlGenerator
& url_generator
,
559 const FileListCallback
& callback
)
560 : DriveApiDataRequest
<FileList
>(sender
, callback
),
561 url_generator_(url_generator
),
563 DCHECK(!callback
.is_null());
566 FilesListRequest::~FilesListRequest() {}
568 GURL
FilesListRequest::GetURLInternal() const {
569 return url_generator_
.GetFilesListUrl(max_results_
, page_token_
, q_
);
572 //======================== FilesListNextPageRequest =========================
574 FilesListNextPageRequest::FilesListNextPageRequest(
575 RequestSender
* sender
,
576 const FileListCallback
& callback
)
577 : DriveApiDataRequest
<FileList
>(sender
, callback
) {
578 DCHECK(!callback
.is_null());
581 FilesListNextPageRequest::~FilesListNextPageRequest() {
584 GURL
FilesListNextPageRequest::GetURLInternal() const {
588 //============================ FilesDeleteRequest =============================
590 FilesDeleteRequest::FilesDeleteRequest(
591 RequestSender
* sender
,
592 const DriveApiUrlGenerator
& url_generator
,
593 const EntryActionCallback
& callback
)
594 : EntryActionRequest(sender
, callback
),
595 url_generator_(url_generator
) {
596 DCHECK(!callback
.is_null());
599 FilesDeleteRequest::~FilesDeleteRequest() {}
601 net::URLFetcher::RequestType
FilesDeleteRequest::GetRequestType() const {
602 return net::URLFetcher::DELETE_REQUEST
;
605 GURL
FilesDeleteRequest::GetURL() const {
606 return url_generator_
.GetFilesDeleteUrl(file_id_
);
609 std::vector
<std::string
> FilesDeleteRequest::GetExtraRequestHeaders() const {
610 std::vector
<std::string
> headers(
611 EntryActionRequest::GetExtraRequestHeaders());
612 headers
.push_back(util::GenerateIfMatchHeader(etag_
));
616 //============================ FilesTrashRequest =============================
618 FilesTrashRequest::FilesTrashRequest(
619 RequestSender
* sender
,
620 const DriveApiUrlGenerator
& url_generator
,
621 const FileResourceCallback
& callback
)
622 : DriveApiDataRequest
<FileResource
>(sender
, callback
),
623 url_generator_(url_generator
) {
624 DCHECK(!callback
.is_null());
627 FilesTrashRequest::~FilesTrashRequest() {}
629 net::URLFetcher::RequestType
FilesTrashRequest::GetRequestType() const {
630 return net::URLFetcher::POST
;
633 GURL
FilesTrashRequest::GetURLInternal() const {
634 return url_generator_
.GetFilesTrashUrl(file_id_
);
637 //============================== AboutGetRequest =============================
639 AboutGetRequest::AboutGetRequest(
640 RequestSender
* sender
,
641 const DriveApiUrlGenerator
& url_generator
,
642 const AboutResourceCallback
& callback
)
643 : DriveApiDataRequest
<AboutResource
>(sender
, callback
),
644 url_generator_(url_generator
) {
645 DCHECK(!callback
.is_null());
648 AboutGetRequest::~AboutGetRequest() {}
650 GURL
AboutGetRequest::GetURLInternal() const {
651 return url_generator_
.GetAboutGetUrl();
654 //============================ ChangesListRequest ===========================
656 ChangesListRequest::ChangesListRequest(
657 RequestSender
* sender
,
658 const DriveApiUrlGenerator
& url_generator
,
659 const ChangeListCallback
& callback
)
660 : DriveApiDataRequest
<ChangeList
>(sender
, callback
),
661 url_generator_(url_generator
),
662 include_deleted_(true),
664 start_change_id_(0) {
665 DCHECK(!callback
.is_null());
668 ChangesListRequest::~ChangesListRequest() {}
670 GURL
ChangesListRequest::GetURLInternal() const {
671 return url_generator_
.GetChangesListUrl(
672 include_deleted_
, max_results_
, page_token_
, start_change_id_
);
675 //======================== ChangesListNextPageRequest =========================
677 ChangesListNextPageRequest::ChangesListNextPageRequest(
678 RequestSender
* sender
,
679 const ChangeListCallback
& callback
)
680 : DriveApiDataRequest
<ChangeList
>(sender
, callback
) {
681 DCHECK(!callback
.is_null());
684 ChangesListNextPageRequest::~ChangesListNextPageRequest() {
687 GURL
ChangesListNextPageRequest::GetURLInternal() const {
691 //============================== AppsListRequest ===========================
693 AppsListRequest::AppsListRequest(
694 RequestSender
* sender
,
695 const DriveApiUrlGenerator
& url_generator
,
696 bool use_internal_endpoint
,
697 const AppListCallback
& callback
)
698 : DriveApiDataRequest
<AppList
>(sender
, callback
),
699 url_generator_(url_generator
),
700 use_internal_endpoint_(use_internal_endpoint
) {
701 DCHECK(!callback
.is_null());
704 AppsListRequest::~AppsListRequest() {}
706 GURL
AppsListRequest::GetURLInternal() const {
707 return url_generator_
.GetAppsListUrl(use_internal_endpoint_
);
710 //============================== AppsDeleteRequest ===========================
712 AppsDeleteRequest::AppsDeleteRequest(RequestSender
* sender
,
713 const DriveApiUrlGenerator
& url_generator
,
714 const EntryActionCallback
& callback
)
715 : EntryActionRequest(sender
, callback
),
716 url_generator_(url_generator
) {
717 DCHECK(!callback
.is_null());
720 AppsDeleteRequest::~AppsDeleteRequest() {}
722 net::URLFetcher::RequestType
AppsDeleteRequest::GetRequestType() const {
723 return net::URLFetcher::DELETE_REQUEST
;
726 GURL
AppsDeleteRequest::GetURL() const {
727 return url_generator_
.GetAppsDeleteUrl(app_id_
);
730 //========================== ChildrenInsertRequest ============================
732 ChildrenInsertRequest::ChildrenInsertRequest(
733 RequestSender
* sender
,
734 const DriveApiUrlGenerator
& url_generator
,
735 const EntryActionCallback
& callback
)
736 : EntryActionRequest(sender
, callback
),
737 url_generator_(url_generator
) {
738 DCHECK(!callback
.is_null());
741 ChildrenInsertRequest::~ChildrenInsertRequest() {}
743 net::URLFetcher::RequestType
ChildrenInsertRequest::GetRequestType() const {
744 return net::URLFetcher::POST
;
747 GURL
ChildrenInsertRequest::GetURL() const {
748 return url_generator_
.GetChildrenInsertUrl(folder_id_
);
751 bool ChildrenInsertRequest::GetContentData(std::string
* upload_content_type
,
752 std::string
* upload_content
) {
753 *upload_content_type
= util::kContentTypeApplicationJson
;
755 base::DictionaryValue root
;
756 root
.SetString("id", id_
);
758 base::JSONWriter::Write(root
, upload_content
);
759 DVLOG(1) << "InsertResource data: " << *upload_content_type
<< ", ["
760 << *upload_content
<< "]";
764 //========================== ChildrenDeleteRequest ============================
766 ChildrenDeleteRequest::ChildrenDeleteRequest(
767 RequestSender
* sender
,
768 const DriveApiUrlGenerator
& url_generator
,
769 const EntryActionCallback
& callback
)
770 : EntryActionRequest(sender
, callback
),
771 url_generator_(url_generator
) {
772 DCHECK(!callback
.is_null());
775 ChildrenDeleteRequest::~ChildrenDeleteRequest() {}
777 net::URLFetcher::RequestType
ChildrenDeleteRequest::GetRequestType() const {
778 return net::URLFetcher::DELETE_REQUEST
;
781 GURL
ChildrenDeleteRequest::GetURL() const {
782 return url_generator_
.GetChildrenDeleteUrl(child_id_
, folder_id_
);
785 //======================= InitiateUploadNewFileRequest =======================
787 InitiateUploadNewFileRequest::InitiateUploadNewFileRequest(
788 RequestSender
* sender
,
789 const DriveApiUrlGenerator
& url_generator
,
790 const std::string
& content_type
,
791 int64 content_length
,
792 const std::string
& parent_resource_id
,
793 const std::string
& title
,
794 const InitiateUploadCallback
& callback
)
795 : InitiateUploadRequestBase(sender
,
799 url_generator_(url_generator
),
800 parent_resource_id_(parent_resource_id
),
804 InitiateUploadNewFileRequest::~InitiateUploadNewFileRequest() {}
806 GURL
InitiateUploadNewFileRequest::GetURL() const {
807 return url_generator_
.GetInitiateUploadNewFileUrl(!modified_date_
.is_null());
810 net::URLFetcher::RequestType
811 InitiateUploadNewFileRequest::GetRequestType() const {
812 return net::URLFetcher::POST
;
815 bool InitiateUploadNewFileRequest::GetContentData(
816 std::string
* upload_content_type
,
817 std::string
* upload_content
) {
818 *upload_content_type
= util::kContentTypeApplicationJson
;
820 base::DictionaryValue root
;
821 root
.SetString("title", title_
);
824 scoped_ptr
<base::ListValue
> parents(new base::ListValue
);
825 parents
->Append(util::CreateParentValue(parent_resource_id_
).release());
826 root
.Set("parents", parents
.release());
828 if (!modified_date_
.is_null())
829 root
.SetString("modifiedDate", util::FormatTimeAsString(modified_date_
));
831 if (!last_viewed_by_me_date_
.is_null()) {
832 root
.SetString("lastViewedByMeDate",
833 util::FormatTimeAsString(last_viewed_by_me_date_
));
836 AttachProperties(properties_
, &root
);
837 base::JSONWriter::Write(root
, upload_content
);
839 DVLOG(1) << "InitiateUploadNewFile data: " << *upload_content_type
<< ", ["
840 << *upload_content
<< "]";
844 //===================== InitiateUploadExistingFileRequest ====================
846 InitiateUploadExistingFileRequest::InitiateUploadExistingFileRequest(
847 RequestSender
* sender
,
848 const DriveApiUrlGenerator
& url_generator
,
849 const std::string
& content_type
,
850 int64 content_length
,
851 const std::string
& resource_id
,
852 const std::string
& etag
,
853 const InitiateUploadCallback
& callback
)
854 : InitiateUploadRequestBase(sender
,
858 url_generator_(url_generator
),
859 resource_id_(resource_id
),
863 InitiateUploadExistingFileRequest::~InitiateUploadExistingFileRequest() {}
865 GURL
InitiateUploadExistingFileRequest::GetURL() const {
866 return url_generator_
.GetInitiateUploadExistingFileUrl(
867 resource_id_
, !modified_date_
.is_null());
870 net::URLFetcher::RequestType
871 InitiateUploadExistingFileRequest::GetRequestType() const {
872 return net::URLFetcher::PUT
;
875 std::vector
<std::string
>
876 InitiateUploadExistingFileRequest::GetExtraRequestHeaders() const {
877 std::vector
<std::string
> headers(
878 InitiateUploadRequestBase::GetExtraRequestHeaders());
879 headers
.push_back(util::GenerateIfMatchHeader(etag_
));
883 bool InitiateUploadExistingFileRequest::GetContentData(
884 std::string
* upload_content_type
,
885 std::string
* upload_content
) {
886 base::DictionaryValue root
;
887 if (!parent_resource_id_
.empty()) {
888 scoped_ptr
<base::ListValue
> parents(new base::ListValue
);
889 parents
->Append(util::CreateParentValue(parent_resource_id_
).release());
890 root
.Set("parents", parents
.release());
894 root
.SetString("title", title_
);
896 if (!modified_date_
.is_null())
897 root
.SetString("modifiedDate", util::FormatTimeAsString(modified_date_
));
899 if (!last_viewed_by_me_date_
.is_null()) {
900 root
.SetString("lastViewedByMeDate",
901 util::FormatTimeAsString(last_viewed_by_me_date_
));
904 AttachProperties(properties_
, &root
);
908 *upload_content_type
= util::kContentTypeApplicationJson
;
909 base::JSONWriter::Write(root
, upload_content
);
910 DVLOG(1) << "InitiateUploadExistingFile data: " << *upload_content_type
911 << ", [" << *upload_content
<< "]";
915 //============================ ResumeUploadRequest ===========================
917 ResumeUploadRequest::ResumeUploadRequest(
918 RequestSender
* sender
,
919 const GURL
& upload_location
,
920 int64 start_position
,
922 int64 content_length
,
923 const std::string
& content_type
,
924 const base::FilePath
& local_file_path
,
925 const UploadRangeCallback
& callback
,
926 const ProgressCallback
& progress_callback
)
927 : ResumeUploadRequestBase(sender
,
935 progress_callback_(progress_callback
) {
936 DCHECK(!callback_
.is_null());
939 ResumeUploadRequest::~ResumeUploadRequest() {}
941 void ResumeUploadRequest::OnRangeRequestComplete(
942 const UploadRangeResponse
& response
,
943 scoped_ptr
<base::Value
> value
) {
944 DCHECK(CalledOnValidThread());
945 ParseFileResourceWithUploadRangeAndRun(callback_
, response
, value
.Pass());
948 void ResumeUploadRequest::OnURLFetchUploadProgress(
949 const net::URLFetcher
* source
, int64 current
, int64 total
) {
950 if (!progress_callback_
.is_null())
951 progress_callback_
.Run(current
, total
);
954 //========================== GetUploadStatusRequest ==========================
956 GetUploadStatusRequest::GetUploadStatusRequest(
957 RequestSender
* sender
,
958 const GURL
& upload_url
,
959 int64 content_length
,
960 const UploadRangeCallback
& callback
)
961 : GetUploadStatusRequestBase(sender
,
964 callback_(callback
) {
965 DCHECK(!callback
.is_null());
968 GetUploadStatusRequest::~GetUploadStatusRequest() {}
970 void GetUploadStatusRequest::OnRangeRequestComplete(
971 const UploadRangeResponse
& response
,
972 scoped_ptr
<base::Value
> value
) {
973 DCHECK(CalledOnValidThread());
974 ParseFileResourceWithUploadRangeAndRun(callback_
, response
, value
.Pass());
977 //======================= MultipartUploadNewFileDelegate =======================
979 MultipartUploadNewFileDelegate::MultipartUploadNewFileDelegate(
980 RequestSender
* sender
,
981 const std::string
& title
,
982 const std::string
& parent_resource_id
,
983 const std::string
& content_type
,
984 int64 content_length
,
985 const base::Time
& modified_date
,
986 const base::Time
& last_viewed_by_me_date
,
987 const base::FilePath
& local_file_path
,
988 const Properties
& properties
,
989 const DriveApiUrlGenerator
& url_generator
,
990 const FileResourceCallback
& callback
,
991 const ProgressCallback
& progress_callback
)
992 : MultipartUploadRequestBase(
993 sender
->blocking_task_runner(),
994 CreateMultipartUploadMetadataJson(title
,
997 last_viewed_by_me_date
,
1004 has_modified_date_(!modified_date
.is_null()),
1005 url_generator_(url_generator
) {
1008 MultipartUploadNewFileDelegate::~MultipartUploadNewFileDelegate() {
1011 GURL
MultipartUploadNewFileDelegate::GetURL() const {
1012 return url_generator_
.GetMultipartUploadNewFileUrl(has_modified_date_
);
1015 net::URLFetcher::RequestType
MultipartUploadNewFileDelegate::GetRequestType()
1017 return net::URLFetcher::POST
;
1020 //====================== MultipartUploadExistingFileDelegate ===================
1022 MultipartUploadExistingFileDelegate::MultipartUploadExistingFileDelegate(
1023 RequestSender
* sender
,
1024 const std::string
& title
,
1025 const std::string
& resource_id
,
1026 const std::string
& parent_resource_id
,
1027 const std::string
& content_type
,
1028 int64 content_length
,
1029 const base::Time
& modified_date
,
1030 const base::Time
& last_viewed_by_me_date
,
1031 const base::FilePath
& local_file_path
,
1032 const std::string
& etag
,
1033 const Properties
& properties
,
1034 const DriveApiUrlGenerator
& url_generator
,
1035 const FileResourceCallback
& callback
,
1036 const ProgressCallback
& progress_callback
)
1037 : MultipartUploadRequestBase(
1038 sender
->blocking_task_runner(),
1039 CreateMultipartUploadMetadataJson(title
,
1042 last_viewed_by_me_date
,
1049 resource_id_(resource_id
),
1051 has_modified_date_(!modified_date
.is_null()),
1052 url_generator_(url_generator
) {
1055 MultipartUploadExistingFileDelegate::~MultipartUploadExistingFileDelegate() {
1058 std::vector
<std::string
>
1059 MultipartUploadExistingFileDelegate::GetExtraRequestHeaders() const {
1060 std::vector
<std::string
> headers(
1061 MultipartUploadRequestBase::GetExtraRequestHeaders());
1062 headers
.push_back(util::GenerateIfMatchHeader(etag_
));
1066 GURL
MultipartUploadExistingFileDelegate::GetURL() const {
1067 return url_generator_
.GetMultipartUploadExistingFileUrl(resource_id_
,
1068 has_modified_date_
);
1071 net::URLFetcher::RequestType
1072 MultipartUploadExistingFileDelegate::GetRequestType() const {
1073 return net::URLFetcher::PUT
;
1076 //========================== DownloadFileRequest ==========================
1078 DownloadFileRequest::DownloadFileRequest(
1079 RequestSender
* sender
,
1080 const DriveApiUrlGenerator
& url_generator
,
1081 const std::string
& resource_id
,
1082 const base::FilePath
& output_file_path
,
1083 const DownloadActionCallback
& download_action_callback
,
1084 const GetContentCallback
& get_content_callback
,
1085 const ProgressCallback
& progress_callback
)
1086 : DownloadFileRequestBase(
1088 download_action_callback
,
1089 get_content_callback
,
1091 url_generator
.GenerateDownloadFileUrl(resource_id
),
1095 DownloadFileRequest::~DownloadFileRequest() {
1098 //========================== PermissionsInsertRequest ==========================
1100 PermissionsInsertRequest::PermissionsInsertRequest(
1101 RequestSender
* sender
,
1102 const DriveApiUrlGenerator
& url_generator
,
1103 const EntryActionCallback
& callback
)
1104 : EntryActionRequest(sender
, callback
),
1105 url_generator_(url_generator
),
1106 type_(PERMISSION_TYPE_USER
),
1107 role_(PERMISSION_ROLE_READER
) {
1110 PermissionsInsertRequest::~PermissionsInsertRequest() {
1113 GURL
PermissionsInsertRequest::GetURL() const {
1114 return url_generator_
.GetPermissionsInsertUrl(id_
);
1117 net::URLFetcher::RequestType
1118 PermissionsInsertRequest::GetRequestType() const {
1119 return net::URLFetcher::POST
;
1122 bool PermissionsInsertRequest::GetContentData(std::string
* upload_content_type
,
1123 std::string
* upload_content
) {
1124 *upload_content_type
= util::kContentTypeApplicationJson
;
1126 base::DictionaryValue root
;
1128 case PERMISSION_TYPE_ANYONE
:
1129 root
.SetString("type", "anyone");
1131 case PERMISSION_TYPE_DOMAIN
:
1132 root
.SetString("type", "domain");
1134 case PERMISSION_TYPE_GROUP
:
1135 root
.SetString("type", "group");
1137 case PERMISSION_TYPE_USER
:
1138 root
.SetString("type", "user");
1142 case PERMISSION_ROLE_OWNER
:
1143 root
.SetString("role", "owner");
1145 case PERMISSION_ROLE_READER
:
1146 root
.SetString("role", "reader");
1148 case PERMISSION_ROLE_WRITER
:
1149 root
.SetString("role", "writer");
1151 case PERMISSION_ROLE_COMMENTER
:
1152 root
.SetString("role", "reader");
1154 base::ListValue
* list
= new base::ListValue
;
1155 list
->AppendString("commenter");
1156 root
.Set("additionalRoles", list
);
1160 root
.SetString("value", value_
);
1161 base::JSONWriter::Write(root
, upload_content
);
1165 //======================= SingleBatchableDelegateRequest =======================
1167 SingleBatchableDelegateRequest::SingleBatchableDelegateRequest(
1168 RequestSender
* sender
,
1169 BatchableDelegate
* delegate
)
1170 : UrlFetchRequestBase(sender
),
1171 delegate_(delegate
),
1172 weak_ptr_factory_(this) {
1175 SingleBatchableDelegateRequest::~SingleBatchableDelegateRequest() {
1178 GURL
SingleBatchableDelegateRequest::GetURL() const {
1179 return delegate_
->GetURL();
1182 net::URLFetcher::RequestType
SingleBatchableDelegateRequest::GetRequestType()
1184 return delegate_
->GetRequestType();
1187 std::vector
<std::string
>
1188 SingleBatchableDelegateRequest::GetExtraRequestHeaders() const {
1189 return delegate_
->GetExtraRequestHeaders();
1192 void SingleBatchableDelegateRequest::Prepare(const PrepareCallback
& callback
) {
1193 delegate_
->Prepare(callback
);
1196 bool SingleBatchableDelegateRequest::GetContentData(
1197 std::string
* upload_content_type
,
1198 std::string
* upload_content
) {
1199 return delegate_
->GetContentData(upload_content_type
, upload_content
);
1202 void SingleBatchableDelegateRequest::ProcessURLFetchResults(
1203 const net::URLFetcher
* source
) {
1204 delegate_
->NotifyResult(
1205 GetErrorCode(), response_writer()->data(),
1207 &SingleBatchableDelegateRequest::OnProcessURLFetchResultsComplete
,
1208 weak_ptr_factory_
.GetWeakPtr()));
1211 void SingleBatchableDelegateRequest::RunCallbackOnPrematureFailure(
1212 DriveApiErrorCode code
) {
1213 delegate_
->NotifyError(code
);
1216 void SingleBatchableDelegateRequest::OnURLFetchUploadProgress(
1217 const net::URLFetcher
* source
,
1220 delegate_
->NotifyUploadProgress(source
, current
, total
);
1223 //========================== BatchUploadRequest ==========================
1225 BatchUploadChildEntry::BatchUploadChildEntry(BatchableDelegate
* request
)
1226 : request(request
), prepared(false), data_offset(0), data_size(0) {
1229 BatchUploadChildEntry::~BatchUploadChildEntry() {
1232 BatchUploadRequest::BatchUploadRequest(
1233 RequestSender
* sender
,
1234 const DriveApiUrlGenerator
& url_generator
)
1235 : UrlFetchRequestBase(sender
),
1237 url_generator_(url_generator
),
1239 last_progress_value_(0),
1240 weak_ptr_factory_(this) {
1243 BatchUploadRequest::~BatchUploadRequest() {
1246 void BatchUploadRequest::SetBoundaryForTesting(const std::string
& boundary
) {
1247 boundary_
= boundary
;
1250 void BatchUploadRequest::AddRequest(BatchableDelegate
* request
) {
1251 DCHECK(CalledOnValidThread());
1253 DCHECK(GetChildEntry(request
) == child_requests_
.end());
1254 DCHECK(!committed_
);
1255 child_requests_
.push_back(new BatchUploadChildEntry(request
));
1256 request
->Prepare(base::Bind(&BatchUploadRequest::OnChildRequestPrepared
,
1257 weak_ptr_factory_
.GetWeakPtr(), request
));
1260 void BatchUploadRequest::OnChildRequestPrepared(RequestID request_id
,
1261 DriveApiErrorCode result
) {
1262 DCHECK(CalledOnValidThread());
1263 auto const child
= GetChildEntry(request_id
);
1264 DCHECK(child
!= child_requests_
.end());
1265 if (IsSuccessfulDriveApiErrorCode(result
)) {
1266 (*child
)->prepared
= true;
1268 (*child
)->request
->NotifyError(result
);
1269 child_requests_
.erase(child
);
1271 MayCompletePrepare();
1274 void BatchUploadRequest::Commit() {
1275 DCHECK(CalledOnValidThread());
1276 DCHECK(!committed_
);
1277 if (child_requests_
.empty()) {
1281 MayCompletePrepare();
1285 void BatchUploadRequest::Prepare(const PrepareCallback
& callback
) {
1286 DCHECK(CalledOnValidThread());
1287 DCHECK(!callback
.is_null());
1288 prepare_callback_
= callback
;
1289 MayCompletePrepare();
1292 void BatchUploadRequest::Cancel() {
1293 child_requests_
.clear();
1294 UrlFetchRequestBase::Cancel();
1297 // Obtains corresponding child entry of |request_id|. Returns NULL if the
1298 // entry is not found.
1299 ScopedVector
<BatchUploadChildEntry
>::iterator
BatchUploadRequest::GetChildEntry(
1300 RequestID request_id
) {
1301 for (auto it
= child_requests_
.begin(); it
!= child_requests_
.end(); ++it
) {
1302 if ((*it
)->request
.get() == request_id
)
1305 return child_requests_
.end();
1308 void BatchUploadRequest::MayCompletePrepare() {
1309 if (!committed_
|| prepare_callback_
.is_null())
1311 for (const auto& child
: child_requests_
) {
1312 if (!child
->prepared
)
1316 // Build multipart body here.
1317 std::vector
<ContentTypeAndData
> parts
;
1318 for (auto& child
: child_requests_
) {
1321 const bool result
= child
->request
->GetContentData(&type
, &data
);
1322 // Upload request must have content data.
1325 const GURL url
= child
->request
->GetURL();
1327 switch (child
->request
->GetRequestType()) {
1328 case net::URLFetcher::POST
:
1331 case net::URLFetcher::PUT
:
1338 const std::string header
= base::StringPrintf(
1339 kBatchUploadRequestFormat
, method
.c_str(), url
.path().c_str(),
1340 url_generator_
.GetBatchUploadUrl().host().c_str(), type
.c_str());
1342 child
->data_offset
= header
.size();
1343 child
->data_size
= data
.size();
1345 parts
.push_back(ContentTypeAndData());
1346 parts
.back().type
= kHttpContentType
;
1347 parts
.back().data
= header
;
1348 parts
.back().data
.append(data
);
1351 std::vector
<uint64
> part_data_offset
;
1352 GenerateMultipartBody(MULTIPART_MIXED
, boundary_
, parts
, &upload_content_
,
1354 DCHECK(part_data_offset
.size() == child_requests_
.size());
1355 for (size_t i
= 0; i
< child_requests_
.size(); ++i
) {
1356 child_requests_
[i
]->data_offset
+= part_data_offset
[i
];
1358 prepare_callback_
.Run(HTTP_SUCCESS
);
1361 bool BatchUploadRequest::GetContentData(std::string
* upload_content_type
,
1362 std::string
* upload_content_data
) {
1363 upload_content_type
->assign(upload_content_
.type
);
1364 upload_content_data
->assign(upload_content_
.data
);
1368 base::WeakPtr
<BatchUploadRequest
>
1369 BatchUploadRequest::GetWeakPtrAsBatchUploadRequest() {
1370 return weak_ptr_factory_
.GetWeakPtr();
1373 GURL
BatchUploadRequest::GetURL() const {
1374 return url_generator_
.GetBatchUploadUrl();
1377 net::URLFetcher::RequestType
BatchUploadRequest::GetRequestType() const {
1378 return net::URLFetcher::PUT
;
1381 std::vector
<std::string
> BatchUploadRequest::GetExtraRequestHeaders() const {
1382 std::vector
<std::string
> headers
;
1383 headers
.push_back(kBatchUploadHeader
);
1387 void BatchUploadRequest::ProcessURLFetchResults(const net::URLFetcher
* source
) {
1388 if (!IsSuccessfulDriveApiErrorCode(GetErrorCode())) {
1389 RunCallbackOnPrematureFailure(GetErrorCode());
1390 sender_
->RequestFinished(this);
1394 std::string content_type
;
1395 source
->GetResponseHeaders()->EnumerateHeader(
1396 /* need only first header */ NULL
, "Content-Type", &content_type
);
1398 std::vector
<MultipartHttpResponse
> parts
;
1399 if (!ParseMultipartResponse(content_type
, response_writer()->data(),
1401 child_requests_
.size() != parts
.size()) {
1402 RunCallbackOnPrematureFailure(DRIVE_PARSE_ERROR
);
1403 sender_
->RequestFinished(this);
1407 for (size_t i
= 0; i
< parts
.size(); ++i
) {
1408 BatchableDelegate
* const delegate
= child_requests_
[i
]->request
.get();
1409 // Pass onwership of |delegate| so that child_requests_.clear() won't
1410 // kill the delegate. It has to be deleted after the notification.
1411 delegate
->NotifyResult(
1412 parts
[i
].code
, parts
[i
].body
,
1413 base::Bind(&EmptyClosure
, base::Passed(&child_requests_
[i
]->request
)));
1415 child_requests_
.clear();
1417 sender_
->RequestFinished(this);
1420 void BatchUploadRequest::RunCallbackOnPrematureFailure(DriveApiErrorCode code
) {
1421 for (auto child
: child_requests_
) {
1422 child
->request
->NotifyError(code
);
1424 child_requests_
.clear();
1427 void BatchUploadRequest::OnURLFetchUploadProgress(const net::URLFetcher
* source
,
1430 for (auto child
: child_requests_
) {
1431 if (child
->data_offset
<= current
&&
1432 current
<= child
->data_offset
+ child
->data_size
) {
1433 child
->request
->NotifyUploadProgress(source
, current
- child
->data_offset
,
1435 } else if (last_progress_value_
< child
->data_offset
+ child
->data_size
&&
1436 child
->data_offset
+ child
->data_size
< current
) {
1437 child
->request
->NotifyUploadProgress(source
, child
->data_size
,
1441 last_progress_value_
= current
;
1443 } // namespace drive
1444 } // namespace google_apis