summaryrefslogtreecommitdiffstats
path: root/net/http
diff options
context:
space:
mode:
authorcbentzel@chromium.org <cbentzel@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-06-17 10:42:15 +0000
committercbentzel@chromium.org <cbentzel@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-06-17 10:42:15 +0000
commit044de06466c30b563c2e999af6d2f782d5fa73a5 (patch)
treea386f98f6ba96cb9d43676accdf9cb80a68b6a0a /net/http
parent38712b1abf0cd6b2a60387e402f2b05d937a8ba5 (diff)
downloadchromium_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.h1
-rw-r--r--net/http/http_auth_handler.cc3
-rw-r--r--net/http/http_auth_handler_basic_unittest.cc8
-rw-r--r--net/http/http_network_transaction.cc153
-rw-r--r--net/http/http_network_transaction.h40
-rw-r--r--net/http/http_network_transaction_unittest.cc500
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);
+ }
+ }
}
}