Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(86)

Unified Diff: google_apis/drive/drive_api_requests.cc

Issue 1081313002: Drive: Add response handling to BatchUploadRequst class. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Fixed. Created 5 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
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 8c16a03b1895843c435045a7a0f041977bc89a22..d9ec86faa2be9e4a472a441161a3fea64b26577e 100644
--- a/google_apis/drive/drive_api_requests.cc
+++ b/google_apis/drive/drive_api_requests.cc
@@ -16,6 +16,7 @@
#include "google_apis/drive/request_util.h"
#include "google_apis/drive/time_util.h"
#include "net/base/url_util.h"
+#include "net/http/http_response_headers.h"
namespace google_apis {
namespace drive {
@@ -36,6 +37,12 @@ const char kBatchUploadHeader[] = "X-Goog-Upload-Protocol: batch";
// Content type of HTTP request.
const char kHttpContentType[] = "application/http";
+// Break line in HTTP message.
+const char kHttpBr[] = "\r\n";
+
+// Mime type of multipart mixed.
+const char kMultipartMixedMimeTypePrefix[] = "multipart/mixed; boundary=";
+
// 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
@@ -126,8 +133,142 @@ std::string CreateMultipartUploadMetadataJson(
return json_string;
}
+// Checks if the string between |begin| and |end| equals to |expected|. The
+// string between |begin| and |end| can include kHttpBr. |expected| must not
+// include kHttpBr.
+bool IsLineEqual(std::string::const_iterator begin,
+ std::string::const_iterator end,
+ const std::string& expected) {
+ size_t size = end - begin;
+ const std::string br(kHttpBr);
+ if (size >= br.size() && std::equal(br.begin(), br.end(), end - br.size())) {
+ size -= br.size();
+ }
+
+ if (size != expected.size())
+ return false;
+
+ return std::equal(expected.begin(), expected.end(), begin);
+}
+
+// Obtains substring of string between |begin| and |end| that follows |prefix|.
+// Returns false if |prefix| does not match.
+bool GetAfterPrefix(std::string::const_iterator begin,
+ std::string::const_iterator end,
+ const std::string& prefix,
+ std::string* output) {
+ std::string::const_iterator output_begin = begin + prefix.size();
kinaba 2015/04/27 08:09:37 strictly speaking this addition is not legal if ou
hirono 2015/04/27 12:32:45 I turned to use StringPiece and just removed this
+ if (output_begin > end)
+ return false;
+ if (std::equal(prefix.begin(), prefix.end(), begin)) {
+ *output = std::string(output_begin, end);
+ return true;
+ } else {
+ return false;
+ }
+}
+
} // namespace
+MultipartHttpResponse::MultipartHttpResponse() : code(HTTP_SUCCESS) {
+}
+
+MultipartHttpResponse::~MultipartHttpResponse() {
+}
+
+bool ParseMultipartResponse(const std::string& boundary,
+ const std::string& response,
+ std::vector<MultipartHttpResponse>* parts) {
+ if (boundary.empty() || response.empty())
+ return false;
+ const size_t br_size = std::string(kHttpBr).size();
+ std::vector<std::string::const_iterator> lines;
+ std::string::const_iterator it = response.begin();
+ while (true) {
+ lines.push_back(it);
+ it = std::search(it, response.end(), kHttpBr, kHttpBr + br_size);
+ if (it == response.end()) {
+ lines.push_back(it);
+ break;
+ }
+ it += br_size;
+ }
kinaba 2015/04/27 08:09:37 nit: this loop can be factored out as a function,
hirono 2015/04/27 12:32:45 Done.
+
+ const std::string header = "--" + boundary;
+ const std::string terminator = "--" + boundary + "--";
+
+ enum {
+ STATE_START,
+ STATE_PART_HEADER,
+ STATE_PART_HTTP_STATUS_LINE,
+ STATE_PART_HTTP_HEADER,
+ STATE_PART_HTTP_BODY
+ } state = STATE_START;
+
+ const std::string kHttpStatusPrefix = "HTTP/1.1 ";
+ std::vector<MultipartHttpResponse> responses;
+ DriveApiErrorCode code;
+ std::string::const_iterator body_begin;
+ for (size_t i = 0; i < lines.size() - 1; ++i) {
kinaba 2015/04/27 08:09:37 You can utilize base/strings/string_piece.h. Let
hirono 2015/04/27 12:32:45 Done.
+ if (state == STATE_PART_HEADER && IsLineEqual(lines[i], lines[i + 1], "")) {
+ state = STATE_PART_HTTP_STATUS_LINE;
+ continue;
+ }
+
+ if (state == STATE_PART_HTTP_STATUS_LINE) {
+ std::string code_string;
+ if (GetAfterPrefix(lines[i], lines[i + 1], kHttpStatusPrefix,
+ &code_string)) {
+ code = static_cast<DriveApiErrorCode>(atoi(code_string.c_str()));
+ if (code <= 0)
+ code = DRIVE_PARSE_ERROR;
+ } else {
+ code = DRIVE_PARSE_ERROR;
+ }
+ state = STATE_PART_HTTP_HEADER;
+ continue;
+ }
+
+ if (state == STATE_PART_HTTP_HEADER &&
+ IsLineEqual(lines[i], lines[i + 1], "")) {
+ state = STATE_PART_HTTP_BODY;
+ body_begin = lines[i + 1];
+ continue;
+ }
+
+ const bool is_new_part = IsLineEqual(lines[i], lines[i + 1], header);
kinaba 2015/04/27 08:09:37 I'm not sure how much we need to be pedantic, but
hirono 2015/04/27 12:32:45 Let me handle this. Done
+ const bool was_last_part = IsLineEqual(lines[i], lines[i + 1], terminator);
+ if (is_new_part || was_last_part) {
+ switch (state) {
+ case STATE_START:
+ break;
+ case STATE_PART_HEADER:
+ case STATE_PART_HTTP_STATUS_LINE:
+ responses.push_back(MultipartHttpResponse());
+ responses.back().code = DRIVE_PARSE_ERROR;
+ break;
+ case STATE_PART_HTTP_HEADER:
+ responses.push_back(MultipartHttpResponse());
+ responses.back().code = code;
+ break;
+ case STATE_PART_HTTP_BODY:
+ responses.push_back(MultipartHttpResponse());
+ responses.back().code = code;
+ responses.back().body = std::string(body_begin, lines[i]);
kinaba 2015/04/27 08:09:37 then body will include the last CRLF (i.e., two ch
hirono 2015/04/27 12:32:45 Yes, it should not include CRLF. Thank you for cat
+ break;
+ }
+ }
+
+ if (is_new_part)
+ state = STATE_PART_HEADER;
+ if (was_last_part)
+ break;
kinaba 2015/04/27 08:09:37 nit: I slightly prefer these 4 lines to be put ins
hirono 2015/04/27 12:32:45 Done.
+ }
+
+ parts->swap(responses);
+ return true;
+}
+
Property::Property() : visibility_(VISIBILITY_PRIVATE) {
}
@@ -1014,14 +1155,12 @@ void BatchUploadRequest::AddRequest(BatchableRequestBase* 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));
+ request->Prepare(base::Bind(&BatchUploadRequest::OnChildRequestPrepared,
+ weak_ptr_factory_.GetWeakPtr(), request));
}
-void BatchUploadRequest::OnChildRequestPrepared(
- RequestID request_id, DriveApiErrorCode result) {
+void BatchUploadRequest::OnChildRequestPrepared(RequestID request_id,
+ DriveApiErrorCode result) {
DCHECK(CalledOnValidThread());
auto const child = GetChildEntry(request_id);
DCHECK(child != child_requests_.end());
@@ -1061,8 +1200,8 @@ void BatchUploadRequest::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) {
+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;
@@ -1104,11 +1243,8 @@ void BatchUploadRequest::MayCompletePrepare() {
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(),
+ kBatchUploadRequestFormat, method.c_str(), url.path().c_str(),
+ url_generator_.GetBatchUploadUrl().host().c_str(), type.c_str(),
data.c_str());
}
@@ -1149,14 +1285,32 @@ void BatchUploadRequest::ProcessURLFetchResults(const net::URLFetcher* source) {
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.
+ std::string content_type;
+ source->GetResponseHeaders()->EnumerateHeader(
+ /* need only first header */ NULL, "Content-Type", &content_type);
+ std::string boundary;
+ if (!GetAfterPrefix(content_type.begin(), content_type.end(),
+ kMultipartMixedMimeTypePrefix, &boundary)) {
kinaba 2015/04/27 08:09:37 According to the RFC boundary field can be quoted
hirono 2015/04/27 12:32:45 Done.
+ RunCallbackOnPrematureFailure(DRIVE_PARSE_ERROR);
+ sender_->RequestFinished(this);
+ return;
+ }
+
+ std::vector<MultipartHttpResponse> parts;
+ if (!ParseMultipartResponse(boundary, response_writer()->data(), &parts) ||
+ child_requests_.size() != parts.size()) {
+ RunCallbackOnPrematureFailure(DRIVE_PARSE_ERROR);
+ sender_->RequestFinished(this);
+ return;
+ }
+
+ for (size_t i = 0; i < parts.size(); ++i) {
+ child_requests_[i].request->ProcessURLFetchResults(parts[i].code,
+ parts[i].body);
}
child_requests_.clear();
+ sender_->RequestFinished(this);
}
void BatchUploadRequest::RunCallbackOnPrematureFailure(DriveApiErrorCode code) {

Powered by Google App Engine
This is Rietveld 408576698