blob: dcc893eeb815248aef2bea10d9c65ef5350f3700 [file] [log] [blame]
// 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/string_util.h"
#include "base/utf_string_conversions.h"
#include "net/base/mock_host_resolver.h"
#include "net/base/net_errors.h"
#include "net/base/ssl_config_service_defaults.h"
#include "net/base/test_completion_callback.h"
#include "net/http/http_auth_handler_factory.h"
#include "net/http/http_network_session.h"
#include "net/http/http_proxy_client_socket.h"
#include "net/proxy/proxy_service.h"
#include "net/socket/client_socket_handle.h"
#include "net/socket/client_socket_pool_histograms.h"
#include "net/socket/socket_test_util.h"
#include "net/spdy/spdy_session_pool.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace net {
namespace {
const int kMaxSockets = 32;
const int kMaxSocketsPerGroup = 6;
enum HttpProxyType {
HTTP,
HTTPS
};
typedef ::testing::TestWithParam<HttpProxyType> TestWithHttpParam;
class HttpProxyClientSocketPoolTest : public TestWithHttpParam {
protected:
HttpProxyClientSocketPoolTest()
: ssl_config_(),
ignored_tcp_socket_params_(new TCPSocketParams(
HostPortPair("proxy", 80), MEDIUM, GURL(), false)),
ignored_ssl_socket_params_(new SSLSocketParams(
ignored_tcp_socket_params_, NULL, NULL, ProxyServer::SCHEME_DIRECT,
"host", ssl_config_, 0, false, false)),
tcp_histograms_(new ClientSocketPoolHistograms("MockTCP")),
tcp_socket_pool_(new MockTCPClientSocketPool(kMaxSockets,
kMaxSocketsPerGroup, tcp_histograms_, &tcp_client_socket_factory_)),
ssl_socket_pool_(new MockSSLClientSocketPool(kMaxSockets,
kMaxSocketsPerGroup, tcp_histograms_, &tcp_client_socket_factory_)),
http_auth_handler_factory_(HttpAuthHandlerFactory::CreateDefault()),
session_(new HttpNetworkSession(new MockHostResolver,
ProxyService::CreateNull(),
&socket_factory_,
new SSLConfigServiceDefaults,
new SpdySessionPool(NULL),
http_auth_handler_factory_.get(),
NULL,
NULL)),
http_proxy_histograms_(
new ClientSocketPoolHistograms("HttpProxyUnitTest")),
pool_(new HttpProxyClientSocketPool(kMaxSockets, kMaxSocketsPerGroup,
http_proxy_histograms_, NULL, tcp_socket_pool_, ssl_socket_pool_,
NULL)) {
}
void AddAuthToCache() {
const string16 kFoo(ASCIIToUTF16("foo"));
const string16 kBar(ASCIIToUTF16("bar"));
session_->auth_cache()->Add(GURL("http://proxy/"), "MyRealm1", "Basic",
"Basic realm=MyRealm1", kFoo, kBar, "/");
}
scoped_refptr<TCPSocketParams> GetTcpParams() {
if (GetParam() == HTTPS)
return scoped_refptr<TCPSocketParams>();
return ignored_tcp_socket_params_;
}
scoped_refptr<SSLSocketParams> GetSslParams() {
if (GetParam() == HTTP)
return scoped_refptr<SSLSocketParams>();
return ignored_ssl_socket_params_;
}
// Returns the a correctly constructed HttpProxyParms
// for the HTTP or HTTPS proxy.
scoped_refptr<HttpProxySocketParams> GetParams(bool tunnel) {
return scoped_refptr<HttpProxySocketParams>(new HttpProxySocketParams(
GetTcpParams(), GetSslParams(), GURL("http://host/"), "",
HostPortPair("host", 80), session_, tunnel));
}
scoped_refptr<HttpProxySocketParams> GetTunnelParams() {
return GetParams(true);
}
scoped_refptr<HttpProxySocketParams> GetNoTunnelParams() {
return GetParams(false);
}
SSLConfig ssl_config_;
scoped_refptr<TCPSocketParams> ignored_tcp_socket_params_;
scoped_refptr<SSLSocketParams> ignored_ssl_socket_params_;
scoped_refptr<ClientSocketPoolHistograms> tcp_histograms_;
MockClientSocketFactory tcp_client_socket_factory_;
scoped_refptr<MockTCPClientSocketPool> tcp_socket_pool_;
scoped_refptr<MockSSLClientSocketPool> ssl_socket_pool_;
MockClientSocketFactory socket_factory_;
scoped_ptr<HttpAuthHandlerFactory> http_auth_handler_factory_;
scoped_refptr<HttpNetworkSession> session_;
scoped_refptr<ClientSocketPoolHistograms> http_proxy_histograms_;
scoped_refptr<HttpProxyClientSocketPool> pool_;
};
//-----------------------------------------------------------------------------
// All tests are run with three different connection types: SPDY after NPN
// negotiation, SPDY without SSL, and SPDY with SSL.
INSTANTIATE_TEST_CASE_P(HttpProxyClientSocketPoolTests,
HttpProxyClientSocketPoolTest,
::testing::Values(HTTP, HTTPS));
TEST_P(HttpProxyClientSocketPoolTest, NoTunnel) {
StaticSocketDataProvider data;
data.set_connect_data(MockConnect(false, 0));
tcp_client_socket_factory_.AddSocketDataProvider(&data);
ClientSocketHandle handle;
int rv = handle.Init("a", GetNoTunnelParams(), LOW, NULL, pool_,
BoundNetLog());
EXPECT_EQ(OK, rv);
EXPECT_TRUE(handle.is_initialized());
EXPECT_TRUE(handle.socket());
HttpProxyClientSocket* tunnel_socket =
static_cast<HttpProxyClientSocket*>(handle.socket());
EXPECT_TRUE(tunnel_socket->IsConnected());
}
TEST_P(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);
ClientSocketHandle handle;
TestCompletionCallback callback;
int rv = handle.Init("a", GetTunnelParams(), 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());
HttpProxyClientSocket* tunnel_socket =
static_cast<HttpProxyClientSocket*>(handle.socket());
EXPECT_FALSE(tunnel_socket->IsConnected());
}
TEST_P(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 Zm9vOmJhcg==\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);
AddAuthToCache();
ClientSocketHandle handle;
TestCompletionCallback callback;
int rv = handle.Init("a", GetTunnelParams(), LOW, &callback, pool_,
BoundNetLog());
EXPECT_EQ(OK, rv);
EXPECT_TRUE(handle.is_initialized());
EXPECT_TRUE(handle.socket());
HttpProxyClientSocket* tunnel_socket =
static_cast<HttpProxyClientSocket*>(handle.socket());
EXPECT_TRUE(tunnel_socket->IsConnected());
}
TEST_P(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 Zm9vOmJhcg==\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);
AddAuthToCache();
ClientSocketHandle handle;
TestCompletionCallback callback;
int rv = handle.Init("a", GetTunnelParams(), 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());
HttpProxyClientSocket* tunnel_socket =
static_cast<HttpProxyClientSocket*>(handle.socket());
EXPECT_TRUE(tunnel_socket->IsConnected());
}
TEST_P(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", GetTunnelParams(), 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_P(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 Zm9vOmJhcg==\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);
AddAuthToCache();
ClientSocketHandle handle;
TestCompletionCallback callback;
int rv = handle.Init("a", GetTunnelParams(), 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_P(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 Zm9vOmJhcg==\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);
AddAuthToCache();
ClientSocketHandle handle;
TestCompletionCallback callback;
int rv = handle.Init("a", GetTunnelParams(), 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