Updating trunk VERSION from 2139.0 to 2140.0
[chromium-blink-merge.git] / sync / internal_api / attachments / attachment_uploader_impl.cc
blob4d197eb6da7edb9d4114c7111c77b0eafe15aca3
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.
5 #include "sync/internal_api/public/attachments/attachment_uploader_impl.h"
7 #include "base/bind.h"
8 #include "base/message_loop/message_loop.h"
9 #include "base/threading/non_thread_safe.h"
10 #include "google_apis/gaia/gaia_constants.h"
11 #include "net/base/load_flags.h"
12 #include "net/http/http_status_code.h"
13 #include "net/url_request/url_fetcher.h"
14 #include "net/url_request/url_fetcher_delegate.h"
15 #include "sync/api/attachments/attachment.h"
16 #include "sync/protocol/sync.pb.h"
18 namespace {
20 const char kContentType[] = "application/octet-stream";
21 const char kAttachments[] = "attachments/";
23 } // namespace
25 namespace syncer {
27 // Encapsulates all the state associated with a single upload.
28 class AttachmentUploaderImpl::UploadState : public net::URLFetcherDelegate,
29 public OAuth2TokenService::Consumer,
30 public base::NonThreadSafe {
31 public:
32 // Construct an UploadState.
34 // |owner| is a pointer to the object that will own (and must outlive!) this
35 // |UploadState.
36 UploadState(
37 const GURL& upload_url,
38 const scoped_refptr<net::URLRequestContextGetter>&
39 url_request_context_getter,
40 const Attachment& attachment,
41 const UploadCallback& user_callback,
42 const std::string& account_id,
43 const OAuth2TokenService::ScopeSet& scopes,
44 OAuth2TokenServiceRequest::TokenServiceProvider* token_service_provider,
45 AttachmentUploaderImpl* owner);
47 virtual ~UploadState();
49 // Add |user_callback| to the list of callbacks to be invoked when this upload
50 // completed.
51 void AddUserCallback(const UploadCallback& user_callback);
53 // Return the Attachment this object is uploading.
54 const Attachment& GetAttachment();
56 // URLFetcher implementation.
57 virtual void OnURLFetchComplete(const net::URLFetcher* source) OVERRIDE;
59 // OAuth2TokenService::Consumer.
60 virtual void OnGetTokenSuccess(const OAuth2TokenService::Request* request,
61 const std::string& access_token,
62 const base::Time& expiration_time) OVERRIDE;
63 virtual void OnGetTokenFailure(const OAuth2TokenService::Request* request,
64 const GoogleServiceAuthError& error) OVERRIDE;
66 private:
67 typedef std::vector<UploadCallback> UploadCallbackList;
69 void GetToken();
71 void ReportResult(const UploadResult& result,
72 const AttachmentId& attachment_id);
74 GURL upload_url_;
75 const scoped_refptr<net::URLRequestContextGetter>&
76 url_request_context_getter_;
77 Attachment attachment_;
78 UploadCallbackList user_callbacks_;
79 scoped_ptr<net::URLFetcher> fetcher_;
80 std::string account_id_;
81 OAuth2TokenService::ScopeSet scopes_;
82 std::string access_token_;
83 OAuth2TokenServiceRequest::TokenServiceProvider* token_service_provider_;
84 // Pointer to the AttachmentUploaderImpl that owns this object.
85 AttachmentUploaderImpl* owner_;
86 scoped_ptr<OAuth2TokenServiceRequest> access_token_request_;
88 DISALLOW_COPY_AND_ASSIGN(UploadState);
91 AttachmentUploaderImpl::UploadState::UploadState(
92 const GURL& upload_url,
93 const scoped_refptr<net::URLRequestContextGetter>&
94 url_request_context_getter,
95 const Attachment& attachment,
96 const UploadCallback& user_callback,
97 const std::string& account_id,
98 const OAuth2TokenService::ScopeSet& scopes,
99 OAuth2TokenServiceRequest::TokenServiceProvider* token_service_provider,
100 AttachmentUploaderImpl* owner)
101 : OAuth2TokenService::Consumer("attachment-uploader-impl"),
102 upload_url_(upload_url),
103 url_request_context_getter_(url_request_context_getter),
104 attachment_(attachment),
105 user_callbacks_(1, user_callback),
106 account_id_(account_id),
107 scopes_(scopes),
108 token_service_provider_(token_service_provider),
109 owner_(owner) {
110 DCHECK(upload_url_.is_valid());
111 DCHECK(url_request_context_getter_.get());
112 DCHECK(!account_id_.empty());
113 DCHECK(!scopes_.empty());
114 DCHECK(token_service_provider_);
115 DCHECK(owner_);
116 GetToken();
119 AttachmentUploaderImpl::UploadState::~UploadState() {
122 void AttachmentUploaderImpl::UploadState::AddUserCallback(
123 const UploadCallback& user_callback) {
124 DCHECK(CalledOnValidThread());
125 user_callbacks_.push_back(user_callback);
128 const Attachment& AttachmentUploaderImpl::UploadState::GetAttachment() {
129 DCHECK(CalledOnValidThread());
130 return attachment_;
133 void AttachmentUploaderImpl::UploadState::OnURLFetchComplete(
134 const net::URLFetcher* source) {
135 DCHECK(CalledOnValidThread());
136 UploadResult result = UPLOAD_UNSPECIFIED_ERROR;
137 AttachmentId attachment_id = attachment_.GetId();
138 if (source->GetResponseCode() == net::HTTP_OK) {
139 result = UPLOAD_SUCCESS;
140 } else if (source->GetResponseCode() == net::HTTP_UNAUTHORIZED) {
141 // TODO(maniscalco): One possibility is that we received a 401 because our
142 // access token has expired. We should probably fetch a new access token
143 // and retry this upload before giving up and reporting failure to our
144 // caller (bug 380437).
145 OAuth2TokenServiceRequest::InvalidateToken(
146 token_service_provider_, account_id_, scopes_, access_token_);
147 } else {
148 // TODO(maniscalco): Once the protocol is better defined, deal with the
149 // various HTTP response codes we may encounter.
151 ReportResult(result, attachment_id);
154 void AttachmentUploaderImpl::UploadState::OnGetTokenSuccess(
155 const OAuth2TokenService::Request* request,
156 const std::string& access_token,
157 const base::Time& expiration_time) {
158 DCHECK_EQ(access_token_request_.get(), request);
159 access_token_request_.reset();
160 access_token_ = access_token;
161 fetcher_.reset(
162 net::URLFetcher::Create(upload_url_, net::URLFetcher::POST, this));
163 fetcher_->SetRequestContext(url_request_context_getter_.get());
164 // TODO(maniscalco): Is there a better way? Copying the attachment data into
165 // a string feels wrong given how large attachments may be (several MBs). If
166 // we may end up switching from URLFetcher to URLRequest, this copy won't be
167 // necessary.
168 scoped_refptr<base::RefCountedMemory> memory = attachment_.GetData();
169 const std::string upload_content(memory->front_as<char>(), memory->size());
170 fetcher_->SetUploadData(kContentType, upload_content);
171 const std::string auth_header("Authorization: Bearer " + access_token_);
172 fetcher_->AddExtraRequestHeader(auth_header);
173 fetcher_->SetLoadFlags(net::LOAD_DO_NOT_SAVE_COOKIES |
174 net::LOAD_DO_NOT_SEND_COOKIES |
175 net::LOAD_DISABLE_CACHE);
176 // TODO(maniscalco): Set an appropriate headers (User-Agent, Content-type, and
177 // Content-length) on the request and include the content's MD5,
178 // AttachmentId's unique_id and the "sync birthday" (bug 371521).
179 fetcher_->Start();
182 void AttachmentUploaderImpl::UploadState::OnGetTokenFailure(
183 const OAuth2TokenService::Request* request,
184 const GoogleServiceAuthError& error) {
185 DCHECK_EQ(access_token_request_.get(), request);
186 access_token_request_.reset();
187 ReportResult(UPLOAD_UNSPECIFIED_ERROR, attachment_.GetId());
190 void AttachmentUploaderImpl::UploadState::GetToken() {
191 access_token_request_ = OAuth2TokenServiceRequest::CreateAndStart(
192 token_service_provider_, account_id_, scopes_, this);
195 void AttachmentUploaderImpl::UploadState::ReportResult(
196 const UploadResult& result,
197 const AttachmentId& attachment_id) {
198 UploadCallbackList::const_iterator iter = user_callbacks_.begin();
199 UploadCallbackList::const_iterator end = user_callbacks_.end();
200 for (; iter != end; ++iter) {
201 base::MessageLoop::current()->PostTask(
202 FROM_HERE, base::Bind(*iter, result, attachment_id));
204 // Destroy this object and return immediately.
205 owner_->DeleteUploadStateFor(attachment_.GetId().GetProto().unique_id());
206 return;
209 AttachmentUploaderImpl::AttachmentUploaderImpl(
210 const GURL& sync_service_url,
211 const scoped_refptr<net::URLRequestContextGetter>&
212 url_request_context_getter,
213 const std::string& account_id,
214 const OAuth2TokenService::ScopeSet& scopes,
215 const scoped_refptr<OAuth2TokenServiceRequest::TokenServiceProvider>&
216 token_service_provider)
217 : sync_service_url_(sync_service_url),
218 url_request_context_getter_(url_request_context_getter),
219 account_id_(account_id),
220 scopes_(scopes),
221 token_service_provider_(token_service_provider) {
222 DCHECK(CalledOnValidThread());
223 DCHECK(!account_id.empty());
224 DCHECK(!scopes.empty());
225 DCHECK(token_service_provider_.get());
228 AttachmentUploaderImpl::~AttachmentUploaderImpl() {
229 DCHECK(CalledOnValidThread());
232 void AttachmentUploaderImpl::UploadAttachment(const Attachment& attachment,
233 const UploadCallback& callback) {
234 DCHECK(CalledOnValidThread());
235 const AttachmentId attachment_id = attachment.GetId();
236 const std::string unique_id = attachment_id.GetProto().unique_id();
237 DCHECK(!unique_id.empty());
238 StateMap::iterator iter = state_map_.find(unique_id);
239 if (iter == state_map_.end()) {
240 const GURL url = GetURLForAttachmentId(sync_service_url_, attachment_id);
241 scoped_ptr<UploadState> upload_state(
242 new UploadState(url,
243 url_request_context_getter_,
244 attachment,
245 callback,
246 account_id_,
247 scopes_,
248 token_service_provider_.get(),
249 this));
250 state_map_.add(unique_id, upload_state.Pass());
251 } else {
252 DCHECK(
253 attachment.GetData()->Equals(iter->second->GetAttachment().GetData()));
254 // We already have an upload for this attachment. "Join" it.
255 iter->second->AddUserCallback(callback);
259 // Static.
260 GURL AttachmentUploaderImpl::GetURLForAttachmentId(
261 const GURL& sync_service_url,
262 const AttachmentId& attachment_id) {
263 std::string path = sync_service_url.path();
264 if (path.empty() || *path.rbegin() != '/') {
265 path += '/';
267 path += kAttachments;
268 path += attachment_id.GetProto().unique_id();
269 GURL::Replacements replacements;
270 replacements.SetPathStr(path);
271 return sync_service_url.ReplaceComponents(replacements);
274 void AttachmentUploaderImpl::DeleteUploadStateFor(const UniqueId& unique_id) {
275 state_map_.erase(unique_id);
278 } // namespace syncer