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_uploader_impl.h" | 5 #include "sync/internal_api/public/attachments/attachment_uploader_impl.h" |
6 | 6 |
7 #include "base/bind.h" | 7 #include "base/bind.h" |
| 8 #include "base/memory/weak_ptr.h" |
8 #include "base/message_loop/message_loop.h" | 9 #include "base/message_loop/message_loop.h" |
9 #include "base/threading/non_thread_safe.h" | 10 #include "base/threading/non_thread_safe.h" |
10 #include "google_apis/gaia/gaia_constants.h" | 11 #include "google_apis/gaia/gaia_constants.h" |
11 #include "net/base/load_flags.h" | 12 #include "net/base/load_flags.h" |
12 #include "net/http/http_status_code.h" | 13 #include "net/http/http_status_code.h" |
13 #include "net/url_request/url_fetcher.h" | 14 #include "net/url_request/url_fetcher.h" |
14 #include "net/url_request/url_fetcher_delegate.h" | 15 #include "net/url_request/url_fetcher_delegate.h" |
15 #include "sync/api/attachments/attachment.h" | 16 #include "sync/api/attachments/attachment.h" |
16 #include "sync/protocol/sync.pb.h" | 17 #include "sync/protocol/sync.pb.h" |
17 | 18 |
18 namespace { | 19 namespace { |
19 | 20 |
20 const char kContentType[] = "application/octet-stream"; | 21 const char kContentType[] = "application/octet-stream"; |
21 const char kAttachments[] = "attachments/"; | 22 const char kAttachments[] = "attachments/"; |
22 | 23 |
23 } // namespace | 24 } // namespace |
24 | 25 |
25 namespace syncer { | 26 namespace syncer { |
26 | 27 |
27 // Encapsulates all the state associated with a single upload. | 28 // Encapsulates all the state associated with a single upload. |
28 class AttachmentUploaderImpl::UploadState : public net::URLFetcherDelegate, | 29 class AttachmentUploaderImpl::UploadState : public net::URLFetcherDelegate, |
29 public OAuth2TokenService::Consumer, | 30 public OAuth2TokenService::Consumer, |
30 public base::NonThreadSafe { | 31 public base::NonThreadSafe { |
31 public: | 32 public: |
32 // Construct an UploadState. | 33 // Construct an UploadState. |
33 // | 34 // |
34 // |owner| is a pointer to the object that will own (and must outlive!) this | 35 // UploadState encapsulates the state associated with a single upload. When |
35 // |UploadState. | 36 // the upload completes, the UploadState object becomes "stopped". |
| 37 // |
| 38 // |owner| is a pointer to the object that owns this UploadState. Upon |
| 39 // completion this object will PostTask to owner's OnUploadStateStopped |
| 40 // method. |
36 UploadState( | 41 UploadState( |
37 const GURL& upload_url, | 42 const GURL& upload_url, |
38 const scoped_refptr<net::URLRequestContextGetter>& | 43 const scoped_refptr<net::URLRequestContextGetter>& |
39 url_request_context_getter, | 44 url_request_context_getter, |
40 const Attachment& attachment, | 45 const Attachment& attachment, |
41 const UploadCallback& user_callback, | 46 const UploadCallback& user_callback, |
42 const std::string& account_id, | 47 const std::string& account_id, |
43 const OAuth2TokenService::ScopeSet& scopes, | 48 const OAuth2TokenService::ScopeSet& scopes, |
44 OAuth2TokenServiceRequest::TokenServiceProvider* token_service_provider, | 49 OAuth2TokenServiceRequest::TokenServiceProvider* token_service_provider, |
45 AttachmentUploaderImpl* owner); | 50 const base::WeakPtr<AttachmentUploaderImpl>& owner); |
46 | 51 |
47 virtual ~UploadState(); | 52 virtual ~UploadState(); |
48 | 53 |
| 54 // Returns true if this object is stopped. Once stopped, this object is |
| 55 // effectively dead and can be destroyed. |
| 56 bool IsStopped() const; |
| 57 |
49 // Add |user_callback| to the list of callbacks to be invoked when this upload | 58 // Add |user_callback| to the list of callbacks to be invoked when this upload |
50 // completed. | 59 // completed. |
| 60 // |
| 61 // It is an error to call |AddUserCallback| on a stopped UploadState (see |
| 62 // |IsStopped|). |
51 void AddUserCallback(const UploadCallback& user_callback); | 63 void AddUserCallback(const UploadCallback& user_callback); |
52 | 64 |
53 // Return the Attachment this object is uploading. | 65 // Return the Attachment this object is uploading. |
54 const Attachment& GetAttachment(); | 66 const Attachment& GetAttachment(); |
55 | 67 |
56 // URLFetcher implementation. | 68 // URLFetcher implementation. |
57 virtual void OnURLFetchComplete(const net::URLFetcher* source) OVERRIDE; | 69 virtual void OnURLFetchComplete(const net::URLFetcher* source) OVERRIDE; |
58 | 70 |
59 // OAuth2TokenService::Consumer. | 71 // OAuth2TokenService::Consumer. |
60 virtual void OnGetTokenSuccess(const OAuth2TokenService::Request* request, | 72 virtual void OnGetTokenSuccess(const OAuth2TokenService::Request* request, |
61 const std::string& access_token, | 73 const std::string& access_token, |
62 const base::Time& expiration_time) OVERRIDE; | 74 const base::Time& expiration_time) OVERRIDE; |
63 virtual void OnGetTokenFailure(const OAuth2TokenService::Request* request, | 75 virtual void OnGetTokenFailure(const OAuth2TokenService::Request* request, |
64 const GoogleServiceAuthError& error) OVERRIDE; | 76 const GoogleServiceAuthError& error) OVERRIDE; |
65 | 77 |
66 private: | 78 private: |
67 typedef std::vector<UploadCallback> UploadCallbackList; | 79 typedef std::vector<UploadCallback> UploadCallbackList; |
68 | 80 |
69 void GetToken(); | 81 void GetToken(); |
70 | 82 |
71 void ReportResult(const UploadResult& result, | 83 void StopAndReportResult(const UploadResult& result, |
72 const AttachmentId& attachment_id); | 84 const AttachmentId& attachment_id); |
73 | 85 |
| 86 bool is_stopped_; |
74 GURL upload_url_; | 87 GURL upload_url_; |
75 const scoped_refptr<net::URLRequestContextGetter>& | 88 const scoped_refptr<net::URLRequestContextGetter>& |
76 url_request_context_getter_; | 89 url_request_context_getter_; |
77 Attachment attachment_; | 90 Attachment attachment_; |
78 UploadCallbackList user_callbacks_; | 91 UploadCallbackList user_callbacks_; |
79 scoped_ptr<net::URLFetcher> fetcher_; | 92 scoped_ptr<net::URLFetcher> fetcher_; |
80 std::string account_id_; | 93 std::string account_id_; |
81 OAuth2TokenService::ScopeSet scopes_; | 94 OAuth2TokenService::ScopeSet scopes_; |
82 std::string access_token_; | 95 std::string access_token_; |
83 OAuth2TokenServiceRequest::TokenServiceProvider* token_service_provider_; | 96 OAuth2TokenServiceRequest::TokenServiceProvider* token_service_provider_; |
84 // Pointer to the AttachmentUploaderImpl that owns this object. | 97 // Pointer to the AttachmentUploaderImpl that owns this object. |
85 AttachmentUploaderImpl* owner_; | 98 base::WeakPtr<AttachmentUploaderImpl> owner_; |
86 scoped_ptr<OAuth2TokenServiceRequest> access_token_request_; | 99 scoped_ptr<OAuth2TokenServiceRequest> access_token_request_; |
87 | 100 |
88 DISALLOW_COPY_AND_ASSIGN(UploadState); | 101 DISALLOW_COPY_AND_ASSIGN(UploadState); |
89 }; | 102 }; |
90 | 103 |
91 AttachmentUploaderImpl::UploadState::UploadState( | 104 AttachmentUploaderImpl::UploadState::UploadState( |
92 const GURL& upload_url, | 105 const GURL& upload_url, |
93 const scoped_refptr<net::URLRequestContextGetter>& | 106 const scoped_refptr<net::URLRequestContextGetter>& |
94 url_request_context_getter, | 107 url_request_context_getter, |
95 const Attachment& attachment, | 108 const Attachment& attachment, |
96 const UploadCallback& user_callback, | 109 const UploadCallback& user_callback, |
97 const std::string& account_id, | 110 const std::string& account_id, |
98 const OAuth2TokenService::ScopeSet& scopes, | 111 const OAuth2TokenService::ScopeSet& scopes, |
99 OAuth2TokenServiceRequest::TokenServiceProvider* token_service_provider, | 112 OAuth2TokenServiceRequest::TokenServiceProvider* token_service_provider, |
100 AttachmentUploaderImpl* owner) | 113 const base::WeakPtr<AttachmentUploaderImpl>& owner) |
101 : OAuth2TokenService::Consumer("attachment-uploader-impl"), | 114 : OAuth2TokenService::Consumer("attachment-uploader-impl"), |
| 115 is_stopped_(false), |
102 upload_url_(upload_url), | 116 upload_url_(upload_url), |
103 url_request_context_getter_(url_request_context_getter), | 117 url_request_context_getter_(url_request_context_getter), |
104 attachment_(attachment), | 118 attachment_(attachment), |
105 user_callbacks_(1, user_callback), | 119 user_callbacks_(1, user_callback), |
106 account_id_(account_id), | 120 account_id_(account_id), |
107 scopes_(scopes), | 121 scopes_(scopes), |
108 token_service_provider_(token_service_provider), | 122 token_service_provider_(token_service_provider), |
109 owner_(owner) { | 123 owner_(owner) { |
110 DCHECK(upload_url_.is_valid()); | 124 DCHECK(upload_url_.is_valid()); |
111 DCHECK(url_request_context_getter_.get()); | 125 DCHECK(url_request_context_getter_.get()); |
112 DCHECK(!account_id_.empty()); | 126 DCHECK(!account_id_.empty()); |
113 DCHECK(!scopes_.empty()); | 127 DCHECK(!scopes_.empty()); |
114 DCHECK(token_service_provider_); | 128 DCHECK(token_service_provider_); |
115 DCHECK(owner_); | |
116 GetToken(); | 129 GetToken(); |
117 } | 130 } |
118 | 131 |
119 AttachmentUploaderImpl::UploadState::~UploadState() { | 132 AttachmentUploaderImpl::UploadState::~UploadState() { |
120 } | 133 } |
121 | 134 |
| 135 bool AttachmentUploaderImpl::UploadState::IsStopped() const { |
| 136 DCHECK(CalledOnValidThread()); |
| 137 return is_stopped_; |
| 138 } |
| 139 |
122 void AttachmentUploaderImpl::UploadState::AddUserCallback( | 140 void AttachmentUploaderImpl::UploadState::AddUserCallback( |
123 const UploadCallback& user_callback) { | 141 const UploadCallback& user_callback) { |
124 DCHECK(CalledOnValidThread()); | 142 DCHECK(CalledOnValidThread()); |
| 143 DCHECK(!is_stopped_); |
125 user_callbacks_.push_back(user_callback); | 144 user_callbacks_.push_back(user_callback); |
126 } | 145 } |
127 | 146 |
128 const Attachment& AttachmentUploaderImpl::UploadState::GetAttachment() { | 147 const Attachment& AttachmentUploaderImpl::UploadState::GetAttachment() { |
129 DCHECK(CalledOnValidThread()); | 148 DCHECK(CalledOnValidThread()); |
130 return attachment_; | 149 return attachment_; |
131 } | 150 } |
132 | 151 |
133 void AttachmentUploaderImpl::UploadState::OnURLFetchComplete( | 152 void AttachmentUploaderImpl::UploadState::OnURLFetchComplete( |
134 const net::URLFetcher* source) { | 153 const net::URLFetcher* source) { |
135 DCHECK(CalledOnValidThread()); | 154 DCHECK(CalledOnValidThread()); |
| 155 if (is_stopped_) { |
| 156 return; |
| 157 } |
| 158 |
136 UploadResult result = UPLOAD_TRANSIENT_ERROR; | 159 UploadResult result = UPLOAD_TRANSIENT_ERROR; |
137 AttachmentId attachment_id = attachment_.GetId(); | 160 AttachmentId attachment_id = attachment_.GetId(); |
138 const int response_code = source->GetResponseCode(); | 161 const int response_code = source->GetResponseCode(); |
139 if (response_code == net::HTTP_OK) { | 162 if (response_code == net::HTTP_OK) { |
140 result = UPLOAD_SUCCESS; | 163 result = UPLOAD_SUCCESS; |
141 } else if (response_code == net::HTTP_UNAUTHORIZED) { | 164 } else if (response_code == net::HTTP_UNAUTHORIZED) { |
142 // Server tells us we've got a bad token so invalidate it. | 165 // Server tells us we've got a bad token so invalidate it. |
143 OAuth2TokenServiceRequest::InvalidateToken( | 166 OAuth2TokenServiceRequest::InvalidateToken( |
144 token_service_provider_, account_id_, scopes_, access_token_); | 167 token_service_provider_, account_id_, scopes_, access_token_); |
145 // 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. |
146 result = UPLOAD_TRANSIENT_ERROR; | 169 result = UPLOAD_TRANSIENT_ERROR; |
147 } else if (response_code == net::HTTP_FORBIDDEN) { | 170 } else if (response_code == net::HTTP_FORBIDDEN) { |
148 // User is not allowed to use attachments. Retrying won't help. | 171 // User is not allowed to use attachments. Retrying won't help. |
149 result = UPLOAD_UNSPECIFIED_ERROR; | 172 result = UPLOAD_UNSPECIFIED_ERROR; |
150 } else if (response_code == net::URLFetcher::RESPONSE_CODE_INVALID) { | 173 } else if (response_code == net::URLFetcher::RESPONSE_CODE_INVALID) { |
151 result = UPLOAD_TRANSIENT_ERROR; | 174 result = UPLOAD_TRANSIENT_ERROR; |
152 } | 175 } |
153 ReportResult(result, attachment_id); | 176 StopAndReportResult(result, attachment_id); |
154 } | 177 } |
155 | 178 |
156 void AttachmentUploaderImpl::UploadState::OnGetTokenSuccess( | 179 void AttachmentUploaderImpl::UploadState::OnGetTokenSuccess( |
157 const OAuth2TokenService::Request* request, | 180 const OAuth2TokenService::Request* request, |
158 const std::string& access_token, | 181 const std::string& access_token, |
159 const base::Time& expiration_time) { | 182 const base::Time& expiration_time) { |
| 183 DCHECK(CalledOnValidThread()); |
| 184 if (is_stopped_) { |
| 185 return; |
| 186 } |
| 187 |
160 DCHECK_EQ(access_token_request_.get(), request); | 188 DCHECK_EQ(access_token_request_.get(), request); |
161 access_token_request_.reset(); | 189 access_token_request_.reset(); |
162 access_token_ = access_token; | 190 access_token_ = access_token; |
163 fetcher_.reset( | 191 fetcher_.reset( |
164 net::URLFetcher::Create(upload_url_, net::URLFetcher::POST, this)); | 192 net::URLFetcher::Create(upload_url_, net::URLFetcher::POST, this)); |
165 fetcher_->SetAutomaticallyRetryOn5xx(false); | 193 fetcher_->SetAutomaticallyRetryOn5xx(false); |
166 fetcher_->SetRequestContext(url_request_context_getter_.get()); | 194 fetcher_->SetRequestContext(url_request_context_getter_.get()); |
167 // TODO(maniscalco): Is there a better way? Copying the attachment data into | 195 // TODO(maniscalco): Is there a better way? Copying the attachment data into |
168 // a string feels wrong given how large attachments may be (several MBs). If | 196 // a string feels wrong given how large attachments may be (several MBs). If |
169 // we may end up switching from URLFetcher to URLRequest, this copy won't be | 197 // we may end up switching from URLFetcher to URLRequest, this copy won't be |
170 // necessary. | 198 // necessary. |
171 scoped_refptr<base::RefCountedMemory> memory = attachment_.GetData(); | 199 scoped_refptr<base::RefCountedMemory> memory = attachment_.GetData(); |
172 const std::string upload_content(memory->front_as<char>(), memory->size()); | 200 const std::string upload_content(memory->front_as<char>(), memory->size()); |
173 fetcher_->SetUploadData(kContentType, upload_content); | 201 fetcher_->SetUploadData(kContentType, upload_content); |
174 const std::string auth_header("Authorization: Bearer " + access_token_); | 202 const std::string auth_header("Authorization: Bearer " + access_token_); |
175 fetcher_->AddExtraRequestHeader(auth_header); | 203 fetcher_->AddExtraRequestHeader(auth_header); |
176 fetcher_->SetLoadFlags(net::LOAD_DO_NOT_SAVE_COOKIES | | 204 fetcher_->SetLoadFlags(net::LOAD_DO_NOT_SAVE_COOKIES | |
177 net::LOAD_DO_NOT_SEND_COOKIES | | 205 net::LOAD_DO_NOT_SEND_COOKIES | |
178 net::LOAD_DISABLE_CACHE); | 206 net::LOAD_DISABLE_CACHE); |
179 // TODO(maniscalco): Set an appropriate headers (User-Agent, Content-type, and | 207 // TODO(maniscalco): Set an appropriate headers (User-Agent, Content-type, and |
180 // Content-length) on the request and include the content's MD5, | 208 // Content-length) on the request and include the content's MD5, |
181 // AttachmentId's unique_id and the "sync birthday" (bug 371521). | 209 // AttachmentId's unique_id and the "sync birthday" (bug 371521). |
182 fetcher_->Start(); | 210 fetcher_->Start(); |
183 } | 211 } |
184 | 212 |
185 void AttachmentUploaderImpl::UploadState::OnGetTokenFailure( | 213 void AttachmentUploaderImpl::UploadState::OnGetTokenFailure( |
186 const OAuth2TokenService::Request* request, | 214 const OAuth2TokenService::Request* request, |
187 const GoogleServiceAuthError& error) { | 215 const GoogleServiceAuthError& error) { |
| 216 DCHECK(CalledOnValidThread()); |
| 217 if (is_stopped_) { |
| 218 return; |
| 219 } |
| 220 |
188 DCHECK_EQ(access_token_request_.get(), request); | 221 DCHECK_EQ(access_token_request_.get(), request); |
189 access_token_request_.reset(); | 222 access_token_request_.reset(); |
190 // TODO(maniscalco): We treat this as a transient error, but it may in fact be | 223 // TODO(maniscalco): We treat this as a transient error, but it may in fact be |
191 // a very long lived error and require user action. Consider differentiating | 224 // a very long lived error and require user action. Consider differentiating |
192 // between the causes of GetToken failure and act accordingly. Think about | 225 // between the causes of GetToken failure and act accordingly. Think about |
193 // the causes of GetToken failure. Are there (bug 412802). | 226 // the causes of GetToken failure. Are there (bug 412802). |
194 ReportResult(UPLOAD_TRANSIENT_ERROR, attachment_.GetId()); | 227 StopAndReportResult(UPLOAD_TRANSIENT_ERROR, attachment_.GetId()); |
195 } | 228 } |
196 | 229 |
197 void AttachmentUploaderImpl::UploadState::GetToken() { | 230 void AttachmentUploaderImpl::UploadState::GetToken() { |
198 access_token_request_ = OAuth2TokenServiceRequest::CreateAndStart( | 231 access_token_request_ = OAuth2TokenServiceRequest::CreateAndStart( |
199 token_service_provider_, account_id_, scopes_, this); | 232 token_service_provider_, account_id_, scopes_, this); |
200 } | 233 } |
201 | 234 |
202 void AttachmentUploaderImpl::UploadState::ReportResult( | 235 void AttachmentUploaderImpl::UploadState::StopAndReportResult( |
203 const UploadResult& result, | 236 const UploadResult& result, |
204 const AttachmentId& attachment_id) { | 237 const AttachmentId& attachment_id) { |
| 238 DCHECK(!is_stopped_); |
| 239 is_stopped_ = true; |
205 UploadCallbackList::const_iterator iter = user_callbacks_.begin(); | 240 UploadCallbackList::const_iterator iter = user_callbacks_.begin(); |
206 UploadCallbackList::const_iterator end = user_callbacks_.end(); | 241 UploadCallbackList::const_iterator end = user_callbacks_.end(); |
207 for (; iter != end; ++iter) { | 242 for (; iter != end; ++iter) { |
208 base::MessageLoop::current()->PostTask( | 243 base::MessageLoop::current()->PostTask( |
209 FROM_HERE, base::Bind(*iter, result, attachment_id)); | 244 FROM_HERE, base::Bind(*iter, result, attachment_id)); |
210 } | 245 } |
211 // Destroy this object and return immediately. | 246 base::MessageLoop::current()->PostTask( |
212 owner_->DeleteUploadStateFor(attachment_.GetId().GetProto().unique_id()); | 247 FROM_HERE, |
213 return; | 248 base::Bind(&AttachmentUploaderImpl::OnUploadStateStopped, |
| 249 owner_, |
| 250 attachment_id.GetProto().unique_id())); |
214 } | 251 } |
215 | 252 |
216 AttachmentUploaderImpl::AttachmentUploaderImpl( | 253 AttachmentUploaderImpl::AttachmentUploaderImpl( |
217 const GURL& sync_service_url, | 254 const GURL& sync_service_url, |
218 const scoped_refptr<net::URLRequestContextGetter>& | 255 const scoped_refptr<net::URLRequestContextGetter>& |
219 url_request_context_getter, | 256 url_request_context_getter, |
220 const std::string& account_id, | 257 const std::string& account_id, |
221 const OAuth2TokenService::ScopeSet& scopes, | 258 const OAuth2TokenService::ScopeSet& scopes, |
222 const scoped_refptr<OAuth2TokenServiceRequest::TokenServiceProvider>& | 259 const scoped_refptr<OAuth2TokenServiceRequest::TokenServiceProvider>& |
223 token_service_provider) | 260 token_service_provider) |
224 : sync_service_url_(sync_service_url), | 261 : sync_service_url_(sync_service_url), |
225 url_request_context_getter_(url_request_context_getter), | 262 url_request_context_getter_(url_request_context_getter), |
226 account_id_(account_id), | 263 account_id_(account_id), |
227 scopes_(scopes), | 264 scopes_(scopes), |
228 token_service_provider_(token_service_provider) { | 265 token_service_provider_(token_service_provider), |
| 266 weak_ptr_factory_(this) { |
229 DCHECK(CalledOnValidThread()); | 267 DCHECK(CalledOnValidThread()); |
230 DCHECK(!account_id.empty()); | 268 DCHECK(!account_id.empty()); |
231 DCHECK(!scopes.empty()); | 269 DCHECK(!scopes.empty()); |
232 DCHECK(token_service_provider_.get()); | 270 DCHECK(token_service_provider_.get()); |
233 } | 271 } |
234 | 272 |
235 AttachmentUploaderImpl::~AttachmentUploaderImpl() { | 273 AttachmentUploaderImpl::~AttachmentUploaderImpl() { |
236 DCHECK(CalledOnValidThread()); | 274 DCHECK(CalledOnValidThread()); |
237 } | 275 } |
238 | 276 |
239 void AttachmentUploaderImpl::UploadAttachment(const Attachment& attachment, | 277 void AttachmentUploaderImpl::UploadAttachment(const Attachment& attachment, |
240 const UploadCallback& callback) { | 278 const UploadCallback& callback) { |
241 DCHECK(CalledOnValidThread()); | 279 DCHECK(CalledOnValidThread()); |
242 const AttachmentId attachment_id = attachment.GetId(); | 280 const AttachmentId attachment_id = attachment.GetId(); |
243 const std::string unique_id = attachment_id.GetProto().unique_id(); | 281 const std::string unique_id = attachment_id.GetProto().unique_id(); |
244 DCHECK(!unique_id.empty()); | 282 DCHECK(!unique_id.empty()); |
245 StateMap::iterator iter = state_map_.find(unique_id); | 283 StateMap::iterator iter = state_map_.find(unique_id); |
246 if (iter == state_map_.end()) { | 284 if (iter != state_map_.end()) { |
247 const GURL url = GetURLForAttachmentId(sync_service_url_, attachment_id); | 285 // We have an old upload request for this attachment... |
248 scoped_ptr<UploadState> upload_state( | 286 if (!iter->second->IsStopped()) { |
249 new UploadState(url, | 287 // "join" to it. |
250 url_request_context_getter_, | 288 DCHECK(attachment.GetData() |
251 attachment, | 289 ->Equals(iter->second->GetAttachment().GetData())); |
252 callback, | 290 iter->second->AddUserCallback(callback); |
253 account_id_, | 291 return; |
254 scopes_, | 292 } else { |
255 token_service_provider_.get(), | 293 // It's stopped so we can't use it. Delete it. |
256 this)); | 294 state_map_.erase(iter); |
257 state_map_.add(unique_id, upload_state.Pass()); | 295 } |
258 } else { | |
259 DCHECK( | |
260 attachment.GetData()->Equals(iter->second->GetAttachment().GetData())); | |
261 // We already have an upload for this attachment. "Join" it. | |
262 iter->second->AddUserCallback(callback); | |
263 } | 296 } |
| 297 |
| 298 const GURL url = GetURLForAttachmentId(sync_service_url_, attachment_id); |
| 299 scoped_ptr<UploadState> upload_state( |
| 300 new UploadState(url, |
| 301 url_request_context_getter_, |
| 302 attachment, |
| 303 callback, |
| 304 account_id_, |
| 305 scopes_, |
| 306 token_service_provider_.get(), |
| 307 weak_ptr_factory_.GetWeakPtr())); |
| 308 state_map_.add(unique_id, upload_state.Pass()); |
264 } | 309 } |
265 | 310 |
266 // Static. | 311 // Static. |
267 GURL AttachmentUploaderImpl::GetURLForAttachmentId( | 312 GURL AttachmentUploaderImpl::GetURLForAttachmentId( |
268 const GURL& sync_service_url, | 313 const GURL& sync_service_url, |
269 const AttachmentId& attachment_id) { | 314 const AttachmentId& attachment_id) { |
270 std::string path = sync_service_url.path(); | 315 std::string path = sync_service_url.path(); |
271 if (path.empty() || *path.rbegin() != '/') { | 316 if (path.empty() || *path.rbegin() != '/') { |
272 path += '/'; | 317 path += '/'; |
273 } | 318 } |
274 path += kAttachments; | 319 path += kAttachments; |
275 path += attachment_id.GetProto().unique_id(); | 320 path += attachment_id.GetProto().unique_id(); |
276 GURL::Replacements replacements; | 321 GURL::Replacements replacements; |
277 replacements.SetPathStr(path); | 322 replacements.SetPathStr(path); |
278 return sync_service_url.ReplaceComponents(replacements); | 323 return sync_service_url.ReplaceComponents(replacements); |
279 } | 324 } |
280 | 325 |
281 void AttachmentUploaderImpl::DeleteUploadStateFor(const UniqueId& unique_id) { | 326 void AttachmentUploaderImpl::OnUploadStateStopped(const UniqueId& unique_id) { |
282 state_map_.erase(unique_id); | 327 StateMap::iterator iter = state_map_.find(unique_id); |
| 328 // Only erase if stopped. Because this method is called asynchronously, it's |
| 329 // possible that a new request for this same id arrived after the UploadState |
| 330 // stopped, but before this method was invoked. In that case the UploadState |
| 331 // in the map might be a new one. |
| 332 if (iter != state_map_.end() && iter->second->IsStopped()) { |
| 333 state_map_.erase(iter); |
| 334 } |
283 } | 335 } |
284 | 336 |
285 } // namespace syncer | 337 } // namespace syncer |
OLD | NEW |