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

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: Fix compile error on windows. 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
« no previous file with comments | « google_apis/drive/drive_api_requests.h ('k') | google_apis/drive/drive_api_requests_unittest.cc » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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..d770f4bb5304ec4b09da81b124bc979b48d87244 100644
--- a/google_apis/drive/drive_api_requests.cc
+++ b/google_apis/drive/drive_api_requests.cc
@@ -9,6 +9,8 @@
#include "base/json/json_writer.h"
#include "base/location.h"
#include "base/sequenced_task_runner.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_piece.h"
#include "base/strings/stringprintf.h"
#include "base/task_runner_util.h"
#include "base/values.h"
@@ -16,6 +18,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 +39,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 +135,174 @@ std::string CreateMultipartUploadMetadataJson(
return json_string;
}
+// Splits |string| into lines by |kHttpBr|.
+// Each line does not include |kHttpBr|.
+void SplitIntoLines(const std::string& string,
+ std::vector<base::StringPiece>* output) {
+ const size_t br_size = std::string(kHttpBr).size();
+ std::string::const_iterator it = string.begin();
+ std::vector<base::StringPiece> lines;
+ while (true) {
+ const std::string::const_iterator next_pos =
+ std::search(it, string.end(), kHttpBr, kHttpBr + br_size);
+ lines.push_back(base::StringPiece(it, next_pos));
+ if (next_pos == string.end())
+ break;
+ it = next_pos + br_size;
+ }
+ output->swap(lines);
+}
+
+// Remove transport padding (spaces and tabs at the end of line) from |piece|.
+base::StringPiece TrimTransportPadding(const base::StringPiece& piece) {
+ size_t trim_size = 0;
+ while (trim_size < piece.size() &&
+ (piece[piece.size() - 1 - trim_size] == ' ' ||
+ piece[piece.size() - 1 - trim_size] == '\t')) {
+ ++trim_size;
+ }
+ return piece.substr(0, piece.size() - trim_size);
+}
+
} // namespace
+MultipartHttpResponse::MultipartHttpResponse() : code(HTTP_SUCCESS) {
+}
+
+MultipartHttpResponse::~MultipartHttpResponse() {
+}
+
+// The |response| must be multipart/mixed format that contains child HTTP
+// response of drive batch request.
+// https://www.ietf.org/rfc/rfc2046.txt
+//
+// It looks like:
+// --Boundary
+// Content-type: application/http
+//
+// HTTP/1.1 200 OK
+// Header of child response
+//
+// Body of child response
+// --Boundary
+// Content-type: application/http
+//
+// HTTP/1.1 404 Not Found
+// Header of child response
+//
+// Body of child response
+// --Boundary--
+bool ParseMultipartResponse(const std::string& content_type,
+ const std::string& response,
+ std::vector<MultipartHttpResponse>* parts) {
+ if (response.empty())
+ return false;
+
+ base::StringPiece content_type_piece(content_type);
+ if (!content_type_piece.starts_with(kMultipartMixedMimeTypePrefix)) {
+ return false;
+ }
+ content_type_piece.remove_prefix(
+ base::StringPiece(kMultipartMixedMimeTypePrefix).size());
+
+ if (content_type_piece.empty())
+ return false;
+ if (content_type_piece[0] == '"') {
+ if (content_type_piece.size() <= 2 ||
+ content_type_piece[content_type_piece.size() - 1] != '"') {
+ return false;
+ }
+ content_type_piece =
+ content_type_piece.substr(1, content_type_piece.size() - 2);
+ }
+
+ std::string boundary;
+ content_type_piece.CopyToString(&boundary);
+ const std::string header = "--" + boundary;
+ const std::string terminator = "--" + boundary + "--";
+
+ std::vector<base::StringPiece> lines;
+ SplitIntoLines(response, &lines);
+
+ 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 = DRIVE_PARSE_ERROR;
+ std::string body;
+ for (const auto& line : lines) {
+ if (state == STATE_PART_HEADER && line.empty()) {
+ state = STATE_PART_HTTP_STATUS_LINE;
+ continue;
+ }
+
+ if (state == STATE_PART_HTTP_STATUS_LINE) {
+ if (line.starts_with(kHttpStatusPrefix)) {
+ int int_code;
+ base::StringToInt(
+ line.substr(base::StringPiece(kHttpStatusPrefix).size()),
+ &int_code);
+ if (int_code > 0)
+ code = static_cast<DriveApiErrorCode>(int_code);
+ else
+ code = DRIVE_PARSE_ERROR;
+ } else {
+ code = DRIVE_PARSE_ERROR;
+ }
+ state = STATE_PART_HTTP_HEADER;
+ continue;
+ }
+
+ if (state == STATE_PART_HTTP_HEADER && line.empty()) {
+ state = STATE_PART_HTTP_BODY;
+ body.clear();
+ continue;
+ }
+ const base::StringPiece chopped_line = TrimTransportPadding(line);
+ const bool is_new_part = chopped_line == header;
+ const bool was_last_part = chopped_line == 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:
+ // Drop the last kHttpBr.
+ if (!body.empty())
+ body.resize(body.size() - 2);
+ responses.push_back(MultipartHttpResponse());
+ responses.back().code = code;
+ responses.back().body.swap(body);
+ break;
+ }
+ if (is_new_part)
+ state = STATE_PART_HEADER;
+ if (was_last_part)
+ break;
+ } else if (state == STATE_PART_HTTP_BODY) {
+ line.AppendToString(&body);
+ body.append(kHttpBr);
+ }
+ }
+
+ parts->swap(responses);
+ return true;
+}
+
Property::Property() : visibility_(VISIBILITY_PRIVATE) {
}
@@ -1014,14 +1189,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 +1234,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 +1277,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 +1319,26 @@ 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::vector<MultipartHttpResponse> parts;
+ if (!ParseMultipartResponse(content_type, 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) {
« no previous file with comments | « google_apis/drive/drive_api_requests.h ('k') | google_apis/drive/drive_api_requests_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698