Chromium Code Reviews| 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..742629ddd6d2f24ea550ac294aafb957606bd22d 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: www.googleapis.com\n" |
|
kinaba
2015/04/13 08:25:27
How about getting the Host domain info from UrlGen
hirono
2015/04/13 09:16:09
Done.
|
| + "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,121 @@ 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. |
| - sender_->RequestFinished(child.request); |
| + for (const auto& pair : child_requests_) { |
|
kinaba
2015/04/13 08:25:27
Maybe: id_request_pair? for readability
hirono
2015/04/13 09:16:09
Removed this line for turning child_requests_ to v
|
| + // Request will be deleted in |RequestFinished| method. |
| + sender_->RequestFinished(pair.second.request); |
| } |
| } |
| -void BatchUploadRequest::AddRequest(UrlFetchRequestBase* request) { |
| +void BatchUploadRequest::SetBoundaryForTesting(const std::string& boundary) { |
| + boundary_ = boundary; |
| +} |
| + |
| +void BatchUploadRequest::AddRequest(BatchableRequestBase* request) { |
|
kinaba
2015/04/13 08:25:27
I'm not familiar with batched request, but is it o
hirono
2015/04/13 09:16:09
The order can affect the result. I turned child_re
|
| + DCHECK(CalledOnValidThread()); |
| DCHECK(request); |
| - child_requests_.push_back(BatchUploadChildEntry(request)); |
| + DCHECK(!child_requests_.count(request)); |
| + DCHECK(!committed_); |
| + child_requests_[request] = BatchUploadChildEntry(request); |
| + request->Prepare( |
| + base::Bind(&BatchUploadRequest::OnChildRequestPrepared, |
| + weak_ptr_factory_.GetWeakPtr(), |
| + request)); |
| +} |
| + |
| +void BatchUploadRequest::OnChildRequestPrepared( |
| + RequestID request_id, DriveApiErrorCode result) { |
| + DCHECK(CalledOnValidThread()); |
| + DCHECK(child_requests_.count(request_id)); |
| + if (IsSuccessfulDriveApiErrorCode(result)) { |
| + child_requests_[request_id].prepared = true; |
| + } else { |
| + child_requests_[request_id].request->RunCallbackOnPrematureFailure(result); |
| + sender_->RequestFinished(child_requests_[request_id].request); |
| + child_requests_.erase(request_id); |
| + } |
| + MayCompletePrepare(); |
| } |
| void BatchUploadRequest::Commit() { |
| - NOTIMPLEMENTED(); |
| + DCHECK(CalledOnValidThread()); |
| + DCHECK(!committed_); |
| + if (child_requests_.empty()) { |
|
kinaba
2015/04/13 08:25:27
What about having this case as (D)CHECK? Does the
hirono
2015/04/13 09:16:09
Turned it to CHECK, and updated the comment of fun
|
| + sender_->RequestFinished(this); |
| + return; |
| + } |
| + committed_ = true; |
| + MayCompletePrepare(); |
| +} |
| + |
| +void BatchUploadRequest::Prepare(const PrepareCallback& callback) { |
| + DCHECK(CalledOnValidThread()); |
| + DCHECK(!callback.is_null()); |
| + prepare_callback_ = callback; |
| + MayCompletePrepare(); |
| +} |
| + |
| +void BatchUploadRequest::Cancel() { |
| + for (auto& pair : child_requests_) { |
| + // Request cancel should delete the request instance. |
| + pair.second.request->Cancel(); |
| + } |
| + child_requests_.clear(); |
| + UrlFetchRequestBase::Cancel(); |
| +} |
| + |
| +void BatchUploadRequest::MayCompletePrepare() { |
| + if (!committed_ || prepare_callback_.is_null()) |
| + return; |
| + for (const auto& pair : child_requests_) { |
| + if (!pair.second.prepared) |
| + return; |
| + } |
| + |
| + // Build multipart body here. |
| + std::vector<ContentTypeAndData> parts; |
| + for (const auto& pair : child_requests_) { |
| + std::string type; |
| + std::string data; |
| + const bool result = pair.second.request->GetContentData(&type, &data); |
| + // Upload request must have content data. |
| + DCHECK(result); |
| + |
| + const GURL url = pair.second.request->GetURL(); |
| + std::string method; |
| + switch (pair.second.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(), 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 +1116,40 @@ 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 { |
| + return { kBatchUploadHeader }; |
| } |
| void BatchUploadRequest::ProcessURLFetchResults(const net::URLFetcher* source) { |
| - NOTIMPLEMENTED(); |
| + if (!IsSuccessfulDriveApiErrorCode(GetErrorCode())) { |
| + RunCallbackOnPrematureFailure(GetErrorCode()); |
| + sender_->RequestFinished(this); |
| + return; |
| + } |
| + |
| + for (auto& entry : child_requests_) { |
| + // TODO(hirono): Split the mutlipart result and return the correct code and |
| + // body. |
| + entry.second.request->ProcessURLFetchResults(HTTP_SERVICE_UNAVAILABLE, ""); |
| + // Request deletes itself after processing. |
| + } |
| + |
| + child_requests_.clear(); |
| } |
| void BatchUploadRequest::RunCallbackOnPrematureFailure(DriveApiErrorCode code) { |
| - NOTIMPLEMENTED(); |
| + for (auto pair : child_requests_) { |
| + pair.second.request->RunCallbackOnPrematureFailure(code); |
| + sender_->RequestFinished(pair.second.request); |
| + } |
| + child_requests_.clear(); |
| } |
| } // namespace drive |