OLD | NEW |
---|---|
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 |
OLD | NEW |