blob: 279e61dfcc2dbad727d5e9e45058beb9f97c6488 [file] [log] [blame]
// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "net/http/http_stream_pool_group.h"
#include <memory>
#include "base/functional/callback_helpers.h"
#include "base/test/task_environment.h"
#include "net/base/address_list.h"
#include "net/base/completion_once_callback.h"
#include "net/base/ip_address.h"
#include "net/base/network_anonymization_key.h"
#include "net/base/network_change_notifier.h"
#include "net/base/privacy_mode.h"
#include "net/http/http_network_session.h"
#include "net/http/http_stream.h"
#include "net/http/http_stream_pool.h"
#include "net/http/http_stream_pool_test_util.h"
#include "net/log/net_log.h"
#include "net/socket/socket_test_util.h"
#include "net/socket/stream_socket.h"
#include "net/spdy/spdy_test_util_common.h"
#include "net/test/gtest_util.h"
#include "net/test/test_with_task_environment.h"
#include "url/scheme_host_port.h"
namespace net {
using test::IsOk;
using Group = HttpStreamPool::Group;
class HttpStreamPoolGroupTest : public TestWithTaskEnvironment {
public:
HttpStreamPoolGroupTest()
: TestWithTaskEnvironment(
base::test::TaskEnvironment::TimeSource::MOCK_TIME) {
http_network_session_ =
SpdySessionDependencies::SpdyCreateSession(&session_deps_);
InitializePool();
}
protected:
void InitializePool(bool cleanup_on_ip_address_change = true) {
pool_ = std::make_unique<HttpStreamPool>(http_network_session_.get(),
cleanup_on_ip_address_change);
}
Group& GetTestGroup() {
const HttpStreamKey key(url::SchemeHostPort("http", "a.test", 80),
PRIVACY_MODE_DISABLED, SocketTag(),
NetworkAnonymizationKey(), SecureDnsPolicy::kAllow,
/*disable_cert_network_fetches=*/false);
return pool().GetOrCreateGroupForTesting(key);
}
HttpStreamPool& pool() { return *pool_; }
private:
// For creating HttpNetworkSession.
SpdySessionDependencies session_deps_;
std::unique_ptr<HttpNetworkSession> http_network_session_;
std::unique_ptr<HttpStreamPool> pool_;
};
TEST_F(HttpStreamPoolGroupTest, CreateTextBasedStream) {
auto stream_socket = std::make_unique<FakeStreamSocket>();
Group& group = GetTestGroup();
std::unique_ptr<HttpStream> stream = group.CreateTextBasedStream(
std::move(stream_socket), LoadTimingInfo::ConnectTiming());
CHECK(stream);
ASSERT_EQ(group.ActiveStreamSocketCount(), 1u);
ASSERT_EQ(group.IdleStreamSocketCount(), 0u);
ASSERT_EQ(pool().TotalActiveStreamCount(), 1u);
}
TEST_F(HttpStreamPoolGroupTest, ReleaseStreamSocketUnused) {
auto stream_socket = std::make_unique<FakeStreamSocket>();
Group& group = GetTestGroup();
std::unique_ptr<HttpStream> stream = group.CreateTextBasedStream(
std::move(stream_socket), LoadTimingInfo::ConnectTiming());
CHECK(stream);
stream.reset();
ASSERT_EQ(group.ActiveStreamSocketCount(), 1u);
ASSERT_EQ(group.IdleStreamSocketCount(), 1u);
ASSERT_EQ(pool().TotalActiveStreamCount(), 1u);
FastForwardBy(Group::kUnusedIdleStreamSocketTimeout);
group.CleanupTimedoutIdleStreamSocketsForTesting();
ASSERT_EQ(group.ActiveStreamSocketCount(), 0u);
ASSERT_EQ(group.IdleStreamSocketCount(), 0u);
ASSERT_EQ(pool().TotalActiveStreamCount(), 0u);
}
TEST_F(HttpStreamPoolGroupTest, ReleaseStreamSocketUsed) {
auto stream_socket = std::make_unique<FakeStreamSocket>();
stream_socket->set_was_ever_used(true);
Group& group = GetTestGroup();
std::unique_ptr<HttpStream> stream = group.CreateTextBasedStream(
std::move(stream_socket), LoadTimingInfo::ConnectTiming());
CHECK(stream);
stream.reset();
ASSERT_EQ(group.ActiveStreamSocketCount(), 1u);
ASSERT_EQ(group.IdleStreamSocketCount(), 1u);
ASSERT_EQ(pool().TotalActiveStreamCount(), 1u);
static_assert(Group::kUnusedIdleStreamSocketTimeout <=
Group::kUsedIdleStreamSocketTimeout);
FastForwardBy(Group::kUnusedIdleStreamSocketTimeout);
group.CleanupTimedoutIdleStreamSocketsForTesting();
ASSERT_EQ(group.ActiveStreamSocketCount(), 1u);
ASSERT_EQ(group.IdleStreamSocketCount(), 1u);
ASSERT_EQ(pool().TotalActiveStreamCount(), 1u);
FastForwardBy(Group::kUsedIdleStreamSocketTimeout);
group.CleanupTimedoutIdleStreamSocketsForTesting();
ASSERT_EQ(group.ActiveStreamSocketCount(), 0u);
ASSERT_EQ(group.IdleStreamSocketCount(), 0u);
ASSERT_EQ(pool().TotalActiveStreamCount(), 0u);
}
TEST_F(HttpStreamPoolGroupTest, ReleaseStreamSocketNotIdle) {
auto stream_socket = std::make_unique<FakeStreamSocket>();
stream_socket->set_is_idle(false);
Group& group = GetTestGroup();
std::unique_ptr<HttpStream> stream = group.CreateTextBasedStream(
std::move(stream_socket), LoadTimingInfo::ConnectTiming());
CHECK(stream);
stream.reset();
ASSERT_EQ(group.ActiveStreamSocketCount(), 0u);
ASSERT_EQ(group.IdleStreamSocketCount(), 0u);
ASSERT_EQ(pool().TotalActiveStreamCount(), 0u);
}
TEST_F(HttpStreamPoolGroupTest, IdleSocketDisconnected) {
auto stream_socket = std::make_unique<FakeStreamSocket>();
FakeStreamSocket* raw_stream_socket = stream_socket.get();
Group& group = GetTestGroup();
std::unique_ptr<HttpStream> stream = group.CreateTextBasedStream(
std::move(stream_socket), LoadTimingInfo::ConnectTiming());
CHECK(stream);
stream.reset();
ASSERT_EQ(group.ActiveStreamSocketCount(), 1u);
ASSERT_EQ(group.IdleStreamSocketCount(), 1u);
ASSERT_EQ(pool().TotalActiveStreamCount(), 1u);
raw_stream_socket->set_is_connected(false);
group.CleanupTimedoutIdleStreamSocketsForTesting();
ASSERT_EQ(group.ActiveStreamSocketCount(), 0u);
ASSERT_EQ(group.IdleStreamSocketCount(), 0u);
}
TEST_F(HttpStreamPoolGroupTest, IdleSocketReceivedDataUnexpectedly) {
auto stream_socket = std::make_unique<FakeStreamSocket>();
FakeStreamSocket* raw_stream_socket = stream_socket.get();
Group& group = GetTestGroup();
std::unique_ptr<HttpStream> stream = group.CreateTextBasedStream(
std::move(stream_socket), LoadTimingInfo::ConnectTiming());
CHECK(stream);
stream.reset();
ASSERT_EQ(group.ActiveStreamSocketCount(), 1u);
ASSERT_EQ(group.IdleStreamSocketCount(), 1u);
ASSERT_EQ(pool().TotalActiveStreamCount(), 1u);
// Simulate the socket was used and not idle (received data).
raw_stream_socket->set_was_ever_used(true);
raw_stream_socket->set_is_idle(false);
group.CleanupTimedoutIdleStreamSocketsForTesting();
ASSERT_EQ(group.ActiveStreamSocketCount(), 0u);
ASSERT_EQ(group.IdleStreamSocketCount(), 0u);
}
TEST_F(HttpStreamPoolGroupTest, GetIdleStreamSocket) {
Group& group = GetTestGroup();
ASSERT_FALSE(group.GetIdleStreamSocket());
auto stream_socket = std::make_unique<FakeStreamSocket>();
group.AddIdleStreamSocket(std::move(stream_socket));
ASSERT_EQ(group.IdleStreamSocketCount(), 1u);
std::unique_ptr<StreamSocket> socket = group.GetIdleStreamSocket();
ASSERT_TRUE(socket);
ASSERT_EQ(group.IdleStreamSocketCount(), 0u);
}
TEST_F(HttpStreamPoolGroupTest, GetIdleStreamSocketPreferUsed) {
Group& group = GetTestGroup();
// Add 3 idle streams. the first and the third ones are marked as used.
auto stream_socket1 = std::make_unique<FakeStreamSocket>();
auto stream_socket2 = std::make_unique<FakeStreamSocket>();
auto stream_socket3 = std::make_unique<FakeStreamSocket>();
stream_socket1->set_was_ever_used(true);
stream_socket3->set_was_ever_used(true);
stream_socket1->set_peer_addr(IPEndPoint(IPAddress(192, 0, 2, 1), 80));
stream_socket2->set_peer_addr(IPEndPoint(IPAddress(192, 0, 2, 2), 80));
stream_socket3->set_peer_addr(IPEndPoint(IPAddress(192, 0, 2, 3), 80));
group.AddIdleStreamSocket(std::move(stream_socket1));
group.AddIdleStreamSocket(std::move(stream_socket2));
group.AddIdleStreamSocket(std::move(stream_socket3));
ASSERT_EQ(group.IdleStreamSocketCount(), 3u);
std::unique_ptr<StreamSocket> socket = group.GetIdleStreamSocket();
ASSERT_TRUE(socket);
ASSERT_EQ(group.IdleStreamSocketCount(), 2u);
IPEndPoint peer;
int rv = socket->GetPeerAddress(&peer);
EXPECT_THAT(rv, IsOk());
EXPECT_THAT(peer, IPEndPoint(IPAddress(192, 0, 2, 3), 80));
}
TEST_F(HttpStreamPoolGroupTest, GetIdleStreamSocketDisconnectedDuringIdle) {
Group& group = GetTestGroup();
ASSERT_FALSE(group.GetIdleStreamSocket());
auto stream_socket = std::make_unique<FakeStreamSocket>();
FakeStreamSocket* raw_stream_socket = stream_socket.get();
group.AddIdleStreamSocket(std::move(stream_socket));
ASSERT_EQ(group.IdleStreamSocketCount(), 1u);
raw_stream_socket->set_is_connected(false);
ASSERT_FALSE(group.GetIdleStreamSocket());
ASSERT_EQ(group.IdleStreamSocketCount(), 0u);
}
TEST_F(HttpStreamPoolGroupTest, GetIdleStreamSocketTimedout) {
Group& group = GetTestGroup();
auto stream_socket = std::make_unique<FakeStreamSocket>();
group.AddIdleStreamSocket(std::move(stream_socket));
ASSERT_EQ(group.IdleStreamSocketCount(), 1u);
FastForwardBy(HttpStreamPool::Group::kUnusedIdleStreamSocketTimeout);
ASSERT_FALSE(group.GetIdleStreamSocket());
ASSERT_EQ(group.IdleStreamSocketCount(), 0u);
}
TEST_F(HttpStreamPoolGroupTest, IPAddressChangeCleanupIdleSocket) {
auto stream_socket = std::make_unique<FakeStreamSocket>();
Group& group = GetTestGroup();
std::unique_ptr<HttpStream> stream = group.CreateTextBasedStream(
std::move(stream_socket), LoadTimingInfo::ConnectTiming());
CHECK(stream);
stream.reset();
ASSERT_EQ(group.ActiveStreamSocketCount(), 1u);
ASSERT_EQ(group.IdleStreamSocketCount(), 1u);
ASSERT_EQ(pool().TotalActiveStreamCount(), 1u);
NetworkChangeNotifier::NotifyObserversOfIPAddressChangeForTests();
RunUntilIdle();
group.CleanupTimedoutIdleStreamSocketsForTesting();
ASSERT_EQ(group.ActiveStreamSocketCount(), 0u);
ASSERT_EQ(group.IdleStreamSocketCount(), 0u);
}
TEST_F(HttpStreamPoolGroupTest, IPAddressChangeReleaseStreamSocket) {
auto stream_socket = std::make_unique<FakeStreamSocket>();
Group& group = GetTestGroup();
std::unique_ptr<HttpStream> stream = group.CreateTextBasedStream(
std::move(stream_socket), LoadTimingInfo::ConnectTiming());
CHECK(stream);
ASSERT_EQ(group.ActiveStreamSocketCount(), 1u);
ASSERT_EQ(group.IdleStreamSocketCount(), 0u);
ASSERT_EQ(pool().TotalActiveStreamCount(), 1u);
NetworkChangeNotifier::NotifyObserversOfIPAddressChangeForTests();
RunUntilIdle();
stream.reset();
group.CleanupTimedoutIdleStreamSocketsForTesting();
ASSERT_EQ(group.ActiveStreamSocketCount(), 0u);
ASSERT_EQ(group.IdleStreamSocketCount(), 0u);
ASSERT_EQ(pool().TotalActiveStreamCount(), 0u);
}
TEST_F(HttpStreamPoolGroupTest, IPAddressChangeIgnored) {
InitializePool(/*cleanup_on_ip_address_change=*/false);
auto stream_socket = std::make_unique<FakeStreamSocket>();
Group& group = GetTestGroup();
std::unique_ptr<HttpStream> stream = group.CreateTextBasedStream(
std::move(stream_socket), LoadTimingInfo::ConnectTiming());
CHECK(stream);
ASSERT_EQ(group.ActiveStreamSocketCount(), 1u);
ASSERT_EQ(group.IdleStreamSocketCount(), 0u);
ASSERT_EQ(pool().TotalActiveStreamCount(), 1u);
NetworkChangeNotifier::NotifyObserversOfIPAddressChangeForTests();
RunUntilIdle();
stream.reset();
group.CleanupTimedoutIdleStreamSocketsForTesting();
ASSERT_EQ(group.ActiveStreamSocketCount(), 1u);
ASSERT_EQ(group.IdleStreamSocketCount(), 1u);
ASSERT_EQ(pool().TotalActiveStreamCount(), 1u);
}
} // namespace net