summaryrefslogtreecommitdiffstats
path: root/google_apis/drive
diff options
context:
space:
mode:
authorhirono <hirono@chromium.org>2015-04-29 20:08:27 -0700
committerCommit bot <commit-bot@chromium.org>2015-04-30 03:10:03 +0000
commit35d010b6e1351dfad5a7c8872946d2fef6ad8545 (patch)
treedf75ee5fd63b8cd6bb94b4b2d1d4b2c83e0c70a3 /google_apis/drive
parentd19bbadb7bd8b35bb0d625ae467807f197545698 (diff)
downloadchromium_src-35d010b6e1351dfad5a7c8872946d2fef6ad8545.zip
chromium_src-35d010b6e1351dfad5a7c8872946d2fef6ad8545.tar.gz
chromium_src-35d010b6e1351dfad5a7c8872946d2fef6ad8545.tar.bz2
Drive: Add response handling to BatchUploadRequst class.
BUG=451917 TEST=DriveApiRequestTest.BatchUploadRequest Review URL: https://codereview.chromium.org/1081313002 Cr-Commit-Position: refs/heads/master@{#327646}
Diffstat (limited to 'google_apis/drive')
-rw-r--r--google_apis/drive/drive_api_requests.cc218
-rw-r--r--google_apis/drive/drive_api_requests.h24
-rw-r--r--google_apis/drive/drive_api_requests_unittest.cc237
-rw-r--r--google_apis/drive/drive_api_url_generator_unittest.cc5
4 files changed, 403 insertions, 81 deletions
diff --git a/google_apis/drive/drive_api_requests.cc b/google_apis/drive/drive_api_requests.cc
index 8c16a03..d770f4b 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) {
diff --git a/google_apis/drive/drive_api_requests.h b/google_apis/drive/drive_api_requests.h
index 3aaee36..6017799 100644
--- a/google_apis/drive/drive_api_requests.h
+++ b/google_apis/drive/drive_api_requests.h
@@ -66,6 +66,21 @@ class Property {
// List of properties for a single file or a directory.
typedef std::vector<Property> Properties;
+// Child response embedded in multipart parent response.
+struct MultipartHttpResponse {
+ MultipartHttpResponse();
+ ~MultipartHttpResponse();
+ DriveApiErrorCode code;
+ std::string body;
+};
+
+// Splits multipart |response| into |parts|. Each part must be HTTP sub-response
+// of drive batch request. |content_type| is a value of Content-Type response
+// header. Returns true on succcess.
+bool ParseMultipartResponse(const std::string& content_type,
+ const std::string& response,
+ std::vector<MultipartHttpResponse>* parts);
+
//============================ DriveApiPartialFieldRequest ====================
// This is base class of the Drive API related requests. All Drive API requests
@@ -1135,9 +1150,8 @@ class BatchUploadRequest : public UrlFetchRequestBase {
GURL GetURL() const override;
net::URLFetcher::RequestType GetRequestType() const override;
std::vector<std::string> GetExtraRequestHeaders() const override;
- bool GetContentData(
- std::string* upload_content_type,
- std::string* upload_content) override;
+ bool GetContentData(std::string* upload_content_type,
+ std::string* upload_content) override;
void ProcessURLFetchResults(const net::URLFetcher* source) override;
void RunCallbackOnPrematureFailure(DriveApiErrorCode code) override;
@@ -1145,8 +1159,8 @@ class BatchUploadRequest : public UrlFetchRequestBase {
typedef void* RequestID;
// Obtains corresponding child entry of |request_id|. Returns NULL if the
// entry is not found.
- std::vector<BatchUploadChildEntry>::iterator
- GetChildEntry(RequestID request_id);
+ std::vector<BatchUploadChildEntry>::iterator GetChildEntry(
+ RequestID request_id);
// Called after child requests' |Prepare| method.
void OnChildRequestPrepared(RequestID request_id, DriveApiErrorCode result);
diff --git a/google_apis/drive/drive_api_requests_unittest.cc b/google_apis/drive/drive_api_requests_unittest.cc
index 52bf104..d83292e 100644
--- a/google_apis/drive/drive_api_requests_unittest.cc
+++ b/google_apis/drive/drive_api_requests_unittest.cc
@@ -410,6 +410,28 @@ class DriveApiRequestsTest : public testing::Test {
scoped_ptr<net::test_server::BasicHttpResponse> response(
new net::test_server::BasicHttpResponse);
response->set_code(net::HTTP_OK);
+ response->set_content_type("multipart/mixed; boundary=BOUNDARY");
+ response->set_content(
+ "--BOUNDARY\r\n"
+ "Content-Type: application/http\r\n"
+ "\r\n"
+ "HTTP/1.1 200 OK\r\n"
+ "Content-Type: application/json; charset=UTF-8\r\n"
+ "\r\n"
+ "{\r\n"
+ " \"kind\": \"drive#file\",\r\n"
+ " \"id\": \"file_id_1\"\r\n"
+ "}\r\n"
+ "\r\n"
+ "--BOUNDARY\r\n"
+ "Content-Type: application/http\r\n"
+ "\r\n"
+ "HTTP/1.1 503 Service Unavailable\r\n"
+ "Content-Type: application/json; charset=UTF-8\r\n"
+ "\r\n"
+ "{}\r\n"
+ "\r\n"
+ "--BOUNDARY--\r\n");
return response.Pass();
}
@@ -1916,33 +1938,25 @@ TEST_F(DriveApiRequestsTest, BatchUploadRequest) {
ASSERT_TRUE(test_util::WriteStringToFile(kTestFilePath, kTestContent));
// Create batch request.
- drive::BatchUploadRequest* const request = new drive::BatchUploadRequest(
- request_sender_.get(), *url_generator_);
+ drive::BatchUploadRequest* const request =
+ new drive::BatchUploadRequest(request_sender_.get(), *url_generator_);
request->SetBoundaryForTesting("OUTERBOUNDARY");
request_sender_->StartRequestWithRetry(request);
// Create child request.
- DriveApiErrorCode error = DRIVE_OTHER_ERROR;
- scoped_ptr<FileResource> file_resource;
+ DriveApiErrorCode errors[] = {DRIVE_OTHER_ERROR, DRIVE_OTHER_ERROR};
+ scoped_ptr<FileResource> file_resources[2];
base::RunLoop run_loop[2];
for (int i = 0; i < 2; ++i) {
const FileResourceCallback callback = test_util::CreateQuitCallback(
&run_loop[i],
- test_util::CreateCopyResultCallback(&error, &file_resource));
+ test_util::CreateCopyResultCallback(&errors[i], &file_resources[i]));
drive::MultipartUploadNewFileRequest* const child_request =
new drive::MultipartUploadNewFileRequest(
- request_sender_.get(),
- base::StringPrintf("new file title %d", i),
- "parent_resource_id",
- kTestContentType,
- kTestContent.size(),
- base::Time(),
- base::Time(),
- kTestFilePath,
- drive::Properties(),
- *url_generator_,
- callback,
- ProgressCallback());
+ request_sender_.get(), base::StringPrintf("new file title %d", i),
+ "parent_resource_id", kTestContentType, kTestContent.size(),
+ base::Time(), base::Time(), kTestFilePath, drive::Properties(),
+ *url_generator_, callback, ProgressCallback());
child_request->SetBoundaryForTesting("INNERBOUNDARY");
request->AddRequest(child_request);
}
@@ -1954,44 +1968,50 @@ TEST_F(DriveApiRequestsTest, BatchUploadRequest) {
EXPECT_EQ("batch", http_request_.headers["X-Goog-Upload-Protocol"]);
EXPECT_EQ("multipart/mixed; boundary=OUTERBOUNDARY",
http_request_.headers["Content-Type"]);
- EXPECT_EQ("--OUTERBOUNDARY\n"
- "Content-Type: application/http\n"
- "\n"
- "POST /upload/drive/v2/files HTTP/1.1\n"
- "Host: 127.0.0.1\n"
- "X-Goog-Upload-Protocol: multipart\n"
- "Content-Type: multipart/related; boundary=INNERBOUNDARY\n"
- "\n"
- "--INNERBOUNDARY\n"
- "Content-Type: application/json\n"
- "\n"
- "{\"parents\":[{\"id\":\"parent_resource_id\","
- "\"kind\":\"drive#fileLink\"}],\"title\":\"new file title 0\"}\n"
- "--INNERBOUNDARY\n"
- "Content-Type: text/plain\n"
- "\n"
- "aaaaaaaaaa\n"
- "--INNERBOUNDARY--\n"
- "--OUTERBOUNDARY\n"
- "Content-Type: application/http\n"
- "\n"
- "POST /upload/drive/v2/files HTTP/1.1\n"
- "Host: 127.0.0.1\n"
- "X-Goog-Upload-Protocol: multipart\n"
- "Content-Type: multipart/related; boundary=INNERBOUNDARY\n"
- "\n"
- "--INNERBOUNDARY\n"
- "Content-Type: application/json\n"
- "\n"
- "{\"parents\":[{\"id\":\"parent_resource_id\","
- "\"kind\":\"drive#fileLink\"}],\"title\":\"new file title 1\"}\n"
- "--INNERBOUNDARY\n"
- "Content-Type: text/plain\n"
- "\n"
- "aaaaaaaaaa\n"
- "--INNERBOUNDARY--\n"
- "--OUTERBOUNDARY--",
- http_request_.content);
+ EXPECT_EQ(
+ "--OUTERBOUNDARY\n"
+ "Content-Type: application/http\n"
+ "\n"
+ "POST /upload/drive/v2/files HTTP/1.1\n"
+ "Host: 127.0.0.1\n"
+ "X-Goog-Upload-Protocol: multipart\n"
+ "Content-Type: multipart/related; boundary=INNERBOUNDARY\n"
+ "\n"
+ "--INNERBOUNDARY\n"
+ "Content-Type: application/json\n"
+ "\n"
+ "{\"parents\":[{\"id\":\"parent_resource_id\","
+ "\"kind\":\"drive#fileLink\"}],\"title\":\"new file title 0\"}\n"
+ "--INNERBOUNDARY\n"
+ "Content-Type: text/plain\n"
+ "\n"
+ "aaaaaaaaaa\n"
+ "--INNERBOUNDARY--\n"
+ "--OUTERBOUNDARY\n"
+ "Content-Type: application/http\n"
+ "\n"
+ "POST /upload/drive/v2/files HTTP/1.1\n"
+ "Host: 127.0.0.1\n"
+ "X-Goog-Upload-Protocol: multipart\n"
+ "Content-Type: multipart/related; boundary=INNERBOUNDARY\n"
+ "\n"
+ "--INNERBOUNDARY\n"
+ "Content-Type: application/json\n"
+ "\n"
+ "{\"parents\":[{\"id\":\"parent_resource_id\","
+ "\"kind\":\"drive#fileLink\"}],\"title\":\"new file title 1\"}\n"
+ "--INNERBOUNDARY\n"
+ "Content-Type: text/plain\n"
+ "\n"
+ "aaaaaaaaaa\n"
+ "--INNERBOUNDARY--\n"
+ "--OUTERBOUNDARY--",
+ http_request_.content);
+ EXPECT_EQ(HTTP_SUCCESS, errors[0]);
+ ASSERT_TRUE(file_resources[0]);
+ EXPECT_EQ("file_id_1", file_resources[0]->file_id());
+ ASSERT_FALSE(file_resources[1]);
+ EXPECT_EQ(DRIVE_PARSE_ERROR, errors[1]);
}
TEST_F(DriveApiRequestsTest, EmptyBatchUploadRequest) {
@@ -2000,4 +2020,111 @@ TEST_F(DriveApiRequestsTest, EmptyBatchUploadRequest) {
EXPECT_DEATH(request->Commit(), "");
}
+TEST(ParseMultipartResponseTest, Empty) {
+ std::vector<drive::MultipartHttpResponse> parts;
+ EXPECT_FALSE(drive::ParseMultipartResponse(
+ "multipart/mixed; boundary=BOUNDARY", "", &parts));
+ EXPECT_FALSE(drive::ParseMultipartResponse("multipart/mixed; boundary=",
+ "CONTENT", &parts));
+}
+
+TEST(ParseMultipartResponseTest, Basic) {
+ std::vector<drive::MultipartHttpResponse> parts;
+ ASSERT_TRUE(
+ drive::ParseMultipartResponse("multipart/mixed; boundary=BOUNDARY",
+ "--BOUNDARY\r\n"
+ "Content-Type: application/http\r\n"
+ "\r\n"
+ "HTTP/1.1 200 OK\r\n"
+ "Header: value\r\n"
+ "\r\n"
+ "First line\r\n"
+ "Second line\r\n"
+ "--BOUNDARY\r\n"
+ "Content-Type: application/http\r\n"
+ "\r\n"
+ "HTTP/1.1 404 Not Found\r\n"
+ "Header: value\r\n"
+ "--BOUNDARY--",
+ &parts));
+ ASSERT_EQ(2u, parts.size());
+ EXPECT_EQ(HTTP_SUCCESS, parts[0].code);
+ EXPECT_EQ("First line\r\nSecond line", parts[0].body);
+ EXPECT_EQ(HTTP_NOT_FOUND, parts[1].code);
+ EXPECT_EQ("", parts[1].body);
+}
+
+TEST(ParseMultipartResponseTest, InvalidStatusLine) {
+ std::vector<drive::MultipartHttpResponse> parts;
+ ASSERT_TRUE(
+ drive::ParseMultipartResponse("multipart/mixed; boundary=BOUNDARY",
+ "--BOUNDARY\r\n"
+ "Content-Type: application/http\r\n"
+ "\r\n"
+ "InvalidStatusLine 200 \r\n"
+ "Header: value\r\n"
+ "\r\n"
+ "{}\r\n"
+ "--BOUNDARY--",
+ &parts));
+ ASSERT_EQ(1u, parts.size());
+ EXPECT_EQ(DRIVE_PARSE_ERROR, parts[0].code);
+ EXPECT_EQ("{}", parts[0].body);
+}
+
+TEST(ParseMultipartResponseTest, BoundaryInTheBodyAndPreamble) {
+ std::vector<drive::MultipartHttpResponse> parts;
+ ASSERT_TRUE(
+ drive::ParseMultipartResponse("multipart/mixed; boundary=BOUNDARY",
+ "BOUNDARY\r\n"
+ "PREUMBLE\r\n"
+ "--BOUNDARY\r\n"
+ "Content-Type: application/http\r\n"
+ "\r\n"
+ "HTTP/1.1 200 OK\r\n"
+ "Header: value\r\n"
+ "\r\n"
+ "{--BOUNDARY}\r\n"
+ "--BOUNDARY--",
+ &parts));
+ ASSERT_EQ(1u, parts.size());
+ EXPECT_EQ(HTTP_SUCCESS, parts[0].code);
+ EXPECT_EQ("{--BOUNDARY}", parts[0].body);
+}
+
+TEST(ParseMultipartResponseTest, QuatedBoundary) {
+ std::vector<drive::MultipartHttpResponse> parts;
+ ASSERT_TRUE(
+ drive::ParseMultipartResponse("multipart/mixed; boundary=\"BOUNDARY\"",
+ "--BOUNDARY\r\n"
+ "Content-Type: application/http\r\n"
+ "\r\n"
+ "HTTP/1.1 200 OK\r\n"
+ "Header: value\r\n"
+ "\r\n"
+ "BODY\r\n"
+ "--BOUNDARY--",
+ &parts));
+ ASSERT_EQ(1u, parts.size());
+ EXPECT_EQ(HTTP_SUCCESS, parts[0].code);
+ EXPECT_EQ("BODY", parts[0].body);
+}
+
+TEST(ParseMultipartResponseTest, BoundaryWithTransportPadding) {
+ std::vector<drive::MultipartHttpResponse> parts;
+ ASSERT_TRUE(
+ drive::ParseMultipartResponse("multipart/mixed; boundary=BOUNDARY",
+ "--BOUNDARY \t\r\n"
+ "Content-Type: application/http\r\n"
+ "\r\n"
+ "HTTP/1.1 200 OK\r\n"
+ "Header: value\r\n"
+ "\r\n"
+ "BODY\r\n"
+ "--BOUNDARY-- \t",
+ &parts));
+ ASSERT_EQ(1u, parts.size());
+ EXPECT_EQ(HTTP_SUCCESS, parts[0].code);
+ EXPECT_EQ("BODY", parts[0].body);
+}
} // namespace google_apis
diff --git a/google_apis/drive/drive_api_url_generator_unittest.cc b/google_apis/drive/drive_api_url_generator_unittest.cc
index 92e128e..18290b1 100644
--- a/google_apis/drive/drive_api_url_generator_unittest.cc
+++ b/google_apis/drive/drive_api_url_generator_unittest.cc
@@ -364,9 +364,8 @@ TEST_F(DriveApiUrlGeneratorTest, GenerateThumbnailUrl) {
}
TEST_F(DriveApiUrlGeneratorTest, BatchUploadUrl) {
- EXPECT_EQ(
- "https://www.example.com/upload/drive",
- url_generator_.GetBatchUploadUrl().spec());
+ EXPECT_EQ("https://www.example.com/upload/drive",
+ url_generator_.GetBatchUploadUrl().spec());
}
} // namespace google_apis