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/base_requests.h" | 5 #include "google_apis/drive/base_requests.h" |
6 | 6 |
7 #include "base/files/file_util.h" | 7 #include "base/files/file_util.h" |
8 #include "base/json/json_reader.h" | 8 #include "base/json/json_reader.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" |
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
48 | 48 |
49 // Template for initiate upload of both GData WAPI and Drive API v2. | 49 // Template for initiate upload of both GData WAPI and Drive API v2. |
50 const char kUploadContentType[] = "X-Upload-Content-Type: "; | 50 const char kUploadContentType[] = "X-Upload-Content-Type: "; |
51 const char kUploadContentLength[] = "X-Upload-Content-Length: "; | 51 const char kUploadContentLength[] = "X-Upload-Content-Length: "; |
52 const char kUploadResponseLocation[] = "location"; | 52 const char kUploadResponseLocation[] = "location"; |
53 | 53 |
54 // Template for upload data range of both GData WAPI and Drive API v2. | 54 // Template for upload data range of both GData WAPI and Drive API v2. |
55 const char kUploadContentRange[] = "Content-Range: bytes "; | 55 const char kUploadContentRange[] = "Content-Range: bytes "; |
56 const char kUploadResponseRange[] = "range"; | 56 const char kUploadResponseRange[] = "range"; |
57 | 57 |
58 // The prefix of multipart/related mime type. | 58 // Mime type of JSON. |
59 const char kMultipartMimeTypePrefix[] = "multipart/related; boundary="; | 59 const char kJsonMimeType[] = "application/json"; |
60 | 60 |
61 // Template for multipart request body. | 61 // Mime type of multipart related. |
62 const char kMessageFormatBeforeFile[] = | 62 const char kMultipartRelatedMimeTypePrefix[] = |
63 "--%s\nContent-Type: %s\n\n%s\n--%s\nContent-Type: %s\n\n"; | 63 "multipart/related; boundary="; |
64 const char kMessageFormatAfterFile[] = "\n--%s--"; | 64 |
| 65 // Mime type of multipart mixed. |
| 66 const char kMultipartMixedMimeTypePrefix[] = |
| 67 "multipart/mixed; boundary="; |
| 68 |
| 69 // Header for each item in a multipart message. |
| 70 const char kMultipartItemHeaderFormat[] = "--%s\nContent-Type: %s\n\n"; |
| 71 |
| 72 // Footer for whole multipart message. |
| 73 const char kMultipartFooterFormat[] = "--%s--"; |
65 | 74 |
66 // Characters to be used for multipart/related boundary. | 75 // Characters to be used for multipart/related boundary. |
67 const char kBoundaryCharacters[] = | 76 const char kBoundaryCharacters[] = |
68 "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; | 77 "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; |
| 78 |
69 // Size of multipart/related's boundary. | 79 // Size of multipart/related's boundary. |
70 const char kBoundarySize = 70; | 80 const char kBoundarySize = 70; |
71 | 81 |
72 // Parses JSON passed in |json| on |blocking_task_runner|. Runs |callback| on | 82 // Parses JSON passed in |json| on |blocking_task_runner|. Runs |callback| on |
73 // the calling thread when finished with either success or failure. | 83 // the calling thread when finished with either success or failure. |
74 // The callback must not be null. | 84 // The callback must not be null. |
75 void ParseJsonOnBlockingPool( | 85 void ParseJsonOnBlockingPool( |
76 base::TaskRunner* blocking_task_runner, | 86 base::TaskRunner* blocking_task_runner, |
77 const std::string& json, | 87 const std::string& json, |
78 const base::Callback<void(scoped_ptr<base::Value> value)>& callback) { | 88 const base::Callback<void(scoped_ptr<base::Value> value)>& callback) { |
(...skipping 16 matching lines...) Expand all Loading... |
95 // Check that response code indicates response headers are valid (i.e. not | 105 // Check that response code indicates response headers are valid (i.e. not |
96 // malformed) before we retrieve the headers. | 106 // malformed) before we retrieve the headers. |
97 if (url_fetcher->GetResponseCode() == URLFetcher::RESPONSE_CODE_INVALID) { | 107 if (url_fetcher->GetResponseCode() == URLFetcher::RESPONSE_CODE_INVALID) { |
98 headers.assign("Response headers are malformed!!"); | 108 headers.assign("Response headers are malformed!!"); |
99 } else { | 109 } else { |
100 url_fetcher->GetResponseHeaders()->GetNormalizedHeaders(&headers); | 110 url_fetcher->GetResponseHeaders()->GetNormalizedHeaders(&headers); |
101 } | 111 } |
102 return headers; | 112 return headers; |
103 } | 113 } |
104 | 114 |
105 bool IsSuccessfulResponseCode(int response_code) { | |
106 return 200 <= response_code && response_code <= 299; | |
107 } | |
108 | |
109 // Obtains the multipart body for the metadata string and file contents. If | 115 // Obtains the multipart body for the metadata string and file contents. If |
110 // predetermined_boundary is empty, the function generates the boundary string. | 116 // predetermined_boundary is empty, the function generates the boundary string. |
111 bool GetMultipartContent(const std::string& predetermined_boundary, | 117 bool GetMultipartContent(const std::string& predetermined_boundary, |
112 const std::string& metadata_json, | 118 const std::string& metadata_json, |
113 const std::string& content_type, | 119 const std::string& content_type, |
114 const base::FilePath& path, | 120 const base::FilePath& path, |
115 std::string* upload_content_type, | 121 std::string* upload_content_type, |
116 std::string* upload_content_data) { | 122 std::string* upload_content_data) { |
117 std::string file_content; | 123 std::vector<google_apis::ContentTypeAndData> parts(2); |
118 if (!ReadFileToString(path, &file_content)) | 124 parts[0].type = kJsonMimeType; |
| 125 parts[0].data = metadata_json; |
| 126 parts[1].type = content_type; |
| 127 if (!ReadFileToString(path, &parts[1].data)) |
119 return false; | 128 return false; |
120 | 129 |
121 std::string boundary; | 130 google_apis::ContentTypeAndData output; |
122 if (predetermined_boundary.empty()) { | 131 GenerateMultipartBody( |
123 while (true) { | 132 google_apis::MULTIPART_RELATED, predetermined_boundary, parts, &output); |
124 boundary.resize(kBoundarySize); | 133 upload_content_type->swap(output.type); |
125 for (int i = 0; i < kBoundarySize; ++i) { | 134 upload_content_data->swap(output.data); |
126 // Subtract 2 from the array size to exclude '\0', and to turn the size | |
127 // into the last index. | |
128 const int last_char_index = arraysize(kBoundaryCharacters) - 2; | |
129 boundary[i] = kBoundaryCharacters[base::RandInt(0, last_char_index)]; | |
130 } | |
131 if (metadata_json.find(boundary, 0) == std::string::npos && | |
132 file_content.find(boundary, 0) == std::string::npos) { | |
133 break; | |
134 } | |
135 } | |
136 } else { | |
137 boundary = predetermined_boundary; | |
138 } | |
139 const std::string body_before_file = base::StringPrintf( | |
140 kMessageFormatBeforeFile, boundary.c_str(), "application/json", | |
141 metadata_json.c_str(), boundary.c_str(), content_type.c_str()); | |
142 const std::string body_after_file = | |
143 base::StringPrintf(kMessageFormatAfterFile, boundary.c_str()); | |
144 | |
145 *upload_content_type = kMultipartMimeTypePrefix + boundary; | |
146 *upload_content_data = body_before_file + file_content + body_after_file; | |
147 return true; | 135 return true; |
148 } | 136 } |
149 | 137 |
150 } // namespace | 138 } // namespace |
151 | 139 |
152 namespace google_apis { | 140 namespace google_apis { |
153 | 141 |
154 scoped_ptr<base::Value> ParseJson(const std::string& json) { | 142 scoped_ptr<base::Value> ParseJson(const std::string& json) { |
155 int error_code = -1; | 143 int error_code = -1; |
156 std::string error_message; | 144 std::string error_message; |
(...skipping 11 matching lines...) Expand all Loading... |
168 json.substr(0, 50).c_str(), | 156 json.substr(0, 50).c_str(), |
169 base::Uint64ToString(json.size() - 60).c_str(), | 157 base::Uint64ToString(json.size() - 60).c_str(), |
170 json.substr(json.size() - 10).c_str()); | 158 json.substr(json.size() - 10).c_str()); |
171 } | 159 } |
172 LOG(WARNING) << "Error while parsing entry response: " << error_message | 160 LOG(WARNING) << "Error while parsing entry response: " << error_message |
173 << ", code: " << error_code << ", json:\n" << trimmed_json; | 161 << ", code: " << error_code << ", json:\n" << trimmed_json; |
174 } | 162 } |
175 return value.Pass(); | 163 return value.Pass(); |
176 } | 164 } |
177 | 165 |
| 166 void GenerateMultipartBody(MultipartType multipart_type, |
| 167 const std::string& predetermined_boundary, |
| 168 const std::vector<ContentTypeAndData>& parts, |
| 169 ContentTypeAndData* output) { |
| 170 std::string boundary; |
| 171 // Generate random boundary. |
| 172 if (predetermined_boundary.empty()) { |
| 173 while (true) { |
| 174 boundary.resize(kBoundarySize); |
| 175 for (int i = 0; i < kBoundarySize; ++i) { |
| 176 // Subtract 2 from the array size to exclude '\0', and to turn the size |
| 177 // into the last index. |
| 178 const int last_char_index = arraysize(kBoundaryCharacters) - 2; |
| 179 boundary[i] = kBoundaryCharacters[base::RandInt(0, last_char_index)]; |
| 180 } |
| 181 bool conflict_with_content = false; |
| 182 for (auto& part : parts) { |
| 183 if (part.data.find(boundary, 0) != std::string::npos) { |
| 184 conflict_with_content = true; |
| 185 break; |
| 186 } |
| 187 } |
| 188 if (!conflict_with_content) |
| 189 break; |
| 190 } |
| 191 } else { |
| 192 boundary = predetermined_boundary; |
| 193 } |
| 194 |
| 195 switch (multipart_type) { |
| 196 case MULTIPART_RELATED: |
| 197 output->type = kMultipartRelatedMimeTypePrefix + boundary; |
| 198 break; |
| 199 case MULTIPART_MIXED: |
| 200 output->type = kMultipartMixedMimeTypePrefix + boundary; |
| 201 break; |
| 202 } |
| 203 |
| 204 output->data.clear(); |
| 205 for (auto& part : parts) { |
| 206 output->data.append(base::StringPrintf( |
| 207 kMultipartItemHeaderFormat, boundary.c_str(), part.type.c_str())); |
| 208 output->data.append(part.data); |
| 209 output->data.append("\n"); |
| 210 } |
| 211 output->data.append( |
| 212 base::StringPrintf(kMultipartFooterFormat, boundary.c_str())); |
| 213 } |
| 214 |
178 //=========================== ResponseWriter ================================== | 215 //=========================== ResponseWriter ================================== |
179 ResponseWriter::ResponseWriter(base::SequencedTaskRunner* file_task_runner, | 216 ResponseWriter::ResponseWriter(base::SequencedTaskRunner* file_task_runner, |
180 const base::FilePath& file_path, | 217 const base::FilePath& file_path, |
181 const GetContentCallback& get_content_callback) | 218 const GetContentCallback& get_content_callback) |
182 : get_content_callback_(get_content_callback), | 219 : get_content_callback_(get_content_callback), |
183 weak_ptr_factory_(this) { | 220 weak_ptr_factory_(this) { |
184 if (!file_path.empty()) { | 221 if (!file_path.empty()) { |
185 file_writer_.reset( | 222 file_writer_.reset( |
186 new net::URLFetcherFileWriter(file_task_runner, file_path)); | 223 new net::URLFetcherFileWriter(file_task_runner, file_path)); |
187 } | 224 } |
(...skipping 99 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
287 const std::string& custom_user_agent, | 324 const std::string& custom_user_agent, |
288 const ReAuthenticateCallback& callback, | 325 const ReAuthenticateCallback& callback, |
289 DriveApiErrorCode code) { | 326 DriveApiErrorCode code) { |
290 DCHECK(CalledOnValidThread()); | 327 DCHECK(CalledOnValidThread()); |
291 DCHECK(!access_token.empty()); | 328 DCHECK(!access_token.empty()); |
292 DCHECK(!callback.is_null()); | 329 DCHECK(!callback.is_null()); |
293 DCHECK(re_authenticate_callback_.is_null()); | 330 DCHECK(re_authenticate_callback_.is_null()); |
294 | 331 |
295 const GURL url = GetURL(); | 332 const GURL url = GetURL(); |
296 DriveApiErrorCode error_code; | 333 DriveApiErrorCode error_code; |
297 if (code != HTTP_SUCCESS && code != HTTP_CREATED && code != HTTP_NO_CONTENT) | 334 if (IsSuccessfulDriveApiErrorCode(code)) |
298 error_code = code; | 335 error_code = code; |
299 else if (url.is_empty()) | 336 else if (url.is_empty()) |
300 error_code = DRIVE_OTHER_ERROR; | 337 error_code = DRIVE_OTHER_ERROR; |
301 else | 338 else |
302 error_code = HTTP_SUCCESS; | 339 error_code = HTTP_SUCCESS; |
303 | 340 |
304 if (error_code != HTTP_SUCCESS) { | 341 if (error_code != HTTP_SUCCESS) { |
305 // Error is found on generating the url or preparing the request. Send the | 342 // Error is found on generating the url or preparing the request. Send the |
306 // error message to the callback, and then return immediately without trying | 343 // error message to the callback, and then return immediately without trying |
307 // to connect to the server. We need to call CompleteRequestWithError | 344 // to connect to the server. We need to call CompleteRequestWithError |
(...skipping 134 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
442 case net::ERR_NETWORK_CHANGED: | 479 case net::ERR_NETWORK_CHANGED: |
443 error_code_ = DRIVE_NO_CONNECTION; | 480 error_code_ = DRIVE_NO_CONNECTION; |
444 break; | 481 break; |
445 default: | 482 default: |
446 error_code_ = DRIVE_OTHER_ERROR; | 483 error_code_ = DRIVE_OTHER_ERROR; |
447 } | 484 } |
448 } | 485 } |
449 | 486 |
450 // The server may return detailed error status in JSON. | 487 // The server may return detailed error status in JSON. |
451 // See https://developers.google.com/drive/handle-errors | 488 // See https://developers.google.com/drive/handle-errors |
452 if (!IsSuccessfulResponseCode(error_code_)) { | 489 if (!IsSuccessfulDriveApiErrorCode(error_code_)) { |
453 DVLOG(1) << response_writer_->data(); | 490 DVLOG(1) << response_writer_->data(); |
454 | 491 |
455 const char kErrorKey[] = "error"; | 492 const char kErrorKey[] = "error"; |
456 const char kErrorErrorsKey[] = "errors"; | 493 const char kErrorErrorsKey[] = "errors"; |
457 const char kErrorReasonKey[] = "reason"; | 494 const char kErrorReasonKey[] = "reason"; |
458 const char kErrorMessageKey[] = "message"; | 495 const char kErrorMessageKey[] = "message"; |
459 const char kErrorReasonRateLimitExceeded[] = "rateLimitExceeded"; | 496 const char kErrorReasonRateLimitExceeded[] = "rateLimitExceeded"; |
460 const char kErrorReasonUserRateLimitExceeded[] = "userRateLimitExceeded"; | 497 const char kErrorReasonUserRateLimitExceeded[] = "userRateLimitExceeded"; |
461 const char kErrorReasonQuotaExceeded[] = "quotaExceeded"; | 498 const char kErrorReasonQuotaExceeded[] = "quotaExceeded"; |
462 | 499 |
(...skipping 486 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
949 download_action_callback_.Run(code, temp_file); | 986 download_action_callback_.Run(code, temp_file); |
950 OnProcessURLFetchResultsComplete(); | 987 OnProcessURLFetchResultsComplete(); |
951 } | 988 } |
952 | 989 |
953 void DownloadFileRequestBase::RunCallbackOnPrematureFailure( | 990 void DownloadFileRequestBase::RunCallbackOnPrematureFailure( |
954 DriveApiErrorCode code) { | 991 DriveApiErrorCode code) { |
955 download_action_callback_.Run(code, base::FilePath()); | 992 download_action_callback_.Run(code, base::FilePath()); |
956 } | 993 } |
957 | 994 |
958 } // namespace google_apis | 995 } // namespace google_apis |
OLD | NEW |