diff options
author | cbentzel@chromium.org <cbentzel@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-04-14 20:12:45 +0000 |
---|---|---|
committer | cbentzel@chromium.org <cbentzel@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-04-14 20:12:45 +0000 |
commit | e5ae96a15b687fffe178eb8c4a7ea79a1ddd679c (patch) | |
tree | 4ffb7e20097a482248f465eef2a7540d79d58e8f /net/http/http_network_transaction_unittest.cc | |
parent | da5922762971a646407390f5c8d88a2447b2effc (diff) | |
download | chromium_src-e5ae96a15b687fffe178eb8c4a7ea79a1ddd679c.zip chromium_src-e5ae96a15b687fffe178eb8c4a7ea79a1ddd679c.tar.gz chromium_src-e5ae96a15b687fffe178eb8c4a7ea79a1ddd679c.tar.bz2 |
Kerberos uses an SPN (Service Principal Name) to identify a server. This is typically in the form "HTTP/host:port", with the ":port" suffix being optional, and the "HTTP/" prefix is fixed regardless of whether the service is accessed over HTTP or HTTPS.
The issue this is fixing is that the URL host may be an incomplete domain name, a numerical address, or an alias for a canonical DNS name.
By default, Chrome will skip adding the optional port to the SPN, and will use the canonical DNS name for the server (which may be the original server name if it is an A or AAAA record). This matches IE and Firefox's default behavior.
Some intranets are set up so the original host name should be used rather than the canonical name. The canonical name resolution can be disabled with the --disable-spnego-cname-lookup command line flag.
Some intranets are also set up so the optional port should be specified when it is non-standard (non 80 or 443). Use the --enable-spnego-port command line flag.
BUG=29862
TEST=net_unittests.exe --gtest_filter="*CanonicalName*"
Review URL: http://codereview.chromium.org/1535019
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@44526 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'net/http/http_network_transaction_unittest.cc')
-rw-r--r-- | net/http/http_network_transaction_unittest.cc | 198 |
1 files changed, 197 insertions, 1 deletions
diff --git a/net/http/http_network_transaction_unittest.cc b/net/http/http_network_transaction_unittest.cc index aa18df5..b9458d5 100644 --- a/net/http/http_network_transaction_unittest.cc +++ b/net/http/http_network_transaction_unittest.cc @@ -65,7 +65,6 @@ ProxyService* CreateFixedProxyService(const std::string& proxy) { return ProxyService::CreateFixed(proxy_config); } - HttpNetworkSession* CreateSession(SessionDependencies* session_deps) { return new HttpNetworkSession(NULL, session_deps->host_resolver, @@ -4679,4 +4678,201 @@ TEST_F(HttpNetworkTransactionTest, FailNpnSpdyAndFallback) { EXPECT_EQ("hello world", response_data); } +// MockAuthHandlerCanonical is used by the ResolveCanonicalName +// HttpNetworkTransaction unit test below. Callers set up expectations for +// whether the canonical name needs to be resolved. +class MockAuthHandlerCanonical : public HttpAuthHandler { + public: + enum Resolve { + RESOLVE_INIT, + RESOLVE_SKIP, + RESOLVE_SYNC, + RESOLVE_ASYNC, + RESOLVE_TESTED, + }; + + MockAuthHandlerCanonical() : resolve_(RESOLVE_INIT), user_callback_(NULL) {} + virtual ~MockAuthHandlerCanonical() {} + + void SetResolveExpectation(Resolve resolve) { + EXPECT_EQ(RESOLVE_INIT, resolve_); + resolve_ = resolve; + } + + void ResetResolveExpectation() { + EXPECT_EQ(RESOLVE_TESTED, resolve_); + resolve_ = RESOLVE_INIT; + } + + virtual bool NeedsCanonicalName() { + switch (resolve_) { + case RESOLVE_SYNC: + case RESOLVE_ASYNC: + return true; + case RESOLVE_SKIP: + resolve_ = RESOLVE_TESTED; + return false; + default: + NOTREACHED(); + return false; + } + } + + virtual int ResolveCanonicalName(HostResolver* host_resolver, + CompletionCallback* callback, + const BoundNetLog& net_log) { + EXPECT_NE(RESOLVE_TESTED, resolve_); + int rv = OK; + switch (resolve_) { + case RESOLVE_SYNC: + resolve_ = RESOLVE_TESTED; + break; + case RESOLVE_ASYNC: + EXPECT_TRUE(user_callback_ == NULL); + rv = ERR_IO_PENDING; + user_callback_ = callback; + MessageLoop::current()->PostTask( + FROM_HERE, + NewRunnableMethod( + this, &MockAuthHandlerCanonical::OnResolveCanonicalName)); + break; + default: + NOTREACHED(); + break; + } + return rv; + } + + void OnResolveCanonicalName() { + EXPECT_EQ(RESOLVE_ASYNC, resolve_); + EXPECT_TRUE(user_callback_ != NULL); + resolve_ = RESOLVE_TESTED; + CompletionCallback* callback = user_callback_; + user_callback_ = NULL; + callback->Run(OK); + } + + virtual bool Init(HttpAuth::ChallengeTokenizer* challenge) { + scheme_ = "mock"; + score_ = 1; + properties_ = 0; + return true; + } + + virtual int GenerateAuthToken(const std::wstring& username, + const std::wstring& password, + const HttpRequestInfo* request, + const ProxyInfo* proxy, + std::string* auth_token) { + auth_token->assign("Mock AUTH myserver.example.com"); + return OK; + } + + virtual int GenerateDefaultAuthToken(const HttpRequestInfo* request, + const ProxyInfo* proxy, + std::string* auth_token) { + auth_token->assign("Mock DEFAULT_AUTH myserver.example.com"); + return OK; + } + + // The Factory class simply returns the same handler each time + // CreateAuthHandler is called. + class Factory : public HttpAuthHandlerFactory { + public: + Factory() {} + virtual ~Factory() {} + + void set_mock_handler(MockAuthHandlerCanonical* mock_handler) { + mock_handler_ = mock_handler; + } + MockAuthHandlerCanonical* mock_handler() const { + return mock_handler_.get(); + } + + virtual int CreateAuthHandler(HttpAuth::ChallengeTokenizer* challenge, + HttpAuth::Target target, + const GURL& origin, + scoped_refptr<HttpAuthHandler>* handler) { + *handler = mock_handler_; + return OK; + } + + private: + scoped_refptr<MockAuthHandlerCanonical> mock_handler_; + }; + + private: + Resolve resolve_; + CompletionCallback* user_callback_; +}; + +// Tests that ResolveCanonicalName is handled correctly by the +// HttpNetworkTransaction. +TEST_F(HttpNetworkTransactionTest, ResolveCanonicalName) { + SessionDependencies session_deps; + scoped_refptr<MockAuthHandlerCanonical> auth_handler( + new MockAuthHandlerCanonical()); + auth_handler->Init(NULL); + MockAuthHandlerCanonical::Factory* auth_factory( + new MockAuthHandlerCanonical::Factory()); + auth_factory->set_mock_handler(auth_handler); + session_deps.http_auth_handler_factory.reset(auth_factory); + + for (int i = 0; i < 2; ++i) { + scoped_ptr<HttpTransaction> trans( + new HttpNetworkTransaction(CreateSession(&session_deps))); + + // Set up expectations for this pass of the test. Many of the EXPECT calls + // are contained inside the MockAuthHandlerCanonical codebase in response to + // the expectations. + MockAuthHandlerCanonical::Resolve resolve = (i == 0) ? + MockAuthHandlerCanonical::RESOLVE_SYNC : + MockAuthHandlerCanonical::RESOLVE_ASYNC; + auth_handler->SetResolveExpectation(resolve); + HttpRequestInfo request; + request.method = "GET"; + request.url = GURL("http://myserver/"); + request.load_flags = 0; + + MockWrite data_writes1[] = { + MockWrite("GET / HTTP/1.1\r\n" + "Host: myserver\r\n" + "Connection: keep-alive\r\n\r\n"), + }; + + MockRead data_reads1[] = { + MockRead("HTTP/1.1 401 Unauthorized\r\n"), + MockRead("WWW-Authenticate: Mock myserver.example.com\r\n"), + MockRead("Content-Type: text/html; charset=iso-8859-1\r\n"), + MockRead("Content-Length: 14\r\n\r\n"), + MockRead("Unauthorized\r\n"), + }; + + StaticSocketDataProvider data1(data_reads1, arraysize(data_reads1), + data_writes1, arraysize(data_writes1)); + session_deps.socket_factory.AddSocketDataProvider(&data1); + + TestCompletionCallback callback1; + + int rv = trans->Start(&request, &callback1, NULL); + EXPECT_EQ(ERR_IO_PENDING, rv); + + rv = callback1.WaitForResult(); + EXPECT_EQ(OK, rv); + + const HttpResponseInfo* response = trans->GetResponseInfo(); + EXPECT_FALSE(response == NULL); + + // The password prompt is set after the canonical name is resolved. + // If it isn't present or is incorrect, it indicates that the scheme + // did not complete correctly. + EXPECT_FALSE(response->auth_challenge.get() == NULL); + + EXPECT_EQ(L"myserver:80", response->auth_challenge->host_and_port); + EXPECT_EQ(L"", response->auth_challenge->realm); + EXPECT_EQ(L"mock", response->auth_challenge->scheme); + auth_handler->ResetResolveExpectation(); + } +} + } // namespace net |