blob: f39a1367f8b66974d0f114b096b0fe781d1de7fa [file] [log] [blame]
// Copyright 2017 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/quic/quic_connectivity_probing_manager.h"
#include "base/stl_util.h"
#include "base/test/test_mock_time_task_runner.h"
#include "net/log/test_net_log.h"
#include "net/socket/socket_test_util.h"
#include "net/test/gtest_util.h"
#include "net/third_party/quic/test_tools/mock_clock.h"
#include "net/third_party/quic/test_tools/quic_test_utils.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
using testing::_;
using testing::Return;
namespace net {
namespace test {
namespace {
const NetworkChangeNotifier::NetworkHandle testNetworkHandle = 1;
const IPEndPoint kIpEndPoint =
IPEndPoint(IPAddress::IPv4AllZeros(), quic::test::kTestPort);
const quic::QuicSocketAddress testPeerAddress =
quic::QuicSocketAddress(quic::QuicSocketAddressImpl(kIpEndPoint));
const IPEndPoint newIpEndPoint =
IPEndPoint(IPAddress::IPv4AllZeros(), quic::test::kTestPort + 1);
const quic::QuicSocketAddress newPeerAddress =
quic::QuicSocketAddress(quic::QuicSocketAddressImpl(newIpEndPoint));
} // anonymous namespace
class MockQuicChromiumClientSession
: public QuicConnectivityProbingManager::Delegate,
public QuicChromiumPacketReader::Visitor {
public:
MockQuicChromiumClientSession()
: probed_network_(NetworkChangeNotifier::kInvalidNetworkHandle) {}
~MockQuicChromiumClientSession() override {}
// QuicChromiumPacketReader::Visitor interface.
MOCK_METHOD2(OnReadError,
void(int result, const DatagramClientSocket* socket));
MOCK_METHOD3(OnPacket,
bool(const quic::QuicReceivedPacket& packet,
const quic::QuicSocketAddress& local_address,
const quic::QuicSocketAddress& peer_address));
MOCK_METHOD2(OnProbeFailed,
void(NetworkChangeNotifier::NetworkHandle network,
const quic::QuicSocketAddress& peer_address));
MOCK_METHOD2(OnSendConnectivityProbingPacket,
bool(QuicChromiumPacketWriter* writer,
const quic::QuicSocketAddress& peer_address));
void OnProbeSucceeded(
NetworkChangeNotifier::NetworkHandle network,
const quic::QuicSocketAddress& peer_address,
const quic::QuicSocketAddress& self_address,
std::unique_ptr<DatagramClientSocket> socket,
std::unique_ptr<QuicChromiumPacketWriter> writer,
std::unique_ptr<QuicChromiumPacketReader> reader) override {
probed_network_ = network;
probed_peer_address_ = peer_address;
}
NetworkChangeNotifier::NetworkHandle probed_network() const {
return probed_network_;
}
quic::QuicSocketAddress probed_peer_address() const {
return probed_peer_address_;
}
private:
NetworkChangeNotifier::NetworkHandle probed_network_;
quic::QuicSocketAddress probed_peer_address_;
DISALLOW_COPY_AND_ASSIGN(MockQuicChromiumClientSession);
};
class QuicConnectivityProbingManagerTest : public ::testing::Test {
public:
QuicConnectivityProbingManagerTest()
: test_task_runner_(new base::TestMockTimeTaskRunner()),
test_task_runner_context_(test_task_runner_),
probing_manager_(&session_, test_task_runner_.get()),
default_read_(new MockRead(SYNCHRONOUS, ERR_IO_PENDING, 0)),
socket_data_(
new SequencedSocketData(base::make_span(default_read_.get(), 1),
base::span<MockWrite>())) {
socket_factory_.AddSocketDataProvider(socket_data_.get());
// Create a connected socket for probing.
socket_ = socket_factory_.CreateDatagramClientSocket(
DatagramSocket::DEFAULT_BIND, &net_log_, NetLogSource());
EXPECT_THAT(socket_->Connect(kIpEndPoint), IsOk());
IPEndPoint self_address;
socket_->GetLocalAddress(&self_address);
self_address_ =
quic::QuicSocketAddress(quic::QuicSocketAddressImpl(self_address));
// Create packet writer and reader for probing.
writer_.reset(
new QuicChromiumPacketWriter(socket_.get(), test_task_runner_.get()));
reader_.reset(new QuicChromiumPacketReader(
socket_.get(), &clock_, &session_, kQuicYieldAfterPacketsRead,
quic::QuicTime::Delta::FromMilliseconds(
kQuicYieldAfterDurationMilliseconds),
bound_test_net_log_.bound()));
}
protected:
// All tests will run inside the scope of |test_task_runner_|.
scoped_refptr<base::TestMockTimeTaskRunner> test_task_runner_;
base::TestMockTimeTaskRunner::ScopedContext test_task_runner_context_;
MockQuicChromiumClientSession session_;
QuicConnectivityProbingManager probing_manager_;
std::unique_ptr<MockRead> default_read_;
std::unique_ptr<SequencedSocketData> socket_data_;
std::unique_ptr<DatagramClientSocket> socket_;
std::unique_ptr<QuicChromiumPacketWriter> writer_;
std::unique_ptr<QuicChromiumPacketReader> reader_;
quic::QuicSocketAddress self_address_;
quic::MockClock clock_;
MockClientSocketFactory socket_factory_;
TestNetLog net_log_;
BoundTestNetLog bound_test_net_log_;
DISALLOW_COPY_AND_ASSIGN(QuicConnectivityProbingManagerTest);
};
TEST_F(QuicConnectivityProbingManagerTest, ReceiveProbingResponseOnSamePath) {
int initial_timeout_ms = 100;
EXPECT_CALL(session_, OnSendConnectivityProbingPacket(_, testPeerAddress))
.WillOnce(Return(true));
probing_manager_.StartProbing(
testNetworkHandle, testPeerAddress, std::move(socket_),
std::move(writer_), std::move(reader_),
base::TimeDelta::FromMilliseconds(initial_timeout_ms),
bound_test_net_log_.bound());
EXPECT_EQ(1u, test_task_runner_->GetPendingTaskCount());
// Fast forward initial_timeout_ms, timeout the first connectivity probing
// packet, introduce another probing packet to sent out with timeout set to
// 2 * initial_timeout_ms.
EXPECT_CALL(session_, OnSendConnectivityProbingPacket(_, testPeerAddress))
.WillOnce(Return(true));
test_task_runner_->FastForwardBy(
base::TimeDelta::FromMilliseconds(initial_timeout_ms));
EXPECT_EQ(1u, test_task_runner_->GetPendingTaskCount());
// Fast forward initial_timeout_ms, should be no-op.
EXPECT_CALL(session_, OnSendConnectivityProbingPacket(_, testPeerAddress))
.Times(0);
test_task_runner_->FastForwardBy(
base::TimeDelta::FromMilliseconds(initial_timeout_ms));
EXPECT_EQ(1u, test_task_runner_->GetPendingTaskCount());
// Notify the manager a connectivity probing packet is received from
// testPeerAddress to |self_address_|, manager should decalre probing as
// successful, notify delegate and will no longer send connectivity probing
// packet for this probing.
EXPECT_CALL(session_, OnSendConnectivityProbingPacket(_, testPeerAddress))
.Times(0);
probing_manager_.OnConnectivityProbingReceived(self_address_,
testPeerAddress);
EXPECT_EQ(session_.probed_network(), testNetworkHandle);
EXPECT_EQ(1u, test_task_runner_->GetPendingTaskCount());
// Verify there's nothing to send.
EXPECT_CALL(session_, OnSendConnectivityProbingPacket(_, testPeerAddress))
.Times(0);
test_task_runner_->FastForwardBy(
base::TimeDelta::FromMilliseconds(initial_timeout_ms));
EXPECT_EQ(0u, test_task_runner_->GetPendingTaskCount());
}
TEST_F(QuicConnectivityProbingManagerTest,
ReceiveProbingResponseOnDifferentPath) {
int initial_timeout_ms = 100;
EXPECT_CALL(session_, OnSendConnectivityProbingPacket(_, testPeerAddress))
.WillOnce(Return(true));
probing_manager_.StartProbing(
testNetworkHandle, testPeerAddress, std::move(socket_),
std::move(writer_), std::move(reader_),
base::TimeDelta::FromMilliseconds(initial_timeout_ms),
bound_test_net_log_.bound());
EXPECT_EQ(1u, test_task_runner_->GetPendingTaskCount());
// Fast forward initial_timeout_ms, timeout the first connectivity probing
// packet, introduce another probing packet to sent out with timeout set to
// 2 * initial_timeout_ms.
EXPECT_CALL(session_, OnSendConnectivityProbingPacket(_, testPeerAddress))
.WillOnce(Return(true));
test_task_runner_->FastForwardBy(
base::TimeDelta::FromMilliseconds(initial_timeout_ms));
EXPECT_EQ(1u, test_task_runner_->GetPendingTaskCount());
// Fast forward initial_timeout_ms, should be no-op.
EXPECT_CALL(session_, OnSendConnectivityProbingPacket(_, testPeerAddress))
.Times(0);
test_task_runner_->FastForwardBy(
base::TimeDelta::FromMilliseconds(initial_timeout_ms));
EXPECT_EQ(1u, test_task_runner_->GetPendingTaskCount());
// Notify the manager a connectivity probing packet is received from
// testPeerAddress to a different self address, manager should ignore the
// probing response and continue waiting.
EXPECT_CALL(session_, OnSendConnectivityProbingPacket(_, testPeerAddress))
.Times(0);
probing_manager_.OnConnectivityProbingReceived(quic::QuicSocketAddress(),
testPeerAddress);
EXPECT_NE(session_.probed_network(), testNetworkHandle);
EXPECT_EQ(1u, test_task_runner_->GetPendingTaskCount());
// Fast forward another initial_timeout_ms, another probing packet will be
// sent.
EXPECT_CALL(session_, OnSendConnectivityProbingPacket(_, testPeerAddress))
.WillOnce(Return(true));
test_task_runner_->FastForwardBy(
base::TimeDelta::FromMilliseconds(initial_timeout_ms));
EXPECT_EQ(1u, test_task_runner_->GetPendingTaskCount());
// Finally receive the probing response on the same path.
EXPECT_CALL(session_, OnSendConnectivityProbingPacket(_, testPeerAddress))
.Times(0);
probing_manager_.OnConnectivityProbingReceived(self_address_,
testPeerAddress);
EXPECT_EQ(session_.probed_network(), testNetworkHandle);
EXPECT_EQ(1u, test_task_runner_->GetPendingTaskCount());
// Verify there's nothing to send.
EXPECT_CALL(session_, OnSendConnectivityProbingPacket(_, testPeerAddress))
.Times(0);
test_task_runner_->RunUntilIdle();
}
TEST_F(QuicConnectivityProbingManagerTest, RetryProbingWithExponentailBackoff) {
int initial_timeout_ms = 100;
EXPECT_CALL(session_, OnSendConnectivityProbingPacket(_, testPeerAddress))
.WillOnce(Return(true));
probing_manager_.StartProbing(
testNetworkHandle, testPeerAddress, std::move(socket_),
std::move(writer_), std::move(reader_),
base::TimeDelta::FromMilliseconds(initial_timeout_ms),
bound_test_net_log_.bound());
EXPECT_EQ(1u, test_task_runner_->GetPendingTaskCount());
// For expential backoff, this will try to resend: 100ms, 200ms, 400ms, 800ms,
// 1600ms.
for (int retry_count = 0; retry_count < 4; retry_count++) {
EXPECT_CALL(session_, OnSendConnectivityProbingPacket(_, testPeerAddress))
.WillOnce(Return(true));
int timeout_ms = (1 << retry_count) * initial_timeout_ms;
test_task_runner_->FastForwardBy(
base::TimeDelta::FromMilliseconds(timeout_ms));
EXPECT_EQ(1u, test_task_runner_->GetPendingTaskCount());
}
// Move forward another 1600ms, expect probing manager will no longer send any
// connectivity probing packet but declare probing as failed .
EXPECT_CALL(session_, OnProbeFailed(testNetworkHandle, testPeerAddress))
.Times(1);
int timeout_ms = (1 << 4) * initial_timeout_ms;
test_task_runner_->FastForwardBy(
base::TimeDelta::FromMilliseconds(timeout_ms));
EXPECT_EQ(0u, test_task_runner_->GetPendingTaskCount());
}
TEST_F(QuicConnectivityProbingManagerTest, CancelProbing) {
int initial_timeout_ms = 100;
EXPECT_CALL(session_, OnSendConnectivityProbingPacket(_, testPeerAddress))
.WillOnce(Return(true));
probing_manager_.StartProbing(
testNetworkHandle, testPeerAddress, std::move(socket_),
std::move(writer_), std::move(reader_),
base::TimeDelta::FromMilliseconds(initial_timeout_ms),
bound_test_net_log_.bound());
EXPECT_EQ(1u, test_task_runner_->GetPendingTaskCount());
// Fast forward initial_timeout_ms, timeout the first connectivity probing
// packet, introduce another probing packet to sent out with timeout set to
// 2 * initial_timeout_ms.
EXPECT_CALL(session_, OnSendConnectivityProbingPacket(_, testPeerAddress))
.WillOnce(Return(true));
test_task_runner_->FastForwardBy(
base::TimeDelta::FromMilliseconds(initial_timeout_ms));
EXPECT_EQ(1u, test_task_runner_->GetPendingTaskCount());
// Fast forward initial_timeout_ms, should be no-op.
EXPECT_CALL(session_, OnSendConnectivityProbingPacket(_, testPeerAddress))
.Times(0);
test_task_runner_->FastForwardBy(
base::TimeDelta::FromMilliseconds(initial_timeout_ms));
EXPECT_EQ(1u, test_task_runner_->GetPendingTaskCount());
// Request cancel probing, manager will no longer send connectivity probes.
EXPECT_CALL(session_, OnSendConnectivityProbingPacket(_, _)).Times(0);
EXPECT_CALL(session_, OnProbeFailed(_, _)).Times(0);
probing_manager_.CancelProbing(testNetworkHandle, testPeerAddress);
EXPECT_FALSE(
probing_manager_.IsUnderProbing(testNetworkHandle, testPeerAddress));
test_task_runner_->RunUntilIdle();
}
TEST_F(QuicConnectivityProbingManagerTest, DoNotCancelProbing) {
int initial_timeout_ms = 100;
EXPECT_CALL(session_, OnSendConnectivityProbingPacket(_, testPeerAddress))
.WillOnce(Return(true));
// Start probing |testPeerAddress| on |testNetworkHandle|.
probing_manager_.StartProbing(
testNetworkHandle, testPeerAddress, std::move(socket_),
std::move(writer_), std::move(reader_),
base::TimeDelta::FromMilliseconds(initial_timeout_ms),
bound_test_net_log_.bound());
EXPECT_EQ(1u, test_task_runner_->GetPendingTaskCount());
// Request cancel probing for |newPeerAddress| on |testNetworkHandle| doesn't
// affect the exisiting probing.
probing_manager_.CancelProbing(testNetworkHandle, newPeerAddress);
EXPECT_TRUE(
probing_manager_.IsUnderProbing(testNetworkHandle, testPeerAddress));
EXPECT_EQ(1u, test_task_runner_->GetPendingTaskCount());
for (int retry_count = 0; retry_count < 4; retry_count++) {
EXPECT_CALL(session_, OnSendConnectivityProbingPacket(_, testPeerAddress))
.WillOnce(Return(true));
int timeout_ms = (1 << retry_count) * initial_timeout_ms;
test_task_runner_->FastForwardBy(
base::TimeDelta::FromMilliseconds(timeout_ms));
EXPECT_EQ(1u, test_task_runner_->GetPendingTaskCount());
}
EXPECT_CALL(session_, OnProbeFailed(testNetworkHandle, testPeerAddress))
.Times(1);
EXPECT_CALL(session_, OnSendConnectivityProbingPacket(_, _)).Times(0);
int timeout_ms = (1 << 4) * initial_timeout_ms;
test_task_runner_->FastForwardBy(
base::TimeDelta::FromMilliseconds(timeout_ms));
EXPECT_EQ(0u, test_task_runner_->GetPendingTaskCount());
}
TEST_F(QuicConnectivityProbingManagerTest, ProbingWriterError) {
int initial_timeout_ms = 100;
EXPECT_CALL(session_, OnSendConnectivityProbingPacket(_, testPeerAddress))
.WillOnce(Return(true));
QuicChromiumPacketWriter* writer_ptr = writer_.get();
probing_manager_.StartProbing(
testNetworkHandle, testPeerAddress, std::move(socket_),
std::move(writer_), std::move(reader_),
base::TimeDelta::FromMilliseconds(initial_timeout_ms),
bound_test_net_log_.bound());
EXPECT_EQ(1u, test_task_runner_->GetPendingTaskCount());
// Fast forward initial_timeout_ms, timeout the first connectivity probing
// packet, introduce another probing packet to sent out with timeout set to
// 2 * initial_timeout_ms.
EXPECT_CALL(session_, OnSendConnectivityProbingPacket(_, testPeerAddress))
.WillOnce(Return(true));
test_task_runner_->FastForwardBy(
base::TimeDelta::FromMilliseconds(initial_timeout_ms));
EXPECT_EQ(1u, test_task_runner_->GetPendingTaskCount());
// Fast forward initial_timeout_ms, should be no-op.
EXPECT_CALL(session_, OnSendConnectivityProbingPacket(_, testPeerAddress))
.Times(0);
test_task_runner_->FastForwardBy(
base::TimeDelta::FromMilliseconds(initial_timeout_ms));
EXPECT_EQ(1u, test_task_runner_->GetPendingTaskCount());
// Probing packet writer received an write error, notifies manager to handle
// write error. Manager will notify session of the probe failure, cancel
// probing to prevent future connectivity probing packet to be sent.
EXPECT_CALL(session_, OnSendConnectivityProbingPacket(_, _)).Times(0);
EXPECT_CALL(session_, OnProbeFailed(testNetworkHandle, testPeerAddress))
.Times(1);
writer_ptr->OnWriteComplete(ERR_CONNECTION_CLOSED);
test_task_runner_->FastForwardBy(
base::TimeDelta::FromMilliseconds(initial_timeout_ms));
EXPECT_EQ(0u, test_task_runner_->GetPendingTaskCount());
}
TEST_F(QuicConnectivityProbingManagerTest,
ProbeServerPreferredAddressSucceeded) {
int initial_timeout_ms = 100;
EXPECT_CALL(session_, OnSendConnectivityProbingPacket(_, testPeerAddress))
.WillOnce(Return(true));
// A probe for server preferred address is usually initiated with an
// invalid network handle passed in.
probing_manager_.StartProbing(
NetworkChangeNotifier::kInvalidNetworkHandle, testPeerAddress,
std::move(socket_), std::move(writer_), std::move(reader_),
base::TimeDelta::FromMilliseconds(initial_timeout_ms),
bound_test_net_log_.bound());
EXPECT_EQ(1u, test_task_runner_->GetPendingTaskCount());
EXPECT_CALL(session_, OnSendConnectivityProbingPacket(_, testPeerAddress))
.WillOnce(Return(true));
test_task_runner_->FastForwardBy(
base::TimeDelta::FromMilliseconds(initial_timeout_ms));
EXPECT_EQ(1u, test_task_runner_->GetPendingTaskCount());
// Fast forward initial_timeout_ms, should be no-op.
EXPECT_CALL(session_, OnSendConnectivityProbingPacket(_, testPeerAddress))
.Times(0);
test_task_runner_->FastForwardBy(
base::TimeDelta::FromMilliseconds(initial_timeout_ms));
EXPECT_EQ(1u, test_task_runner_->GetPendingTaskCount());
// Notify the manager a connectivity probing packet is received from
// testPeerAddress to |self_address_|, manager should decalre probing as
// successful, notify delegate and will no longer send connectivity probes.
EXPECT_CALL(session_, OnSendConnectivityProbingPacket(_, testPeerAddress))
.Times(0);
probing_manager_.OnConnectivityProbingReceived(self_address_,
testPeerAddress);
// Verify that session marked <kInvalidNetworkHandle, testPeerAddress> as
// successfully probed.
EXPECT_EQ(session_.probed_network(),
NetworkChangeNotifier::kInvalidNetworkHandle);
EXPECT_EQ(session_.probed_peer_address(), testPeerAddress);
EXPECT_EQ(1u, test_task_runner_->GetPendingTaskCount());
EXPECT_CALL(session_, OnSendConnectivityProbingPacket(_, testPeerAddress))
.Times(0);
test_task_runner_->FastForwardBy(
base::TimeDelta::FromMilliseconds(initial_timeout_ms));
EXPECT_EQ(0u, test_task_runner_->GetPendingTaskCount());
}
TEST_F(QuicConnectivityProbingManagerTest, ProbeServerPreferredAddressFailed) {
int initial_timeout_ms = 100;
EXPECT_CALL(session_, OnSendConnectivityProbingPacket(_, testPeerAddress))
.WillOnce(Return(true));
QuicChromiumPacketWriter* writer_ptr = writer_.get();
// A probe for server preferred address is usually initiated with an
// invalid network handle passed in.
probing_manager_.StartProbing(
NetworkChangeNotifier::kInvalidNetworkHandle, testPeerAddress,
std::move(socket_), std::move(writer_), std::move(reader_),
base::TimeDelta::FromMilliseconds(initial_timeout_ms),
bound_test_net_log_.bound());
EXPECT_EQ(1u, test_task_runner_->GetPendingTaskCount());
EXPECT_CALL(session_, OnSendConnectivityProbingPacket(_, testPeerAddress))
.WillOnce(Return(true));
test_task_runner_->FastForwardBy(
base::TimeDelta::FromMilliseconds(initial_timeout_ms));
EXPECT_EQ(1u, test_task_runner_->GetPendingTaskCount());
// Fast forward initial_timeout_ms, should be no-op.
EXPECT_CALL(session_, OnSendConnectivityProbingPacket(_, testPeerAddress))
.Times(0);
test_task_runner_->FastForwardBy(
base::TimeDelta::FromMilliseconds(initial_timeout_ms));
EXPECT_EQ(1u, test_task_runner_->GetPendingTaskCount());
// Probing packet writer received an write error, notifies manager to handle
// write error. Manager will notify session of the probe failure, cancel
// probing to prevent future connectivity probing packet to be sent.
EXPECT_CALL(session_, OnSendConnectivityProbingPacket(_, _)).Times(0);
EXPECT_CALL(session_,
OnProbeFailed(NetworkChangeNotifier::kInvalidNetworkHandle,
testPeerAddress))
.Times(1);
writer_ptr->OnWriteComplete(ERR_CONNECTION_CLOSED);
test_task_runner_->FastForwardBy(
base::TimeDelta::FromMilliseconds(initial_timeout_ms));
EXPECT_EQ(0u, test_task_runner_->GetPendingTaskCount());
}
} // namespace test
} // namespace net