Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(282)

Side by Side Diff: google_apis/drive/base_requests.cc

Issue 1053623004: Drive: Add multipart related utility methods. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 5 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « google_apis/drive/base_requests.h ('k') | google_apis/drive/drive_api_error_codes.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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
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
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
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
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
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
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
OLDNEW
« no previous file with comments | « google_apis/drive/base_requests.h ('k') | google_apis/drive/drive_api_error_codes.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698