Pin Chrome's shortcut to the Win10 Start menu on install and OS upgrade.
[chromium-blink-merge.git] / google_apis / drive / drive_api_requests.cc
blobd9089bbe4378f1a6533fb16cb5d303d47ffc2ebe
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"
7 #include "base/bind.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 {
26 namespace drive {
27 namespace {
29 // Format of one request in batch uploading request.
30 const char kBatchUploadRequestFormat[] =
31 "%s %s HTTP/1.1\n"
32 "Host: %s\n"
33 "X-Goog-Upload-Protocol: multipart\n"
34 "Content-Type: %s\n"
35 "\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=";
49 // UMA names.
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;
66 if (value) {
67 file_resource = FileResource::CreateFrom(*value);
68 if (!file_resource) {
69 callback.Run(
70 UploadRangeResponse(DRIVE_PARSE_ERROR,
71 response.start_position_received,
72 response.end_position_received),
73 scoped_ptr<FileResource>());
74 return;
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) {
85 DCHECK(request_body);
86 if (properties.empty())
87 return;
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";
96 break;
97 case Property::VISIBILITY_PUBLIC:
98 visibility_as_string = "PUBLIC";
99 break;
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;
119 if (!title.empty())
120 root.SetString("title", title);
122 // Fill parent link.
123 if (!parent_resource_id.empty()) {
124 scoped_ptr<base::ListValue> parents(new base::ListValue);
125 parents->Append(
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);
143 return 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;
153 while (true) {
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())
158 break;
159 it = next_pos + br_size;
161 output->swap(lines);
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')) {
170 ++trim_size;
172 return piece.substr(0, piece.size() - trim_size);
175 void EmptyClosure(scoped_ptr<BatchableDelegate>) {
178 } // namespace
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
190 // It looks like:
191 // --Boundary
192 // Content-type: application/http
194 // HTTP/1.1 200 OK
195 // Header of child response
197 // Body of child response
198 // --Boundary
199 // Content-type: application/http
201 // HTTP/1.1 404 Not Found
202 // Header of child response
204 // Body of child response
205 // --Boundary--
206 bool ParseMultipartResponse(const std::string& content_type,
207 const std::string& response,
208 std::vector<MultipartHttpResponse>* parts) {
209 if (response.empty())
210 return false;
212 base::StringPiece content_type_piece(content_type);
213 if (!content_type_piece.starts_with(kMultipartMixedMimeTypePrefix)) {
214 return false;
216 content_type_piece.remove_prefix(
217 base::StringPiece(kMultipartMixedMimeTypePrefix).size());
219 if (content_type_piece.empty())
220 return false;
221 if (content_type_piece[0] == '"') {
222 if (content_type_piece.size() <= 2 ||
223 content_type_piece[content_type_piece.size() - 1] != '"') {
224 return false;
226 content_type_piece =
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);
238 enum {
239 STATE_START,
240 STATE_PART_HEADER,
241 STATE_PART_HTTP_STATUS_LINE,
242 STATE_PART_HTTP_HEADER,
243 STATE_PART_HTTP_BODY
244 } state = STATE_START;
246 const std::string kHttpStatusPrefix = "HTTP/1.1 ";
247 std::vector<MultipartHttpResponse> responses;
248 DriveApiErrorCode code = DRIVE_PARSE_ERROR;
249 std::string body;
250 for (const auto& line : lines) {
251 if (state == STATE_PART_HEADER && line.empty()) {
252 state = STATE_PART_HTTP_STATUS_LINE;
253 continue;
256 if (state == STATE_PART_HTTP_STATUS_LINE) {
257 if (line.starts_with(kHttpStatusPrefix)) {
258 int int_code;
259 base::StringToInt(
260 line.substr(base::StringPiece(kHttpStatusPrefix).size()),
261 &int_code);
262 if (int_code > 0)
263 code = static_cast<DriveApiErrorCode>(int_code);
264 else
265 code = DRIVE_PARSE_ERROR;
266 } else {
267 code = DRIVE_PARSE_ERROR;
269 state = STATE_PART_HTTP_HEADER;
270 continue;
273 if (state == STATE_PART_HTTP_HEADER && line.empty()) {
274 state = STATE_PART_HTTP_BODY;
275 body.clear();
276 continue;
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) {
282 switch (state) {
283 case STATE_START:
284 break;
285 case STATE_PART_HEADER:
286 case STATE_PART_HTTP_STATUS_LINE:
287 responses.push_back(MultipartHttpResponse());
288 responses.back().code = DRIVE_PARSE_ERROR;
289 break;
290 case STATE_PART_HTTP_HEADER:
291 responses.push_back(MultipartHttpResponse());
292 responses.back().code = code;
293 break;
294 case STATE_PART_HTTP_BODY:
295 // Drop the last kHttpBr.
296 if (!body.empty())
297 body.resize(body.size() - 2);
298 responses.push_back(MultipartHttpResponse());
299 responses.back().code = code;
300 responses.back().body.swap(body);
301 break;
303 if (is_new_part)
304 state = STATE_PART_HEADER;
305 if (was_last_part)
306 break;
307 } else if (state == STATE_PART_HTTP_BODY) {
308 line.AppendToString(&body);
309 body.append(kHttpBr);
313 parts->swap(responses);
314 return true;
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_);
336 return url;
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_,
357 embed_origin_);
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);
425 if (!title_.empty())
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 << "]";
433 return true;
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);
462 return headers;
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() &&
475 parents_.empty())
476 return false;
478 *upload_content_type = util::kContentTypeApplicationJson;
480 base::DictionaryValue root;
481 if (!title_.empty())
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 << "]";
507 return true;
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())
535 return false;
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);
554 if (!title_.empty())
555 root.SetString("title", title_);
557 base::JSONWriter::Write(root, upload_content);
558 DVLOG(1) << "FilesCopy data: " << *upload_content_type << ", ["
559 << *upload_content << "]";
560 return true;
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),
571 max_results_(100) {
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 {
594 return next_link_;
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_));
622 return headers;
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),
672 max_results_(100),
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 {
697 return next_link_;
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 << "]";
770 return true;
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,
805 callback,
806 content_type,
807 content_length),
808 url_generator_(url_generator),
809 parent_resource_id_(parent_resource_id),
810 title_(title) {
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_);
832 // Fill parent link.
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 << "]";
850 return true;
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,
864 callback,
865 content_type,
866 content_length),
867 url_generator_(url_generator),
868 resource_id_(resource_id),
869 etag_(etag) {
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_));
889 return headers;
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());
902 if (!title_.empty())
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);
914 if (root.empty())
915 return false;
917 *upload_content_type = util::kContentTypeApplicationJson;
918 base::JSONWriter::Write(root, upload_content);
919 DVLOG(1) << "InitiateUploadExistingFile data: " << *upload_content_type
920 << ", [" << *upload_content << "]";
921 return true;
924 //============================ ResumeUploadRequest ===========================
926 ResumeUploadRequest::ResumeUploadRequest(
927 RequestSender* sender,
928 const GURL& upload_location,
929 int64 start_position,
930 int64 end_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,
937 upload_location,
938 start_position,
939 end_position,
940 content_length,
941 content_type,
942 local_file_path),
943 callback_(callback),
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,
971 upload_url,
972 content_length),
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(
1002 task_runner,
1003 CreateMultipartUploadMetadataJson(title,
1004 parent_resource_id,
1005 modified_date,
1006 last_viewed_by_me_date,
1007 properties),
1008 content_type,
1009 content_length,
1010 local_file_path,
1011 callback,
1012 progress_callback),
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()
1025 const {
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(
1047 task_runner,
1048 CreateMultipartUploadMetadataJson(title,
1049 parent_resource_id,
1050 modified_date,
1051 last_viewed_by_me_date,
1052 properties),
1053 content_type,
1054 content_length,
1055 local_file_path,
1056 callback,
1057 progress_callback),
1058 resource_id_(resource_id),
1059 etag_(etag),
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_));
1072 return headers;
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(
1096 sender,
1097 download_action_callback,
1098 get_content_callback,
1099 progress_callback,
1100 url_generator.GenerateDownloadFileUrl(resource_id),
1101 output_file_path) {
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;
1136 switch (type_) {
1137 case PERMISSION_TYPE_ANYONE:
1138 root.SetString("type", "anyone");
1139 break;
1140 case PERMISSION_TYPE_DOMAIN:
1141 root.SetString("type", "domain");
1142 break;
1143 case PERMISSION_TYPE_GROUP:
1144 root.SetString("type", "group");
1145 break;
1146 case PERMISSION_TYPE_USER:
1147 root.SetString("type", "user");
1148 break;
1150 switch (role_) {
1151 case PERMISSION_ROLE_OWNER:
1152 root.SetString("role", "owner");
1153 break;
1154 case PERMISSION_ROLE_READER:
1155 root.SetString("role", "reader");
1156 break;
1157 case PERMISSION_ROLE_WRITER:
1158 root.SetString("role", "writer");
1159 break;
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);
1167 break;
1169 root.SetString("value", value_);
1170 base::JSONWriter::Write(root, upload_content);
1171 return true;
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()
1192 const {
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(),
1215 base::Bind(
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,
1227 int64 current,
1228 int64 total) {
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),
1245 sender_(sender),
1246 url_generator_(url_generator),
1247 committed_(false),
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());
1261 DCHECK(request);
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;
1276 } else {
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()) {
1287 Cancel();
1288 } else {
1289 committed_ = true;
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)
1312 return it;
1314 return child_requests_.end();
1317 void BatchUploadRequest::MayCompletePrepare() {
1318 if (!committed_ || prepare_callback_.is_null())
1319 return;
1320 for (const auto& child : child_requests_) {
1321 if (!child->prepared)
1322 return;
1325 // Build multipart body here.
1326 int64 total_size = 0;
1327 std::vector<ContentTypeAndData> parts;
1328 for (auto& child : child_requests_) {
1329 std::string type;
1330 std::string data;
1331 const bool result = child->request->GetContentData(&type, &data);
1332 // Upload request must have content data.
1333 DCHECK(result);
1335 const GURL url = child->request->GetURL();
1336 std::string method;
1337 switch (child->request->GetRequestType()) {
1338 case net::URLFetcher::POST:
1339 method = "POST";
1340 break;
1341 case net::URLFetcher::PUT:
1342 method = "PUT";
1343 break;
1344 default:
1345 NOTREACHED();
1346 break;
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,
1364 total_size / 1024);
1366 std::vector<uint64> part_data_offset;
1367 GenerateMultipartBody(MULTIPART_MIXED, boundary_, parts, &upload_content_,
1368 &part_data_offset);
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);
1380 return true;
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);
1399 return headers;
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);
1408 return;
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(),
1417 &parts) ||
1418 child_requests_.size() != parts.size()) {
1419 RunCallbackOnPrematureFailure(DRIVE_PARSE_ERROR);
1420 sender_->RequestFinished(this);
1421 return;
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,
1445 int64 current,
1446 int64 total) {
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,
1451 child->data_size);
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,
1455 child->data_size);
1458 last_progress_value_ = current;
1460 } // namespace drive
1461 } // namespace google_apis