summaryrefslogtreecommitdiffstats
path: root/net/http
diff options
context:
space:
mode:
authorvandebo@chromium.org <vandebo@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-07-12 18:11:13 +0000
committervandebo@chromium.org <vandebo@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-07-12 18:11:13 +0000
commite772db3f04f1079a07d702c6aa4e0394f2147af9 (patch)
treeedfe7bec726506679a8163fb4fd207287c8df77d /net/http
parent1e89e05c0bae9404547e95b86cce02d233706f28 (diff)
downloadchromium_src-e772db3f04f1079a07d702c6aa4e0394f2147af9.zip
chromium_src-e772db3f04f1079a07d702c6aa4e0394f2147af9.tar.gz
chromium_src-e772db3f04f1079a07d702c6aa4e0394f2147af9.tar.bz2
Put HttpProxyClientSocket into a pool.
This CL requires http://codereview.chromium.org/2799036 - Cleanup the HttpProxyClientSocket interface a touch. - Make HttpAuthController reference counted. - Enable ClientSocketPool to return recoverable connections. BUG=42795 TEST=existing unit tests Review URL: http://codereview.chromium.org/2817033 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@52104 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'net/http')
-rw-r--r--net/http/http_auth_controller.cc2
-rw-r--r--net/http/http_auth_controller.h28
-rw-r--r--net/http/http_network_session.cc19
-rw-r--r--net/http/http_network_session.h6
-rw-r--r--net/http/http_network_transaction.cc142
-rw-r--r--net/http/http_network_transaction.h13
-rw-r--r--net/http/http_network_transaction_unittest.cc15
-rw-r--r--net/http/http_proxy_client_socket.cc17
-rw-r--r--net/http/http_proxy_client_socket.h22
-rw-r--r--net/http/http_proxy_client_socket_pool.cc213
-rw-r--r--net/http/http_proxy_client_socket_pool.h201
-rw-r--r--net/http/http_proxy_client_socket_pool_unittest.cc318
12 files changed, 859 insertions, 137 deletions
diff --git a/net/http/http_auth_controller.cc b/net/http/http_auth_controller.cc
index cb899cb..22b4f20 100644
--- a/net/http/http_auth_controller.cc
+++ b/net/http/http_auth_controller.cc
@@ -62,6 +62,8 @@ HttpAuthController::HttpAuthController(
net_log_(net_log) {
}
+HttpAuthController::~HttpAuthController() {}
+
int HttpAuthController::MaybeGenerateAuthToken(const HttpRequestInfo* request,
CompletionCallback* callback) {
bool needs_auth = HaveAuth() || SelectPreemptiveAuth();
diff --git a/net/http/http_auth_controller.h b/net/http/http_auth_controller.h
index 016f88e..655e46d 100644
--- a/net/http/http_auth_controller.h
+++ b/net/http/http_auth_controller.h
@@ -23,7 +23,7 @@ class HttpNetworkSession;
class HttpRequestHeaders;
struct HttpRequestInfo;
-class HttpAuthController {
+class HttpAuthController : public base::RefCounted<HttpAuthController> {
public:
// The arguments are self explanatory except possibly for |auth_url|, which
// should be both the auth target and auth path in a single url argument.
@@ -35,32 +35,34 @@ class HttpAuthController {
// 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(const HttpRequestInfo* request,
- CompletionCallback* callback);
+ virtual int MaybeGenerateAuthToken(const HttpRequestInfo* request,
+ CompletionCallback* callback);
// Adds either the proxy auth header, or the origin server auth header,
// as specified by |target_|.
- void AddAuthorizationHeader(HttpRequestHeaders* authorization_headers);
+ virtual void AddAuthorizationHeader(
+ HttpRequestHeaders* authorization_headers);
// Checks for and handles HTTP status code 401 or 407.
// |HandleAuthChallenge()| returns OK on success, or a network error code
// otherwise. It may also populate |auth_info_|.
- int HandleAuthChallenge(scoped_refptr<HttpResponseHeaders> headers,
- bool do_not_send_server_auth,
- bool establishing_tunnel);
+ virtual int HandleAuthChallenge(scoped_refptr<HttpResponseHeaders> headers,
+ bool do_not_send_server_auth,
+ bool establishing_tunnel);
// Store the supplied credentials and prepare to restart the auth.
- void ResetAuth(const std::wstring& username, const std::wstring& password);
+ virtual void ResetAuth(const std::wstring& username,
+ const std::wstring& password);
- bool HaveAuthHandler() const {
+ virtual bool HaveAuthHandler() const {
return handler_.get() != NULL;
}
- bool HaveAuth() const {
+ virtual bool HaveAuth() const {
return handler_.get() && !identity_.invalid;
}
- scoped_refptr<AuthChallengeInfo> auth_info() {
+ virtual scoped_refptr<AuthChallengeInfo> auth_info() {
return auth_info_;
}
@@ -68,6 +70,10 @@ class HttpAuthController {
net_log_ = net_log;
}
+ protected: // So that we can mock this object.
+ friend class base::RefCounted<HttpAuthController>;
+ virtual ~HttpAuthController();
+
private:
// Searches the auth cache for an entry that encompasses the request's path.
// If such an entry is found, updates |identity_| and |handler_| with the
diff --git a/net/http/http_network_session.cc b/net/http/http_network_session.cc
index ea5a5cf..e49674b 100644
--- a/net/http/http_network_session.cc
+++ b/net/http/http_network_session.cc
@@ -47,6 +47,8 @@ HttpNetworkSession::HttpNetworkSession(
// TODO(vandebo) when we've completely converted to pools, the base TCP
// pool name should get changed to TCP instead of Transport.
: tcp_pool_histograms_(new ClientSocketPoolHistograms("Transport")),
+ tcp_for_http_proxy_pool_histograms_(
+ new ClientSocketPoolHistograms("TCPforHTTPProxy")),
http_proxy_pool_histograms_(new ClientSocketPoolHistograms("HTTPProxy")),
tcp_for_socks_pool_histograms_(
new ClientSocketPoolHistograms("TCPforSOCKS")),
@@ -69,7 +71,7 @@ HttpNetworkSession::HttpNetworkSession(
HttpNetworkSession::~HttpNetworkSession() {
}
-const scoped_refptr<TCPClientSocketPool>&
+const scoped_refptr<HttpProxyClientSocketPool>&
HttpNetworkSession::GetSocketPoolForHTTPProxy(const HostPortPair& http_proxy) {
HTTPProxySocketPoolMap::const_iterator it =
http_proxy_socket_pool_.find(http_proxy);
@@ -77,10 +79,17 @@ HttpNetworkSession::GetSocketPoolForHTTPProxy(const HostPortPair& http_proxy) {
return it->second;
std::pair<HTTPProxySocketPoolMap::iterator, bool> ret =
- http_proxy_socket_pool_.insert(std::make_pair(http_proxy,
- new TCPClientSocketPool(g_max_sockets_per_proxy_server,
- g_max_sockets_per_group, http_proxy_pool_histograms_,
- host_resolver_, socket_factory_, net_log_)));
+ http_proxy_socket_pool_.insert(
+ std::make_pair(
+ http_proxy,
+ new HttpProxyClientSocketPool(
+ g_max_sockets_per_proxy_server, g_max_sockets_per_group,
+ http_proxy_pool_histograms_, host_resolver_,
+ new TCPClientSocketPool(
+ g_max_sockets_per_proxy_server, g_max_sockets_per_group,
+ tcp_for_http_proxy_pool_histograms_, host_resolver_,
+ socket_factory_, net_log_),
+ net_log_)));
return ret.first->second;
}
diff --git a/net/http/http_network_session.h b/net/http/http_network_session.h
index 319753c..96cc7ba 100644
--- a/net/http/http_network_session.h
+++ b/net/http/http_network_session.h
@@ -16,6 +16,7 @@
#include "net/http/http_auth_cache.h"
#include "net/http/http_network_delegate.h"
#include "net/http/http_network_transaction.h"
+#include "net/http/http_proxy_client_socket_pool.h"
#include "net/proxy/proxy_service.h"
#include "net/socket/client_socket_pool_histograms.h"
#include "net/socket/socks_client_socket_pool.h"
@@ -71,7 +72,7 @@ class HttpNetworkSession : public base::RefCounted<HttpNetworkSession> {
const scoped_refptr<SOCKSClientSocketPool>& GetSocketPoolForSOCKSProxy(
const HostPortPair& socks_proxy);
- const scoped_refptr<TCPClientSocketPool>& GetSocketPoolForHTTPProxy(
+ const scoped_refptr<HttpProxyClientSocketPool>& GetSocketPoolForHTTPProxy(
const HostPortPair& http_proxy);
// SSL sockets come from the socket_factory().
@@ -98,7 +99,7 @@ class HttpNetworkSession : public base::RefCounted<HttpNetworkSession> {
static void set_fixed_https_port(uint16 port);
private:
- typedef std::map<HostPortPair, scoped_refptr<TCPClientSocketPool> >
+ typedef std::map<HostPortPair, scoped_refptr<HttpProxyClientSocketPool> >
HTTPProxySocketPoolMap;
typedef std::map<HostPortPair, scoped_refptr<SOCKSClientSocketPool> >
SOCKSSocketPoolMap;
@@ -112,6 +113,7 @@ class HttpNetworkSession : public base::RefCounted<HttpNetworkSession> {
SSLClientAuthCache ssl_client_auth_cache_;
HttpAlternateProtocols alternate_protocols_;
scoped_refptr<ClientSocketPoolHistograms> tcp_pool_histograms_;
+ scoped_refptr<ClientSocketPoolHistograms> tcp_for_http_proxy_pool_histograms_;
scoped_refptr<ClientSocketPoolHistograms> http_proxy_pool_histograms_;
scoped_refptr<ClientSocketPoolHistograms> tcp_for_socks_pool_histograms_;
scoped_refptr<ClientSocketPoolHistograms> socks_pool_histograms_;
diff --git a/net/http/http_network_transaction.cc b/net/http/http_network_transaction.cc
index c0fe07b..38e85f6 100644
--- a/net/http/http_network_transaction.cc
+++ b/net/http/http_network_transaction.cc
@@ -31,6 +31,7 @@
#include "net/http/http_net_log_params.h"
#include "net/http/http_network_session.h"
#include "net/http/http_proxy_client_socket.h"
+#include "net/http/http_proxy_client_socket_pool.h"
#include "net/http/http_request_headers.h"
#include "net/http/http_request_info.h"
#include "net/http/http_response_headers.h"
@@ -309,15 +310,13 @@ int HttpNetworkTransaction::RestartWithAuth(
}
pending_auth_target_ = HttpAuth::AUTH_NONE;
+ auth_controllers_[target]->ResetAuth(username, password);
+
if (target == HttpAuth::AUTH_PROXY && using_ssl_ && proxy_info_.is_http()) {
DCHECK(establishing_tunnel_);
ResetStateForRestart();
- tunnel_credentials_.username = username;
- tunnel_credentials_.password = password;
- tunnel_credentials_.invalid = false;
next_state_ = STATE_TUNNEL_RESTART_WITH_AUTH;
} else {
- auth_controllers_[target]->ResetAuth(username, password);
PrepareForAuthRestart(target);
}
@@ -432,8 +431,6 @@ LoadState HttpNetworkTransaction::GetLoadState() const {
return LOAD_STATE_RESOLVING_PROXY_FOR_URL;
case STATE_INIT_CONNECTION_COMPLETE:
return connection_->GetLoadState();
- case STATE_TUNNEL_CONNECT_COMPLETE:
- return LOAD_STATE_ESTABLISHING_PROXY_TUNNEL;
case STATE_SSL_CONNECT_COMPLETE:
return LOAD_STATE_SSL_HANDSHAKE;
case STATE_GENERATE_PROXY_AUTH_TOKEN_COMPLETE:
@@ -520,13 +517,6 @@ int HttpNetworkTransaction::DoLoop(int result) {
case STATE_INIT_CONNECTION_COMPLETE:
rv = DoInitConnectionComplete(rv);
break;
- case STATE_TUNNEL_CONNECT:
- DCHECK_EQ(OK, rv);
- rv = DoTunnelConnect();
- break;
- case STATE_TUNNEL_CONNECT_COMPLETE:
- rv = DoTunnelConnectComplete(rv);
- break;
case STATE_TUNNEL_RESTART_WITH_AUTH:
DCHECK_EQ(OK, rv);
rv = DoTunnelRestartWithAuth();
@@ -717,10 +707,9 @@ int HttpNetworkTransaction::DoInitConnection() {
for (int i = 0; i < HttpAuth::AUTH_NUM_TARGETS; i++) {
HttpAuth::Target target = static_cast<HttpAuth::Target>(i);
if (!auth_controllers_[target].get())
- auth_controllers_[target].reset(new HttpAuthController(target,
- AuthURL(target),
- session_,
- net_log_));
+ auth_controllers_[target] = new HttpAuthController(target,
+ AuthURL(target),
+ session_, net_log_);
}
next_state_ = STATE_INIT_CONNECTION_COMPLETE;
@@ -795,10 +784,22 @@ int HttpNetworkTransaction::DoInitConnection() {
&io_callback_,
session_->GetSocketPoolForSOCKSProxy(proxy_host_port_pair), net_log_);
} else {
- rv = connection_->Init(
- connection_group, tcp_params, request_->priority,
- &io_callback_,
- session_->GetSocketPoolForHTTPProxy(proxy_host_port_pair), net_log_);
+ DCHECK(proxy_info_.is_http());
+ scoped_refptr<HttpAuthController> http_proxy_auth;
+ if (using_ssl_) {
+ http_proxy_auth = auth_controllers_[HttpAuth::AUTH_PROXY];
+ establishing_tunnel_ = true;
+ }
+
+ HttpProxySocketParams http_proxy_params(tcp_params, request_->url,
+ endpoint_, http_proxy_auth,
+ using_ssl_);
+
+ rv = connection_->Init(connection_group, http_proxy_params,
+ request_->priority, &io_callback_,
+ session_->GetSocketPoolForHTTPProxy(
+ proxy_host_port_pair),
+ net_log_);
}
} else {
TCPSocketParams tcp_params(endpoint_, request_->priority,
@@ -813,6 +814,29 @@ int HttpNetworkTransaction::DoInitConnection() {
int HttpNetworkTransaction::DoInitConnectionComplete(int result) {
if (result < 0) {
+ if (result == ERR_RETRY_CONNECTION) {
+ DCHECK(establishing_tunnel_);
+ next_state_ = STATE_INIT_CONNECTION;
+ connection_->socket()->Disconnect();
+ connection_->Reset();
+ return OK;
+ }
+
+ if (result == ERR_PROXY_AUTH_REQUESTED) {
+ DCHECK(establishing_tunnel_);
+ HttpProxyClientSocket* tunnel_socket =
+ static_cast<HttpProxyClientSocket*>(connection_->socket());
+ DCHECK(tunnel_socket);
+ DCHECK(!tunnel_socket->IsConnected());
+ const HttpResponseInfo* auth_response = tunnel_socket->GetResponseInfo();
+
+ response_.headers = auth_response->headers;
+ headers_valid_ = true;
+ response_.auth_challenge = auth_response->auth_challenge;
+ pending_auth_target_ = HttpAuth::AUTH_PROXY;
+ return OK;
+ }
+
if (alternate_protocol_mode_ == kUsingAlternateProtocol) {
// Mark the alternate protocol as broken and fallback.
MarkBrokenAlternateProtocolAndFallback();
@@ -823,6 +847,10 @@ int HttpNetworkTransaction::DoInitConnectionComplete(int result) {
}
DCHECK_EQ(OK, result);
+ if (establishing_tunnel_) {
+ DCHECK(connection_->socket()->IsConnected());
+ establishing_tunnel_ = false;
+ }
if (using_spdy_) {
DCHECK(!connection_->is_initialized());
@@ -848,74 +876,21 @@ int HttpNetworkTransaction::DoInitConnectionComplete(int result) {
// Now we have a TCP connected socket. Perform other connection setup as
// needed.
UpdateConnectionTypeHistograms(CONNECTION_HTTP);
- if (using_ssl_) {
- if (proxy_info_.is_direct() || proxy_info_.is_socks())
- next_state_ = STATE_SSL_CONNECT;
- else
- next_state_ = STATE_TUNNEL_CONNECT;
- } else {
+ if (using_ssl_)
+ next_state_ = STATE_SSL_CONNECT;
+ else
next_state_ = STATE_GENERATE_PROXY_AUTH_TOKEN;
- }
}
return OK;
}
-int HttpNetworkTransaction::DoTunnelConnect() {
- next_state_ = STATE_TUNNEL_CONNECT_COMPLETE;
- establishing_tunnel_ = true;
-
- // Add a tunnel socket on top of our existing transport socket.
- ClientSocket* socket = connection_->release_socket();
- ClientSocketHandle* transport_socket_handle = new ClientSocketHandle();
- transport_socket_handle->set_socket(socket);
- socket = new HttpProxyClientSocket(transport_socket_handle, request_->url,
- endpoint_, auth_controllers_[HttpAuth::AUTH_PROXY].release(), true);
- connection_->set_socket(socket);
- return connection_->socket()->Connect(&io_callback_);
-}
-
-int HttpNetworkTransaction::DoTunnelConnectComplete(int result) {
- if (result == OK) {
- next_state_ = STATE_SSL_CONNECT;
- establishing_tunnel_ = false;
- }
-
- if (result == ERR_RETRY_CONNECTION) {
- HttpProxyClientSocket* tunnel_socket =
- reinterpret_cast<HttpProxyClientSocket*>(connection_->socket());
- auth_controllers_[HttpAuth::AUTH_PROXY].reset(
- tunnel_socket->TakeAuthController());
- next_state_ = STATE_INIT_CONNECTION;
- connection_->socket()->Disconnect();
- connection_->Reset();
- result = OK;
- } else if (result == ERR_PROXY_AUTH_REQUESTED) {
- HttpProxyClientSocket* tunnel_socket =
- reinterpret_cast<HttpProxyClientSocket*>(connection_->socket());
- const HttpResponseInfo* auth_response = tunnel_socket->GetResponseInfo();
-
- response_.headers = auth_response->headers;
- headers_valid_ = true;
- response_.auth_challenge = auth_response->auth_challenge;
- pending_auth_target_ = HttpAuth::AUTH_PROXY;
- result = OK;
- }
- return result;
-}
-
int HttpNetworkTransaction::DoTunnelRestartWithAuth() {
- DCHECK(establishing_tunnel_);
- DCHECK(!tunnel_credentials_.invalid);
- next_state_ = STATE_TUNNEL_CONNECT_COMPLETE;
-
+ next_state_ = STATE_INIT_CONNECTION_COMPLETE;
HttpProxyClientSocket* tunnel_socket =
reinterpret_cast<HttpProxyClientSocket*>(connection_->socket());
- tunnel_credentials_.invalid = true;
- return tunnel_socket->RestartWithAuth(tunnel_credentials_.username,
- tunnel_credentials_.password,
- &io_callback_);
+ return tunnel_socket->RestartWithAuth(&io_callback_);
}
int HttpNetworkTransaction::DoSSLConnect() {
@@ -1212,7 +1187,7 @@ int HttpNetworkTransaction::DoReadHeadersComplete(int result) {
endpoint_,
session_->mutable_alternate_protocols());
- int rv = HandleAuthChallenge(false);
+ int rv = HandleAuthChallenge();
if (rv != OK)
return rv;
@@ -1795,7 +1770,7 @@ bool HttpNetworkTransaction::ShouldApplyServerAuth() const {
return !(request_->load_flags & LOAD_DO_NOT_SEND_AUTH_DATA);
}
-int HttpNetworkTransaction::HandleAuthChallenge(bool establishing_tunnel) {
+int HttpNetworkTransaction::HandleAuthChallenge() {
scoped_refptr<HttpResponseHeaders> headers = GetResponseHeaders();
DCHECK(headers);
@@ -1808,8 +1783,7 @@ int HttpNetworkTransaction::HandleAuthChallenge(bool establishing_tunnel) {
return ERR_UNEXPECTED_PROXY_AUTH;
int rv = auth_controllers_[target]->HandleAuthChallenge(
- headers, (request_->load_flags & LOAD_DO_NOT_SEND_AUTH_DATA) != 0,
- establishing_tunnel);
+ headers, (request_->load_flags & LOAD_DO_NOT_SEND_AUTH_DATA) != 0, false);
if (auth_controllers_[target]->HaveAuthHandler())
pending_auth_target_ = target;
@@ -1868,8 +1842,6 @@ std::string HttpNetworkTransaction::DescribeState(State state) {
STATE_CASE(STATE_RESOLVE_PROXY_COMPLETE);
STATE_CASE(STATE_INIT_CONNECTION);
STATE_CASE(STATE_INIT_CONNECTION_COMPLETE);
- STATE_CASE(STATE_TUNNEL_CONNECT);
- STATE_CASE(STATE_TUNNEL_CONNECT_COMPLETE);
STATE_CASE(STATE_TUNNEL_RESTART_WITH_AUTH);
STATE_CASE(STATE_SSL_CONNECT);
STATE_CASE(STATE_SSL_CONNECT_COMPLETE);
diff --git a/net/http/http_network_transaction.h b/net/http/http_network_transaction.h
index 0c8d029..373aba8 100644
--- a/net/http/http_network_transaction.h
+++ b/net/http/http_network_transaction.h
@@ -83,8 +83,6 @@ class HttpNetworkTransaction : public HttpTransaction {
STATE_RESOLVE_PROXY_COMPLETE,
STATE_INIT_CONNECTION,
STATE_INIT_CONNECTION_COMPLETE,
- STATE_TUNNEL_CONNECT,
- STATE_TUNNEL_CONNECT_COMPLETE,
STATE_TUNNEL_RESTART_WITH_AUTH,
STATE_SSL_CONNECT,
STATE_SSL_CONNECT_COMPLETE,
@@ -129,8 +127,6 @@ class HttpNetworkTransaction : public HttpTransaction {
int DoResolveProxyComplete(int result);
int DoInitConnection();
int DoInitConnectionComplete(int result);
- int DoTunnelConnect();
- int DoTunnelConnectComplete(int result);
int DoTunnelRestartWithAuth();
int DoSSLConnect();
int DoSSLConnectComplete(int result);
@@ -230,7 +226,7 @@ class HttpNetworkTransaction : public HttpTransaction {
// Handles HTTP status code 401 or 407.
// HandleAuthChallenge() returns a network error code, or OK on success.
// May update |pending_auth_target_| or |response_.auth_challenge|.
- int HandleAuthChallenge(bool establishing_tunnel);
+ int HandleAuthChallenge();
bool HaveAuth(HttpAuth::Target target) const {
return auth_controllers_[target].get() &&
@@ -247,7 +243,8 @@ class HttpNetworkTransaction : public HttpTransaction {
static bool g_ignore_certificate_errors;
- scoped_ptr<HttpAuthController> auth_controllers_[HttpAuth::AUTH_NUM_TARGETS];
+ scoped_refptr<HttpAuthController>
+ auth_controllers_[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
@@ -318,10 +315,6 @@ class HttpNetworkTransaction : public HttpTransaction {
// specified by the URL, due to Alternate-Protocol or fixed testing ports.
HostPortPair endpoint_;
- // Stores login and password between |RestartWithAuth|
- // and |DoTunnelRestartWithAuth|.
- HttpAuth::Identity tunnel_credentials_;
-
// True when the tunnel is in the process of being established - we can't
// read from the socket until the tunnel is done.
bool establishing_tunnel_;
diff --git a/net/http/http_network_transaction_unittest.cc b/net/http/http_network_transaction_unittest.cc
index 36b3ce2..0a2fa31 100644
--- a/net/http/http_network_transaction_unittest.cc
+++ b/net/http/http_network_transaction_unittest.cc
@@ -64,7 +64,7 @@ class HttpNetworkSessionPeer {
void SetSocketPoolForHTTPProxy(
const HostPortPair& http_proxy,
- const scoped_refptr<TCPClientSocketPool>& pool) {
+ const scoped_refptr<HttpProxyClientSocketPool>& pool) {
session_->http_proxy_socket_pool_[http_proxy] = pool;
}
@@ -286,6 +286,8 @@ class CaptureGroupNameSocketPool : public EmulatedClientSocketPool {
typedef CaptureGroupNameSocketPool<TCPClientSocketPool>
CaptureGroupNameTCPSocketPool;
+typedef CaptureGroupNameSocketPool<HttpProxyClientSocketPool>
+CaptureGroupNameHttpProxySocketPool;
typedef CaptureGroupNameSocketPool<SOCKSClientSocketPool>
CaptureGroupNameSOCKSSocketPool;
@@ -1401,6 +1403,13 @@ TEST_F(HttpNetworkTransactionTest, BasicAuthProxyKeepAlive) {
EXPECT_EQ(L"myproxy:70", response->auth_challenge->host_and_port);
EXPECT_EQ(L"MyRealm1", response->auth_challenge->realm);
EXPECT_EQ(L"basic", response->auth_challenge->scheme);
+
+ // Cleanup the transaction so that the sockets are destroyed before the
+ // net log goes out of scope.
+ trans.reset();
+
+ // We also need to run the message queue for the socket releases to complete.
+ MessageLoop::current()->RunAllPending();
}
// Test that we don't read the response body when we fail to establish a tunnel,
@@ -4081,8 +4090,8 @@ TEST_F(HttpNetworkTransactionTest, GroupNameForHTTPProxyConnections) {
HttpNetworkSessionPeer peer(session);
- scoped_refptr<CaptureGroupNameTCPSocketPool> http_proxy_pool(
- new CaptureGroupNameTCPSocketPool(session.get()));
+ scoped_refptr<CaptureGroupNameHttpProxySocketPool> http_proxy_pool(
+ new CaptureGroupNameHttpProxySocketPool(session.get()));
peer.SetSocketPoolForHTTPProxy(
HostPortPair("http_proxy", 80), http_proxy_pool);
diff --git a/net/http/http_proxy_client_socket.cc b/net/http/http_proxy_client_socket.cc
index cdfa183..321b5ea 100644
--- a/net/http/http_proxy_client_socket.cc
+++ b/net/http/http_proxy_client_socket.cc
@@ -50,7 +50,8 @@ void BuildTunnelRequest(const HttpRequestInfo* request_info,
HttpProxyClientSocket::HttpProxyClientSocket(
ClientSocketHandle* transport_socket, const GURL& request_url,
- const HostPortPair& endpoint, HttpAuthController* auth, bool tunnel)
+ const HostPortPair& endpoint, const scoped_refptr<HttpAuthController>& auth,
+ bool tunnel)
: ALLOW_THIS_IN_INITIALIZER_LIST(
io_callback_(this, &HttpProxyClientSocket::OnIOComplete)),
next_state_(STATE_NONE),
@@ -92,14 +93,10 @@ int HttpProxyClientSocket::Connect(CompletionCallback* callback) {
return rv;
}
-int HttpProxyClientSocket::RestartWithAuth(const std::wstring& username,
- const std::wstring& password,
- CompletionCallback* callback) {
+int HttpProxyClientSocket::RestartWithAuth(CompletionCallback* callback) {
DCHECK_EQ(STATE_NONE, next_state_);
DCHECK(!user_callback_);
- auth_->ResetAuth(username, password);
-
int rv = PrepareForAuthRestart();
if (rv != OK)
return rv;
@@ -111,6 +108,9 @@ int HttpProxyClientSocket::RestartWithAuth(const std::wstring& username,
}
int HttpProxyClientSocket::PrepareForAuthRestart() {
+ if (!response_.headers.get())
+ return ERR_CONNECTION_RESET;
+
bool keep_alive = false;
if (response_.headers->IsKeepAlive() &&
http_stream_->CanFindEndOfResponse()) {
@@ -128,12 +128,13 @@ int HttpProxyClientSocket::PrepareForAuthRestart() {
}
int HttpProxyClientSocket::DidDrainBodyForAuthRestart(bool keep_alive) {
+ int rc = OK;
if (keep_alive && transport_->socket()->IsConnectedAndIdle()) {
next_state_ = STATE_GENERATE_AUTH_TOKEN;
transport_->set_is_reused(true);
} else {
transport_->socket()->Disconnect();
- return ERR_RETRY_CONNECTION;
+ rc = ERR_RETRY_CONNECTION;
}
// Reset the other member variables.
@@ -141,7 +142,7 @@ int HttpProxyClientSocket::DidDrainBodyForAuthRestart(bool keep_alive) {
http_stream_.reset();
request_headers_.clear();
response_ = HttpResponseInfo();
- return OK;
+ return rc;
}
void HttpProxyClientSocket::LogBlockedTunnelResponse(int response_code) const {
diff --git a/net/http/http_proxy_client_socket.h b/net/http/http_proxy_client_socket.h
index 6ced031..4870f0c 100644
--- a/net/http/http_proxy_client_socket.h
+++ b/net/http/http_proxy_client_socket.h
@@ -29,30 +29,26 @@ class IOBuffer;;
class HttpProxyClientSocket : public ClientSocket {
public:
- // Takes ownership of |auth| and the |transport_socket|, which should
- // already be connected by the time Connect() is called. If tunnel is true
- // then on Connect() this socket will establish an Http tunnel.
+ // Takes ownership of |transport_socket|, which should already be connected
+ // by the time Connect() is called. If tunnel is true then on Connect()
+ // this socket will establish an Http tunnel.
HttpProxyClientSocket(ClientSocketHandle* transport_socket,
const GURL& request_url, const HostPortPair& endpoint,
- HttpAuthController* auth, bool tunnel);
+ const scoped_refptr<HttpAuthController>& auth,
+ bool tunnel);
// On destruction Disconnect() is called.
virtual ~HttpProxyClientSocket();
// If Connect (or its callback) returns PROXY_AUTH_REQUESTED, then
- // credentials can be provided by calling RestartWithAuth.
- int RestartWithAuth(const std::wstring& username,
- const std::wstring& password,
- CompletionCallback* callback);
+ // credentials should be added to the HttpAuthController before calling
+ // RestartWithAuth.
+ int RestartWithAuth(CompletionCallback* callback);
const HttpResponseInfo* GetResponseInfo() const {
return response_.headers ? &response_ : NULL;
}
- HttpAuthController* TakeAuthController() {
- return auth_.release();
- }
-
// ClientSocket methods:
// Authenticates to the Http Proxy and then passes data freely.
@@ -128,7 +124,7 @@ class HttpProxyClientSocket : public ClientSocket {
scoped_ptr<HttpStream> http_stream_;
HttpRequestInfo request_;
HttpResponseInfo response_;
- scoped_ptr<HttpAuthController> auth_;
+ const scoped_refptr<HttpAuthController> auth_;
// The hostname and port of the endpoint. This is not necessarily the one
// specified by the URL, due to Alternate-Protocol or fixed testing ports.
diff --git a/net/http/http_proxy_client_socket_pool.cc b/net/http/http_proxy_client_socket_pool.cc
new file mode 100644
index 0000000..ce11bc3
--- /dev/null
+++ b/net/http/http_proxy_client_socket_pool.cc
@@ -0,0 +1,213 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/http/http_proxy_client_socket_pool.h"
+
+#include "base/time.h"
+#include "googleurl/src/gurl.h"
+#include "net/base/net_errors.h"
+#include "net/http/http_proxy_client_socket.h"
+#include "net/socket/client_socket_factory.h"
+#include "net/socket/client_socket_handle.h"
+#include "net/socket/client_socket_pool_base.h"
+
+namespace net {
+
+// HttpProxyConnectJobs will time out after this many seconds. Note this is on
+// top of the timeout for the transport socket.
+static const int kHttpProxyConnectJobTimeoutInSeconds = 30;
+
+HttpProxyConnectJob::HttpProxyConnectJob(
+ const std::string& group_name,
+ const HttpProxySocketParams& params,
+ const base::TimeDelta& timeout_duration,
+ const scoped_refptr<TCPClientSocketPool>& tcp_pool,
+ const scoped_refptr<HostResolver>& host_resolver,
+ Delegate* delegate,
+ NetLog* net_log)
+ : ConnectJob(group_name, timeout_duration, delegate,
+ BoundNetLog::Make(net_log, NetLog::SOURCE_CONNECT_JOB)),
+ params_(params),
+ tcp_pool_(tcp_pool),
+ resolver_(host_resolver),
+ ALLOW_THIS_IN_INITIALIZER_LIST(
+ callback_(this, &HttpProxyConnectJob::OnIOComplete)) {
+}
+
+HttpProxyConnectJob::~HttpProxyConnectJob() {}
+
+LoadState HttpProxyConnectJob::GetLoadState() const {
+ switch (next_state_) {
+ case kStateTCPConnect:
+ case kStateTCPConnectComplete:
+ return tcp_socket_handle_->GetLoadState();
+ case kStateHttpProxyConnect:
+ case kStateHttpProxyConnectComplete:
+ return LOAD_STATE_ESTABLISHING_PROXY_TUNNEL;
+ default:
+ NOTREACHED();
+ return LOAD_STATE_IDLE;
+ }
+}
+
+int HttpProxyConnectJob::ConnectInternal() {
+ next_state_ = kStateTCPConnect;
+ return DoLoop(OK);
+}
+
+void HttpProxyConnectJob::OnIOComplete(int result) {
+ int rv = DoLoop(result);
+ if (rv != ERR_IO_PENDING)
+ NotifyDelegateOfCompletion(rv); // Deletes |this|
+}
+
+int HttpProxyConnectJob::DoLoop(int result) {
+ DCHECK_NE(next_state_, kStateNone);
+
+ int rv = result;
+ do {
+ State state = next_state_;
+ next_state_ = kStateNone;
+ switch (state) {
+ case kStateTCPConnect:
+ DCHECK_EQ(OK, rv);
+ rv = DoTCPConnect();
+ break;
+ case kStateTCPConnectComplete:
+ rv = DoTCPConnectComplete(rv);
+ break;
+ case kStateHttpProxyConnect:
+ DCHECK_EQ(OK, rv);
+ rv = DoHttpProxyConnect();
+ break;
+ case kStateHttpProxyConnectComplete:
+ rv = DoHttpProxyConnectComplete(rv);
+ break;
+ default:
+ NOTREACHED() << "bad state";
+ rv = ERR_FAILED;
+ break;
+ }
+ } while (rv != ERR_IO_PENDING && next_state_ != kStateNone);
+
+ return rv;
+}
+
+int HttpProxyConnectJob::DoTCPConnect() {
+ next_state_ = kStateTCPConnectComplete;
+ tcp_socket_handle_.reset(new ClientSocketHandle());
+ return tcp_socket_handle_->Init(group_name(), params_.tcp_params(),
+ params_.tcp_params().destination().priority(),
+ &callback_, tcp_pool_, net_log());
+}
+
+int HttpProxyConnectJob::DoTCPConnectComplete(int result) {
+ if (result != OK)
+ return result;
+
+ // Reset the timer to just the length of time allowed for HttpProxy handshake
+ // so that a fast TCP connection plus a slow HttpProxy failure doesn't take
+ // longer to timeout than it should.
+ ResetTimer(base::TimeDelta::FromSeconds(
+ kHttpProxyConnectJobTimeoutInSeconds));
+ next_state_ = kStateHttpProxyConnect;
+ return result;
+}
+
+int HttpProxyConnectJob::DoHttpProxyConnect() {
+ next_state_ = kStateHttpProxyConnectComplete;
+
+ // Add a HttpProxy connection on top of the tcp socket.
+ socket_.reset(new HttpProxyClientSocket(tcp_socket_handle_.release(),
+ params_.request_url(),
+ params_.endpoint(),
+ params_.auth_controller(),
+ params_.tunnel()));
+ return socket_->Connect(&callback_);
+}
+
+int HttpProxyConnectJob::DoHttpProxyConnectComplete(int result) {
+ DCHECK_NE(result, ERR_RETRY_CONNECTION);
+
+ if (result == OK || result == ERR_PROXY_AUTH_REQUESTED)
+ set_socket(socket_.release());
+
+ return result;
+}
+
+ConnectJob*
+HttpProxyClientSocketPool::HttpProxyConnectJobFactory::NewConnectJob(
+ const std::string& group_name,
+ const PoolBase::Request& request,
+ ConnectJob::Delegate* delegate) const {
+ return new HttpProxyConnectJob(group_name, request.params(),
+ ConnectionTimeout(), tcp_pool_, host_resolver_,
+ delegate, net_log_);
+}
+
+base::TimeDelta
+HttpProxyClientSocketPool::HttpProxyConnectJobFactory::ConnectionTimeout()
+const {
+ return tcp_pool_->ConnectionTimeout() +
+ base::TimeDelta::FromSeconds(kHttpProxyConnectJobTimeoutInSeconds);
+}
+
+HttpProxyClientSocketPool::HttpProxyClientSocketPool(
+ int max_sockets,
+ int max_sockets_per_group,
+ const scoped_refptr<ClientSocketPoolHistograms>& histograms,
+ const scoped_refptr<HostResolver>& host_resolver,
+ const scoped_refptr<TCPClientSocketPool>& tcp_pool,
+ NetLog* net_log)
+ : base_(max_sockets, max_sockets_per_group, histograms,
+ base::TimeDelta::FromSeconds(
+ ClientSocketPool::unused_idle_socket_timeout()),
+ base::TimeDelta::FromSeconds(kUsedIdleSocketTimeout),
+ new HttpProxyConnectJobFactory(tcp_pool, host_resolver, net_log)) {}
+
+HttpProxyClientSocketPool::~HttpProxyClientSocketPool() {}
+
+int HttpProxyClientSocketPool::RequestSocket(const std::string& group_name,
+ const void* socket_params,
+ RequestPriority priority,
+ ClientSocketHandle* handle,
+ CompletionCallback* callback,
+ const BoundNetLog& net_log) {
+ const HttpProxySocketParams* casted_socket_params =
+ static_cast<const HttpProxySocketParams*>(socket_params);
+
+ return base_.RequestSocket(group_name, *casted_socket_params, priority,
+ handle, callback, net_log);
+}
+
+void HttpProxyClientSocketPool::CancelRequest(
+ const std::string& group_name,
+ const ClientSocketHandle* handle) {
+ base_.CancelRequest(group_name, handle);
+}
+
+void HttpProxyClientSocketPool::ReleaseSocket(const std::string& group_name,
+ ClientSocket* socket, int id) {
+ base_.ReleaseSocket(group_name, socket, id);
+}
+
+void HttpProxyClientSocketPool::Flush() {
+ base_.Flush();
+}
+
+void HttpProxyClientSocketPool::CloseIdleSockets() {
+ base_.CloseIdleSockets();
+}
+
+int HttpProxyClientSocketPool::IdleSocketCountInGroup(
+ const std::string& group_name) const {
+ return base_.IdleSocketCountInGroup(group_name);
+}
+
+LoadState HttpProxyClientSocketPool::GetLoadState(
+ const std::string& group_name, const ClientSocketHandle* handle) const {
+ return base_.GetLoadState(group_name, handle);
+}
+
+} // namespace net
diff --git a/net/http/http_proxy_client_socket_pool.h b/net/http/http_proxy_client_socket_pool.h
new file mode 100644
index 0000000..dc85c32
--- /dev/null
+++ b/net/http/http_proxy_client_socket_pool.h
@@ -0,0 +1,201 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_HTTP_HTTP_PROXY_CLIENT_SOCKET_POOL_H_
+#define NET_HTTP_HTTP_PROXY_CLIENT_SOCKET_POOL_H_
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/scoped_ptr.h"
+#include "base/time.h"
+#include "net/base/host_port_pair.h"
+#include "net/base/host_resolver.h"
+#include "net/proxy/proxy_server.h"
+#include "net/socket/client_socket_pool_base.h"
+#include "net/socket/client_socket_pool_histograms.h"
+#include "net/socket/client_socket_pool.h"
+#include "net/socket/tcp_client_socket_pool.h"
+
+namespace net {
+
+class ClientSocketFactory;
+class ConnectJobFactory;
+class HttpAuthController;
+
+class HttpProxySocketParams {
+ public:
+ HttpProxySocketParams(const TCPSocketParams& proxy_server,
+ const GURL& request_url, HostPortPair endpoint,
+ scoped_refptr<HttpAuthController> auth_controller,
+ bool tunnel)
+ : tcp_params_(proxy_server),
+ request_url_(request_url),
+ endpoint_(endpoint),
+ auth_controller_(auth_controller),
+ tunnel_(tunnel) {
+ }
+
+ const TCPSocketParams& tcp_params() const { return tcp_params_; }
+ const GURL& request_url() const { return request_url_; }
+ const HostPortPair& endpoint() const { return endpoint_; }
+ const scoped_refptr<HttpAuthController>& auth_controller() const {
+ return auth_controller_;
+ }
+ bool tunnel() const { return tunnel_; }
+
+ private:
+ const TCPSocketParams tcp_params_;
+ const GURL request_url_;
+ const HostPortPair endpoint_;
+ const scoped_refptr<HttpAuthController> auth_controller_;
+ const bool tunnel_;
+};
+
+// HttpProxyConnectJob optionally establishes a tunnel through the proxy
+// server after connecting the underlying transport socket.
+class HttpProxyConnectJob : public ConnectJob {
+ public:
+ HttpProxyConnectJob(const std::string& group_name,
+ const HttpProxySocketParams& params,
+ const base::TimeDelta& timeout_duration,
+ const scoped_refptr<TCPClientSocketPool>& tcp_pool,
+ const scoped_refptr<HostResolver> &host_resolver,
+ Delegate* delegate,
+ NetLog* net_log);
+ virtual ~HttpProxyConnectJob();
+
+ // ConnectJob methods.
+ virtual LoadState GetLoadState() const;
+
+ private:
+ enum State {
+ kStateTCPConnect,
+ kStateTCPConnectComplete,
+ kStateHttpProxyConnect,
+ kStateHttpProxyConnectComplete,
+ kStateNone,
+ };
+
+ // Begins the tcp connection and the optional Http proxy tunnel. If the
+ // request is not immediately servicable (likely), the request will return
+ // ERR_IO_PENDING. An OK return from this function or the callback means
+ // that the connection is established; ERR_PROXY_AUTH_REQUESTED means
+ // that the tunnel needs authentication credentials, the socket will be
+ // returned in this case, and must be release back to the pool; or
+ // a standard net error code will be returned.
+ virtual int ConnectInternal();
+
+ void OnIOComplete(int result);
+
+ // Runs the state transition loop.
+ int DoLoop(int result);
+
+ int DoTCPConnect();
+ int DoTCPConnectComplete(int result);
+ int DoHttpProxyConnect();
+ int DoHttpProxyConnectComplete(int result);
+
+ HttpProxySocketParams params_;
+ const scoped_refptr<TCPClientSocketPool> tcp_pool_;
+ const scoped_refptr<HostResolver> resolver_;
+
+ State next_state_;
+ CompletionCallbackImpl<HttpProxyConnectJob> callback_;
+ scoped_ptr<ClientSocketHandle> tcp_socket_handle_;
+ scoped_ptr<ClientSocket> socket_;
+
+ DISALLOW_COPY_AND_ASSIGN(HttpProxyConnectJob);
+};
+
+class HttpProxyClientSocketPool : public ClientSocketPool {
+ public:
+ HttpProxyClientSocketPool(
+ int max_sockets,
+ int max_sockets_per_group,
+ const scoped_refptr<ClientSocketPoolHistograms>& histograms,
+ const scoped_refptr<HostResolver>& host_resolver,
+ const scoped_refptr<TCPClientSocketPool>& tcp_pool,
+ NetLog* net_log);
+
+ // ClientSocketPool methods:
+ virtual int RequestSocket(const std::string& group_name,
+ const void* connect_params,
+ RequestPriority priority,
+ ClientSocketHandle* handle,
+ CompletionCallback* callback,
+ const BoundNetLog& net_log);
+
+ virtual void CancelRequest(const std::string& group_name,
+ const ClientSocketHandle* handle);
+
+ virtual void ReleaseSocket(const std::string& group_name,
+ ClientSocket* socket,
+ int id);
+
+ virtual void Flush();
+
+ virtual void CloseIdleSockets();
+
+ virtual int IdleSocketCount() const {
+ return base_.idle_socket_count();
+ }
+
+ virtual int IdleSocketCountInGroup(const std::string& group_name) const;
+
+ virtual LoadState GetLoadState(const std::string& group_name,
+ const ClientSocketHandle* handle) const;
+
+ virtual base::TimeDelta ConnectionTimeout() const {
+ return base_.ConnectionTimeout();
+ }
+
+ virtual scoped_refptr<ClientSocketPoolHistograms> histograms() const {
+ return base_.histograms();
+ };
+
+ protected:
+ virtual ~HttpProxyClientSocketPool();
+
+ private:
+ typedef ClientSocketPoolBase<HttpProxySocketParams> PoolBase;
+
+ class HttpProxyConnectJobFactory : public PoolBase::ConnectJobFactory {
+ public:
+ HttpProxyConnectJobFactory(
+ const scoped_refptr<TCPClientSocketPool>& tcp_pool,
+ HostResolver* host_resolver,
+ NetLog* net_log)
+ : tcp_pool_(tcp_pool),
+ host_resolver_(host_resolver),
+ net_log_(net_log) {}
+
+ virtual ~HttpProxyConnectJobFactory() {}
+
+ // ClientSocketPoolBase::ConnectJobFactory methods.
+ virtual ConnectJob* NewConnectJob(const std::string& group_name,
+ const PoolBase::Request& request,
+ ConnectJob::Delegate* delegate) const;
+
+ virtual base::TimeDelta ConnectionTimeout() const;
+
+ private:
+ const scoped_refptr<TCPClientSocketPool> tcp_pool_;
+ const scoped_refptr<HostResolver> host_resolver_;
+ NetLog* net_log_;
+
+ DISALLOW_COPY_AND_ASSIGN(HttpProxyConnectJobFactory);
+ };
+
+ PoolBase base_;
+
+ DISALLOW_COPY_AND_ASSIGN(HttpProxyClientSocketPool);
+};
+
+REGISTER_SOCKET_PARAMS_FOR_POOL(HttpProxyClientSocketPool,
+ HttpProxySocketParams);
+
+} // namespace net
+
+#endif // NET_HTTP_HTTP_PROXY_CLIENT_SOCKET_POOL_H_
diff --git a/net/http/http_proxy_client_socket_pool_unittest.cc b/net/http/http_proxy_client_socket_pool_unittest.cc
new file mode 100644
index 0000000..a3b6cca
--- /dev/null
+++ b/net/http/http_proxy_client_socket_pool_unittest.cc
@@ -0,0 +1,318 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/http/http_proxy_client_socket_pool.h"
+
+#include "base/callback.h"
+#include "base/compiler_specific.h"
+#include "base/time.h"
+#include "net/base/auth.h"
+#include "net/base/mock_host_resolver.h"
+#include "net/base/net_errors.h"
+#include "net/base/test_completion_callback.h"
+#include "net/http/http_auth_controller.h"
+#include "net/http/http_network_session.h"
+#include "net/http/http_request_headers.h"
+#include "net/http/http_response_headers.h"
+#include "net/socket/client_socket_factory.h"
+#include "net/socket/client_socket_handle.h"
+#include "net/socket/client_socket_pool_histograms.h"
+#include "net/socket/socket_test_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+
+namespace {
+
+const int kMaxSockets = 32;
+const int kMaxSocketsPerGroup = 6;
+
+struct MockHttpAuthControllerData {
+ MockHttpAuthControllerData(std::string header) : auth_header(header) {}
+
+ std::string auth_header;
+};
+
+class MockHttpAuthController : public HttpAuthController {
+ public:
+ MockHttpAuthController()
+ : HttpAuthController(HttpAuth::AUTH_PROXY, GURL(),
+ scoped_refptr<HttpNetworkSession>(NULL),
+ BoundNetLog()),
+ data_(NULL),
+ data_index_(0),
+ data_count_(0) {
+ }
+
+ void SetMockAuthControllerData(struct MockHttpAuthControllerData* data,
+ size_t data_length) {
+ data_ = data;
+ data_count_ = data_length;
+ }
+
+ // HttpAuthController methods.
+ virtual int MaybeGenerateAuthToken(const HttpRequestInfo* request,
+ CompletionCallback* callback) {
+ return OK;
+ }
+ virtual void AddAuthorizationHeader(
+ HttpRequestHeaders* authorization_headers) {
+ authorization_headers->AddHeadersFromString(CurrentData().auth_header);
+ }
+ virtual int HandleAuthChallenge(scoped_refptr<HttpResponseHeaders> headers,
+ bool do_not_send_server_auth,
+ bool establishing_tunnel) {
+ return OK;
+ }
+ virtual bool HaveAuthHandler() const { return HaveAuth(); }
+ virtual bool HaveAuth() const {
+ return CurrentData().auth_header.size() != 0; }
+
+ private:
+ virtual ~MockHttpAuthController() {}
+ const struct MockHttpAuthControllerData& CurrentData() const {
+ DCHECK(data_index_ < data_count_);
+ return data_[data_index_];
+ }
+
+ MockHttpAuthControllerData* data_;
+ size_t data_index_;
+ size_t data_count_;
+};
+
+class HttpProxyClientSocketPoolTest : public ClientSocketPoolTest {
+ protected:
+ HttpProxyClientSocketPoolTest()
+ : ignored_tcp_socket_params_(
+ HostPortPair("proxy", 80), MEDIUM, GURL(), false),
+ tcp_histograms_(new ClientSocketPoolHistograms("MockTCP")),
+ tcp_socket_pool_(new MockTCPClientSocketPool(kMaxSockets,
+ kMaxSocketsPerGroup, tcp_histograms_, &tcp_client_socket_factory_)),
+ notunnel_socket_params_(ignored_tcp_socket_params_, GURL("http://host"),
+ HostPortPair("host", 80), NULL, false),
+ auth_controller_(new MockHttpAuthController),
+ tunnel_socket_params_(ignored_tcp_socket_params_, GURL("http://host"),
+ HostPortPair("host", 80), auth_controller_, true),
+ http_proxy_histograms_(
+ new ClientSocketPoolHistograms("HttpProxyUnitTest")),
+ pool_(new HttpProxyClientSocketPool(kMaxSockets, kMaxSocketsPerGroup,
+ http_proxy_histograms_, NULL, tcp_socket_pool_, NULL)) {
+ }
+
+ int StartRequest(const std::string& group_name, RequestPriority priority) {
+ return StartRequestUsingPool(
+ pool_, group_name, priority, tunnel_socket_params_);
+ }
+
+ TCPSocketParams ignored_tcp_socket_params_;
+ scoped_refptr<ClientSocketPoolHistograms> tcp_histograms_;
+ MockClientSocketFactory tcp_client_socket_factory_;
+ scoped_refptr<MockTCPClientSocketPool> tcp_socket_pool_;
+
+ HttpProxySocketParams notunnel_socket_params_;
+ scoped_refptr<MockHttpAuthController> auth_controller_;
+ HttpProxySocketParams tunnel_socket_params_;
+ scoped_refptr<ClientSocketPoolHistograms> http_proxy_histograms_;
+ scoped_refptr<HttpProxyClientSocketPool> pool_;
+};
+
+TEST_F(HttpProxyClientSocketPoolTest, NoTunnel) {
+ StaticSocketDataProvider data;
+ data.set_connect_data(MockConnect(false, 0));
+ tcp_client_socket_factory_.AddSocketDataProvider(&data);
+
+ ClientSocketHandle handle;
+ int rv = handle.Init("a", notunnel_socket_params_, LOW, NULL, pool_,
+ BoundNetLog());
+ EXPECT_EQ(OK, rv);
+ EXPECT_TRUE(handle.is_initialized());
+ EXPECT_TRUE(handle.socket());
+}
+
+TEST_F(HttpProxyClientSocketPoolTest, NeedAuth) {
+ MockWrite writes[] = {
+ MockWrite("CONNECT host:80 HTTP/1.1\r\n"
+ "Host: host\r\n"
+ "Proxy-Connection: keep-alive\r\n\r\n"),
+ };
+ MockRead reads[] = {
+ // No credentials.
+ MockRead("HTTP/1.1 407 Proxy Authentication Required\r\n"),
+ MockRead("Proxy-Authenticate: Basic realm=\"MyRealm1\"\r\n"),
+ MockRead("Content-Length: 10\r\n\r\n"),
+ MockRead("0123456789"),
+ };
+ StaticSocketDataProvider data(reads, arraysize(reads), writes,
+ arraysize(writes));
+
+ tcp_client_socket_factory_.AddSocketDataProvider(&data);
+ MockHttpAuthControllerData auth_data[] = {
+ MockHttpAuthControllerData(""),
+ };
+ auth_controller_->SetMockAuthControllerData(auth_data, arraysize(auth_data));
+
+ ClientSocketHandle handle;
+ TestCompletionCallback callback;
+ int rv = handle.Init("a", tunnel_socket_params_, LOW, &callback, pool_,
+ BoundNetLog());
+ EXPECT_EQ(ERR_IO_PENDING, rv);
+ EXPECT_FALSE(handle.is_initialized());
+ EXPECT_FALSE(handle.socket());
+
+ EXPECT_EQ(ERR_PROXY_AUTH_REQUESTED, callback.WaitForResult());
+ EXPECT_TRUE(handle.is_initialized());
+ EXPECT_TRUE(handle.socket());
+}
+
+TEST_F(HttpProxyClientSocketPoolTest, HaveAuth) {
+ MockWrite writes[] = {
+ MockWrite(false,
+ "CONNECT host:80 HTTP/1.1\r\n"
+ "Host: host\r\n"
+ "Proxy-Connection: keep-alive\r\n"
+ "Proxy-Authorization: Basic Zm9vOmJheg==\r\n\r\n"),
+ };
+ MockRead reads[] = {
+ MockRead(false, "HTTP/1.1 200 Connection Established\r\n\r\n"),
+ };
+ StaticSocketDataProvider data(reads, arraysize(reads), writes,
+ arraysize(writes));
+ data.set_connect_data(MockConnect(false, 0));
+
+ tcp_client_socket_factory_.AddSocketDataProvider(&data);
+ MockHttpAuthControllerData auth_data[] = {
+ MockHttpAuthControllerData("Proxy-Authorization: Basic Zm9vOmJheg=="),
+ };
+ auth_controller_->SetMockAuthControllerData(auth_data, arraysize(auth_data));
+
+ ClientSocketHandle handle;
+ TestCompletionCallback callback;
+ int rv = handle.Init("a", tunnel_socket_params_, LOW, &callback, pool_,
+ BoundNetLog());
+ EXPECT_EQ(OK, rv);
+ EXPECT_TRUE(handle.is_initialized());
+ EXPECT_TRUE(handle.socket());
+}
+
+TEST_F(HttpProxyClientSocketPoolTest, AsyncHaveAuth) {
+ MockWrite writes[] = {
+ MockWrite("CONNECT host:80 HTTP/1.1\r\n"
+ "Host: host\r\n"
+ "Proxy-Connection: keep-alive\r\n"
+ "Proxy-Authorization: Basic Zm9vOmJheg==\r\n\r\n"),
+ };
+ MockRead reads[] = {
+ MockRead("HTTP/1.1 200 Connection Established\r\n\r\n"),
+ };
+ StaticSocketDataProvider data(reads, arraysize(reads), writes,
+ arraysize(writes));
+
+ tcp_client_socket_factory_.AddSocketDataProvider(&data);
+ MockHttpAuthControllerData auth_data[] = {
+ MockHttpAuthControllerData("Proxy-Authorization: Basic Zm9vOmJheg=="),
+ };
+ auth_controller_->SetMockAuthControllerData(auth_data, arraysize(auth_data));
+
+ ClientSocketHandle handle;
+ TestCompletionCallback callback;
+ int rv = handle.Init("a", tunnel_socket_params_, LOW, &callback, pool_,
+ BoundNetLog());
+ EXPECT_EQ(ERR_IO_PENDING, rv);
+ EXPECT_FALSE(handle.is_initialized());
+ EXPECT_FALSE(handle.socket());
+
+ EXPECT_EQ(OK, callback.WaitForResult());
+ EXPECT_TRUE(handle.is_initialized());
+ EXPECT_TRUE(handle.socket());
+}
+
+TEST_F(HttpProxyClientSocketPoolTest, TCPError) {
+ StaticSocketDataProvider data;
+ data.set_connect_data(MockConnect(true, ERR_CONNECTION_CLOSED));
+
+ tcp_client_socket_factory_.AddSocketDataProvider(&data);
+
+ ClientSocketHandle handle;
+ TestCompletionCallback callback;
+ int rv = handle.Init("a", tunnel_socket_params_, LOW, &callback, pool_,
+ BoundNetLog());
+ EXPECT_EQ(ERR_IO_PENDING, rv);
+ EXPECT_FALSE(handle.is_initialized());
+ EXPECT_FALSE(handle.socket());
+
+ EXPECT_EQ(ERR_CONNECTION_CLOSED, callback.WaitForResult());
+ EXPECT_FALSE(handle.is_initialized());
+ EXPECT_FALSE(handle.socket());
+}
+
+TEST_F(HttpProxyClientSocketPoolTest, TunnelUnexpectedClose) {
+ MockWrite writes[] = {
+ MockWrite("CONNECT host:80 HTTP/1.1\r\n"
+ "Host: host\r\n"
+ "Proxy-Connection: keep-alive\r\n"
+ "Proxy-Authorization: Basic Zm9vOmJheg==\r\n\r\n"),
+ };
+ MockRead reads[] = {
+ MockRead("HTTP/1.1 200 Conn"),
+ MockRead(true, ERR_CONNECTION_CLOSED),
+ };
+ StaticSocketDataProvider data(reads, arraysize(reads), writes,
+ arraysize(writes));
+
+ tcp_client_socket_factory_.AddSocketDataProvider(&data);
+ MockHttpAuthControllerData auth_data[] = {
+ MockHttpAuthControllerData("Proxy-Authorization: Basic Zm9vOmJheg=="),
+ };
+ auth_controller_->SetMockAuthControllerData(auth_data, arraysize(auth_data));
+
+ ClientSocketHandle handle;
+ TestCompletionCallback callback;
+ int rv = handle.Init("a", tunnel_socket_params_, LOW, &callback, pool_,
+ BoundNetLog());
+ EXPECT_EQ(ERR_IO_PENDING, rv);
+ EXPECT_FALSE(handle.is_initialized());
+ EXPECT_FALSE(handle.socket());
+
+ EXPECT_EQ(ERR_TUNNEL_CONNECTION_FAILED, callback.WaitForResult());
+ EXPECT_FALSE(handle.is_initialized());
+ EXPECT_FALSE(handle.socket());
+}
+
+TEST_F(HttpProxyClientSocketPoolTest, TunnelSetupError) {
+ MockWrite writes[] = {
+ MockWrite("CONNECT host:80 HTTP/1.1\r\n"
+ "Host: host\r\n"
+ "Proxy-Connection: keep-alive\r\n"
+ "Proxy-Authorization: Basic Zm9vOmJheg==\r\n\r\n"),
+ };
+ MockRead reads[] = {
+ MockRead("HTTP/1.1 304 Not Modified\r\n\r\n"),
+ };
+ StaticSocketDataProvider data(reads, arraysize(reads), writes,
+ arraysize(writes));
+
+ tcp_client_socket_factory_.AddSocketDataProvider(&data);
+ MockHttpAuthControllerData auth_data[] = {
+ MockHttpAuthControllerData("Proxy-Authorization: Basic Zm9vOmJheg=="),
+ };
+ auth_controller_->SetMockAuthControllerData(auth_data, arraysize(auth_data));
+
+ ClientSocketHandle handle;
+ TestCompletionCallback callback;
+ int rv = handle.Init("a", tunnel_socket_params_, LOW, &callback, pool_,
+ BoundNetLog());
+ EXPECT_EQ(ERR_IO_PENDING, rv);
+ EXPECT_FALSE(handle.is_initialized());
+ EXPECT_FALSE(handle.socket());
+
+ EXPECT_EQ(ERR_TUNNEL_CONNECTION_FAILED, callback.WaitForResult());
+ EXPECT_FALSE(handle.is_initialized());
+ EXPECT_FALSE(handle.socket());
+}
+
+// It would be nice to also test the timeouts in HttpProxyClientSocketPool.
+
+} // namespace
+
+} // namespace net