blob: 0c737ad5ce4a0739455f0d3750dd4f126a17b6d0 [file] [log] [blame]
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "google_apis/gaia/oauth2_token_service_request.h"
#include "base/bind.h"
#include "base/memory/ref_counted.h"
#include "base/memory/scoped_ptr.h"
#include "base/single_thread_task_runner.h"
#include "base/thread_task_runner_handle.h"
#include "google_apis/gaia/google_service_auth_error.h"
#include "google_apis/gaia/oauth2_access_token_consumer.h"
OAuth2TokenServiceRequest::TokenServiceProvider::TokenServiceProvider() {
}
OAuth2TokenServiceRequest::TokenServiceProvider::~TokenServiceProvider() {
}
// Core serves as the base class for OAuth2TokenService operations. Each
// operation should be modeled as a derived type.
//
// Core is used like this:
//
// 1. Constructed on owner thread.
//
// 2. Start() is called on owner thread, which calls StartOnTokenServiceThread()
// on token service thread.
//
// 3. Request is executed.
//
// 4. Stop() is called on owner thread, which calls StopOnTokenServiceThread()
// on token service thread.
//
// 5. Core is destroyed on owner thread.
class OAuth2TokenServiceRequest::Core
: public base::NonThreadSafe,
public base::RefCountedThreadSafe<OAuth2TokenServiceRequest::Core> {
public:
// Note the thread where an instance of Core is constructed is referred to as
// the "owner thread" here.
Core(OAuth2TokenServiceRequest* owner,
const scoped_refptr<TokenServiceProvider>& provider);
// Starts the core. Must be called on the owner thread.
void Start();
// Stops the core. Must be called on the owner thread.
void Stop();
// Returns true if this object has been stopped. Must be called on the owner
// thread.
bool IsStopped() const;
protected:
// Core must be destroyed on the owner thread. If data members must be
// cleaned up or destroyed on the token service thread, do so in the
// StopOnTokenServiceThread method.
virtual ~Core();
// Called on the token service thread.
virtual void StartOnTokenServiceThread() = 0;
// Called on the token service thread.
virtual void StopOnTokenServiceThread() = 0;
base::SingleThreadTaskRunner* token_service_task_runner();
OAuth2TokenService* token_service();
OAuth2TokenServiceRequest* owner();
private:
friend class base::RefCountedThreadSafe<OAuth2TokenServiceRequest::Core>;
void DoNothing();
scoped_refptr<base::SingleThreadTaskRunner> token_service_task_runner_;
OAuth2TokenServiceRequest* owner_;
// Clear on owner thread. OAuth2TokenServiceRequest promises to clear its
// last reference to TokenServiceProvider on the owner thread so the caller
// can ensure it is destroyed on the owner thread if desired.
scoped_refptr<TokenServiceProvider> provider_;
DISALLOW_COPY_AND_ASSIGN(Core);
};
OAuth2TokenServiceRequest::Core::Core(
OAuth2TokenServiceRequest* owner,
const scoped_refptr<TokenServiceProvider>& provider)
: owner_(owner), provider_(provider) {
DCHECK(owner_);
DCHECK(provider_);
token_service_task_runner_ = provider_->GetTokenServiceTaskRunner();
DCHECK(token_service_task_runner_);
}
OAuth2TokenServiceRequest::Core::~Core() {
}
void OAuth2TokenServiceRequest::Core::Start() {
DCHECK(CalledOnValidThread());
token_service_task_runner_->PostTask(
FROM_HERE,
base::Bind(&OAuth2TokenServiceRequest::Core::StartOnTokenServiceThread,
this));
}
void OAuth2TokenServiceRequest::Core::Stop() {
DCHECK(CalledOnValidThread());
DCHECK(!IsStopped());
// Detaches |owner_| from this instance so |owner_| will be called back only
// if |Stop()| has never been called.
owner_ = NULL;
// We are stopping and will likely be destroyed soon. Use a reply closure
// (DoNothing) to retain "this" and ensure we are destroyed in the owner
// thread, not the task runner thread. PostTaskAndReply guarantees that the
// reply closure will execute after StopOnTokenServiceThread has completed.
token_service_task_runner_->PostTaskAndReply(
FROM_HERE,
base::Bind(&OAuth2TokenServiceRequest::Core::StopOnTokenServiceThread,
this),
base::Bind(&OAuth2TokenServiceRequest::Core::DoNothing, this));
}
bool OAuth2TokenServiceRequest::Core::IsStopped() const {
DCHECK(CalledOnValidThread());
return owner_ == NULL;
}
base::SingleThreadTaskRunner*
OAuth2TokenServiceRequest::Core::token_service_task_runner() {
return token_service_task_runner_;
}
OAuth2TokenService* OAuth2TokenServiceRequest::Core::token_service() {
DCHECK(token_service_task_runner_->BelongsToCurrentThread());
return provider_->GetTokenService();
}
OAuth2TokenServiceRequest* OAuth2TokenServiceRequest::Core::owner() {
DCHECK(CalledOnValidThread());
return owner_;
}
void OAuth2TokenServiceRequest::Core::DoNothing() {
DCHECK(CalledOnValidThread());
}
namespace {
// An implementation of Core for getting an access token.
class RequestCore : public OAuth2TokenServiceRequest::Core,
public OAuth2TokenService::Consumer {
public:
RequestCore(OAuth2TokenServiceRequest* owner,
const scoped_refptr<
OAuth2TokenServiceRequest::TokenServiceProvider>& provider,
OAuth2TokenService::Consumer* consumer,
const std::string& account_id,
const OAuth2TokenService::ScopeSet& scopes);
// OAuth2TokenService::Consumer. Must be called on the token service thread.
virtual void OnGetTokenSuccess(const OAuth2TokenService::Request* request,
const std::string& access_token,
const base::Time& expiration_time) OVERRIDE;
virtual void OnGetTokenFailure(const OAuth2TokenService::Request* request,
const GoogleServiceAuthError& error) OVERRIDE;
private:
friend class base::RefCountedThreadSafe<RequestCore>;
// Must be destroyed on the owner thread.
virtual ~RequestCore();
// Core implementation.
virtual void StartOnTokenServiceThread() OVERRIDE;
virtual void StopOnTokenServiceThread() OVERRIDE;
void InformOwnerOnGetTokenSuccess(std::string access_token,
base::Time expiration_time);
void InformOwnerOnGetTokenFailure(GoogleServiceAuthError error);
scoped_refptr<base::SingleThreadTaskRunner> owner_task_runner_;
OAuth2TokenService::Consumer* const consumer_;
std::string account_id_;
OAuth2TokenService::ScopeSet scopes_;
// OAuth2TokenService request for fetching OAuth2 access token; it should be
// created, reset and accessed only on the token service thread.
scoped_ptr<OAuth2TokenService::Request> request_;
DISALLOW_COPY_AND_ASSIGN(RequestCore);
};
RequestCore::RequestCore(
OAuth2TokenServiceRequest* owner,
const scoped_refptr<OAuth2TokenServiceRequest::TokenServiceProvider>&
provider,
OAuth2TokenService::Consumer* consumer,
const std::string& account_id,
const OAuth2TokenService::ScopeSet& scopes)
: OAuth2TokenServiceRequest::Core(owner, provider),
OAuth2TokenService::Consumer("oauth2_token_service"),
owner_task_runner_(base::ThreadTaskRunnerHandle::Get()),
consumer_(consumer),
account_id_(account_id),
scopes_(scopes) {
DCHECK(consumer_);
DCHECK(!account_id_.empty());
DCHECK(!scopes_.empty());
}
RequestCore::~RequestCore() {
}
void RequestCore::StartOnTokenServiceThread() {
DCHECK(token_service_task_runner()->BelongsToCurrentThread());
request_ = token_service()->StartRequest(account_id_, scopes_, this).Pass();
}
void RequestCore::StopOnTokenServiceThread() {
DCHECK(token_service_task_runner()->BelongsToCurrentThread());
request_.reset();
}
void RequestCore::OnGetTokenSuccess(const OAuth2TokenService::Request* request,
const std::string& access_token,
const base::Time& expiration_time) {
DCHECK(token_service_task_runner()->BelongsToCurrentThread());
DCHECK_EQ(request_.get(), request);
owner_task_runner_->PostTask(
FROM_HERE,
base::Bind(&RequestCore::InformOwnerOnGetTokenSuccess,
this,
access_token,
expiration_time));
request_.reset();
}
void RequestCore::OnGetTokenFailure(const OAuth2TokenService::Request* request,
const GoogleServiceAuthError& error) {
DCHECK(token_service_task_runner()->BelongsToCurrentThread());
DCHECK_EQ(request_.get(), request);
owner_task_runner_->PostTask(
FROM_HERE,
base::Bind(&RequestCore::InformOwnerOnGetTokenFailure, this, error));
request_.reset();
}
void RequestCore::InformOwnerOnGetTokenSuccess(std::string access_token,
base::Time expiration_time) {
DCHECK(CalledOnValidThread());
if (!IsStopped()) {
consumer_->OnGetTokenSuccess(owner(), access_token, expiration_time);
}
}
void RequestCore::InformOwnerOnGetTokenFailure(GoogleServiceAuthError error) {
DCHECK(CalledOnValidThread());
if (!IsStopped()) {
consumer_->OnGetTokenFailure(owner(), error);
}
}
// An implementation of Core for invalidating an access token.
class InvalidateCore : public OAuth2TokenServiceRequest::Core {
public:
InvalidateCore(OAuth2TokenServiceRequest* owner,
const scoped_refptr<
OAuth2TokenServiceRequest::TokenServiceProvider>& provider,
const std::string& access_token,
const std::string& account_id,
const OAuth2TokenService::ScopeSet& scopes);
private:
friend class base::RefCountedThreadSafe<InvalidateCore>;
// Must be destroyed on the owner thread.
virtual ~InvalidateCore();
// Core implementation.
virtual void StartOnTokenServiceThread() OVERRIDE;
virtual void StopOnTokenServiceThread() OVERRIDE;
std::string access_token_;
std::string account_id_;
OAuth2TokenService::ScopeSet scopes_;
DISALLOW_COPY_AND_ASSIGN(InvalidateCore);
};
InvalidateCore::InvalidateCore(
OAuth2TokenServiceRequest* owner,
const scoped_refptr<OAuth2TokenServiceRequest::TokenServiceProvider>&
provider,
const std::string& access_token,
const std::string& account_id,
const OAuth2TokenService::ScopeSet& scopes)
: OAuth2TokenServiceRequest::Core(owner, provider),
access_token_(access_token),
account_id_(account_id),
scopes_(scopes) {
DCHECK(!access_token_.empty());
DCHECK(!account_id_.empty());
DCHECK(!scopes.empty());
}
InvalidateCore::~InvalidateCore() {
}
void InvalidateCore::StartOnTokenServiceThread() {
DCHECK(token_service_task_runner()->BelongsToCurrentThread());
token_service()->InvalidateToken(account_id_, scopes_, access_token_);
}
void InvalidateCore::StopOnTokenServiceThread() {
DCHECK(token_service_task_runner()->BelongsToCurrentThread());
// Nothing to do.
}
} // namespace
// static
scoped_ptr<OAuth2TokenServiceRequest> OAuth2TokenServiceRequest::CreateAndStart(
const scoped_refptr<TokenServiceProvider>& provider,
const std::string& account_id,
const OAuth2TokenService::ScopeSet& scopes,
OAuth2TokenService::Consumer* consumer) {
scoped_ptr<OAuth2TokenServiceRequest> request(
new OAuth2TokenServiceRequest(account_id));
scoped_refptr<Core> core(
new RequestCore(request.get(), provider, consumer, account_id, scopes));
request->StartWithCore(core);
return request.Pass();
}
// static
void OAuth2TokenServiceRequest::InvalidateToken(
const scoped_refptr<TokenServiceProvider>& provider,
const std::string& account_id,
const OAuth2TokenService::ScopeSet& scopes,
const std::string& access_token) {
scoped_ptr<OAuth2TokenServiceRequest> request(
new OAuth2TokenServiceRequest(account_id));
scoped_refptr<Core> core(new InvalidateCore(
request.get(), provider, access_token, account_id, scopes));
request->StartWithCore(core);
}
OAuth2TokenServiceRequest::~OAuth2TokenServiceRequest() {
core_->Stop();
}
std::string OAuth2TokenServiceRequest::GetAccountId() const {
return account_id_;
}
OAuth2TokenServiceRequest::OAuth2TokenServiceRequest(
const std::string& account_id)
: account_id_(account_id) {
DCHECK(!account_id_.empty());
}
void OAuth2TokenServiceRequest::StartWithCore(const scoped_refptr<Core>& core) {
DCHECK(core);
core_ = core;
core_->Start();
}