OLD | NEW |
---|---|
1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 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 "sync/internal_api/public/attachments/attachment_downloader_impl.h" | 5 #include "sync/internal_api/public/attachments/attachment_downloader_impl.h" |
6 | 6 |
7 #include "base/base64.h" | |
7 #include "base/bind.h" | 8 #include "base/bind.h" |
8 #include "base/message_loop/message_loop.h" | 9 #include "base/message_loop/message_loop.h" |
10 #include "base/sys_byteorder.h" | |
9 #include "net/base/load_flags.h" | 11 #include "net/base/load_flags.h" |
10 #include "net/http/http_response_headers.h" | 12 #include "net/http/http_response_headers.h" |
11 #include "net/http/http_status_code.h" | 13 #include "net/http/http_status_code.h" |
12 #include "net/http/http_util.h" | 14 #include "net/http/http_util.h" |
13 #include "net/url_request/url_fetcher.h" | 15 #include "net/url_request/url_fetcher.h" |
14 #include "sync/internal_api/public/attachments/attachment_uploader_impl.h" | 16 #include "sync/internal_api/public/attachments/attachment_uploader_impl.h" |
17 #include "sync/internal_api/public/attachments/attachment_util.h" | |
15 #include "sync/protocol/sync.pb.h" | 18 #include "sync/protocol/sync.pb.h" |
16 #include "url/gurl.h" | 19 #include "url/gurl.h" |
17 | 20 |
18 namespace syncer { | 21 namespace syncer { |
19 | 22 |
20 struct AttachmentDownloaderImpl::DownloadState { | 23 struct AttachmentDownloaderImpl::DownloadState { |
21 public: | 24 public: |
22 DownloadState(const AttachmentId& attachment_id, | 25 DownloadState(const AttachmentId& attachment_id, |
23 const AttachmentUrl& attachment_url); | 26 const AttachmentUrl& attachment_url); |
24 | 27 |
(...skipping 84 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
109 DCHECK(CalledOnValidThread()); | 112 DCHECK(CalledOnValidThread()); |
110 DCHECK(request == access_token_request_.get()); | 113 DCHECK(request == access_token_request_.get()); |
111 access_token_request_.reset(); | 114 access_token_request_.reset(); |
112 StateList::const_iterator iter; | 115 StateList::const_iterator iter; |
113 // Without access token all downloads fail. | 116 // Without access token all downloads fail. |
114 for (iter = requests_waiting_for_access_token_.begin(); | 117 for (iter = requests_waiting_for_access_token_.begin(); |
115 iter != requests_waiting_for_access_token_.end(); | 118 iter != requests_waiting_for_access_token_.end(); |
116 ++iter) { | 119 ++iter) { |
117 DownloadState* download_state = *iter; | 120 DownloadState* download_state = *iter; |
118 scoped_refptr<base::RefCountedString> null_attachment_data; | 121 scoped_refptr<base::RefCountedString> null_attachment_data; |
119 ReportResult( | 122 ReportResult(*download_state, DOWNLOAD_TRANSIENT_ERROR, |
120 *download_state, DOWNLOAD_TRANSIENT_ERROR, null_attachment_data); | 123 null_attachment_data, 0); |
121 DCHECK(state_map_.find(download_state->attachment_url) != state_map_.end()); | 124 DCHECK(state_map_.find(download_state->attachment_url) != state_map_.end()); |
122 state_map_.erase(download_state->attachment_url); | 125 state_map_.erase(download_state->attachment_url); |
123 } | 126 } |
124 requests_waiting_for_access_token_.clear(); | 127 requests_waiting_for_access_token_.clear(); |
125 } | 128 } |
126 | 129 |
127 void AttachmentDownloaderImpl::OnURLFetchComplete( | 130 void AttachmentDownloaderImpl::OnURLFetchComplete( |
128 const net::URLFetcher* source) { | 131 const net::URLFetcher* source) { |
129 DCHECK(CalledOnValidThread()); | 132 DCHECK(CalledOnValidThread()); |
130 | 133 |
131 // Find DownloadState by url. | 134 // Find DownloadState by url. |
132 AttachmentUrl url = source->GetOriginalURL().spec(); | 135 AttachmentUrl url = source->GetOriginalURL().spec(); |
133 StateMap::iterator iter = state_map_.find(url); | 136 StateMap::iterator iter = state_map_.find(url); |
134 DCHECK(iter != state_map_.end()); | 137 DCHECK(iter != state_map_.end()); |
135 const DownloadState& download_state = *iter->second; | 138 const DownloadState& download_state = *iter->second; |
136 DCHECK(source == download_state.url_fetcher.get()); | 139 DCHECK(source == download_state.url_fetcher.get()); |
137 | 140 |
138 DownloadResult result = DOWNLOAD_TRANSIENT_ERROR; | 141 DownloadResult result = DOWNLOAD_TRANSIENT_ERROR; |
139 scoped_refptr<base::RefCountedString> attachment_data; | 142 scoped_refptr<base::RefCountedString> attachment_data; |
143 uint32_t attachment_crc = 0; | |
140 | 144 |
141 const int response_code = source->GetResponseCode(); | 145 const int response_code = source->GetResponseCode(); |
142 if (response_code == net::HTTP_OK) { | 146 if (response_code == net::HTTP_OK) { |
143 std::string data_as_string; | 147 std::string data_as_string; |
144 source->GetResponseAsString(&data_as_string); | 148 source->GetResponseAsString(&data_as_string); |
145 if (VerifyHashIfPresent(*source, data_as_string)) { | 149 attachment_data = base::RefCountedString::TakeString(&data_as_string); |
150 | |
151 attachment_crc = ComputeCrc32c(attachment_data); | |
152 uint32_t crc32c_from_headers = 0; | |
153 if (ExtractCrc32c(source->GetResponseHeaders(), &crc32c_from_headers) && | |
154 attachment_crc != crc32c_from_headers) { | |
155 // Fail download only if there is useful crc in header and it doesn't | |
maniscalco
2014/11/11 00:44:54
crc -> crc32c (and elsewhere in this comment)
pavely
2014/11/11 22:27:14
Done.
| |
156 // match data. All other cases are fine. When crc is not in headers | |
157 // locally calculated one will be stored and used for further checks. | |
maniscalco
2014/11/11 00:44:54
extra space between be and stored
pavely
2014/11/11 22:27:14
Done.
| |
158 result = DOWNLOAD_TRANSIENT_ERROR; | |
159 } else { | |
146 result = DOWNLOAD_SUCCESS; | 160 result = DOWNLOAD_SUCCESS; |
147 attachment_data = base::RefCountedString::TakeString(&data_as_string); | |
148 } else { | |
149 // TODO(maniscalco): Test me! | |
150 result = DOWNLOAD_TRANSIENT_ERROR; | |
151 } | 161 } |
152 } else if (response_code == net::HTTP_UNAUTHORIZED) { | 162 } else if (response_code == net::HTTP_UNAUTHORIZED) { |
153 // Server tells us we've got a bad token so invalidate it. | 163 // Server tells us we've got a bad token so invalidate it. |
154 OAuth2TokenServiceRequest::InvalidateToken(token_service_provider_.get(), | 164 OAuth2TokenServiceRequest::InvalidateToken(token_service_provider_.get(), |
155 account_id_, | 165 account_id_, |
156 oauth2_scopes_, | 166 oauth2_scopes_, |
157 download_state.access_token); | 167 download_state.access_token); |
158 // Fail the request, but indicate that it may be successful if retried. | 168 // Fail the request, but indicate that it may be successful if retried. |
159 result = DOWNLOAD_TRANSIENT_ERROR; | 169 result = DOWNLOAD_TRANSIENT_ERROR; |
160 } else if (response_code == net::HTTP_FORBIDDEN) { | 170 } else if (response_code == net::HTTP_FORBIDDEN) { |
161 // User is not allowed to use attachments. Retrying won't help. | 171 // User is not allowed to use attachments. Retrying won't help. |
162 result = DOWNLOAD_UNSPECIFIED_ERROR; | 172 result = DOWNLOAD_UNSPECIFIED_ERROR; |
163 } else if (response_code == net::URLFetcher::RESPONSE_CODE_INVALID) { | 173 } else if (response_code == net::URLFetcher::RESPONSE_CODE_INVALID) { |
164 result = DOWNLOAD_TRANSIENT_ERROR; | 174 result = DOWNLOAD_TRANSIENT_ERROR; |
165 } | 175 } |
166 ReportResult(download_state, result, attachment_data); | 176 ReportResult(download_state, result, attachment_data, attachment_crc); |
167 state_map_.erase(iter); | 177 state_map_.erase(iter); |
168 } | 178 } |
169 | 179 |
170 scoped_ptr<net::URLFetcher> AttachmentDownloaderImpl::CreateFetcher( | 180 scoped_ptr<net::URLFetcher> AttachmentDownloaderImpl::CreateFetcher( |
171 const AttachmentUrl& url, | 181 const AttachmentUrl& url, |
172 const std::string& access_token) { | 182 const std::string& access_token) { |
173 scoped_ptr<net::URLFetcher> url_fetcher( | 183 scoped_ptr<net::URLFetcher> url_fetcher( |
174 net::URLFetcher::Create(GURL(url), net::URLFetcher::GET, this)); | 184 net::URLFetcher::Create(GURL(url), net::URLFetcher::GET, this)); |
175 url_fetcher->SetAutomaticallyRetryOn5xx(false); | 185 url_fetcher->SetAutomaticallyRetryOn5xx(false); |
176 const std::string auth_header("Authorization: Bearer " + access_token); | 186 const std::string auth_header("Authorization: Bearer " + access_token); |
(...skipping 13 matching lines...) Expand all Loading... | |
190 // Start access token request if there is no active one. | 200 // Start access token request if there is no active one. |
191 if (access_token_request_ == NULL) { | 201 if (access_token_request_ == NULL) { |
192 access_token_request_ = OAuth2TokenServiceRequest::CreateAndStart( | 202 access_token_request_ = OAuth2TokenServiceRequest::CreateAndStart( |
193 token_service_provider_.get(), account_id_, oauth2_scopes_, this); | 203 token_service_provider_.get(), account_id_, oauth2_scopes_, this); |
194 } | 204 } |
195 } | 205 } |
196 | 206 |
197 void AttachmentDownloaderImpl::ReportResult( | 207 void AttachmentDownloaderImpl::ReportResult( |
198 const DownloadState& download_state, | 208 const DownloadState& download_state, |
199 const DownloadResult& result, | 209 const DownloadResult& result, |
200 const scoped_refptr<base::RefCountedString>& attachment_data) { | 210 const scoped_refptr<base::RefCountedString>& attachment_data, |
211 uint32_t attachment_crc) { | |
maniscalco
2014/11/11 00:44:54
crc -> crc32c (and elsewhere in this file)
pavely
2014/11/11 22:27:15
Done.
| |
201 std::vector<DownloadCallback>::const_iterator iter; | 212 std::vector<DownloadCallback>::const_iterator iter; |
202 for (iter = download_state.user_callbacks.begin(); | 213 for (iter = download_state.user_callbacks.begin(); |
203 iter != download_state.user_callbacks.end(); | 214 iter != download_state.user_callbacks.end(); |
204 ++iter) { | 215 ++iter) { |
205 scoped_ptr<Attachment> attachment; | 216 scoped_ptr<Attachment> attachment; |
206 if (result == DOWNLOAD_SUCCESS) { | 217 if (result == DOWNLOAD_SUCCESS) { |
207 attachment.reset(new Attachment(Attachment::CreateWithId( | 218 attachment.reset(new Attachment(Attachment::RestoreExisting( |
208 download_state.attachment_id, attachment_data))); | 219 download_state.attachment_id, attachment_data, attachment_crc))); |
209 } | 220 } |
210 | 221 |
211 base::MessageLoop::current()->PostTask( | 222 base::MessageLoop::current()->PostTask( |
212 FROM_HERE, base::Bind(*iter, result, base::Passed(&attachment))); | 223 FROM_HERE, base::Bind(*iter, result, base::Passed(&attachment))); |
213 } | 224 } |
214 } | 225 } |
215 | 226 |
216 bool AttachmentDownloaderImpl::VerifyHashIfPresent( | |
217 const net::URLFetcher& fetcher, | |
218 const std::string& data) { | |
219 const net::HttpResponseHeaders* headers = fetcher.GetResponseHeaders(); | |
220 if (!headers) { | |
221 // No headers? It passes. | |
222 return true; | |
223 } | |
224 | |
225 std::string value; | |
226 if (!ExtractCrc32c(*headers, &value)) { | |
227 // No crc32c? It passes. | |
228 return true; | |
229 } | |
230 | |
231 if (value == | |
232 AttachmentUploaderImpl::ComputeCrc32cHash(data.data(), data.size())) { | |
233 return true; | |
234 } else { | |
235 return false; | |
236 } | |
237 } | |
238 | |
239 bool AttachmentDownloaderImpl::ExtractCrc32c( | 227 bool AttachmentDownloaderImpl::ExtractCrc32c( |
240 const net::HttpResponseHeaders& headers, | 228 const net::HttpResponseHeaders* headers, |
241 std::string* crc32c) { | 229 uint32_t* crc32c) { |
242 DCHECK(crc32c); | 230 DCHECK(crc32c); |
231 if (!headers) { | |
232 return false; | |
233 } | |
234 | |
235 std::string crc32c_encoded; | |
243 std::string header_value; | 236 std::string header_value; |
244 void* iter = NULL; | 237 void* iter = NULL; |
245 // Iterate over all matching headers. | 238 // Iterate over all matching headers. |
246 while (headers.EnumerateHeader(&iter, "x-goog-hash", &header_value)) { | 239 while (headers->EnumerateHeader(&iter, "x-goog-hash", &header_value)) { |
247 // Because EnumerateHeader is smart about list values, header_value will | 240 // Because EnumerateHeader is smart about list values, header_value will |
248 // either be empty or a single name=value pair. | 241 // either be empty or a single name=value pair. |
249 net::HttpUtil::NameValuePairsIterator pair_iter( | 242 net::HttpUtil::NameValuePairsIterator pair_iter( |
250 header_value.begin(), header_value.end(), ','); | 243 header_value.begin(), header_value.end(), ','); |
251 if (pair_iter.GetNext()) { | 244 if (pair_iter.GetNext()) { |
252 if (pair_iter.name() == "crc32c") { | 245 if (pair_iter.name() == "crc32c") { |
253 *crc32c = pair_iter.value(); | 246 crc32c_encoded = pair_iter.value(); |
254 DCHECK(!pair_iter.GetNext()); | 247 DCHECK(!pair_iter.GetNext()); |
255 return true; | 248 break; |
256 } | 249 } |
257 } | 250 } |
258 } | 251 } |
252 // Check if header was found | |
253 if (crc32c_encoded.empty()) | |
254 return false; | |
255 std::string crc32c_raw; | |
256 if (!base::Base64Decode(crc32c_encoded, &crc32c_raw)) | |
257 return false; | |
259 | 258 |
260 return false; | 259 if (crc32c_raw.size() != sizeof(*crc32c)) |
260 return false; | |
261 | |
262 *crc32c = | |
263 base::NetToHost32(*reinterpret_cast<const uint32_t*>(crc32c_raw.c_str())); | |
264 return true; | |
261 } | 265 } |
262 | 266 |
263 } // namespace syncer | 267 } // namespace syncer |
OLD | NEW |