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_downloader_impl.h" 6 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" 15 16 namespace syncer { 17 18 struct AttachmentDownloaderImpl::DownloadState { 19 public: 20 DownloadState(const AttachmentId& attachment_id, 21 const AttachmentUrl& attachment_url); 22 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; 30 }; 31 32 AttachmentDownloaderImpl::DownloadState::DownloadState( 33 const AttachmentId& attachment_id, 34 const AttachmentUrl& attachment_url) 35 : attachment_id(attachment_id), attachment_url(attachment_url) { 36 } 37 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()); 56 } 57 58 AttachmentDownloaderImpl::~AttachmentDownloaderImpl() { 59 } 60 61 void AttachmentDownloaderImpl::DownloadAttachment( 62 const AttachmentId& attachment_id, 63 const DownloadCallback& callback) { 64 DCHECK(CalledOnValidThread()); 65 66 AttachmentUrl url = AttachmentUploaderImpl::GetURLForAttachmentId( 67 sync_service_url_, attachment_id).spec(); 68 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); 77 } 78 DownloadState* download_state = iter->second; 79 DCHECK(download_state->attachment_id == attachment_id); 80 download_state->user_callbacks.push_back(callback); 81 } 82 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(); 100 } 101 requests_waiting_for_access_token_.clear(); 102 } 103 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_TRANSIENT_ERROR, null_attachment_data); 119 DCHECK(state_map_.find(download_state->attachment_url) != state_map_.end()); 120 state_map_.erase(download_state->attachment_url); 121 } 122 requests_waiting_for_access_token_.clear(); 123 } 124 125 void AttachmentDownloaderImpl::OnURLFetchComplete( 126 const net::URLFetcher* source) { 127 DCHECK(CalledOnValidThread()); 128 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()); 135 136 DownloadResult result = DOWNLOAD_TRANSIENT_ERROR; 137 scoped_refptr<base::RefCountedString> attachment_data; 138 139 const int response_code = source->GetResponseCode(); 140 if (response_code == net::HTTP_OK) { 141 result = DOWNLOAD_SUCCESS; 142 std::string data_as_string; 143 source->GetResponseAsString(&data_as_string); 144 attachment_data = base::RefCountedString::TakeString(&data_as_string); 145 } else if (response_code == net::HTTP_UNAUTHORIZED) { 146 // Server tells us we've got a bad token so invalidate it. 147 OAuth2TokenServiceRequest::InvalidateToken(token_service_provider_.get(), 148 account_id_, 149 oauth2_scopes_, 150 download_state.access_token); 151 // Fail the request, but indicate that it may be successful if retried. 152 result = DOWNLOAD_TRANSIENT_ERROR; 153 } else if (response_code == net::HTTP_FORBIDDEN) { 154 // User is not allowed to use attachments. Retrying won't help. 155 result = DOWNLOAD_UNSPECIFIED_ERROR; 156 } else if (response_code == net::URLFetcher::RESPONSE_CODE_INVALID) { 157 result = DOWNLOAD_TRANSIENT_ERROR; 158 } 159 ReportResult(download_state, result, attachment_data); 160 state_map_.erase(iter); 161 } 162 163 scoped_ptr<net::URLFetcher> AttachmentDownloaderImpl::CreateFetcher( 164 const AttachmentUrl& url, 165 const std::string& access_token) { 166 scoped_ptr<net::URLFetcher> url_fetcher( 167 net::URLFetcher::Create(GURL(url), net::URLFetcher::GET, this)); 168 url_fetcher->SetAutomaticallyRetryOn5xx(false); 169 const std::string auth_header("Authorization: Bearer " + access_token); 170 url_fetcher->AddExtraRequestHeader(auth_header); 171 url_fetcher->SetRequestContext(url_request_context_getter_.get()); 172 url_fetcher->SetLoadFlags(net::LOAD_DO_NOT_SAVE_COOKIES | 173 net::LOAD_DO_NOT_SEND_COOKIES | 174 net::LOAD_DISABLE_CACHE); 175 // TODO(maniscalco): Set an appropriate headers (User-Agent, what else?) on 176 // the request (bug 371521). 177 return url_fetcher.Pass(); 178 } 179 180 void AttachmentDownloaderImpl::RequestAccessToken( 181 DownloadState* download_state) { 182 requests_waiting_for_access_token_.push_back(download_state); 183 // Start access token request if there is no active one. 184 if (access_token_request_ == NULL) { 185 access_token_request_ = OAuth2TokenServiceRequest::CreateAndStart( 186 token_service_provider_.get(), account_id_, oauth2_scopes_, this); 187 } 188 } 189 190 void AttachmentDownloaderImpl::ReportResult( 191 const DownloadState& download_state, 192 const DownloadResult& result, 193 const scoped_refptr<base::RefCountedString>& attachment_data) { 194 std::vector<DownloadCallback>::const_iterator iter; 195 for (iter = download_state.user_callbacks.begin(); 196 iter != download_state.user_callbacks.end(); 197 ++iter) { 198 scoped_ptr<Attachment> attachment; 199 if (result == DOWNLOAD_SUCCESS) { 200 attachment.reset(new Attachment(Attachment::CreateWithId( 201 download_state.attachment_id, attachment_data))); 202 } 203 204 base::MessageLoop::current()->PostTask( 205 FROM_HERE, base::Bind(*iter, result, base::Passed(&attachment))); 206 } 207 } 208 209 } // namespace syncer 210