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