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

Side by Side Diff: components/sync/core_impl/attachments/attachment_downloader_impl.cc

Issue 2399953002: [Sync] Move attachments code out of core/. (Closed)
Patch Set: Address comments. Created 4 years, 2 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
OLDNEW
(Empty)
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
3 // found in the LICENSE file.
4
5 #include "components/sync/core/attachments/attachment_downloader_impl.h"
6
7 #include <utility>
8
9 #include "base/base64.h"
10 #include "base/bind.h"
11 #include "base/location.h"
12 #include "base/metrics/histogram.h"
13 #include "base/metrics/histogram_macros.h"
14 #include "base/metrics/sparse_histogram.h"
15 #include "base/single_thread_task_runner.h"
16 #include "base/sys_byteorder.h"
17 #include "base/threading/thread_task_runner_handle.h"
18 #include "base/time/time.h"
19 #include "components/sync/core/attachments/attachment_uploader_impl.h"
20 #include "components/sync/core/attachments/attachment_util.h"
21 #include "components/sync/protocol/sync.pb.h"
22 #include "net/base/load_flags.h"
23 #include "net/http/http_response_headers.h"
24 #include "net/http/http_status_code.h"
25 #include "net/http/http_util.h"
26 #include "net/url_request/url_fetcher.h"
27 #include "net/url_request/url_request_status.h"
28
29 namespace syncer {
30
31 struct AttachmentDownloaderImpl::DownloadState {
32 public:
33 DownloadState(const AttachmentId& attachment_id,
34 const AttachmentUrl& attachment_url);
35
36 AttachmentId attachment_id;
37 AttachmentUrl attachment_url;
38 // |access_token| needed to invalidate if downloading attachment fails with
39 // HTTP_UNAUTHORIZED.
40 std::string access_token;
41 std::unique_ptr<net::URLFetcher> url_fetcher;
42 std::vector<DownloadCallback> user_callbacks;
43 base::TimeTicks start_time;
44 };
45
46 AttachmentDownloaderImpl::DownloadState::DownloadState(
47 const AttachmentId& attachment_id,
48 const AttachmentUrl& attachment_url)
49 : attachment_id(attachment_id), attachment_url(attachment_url) {}
50
51 AttachmentDownloaderImpl::AttachmentDownloaderImpl(
52 const GURL& sync_service_url,
53 const scoped_refptr<net::URLRequestContextGetter>&
54 url_request_context_getter,
55 const std::string& account_id,
56 const OAuth2TokenService::ScopeSet& scopes,
57 const scoped_refptr<OAuth2TokenServiceRequest::TokenServiceProvider>&
58 token_service_provider,
59 const std::string& store_birthday,
60 ModelType model_type)
61 : OAuth2TokenService::Consumer("attachment-downloader-impl"),
62 sync_service_url_(sync_service_url),
63 url_request_context_getter_(url_request_context_getter),
64 account_id_(account_id),
65 oauth2_scopes_(scopes),
66 token_service_provider_(token_service_provider),
67 raw_store_birthday_(store_birthday),
68 model_type_(model_type) {
69 DCHECK(url_request_context_getter_.get());
70 DCHECK(!account_id.empty());
71 DCHECK(!scopes.empty());
72 DCHECK(token_service_provider_.get());
73 DCHECK(!raw_store_birthday_.empty());
74 }
75
76 AttachmentDownloaderImpl::~AttachmentDownloaderImpl() {}
77
78 void AttachmentDownloaderImpl::DownloadAttachment(
79 const AttachmentId& attachment_id,
80 const DownloadCallback& callback) {
81 DCHECK(CalledOnValidThread());
82
83 AttachmentUrl url = AttachmentUploaderImpl::GetURLForAttachmentId(
84 sync_service_url_, attachment_id)
85 .spec();
86
87 StateMap::iterator iter = state_map_.find(url);
88 DownloadState* download_state =
89 iter != state_map_.end() ? iter->second.get() : nullptr;
90 if (!download_state) {
91 // There is no request started for this attachment id. Let's create
92 // DownloadState and request access token for it.
93 std::unique_ptr<DownloadState> new_download_state(
94 new DownloadState(attachment_id, url));
95 download_state = new_download_state.get();
96 state_map_[url] = std::move(new_download_state);
97 RequestAccessToken(download_state);
98 }
99 DCHECK(download_state->attachment_id == attachment_id);
100 download_state->user_callbacks.push_back(callback);
101 }
102
103 void AttachmentDownloaderImpl::OnGetTokenSuccess(
104 const OAuth2TokenService::Request* request,
105 const std::string& access_token,
106 const base::Time& expiration_time) {
107 DCHECK(CalledOnValidThread());
108 DCHECK(request == access_token_request_.get());
109 access_token_request_.reset();
110 StateList::const_iterator iter;
111 // Start downloads for all download requests waiting for access token.
112 for (iter = requests_waiting_for_access_token_.begin();
113 iter != requests_waiting_for_access_token_.end(); ++iter) {
114 DownloadState* download_state = *iter;
115 download_state->access_token = access_token;
116 download_state->url_fetcher =
117 CreateFetcher(download_state->attachment_url, access_token);
118 download_state->start_time = base::TimeTicks::Now();
119 download_state->url_fetcher->Start();
120 }
121 requests_waiting_for_access_token_.clear();
122 }
123
124 void AttachmentDownloaderImpl::OnGetTokenFailure(
125 const OAuth2TokenService::Request* request,
126 const GoogleServiceAuthError& error) {
127 DCHECK(CalledOnValidThread());
128 DCHECK(request == access_token_request_.get());
129 access_token_request_.reset();
130 StateList::const_iterator iter;
131 // Without access token all downloads fail.
132 for (iter = requests_waiting_for_access_token_.begin();
133 iter != requests_waiting_for_access_token_.end(); ++iter) {
134 DownloadState* download_state = *iter;
135 scoped_refptr<base::RefCountedString> null_attachment_data;
136 ReportResult(*download_state, DOWNLOAD_TRANSIENT_ERROR,
137 null_attachment_data);
138 // Don't delete using the URL directly to avoid an access after free error
139 // due to std::unordered_map's implementation. See crbug.com/603275.
140 auto erase_iter = state_map_.find(download_state->attachment_url);
141 DCHECK(erase_iter != state_map_.end());
142 state_map_.erase(erase_iter);
143 }
144 requests_waiting_for_access_token_.clear();
145 }
146
147 void AttachmentDownloaderImpl::OnURLFetchComplete(
148 const net::URLFetcher* source) {
149 DCHECK(CalledOnValidThread());
150
151 // Find DownloadState by url.
152 AttachmentUrl url = source->GetOriginalURL().spec();
153 StateMap::iterator iter = state_map_.find(url);
154 DCHECK(iter != state_map_.end());
155 const DownloadState& download_state = *iter->second;
156 DCHECK(source == download_state.url_fetcher.get());
157
158 DownloadResult result = DOWNLOAD_TRANSIENT_ERROR;
159 scoped_refptr<base::RefCountedString> attachment_data;
160 uint32_t attachment_crc32c = 0;
161
162 net::URLRequestStatus status = source->GetStatus();
163 const int response_code = source->GetResponseCode();
164 UMA_HISTOGRAM_SPARSE_SLOWLY(
165 "Sync.Attachments.DownloadResponseCode",
166 status.is_success() ? response_code : status.error());
167 if (response_code == net::HTTP_OK) {
168 std::string data_as_string;
169 source->GetResponseAsString(&data_as_string);
170 attachment_data = base::RefCountedString::TakeString(&data_as_string);
171
172 UMA_HISTOGRAM_LONG_TIMES(
173 "Sync.Attachments.DownloadTotalTime",
174 base::TimeTicks::Now() - download_state.start_time);
175
176 attachment_crc32c = ComputeCrc32c(attachment_data);
177 uint32_t crc32c_from_headers = 0;
178 if (ExtractCrc32c(source->GetResponseHeaders(), &crc32c_from_headers) &&
179 attachment_crc32c != crc32c_from_headers) {
180 // Fail download only if there is useful crc32c in header and it doesn't
181 // match data. All other cases are fine. When crc32c is not in headers
182 // locally calculated one will be stored and used for further checks.
183 result = DOWNLOAD_TRANSIENT_ERROR;
184 } else {
185 // If the id's crc32c doesn't match that of the downloaded attachment,
186 // then we're stuck and retrying is unlikely to help.
187 if (attachment_crc32c != download_state.attachment_id.GetCrc32c()) {
188 result = DOWNLOAD_UNSPECIFIED_ERROR;
189 } else {
190 result = DOWNLOAD_SUCCESS;
191 }
192 }
193 UMA_HISTOGRAM_BOOLEAN("Sync.Attachments.DownloadChecksumResult",
194 result == DOWNLOAD_SUCCESS);
195 } else if (response_code == net::HTTP_UNAUTHORIZED) {
196 // Server tells us we've got a bad token so invalidate it.
197 OAuth2TokenServiceRequest::InvalidateToken(token_service_provider_.get(),
198 account_id_, oauth2_scopes_,
199 download_state.access_token);
200 // Fail the request, but indicate that it may be successful if retried.
201 result = DOWNLOAD_TRANSIENT_ERROR;
202 } else if (response_code == net::HTTP_FORBIDDEN) {
203 // User is not allowed to use attachments. Retrying won't help.
204 result = DOWNLOAD_UNSPECIFIED_ERROR;
205 } else if (response_code == net::URLFetcher::RESPONSE_CODE_INVALID) {
206 result = DOWNLOAD_TRANSIENT_ERROR;
207 }
208 ReportResult(download_state, result, attachment_data);
209 state_map_.erase(iter);
210 }
211
212 std::unique_ptr<net::URLFetcher> AttachmentDownloaderImpl::CreateFetcher(
213 const AttachmentUrl& url,
214 const std::string& access_token) {
215 std::unique_ptr<net::URLFetcher> url_fetcher =
216 net::URLFetcher::Create(GURL(url), net::URLFetcher::GET, this);
217 AttachmentUploaderImpl::ConfigureURLFetcherCommon(
218 url_fetcher.get(), access_token, raw_store_birthday_, model_type_,
219 url_request_context_getter_.get());
220 return url_fetcher;
221 }
222
223 void AttachmentDownloaderImpl::RequestAccessToken(
224 DownloadState* download_state) {
225 requests_waiting_for_access_token_.push_back(download_state);
226 // Start access token request if there is no active one.
227 if (access_token_request_ == NULL) {
228 access_token_request_ = OAuth2TokenServiceRequest::CreateAndStart(
229 token_service_provider_.get(), account_id_, oauth2_scopes_, this);
230 }
231 }
232
233 void AttachmentDownloaderImpl::ReportResult(
234 const DownloadState& download_state,
235 const DownloadResult& result,
236 const scoped_refptr<base::RefCountedString>& attachment_data) {
237 std::vector<DownloadCallback>::const_iterator iter;
238 for (iter = download_state.user_callbacks.begin();
239 iter != download_state.user_callbacks.end(); ++iter) {
240 std::unique_ptr<Attachment> attachment;
241 if (result == DOWNLOAD_SUCCESS) {
242 attachment.reset(new Attachment(Attachment::CreateFromParts(
243 download_state.attachment_id, attachment_data)));
244 }
245
246 base::ThreadTaskRunnerHandle::Get()->PostTask(
247 FROM_HERE, base::Bind(*iter, result, base::Passed(&attachment)));
248 }
249 }
250
251 bool AttachmentDownloaderImpl::ExtractCrc32c(
252 const net::HttpResponseHeaders* headers,
253 uint32_t* crc32c) {
254 DCHECK(crc32c);
255 if (!headers) {
256 return false;
257 }
258
259 std::string crc32c_encoded;
260 std::string header_value;
261 size_t iter = 0;
262 // Iterate over all matching headers.
263 while (headers->EnumerateHeader(&iter, "x-goog-hash", &header_value)) {
264 // Because EnumerateHeader is smart about list values, header_value will
265 // either be empty or a single name=value pair.
266 net::HttpUtil::NameValuePairsIterator pair_iter(header_value.begin(),
267 header_value.end(), ',');
268 if (pair_iter.GetNext()) {
269 if (pair_iter.name() == "crc32c") {
270 crc32c_encoded = pair_iter.value();
271 DCHECK(!pair_iter.GetNext());
272 break;
273 }
274 }
275 }
276 // Check if header was found
277 if (crc32c_encoded.empty())
278 return false;
279 std::string crc32c_raw;
280 if (!base::Base64Decode(crc32c_encoded, &crc32c_raw))
281 return false;
282
283 if (crc32c_raw.size() != sizeof(*crc32c))
284 return false;
285
286 *crc32c =
287 base::NetToHost32(*reinterpret_cast<const uint32_t*>(crc32c_raw.c_str()));
288 return true;
289 }
290
291 } // namespace syncer
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698