| 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 |
| 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. |
| 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) { |
| 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 |