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

Side by Side Diff: sync/internal_api/attachments/attachment_uploader_impl.cc

Issue 2130453004: [Sync] Move //sync to //components/sync. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Rebase. Created 4 years, 4 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 "sync/internal_api/public/attachments/attachment_uploader_impl.h"
6
7 #include <stdint.h>
8 #include <string>
9 #include <utility>
10 #include <vector>
11
12 #include "base/base64.h"
13 #include "base/base64url.h"
14 #include "base/bind.h"
15 #include "base/location.h"
16 #include "base/macros.h"
17 #include "base/memory/weak_ptr.h"
18 #include "base/metrics/sparse_histogram.h"
19 #include "base/single_thread_task_runner.h"
20 #include "base/strings/string_piece.h"
21 #include "base/strings/stringprintf.h"
22 #include "base/sys_byteorder.h"
23 #include "base/threading/non_thread_safe.h"
24 #include "base/threading/thread_task_runner_handle.h"
25 #include "google_apis/gaia/gaia_constants.h"
26 #include "net/base/load_flags.h"
27 #include "net/http/http_status_code.h"
28 #include "net/url_request/url_fetcher.h"
29 #include "net/url_request/url_fetcher_delegate.h"
30 #include "net/url_request/url_request_status.h"
31 #include "sync/api/attachments/attachment.h"
32 #include "sync/protocol/sync.pb.h"
33
34 namespace {
35
36 const char kContentType[] = "application/octet-stream";
37 const char kAttachments[] = "attachments/";
38 const char kSyncStoreBirthday[] = "X-Sync-Store-Birthday";
39 const char kSyncDataTypeId[] = "X-Sync-Data-Type-Id";
40
41 } // namespace
42
43 namespace syncer {
44
45 // Encapsulates all the state associated with a single upload.
46 class AttachmentUploaderImpl::UploadState : public net::URLFetcherDelegate,
47 public OAuth2TokenService::Consumer,
48 public base::NonThreadSafe {
49 public:
50 // Construct an UploadState.
51 //
52 // UploadState encapsulates the state associated with a single upload. When
53 // the upload completes, the UploadState object becomes "stopped".
54 //
55 // |owner| is a pointer to the object that owns this UploadState. Upon
56 // completion this object will PostTask to owner's OnUploadStateStopped
57 // method.
58 UploadState(
59 const GURL& upload_url,
60 const scoped_refptr<net::URLRequestContextGetter>&
61 url_request_context_getter,
62 const Attachment& attachment,
63 const UploadCallback& user_callback,
64 const std::string& account_id,
65 const OAuth2TokenService::ScopeSet& scopes,
66 OAuth2TokenServiceRequest::TokenServiceProvider* token_service_provider,
67 const std::string& raw_store_birthday,
68 const base::WeakPtr<AttachmentUploaderImpl>& owner,
69 ModelType model_type);
70
71 ~UploadState() override;
72
73 // Returns true if this object is stopped. Once stopped, this object is
74 // effectively dead and can be destroyed.
75 bool IsStopped() const;
76
77 // Add |user_callback| to the list of callbacks to be invoked when this upload
78 // completed.
79 //
80 // It is an error to call |AddUserCallback| on a stopped UploadState (see
81 // |IsStopped|).
82 void AddUserCallback(const UploadCallback& user_callback);
83
84 // Return the Attachment this object is uploading.
85 const Attachment& GetAttachment();
86
87 // URLFetcher implementation.
88 void OnURLFetchComplete(const net::URLFetcher* source) override;
89
90 // OAuth2TokenService::Consumer.
91 void OnGetTokenSuccess(const OAuth2TokenService::Request* request,
92 const std::string& access_token,
93 const base::Time& expiration_time) override;
94 void OnGetTokenFailure(const OAuth2TokenService::Request* request,
95 const GoogleServiceAuthError& error) override;
96
97 private:
98 typedef std::vector<UploadCallback> UploadCallbackList;
99
100 void GetToken();
101
102 void StopAndReportResult(const UploadResult& result,
103 const AttachmentId& attachment_id);
104
105 bool is_stopped_;
106 GURL upload_url_;
107 const scoped_refptr<net::URLRequestContextGetter>&
108 url_request_context_getter_;
109 Attachment attachment_;
110 UploadCallbackList user_callbacks_;
111 std::unique_ptr<net::URLFetcher> fetcher_;
112 std::string account_id_;
113 OAuth2TokenService::ScopeSet scopes_;
114 std::string access_token_;
115 std::string raw_store_birthday_;
116 OAuth2TokenServiceRequest::TokenServiceProvider* token_service_provider_;
117 // Pointer to the AttachmentUploaderImpl that owns this object.
118 base::WeakPtr<AttachmentUploaderImpl> owner_;
119 std::unique_ptr<OAuth2TokenServiceRequest> access_token_request_;
120 ModelType model_type_;
121
122 DISALLOW_COPY_AND_ASSIGN(UploadState);
123 };
124
125 AttachmentUploaderImpl::UploadState::UploadState(
126 const GURL& upload_url,
127 const scoped_refptr<net::URLRequestContextGetter>&
128 url_request_context_getter,
129 const Attachment& attachment,
130 const UploadCallback& user_callback,
131 const std::string& account_id,
132 const OAuth2TokenService::ScopeSet& scopes,
133 OAuth2TokenServiceRequest::TokenServiceProvider* token_service_provider,
134 const std::string& raw_store_birthday,
135 const base::WeakPtr<AttachmentUploaderImpl>& owner,
136 ModelType model_type)
137 : OAuth2TokenService::Consumer("attachment-uploader-impl"),
138 is_stopped_(false),
139 upload_url_(upload_url),
140 url_request_context_getter_(url_request_context_getter),
141 attachment_(attachment),
142 user_callbacks_(1, user_callback),
143 account_id_(account_id),
144 scopes_(scopes),
145 raw_store_birthday_(raw_store_birthday),
146 token_service_provider_(token_service_provider),
147 owner_(owner),
148 model_type_(model_type) {
149 DCHECK(upload_url_.is_valid());
150 DCHECK(url_request_context_getter_.get());
151 DCHECK(!account_id_.empty());
152 DCHECK(!scopes_.empty());
153 DCHECK(token_service_provider_);
154 DCHECK(!raw_store_birthday_.empty());
155 GetToken();
156 }
157
158 AttachmentUploaderImpl::UploadState::~UploadState() {
159 }
160
161 bool AttachmentUploaderImpl::UploadState::IsStopped() const {
162 DCHECK(CalledOnValidThread());
163 return is_stopped_;
164 }
165
166 void AttachmentUploaderImpl::UploadState::AddUserCallback(
167 const UploadCallback& user_callback) {
168 DCHECK(CalledOnValidThread());
169 DCHECK(!is_stopped_);
170 user_callbacks_.push_back(user_callback);
171 }
172
173 const Attachment& AttachmentUploaderImpl::UploadState::GetAttachment() {
174 DCHECK(CalledOnValidThread());
175 return attachment_;
176 }
177
178 void AttachmentUploaderImpl::UploadState::OnURLFetchComplete(
179 const net::URLFetcher* source) {
180 DCHECK(CalledOnValidThread());
181 if (is_stopped_) {
182 return;
183 }
184
185 UploadResult result = UPLOAD_TRANSIENT_ERROR;
186 AttachmentId attachment_id = attachment_.GetId();
187 net::URLRequestStatus status = source->GetStatus();
188 const int response_code = source->GetResponseCode();
189 UMA_HISTOGRAM_SPARSE_SLOWLY("Sync.Attachments.UploadResponseCode",
190 status.is_success() ? response_code : status.error());
191 if (response_code == net::HTTP_OK) {
192 result = UPLOAD_SUCCESS;
193 } else if (response_code == net::HTTP_UNAUTHORIZED) {
194 // Server tells us we've got a bad token so invalidate it.
195 OAuth2TokenServiceRequest::InvalidateToken(
196 token_service_provider_, account_id_, scopes_, access_token_);
197 // Fail the request, but indicate that it may be successful if retried.
198 result = UPLOAD_TRANSIENT_ERROR;
199 } else if (response_code == net::HTTP_FORBIDDEN) {
200 // User is not allowed to use attachments. Retrying won't help.
201 result = UPLOAD_UNSPECIFIED_ERROR;
202 } else if (response_code == net::URLFetcher::RESPONSE_CODE_INVALID) {
203 result = UPLOAD_TRANSIENT_ERROR;
204 }
205 StopAndReportResult(result, attachment_id);
206 }
207
208 void AttachmentUploaderImpl::UploadState::OnGetTokenSuccess(
209 const OAuth2TokenService::Request* request,
210 const std::string& access_token,
211 const base::Time& expiration_time) {
212 DCHECK(CalledOnValidThread());
213 if (is_stopped_) {
214 return;
215 }
216
217 DCHECK_EQ(access_token_request_.get(), request);
218 access_token_request_.reset();
219 access_token_ = access_token;
220 fetcher_ = net::URLFetcher::Create(upload_url_, net::URLFetcher::POST, this);
221 ConfigureURLFetcherCommon(fetcher_.get(), access_token_, raw_store_birthday_,
222 model_type_, url_request_context_getter_.get());
223
224 const uint32_t crc32c = attachment_.GetCrc32c();
225 fetcher_->AddExtraRequestHeader(base::StringPrintf(
226 "X-Goog-Hash: crc32c=%s", FormatCrc32cHash(crc32c).c_str()));
227
228 // TODO(maniscalco): Is there a better way? Copying the attachment data into
229 // a string feels wrong given how large attachments may be (several MBs). If
230 // we may end up switching from URLFetcher to URLRequest, this copy won't be
231 // necessary.
232 scoped_refptr<base::RefCountedMemory> memory = attachment_.GetData();
233 const std::string upload_content(memory->front_as<char>(), memory->size());
234 fetcher_->SetUploadData(kContentType, upload_content);
235
236 fetcher_->Start();
237 }
238
239 void AttachmentUploaderImpl::UploadState::OnGetTokenFailure(
240 const OAuth2TokenService::Request* request,
241 const GoogleServiceAuthError& error) {
242 DCHECK(CalledOnValidThread());
243 if (is_stopped_) {
244 return;
245 }
246
247 DCHECK_EQ(access_token_request_.get(), request);
248 access_token_request_.reset();
249 // TODO(maniscalco): We treat this as a transient error, but it may in fact be
250 // a very long lived error and require user action. Consider differentiating
251 // between the causes of GetToken failure and act accordingly. Think about
252 // the causes of GetToken failure. Are there (bug 412802).
253 StopAndReportResult(UPLOAD_TRANSIENT_ERROR, attachment_.GetId());
254 }
255
256 void AttachmentUploaderImpl::UploadState::GetToken() {
257 access_token_request_ = OAuth2TokenServiceRequest::CreateAndStart(
258 token_service_provider_, account_id_, scopes_, this);
259 }
260
261 void AttachmentUploaderImpl::UploadState::StopAndReportResult(
262 const UploadResult& result,
263 const AttachmentId& attachment_id) {
264 DCHECK(!is_stopped_);
265 is_stopped_ = true;
266 UploadCallbackList::const_iterator iter = user_callbacks_.begin();
267 UploadCallbackList::const_iterator end = user_callbacks_.end();
268 for (; iter != end; ++iter) {
269 base::ThreadTaskRunnerHandle::Get()->PostTask(
270 FROM_HERE, base::Bind(*iter, result, attachment_id));
271 }
272 base::ThreadTaskRunnerHandle::Get()->PostTask(
273 FROM_HERE, base::Bind(&AttachmentUploaderImpl::OnUploadStateStopped,
274 owner_, attachment_id.GetProto().unique_id()));
275 }
276
277 AttachmentUploaderImpl::AttachmentUploaderImpl(
278 const GURL& sync_service_url,
279 const scoped_refptr<net::URLRequestContextGetter>&
280 url_request_context_getter,
281 const std::string& account_id,
282 const OAuth2TokenService::ScopeSet& scopes,
283 const scoped_refptr<OAuth2TokenServiceRequest::TokenServiceProvider>&
284 token_service_provider,
285 const std::string& store_birthday,
286 ModelType model_type)
287 : sync_service_url_(sync_service_url),
288 url_request_context_getter_(url_request_context_getter),
289 account_id_(account_id),
290 scopes_(scopes),
291 token_service_provider_(token_service_provider),
292 raw_store_birthday_(store_birthday),
293 model_type_(model_type),
294 weak_ptr_factory_(this) {
295 DCHECK(CalledOnValidThread());
296 DCHECK(!account_id.empty());
297 DCHECK(!scopes.empty());
298 DCHECK(token_service_provider_.get());
299 DCHECK(!raw_store_birthday_.empty());
300 }
301
302 AttachmentUploaderImpl::~AttachmentUploaderImpl() {
303 DCHECK(CalledOnValidThread());
304 }
305
306 void AttachmentUploaderImpl::UploadAttachment(const Attachment& attachment,
307 const UploadCallback& callback) {
308 DCHECK(CalledOnValidThread());
309 const AttachmentId attachment_id = attachment.GetId();
310 const std::string unique_id = attachment_id.GetProto().unique_id();
311 DCHECK(!unique_id.empty());
312 StateMap::iterator iter = state_map_.find(unique_id);
313 if (iter != state_map_.end()) {
314 // We have an old upload request for this attachment...
315 if (!iter->second->IsStopped()) {
316 // "join" to it.
317 DCHECK(attachment.GetData()
318 ->Equals(iter->second->GetAttachment().GetData()));
319 iter->second->AddUserCallback(callback);
320 return;
321 } else {
322 // It's stopped so we can't use it. Delete it.
323 state_map_.erase(iter);
324 }
325 }
326
327 const GURL url = GetURLForAttachmentId(sync_service_url_, attachment_id);
328 std::unique_ptr<UploadState> upload_state(new UploadState(
329 url, url_request_context_getter_, attachment, callback, account_id_,
330 scopes_, token_service_provider_.get(), raw_store_birthday_,
331 weak_ptr_factory_.GetWeakPtr(), model_type_));
332 state_map_[unique_id] = std::move(upload_state);
333 }
334
335 // Static.
336 GURL AttachmentUploaderImpl::GetURLForAttachmentId(
337 const GURL& sync_service_url,
338 const AttachmentId& attachment_id) {
339 std::string path = sync_service_url.path();
340 if (path.empty() || *path.rbegin() != '/') {
341 path += '/';
342 }
343 path += kAttachments;
344 path += attachment_id.GetProto().unique_id();
345 GURL::Replacements replacements;
346 replacements.SetPathStr(path);
347 return sync_service_url.ReplaceComponents(replacements);
348 }
349
350 void AttachmentUploaderImpl::OnUploadStateStopped(const UniqueId& unique_id) {
351 StateMap::iterator iter = state_map_.find(unique_id);
352 // Only erase if stopped. Because this method is called asynchronously, it's
353 // possible that a new request for this same id arrived after the UploadState
354 // stopped, but before this method was invoked. In that case the UploadState
355 // in the map might be a new one.
356 if (iter != state_map_.end() && iter->second->IsStopped()) {
357 state_map_.erase(iter);
358 }
359 }
360
361 std::string AttachmentUploaderImpl::FormatCrc32cHash(uint32_t crc32c) {
362 const uint32_t crc32c_big_endian = base::HostToNet32(crc32c);
363 const base::StringPiece raw(reinterpret_cast<const char*>(&crc32c_big_endian),
364 sizeof(crc32c_big_endian));
365 std::string encoded;
366 base::Base64Encode(raw, &encoded);
367 return encoded;
368 }
369
370 void AttachmentUploaderImpl::ConfigureURLFetcherCommon(
371 net::URLFetcher* fetcher,
372 const std::string& access_token,
373 const std::string& raw_store_birthday,
374 ModelType model_type,
375 net::URLRequestContextGetter* request_context_getter) {
376 DCHECK(request_context_getter);
377 DCHECK(fetcher);
378 fetcher->SetAutomaticallyRetryOn5xx(false);
379 fetcher->SetRequestContext(request_context_getter);
380 fetcher->SetLoadFlags(net::LOAD_DO_NOT_SAVE_COOKIES |
381 net::LOAD_DO_NOT_SEND_COOKIES |
382 net::LOAD_DISABLE_CACHE);
383 fetcher->AddExtraRequestHeader(base::StringPrintf(
384 "%s: Bearer %s", net::HttpRequestHeaders::kAuthorization,
385 access_token.c_str()));
386 // Encode the birthday. Birthday is opaque so we assume it could contain
387 // anything. Encode it so that it's safe to pass as an HTTP header value.
388 std::string encoded_store_birthday;
389 base::Base64UrlEncode(
390 raw_store_birthday, base::Base64UrlEncodePolicy::OMIT_PADDING,
391 &encoded_store_birthday);
392 fetcher->AddExtraRequestHeader(base::StringPrintf(
393 "%s: %s", kSyncStoreBirthday, encoded_store_birthday.c_str()));
394
395 // Use field number to pass ModelType because it's stable and we have server
396 // code to decode it.
397 const int field_number = GetSpecificsFieldNumberFromModelType(model_type);
398 fetcher->AddExtraRequestHeader(
399 base::StringPrintf("%s: %d", kSyncDataTypeId, field_number));
400 }
401
402 } // namespace syncer
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698