Updating trunk VERSION from 2139.0 to 2140.0
[chromium-blink-merge.git] / sync / internal_api / attachments / attachment_downloader_impl.cc
blobd43b3ea1cc276620738d6cd538fd718436580af9
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_downloader_impl.h"
7 #include "base/bind.h"
8 #include "base/message_loop/message_loop.h"
9 #include "net/base/load_flags.h"
10 #include "net/http/http_status_code.h"
11 #include "net/url_request/url_fetcher.h"
12 #include "sync/internal_api/public/attachments/attachment_uploader_impl.h"
13 #include "sync/protocol/sync.pb.h"
14 #include "url/gurl.h"
16 namespace syncer {
18 struct AttachmentDownloaderImpl::DownloadState {
19 public:
20 DownloadState(const AttachmentId& attachment_id,
21 const AttachmentUrl& attachment_url);
23 AttachmentId attachment_id;
24 AttachmentUrl attachment_url;
25 // |access_token| needed to invalidate if downloading attachment fails with
26 // HTTP_UNAUTHORIZED.
27 std::string access_token;
28 scoped_ptr<net::URLFetcher> url_fetcher;
29 std::vector<DownloadCallback> user_callbacks;
32 AttachmentDownloaderImpl::DownloadState::DownloadState(
33 const AttachmentId& attachment_id,
34 const AttachmentUrl& attachment_url)
35 : attachment_id(attachment_id), attachment_url(attachment_url) {
38 AttachmentDownloaderImpl::AttachmentDownloaderImpl(
39 const GURL& sync_service_url,
40 const scoped_refptr<net::URLRequestContextGetter>&
41 url_request_context_getter,
42 const std::string& account_id,
43 const OAuth2TokenService::ScopeSet& scopes,
44 const scoped_refptr<OAuth2TokenServiceRequest::TokenServiceProvider>&
45 token_service_provider)
46 : OAuth2TokenService::Consumer("attachment-downloader-impl"),
47 sync_service_url_(sync_service_url),
48 url_request_context_getter_(url_request_context_getter),
49 account_id_(account_id),
50 oauth2_scopes_(scopes),
51 token_service_provider_(token_service_provider) {
52 DCHECK(!account_id.empty());
53 DCHECK(!scopes.empty());
54 DCHECK(token_service_provider_.get());
55 DCHECK(url_request_context_getter_.get());
58 AttachmentDownloaderImpl::~AttachmentDownloaderImpl() {
61 void AttachmentDownloaderImpl::DownloadAttachment(
62 const AttachmentId& attachment_id,
63 const DownloadCallback& callback) {
64 DCHECK(CalledOnValidThread());
66 AttachmentUrl url = AttachmentUploaderImpl::GetURLForAttachmentId(
67 sync_service_url_, attachment_id).spec();
69 StateMap::iterator iter = state_map_.find(url);
70 if (iter == state_map_.end()) {
71 // There is no request started for this attachment id. Let's create
72 // DownloadState and request access token for it.
73 scoped_ptr<DownloadState> new_download_state(
74 new DownloadState(attachment_id, url));
75 iter = state_map_.add(url, new_download_state.Pass()).first;
76 RequestAccessToken(iter->second);
78 DownloadState* download_state = iter->second;
79 DCHECK(download_state->attachment_id == attachment_id);
80 download_state->user_callbacks.push_back(callback);
83 void AttachmentDownloaderImpl::OnGetTokenSuccess(
84 const OAuth2TokenService::Request* request,
85 const std::string& access_token,
86 const base::Time& expiration_time) {
87 DCHECK(CalledOnValidThread());
88 DCHECK(request == access_token_request_.get());
89 access_token_request_.reset();
90 StateList::const_iterator iter;
91 // Start downloads for all download requests waiting for access token.
92 for (iter = requests_waiting_for_access_token_.begin();
93 iter != requests_waiting_for_access_token_.end();
94 ++iter) {
95 DownloadState* download_state = *iter;
96 download_state->access_token = access_token;
97 download_state->url_fetcher =
98 CreateFetcher(download_state->attachment_url, access_token).Pass();
99 download_state->url_fetcher->Start();
101 requests_waiting_for_access_token_.clear();
104 void AttachmentDownloaderImpl::OnGetTokenFailure(
105 const OAuth2TokenService::Request* request,
106 const GoogleServiceAuthError& error) {
107 DCHECK(CalledOnValidThread());
108 DCHECK(request == access_token_request_.get());
109 access_token_request_.reset();
110 StateList::const_iterator iter;
111 // Without access token all downloads fail.
112 for (iter = requests_waiting_for_access_token_.begin();
113 iter != requests_waiting_for_access_token_.end();
114 ++iter) {
115 DownloadState* download_state = *iter;
116 scoped_refptr<base::RefCountedString> null_attachment_data;
117 ReportResult(
118 *download_state, DOWNLOAD_UNSPECIFIED_ERROR, null_attachment_data);
119 DCHECK(state_map_.find(download_state->attachment_url) != state_map_.end());
120 state_map_.erase(download_state->attachment_url);
122 requests_waiting_for_access_token_.clear();
125 void AttachmentDownloaderImpl::OnURLFetchComplete(
126 const net::URLFetcher* source) {
127 DCHECK(CalledOnValidThread());
129 // Find DownloadState by url.
130 AttachmentUrl url = source->GetOriginalURL().spec();
131 StateMap::iterator iter = state_map_.find(url);
132 DCHECK(iter != state_map_.end());
133 const DownloadState& download_state = *iter->second;
134 DCHECK(source == download_state.url_fetcher.get());
136 DownloadResult result = DOWNLOAD_UNSPECIFIED_ERROR;
137 scoped_refptr<base::RefCountedString> attachment_data;
139 if (source->GetResponseCode() == net::HTTP_OK) {
140 result = DOWNLOAD_SUCCESS;
141 std::string data_as_string;
142 source->GetResponseAsString(&data_as_string);
143 attachment_data = base::RefCountedString::TakeString(&data_as_string);
144 } else if (source->GetResponseCode() == net::HTTP_UNAUTHORIZED) {
145 OAuth2TokenServiceRequest::InvalidateToken(token_service_provider_.get(),
146 account_id_,
147 oauth2_scopes_,
148 download_state.access_token);
149 // TODO(pavely): crbug/380437. This is transient error. Request new access
150 // token for this DownloadState. The only trick is to do it with exponential
151 // backoff.
153 ReportResult(download_state, result, attachment_data);
154 state_map_.erase(iter);
157 scoped_ptr<net::URLFetcher> AttachmentDownloaderImpl::CreateFetcher(
158 const AttachmentUrl& url,
159 const std::string& access_token) {
160 scoped_ptr<net::URLFetcher> url_fetcher(
161 net::URLFetcher::Create(GURL(url), net::URLFetcher::GET, this));
162 const std::string auth_header("Authorization: Bearer " + access_token);
163 url_fetcher->AddExtraRequestHeader(auth_header);
164 url_fetcher->SetRequestContext(url_request_context_getter_.get());
165 url_fetcher->SetLoadFlags(net::LOAD_DO_NOT_SAVE_COOKIES |
166 net::LOAD_DO_NOT_SEND_COOKIES |
167 net::LOAD_DISABLE_CACHE);
168 // TODO(maniscalco): Set an appropriate headers (User-Agent, what else?) on
169 // the request (bug 371521).
170 return url_fetcher.Pass();
173 void AttachmentDownloaderImpl::RequestAccessToken(
174 DownloadState* download_state) {
175 requests_waiting_for_access_token_.push_back(download_state);
176 // Start access token request if there is no active one.
177 if (access_token_request_ == NULL) {
178 access_token_request_ = OAuth2TokenServiceRequest::CreateAndStart(
179 token_service_provider_.get(), account_id_, oauth2_scopes_, this);
183 void AttachmentDownloaderImpl::ReportResult(
184 const DownloadState& download_state,
185 const DownloadResult& result,
186 const scoped_refptr<base::RefCountedString>& attachment_data) {
187 std::vector<DownloadCallback>::const_iterator iter;
188 for (iter = download_state.user_callbacks.begin();
189 iter != download_state.user_callbacks.end();
190 ++iter) {
191 scoped_ptr<Attachment> attachment;
192 if (result == DOWNLOAD_SUCCESS) {
193 attachment.reset(new Attachment(Attachment::CreateWithId(
194 download_state.attachment_id, attachment_data)));
197 base::MessageLoop::current()->PostTask(
198 FROM_HERE, base::Bind(*iter, result, base::Passed(&attachment)));
202 } // namespace syncer