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

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, 7 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/stringprintf.h" 12 #include "base/strings/stringprintf.h"
13 #include "base/task_runner_util.h" 13 #include "base/task_runner_util.h"
14 #include "base/values.h" 14 #include "base/values.h"
15 #include "google_apis/drive/request_sender.h" 15 #include "google_apis/drive/request_sender.h"
16 #include "google_apis/drive/request_util.h" 16 #include "google_apis/drive/request_util.h"
17 #include "google_apis/drive/time_util.h" 17 #include "google_apis/drive/time_util.h"
18 #include "net/base/url_util.h" 18 #include "net/base/url_util.h"
19 #include "net/http/http_response_headers.h"
19 20
20 namespace google_apis { 21 namespace google_apis {
21 namespace drive { 22 namespace drive {
22 namespace { 23 namespace {
23 24
24 // Format of one request in batch uploading request. 25 // Format of one request in batch uploading request.
25 const char kBatchUploadRequestFormat[] = 26 const char kBatchUploadRequestFormat[] =
26 "%s %s HTTP/1.1\n" 27 "%s %s HTTP/1.1\n"
27 "Host: %s\n" 28 "Host: %s\n"
28 "X-Goog-Upload-Protocol: multipart\n" 29 "X-Goog-Upload-Protocol: multipart\n"
29 "Content-Type: %s\n" 30 "Content-Type: %s\n"
30 "\n" 31 "\n"
31 "%s"; 32 "%s";
32 33
33 // Request header for specifying batch upload. 34 // Request header for specifying batch upload.
34 const char kBatchUploadHeader[] = "X-Goog-Upload-Protocol: batch"; 35 const char kBatchUploadHeader[] = "X-Goog-Upload-Protocol: batch";
35 36
36 // Content type of HTTP request. 37 // Content type of HTTP request.
37 const char kHttpContentType[] = "application/http"; 38 const char kHttpContentType[] = "application/http";
38 39
40 // Break line in HTTP message.
41 const char kHttpBr[] = "\r\n";
42
43 // Mime type of multipart mixed.
44 const char kMultipartMixedMimeTypePrefix[] = "multipart/mixed; boundary=";
45
39 // Parses the JSON value to FileResource instance and runs |callback| on the 46 // Parses the JSON value to FileResource instance and runs |callback| on the
40 // UI thread once parsing is done. 47 // UI thread once parsing is done.
41 // This is customized version of ParseJsonAndRun defined above to adapt the 48 // This is customized version of ParseJsonAndRun defined above to adapt the
42 // remaining response type. 49 // remaining response type.
43 void ParseFileResourceWithUploadRangeAndRun(const UploadRangeCallback& callback, 50 void ParseFileResourceWithUploadRangeAndRun(const UploadRangeCallback& callback,
44 const UploadRangeResponse& response, 51 const UploadRangeResponse& response,
45 scoped_ptr<base::Value> value) { 52 scoped_ptr<base::Value> value) {
46 DCHECK(!callback.is_null()); 53 DCHECK(!callback.is_null());
47 54
48 scoped_ptr<FileResource> file_resource; 55 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( 126 root.SetString("lastViewedByMeDate", google_apis::util::FormatTimeAsString(
120 last_viewed_by_me_date)); 127 last_viewed_by_me_date));
121 } 128 }
122 129
123 AttachProperties(properties, &root); 130 AttachProperties(properties, &root);
124 std::string json_string; 131 std::string json_string;
125 base::JSONWriter::Write(&root, &json_string); 132 base::JSONWriter::Write(&root, &json_string);
126 return json_string; 133 return json_string;
127 } 134 }
128 135
136 // Checks if the string between |begin| and |end| equals to |expected|. The
137 // string between |begin| and |end| can include kHttpBr. |expected| must not
138 // include kHttpBr.
139 bool IsLineEqual(std::string::const_iterator begin,
140 std::string::const_iterator end,
141 const std::string& expected) {
142 size_t size = end - begin;
143 const std::string br(kHttpBr);
144 if (size >= br.size() && std::equal(br.begin(), br.end(), end - br.size())) {
145 size -= br.size();
146 }
147
148 if (size != expected.size())
149 return false;
150
151 return std::equal(expected.begin(), expected.end(), begin);
152 }
153
154 // Obtains substring of string between |begin| and |end| that follows |prefix|.
155 // Returns false if |prefix| does not match.
156 bool GetAfterPrefix(std::string::const_iterator begin,
157 std::string::const_iterator end,
158 const std::string& prefix,
159 std::string* output) {
160 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
161 if (output_begin > end)
162 return false;
163 if (std::equal(prefix.begin(), prefix.end(), begin)) {
164 *output = std::string(output_begin, end);
165 return true;
166 } else {
167 return false;
168 }
169 }
170
129 } // namespace 171 } // namespace
130 172
173 MultipartHttpResponse::MultipartHttpResponse() : code(HTTP_SUCCESS) {
174 }
175
176 MultipartHttpResponse::~MultipartHttpResponse() {
177 }
178
179 bool ParseMultipartResponse(const std::string& boundary,
180 const std::string& response,
181 std::vector<MultipartHttpResponse>* parts) {
182 if (boundary.empty() || response.empty())
183 return false;
184 const size_t br_size = std::string(kHttpBr).size();
185 std::vector<std::string::const_iterator> lines;
186 std::string::const_iterator it = response.begin();
187 while (true) {
188 lines.push_back(it);
189 it = std::search(it, response.end(), kHttpBr, kHttpBr + br_size);
190 if (it == response.end()) {
191 lines.push_back(it);
192 break;
193 }
194 it += br_size;
195 }
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.
196
197 const std::string header = "--" + boundary;
198 const std::string terminator = "--" + boundary + "--";
199
200 enum {
201 STATE_START,
202 STATE_PART_HEADER,
203 STATE_PART_HTTP_STATUS_LINE,
204 STATE_PART_HTTP_HEADER,
205 STATE_PART_HTTP_BODY
206 } state = STATE_START;
207
208 const std::string kHttpStatusPrefix = "HTTP/1.1 ";
209 std::vector<MultipartHttpResponse> responses;
210 DriveApiErrorCode code;
211 std::string::const_iterator body_begin;
212 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.
213 if (state == STATE_PART_HEADER && IsLineEqual(lines[i], lines[i + 1], "")) {
214 state = STATE_PART_HTTP_STATUS_LINE;
215 continue;
216 }
217
218 if (state == STATE_PART_HTTP_STATUS_LINE) {
219 std::string code_string;
220 if (GetAfterPrefix(lines[i], lines[i + 1], kHttpStatusPrefix,
221 &code_string)) {
222 code = static_cast<DriveApiErrorCode>(atoi(code_string.c_str()));
223 if (code <= 0)
224 code = DRIVE_PARSE_ERROR;
225 } else {
226 code = DRIVE_PARSE_ERROR;
227 }
228 state = STATE_PART_HTTP_HEADER;
229 continue;
230 }
231
232 if (state == STATE_PART_HTTP_HEADER &&
233 IsLineEqual(lines[i], lines[i + 1], "")) {
234 state = STATE_PART_HTTP_BODY;
235 body_begin = lines[i + 1];
236 continue;
237 }
238
239 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
240 const bool was_last_part = IsLineEqual(lines[i], lines[i + 1], terminator);
241 if (is_new_part || was_last_part) {
242 switch (state) {
243 case STATE_START:
244 break;
245 case STATE_PART_HEADER:
246 case STATE_PART_HTTP_STATUS_LINE:
247 responses.push_back(MultipartHttpResponse());
248 responses.back().code = DRIVE_PARSE_ERROR;
249 break;
250 case STATE_PART_HTTP_HEADER:
251 responses.push_back(MultipartHttpResponse());
252 responses.back().code = code;
253 break;
254 case STATE_PART_HTTP_BODY:
255 responses.push_back(MultipartHttpResponse());
256 responses.back().code = code;
257 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
258 break;
259 }
260 }
261
262 if (is_new_part)
263 state = STATE_PART_HEADER;
264 if (was_last_part)
265 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.
266 }
267
268 parts->swap(responses);
269 return true;
270 }
271
131 Property::Property() : visibility_(VISIBILITY_PRIVATE) { 272 Property::Property() : visibility_(VISIBILITY_PRIVATE) {
132 } 273 }
133 274
134 Property::~Property() { 275 Property::~Property() {
135 } 276 }
136 277
137 //============================ DriveApiPartialFieldRequest ==================== 278 //============================ DriveApiPartialFieldRequest ====================
138 279
139 DriveApiPartialFieldRequest::DriveApiPartialFieldRequest( 280 DriveApiPartialFieldRequest::DriveApiPartialFieldRequest(
140 RequestSender* sender) : UrlFetchRequestBase(sender) { 281 RequestSender* sender) : UrlFetchRequestBase(sender) {
(...skipping 866 matching lines...) Expand 10 before | Expand all | Expand 10 after
1007 void BatchUploadRequest::SetBoundaryForTesting(const std::string& boundary) { 1148 void BatchUploadRequest::SetBoundaryForTesting(const std::string& boundary) {
1008 boundary_ = boundary; 1149 boundary_ = boundary;
1009 } 1150 }
1010 1151
1011 void BatchUploadRequest::AddRequest(BatchableRequestBase* request) { 1152 void BatchUploadRequest::AddRequest(BatchableRequestBase* request) {
1012 DCHECK(CalledOnValidThread()); 1153 DCHECK(CalledOnValidThread());
1013 DCHECK(request); 1154 DCHECK(request);
1014 DCHECK(GetChildEntry(request) == child_requests_.end()); 1155 DCHECK(GetChildEntry(request) == child_requests_.end());
1015 DCHECK(!committed_); 1156 DCHECK(!committed_);
1016 child_requests_.push_back(BatchUploadChildEntry(request)); 1157 child_requests_.push_back(BatchUploadChildEntry(request));
1017 request->Prepare( 1158 request->Prepare(base::Bind(&BatchUploadRequest::OnChildRequestPrepared,
1018 base::Bind(&BatchUploadRequest::OnChildRequestPrepared, 1159 weak_ptr_factory_.GetWeakPtr(), request));
1019 weak_ptr_factory_.GetWeakPtr(),
1020 request));
1021 } 1160 }
1022 1161
1023 void BatchUploadRequest::OnChildRequestPrepared( 1162 void BatchUploadRequest::OnChildRequestPrepared(RequestID request_id,
1024 RequestID request_id, DriveApiErrorCode result) { 1163 DriveApiErrorCode result) {
1025 DCHECK(CalledOnValidThread()); 1164 DCHECK(CalledOnValidThread());
1026 auto const child = GetChildEntry(request_id); 1165 auto const child = GetChildEntry(request_id);
1027 DCHECK(child != child_requests_.end()); 1166 DCHECK(child != child_requests_.end());
1028 if (IsSuccessfulDriveApiErrorCode(result)) { 1167 if (IsSuccessfulDriveApiErrorCode(result)) {
1029 child->prepared = true; 1168 child->prepared = true;
1030 } else { 1169 } else {
1031 child->request->RunCallbackOnPrematureFailure(result); 1170 child->request->RunCallbackOnPrematureFailure(result);
1032 sender_->RequestFinished(child->request); 1171 sender_->RequestFinished(child->request);
1033 child_requests_.erase(child); 1172 child_requests_.erase(child);
1034 } 1173 }
(...skipping 19 matching lines...) Expand all
1054 for (auto& child : child_requests_) { 1193 for (auto& child : child_requests_) {
1055 // Request cancel should delete the request instance. 1194 // Request cancel should delete the request instance.
1056 child.request->Cancel(); 1195 child.request->Cancel();
1057 } 1196 }
1058 child_requests_.clear(); 1197 child_requests_.clear();
1059 UrlFetchRequestBase::Cancel(); 1198 UrlFetchRequestBase::Cancel();
1060 } 1199 }
1061 1200
1062 // Obtains corresponding child entry of |request_id|. Returns NULL if the 1201 // Obtains corresponding child entry of |request_id|. Returns NULL if the
1063 // entry is not found. 1202 // entry is not found.
1064 std::vector<BatchUploadChildEntry>::iterator 1203 std::vector<BatchUploadChildEntry>::iterator BatchUploadRequest::GetChildEntry(
1065 BatchUploadRequest::GetChildEntry(RequestID request_id) { 1204 RequestID request_id) {
1066 for (auto it = child_requests_.begin(); it != child_requests_.end(); ++it) { 1205 for (auto it = child_requests_.begin(); it != child_requests_.end(); ++it) {
1067 if (it->request == request_id) 1206 if (it->request == request_id)
1068 return it; 1207 return it;
1069 } 1208 }
1070 return child_requests_.end(); 1209 return child_requests_.end();
1071 } 1210 }
1072 1211
1073 void BatchUploadRequest::MayCompletePrepare() { 1212 void BatchUploadRequest::MayCompletePrepare() {
1074 if (!committed_ || prepare_callback_.is_null()) 1213 if (!committed_ || prepare_callback_.is_null())
1075 return; 1214 return;
(...skipping 21 matching lines...) Expand all
1097 method = "PUT"; 1236 method = "PUT";
1098 break; 1237 break;
1099 default: 1238 default:
1100 NOTREACHED(); 1239 NOTREACHED();
1101 break; 1240 break;
1102 } 1241 }
1103 1242
1104 parts.push_back(ContentTypeAndData()); 1243 parts.push_back(ContentTypeAndData());
1105 parts.back().type = kHttpContentType; 1244 parts.back().type = kHttpContentType;
1106 parts.back().data = base::StringPrintf( 1245 parts.back().data = base::StringPrintf(
1107 kBatchUploadRequestFormat, 1246 kBatchUploadRequestFormat, method.c_str(), url.path().c_str(),
1108 method.c_str(), 1247 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()); 1248 data.c_str());
1113 } 1249 }
1114 1250
1115 GenerateMultipartBody(MULTIPART_MIXED, boundary_, parts, &upload_content_); 1251 GenerateMultipartBody(MULTIPART_MIXED, boundary_, parts, &upload_content_);
1116 prepare_callback_.Run(HTTP_SUCCESS); 1252 prepare_callback_.Run(HTTP_SUCCESS);
1117 } 1253 }
1118 1254
1119 bool BatchUploadRequest::GetContentData(std::string* upload_content_type, 1255 bool BatchUploadRequest::GetContentData(std::string* upload_content_type,
1120 std::string* upload_content_data) { 1256 std::string* upload_content_data) {
1121 upload_content_type->assign(upload_content_.type); 1257 upload_content_type->assign(upload_content_.type);
(...skipping 20 matching lines...) Expand all
1142 return headers; 1278 return headers;
1143 } 1279 }
1144 1280
1145 void BatchUploadRequest::ProcessURLFetchResults(const net::URLFetcher* source) { 1281 void BatchUploadRequest::ProcessURLFetchResults(const net::URLFetcher* source) {
1146 if (!IsSuccessfulDriveApiErrorCode(GetErrorCode())) { 1282 if (!IsSuccessfulDriveApiErrorCode(GetErrorCode())) {
1147 RunCallbackOnPrematureFailure(GetErrorCode()); 1283 RunCallbackOnPrematureFailure(GetErrorCode());
1148 sender_->RequestFinished(this); 1284 sender_->RequestFinished(this);
1149 return; 1285 return;
1150 } 1286 }
1151 1287
1152 for (auto& child : child_requests_) { 1288 std::string content_type;
1153 // TODO(hirono): Split the mutlipart result and return the correct code and 1289 source->GetResponseHeaders()->EnumerateHeader(
1154 // body. 1290 /* need only first header */ NULL, "Content-Type", &content_type);
1155 child.request->ProcessURLFetchResults(HTTP_SERVICE_UNAVAILABLE, ""); 1291 std::string boundary;
1156 // Request deletes itself after processing. 1292 if (!GetAfterPrefix(content_type.begin(), content_type.end(),
1293 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.
1294 RunCallbackOnPrematureFailure(DRIVE_PARSE_ERROR);
1295 sender_->RequestFinished(this);
1296 return;
1297 }
1298
1299 std::vector<MultipartHttpResponse> parts;
1300 if (!ParseMultipartResponse(boundary, response_writer()->data(), &parts) ||
1301 child_requests_.size() != parts.size()) {
1302 RunCallbackOnPrematureFailure(DRIVE_PARSE_ERROR);
1303 sender_->RequestFinished(this);
1304 return;
1305 }
1306
1307 for (size_t i = 0; i < parts.size(); ++i) {
1308 child_requests_[i].request->ProcessURLFetchResults(parts[i].code,
1309 parts[i].body);
1157 } 1310 }
1158 1311
1159 child_requests_.clear(); 1312 child_requests_.clear();
1313 sender_->RequestFinished(this);
1160 } 1314 }
1161 1315
1162 void BatchUploadRequest::RunCallbackOnPrematureFailure(DriveApiErrorCode code) { 1316 void BatchUploadRequest::RunCallbackOnPrematureFailure(DriveApiErrorCode code) {
1163 for (auto child : child_requests_) { 1317 for (auto child : child_requests_) {
1164 child.request->RunCallbackOnPrematureFailure(code); 1318 child.request->RunCallbackOnPrematureFailure(code);
1165 sender_->RequestFinished(child.request); 1319 sender_->RequestFinished(child.request);
1166 } 1320 }
1167 child_requests_.clear(); 1321 child_requests_.clear();
1168 } 1322 }
1169 1323
1170 } // namespace drive 1324 } // namespace drive
1171 } // namespace google_apis 1325 } // namespace google_apis
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698