// Copyright (c) 2011 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 "net/http/http_auth_handler_mock.h" #include "base/bind.h" #include "base/callback_helpers.h" #include "base/location.h" #include "base/memory/ptr_util.h" #include "base/single_thread_task_runner.h" #include "base/strings/string_util.h" #include "base/threading/thread_task_runner_handle.h" #include "net/base/net_errors.h" #include "net/http/http_auth_challenge_tokenizer.h" #include "net/http/http_request_info.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" namespace net { void PrintTo(const HttpAuthHandlerMock::State& state, ::std::ostream* os) { switch (state) { case HttpAuthHandlerMock::State::WAIT_FOR_INIT: *os << "WAIT_FOR_INIT"; break; case HttpAuthHandlerMock::State::WAIT_FOR_CHALLENGE: *os << "WAIT_FOR_CHALLENGE"; break; case HttpAuthHandlerMock::State::WAIT_FOR_GENERATE_AUTH_TOKEN: *os << "WAIT_FOR_GENERATE_AUTH_TOKEN"; break; case HttpAuthHandlerMock::State::TOKEN_PENDING: *os << "TOKEN_PENDING"; break; case HttpAuthHandlerMock::State::DONE: *os << "DONE"; break; } } HttpAuthHandlerMock::HttpAuthHandlerMock() : state_(State::WAIT_FOR_INIT), resolve_(RESOLVE_INIT), generate_async_(false), generate_rv_(OK), auth_token_(NULL), first_round_(true), connection_based_(false), allows_default_credentials_(false), allows_explicit_credentials_(true), weak_factory_(this) {} HttpAuthHandlerMock::~HttpAuthHandlerMock() = default; void HttpAuthHandlerMock::SetResolveExpectation(Resolve resolve) { EXPECT_EQ(RESOLVE_INIT, resolve_); resolve_ = resolve; } bool HttpAuthHandlerMock::NeedsCanonicalName() { switch (resolve_) { case RESOLVE_SYNC: case RESOLVE_ASYNC: return true; case RESOLVE_SKIP: resolve_ = RESOLVE_TESTED; return false; default: NOTREACHED(); return false; } } int HttpAuthHandlerMock::ResolveCanonicalName( HostResolver* host_resolver, const CompletionCallback& callback) { EXPECT_NE(RESOLVE_TESTED, resolve_); int rv = OK; switch (resolve_) { case RESOLVE_SYNC: resolve_ = RESOLVE_TESTED; break; case RESOLVE_ASYNC: EXPECT_TRUE(callback_.is_null()); rv = ERR_IO_PENDING; callback_ = callback; base::ThreadTaskRunnerHandle::Get()->PostTask( FROM_HERE, base::Bind(&HttpAuthHandlerMock::OnResolveCanonicalName, weak_factory_.GetWeakPtr())); break; default: NOTREACHED(); break; } return rv; } void HttpAuthHandlerMock::SetGenerateExpectation(bool async, int rv) { generate_async_ = async; generate_rv_ = rv; } HttpAuth::AuthorizationResult HttpAuthHandlerMock::HandleAnotherChallenge( HttpAuthChallengeTokenizer* challenge) { EXPECT_THAT(state_, ::testing::AnyOf(State::WAIT_FOR_CHALLENGE, State::WAIT_FOR_GENERATE_AUTH_TOKEN)); // If we receive an empty challenge for a connection based scheme, or a second // challenge for a non connection based scheme, assume it's a rejection. if (!is_connection_based() || challenge->base64_param().empty()) { state_ = State::DONE; return HttpAuth::AUTHORIZATION_RESULT_REJECT; } if (!base::LowerCaseEqualsASCII(challenge->scheme(), "mock")) { state_ = State::DONE; return HttpAuth::AUTHORIZATION_RESULT_INVALID; } state_ = State::WAIT_FOR_GENERATE_AUTH_TOKEN; return HttpAuth::AUTHORIZATION_RESULT_ACCEPT; } bool HttpAuthHandlerMock::NeedsIdentity() { return first_round_; } bool HttpAuthHandlerMock::AllowsDefaultCredentials() { return allows_default_credentials_; } bool HttpAuthHandlerMock::AllowsExplicitCredentials() { return allows_explicit_credentials_; } bool HttpAuthHandlerMock::Init(HttpAuthChallengeTokenizer* challenge, const SSLInfo& ssl_info) { EXPECT_EQ(State::WAIT_FOR_INIT, state_); state_ = State::WAIT_FOR_GENERATE_AUTH_TOKEN; auth_scheme_ = HttpAuth::AUTH_SCHEME_MOCK; score_ = 1; properties_ = connection_based_ ? IS_CONNECTION_BASED : 0; return true; } int HttpAuthHandlerMock::GenerateAuthTokenImpl( const AuthCredentials* credentials, const HttpRequestInfo* request, const CompletionCallback& callback, std::string* auth_token) { EXPECT_EQ(State::WAIT_FOR_GENERATE_AUTH_TOKEN, state_); first_round_ = false; request_url_ = request->url; if (generate_async_) { EXPECT_TRUE(callback_.is_null()); EXPECT_TRUE(auth_token_ == NULL); callback_ = callback; auth_token_ = auth_token; base::ThreadTaskRunnerHandle::Get()->PostTask( FROM_HERE, base::Bind(&HttpAuthHandlerMock::OnGenerateAuthToken, weak_factory_.GetWeakPtr())); state_ = State::TOKEN_PENDING; return ERR_IO_PENDING; } else { if (generate_rv_ == OK) { *auth_token = "auth_token"; state_ = is_connection_based() ? State::WAIT_FOR_CHALLENGE : State::WAIT_FOR_GENERATE_AUTH_TOKEN; } else { state_ = State::DONE; } return generate_rv_; } } void HttpAuthHandlerMock::OnResolveCanonicalName() { EXPECT_EQ(RESOLVE_ASYNC, resolve_); EXPECT_TRUE(!callback_.is_null()); resolve_ = RESOLVE_TESTED; base::ResetAndReturn(&callback_).Run(OK); } void HttpAuthHandlerMock::OnGenerateAuthToken() { EXPECT_TRUE(generate_async_); EXPECT_TRUE(!callback_.is_null()); EXPECT_EQ(State::TOKEN_PENDING, state_); if (generate_rv_ == OK) { *auth_token_ = "auth_token"; state_ = is_connection_based() ? State::WAIT_FOR_CHALLENGE : State::WAIT_FOR_GENERATE_AUTH_TOKEN; } else { state_ = State::DONE; } auth_token_ = NULL; base::ResetAndReturn(&callback_).Run(generate_rv_); } HttpAuthHandlerMock::Factory::Factory() : do_init_from_challenge_(false) { // TODO(cbentzel): Default do_init_from_challenge_ to true. } HttpAuthHandlerMock::Factory::~Factory() = default; void HttpAuthHandlerMock::Factory::AddMockHandler( HttpAuthHandler* handler, HttpAuth::Target target) { handlers_[target].push_back(base::WrapUnique(handler)); } int HttpAuthHandlerMock::Factory::CreateAuthHandler( HttpAuthChallengeTokenizer* challenge, HttpAuth::Target target, const SSLInfo& ssl_info, const GURL& origin, CreateReason reason, int nonce_count, const NetLogWithSource& net_log, std::unique_ptr* handler) { if (handlers_[target].empty()) return ERR_UNEXPECTED; std::unique_ptr tmp_handler = std::move(handlers_[target][0]); std::vector>& handlers = handlers_[target]; handlers.erase(handlers.begin()); if (do_init_from_challenge_ && !tmp_handler->InitFromChallenge(challenge, target, ssl_info, origin, net_log)) return ERR_INVALID_RESPONSE; handler->swap(tmp_handler); return OK; } } // namespace net