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

Side by Side 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 unified diff | Download patch
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "google_apis/drive/drive_api_requests.h" 5 #include "google_apis/drive/drive_api_requests.h"
6 6
7 #include "base/bind.h" 7 #include "base/bind.h"
8 #include "base/callback.h" 8 #include "base/callback.h"
9 #include "base/json/json_writer.h" 9 #include "base/json/json_writer.h"
10 #include "base/location.h" 10 #include "base/location.h"
11 #include "base/sequenced_task_runner.h" 11 #include "base/sequenced_task_runner.h"
12 #include "base/strings/string_number_conversions.h"
13 #include "base/strings/string_piece.h"
12 #include "base/strings/stringprintf.h" 14 #include "base/strings/stringprintf.h"
13 #include "base/task_runner_util.h" 15 #include "base/task_runner_util.h"
14 #include "base/values.h" 16 #include "base/values.h"
15 #include "google_apis/drive/request_sender.h" 17 #include "google_apis/drive/request_sender.h"
16 #include "google_apis/drive/request_util.h" 18 #include "google_apis/drive/request_util.h"
17 #include "google_apis/drive/time_util.h" 19 #include "google_apis/drive/time_util.h"
18 #include "net/base/url_util.h" 20 #include "net/base/url_util.h"
21 #include "net/http/http_response_headers.h"
19 22
20 namespace google_apis { 23 namespace google_apis {
21 namespace drive { 24 namespace drive {
22 namespace { 25 namespace {
23 26
24 // Format of one request in batch uploading request. 27 // Format of one request in batch uploading request.
25 const char kBatchUploadRequestFormat[] = 28 const char kBatchUploadRequestFormat[] =
26 "%s %s HTTP/1.1\n" 29 "%s %s HTTP/1.1\n"
27 "Host: %s\n" 30 "Host: %s\n"
28 "X-Goog-Upload-Protocol: multipart\n" 31 "X-Goog-Upload-Protocol: multipart\n"
29 "Content-Type: %s\n" 32 "Content-Type: %s\n"
30 "\n" 33 "\n"
31 "%s"; 34 "%s";
32 35
33 // Request header for specifying batch upload. 36 // Request header for specifying batch upload.
34 const char kBatchUploadHeader[] = "X-Goog-Upload-Protocol: batch"; 37 const char kBatchUploadHeader[] = "X-Goog-Upload-Protocol: batch";
35 38
36 // Content type of HTTP request. 39 // Content type of HTTP request.
37 const char kHttpContentType[] = "application/http"; 40 const char kHttpContentType[] = "application/http";
38 41
42 // Break line in HTTP message.
43 const char kHttpBr[] = "\r\n";
44
45 // Mime type of multipart mixed.
46 const char kMultipartMixedMimeTypePrefix[] = "multipart/mixed; boundary=";
47
39 // Parses the JSON value to FileResource instance and runs |callback| on the 48 // Parses the JSON value to FileResource instance and runs |callback| on the
40 // UI thread once parsing is done. 49 // UI thread once parsing is done.
41 // This is customized version of ParseJsonAndRun defined above to adapt the 50 // This is customized version of ParseJsonAndRun defined above to adapt the
42 // remaining response type. 51 // remaining response type.
43 void ParseFileResourceWithUploadRangeAndRun(const UploadRangeCallback& callback, 52 void ParseFileResourceWithUploadRangeAndRun(const UploadRangeCallback& callback,
44 const UploadRangeResponse& response, 53 const UploadRangeResponse& response,
45 scoped_ptr<base::Value> value) { 54 scoped_ptr<base::Value> value) {
46 DCHECK(!callback.is_null()); 55 DCHECK(!callback.is_null());
47 56
48 scoped_ptr<FileResource> file_resource; 57 scoped_ptr<FileResource> file_resource;
(...skipping 70 matching lines...) Expand 10 before | Expand all | Expand 10 after
119 root.SetString("lastViewedByMeDate", google_apis::util::FormatTimeAsString( 128 root.SetString("lastViewedByMeDate", google_apis::util::FormatTimeAsString(
120 last_viewed_by_me_date)); 129 last_viewed_by_me_date));
121 } 130 }
122 131
123 AttachProperties(properties, &root); 132 AttachProperties(properties, &root);
124 std::string json_string; 133 std::string json_string;
125 base::JSONWriter::Write(&root, &json_string); 134 base::JSONWriter::Write(&root, &json_string);
126 return json_string; 135 return json_string;
127 } 136 }
128 137
138 // Splits |string| into lines by |kHttpBr|.
139 // Each line does not include |kHttpBr|.
140 void SplitIntoLines(const std::string& string,
141 std::vector<base::StringPiece>* output) {
142 const size_t br_size = std::string(kHttpBr).size();
143 std::string::const_iterator it = string.begin();
144 std::vector<base::StringPiece> lines;
145 while (true) {
146 const std::string::const_iterator next_pos =
147 std::search(it, string.end(), kHttpBr, kHttpBr + br_size);
148 lines.push_back(base::StringPiece(it, next_pos));
149 if (next_pos == string.end())
150 break;
151 it = next_pos + br_size;
152 }
153 output->swap(lines);
154 }
155
156 // Remove transport padding (spaces and tabs at the end of line) from |piece|.
157 base::StringPiece TrimTransportPadding(const base::StringPiece& piece) {
158 size_t trim_size = 0;
159 while (true) {
160 if (trim_size < piece.size() &&
161 (piece[piece.size() - 1 - trim_size] == ' ' ||
162 piece[piece.size() - 1 - trim_size] == '\t')) {
kinaba 2015/04/28 03:54:54 while(true){if(X){Y;continue;} break;} is equival
hirono 2015/04/28 05:41:18 Done.
163 ++trim_size;
164 continue;
165 }
166 break;
167 }
168 return piece.substr(0, piece.size() - trim_size);
169 }
170
129 } // namespace 171 } // namespace
130 172
173 MultipartHttpResponse::MultipartHttpResponse() : code(HTTP_SUCCESS) {
174 }
175
176 MultipartHttpResponse::~MultipartHttpResponse() {
177 }
178
179 // The |response| must be multipart/mixed format that contains child HTTP
180 // response of drive batch request.
181 // https://www.ietf.org/rfc/rfc2046.txt
182 //
183 // It looks like:
184 // --Boundary
185 // Content-type: application/http
186 //
187 // HTTP/1.1 200 OK
188 // Header of child response
189 //
190 // Body of child response
191 // --Boundary
192 // Content-type: application/http
193 //
194 // HTTP/1.1 404 Not Found
195 // Header of child response
196 //
197 // Body of child response
198 // --Boundary--
199 bool ParseMultipartResponse(const std::string& content_type,
200 const std::string& response,
201 std::vector<MultipartHttpResponse>* parts) {
202 if (response.empty())
203 return false;
204
205 base::StringPiece content_type_piece(content_type);
206 if (!content_type_piece.starts_with(kMultipartMixedMimeTypePrefix)) {
207 return false;
208 }
209 content_type_piece.remove_prefix(
210 base::StringPiece(kMultipartMixedMimeTypePrefix).size());
211 if (content_type_piece[0] == '"' &&
kinaba 2015/04/28 03:54:54 content_type_piece.size() >= 2 && (or move the si
hirono 2015/04/28 05:41:18 Done.
212 content_type_piece[content_type_piece.size() - 1] == '"') {
213 content_type_piece =
214 content_type_piece.substr(1, content_type_piece.size() - 2);
215 }
216 if (content_type_piece.size() == 0)
217 return false;
218
219 std::string boundary;
220 content_type_piece.CopyToString(&boundary);
221
222 std::vector<base::StringPiece> lines;
223 SplitIntoLines(response, &lines);
224 const std::string header = "--" + boundary;
225 const std::string terminator = "--" + boundary + "--";
226
227 enum {
228 STATE_START,
229 STATE_PART_HEADER,
230 STATE_PART_HTTP_STATUS_LINE,
231 STATE_PART_HTTP_HEADER,
232 STATE_PART_HTTP_BODY
233 } state = STATE_START;
234
235 const std::string kHttpStatusPrefix = "HTTP/1.1 ";
236 std::vector<MultipartHttpResponse> responses;
237 DriveApiErrorCode code;
238 std::string body;
239 for (const auto& line : lines) {
240 if (state == STATE_PART_HEADER && line == "") {
kinaba 2015/04/28 03:54:54 line.empty()
hirono 2015/04/28 05:41:17 Done.
241 state = STATE_PART_HTTP_STATUS_LINE;
242 continue;
243 }
244
245 if (state == STATE_PART_HTTP_STATUS_LINE) {
246 if (line.starts_with(kHttpStatusPrefix)) {
247 int int_code;
248 base::StringToInt(
249 line.substr(base::StringPiece(kHttpStatusPrefix).size()),
250 &int_code);
251 if (int_code > 0)
252 code = static_cast<DriveApiErrorCode>(int_code);
253 else
254 code = DRIVE_PARSE_ERROR;
255 } else {
256 code = DRIVE_PARSE_ERROR;
257 }
258 state = STATE_PART_HTTP_HEADER;
259 continue;
260 }
261
262 if (state == STATE_PART_HTTP_HEADER && line == "") {
kinaba 2015/04/28 03:54:54 line.empty()
hirono 2015/04/28 05:41:17 Done.
263 state = STATE_PART_HTTP_BODY;
264 body = "";
kinaba 2015/04/28 03:54:54 body.clear() or body = std::string() (in Chromium
hirono 2015/04/28 05:41:18 Done.
265 continue;
266 }
267 base::StringPiece chopped_line = TrimTransportPadding(line);
268 const bool is_new_part = chopped_line == header;
269 const bool was_last_part = chopped_line == terminator;
270 if (is_new_part || was_last_part) {
271 switch (state) {
272 case STATE_START:
273 break;
274 case STATE_PART_HEADER:
275 case STATE_PART_HTTP_STATUS_LINE:
276 responses.push_back(MultipartHttpResponse());
277 responses.back().code = DRIVE_PARSE_ERROR;
278 break;
279 case STATE_PART_HTTP_HEADER:
280 responses.push_back(MultipartHttpResponse());
281 responses.back().code = code;
282 break;
283 case STATE_PART_HTTP_BODY:
284 responses.push_back(MultipartHttpResponse());
285 responses.back().code = code;
286 responses.back().body.swap(body);
287 break;
288 }
289 if (is_new_part)
290 state = STATE_PART_HEADER;
291 if (was_last_part)
292 break;
293 } else {
294 if (state == STATE_PART_HTTP_BODY) {
kinaba 2015/04/28 03:54:54 else { if(...) {} } => else if(...) {} ?
hirono 2015/04/28 05:41:18 Done.
295 if (!body.empty())
296 body.append(kHttpBr);
kinaba 2015/04/28 03:54:54 If the body begins with multiple empty lines, this
hirono 2015/04/28 05:41:17 Done.
297 line.AppendToString(&body);
298 }
299 }
300 }
301
302 parts->swap(responses);
303 return true;
304 }
305
131 Property::Property() : visibility_(VISIBILITY_PRIVATE) { 306 Property::Property() : visibility_(VISIBILITY_PRIVATE) {
132 } 307 }
133 308
134 Property::~Property() { 309 Property::~Property() {
135 } 310 }
136 311
137 //============================ DriveApiPartialFieldRequest ==================== 312 //============================ DriveApiPartialFieldRequest ====================
138 313
139 DriveApiPartialFieldRequest::DriveApiPartialFieldRequest( 314 DriveApiPartialFieldRequest::DriveApiPartialFieldRequest(
140 RequestSender* sender) : UrlFetchRequestBase(sender) { 315 RequestSender* sender) : UrlFetchRequestBase(sender) {
(...skipping 866 matching lines...) Expand 10 before | Expand all | Expand 10 after
1007 void BatchUploadRequest::SetBoundaryForTesting(const std::string& boundary) { 1182 void BatchUploadRequest::SetBoundaryForTesting(const std::string& boundary) {
1008 boundary_ = boundary; 1183 boundary_ = boundary;
1009 } 1184 }
1010 1185
1011 void BatchUploadRequest::AddRequest(BatchableRequestBase* request) { 1186 void BatchUploadRequest::AddRequest(BatchableRequestBase* request) {
1012 DCHECK(CalledOnValidThread()); 1187 DCHECK(CalledOnValidThread());
1013 DCHECK(request); 1188 DCHECK(request);
1014 DCHECK(GetChildEntry(request) == child_requests_.end()); 1189 DCHECK(GetChildEntry(request) == child_requests_.end());
1015 DCHECK(!committed_); 1190 DCHECK(!committed_);
1016 child_requests_.push_back(BatchUploadChildEntry(request)); 1191 child_requests_.push_back(BatchUploadChildEntry(request));
1017 request->Prepare( 1192 request->Prepare(base::Bind(&BatchUploadRequest::OnChildRequestPrepared,
1018 base::Bind(&BatchUploadRequest::OnChildRequestPrepared, 1193 weak_ptr_factory_.GetWeakPtr(), request));
1019 weak_ptr_factory_.GetWeakPtr(),
1020 request));
1021 } 1194 }
1022 1195
1023 void BatchUploadRequest::OnChildRequestPrepared( 1196 void BatchUploadRequest::OnChildRequestPrepared(RequestID request_id,
1024 RequestID request_id, DriveApiErrorCode result) { 1197 DriveApiErrorCode result) {
1025 DCHECK(CalledOnValidThread()); 1198 DCHECK(CalledOnValidThread());
1026 auto const child = GetChildEntry(request_id); 1199 auto const child = GetChildEntry(request_id);
1027 DCHECK(child != child_requests_.end()); 1200 DCHECK(child != child_requests_.end());
1028 if (IsSuccessfulDriveApiErrorCode(result)) { 1201 if (IsSuccessfulDriveApiErrorCode(result)) {
1029 child->prepared = true; 1202 child->prepared = true;
1030 } else { 1203 } else {
1031 child->request->RunCallbackOnPrematureFailure(result); 1204 child->request->RunCallbackOnPrematureFailure(result);
1032 sender_->RequestFinished(child->request); 1205 sender_->RequestFinished(child->request);
1033 child_requests_.erase(child); 1206 child_requests_.erase(child);
1034 } 1207 }
(...skipping 19 matching lines...) Expand all
1054 for (auto& child : child_requests_) { 1227 for (auto& child : child_requests_) {
1055 // Request cancel should delete the request instance. 1228 // Request cancel should delete the request instance.
1056 child.request->Cancel(); 1229 child.request->Cancel();
1057 } 1230 }
1058 child_requests_.clear(); 1231 child_requests_.clear();
1059 UrlFetchRequestBase::Cancel(); 1232 UrlFetchRequestBase::Cancel();
1060 } 1233 }
1061 1234
1062 // Obtains corresponding child entry of |request_id|. Returns NULL if the 1235 // Obtains corresponding child entry of |request_id|. Returns NULL if the
1063 // entry is not found. 1236 // entry is not found.
1064 std::vector<BatchUploadChildEntry>::iterator 1237 std::vector<BatchUploadChildEntry>::iterator BatchUploadRequest::GetChildEntry(
1065 BatchUploadRequest::GetChildEntry(RequestID request_id) { 1238 RequestID request_id) {
1066 for (auto it = child_requests_.begin(); it != child_requests_.end(); ++it) { 1239 for (auto it = child_requests_.begin(); it != child_requests_.end(); ++it) {
1067 if (it->request == request_id) 1240 if (it->request == request_id)
1068 return it; 1241 return it;
1069 } 1242 }
1070 return child_requests_.end(); 1243 return child_requests_.end();
1071 } 1244 }
1072 1245
1073 void BatchUploadRequest::MayCompletePrepare() { 1246 void BatchUploadRequest::MayCompletePrepare() {
1074 if (!committed_ || prepare_callback_.is_null()) 1247 if (!committed_ || prepare_callback_.is_null())
1075 return; 1248 return;
(...skipping 21 matching lines...) Expand all
1097 method = "PUT"; 1270 method = "PUT";
1098 break; 1271 break;
1099 default: 1272 default:
1100 NOTREACHED(); 1273 NOTREACHED();
1101 break; 1274 break;
1102 } 1275 }
1103 1276
1104 parts.push_back(ContentTypeAndData()); 1277 parts.push_back(ContentTypeAndData());
1105 parts.back().type = kHttpContentType; 1278 parts.back().type = kHttpContentType;
1106 parts.back().data = base::StringPrintf( 1279 parts.back().data = base::StringPrintf(
1107 kBatchUploadRequestFormat, 1280 kBatchUploadRequestFormat, method.c_str(), url.path().c_str(),
1108 method.c_str(), 1281 url_generator_.GetBatchUploadUrl().host().c_str(), type.c_str(),
1109 url.path().c_str(),
1110 url_generator_.GetBatchUploadUrl().host().c_str(),
1111 type.c_str(),
1112 data.c_str()); 1282 data.c_str());
1113 } 1283 }
1114 1284
1115 GenerateMultipartBody(MULTIPART_MIXED, boundary_, parts, &upload_content_); 1285 GenerateMultipartBody(MULTIPART_MIXED, boundary_, parts, &upload_content_);
1116 prepare_callback_.Run(HTTP_SUCCESS); 1286 prepare_callback_.Run(HTTP_SUCCESS);
1117 } 1287 }
1118 1288
1119 bool BatchUploadRequest::GetContentData(std::string* upload_content_type, 1289 bool BatchUploadRequest::GetContentData(std::string* upload_content_type,
1120 std::string* upload_content_data) { 1290 std::string* upload_content_data) {
1121 upload_content_type->assign(upload_content_.type); 1291 upload_content_type->assign(upload_content_.type);
(...skipping 20 matching lines...) Expand all
1142 return headers; 1312 return headers;
1143 } 1313 }
1144 1314
1145 void BatchUploadRequest::ProcessURLFetchResults(const net::URLFetcher* source) { 1315 void BatchUploadRequest::ProcessURLFetchResults(const net::URLFetcher* source) {
1146 if (!IsSuccessfulDriveApiErrorCode(GetErrorCode())) { 1316 if (!IsSuccessfulDriveApiErrorCode(GetErrorCode())) {
1147 RunCallbackOnPrematureFailure(GetErrorCode()); 1317 RunCallbackOnPrematureFailure(GetErrorCode());
1148 sender_->RequestFinished(this); 1318 sender_->RequestFinished(this);
1149 return; 1319 return;
1150 } 1320 }
1151 1321
1152 for (auto& child : child_requests_) { 1322 std::string content_type;
1153 // TODO(hirono): Split the mutlipart result and return the correct code and 1323 source->GetResponseHeaders()->EnumerateHeader(
1154 // body. 1324 /* need only first header */ NULL, "Content-Type", &content_type);
1155 child.request->ProcessURLFetchResults(HTTP_SERVICE_UNAVAILABLE, ""); 1325
1156 // Request deletes itself after processing. 1326 std::vector<MultipartHttpResponse> parts;
1327 if (!ParseMultipartResponse(content_type, response_writer()->data(),
1328 &parts) ||
1329 child_requests_.size() != parts.size()) {
1330 RunCallbackOnPrematureFailure(DRIVE_PARSE_ERROR);
1331 sender_->RequestFinished(this);
1332 return;
1333 }
1334
1335 for (size_t i = 0; i < parts.size(); ++i) {
1336 child_requests_[i].request->ProcessURLFetchResults(parts[i].code,
1337 parts[i].body);
1157 } 1338 }
1158 1339
1159 child_requests_.clear(); 1340 child_requests_.clear();
1341 sender_->RequestFinished(this);
1160 } 1342 }
1161 1343
1162 void BatchUploadRequest::RunCallbackOnPrematureFailure(DriveApiErrorCode code) { 1344 void BatchUploadRequest::RunCallbackOnPrematureFailure(DriveApiErrorCode code) {
1163 for (auto child : child_requests_) { 1345 for (auto child : child_requests_) {
1164 child.request->RunCallbackOnPrematureFailure(code); 1346 child.request->RunCallbackOnPrematureFailure(code);
1165 sender_->RequestFinished(child.request); 1347 sender_->RequestFinished(child.request);
1166 } 1348 }
1167 child_requests_.clear(); 1349 child_requests_.clear();
1168 } 1350 }
1169 1351
1170 } // namespace drive 1352 } // namespace drive
1171 } // namespace google_apis 1353 } // namespace google_apis
OLDNEW
« 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