fileapi: Use standard task-runner to run the fileapi callbacks.
[chromium-blink-merge.git] / google_apis / gaia / merge_session_helper.cc
blob2137b14064da97098ae0bce99439860396949725
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 "google_apis/gaia/merge_session_helper.h"
7 #include <vector>
9 #include "base/json/json_reader.h"
10 #include "base/stl_util.h"
11 #include "base/strings/string_util.h"
12 #include "base/strings/stringprintf.h"
13 #include "base/time/time.h"
14 #include "base/values.h"
15 #include "google_apis/gaia/gaia_auth_fetcher.h"
16 #include "google_apis/gaia/gaia_constants.h"
17 #include "google_apis/gaia/gaia_urls.h"
18 #include "google_apis/gaia/oauth2_token_service.h"
19 #include "net/base/load_flags.h"
20 #include "net/http/http_status_code.h"
21 #include "net/url_request/url_fetcher.h"
22 #include "net/url_request/url_fetcher_delegate.h"
24 MergeSessionHelper::ExternalCcResultFetcher::ExternalCcResultFetcher(
25 MergeSessionHelper* helper) : helper_(helper) {
26 DCHECK(helper_);
29 MergeSessionHelper::ExternalCcResultFetcher::~ExternalCcResultFetcher() {
30 CleanupTransientState();
33 std::string MergeSessionHelper::ExternalCcResultFetcher::GetExternalCcResult() {
34 std::vector<std::string> results;
35 for (ResultMap::const_iterator it = results_.begin(); it != results_.end();
36 ++it) {
37 results.push_back(it->first + ":" + it->second);
39 return JoinString(results, ",");
42 void MergeSessionHelper::ExternalCcResultFetcher::Start() {
43 CleanupTransientState();
44 results_.clear();
45 gaia_auth_fetcher_.reset(
46 new GaiaAuthFetcher(this, helper_->source_,
47 helper_->request_context()));
48 gaia_auth_fetcher_->StartGetCheckConnectionInfo();
50 // Some fetches may timeout. Start a timer to decide when the result fetcher
51 // has waited long enough.
52 // TODO(rogerta): I have no idea how long to wait before timing out.
53 // Gaia folks say this should take no more than 2 second even in mobile.
54 // This will need to be tweaked.
55 timer_.Start(FROM_HERE, base::TimeDelta::FromSeconds(5),
56 this, &MergeSessionHelper::ExternalCcResultFetcher::Timeout);
59 bool MergeSessionHelper::ExternalCcResultFetcher::IsRunning() {
60 return gaia_auth_fetcher_ || fetchers_.size() > 0u;
63 void MergeSessionHelper::ExternalCcResultFetcher::TimeoutForTests() {
64 Timeout();
67 void
68 MergeSessionHelper::ExternalCcResultFetcher::OnGetCheckConnectionInfoSuccess(
69 const std::string& data) {
70 scoped_ptr<base::Value> value(base::JSONReader::Read(data));
71 const base::ListValue* list;
72 if (!value || !value->GetAsList(&list)) {
73 CleanupTransientState();
74 FireGetCheckConnectionInfoCompleted(false);
75 return;
78 // If there is nothing to check, terminate immediately.
79 if (list->GetSize() == 0) {
80 CleanupTransientState();
81 FireGetCheckConnectionInfoCompleted(true);
82 return;
85 // Start a fetcher for each connection URL that needs to be checked.
86 for (size_t i = 0; i < list->GetSize(); ++i) {
87 const base::DictionaryValue* dict;
88 if (list->GetDictionary(i, &dict)) {
89 std::string token;
90 std::string url;
91 if (dict->GetString("carryBackToken", &token) &&
92 dict->GetString("url", &url)) {
93 results_[token] = "null";
94 net::URLFetcher* fetcher = CreateFetcher(GURL(url));
95 fetchers_[fetcher->GetOriginalURL()] = std::make_pair(token, fetcher);
96 fetcher->Start();
102 void
103 MergeSessionHelper::ExternalCcResultFetcher::OnGetCheckConnectionInfoError(
104 const GoogleServiceAuthError& error) {
105 CleanupTransientState();
106 FireGetCheckConnectionInfoCompleted(false);
109 net::URLFetcher* MergeSessionHelper::ExternalCcResultFetcher::CreateFetcher(
110 const GURL& url) {
111 net::URLFetcher* fetcher = net::URLFetcher::Create(
113 url,
114 net::URLFetcher::GET,
115 this);
116 fetcher->SetRequestContext(helper_->request_context());
117 fetcher->SetLoadFlags(net::LOAD_DO_NOT_SEND_COOKIES |
118 net::LOAD_DO_NOT_SAVE_COOKIES);
120 // Fetchers are sometimes cancelled because a network change was detected,
121 // especially at startup and after sign-in on ChromeOS.
122 fetcher->SetAutomaticallyRetryOnNetworkChanges(1);
123 return fetcher;
126 void MergeSessionHelper::ExternalCcResultFetcher::OnURLFetchComplete(
127 const net::URLFetcher* source) {
128 const GURL& url = source->GetOriginalURL();
129 const net::URLRequestStatus& status = source->GetStatus();
130 int response_code = source->GetResponseCode();
131 if (status.is_success() && response_code == net::HTTP_OK &&
132 fetchers_.count(url) > 0) {
133 std::string data;
134 source->GetResponseAsString(&data);
135 // Only up to the first 16 characters of the response are important to GAIA.
136 // Truncate if needed to keep amount data sent back to GAIA down.
137 if (data.size() > 16)
138 data.resize(16);
139 results_[fetchers_[url].first] = data;
141 // Clean up tracking of this fetcher. The rest will be cleaned up after
142 // the timer expires in CleanupTransientState().
143 DCHECK_EQ(source, fetchers_[url].second);
144 fetchers_.erase(url);
145 delete source;
147 // If all expected responses have been received, cancel the timer and
148 // report the result.
149 if (fetchers_.empty()) {
150 CleanupTransientState();
151 FireGetCheckConnectionInfoCompleted(true);
156 void MergeSessionHelper::ExternalCcResultFetcher::Timeout() {
157 CleanupTransientState();
158 FireGetCheckConnectionInfoCompleted(false);
161 void MergeSessionHelper::ExternalCcResultFetcher::CleanupTransientState() {
162 timer_.Stop();
163 gaia_auth_fetcher_.reset();
165 for (URLToTokenAndFetcher::const_iterator it = fetchers_.begin();
166 it != fetchers_.end(); ++it) {
167 delete it->second.second;
169 fetchers_.clear();
172 void MergeSessionHelper::ExternalCcResultFetcher::
173 FireGetCheckConnectionInfoCompleted(bool succeeded) {
174 FOR_EACH_OBSERVER(Observer, helper_->observer_list_,
175 GetCheckConnectionInfoCompleted(succeeded));
178 MergeSessionHelper::MergeSessionHelper(
179 OAuth2TokenService* token_service,
180 const std::string& source,
181 net::URLRequestContextGetter* request_context,
182 Observer* observer)
183 : token_service_(token_service),
184 request_context_(request_context),
185 result_fetcher_(this),
186 source_(source) {
187 if (observer)
188 AddObserver(observer);
191 MergeSessionHelper::~MergeSessionHelper() {
192 DCHECK(accounts_.empty());
195 void MergeSessionHelper::LogIn(const std::string& account_id) {
196 DCHECK(!account_id.empty());
197 VLOG(1) << "MergeSessionHelper::LogIn: " << account_id;
198 accounts_.push_back(account_id);
199 if (accounts_.size() == 1)
200 StartFetching();
203 void MergeSessionHelper::AddObserver(Observer* observer) {
204 observer_list_.AddObserver(observer);
207 void MergeSessionHelper::RemoveObserver(Observer* observer) {
208 observer_list_.RemoveObserver(observer);
211 void MergeSessionHelper::CancelAll() {
212 VLOG(1) << "MergeSessionHelper::CancelAll";
213 gaia_auth_fetcher_.reset();
214 uber_token_fetcher_.reset();
215 accounts_.clear();
218 void MergeSessionHelper::LogOut(
219 const std::string& account_id,
220 const std::vector<std::string>& accounts) {
221 DCHECK(!account_id.empty());
222 VLOG(1) << "MergeSessionHelper::LogOut: " << account_id
223 << " accounts=" << accounts.size();
224 LogOutInternal(account_id, accounts);
227 void MergeSessionHelper::LogOutInternal(
228 const std::string& account_id,
229 const std::vector<std::string>& accounts) {
230 bool pending = !accounts_.empty();
232 if (pending) {
233 for (std::deque<std::string>::const_iterator it = accounts_.begin() + 1;
234 it != accounts_.end(); it++) {
235 if (!it->empty() &&
236 (std::find(accounts.begin(), accounts.end(), *it) == accounts.end() ||
237 *it == account_id)) {
238 // We have a pending log in request for an account followed by
239 // a signout.
240 GoogleServiceAuthError error(GoogleServiceAuthError::REQUEST_CANCELED);
241 SignalComplete(*it, error);
245 // Remove every thing in the work list besides the one that is running.
246 accounts_.resize(1);
249 // Signal a logout to be the next thing to do unless the pending
250 // action is already a logout.
251 if (!pending || !accounts_.front().empty())
252 accounts_.push_back("");
254 for (std::vector<std::string>::const_iterator it = accounts.begin();
255 it != accounts.end(); it++) {
256 if (*it != account_id) {
257 DCHECK(!it->empty());
258 accounts_.push_back(*it);
262 if (!pending)
263 StartLogOutUrlFetch();
266 void MergeSessionHelper::LogOutAllAccounts() {
267 VLOG(1) << "MergeSessionHelper::LogOutAllAccounts";
268 LogOutInternal("", std::vector<std::string>());
271 void MergeSessionHelper::SignalComplete(
272 const std::string& account_id,
273 const GoogleServiceAuthError& error) {
274 // Its possible for the observer to delete |this| object. Don't access
275 // access any members after this calling the observer. This method should
276 // be the last call in any other method.
277 FOR_EACH_OBSERVER(Observer, observer_list_,
278 MergeSessionCompleted(account_id, error));
281 void MergeSessionHelper::StartFetchingExternalCcResult() {
282 result_fetcher_.Start();
285 bool MergeSessionHelper::StillFetchingExternalCcResult() {
286 return result_fetcher_.IsRunning();
289 void MergeSessionHelper::StartLogOutUrlFetch() {
290 DCHECK(accounts_.front().empty());
291 VLOG(1) << "MergeSessionHelper::StartLogOutUrlFetch";
292 GURL logout_url(GaiaUrls::GetInstance()->service_logout_url().Resolve(
293 base::StringPrintf("?source=%s", source_.c_str())));
294 net::URLFetcher* fetcher =
295 net::URLFetcher::Create(logout_url, net::URLFetcher::GET, this);
296 fetcher->SetRequestContext(request_context_);
297 fetcher->Start();
300 void MergeSessionHelper::OnUbertokenSuccess(const std::string& uber_token) {
301 VLOG(1) << "MergeSessionHelper::OnUbertokenSuccess"
302 << " account=" << accounts_.front();
303 gaia_auth_fetcher_.reset(new GaiaAuthFetcher(this,
304 source_,
305 request_context_));
307 // It's possible that not all external checks have completed.
308 // GetExternalCcResult() returns results for those that have.
309 gaia_auth_fetcher_->StartMergeSession(uber_token,
310 result_fetcher_.GetExternalCcResult());
313 void MergeSessionHelper::OnUbertokenFailure(
314 const GoogleServiceAuthError& error) {
315 VLOG(1) << "Failed to retrieve ubertoken"
316 << " account=" << accounts_.front()
317 << " error=" << error.ToString();
318 const std::string account_id = accounts_.front();
319 HandleNextAccount();
320 SignalComplete(account_id, error);
323 void MergeSessionHelper::OnMergeSessionSuccess(const std::string& data) {
324 VLOG(1) << "MergeSession successful account=" << accounts_.front();
325 const std::string account_id = accounts_.front();
326 HandleNextAccount();
327 SignalComplete(account_id, GoogleServiceAuthError::AuthErrorNone());
330 void MergeSessionHelper::OnMergeSessionFailure(
331 const GoogleServiceAuthError& error) {
332 VLOG(1) << "Failed MergeSession"
333 << " account=" << accounts_.front()
334 << " error=" << error.ToString();
335 const std::string account_id = accounts_.front();
336 HandleNextAccount();
337 SignalComplete(account_id, error);
340 void MergeSessionHelper::StartFetching() {
341 VLOG(1) << "MergeSessionHelper::StartFetching account_id="
342 << accounts_.front();
343 uber_token_fetcher_.reset(new UbertokenFetcher(token_service_,
344 this,
345 source_,
346 request_context_));
347 uber_token_fetcher_->StartFetchingToken(accounts_.front());
350 void MergeSessionHelper::OnURLFetchComplete(const net::URLFetcher* source) {
351 DCHECK(accounts_.front().empty());
352 VLOG(1) << "MergeSessionHelper::OnURLFetchComplete";
353 HandleNextAccount();
356 void MergeSessionHelper::HandleNextAccount() {
357 VLOG(1) << "MergeSessionHelper::HandleNextAccount";
358 accounts_.pop_front();
359 gaia_auth_fetcher_.reset();
360 if (accounts_.empty()) {
361 VLOG(1) << "MergeSessionHelper::HandleNextAccount: no more";
362 uber_token_fetcher_.reset();
363 } else {
364 if (accounts_.front().empty()) {
365 StartLogOutUrlFetch();
366 } else {
367 StartFetching();