| Index: net/socket/transport_client_socket_pool_unittest.cc
|
| ===================================================================
|
| --- net/socket/transport_client_socket_pool_unittest.cc (revision 85841)
|
| +++ net/socket/transport_client_socket_pool_unittest.cc (working copy)
|
| @@ -6,10 +6,14 @@
|
|
|
| #include "base/callback.h"
|
| #include "base/compiler_specific.h"
|
| +#include "base/logging.h"
|
| #include "base/message_loop.h"
|
| #include "base/threading/platform_thread.h"
|
| +#include "net/base/ip_endpoint.h"
|
| #include "net/base/mock_host_resolver.h"
|
| #include "net/base/net_errors.h"
|
| +#include "net/base/net_util.h"
|
| +#include "net/base/sys_addrinfo.h"
|
| #include "net/base/test_completion_callback.h"
|
| #include "net/socket/client_socket.h"
|
| #include "net/socket/client_socket_factory.h"
|
| @@ -29,9 +33,23 @@
|
| const int kMaxSocketsPerGroup = 6;
|
| const net::RequestPriority kDefaultPriority = LOW;
|
|
|
| +void SetIPv4Address(IPEndPoint* address) {
|
| + IPAddressNumber number;
|
| + CHECK(ParseIPLiteralToNumber("1.1.1.1", &number));
|
| + *address = IPEndPoint(number, 80);
|
| +}
|
| +
|
| +void SetIPv6Address(IPEndPoint* address) {
|
| + IPAddressNumber number;
|
| + CHECK(ParseIPLiteralToNumber("1:abcd::3:4:ff", &number));
|
| + *address = IPEndPoint(number, 80);
|
| +}
|
| +
|
| class MockClientSocket : public ClientSocket {
|
| public:
|
| - MockClientSocket() : connected_(false) {}
|
| + MockClientSocket(const AddressList& addrlist)
|
| + : connected_(false),
|
| + addrlist_(addrlist) {}
|
|
|
| // ClientSocket methods:
|
| virtual int Connect(CompletionCallback* callback) {
|
| @@ -51,7 +69,13 @@
|
| return ERR_UNEXPECTED;
|
| }
|
| virtual int GetLocalAddress(IPEndPoint* address) const {
|
| - return ERR_UNEXPECTED;
|
| + if (!connected_)
|
| + return ERR_SOCKET_NOT_CONNECTED;
|
| + if (addrlist_.head()->ai_family == AF_INET)
|
| + SetIPv4Address(address);
|
| + else
|
| + SetIPv6Address(address);
|
| + return OK;
|
| }
|
| virtual const BoundNetLog& NetLog() const {
|
| return net_log_;
|
| @@ -76,12 +100,13 @@
|
|
|
| private:
|
| bool connected_;
|
| + const AddressList addrlist_;
|
| BoundNetLog net_log_;
|
| };
|
|
|
| class MockFailingClientSocket : public ClientSocket {
|
| public:
|
| - MockFailingClientSocket() {}
|
| + MockFailingClientSocket(const AddressList& addrlist) : addrlist_(addrlist) {}
|
|
|
| // ClientSocket methods:
|
| virtual int Connect(CompletionCallback* callback) {
|
| @@ -125,6 +150,7 @@
|
| virtual bool SetSendBufferSize(int32 size) { return true; }
|
|
|
| private:
|
| + const AddressList addrlist_;
|
| BoundNetLog net_log_;
|
| };
|
|
|
| @@ -134,12 +160,17 @@
|
| // or fail.
|
| // |should_stall| indicates that this socket should never connect.
|
| // |delay_ms| is the delay, in milliseconds, before simulating a connect.
|
| - MockPendingClientSocket(bool should_connect, bool should_stall, int delay_ms)
|
| + MockPendingClientSocket(
|
| + const AddressList& addrlist,
|
| + bool should_connect,
|
| + bool should_stall,
|
| + int delay_ms)
|
| : method_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)),
|
| should_connect_(should_connect),
|
| should_stall_(should_stall),
|
| delay_ms_(delay_ms),
|
| - is_connected_(false) {}
|
| + is_connected_(false),
|
| + addrlist_(addrlist) {}
|
|
|
| // ClientSocket methods:
|
| virtual int Connect(CompletionCallback* callback) {
|
| @@ -162,7 +193,13 @@
|
| return ERR_UNEXPECTED;
|
| }
|
| virtual int GetLocalAddress(IPEndPoint* address) const {
|
| - return ERR_UNEXPECTED;
|
| + if (!is_connected_)
|
| + return ERR_SOCKET_NOT_CONNECTED;
|
| + if (addrlist_.head()->ai_family == AF_INET)
|
| + SetIPv4Address(address);
|
| + else
|
| + SetIPv6Address(address);
|
| + return OK;
|
| }
|
| virtual const BoundNetLog& NetLog() const {
|
| return net_log_;
|
| @@ -205,6 +242,7 @@
|
| bool should_stall_;
|
| int delay_ms_;
|
| bool is_connected_;
|
| + const AddressList addrlist_;
|
| BoundNetLog net_log_;
|
| };
|
|
|
| @@ -224,7 +262,8 @@
|
| MockClientSocketFactory()
|
| : allocation_count_(0), client_socket_type_(MOCK_CLIENT_SOCKET),
|
| client_socket_types_(NULL), client_socket_index_(0),
|
| - client_socket_index_max_(0) {}
|
| + client_socket_index_max_(0),
|
| + delay_ms_(ClientSocketPool::kMaxConnectRetryIntervalMs) {}
|
|
|
| virtual ClientSocket* CreateTransportClientSocket(
|
| const AddressList& addresses,
|
| @@ -240,21 +279,20 @@
|
|
|
| switch (type) {
|
| case MOCK_CLIENT_SOCKET:
|
| - return new MockClientSocket();
|
| + return new MockClientSocket(addresses);
|
| case MOCK_FAILING_CLIENT_SOCKET:
|
| - return new MockFailingClientSocket();
|
| + return new MockFailingClientSocket(addresses);
|
| case MOCK_PENDING_CLIENT_SOCKET:
|
| - return new MockPendingClientSocket(true, false, 0);
|
| + return new MockPendingClientSocket(addresses, true, false, 0);
|
| case MOCK_PENDING_FAILING_CLIENT_SOCKET:
|
| - return new MockPendingClientSocket(false, false, 0);
|
| + return new MockPendingClientSocket(addresses, false, false, 0);
|
| case MOCK_DELAYED_CLIENT_SOCKET:
|
| - return new MockPendingClientSocket(true, false,
|
| - ClientSocketPool::kMaxConnectRetryIntervalMs);
|
| + return new MockPendingClientSocket(addresses, true, false, delay_ms_);
|
| case MOCK_STALLED_CLIENT_SOCKET:
|
| - return new MockPendingClientSocket(true, true, 0);
|
| + return new MockPendingClientSocket(addresses, true, true, 0);
|
| default:
|
| NOTREACHED();
|
| - return new MockClientSocket();
|
| + return new MockClientSocket(addresses);
|
| }
|
| }
|
|
|
| @@ -289,12 +327,15 @@
|
| client_socket_index_max_ = num_types;
|
| }
|
|
|
| + void set_delay_ms(int delay_ms) { delay_ms_ = delay_ms; }
|
| +
|
| private:
|
| int allocation_count_;
|
| ClientSocketType client_socket_type_;
|
| ClientSocketType* client_socket_types_;
|
| int client_socket_index_;
|
| int client_socket_index_max_;
|
| + int delay_ms_;
|
| };
|
|
|
| class TransportClientSocketPoolTest : public testing::Test {
|
| @@ -355,6 +396,89 @@
|
| ClientSocketPoolTest test_base_;
|
| };
|
|
|
| +TEST(TransportConnectJobTest, MakeAddrListStartWithIPv4) {
|
| + IPAddressNumber ip_number;
|
| + ASSERT_TRUE(ParseIPLiteralToNumber("192.168.1.1", &ip_number));
|
| + AddressList addrlist_v4_1(ip_number, 80, false);
|
| + ASSERT_TRUE(ParseIPLiteralToNumber("192.168.1.2", &ip_number));
|
| + AddressList addrlist_v4_2(ip_number, 80, false);
|
| + ASSERT_TRUE(ParseIPLiteralToNumber("2001:4860:b006::64", &ip_number));
|
| + AddressList addrlist_v6_1(ip_number, 80, false);
|
| + ASSERT_TRUE(ParseIPLiteralToNumber("2001:4860:b006::66", &ip_number));
|
| + AddressList addrlist_v6_2(ip_number, 80, false);
|
| +
|
| + AddressList addrlist;
|
| + const struct addrinfo* ai;
|
| +
|
| + // Test 1: IPv4 only. Expect no change.
|
| + addrlist.Copy(addrlist_v4_1.head(), true);
|
| + addrlist.Append(addrlist_v4_2.head());
|
| + TransportConnectJob::MakeAddrListStartWithIPv4(&addrlist);
|
| + ai = addrlist.head();
|
| + EXPECT_EQ(AF_INET, ai->ai_family);
|
| + ai = ai->ai_next;
|
| + EXPECT_EQ(AF_INET, ai->ai_family);
|
| + EXPECT_TRUE(ai->ai_next == NULL);
|
| +
|
| + // Test 2: IPv6 only. Expect no change.
|
| + addrlist.Copy(addrlist_v6_1.head(), true);
|
| + addrlist.Append(addrlist_v6_2.head());
|
| + TransportConnectJob::MakeAddrListStartWithIPv4(&addrlist);
|
| + ai = addrlist.head();
|
| + EXPECT_EQ(AF_INET6, ai->ai_family);
|
| + ai = ai->ai_next;
|
| + EXPECT_EQ(AF_INET6, ai->ai_family);
|
| + EXPECT_TRUE(ai->ai_next == NULL);
|
| +
|
| + // Test 3: IPv4 then IPv6. Expect no change.
|
| + addrlist.Copy(addrlist_v4_1.head(), true);
|
| + addrlist.Append(addrlist_v4_2.head());
|
| + addrlist.Append(addrlist_v6_1.head());
|
| + addrlist.Append(addrlist_v6_2.head());
|
| + TransportConnectJob::MakeAddrListStartWithIPv4(&addrlist);
|
| + ai = addrlist.head();
|
| + EXPECT_EQ(AF_INET, ai->ai_family);
|
| + ai = ai->ai_next;
|
| + EXPECT_EQ(AF_INET, ai->ai_family);
|
| + ai = ai->ai_next;
|
| + EXPECT_EQ(AF_INET6, ai->ai_family);
|
| + ai = ai->ai_next;
|
| + EXPECT_EQ(AF_INET6, ai->ai_family);
|
| + EXPECT_TRUE(ai->ai_next == NULL);
|
| +
|
| + // Test 4: IPv6, IPv4, IPv6, IPv4. Expect first IPv6 moved to the end.
|
| + addrlist.Copy(addrlist_v6_1.head(), true);
|
| + addrlist.Append(addrlist_v4_1.head());
|
| + addrlist.Append(addrlist_v6_2.head());
|
| + addrlist.Append(addrlist_v4_2.head());
|
| + TransportConnectJob::MakeAddrListStartWithIPv4(&addrlist);
|
| + ai = addrlist.head();
|
| + EXPECT_EQ(AF_INET, ai->ai_family);
|
| + ai = ai->ai_next;
|
| + EXPECT_EQ(AF_INET6, ai->ai_family);
|
| + ai = ai->ai_next;
|
| + EXPECT_EQ(AF_INET, ai->ai_family);
|
| + ai = ai->ai_next;
|
| + EXPECT_EQ(AF_INET6, ai->ai_family);
|
| + EXPECT_TRUE(ai->ai_next == NULL);
|
| +
|
| + // Test 5: IPv6, IPv6, IPv4, IPv4. Expect first two IPv6's moved to the end.
|
| + addrlist.Copy(addrlist_v6_1.head(), true);
|
| + addrlist.Append(addrlist_v6_2.head());
|
| + addrlist.Append(addrlist_v4_1.head());
|
| + addrlist.Append(addrlist_v4_2.head());
|
| + TransportConnectJob::MakeAddrListStartWithIPv4(&addrlist);
|
| + ai = addrlist.head();
|
| + EXPECT_EQ(AF_INET, ai->ai_family);
|
| + ai = ai->ai_next;
|
| + EXPECT_EQ(AF_INET, ai->ai_family);
|
| + ai = ai->ai_next;
|
| + EXPECT_EQ(AF_INET6, ai->ai_family);
|
| + ai = ai->ai_next;
|
| + EXPECT_EQ(AF_INET6, ai->ai_family);
|
| + EXPECT_TRUE(ai->ai_next == NULL);
|
| +}
|
| +
|
| TEST_F(TransportClientSocketPoolTest, Basic) {
|
| TestCompletionCallback callback;
|
| ClientSocketHandle handle;
|
| @@ -787,7 +911,7 @@
|
|
|
| // Wait for the backup socket timer to fire.
|
| base::PlatformThread::Sleep(
|
| - ClientSocketPool::kMaxConnectRetryIntervalMs * 2);
|
| + ClientSocketPool::kMaxConnectRetryIntervalMs + 50);
|
|
|
| // Let the appropriate socket connect.
|
| MessageLoop::current()->RunAllPending();
|
| @@ -935,6 +1059,157 @@
|
| host_resolver_->set_synchronous_mode(false);
|
| }
|
|
|
| +// Test the case of the IPv6 address stalling, and falling back to the IPv4
|
| +// socket which finishes first.
|
| +TEST_F(TransportClientSocketPoolTest, IPv6FallbackSocketIPv4FinishesFirst) {
|
| + // Create a pool without backup jobs.
|
| + ClientSocketPoolBaseHelper::set_connect_backup_jobs_enabled(false);
|
| + TransportClientSocketPool pool(kMaxSockets,
|
| + kMaxSocketsPerGroup,
|
| + histograms_.get(),
|
| + host_resolver_.get(),
|
| + &client_socket_factory_,
|
| + NULL);
|
| +
|
| + MockClientSocketFactory::ClientSocketType case_types[] = {
|
| + // This is the IPv6 socket.
|
| + MockClientSocketFactory::MOCK_STALLED_CLIENT_SOCKET,
|
| + // This is the IPv4 socket.
|
| + MockClientSocketFactory::MOCK_PENDING_CLIENT_SOCKET
|
| + };
|
| +
|
| + client_socket_factory_.set_client_socket_types(case_types, 2);
|
| +
|
| + // Resolve an AddressList with a IPv6 address first and then a IPv4 address.
|
| + host_resolver_->rules()->AddIPLiteralRule(
|
| + "*", "2:abcd::3:4:ff,2.2.2.2", "");
|
| +
|
| + TestCompletionCallback callback;
|
| + ClientSocketHandle handle;
|
| + int rv = handle.Init("a", low_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());
|
| + IPEndPoint endpoint;
|
| + handle.socket()->GetLocalAddress(&endpoint);
|
| + EXPECT_EQ(kIPv4AddressSize, endpoint.address().size());
|
| + EXPECT_EQ(2, client_socket_factory_.allocation_count());
|
| +}
|
| +
|
| +// Test the case of the IPv6 address being slow, thus falling back to trying to
|
| +// connect to the IPv4 address, but having the connect to the IPv6 address
|
| +// finish first.
|
| +TEST_F(TransportClientSocketPoolTest, IPv6FallbackSocketIPv6FinishesFirst) {
|
| + // Create a pool without backup jobs.
|
| + ClientSocketPoolBaseHelper::set_connect_backup_jobs_enabled(false);
|
| + TransportClientSocketPool pool(kMaxSockets,
|
| + kMaxSocketsPerGroup,
|
| + histograms_.get(),
|
| + host_resolver_.get(),
|
| + &client_socket_factory_,
|
| + NULL);
|
| +
|
| + MockClientSocketFactory::ClientSocketType case_types[] = {
|
| + // This is the IPv6 socket.
|
| + MockClientSocketFactory::MOCK_DELAYED_CLIENT_SOCKET,
|
| + // This is the IPv4 socket.
|
| + MockClientSocketFactory::MOCK_STALLED_CLIENT_SOCKET
|
| + };
|
| +
|
| + client_socket_factory_.set_client_socket_types(case_types, 2);
|
| + client_socket_factory_.set_delay_ms(
|
| + TransportConnectJob::kIPv6FallbackTimerInMs + 50);
|
| +
|
| + // Resolve an AddressList with a IPv6 address first and then a IPv4 address.
|
| + host_resolver_->rules()->AddIPLiteralRule(
|
| + "*", "2:abcd::3:4:ff,2.2.2.2", "");
|
| +
|
| + TestCompletionCallback callback;
|
| + ClientSocketHandle handle;
|
| + int rv = handle.Init("a", low_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());
|
| + IPEndPoint endpoint;
|
| + handle.socket()->GetLocalAddress(&endpoint);
|
| + EXPECT_EQ(kIPv6AddressSize, endpoint.address().size());
|
| + EXPECT_EQ(2, client_socket_factory_.allocation_count());
|
| +}
|
| +
|
| +TEST_F(TransportClientSocketPoolTest, IPv6NoIPv4AddressesToFallbackTo) {
|
| + // Create a pool without backup jobs.
|
| + ClientSocketPoolBaseHelper::set_connect_backup_jobs_enabled(false);
|
| + TransportClientSocketPool pool(kMaxSockets,
|
| + kMaxSocketsPerGroup,
|
| + histograms_.get(),
|
| + host_resolver_.get(),
|
| + &client_socket_factory_,
|
| + NULL);
|
| +
|
| + client_socket_factory_.set_client_socket_type(
|
| + MockClientSocketFactory::MOCK_DELAYED_CLIENT_SOCKET);
|
| +
|
| + // Resolve an AddressList with only IPv6 addresses.
|
| + host_resolver_->rules()->AddIPLiteralRule(
|
| + "*", "2:abcd::3:4:ff,3:abcd::3:4:ff", "");
|
| +
|
| + TestCompletionCallback callback;
|
| + ClientSocketHandle handle;
|
| + int rv = handle.Init("a", low_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());
|
| + IPEndPoint endpoint;
|
| + handle.socket()->GetLocalAddress(&endpoint);
|
| + EXPECT_EQ(kIPv6AddressSize, endpoint.address().size());
|
| + EXPECT_EQ(1, client_socket_factory_.allocation_count());
|
| +}
|
| +
|
| +TEST_F(TransportClientSocketPoolTest, IPv4HasNoFallback) {
|
| + // Create a pool without backup jobs.
|
| + ClientSocketPoolBaseHelper::set_connect_backup_jobs_enabled(false);
|
| + TransportClientSocketPool pool(kMaxSockets,
|
| + kMaxSocketsPerGroup,
|
| + histograms_.get(),
|
| + host_resolver_.get(),
|
| + &client_socket_factory_,
|
| + NULL);
|
| +
|
| + client_socket_factory_.set_client_socket_type(
|
| + MockClientSocketFactory::MOCK_DELAYED_CLIENT_SOCKET);
|
| +
|
| + // Resolve an AddressList with only IPv4 addresses.
|
| + host_resolver_->rules()->AddIPLiteralRule(
|
| + "*", "1.1.1.1", "");
|
| +
|
| + TestCompletionCallback callback;
|
| + ClientSocketHandle handle;
|
| + int rv = handle.Init("a", low_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());
|
| + IPEndPoint endpoint;
|
| + handle.socket()->GetLocalAddress(&endpoint);
|
| + EXPECT_EQ(kIPv4AddressSize, endpoint.address().size());
|
| + EXPECT_EQ(1, client_socket_factory_.allocation_count());
|
| +}
|
| +
|
| } // namespace
|
|
|
| } // namespace net
|
|
|