| 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 |