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 (trim_size < piece.size() && |
| 160 (piece[piece.size() - 1 - trim_size] == ' ' || |
| 161 piece[piece.size() - 1 - trim_size] == '\t')) { |
| 162 ++trim_size; |
| 163 } |
| 164 return piece.substr(0, piece.size() - trim_size); |
| 165 } |
| 166 |
129 } // namespace | 167 } // namespace |
130 | 168 |
| 169 MultipartHttpResponse::MultipartHttpResponse() : code(HTTP_SUCCESS) { |
| 170 } |
| 171 |
| 172 MultipartHttpResponse::~MultipartHttpResponse() { |
| 173 } |
| 174 |
| 175 // The |response| must be multipart/mixed format that contains child HTTP |
| 176 // response of drive batch request. |
| 177 // https://www.ietf.org/rfc/rfc2046.txt |
| 178 // |
| 179 // It looks like: |
| 180 // --Boundary |
| 181 // Content-type: application/http |
| 182 // |
| 183 // HTTP/1.1 200 OK |
| 184 // Header of child response |
| 185 // |
| 186 // Body of child response |
| 187 // --Boundary |
| 188 // Content-type: application/http |
| 189 // |
| 190 // HTTP/1.1 404 Not Found |
| 191 // Header of child response |
| 192 // |
| 193 // Body of child response |
| 194 // --Boundary-- |
| 195 bool ParseMultipartResponse(const std::string& content_type, |
| 196 const std::string& response, |
| 197 std::vector<MultipartHttpResponse>* parts) { |
| 198 if (response.empty()) |
| 199 return false; |
| 200 |
| 201 base::StringPiece content_type_piece(content_type); |
| 202 if (!content_type_piece.starts_with(kMultipartMixedMimeTypePrefix)) { |
| 203 return false; |
| 204 } |
| 205 content_type_piece.remove_prefix( |
| 206 base::StringPiece(kMultipartMixedMimeTypePrefix).size()); |
| 207 |
| 208 if (content_type_piece.empty()) |
| 209 return false; |
| 210 if (content_type_piece[0] == '"') { |
| 211 if (content_type_piece.size() <= 2 || |
| 212 content_type_piece[content_type_piece.size() - 1] != '"') { |
| 213 return false; |
| 214 } |
| 215 content_type_piece = |
| 216 content_type_piece.substr(1, content_type_piece.size() - 2); |
| 217 } |
| 218 |
| 219 std::string boundary; |
| 220 content_type_piece.CopyToString(&boundary); |
| 221 const std::string header = "--" + boundary; |
| 222 const std::string terminator = "--" + boundary + "--"; |
| 223 |
| 224 std::vector<base::StringPiece> lines; |
| 225 SplitIntoLines(response, &lines); |
| 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 = DRIVE_PARSE_ERROR; |
| 238 std::string body; |
| 239 for (const auto& line : lines) { |
| 240 if (state == STATE_PART_HEADER && line.empty()) { |
| 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.empty()) { |
| 263 state = STATE_PART_HTTP_BODY; |
| 264 body.clear(); |
| 265 continue; |
| 266 } |
| 267 const 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 // Drop the last kHttpBr. |
| 285 if (!body.empty()) |
| 286 body.resize(body.size() - 2); |
| 287 responses.push_back(MultipartHttpResponse()); |
| 288 responses.back().code = code; |
| 289 responses.back().body.swap(body); |
| 290 break; |
| 291 } |
| 292 if (is_new_part) |
| 293 state = STATE_PART_HEADER; |
| 294 if (was_last_part) |
| 295 break; |
| 296 } else if (state == STATE_PART_HTTP_BODY) { |
| 297 line.AppendToString(&body); |
| 298 body.append(kHttpBr); |
| 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 |