Index: google_apis/drive/drive_api_requests.cc |
diff --git a/google_apis/drive/drive_api_requests.cc b/google_apis/drive/drive_api_requests.cc |
index 089f2b999af2cab357f0745f94d9a652d273f54b..8c16a03b1895843c435045a7a0f041977bc89a22 100644 |
--- a/google_apis/drive/drive_api_requests.cc |
+++ b/google_apis/drive/drive_api_requests.cc |
@@ -9,6 +9,7 @@ |
#include "base/json/json_writer.h" |
#include "base/location.h" |
#include "base/sequenced_task_runner.h" |
+#include "base/strings/stringprintf.h" |
#include "base/task_runner_util.h" |
#include "base/values.h" |
#include "google_apis/drive/request_sender.h" |
@@ -20,6 +21,21 @@ namespace google_apis { |
namespace drive { |
namespace { |
+// Format of one request in batch uploading request. |
+const char kBatchUploadRequestFormat[] = |
+ "%s %s HTTP/1.1\n" |
+ "Host: %s\n" |
+ "X-Goog-Upload-Protocol: multipart\n" |
+ "Content-Type: %s\n" |
+ "\n" |
+ "%s"; |
+ |
+// Request header for specifying batch upload. |
+const char kBatchUploadHeader[] = "X-Goog-Upload-Protocol: batch"; |
+ |
+// Content type of HTTP request. |
+const char kHttpContentType[] = "application/http"; |
+ |
// Parses the JSON value to FileResource instance and runs |callback| on the |
// UI thread once parsing is done. |
// This is customized version of ParseJsonAndRun defined above to adapt the |
@@ -977,23 +993,134 @@ BatchUploadRequest::BatchUploadRequest( |
: UrlFetchRequestBase(sender), |
sender_(sender), |
url_generator_(url_generator), |
+ committed_(false), |
weak_ptr_factory_(this) { |
} |
BatchUploadRequest::~BatchUploadRequest() { |
for (const auto& child : child_requests_) { |
- // Request will be deleted in RequestFinished method. |
+ // Request will be deleted in |RequestFinished| method. |
sender_->RequestFinished(child.request); |
} |
} |
-void BatchUploadRequest::AddRequest(UrlFetchRequestBase* request) { |
+void BatchUploadRequest::SetBoundaryForTesting(const std::string& boundary) { |
+ boundary_ = boundary; |
+} |
+ |
+void BatchUploadRequest::AddRequest(BatchableRequestBase* request) { |
+ DCHECK(CalledOnValidThread()); |
DCHECK(request); |
+ DCHECK(GetChildEntry(request) == child_requests_.end()); |
+ DCHECK(!committed_); |
child_requests_.push_back(BatchUploadChildEntry(request)); |
+ request->Prepare( |
+ base::Bind(&BatchUploadRequest::OnChildRequestPrepared, |
+ weak_ptr_factory_.GetWeakPtr(), |
+ request)); |
+} |
+ |
+void BatchUploadRequest::OnChildRequestPrepared( |
+ RequestID request_id, DriveApiErrorCode result) { |
+ DCHECK(CalledOnValidThread()); |
+ auto const child = GetChildEntry(request_id); |
+ DCHECK(child != child_requests_.end()); |
+ if (IsSuccessfulDriveApiErrorCode(result)) { |
+ child->prepared = true; |
+ } else { |
+ child->request->RunCallbackOnPrematureFailure(result); |
+ sender_->RequestFinished(child->request); |
+ child_requests_.erase(child); |
+ } |
+ MayCompletePrepare(); |
} |
void BatchUploadRequest::Commit() { |
- NOTIMPLEMENTED(); |
+ DCHECK(CalledOnValidThread()); |
+ DCHECK(!committed_); |
+ CHECK(!child_requests_.empty()); |
+ committed_ = true; |
+ MayCompletePrepare(); |
+} |
+ |
+void BatchUploadRequest::Prepare(const PrepareCallback& callback) { |
+ DCHECK(CalledOnValidThread()); |
+ DCHECK(!callback.is_null()); |
+ prepare_callback_ = callback; |
+ MayCompletePrepare(); |
+} |
+ |
+void BatchUploadRequest::Cancel() { |
+ for (auto& child : child_requests_) { |
+ // Request cancel should delete the request instance. |
+ child.request->Cancel(); |
+ } |
+ child_requests_.clear(); |
+ UrlFetchRequestBase::Cancel(); |
+} |
+ |
+// Obtains corresponding child entry of |request_id|. Returns NULL if the |
+// entry is not found. |
+std::vector<BatchUploadChildEntry>::iterator |
+BatchUploadRequest::GetChildEntry(RequestID request_id) { |
+ for (auto it = child_requests_.begin(); it != child_requests_.end(); ++it) { |
+ if (it->request == request_id) |
+ return it; |
+ } |
+ return child_requests_.end(); |
+} |
+ |
+void BatchUploadRequest::MayCompletePrepare() { |
+ if (!committed_ || prepare_callback_.is_null()) |
+ return; |
+ for (const auto& child : child_requests_) { |
+ if (!child.prepared) |
+ return; |
+ } |
+ |
+ // Build multipart body here. |
+ std::vector<ContentTypeAndData> parts; |
+ for (const auto& child : child_requests_) { |
+ std::string type; |
+ std::string data; |
+ const bool result = child.request->GetContentData(&type, &data); |
+ // Upload request must have content data. |
+ DCHECK(result); |
+ |
+ const GURL url = child.request->GetURL(); |
+ std::string method; |
+ switch (child.request->GetRequestType()) { |
+ case net::URLFetcher::POST: |
+ method = "POST"; |
+ break; |
+ case net::URLFetcher::PUT: |
+ method = "PUT"; |
+ break; |
+ default: |
+ NOTREACHED(); |
+ break; |
+ } |
+ |
+ parts.push_back(ContentTypeAndData()); |
+ parts.back().type = kHttpContentType; |
+ parts.back().data = base::StringPrintf( |
+ kBatchUploadRequestFormat, |
+ method.c_str(), |
+ url.path().c_str(), |
+ url_generator_.GetBatchUploadUrl().host().c_str(), |
+ type.c_str(), |
+ data.c_str()); |
+ } |
+ |
+ GenerateMultipartBody(MULTIPART_MIXED, boundary_, parts, &upload_content_); |
+ prepare_callback_.Run(HTTP_SUCCESS); |
+} |
+ |
+bool BatchUploadRequest::GetContentData(std::string* upload_content_type, |
+ std::string* upload_content_data) { |
+ upload_content_type->assign(upload_content_.type); |
+ upload_content_data->assign(upload_content_.data); |
+ return true; |
} |
base::WeakPtr<BatchUploadRequest> |
@@ -1002,16 +1129,42 @@ BatchUploadRequest::GetWeakPtrAsBatchUploadRequest() { |
} |
GURL BatchUploadRequest::GetURL() const { |
- NOTIMPLEMENTED(); |
- return GURL(); |
+ return url_generator_.GetBatchUploadUrl(); |
+} |
+ |
+net::URLFetcher::RequestType BatchUploadRequest::GetRequestType() const { |
+ return net::URLFetcher::PUT; |
+} |
+ |
+std::vector<std::string> BatchUploadRequest::GetExtraRequestHeaders() const { |
+ std::vector<std::string> headers; |
+ headers.push_back(kBatchUploadHeader); |
+ return headers; |
} |
void BatchUploadRequest::ProcessURLFetchResults(const net::URLFetcher* source) { |
- NOTIMPLEMENTED(); |
+ if (!IsSuccessfulDriveApiErrorCode(GetErrorCode())) { |
+ RunCallbackOnPrematureFailure(GetErrorCode()); |
+ sender_->RequestFinished(this); |
+ return; |
+ } |
+ |
+ for (auto& child : child_requests_) { |
+ // TODO(hirono): Split the mutlipart result and return the correct code and |
+ // body. |
+ child.request->ProcessURLFetchResults(HTTP_SERVICE_UNAVAILABLE, ""); |
+ // Request deletes itself after processing. |
+ } |
+ |
+ child_requests_.clear(); |
} |
void BatchUploadRequest::RunCallbackOnPrematureFailure(DriveApiErrorCode code) { |
- NOTIMPLEMENTED(); |
+ for (auto child : child_requests_) { |
+ child.request->RunCallbackOnPrematureFailure(code); |
+ sender_->RequestFinished(child.request); |
+ } |
+ child_requests_.clear(); |
} |
} // namespace drive |