diff options
author | cbentzel@chromium.org <cbentzel@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-06-17 10:42:15 +0000 |
---|---|---|
committer | cbentzel@chromium.org <cbentzel@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-06-17 10:42:15 +0000 |
commit | 044de06466c30b563c2e999af6d2f782d5fa73a5 (patch) | |
tree | a386f98f6ba96cb9d43676accdf9cb80a68b6a0a /net/http | |
parent | 38712b1abf0cd6b2a60387e402f2b05d937a8ba5 (diff) | |
download | chromium_src-044de06466c30b563c2e999af6d2f782d5fa73a5.zip chromium_src-044de06466c30b563c2e999af6d2f782d5fa73a5.tar.gz chromium_src-044de06466c30b563c2e999af6d2f782d5fa73a5.tar.bz2 |
HttpNetworkTransaction handles asynchronous auth token generation.
BUG=21050,42222
TEST=net_unittests
Review URL: http://codereview.chromium.org/2722009
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@50088 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'net/http')
-rw-r--r-- | net/http/http_auth.h | 1 | ||||
-rw-r--r-- | net/http/http_auth_handler.cc | 3 | ||||
-rw-r--r-- | net/http/http_auth_handler_basic_unittest.cc | 8 | ||||
-rw-r--r-- | net/http/http_network_transaction.cc | 153 | ||||
-rw-r--r-- | net/http/http_network_transaction.h | 40 | ||||
-rw-r--r-- | net/http/http_network_transaction_unittest.cc | 500 |
6 files changed, 600 insertions, 105 deletions
diff --git a/net/http/http_auth.h b/net/http/http_auth.h index b56a1d9..bba633d 100644 --- a/net/http/http_auth.h +++ b/net/http/http_auth.h @@ -29,6 +29,7 @@ class HttpAuth { // in an array, so start from 0. AUTH_PROXY = 0, AUTH_SERVER = 1, + AUTH_NUM_TARGETS = 2, }; // Describes where the identity used for authentication came from. diff --git a/net/http/http_auth_handler.cc b/net/http/http_auth_handler.cc index cf690bc..fe6f91d 100644 --- a/net/http/http_auth_handler.cc +++ b/net/http/http_auth_handler.cc @@ -37,10 +37,11 @@ int HttpAuthHandler::GenerateAuthToken(const std::wstring* username, const HttpRequestInfo* request, CompletionCallback* callback, std::string* auth_token) { - // TODO(cbentzel): Enforce non-NULL callback+auth_token. + // TODO(cbentzel): Enforce non-NULL callback after cleaning up SocketStream. DCHECK(request); DCHECK((username == NULL) == (password == NULL)); DCHECK(username != NULL || AllowsDefaultCredentials()); + DCHECK(auth_token != NULL); return GenerateAuthTokenImpl(username, password, request, callback, auth_token); } diff --git a/net/http/http_auth_handler_basic_unittest.cc b/net/http/http_auth_handler_basic_unittest.cc index 6b25deb..12e8830 100644 --- a/net/http/http_auth_handler_basic_unittest.cc +++ b/net/http/http_auth_handler_basic_unittest.cc @@ -32,14 +32,14 @@ TEST(HttpAuthHandlerBasicTest, GenerateAuthToken) { scoped_ptr<HttpAuthHandler> basic; EXPECT_EQ(OK, factory.CreateAuthHandlerFromString( challenge, HttpAuth::AUTH_SERVER, origin, BoundNetLog(), &basic)); - std::string credentials; std::wstring username(tests[i].username); std::wstring password(tests[i].password); HttpRequestInfo request_info; - int rv = basic->GenerateAuthToken( - &username, &password, &request_info, NULL, &credentials); + std::string auth_token; + int rv = basic->GenerateAuthToken(&username, &password, &request_info, + NULL, &auth_token); EXPECT_EQ(OK, rv); - EXPECT_STREQ(tests[i].expected_credentials, credentials.c_str()); + EXPECT_STREQ(tests[i].expected_credentials, auth_token.c_str()); } } diff --git a/net/http/http_network_transaction.cc b/net/http/http_network_transaction.cc index 27db570..ccc956b 100644 --- a/net/http/http_network_transaction.cc +++ b/net/http/http_network_transaction.cc @@ -353,9 +353,10 @@ int HttpNetworkTransaction::RestartIgnoringLastError( // TODO(wtc): Should we update any of the connection histograms that we // update in DoSSLConnectComplete if |result| is OK? if (using_spdy_) { + // TODO(cbentzel): Add auth support to spdy. See http://crbug.com/46620 next_state_ = STATE_SPDY_SEND_REQUEST; } else { - next_state_ = STATE_SEND_REQUEST; + next_state_ = STATE_GENERATE_PROXY_AUTH_TOKEN; } } else { connection_->socket()->Disconnect(); @@ -481,9 +482,9 @@ void HttpNetworkTransaction::DidDrainBodyForAuthRestart(bool keep_alive) { // often enough to be worth the trouble. if (using_ssl_ && proxy_info_.is_http() && ssl_connect_start_time_.is_null()) - next_state_ = STATE_TUNNEL_SEND_REQUEST; + next_state_ = STATE_TUNNEL_GENERATE_AUTH_TOKEN; else - next_state_ = STATE_SEND_REQUEST; + next_state_ = STATE_GENERATE_PROXY_AUTH_TOKEN; connection_->set_is_reused(true); reused_socket_ = true; } else { @@ -554,11 +555,14 @@ LoadState HttpNetworkTransaction::GetLoadState() const { return LOAD_STATE_RESOLVING_PROXY_FOR_URL; case STATE_INIT_CONNECTION_COMPLETE: return connection_->GetLoadState(); + case STATE_TUNNEL_GENERATE_AUTH_TOKEN_COMPLETE: case STATE_TUNNEL_SEND_REQUEST_COMPLETE: case STATE_TUNNEL_READ_HEADERS_COMPLETE: return LOAD_STATE_ESTABLISHING_PROXY_TUNNEL; case STATE_SSL_CONNECT_COMPLETE: return LOAD_STATE_SSL_HANDSHAKE; + case STATE_GENERATE_PROXY_AUTH_TOKEN_COMPLETE: + case STATE_GENERATE_SERVER_AUTH_TOKEN_COMPLETE: case STATE_SEND_REQUEST_COMPLETE: case STATE_SPDY_SEND_REQUEST_COMPLETE: return LOAD_STATE_SENDING_REQUEST; @@ -631,6 +635,13 @@ int HttpNetworkTransaction::DoLoop(int result) { case STATE_INIT_CONNECTION_COMPLETE: rv = DoInitConnectionComplete(rv); break; + case STATE_TUNNEL_GENERATE_AUTH_TOKEN: + DCHECK_EQ(OK, rv); + rv = DoTunnelGenerateAuthToken(); + break; + case STATE_TUNNEL_GENERATE_AUTH_TOKEN_COMPLETE: + rv = DoTunnelGenerateAuthTokenComplete(rv); + break; case STATE_TUNNEL_SEND_REQUEST: DCHECK_EQ(OK, rv); net_log_.BeginEvent(NetLog::TYPE_HTTP_TRANSACTION_TUNNEL_SEND_REQUEST, @@ -660,6 +671,20 @@ int HttpNetworkTransaction::DoLoop(int result) { case STATE_SSL_CONNECT_COMPLETE: rv = DoSSLConnectComplete(rv); break; + case STATE_GENERATE_PROXY_AUTH_TOKEN: + DCHECK_EQ(OK, rv); + rv = DoGenerateProxyAuthToken(); + break; + case STATE_GENERATE_PROXY_AUTH_TOKEN_COMPLETE: + rv = DoGenerateProxyAuthTokenComplete(rv); + break; + case STATE_GENERATE_SERVER_AUTH_TOKEN: + DCHECK_EQ(OK, rv); + rv = DoGenerateServerAuthToken(); + break; + case STATE_GENERATE_SERVER_AUTH_TOKEN_COMPLETE: + rv = DoGenerateServerAuthTokenComplete(rv); + break; case STATE_SEND_REQUEST: DCHECK_EQ(OK, rv); net_log_.BeginEvent(NetLog::TYPE_HTTP_TRANSACTION_SEND_REQUEST, NULL); @@ -934,6 +959,7 @@ int HttpNetworkTransaction::DoInitConnectionComplete(int result) { if (using_spdy_) { DCHECK(!connection_->is_initialized()); + // TODO(cbentzel): Add auth support to spdy. See http://crbug.com/46620 next_state_ = STATE_SPDY_SEND_REQUEST; return OK; } @@ -950,7 +976,7 @@ int HttpNetworkTransaction::DoInitConnectionComplete(int result) { reinterpret_cast<SSLClientSocket*>(connection_->socket()); response_.was_npn_negotiated = ssl_socket->wasNpnNegotiated(); } - next_state_ = STATE_SEND_REQUEST; + next_state_ = STATE_GENERATE_PROXY_AUTH_TOKEN; } else { // Now we have a TCP connected socket. Perform other connection setup as // needed. @@ -959,9 +985,9 @@ int HttpNetworkTransaction::DoInitConnectionComplete(int result) { if (proxy_info_.is_direct() || proxy_info_.is_socks()) next_state_ = STATE_SSL_CONNECT; else - next_state_ = STATE_TUNNEL_SEND_REQUEST; + next_state_ = STATE_TUNNEL_GENERATE_AUTH_TOKEN; } else { - next_state_ = STATE_SEND_REQUEST; + next_state_ = STATE_GENERATE_PROXY_AUTH_TOKEN; } } @@ -975,27 +1001,29 @@ void HttpNetworkTransaction::ClearTunnelState() { headers_valid_ = false; } +int HttpNetworkTransaction::DoTunnelGenerateAuthToken() { + next_state_ = STATE_TUNNEL_GENERATE_AUTH_TOKEN_COMPLETE; + return MaybeGenerateAuthToken(HttpAuth::AUTH_PROXY); +} + +int HttpNetworkTransaction::DoTunnelGenerateAuthTokenComplete(int rv) { + DCHECK_NE(ERR_IO_PENDING, rv); + if (rv == OK) + next_state_ = STATE_TUNNEL_SEND_REQUEST; + return rv; +} + int HttpNetworkTransaction::DoTunnelSendRequest() { next_state_ = STATE_TUNNEL_SEND_REQUEST_COMPLETE; // This is constructed lazily (instead of within our Start method), so that // we have proxy info available. if (request_headers_.empty()) { - // Figure out if we can/should add Proxy-Authentication headers. - bool have_proxy_auth = - HaveAuth(HttpAuth::AUTH_PROXY) || - SelectPreemptiveAuth(HttpAuth::AUTH_PROXY); - - std::string request_line; - HttpRequestHeaders request_headers; HttpRequestHeaders authorization_headers; - - // TODO(wtc): If BuildAuthorizationHeader fails (returns an authorization - // header with no credentials), we should return an error to prevent - // entering an infinite auth restart loop. See http://crbug.com/21050. - if (have_proxy_auth) + if (HaveAuth(HttpAuth::AUTH_PROXY)) AddAuthorizationHeader(HttpAuth::AUTH_PROXY, &authorization_headers); - + std::string request_line; + HttpRequestHeaders request_headers; BuildTunnelRequest(request_, authorization_headers, endpoint_, &request_line, &request_headers); if (net_log_.HasListener()) { @@ -1162,6 +1190,7 @@ int HttpNetworkTransaction::DoSSLConnectComplete(int result) { 100); UpdateConnectionTypeHistograms(CONNECTION_SPDY); + // TODO(cbentzel): Add auth support to spdy. See http://crbug.com/46620 next_state_ = STATE_SPDY_SEND_REQUEST; } else { UMA_HISTOGRAM_CUSTOM_TIMES("Net.SSL_Connection_Latency", @@ -1170,7 +1199,7 @@ int HttpNetworkTransaction::DoSSLConnectComplete(int result) { base::TimeDelta::FromMinutes(10), 100); - next_state_ = STATE_SEND_REQUEST; + next_state_ = STATE_GENERATE_PROXY_AUTH_TOKEN; } } else if (result == ERR_SSL_CLIENT_AUTH_CERT_NEEDED) { result = HandleCertificateRequest(result); @@ -1180,6 +1209,34 @@ int HttpNetworkTransaction::DoSSLConnectComplete(int result) { return result; } +int HttpNetworkTransaction::DoGenerateProxyAuthToken() { + next_state_ = STATE_GENERATE_PROXY_AUTH_TOKEN_COMPLETE; + if (!ShouldApplyProxyAuth()) + return OK; + return MaybeGenerateAuthToken(HttpAuth::AUTH_PROXY); +} + +int HttpNetworkTransaction::DoGenerateProxyAuthTokenComplete(int rv) { + DCHECK_NE(ERR_IO_PENDING, rv); + if (rv == OK) + next_state_ = STATE_GENERATE_SERVER_AUTH_TOKEN; + return rv; +} + +int HttpNetworkTransaction::DoGenerateServerAuthToken() { + next_state_ = STATE_GENERATE_SERVER_AUTH_TOKEN_COMPLETE; + if (!ShouldApplyServerAuth()) + return OK; + return MaybeGenerateAuthToken(HttpAuth::AUTH_SERVER); +} + +int HttpNetworkTransaction::DoGenerateServerAuthTokenComplete(int rv) { + DCHECK_NE(ERR_IO_PENDING, rv); + if (rv == OK) + next_state_ = STATE_SEND_REQUEST; + return rv; +} + int HttpNetworkTransaction::DoSendRequest() { next_state_ = STATE_SEND_REQUEST_COMPLETE; @@ -1196,27 +1253,17 @@ int HttpNetworkTransaction::DoSendRequest() { if (request_headers_.empty()) { // Figure out if we can/should add Proxy-Authentication & Authentication // headers. - bool have_proxy_auth = - ShouldApplyProxyAuth() && - (HaveAuth(HttpAuth::AUTH_PROXY) || - SelectPreemptiveAuth(HttpAuth::AUTH_PROXY)); - bool have_server_auth = - ShouldApplyServerAuth() && - (HaveAuth(HttpAuth::AUTH_SERVER) || - SelectPreemptiveAuth(HttpAuth::AUTH_SERVER)); - - std::string request_line; - HttpRequestHeaders request_headers; HttpRequestHeaders authorization_headers; - - // TODO(wtc): If BuildAuthorizationHeader fails (returns an authorization - // header with no credentials), we should return an error to prevent - // entering an infinite auth restart loop. See http://crbug.com/21050. + bool have_proxy_auth = (ShouldApplyProxyAuth() && + HaveAuth(HttpAuth::AUTH_PROXY)); + bool have_server_auth = (ShouldApplyServerAuth() && + HaveAuth(HttpAuth::AUTH_SERVER)); if (have_proxy_auth) AddAuthorizationHeader(HttpAuth::AUTH_PROXY, &authorization_headers); if (have_server_auth) AddAuthorizationHeader(HttpAuth::AUTH_SERVER, &authorization_headers); - + std::string request_line; + HttpRequestHeaders request_headers; BuildRequestHeaders(request_, authorization_headers, request_body, !using_ssl_ && proxy_info_.is_http(), &request_line, &request_headers); @@ -1244,15 +1291,12 @@ int HttpNetworkTransaction::DoSendRequest() { int HttpNetworkTransaction::DoSendRequestComplete(int result) { if (result < 0) return HandleIOError(result); - next_state_ = STATE_READ_HEADERS; - return OK; } int HttpNetworkTransaction::DoReadHeaders() { next_state_ = STATE_READ_HEADERS_COMPLETE; - return http_stream_->ReadResponseHeaders(&io_callback_); } @@ -1927,11 +1971,10 @@ bool HttpNetworkTransaction::ShouldApplyServerAuth() const { return !(request_->load_flags & LOAD_DO_NOT_SEND_AUTH_DATA); } -void HttpNetworkTransaction::AddAuthorizationHeader( - HttpAuth::Target target, HttpRequestHeaders* authorization_headers) const { - DCHECK(HaveAuth(target)); - - // Add a Authorization/Proxy-Authorization header line. +int HttpNetworkTransaction::MaybeGenerateAuthToken(HttpAuth::Target target) { + bool needs_auth = HaveAuth(target) || SelectPreemptiveAuth(target); + if (!needs_auth) + return OK; const std::wstring* username = NULL; const std::wstring* password = NULL; const HttpAuth::Identity& identity = auth_identity_[target]; @@ -1939,17 +1982,19 @@ void HttpNetworkTransaction::AddAuthorizationHeader( username = &identity.username; password = &identity.password; } - std::string auth_token; - int rv = auth_handler_[target]->GenerateAuthToken( - username, password, request_, NULL, &auth_token); - if (rv == OK) { - authorization_headers->SetHeader( - HttpAuth::GetAuthorizationHeaderName(target), auth_token); - } + DCHECK(auth_token_[target].empty()); + return auth_handler_[target]->GenerateAuthToken( + username, password, request_, &io_callback_, &auth_token_[target]); +} - // TODO(cbentzel): Evict username and password from cache on non-OK return? - // TODO(cbentzel): Never use this scheme again if - // ERR_UNSUPPORTED_AUTH_SCHEME is returned. +void HttpNetworkTransaction::AddAuthorizationHeader( + HttpAuth::Target target, HttpRequestHeaders* authorization_headers) { + DCHECK(HaveAuth(target)); + DCHECK(!auth_token_[target].empty()); + authorization_headers->SetHeader( + HttpAuth::GetAuthorizationHeaderName(target), + auth_token_[target]); + auth_token_[target].clear(); } GURL HttpNetworkTransaction::AuthOrigin(HttpAuth::Target target) const { diff --git a/net/http/http_network_transaction.h b/net/http/http_network_transaction.h index 439da93..147c5e2 100644 --- a/net/http/http_network_transaction.h +++ b/net/http/http_network_transaction.h @@ -83,12 +83,18 @@ class HttpNetworkTransaction : public HttpTransaction { STATE_RESOLVE_PROXY_COMPLETE, STATE_INIT_CONNECTION, STATE_INIT_CONNECTION_COMPLETE, + STATE_TUNNEL_GENERATE_AUTH_TOKEN, + STATE_TUNNEL_GENERATE_AUTH_TOKEN_COMPLETE, STATE_TUNNEL_SEND_REQUEST, STATE_TUNNEL_SEND_REQUEST_COMPLETE, STATE_TUNNEL_READ_HEADERS, STATE_TUNNEL_READ_HEADERS_COMPLETE, STATE_SSL_CONNECT, STATE_SSL_CONNECT_COMPLETE, + STATE_GENERATE_PROXY_AUTH_TOKEN, + STATE_GENERATE_PROXY_AUTH_TOKEN_COMPLETE, + STATE_GENERATE_SERVER_AUTH_TOKEN, + STATE_GENERATE_SERVER_AUTH_TOKEN_COMPLETE, STATE_SEND_REQUEST, STATE_SEND_REQUEST_COMPLETE, STATE_READ_HEADERS, @@ -128,12 +134,18 @@ class HttpNetworkTransaction : public HttpTransaction { int DoResolveProxyComplete(int result); int DoInitConnection(); int DoInitConnectionComplete(int result); + int DoTunnelGenerateAuthToken(); + int DoTunnelGenerateAuthTokenComplete(int result); int DoTunnelSendRequest(); int DoTunnelSendRequestComplete(int result); int DoTunnelReadHeaders(); int DoTunnelReadHeadersComplete(int result); int DoSSLConnect(); int DoSSLConnectComplete(int result); + int DoGenerateProxyAuthToken(); + int DoGenerateProxyAuthTokenComplete(int result); + int DoGenerateServerAuthToken(); + int DoGenerateServerAuthTokenComplete(int result); int DoSendRequest(); int DoSendRequestComplete(int result); int DoReadHeaders(); @@ -231,7 +243,7 @@ class HttpNetworkTransaction : public HttpTransaction { // Adds either the proxy auth header, or the origin server auth header, // as specified by |target|. void AddAuthorizationHeader( - HttpAuth::Target target, HttpRequestHeaders* authorization_headers) const; + HttpAuth::Target target, HttpRequestHeaders* authorization_headers); // Returns a log message for all the response headers related to the auth // challenge. @@ -279,6 +291,12 @@ class HttpNetworkTransaction : public HttpTransaction { // For proxy authentication the path is always empty string. std::string AuthPath(HttpAuth::Target target) const; + // Generate an authentication token for |target| if necessary. The return + // value is a net error code. |OK| will be returned both in the case that + // a token is correctly generated synchronously, as well as when no tokens + // were necessary. + int MaybeGenerateAuthToken(HttpAuth::Target target); + void MarkBrokenAlternateProtocolAndFallback(); // Returns a string representation of a HttpAuth::Target value that can be @@ -287,21 +305,19 @@ class HttpNetworkTransaction : public HttpTransaction { static bool g_ignore_certificate_errors; - // The following three auth members are arrays of size two -- index 0 is - // for the proxy server, and index 1 is for the origin server. - // Use the enum HttpAuth::Target to index into them. - // TODO(cbentzel): Just use explicit proxy_auth_handler_ and - // server_auth_handler_ and move identity into the handler directly. - - // auth_handler encapsulates the logic for the particular auth-scheme. + // |auth_handler_| encapsulates the logic for the particular auth-scheme. // This includes the challenge's parameters. If NULL, then there is no // associated auth handler. - scoped_ptr<HttpAuthHandler> auth_handler_[2]; + scoped_ptr<HttpAuthHandler> auth_handler_[HttpAuth::AUTH_NUM_TARGETS]; - // auth_identity_ holds the (username/password) that should be used by - // the auth_handler_ to generate credentials. This identity can come from + // |auth_identity_| holds the (username/password) that should be used by + // the |auth_handler_| to generate credentials. This identity can come from // a number of places (url, cache, prompt). - HttpAuth::Identity auth_identity_[2]; + HttpAuth::Identity auth_identity_[HttpAuth::AUTH_NUM_TARGETS]; + + // |auth_token_| contains the opaque string to pass to the proxy or + // server to authenticate the client. + std::string auth_token_[HttpAuth::AUTH_NUM_TARGETS]; // Whether this transaction is waiting for proxy auth, server auth, or is // not waiting for any auth at all. |pending_auth_target_| is read and diff --git a/net/http/http_network_transaction_unittest.cc b/net/http/http_network_transaction_unittest.cc index 2f0971c..19eac17 100644 --- a/net/http/http_network_transaction_unittest.cc +++ b/net/http/http_network_transaction_unittest.cc @@ -5388,10 +5388,8 @@ TEST_F(HttpNetworkTransactionTest, HttpNetworkTransaction::SetUseAlternateProtocols(false); } -// 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 { +// MockAuthHandler is used in tests to reliably trigger edge cases. +class MockAuthHandler : public HttpAuthHandler { public: enum Resolve { RESOLVE_INIT, @@ -5401,12 +5399,13 @@ class MockAuthHandlerCanonical : public HttpAuthHandler { RESOLVE_TESTED, }; - MockAuthHandlerCanonical() + MockAuthHandler() : resolve_(RESOLVE_INIT), user_callback_(NULL), - ALLOW_THIS_IN_INITIALIZER_LIST(method_factory_(this)) { + ALLOW_THIS_IN_INITIALIZER_LIST(method_factory_(this)), + generate_async_(false), generate_rv_(OK), auth_token_(NULL) { } - virtual ~MockAuthHandlerCanonical() { + virtual ~MockAuthHandler() { } void SetResolveExpectation(Resolve resolve) { @@ -5414,11 +5413,6 @@ class MockAuthHandlerCanonical : public HttpAuthHandler { resolve_ = resolve; } - void ResetResolveExpectation() { - EXPECT_EQ(RESOLVE_TESTED, resolve_); - resolve_ = RESOLVE_INIT; - } - virtual bool NeedsCanonicalName() { switch (resolve_) { case RESOLVE_SYNC: @@ -5447,7 +5441,7 @@ class MockAuthHandlerCanonical : public HttpAuthHandler { user_callback_ = callback; MessageLoop::current()->PostTask( FROM_HERE, method_factory_.NewRunnableMethod( - &MockAuthHandlerCanonical::OnResolveCanonicalName)); + &MockAuthHandler::OnResolveCanonicalName)); break; default: NOTREACHED(); @@ -5456,6 +5450,11 @@ class MockAuthHandlerCanonical : public HttpAuthHandler { return rv; } + void SetGenerateExpectation(bool async, int rv) { + generate_async_ = async; + generate_rv_ = rv; + } + // The Factory class simply returns the same handler each time // CreateAuthHandler is called. class Factory : public HttpAuthHandlerFactory { @@ -5463,8 +5462,9 @@ class MockAuthHandlerCanonical : public HttpAuthHandler { Factory() {} virtual ~Factory() {} - void set_mock_handler(HttpAuthHandler* handler) { - handler_.reset(handler); + void set_mock_handler(HttpAuthHandler* handler, HttpAuth::Target target) { + EXPECT_TRUE(handlers_[target].get() == NULL); + handlers_[target].reset(handler); } virtual int CreateAuthHandler(HttpAuth::ChallengeTokenizer* challenge, @@ -5474,14 +5474,14 @@ class MockAuthHandlerCanonical : public HttpAuthHandler { int nonce_count, const BoundNetLog& net_log, scoped_ptr<HttpAuthHandler>* handler) { - if (!handler_.get()) + if (!handlers_[target].get()) return ERR_UNEXPECTED; - handler->swap(handler_); + handler->swap(handlers_[target]); return OK; } private: - scoped_ptr<HttpAuthHandler> handler_; + scoped_ptr<HttpAuthHandler> handlers_[HttpAuth::AUTH_NUM_TARGETS]; }; protected: @@ -5497,11 +5497,20 @@ class MockAuthHandlerCanonical : public HttpAuthHandler { const HttpRequestInfo* request, CompletionCallback* callback, std::string* auth_token) { - if (username == NULL) - auth_token->assign("Mock DEFAULT_AUTH myserver.example.com"); - else - auth_token->assign("Mock AUTH myserver.example.com"); - return OK; + if (generate_async_) { + EXPECT_TRUE(user_callback_ == NULL); + EXPECT_TRUE(auth_token_ == NULL); + user_callback_ = callback; + auth_token_ = auth_token; + MessageLoop::current()->PostTask( + FROM_HERE, method_factory_.NewRunnableMethod( + &MockAuthHandler::OnGenerateAuthToken)); + return ERR_IO_PENDING; + } else { + if (generate_rv_ == OK) + *auth_token = "auth_token"; + return generate_rv_; + } } private: @@ -5514,39 +5523,51 @@ class MockAuthHandlerCanonical : public HttpAuthHandler { callback->Run(OK); } + void OnGenerateAuthToken() { + EXPECT_EQ(true, generate_async_); + EXPECT_TRUE(user_callback_ != NULL); + if (generate_rv_ == OK) + *auth_token_ = "auth_token"; + auth_token_ = NULL; + CompletionCallback* callback = user_callback_; + user_callback_ = NULL; + callback->Run(generate_rv_); + } + Resolve resolve_; CompletionCallback* user_callback_; - ScopedRunnableMethodFactory<MockAuthHandlerCanonical> method_factory_; + ScopedRunnableMethodFactory<MockAuthHandler> method_factory_; + bool generate_async_; + int generate_rv_; + std::string* auth_token_; }; // Tests that ResolveCanonicalName is handled correctly by the // HttpNetworkTransaction. TEST_F(HttpNetworkTransactionTest, ResolveCanonicalName) { SessionDependencies session_deps; - MockAuthHandlerCanonical::Factory* auth_factory( - new MockAuthHandlerCanonical::Factory()); + MockAuthHandler::Factory* auth_factory(new MockAuthHandler::Factory()); session_deps.http_auth_handler_factory.reset(auth_factory); for (int i = 0; i < 2; ++i) { - MockAuthHandlerCanonical* auth_handler(new MockAuthHandlerCanonical()); + MockAuthHandler* auth_handler(new MockAuthHandler()); std::string auth_challenge = "Mock"; GURL origin("http://www.example.com"); HttpAuth::ChallengeTokenizer tokenizer(auth_challenge.begin(), auth_challenge.end()); auth_handler->InitFromChallenge(&tokenizer, HttpAuth::AUTH_SERVER, origin, BoundNetLog()); - auth_factory->set_mock_handler(auth_handler); + auth_factory->set_mock_handler(auth_handler, HttpAuth::AUTH_SERVER); 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 + // are contained inside the MockAuthHandler codebase in response to // the expectations. - MockAuthHandlerCanonical::Resolve resolve = - (i == 0) ? - MockAuthHandlerCanonical::RESOLVE_SYNC : - MockAuthHandlerCanonical::RESOLVE_ASYNC; + MockAuthHandler::Resolve resolve = ((i == 0) ? + MockAuthHandler::RESOLVE_SYNC : + MockAuthHandler::RESOLVE_ASYNC); auth_handler->SetResolveExpectation(resolve); HttpRequestInfo request; request.method = "GET"; @@ -5590,7 +5611,418 @@ TEST_F(HttpNetworkTransactionTest, ResolveCanonicalName) { 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(); + } +} + +// GenerateAuthToken is a mighty big test. +// It tests all permutation of GenerateAuthToken behavior: +// - Synchronous and Asynchronous completion. +// - OK or error on completion. +// - Direct connection, non-authenticating proxy, and authenticating proxy. +// - HTTP or HTTPS backend (to include proxy tunneling). +// - Non-authenticating and authenticating backend. +// +// In all, there are 44 reasonable permuations (for example, if there are +// problems generating an auth token for an authenticating proxy, we don't +// need to test all permutations of the backend server). +// +// The test proceeds by going over each of the configuration cases, and +// potentially running up to three rounds in each of the tests. The TestConfig +// specifies both the configuration for the test as well as the expectations +// for the results. +TEST_F(HttpNetworkTransactionTest, GenerateAuthToken) { + const char* kServer = "http://www.example.com"; + const char* kSecureServer = "https://www.example.com"; + const char* kProxy = "myproxy:70"; + const int kAuthErr = ERR_INVALID_AUTH_CREDENTIALS; + + enum AuthTiming { + AUTH_NONE, + AUTH_SYNC, + AUTH_ASYNC, + }; + + const MockWrite kGet( + "GET / HTTP/1.1\r\n" + "Host: www.example.com\r\n" + "Connection: keep-alive\r\n\r\n"); + const MockWrite kGetProxy( + "GET http://www.example.com/ HTTP/1.1\r\n" + "Host: www.example.com\r\n" + "Proxy-Connection: keep-alive\r\n\r\n"); + const MockWrite kGetAuth( + "GET / HTTP/1.1\r\n" + "Host: www.example.com\r\n" + "Connection: keep-alive\r\n" + "Authorization: auth_token\r\n\r\n"); + const MockWrite kGetProxyAuth( + "GET http://www.example.com/ HTTP/1.1\r\n" + "Host: www.example.com\r\n" + "Proxy-Connection: keep-alive\r\n" + "Proxy-Authorization: auth_token\r\n\r\n"); + const MockWrite kGetAuthThroughProxy( + "GET http://www.example.com/ HTTP/1.1\r\n" + "Host: www.example.com\r\n" + "Proxy-Connection: keep-alive\r\n" + "Authorization: auth_token\r\n\r\n"); + const MockWrite kGetAuthWithProxyAuth( + "GET http://www.example.com/ HTTP/1.1\r\n" + "Host: www.example.com\r\n" + "Proxy-Connection: keep-alive\r\n" + "Proxy-Authorization: auth_token\r\n" + "Authorization: auth_token\r\n\r\n"); + const MockWrite kConnect( + "CONNECT www.example.com:443 HTTP/1.1\r\n" + "Host: www.example.com\r\n" + "Proxy-Connection: keep-alive\r\n\r\n"); + const MockWrite kConnectProxyAuth( + "CONNECT www.example.com:443 HTTP/1.1\r\n" + "Host: www.example.com\r\n" + "Proxy-Connection: keep-alive\r\n" + "Proxy-Authorization: auth_token\r\n\r\n"); + + const MockRead kSuccess( + "HTTP/1.1 200 OK\r\n" + "Content-Type: text/html; charset=iso-8859-1\r\n" + "Content-Length: 3\r\n\r\n" + "Yes"); + const MockRead kFailure( + "Should not be called."); + const MockRead kServerChallenge( + "HTTP/1.1 401 Unauthorized\r\n" + "WWW-Authenticate: Mock realm=server\r\n" + "Content-Type: text/html; charset=iso-8859-1\r\n" + "Content-Length: 14\r\n\r\n" + "Unauthorized\r\n"); + const MockRead kProxyChallenge( + "HTTP/1.1 407 Unauthorized\r\n" + "Proxy-Authenticate: Mock realm=proxy\r\n" + "Proxy-Connection: close\r\n" + "Content-Type: text/html; charset=iso-8859-1\r\n" + "Content-Length: 14\r\n\r\n" + "Unauthorized\r\n"); + const MockRead kProxyConnected( + "HTTP/1.1 200 Connection Established\r\n\r\n"); + + // NOTE(cbentzel): I wanted TestReadWriteRound to be a simple struct with + // no constructors, but the C++ compiler on Windows warns about + // unspecified data in compound literals. So, moved to using constructors, + // and TestRound's created with the default constructor should not be used. + struct TestRound { + TestRound() + : expected_rv(ERR_UNEXPECTED), + extra_write(NULL), + extra_read(NULL) { + } + TestRound(const MockWrite& write_arg, const MockRead& read_arg, + int expected_rv_arg) + : write(write_arg), + read(read_arg), + expected_rv(expected_rv_arg), + extra_write(NULL), + extra_read(NULL) { + } + TestRound(const MockWrite& write_arg, const MockRead& read_arg, + int expected_rv_arg, const MockWrite* extra_write_arg, + const MockWrite* extra_read_arg) + : write(write_arg), + read(read_arg), + expected_rv(expected_rv_arg), + extra_write(extra_write_arg), + extra_read(extra_read_arg) { + } + MockWrite write; + MockRead read; + int expected_rv; + const MockWrite* extra_write; + const MockRead* extra_read; + }; + + static const int kNoSSL = 500; + + struct TestConfig { + const char* proxy_url; + AuthTiming proxy_auth_timing; + int proxy_auth_rv; + const char* server_url; + AuthTiming server_auth_timing; + int server_auth_rv; + int num_auth_rounds; + int first_ssl_round; + TestRound rounds[3]; + } test_configs[] = { + // Non-authenticating HTTP server with a direct connection. + { NULL, AUTH_NONE, OK, kServer, AUTH_NONE, OK, 1, kNoSSL, + { TestRound(kGet, kSuccess, OK)}}, + // Authenticating HTTP server with a direct connection. + { NULL, AUTH_NONE, OK, kServer, AUTH_SYNC, OK, 2, kNoSSL, + { TestRound(kGet, kServerChallenge, OK), + TestRound(kGetAuth, kSuccess, OK)}}, + { NULL, AUTH_NONE, OK, kServer, AUTH_SYNC, kAuthErr, 2, kNoSSL, + { TestRound(kGet, kServerChallenge, OK), + TestRound(kGetAuth, kFailure, kAuthErr)}}, + { NULL, AUTH_NONE, OK, kServer, AUTH_ASYNC, OK, 2, kNoSSL, + { TestRound(kGet, kServerChallenge, OK), + TestRound(kGetAuth, kSuccess, OK)}}, + { NULL, AUTH_NONE, OK, kServer, AUTH_ASYNC, kAuthErr, 2, kNoSSL, + { TestRound(kGet, kServerChallenge, OK), + TestRound(kGetAuth, kFailure, kAuthErr)}}, + // Non-authenticating HTTP server through a non-authenticating proxy. + { kProxy, AUTH_NONE, OK, kServer, AUTH_NONE, OK, 1, kNoSSL, + { TestRound(kGetProxy, kSuccess, OK)}}, + // Authenticating HTTP server through a non-authenticating proxy. + { kProxy, AUTH_NONE, OK, kServer, AUTH_SYNC, OK, 2, kNoSSL, + { TestRound(kGetProxy, kServerChallenge, OK), + TestRound(kGetAuthThroughProxy, kSuccess, OK)}}, + { kProxy, AUTH_NONE, OK, kServer, AUTH_SYNC, kAuthErr, 2, kNoSSL, + { TestRound(kGetProxy, kServerChallenge, OK), + TestRound(kGetAuthThroughProxy, kFailure, kAuthErr)}}, + { kProxy, AUTH_NONE, OK, kServer, AUTH_ASYNC, OK, 2, kNoSSL, + { TestRound(kGetProxy, kServerChallenge, OK), + TestRound(kGetAuthThroughProxy, kSuccess, OK)}}, + { kProxy, AUTH_NONE, OK, kServer, AUTH_ASYNC, kAuthErr, 2, kNoSSL, + { TestRound(kGetProxy, kServerChallenge, OK), + TestRound(kGetAuthThroughProxy, kFailure, kAuthErr)}}, + // Non-authenticating HTTP server through an authenticating proxy. + { kProxy, AUTH_SYNC, OK, kServer, AUTH_NONE, OK, 2, kNoSSL, + { TestRound(kGetProxy, kProxyChallenge, OK), + TestRound(kGetProxyAuth, kSuccess, OK)}}, + { kProxy, AUTH_SYNC, kAuthErr, kServer, AUTH_NONE, OK, 2, kNoSSL, + { TestRound(kGetProxy, kProxyChallenge, OK), + TestRound(kGetProxyAuth, kFailure, kAuthErr)}}, + { kProxy, AUTH_ASYNC, OK, kServer, AUTH_NONE, OK, 2, kNoSSL, + { TestRound(kGetProxy, kProxyChallenge, OK), + TestRound(kGetProxyAuth, kSuccess, OK)}}, + { kProxy, AUTH_ASYNC, kAuthErr, kServer, AUTH_NONE, OK, 2, kNoSSL, + { TestRound(kGetProxy, kProxyChallenge, OK), + TestRound(kGetProxyAuth, kFailure, kAuthErr)}}, + // Authenticating HTTP server through an authenticating proxy. + { kProxy, AUTH_SYNC, OK, kServer, AUTH_SYNC, OK, 3, kNoSSL, + { TestRound(kGetProxy, kProxyChallenge, OK), + TestRound(kGetProxyAuth, kServerChallenge, OK), + TestRound(kGetAuthWithProxyAuth, kSuccess, OK)}}, + { kProxy, AUTH_SYNC, OK, kServer, AUTH_SYNC, kAuthErr, 3, kNoSSL, + { TestRound(kGetProxy, kProxyChallenge, OK), + TestRound(kGetProxyAuth, kServerChallenge, OK), + TestRound(kGetAuthWithProxyAuth, kFailure, kAuthErr)}}, + { kProxy, AUTH_ASYNC, OK, kServer, AUTH_SYNC, OK, 3, kNoSSL, + { TestRound(kGetProxy, kProxyChallenge, OK), + TestRound(kGetProxyAuth, kServerChallenge, OK), + TestRound(kGetAuthWithProxyAuth, kSuccess, OK)}}, + { kProxy, AUTH_ASYNC, OK, kServer, AUTH_SYNC, kAuthErr, 3, kNoSSL, + { TestRound(kGetProxy, kProxyChallenge, OK), + TestRound(kGetProxyAuth, kServerChallenge, OK), + TestRound(kGetAuthWithProxyAuth, kFailure, kAuthErr)}}, + { kProxy, AUTH_SYNC, OK, kServer, AUTH_ASYNC, OK, 3, kNoSSL, + { TestRound(kGetProxy, kProxyChallenge, OK), + TestRound(kGetProxyAuth, kServerChallenge, OK), + TestRound(kGetAuthWithProxyAuth, kSuccess, OK)}}, + { kProxy, AUTH_SYNC, OK, kServer, AUTH_ASYNC, kAuthErr, 3, kNoSSL, + { TestRound(kGetProxy, kProxyChallenge, OK), + TestRound(kGetProxyAuth, kServerChallenge, OK), + TestRound(kGetAuthWithProxyAuth, kFailure, kAuthErr)}}, + { kProxy, AUTH_ASYNC, OK, kServer, AUTH_ASYNC, OK, 3, kNoSSL, + { TestRound(kGetProxy, kProxyChallenge, OK), + TestRound(kGetProxyAuth, kServerChallenge, OK), + TestRound(kGetAuthWithProxyAuth, kSuccess, OK)}}, + { kProxy, AUTH_ASYNC, OK, kServer, AUTH_ASYNC, kAuthErr, 3, kNoSSL, + { TestRound(kGetProxy, kProxyChallenge, OK), + TestRound(kGetProxyAuth, kServerChallenge, OK), + TestRound(kGetAuthWithProxyAuth, kFailure, kAuthErr)}}, + // Non-authenticating HTTPS server with a direct connection. + { NULL, AUTH_NONE, OK, kSecureServer, AUTH_NONE, OK, 1, 0, + { TestRound(kGet, kSuccess, OK)}}, + // Authenticating HTTPS server with a direct connection. + { NULL, AUTH_NONE, OK, kSecureServer, AUTH_SYNC, OK, 2, 0, + { TestRound(kGet, kServerChallenge, OK), + TestRound(kGetAuth, kSuccess, OK)}}, + { NULL, AUTH_NONE, OK, kSecureServer, AUTH_SYNC, kAuthErr, 2, 0, + { TestRound(kGet, kServerChallenge, OK), + TestRound(kGetAuth, kFailure, kAuthErr)}}, + { NULL, AUTH_NONE, OK, kSecureServer, AUTH_ASYNC, OK, 2, 0, + { TestRound(kGet, kServerChallenge, OK), + TestRound(kGetAuth, kSuccess, OK)}}, + { NULL, AUTH_NONE, OK, kSecureServer, AUTH_ASYNC, kAuthErr, 2, 0, + { TestRound(kGet, kServerChallenge, OK), + TestRound(kGetAuth, kFailure, kAuthErr)}}, + // Non-authenticating HTTPS server with a non-authenticating proxy. + { kProxy, AUTH_NONE, OK, kSecureServer, AUTH_NONE, OK, 1, 0, + { TestRound(kConnect, kProxyConnected, OK, &kGet, &kSuccess)}}, + // Authenticating HTTPS server through a non-authenticating proxy. + { kProxy, AUTH_NONE, OK, kSecureServer, AUTH_SYNC, OK, 2, 0, + { TestRound(kConnect, kProxyConnected, OK, &kGet, &kServerChallenge), + TestRound(kGetAuth, kSuccess, OK)}}, + { kProxy, AUTH_NONE, OK, kSecureServer, AUTH_SYNC, kAuthErr, 2, 0, + { TestRound(kConnect, kProxyConnected, OK, &kGet, &kServerChallenge), + TestRound(kGetAuth, kFailure, kAuthErr)}}, + { kProxy, AUTH_NONE, OK, kSecureServer, AUTH_ASYNC, OK, 2, 0, + { TestRound(kConnect, kProxyConnected, OK, &kGet, &kServerChallenge), + TestRound(kGetAuth, kSuccess, OK)}}, + { kProxy, AUTH_NONE, OK, kSecureServer, AUTH_ASYNC, kAuthErr, 2, 0, + { TestRound(kConnect, kProxyConnected, OK, &kGet, &kServerChallenge), + TestRound(kGetAuth, kFailure, kAuthErr)}}, + // Non-Authenticating HTTPS server through an authenticating proxy. + { kProxy, AUTH_SYNC, OK, kSecureServer, AUTH_NONE, OK, 2, 1, + { TestRound(kConnect, kProxyChallenge, OK), + TestRound(kConnectProxyAuth, kProxyConnected, OK, &kGet, &kSuccess)}}, + { kProxy, AUTH_SYNC, kAuthErr, kSecureServer, AUTH_NONE, OK, 2, kNoSSL, + { TestRound(kConnect, kProxyChallenge, OK), + TestRound(kConnectProxyAuth, kFailure, kAuthErr)}}, + { kProxy, AUTH_ASYNC, OK, kSecureServer, AUTH_NONE, OK, 2, 1, + { TestRound(kConnect, kProxyChallenge, OK), + TestRound(kConnectProxyAuth, kProxyConnected, OK, &kGet, &kSuccess)}}, + { kProxy, AUTH_ASYNC, kAuthErr, kSecureServer, AUTH_NONE, OK, 2, kNoSSL, + { TestRound(kConnect, kProxyChallenge, OK), + TestRound(kConnectProxyAuth, kFailure, kAuthErr)}}, + // Authenticating HTTPS server through an authenticating proxy. + { kProxy, AUTH_SYNC, OK, kSecureServer, AUTH_SYNC, OK, 3, 1, + { TestRound(kConnect, kProxyChallenge, OK), + TestRound(kConnectProxyAuth, kProxyConnected, OK, + &kGet, &kServerChallenge), + TestRound(kGetAuth, kSuccess, OK)}}, + { kProxy, AUTH_SYNC, OK, kSecureServer, AUTH_SYNC, kAuthErr, 3, 1, + { TestRound(kConnect, kProxyChallenge, OK), + TestRound(kConnectProxyAuth, kProxyConnected, OK, + &kGet, &kServerChallenge), + TestRound(kGetAuth, kFailure, kAuthErr)}}, + { kProxy, AUTH_ASYNC, OK, kSecureServer, AUTH_SYNC, OK, 3, 1, + { TestRound(kConnect, kProxyChallenge, OK), + TestRound(kConnectProxyAuth, kProxyConnected, OK, + &kGet, &kServerChallenge), + TestRound(kGetAuth, kSuccess, OK)}}, + { kProxy, AUTH_ASYNC, OK, kSecureServer, AUTH_SYNC, kAuthErr, 3, 1, + { TestRound(kConnect, kProxyChallenge, OK), + TestRound(kConnectProxyAuth, kProxyConnected, OK, + &kGet, &kServerChallenge), + TestRound(kGetAuth, kFailure, kAuthErr)}}, + { kProxy, AUTH_SYNC, OK, kSecureServer, AUTH_ASYNC, OK, 3, 1, + { TestRound(kConnect, kProxyChallenge, OK), + TestRound(kConnectProxyAuth, kProxyConnected, OK, + &kGet, &kServerChallenge), + TestRound(kGetAuth, kSuccess, OK)}}, + { kProxy, AUTH_SYNC, OK, kSecureServer, AUTH_ASYNC, kAuthErr, 3, 1, + { TestRound(kConnect, kProxyChallenge, OK), + TestRound(kConnectProxyAuth, kProxyConnected, OK, + &kGet, &kServerChallenge), + TestRound(kGetAuth, kFailure, kAuthErr)}}, + { kProxy, AUTH_ASYNC, OK, kSecureServer, AUTH_ASYNC, OK, 3, 1, + { TestRound(kConnect, kProxyChallenge, OK), + TestRound(kConnectProxyAuth, kProxyConnected, OK, + &kGet, &kServerChallenge), + TestRound(kGetAuth, kSuccess, OK)}}, + { kProxy, AUTH_ASYNC, OK, kSecureServer, AUTH_ASYNC, kAuthErr, 3, 1, + { TestRound(kConnect, kProxyChallenge, OK), + TestRound(kConnectProxyAuth, kProxyConnected, OK, + &kGet, &kServerChallenge), + TestRound(kGetAuth, kFailure, kAuthErr)}}, + }; + + SessionDependencies session_deps; + scoped_refptr<HttpNetworkSession> session = CreateSession(&session_deps); + MockAuthHandler::Factory* auth_factory(new MockAuthHandler::Factory()); + session_deps.http_auth_handler_factory.reset(auth_factory); + + for (size_t i = 0; i < ARRAYSIZE_UNSAFE(test_configs); ++i) { + const TestConfig& test_config = test_configs[i]; + if (test_config.proxy_auth_timing != AUTH_NONE) { + MockAuthHandler* auth_handler(new MockAuthHandler()); + std::string auth_challenge = "Mock realm=proxy"; + GURL origin(test_config.proxy_url); + HttpAuth::ChallengeTokenizer tokenizer(auth_challenge.begin(), + auth_challenge.end()); + auth_handler->InitFromChallenge(&tokenizer, HttpAuth::AUTH_PROXY, + origin, BoundNetLog()); + auth_handler->SetGenerateExpectation( + test_config.proxy_auth_timing == AUTH_ASYNC, + test_config.proxy_auth_rv); + auth_handler->SetResolveExpectation(MockAuthHandler::RESOLVE_SKIP); + auth_factory->set_mock_handler(auth_handler, HttpAuth::AUTH_PROXY); + } + if (test_config.server_auth_timing != AUTH_NONE) { + MockAuthHandler* auth_handler(new MockAuthHandler()); + std::string auth_challenge = "Mock realm=server"; + GURL origin(test_config.server_url); + HttpAuth::ChallengeTokenizer tokenizer(auth_challenge.begin(), + auth_challenge.end()); + auth_handler->InitFromChallenge(&tokenizer, HttpAuth::AUTH_SERVER, + origin, BoundNetLog()); + auth_handler->SetGenerateExpectation( + test_config.server_auth_timing == AUTH_ASYNC, + test_config.server_auth_rv); + auth_handler->SetResolveExpectation(MockAuthHandler::RESOLVE_SKIP); + auth_factory->set_mock_handler(auth_handler, HttpAuth::AUTH_SERVER); + } + if (test_config.proxy_url) { + session_deps.proxy_service = + CreateFixedProxyService(test_config.proxy_url); + } else { + session_deps.proxy_service = ProxyService::CreateNull(); + } + + HttpRequestInfo request; + request.method = "GET"; + request.url = GURL(test_config.server_url); + request.load_flags = 0; + + scoped_ptr<HttpTransaction> trans( + new HttpNetworkTransaction(CreateSession(&session_deps))); + + for (int round = 0; round < test_config.num_auth_rounds; ++round) { + const TestRound& read_write_round = test_config.rounds[round]; + + // Set up expected reads and writes. + MockRead reads[2]; + reads[0] = read_write_round.read; + size_t length_reads = 1; + if (read_write_round.extra_read) { + reads[1] = *read_write_round.extra_read; + length_reads = 2; + } + + MockWrite writes[2]; + writes[0] = read_write_round.write; + size_t length_writes = 1; + if (read_write_round.extra_write) { + writes[1] = *read_write_round.extra_write; + length_writes = 2; + } + StaticSocketDataProvider data_provider( + reads, length_reads, writes, length_writes); + session_deps.socket_factory.AddSocketDataProvider(&data_provider); + + // Add an SSL sequence if necessary. + SSLSocketDataProvider ssl_socket_data_provider(false, OK); + if (round >= test_config.first_ssl_round) + session_deps.socket_factory.AddSSLSocketDataProvider( + &ssl_socket_data_provider); + + // Start or restart the transaction. + TestCompletionCallback callback; + int rv; + if (round == 0) { + rv = trans->Start(&request, &callback, BoundNetLog()); + } else { + rv = trans->RestartWithAuth(L"foo", L"bar", &callback); + } + if (rv == ERR_IO_PENDING) + rv = callback.WaitForResult(); + + // Compare results with expected data. + EXPECT_EQ(read_write_round.expected_rv, rv); + const HttpResponseInfo* response = trans->GetResponseInfo(); + if (read_write_round.expected_rv == OK) { + EXPECT_FALSE(response == NULL); + } else { + EXPECT_TRUE(response == NULL); + EXPECT_EQ(round + 1, test_config.num_auth_rounds); + continue; + } + if (round + 1 < test_config.num_auth_rounds) { + EXPECT_FALSE(response->auth_challenge.get() == NULL); + } else { + EXPECT_TRUE(response->auth_challenge.get() == NULL); + } + } } } |