blob: 1f4fe941c723a04945b7d6a068db734d75d8fef5 [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.
#ifndef NET_HTTP_HTTP_STREAM_POOL_TEST_UTIL_H_
#define NET_HTTP_HTTP_STREAM_POOL_TEST_UTIL_H_
#include <list>
#include <memory>
#include <optional>
#include <set>
#include <string>
#include <vector>
#include "base/memory/weak_ptr.h"
#include "base/test/test_future.h"
#include "net/base/completion_once_callback.h"
#include "net/base/net_errors.h"
#include "net/base/request_priority.h"
#include "net/dns/host_resolver.h"
#include "net/http/http_stream_key.h"
#include "net/http/http_stream_pool.h"
#include "net/http/http_stream_pool_job.h"
#include "net/socket/socket_test_util.h"
#include "net/socket/stream_socket.h"
#include "net/traffic_annotation/network_traffic_annotation.h"
#include "url/scheme_host_port.h"
namespace net {
class IOBuffer;
class SSLInfo;
// Provides fake service endpoint resolution results for testing.
class FakeServiceEndpointResolution {
public:
FakeServiceEndpointResolution();
~FakeServiceEndpointResolution();
FakeServiceEndpointResolution(const FakeServiceEndpointResolution&);
FakeServiceEndpointResolution& operator=(
const FakeServiceEndpointResolution&);
int start_result() const { return start_result_; }
const std::vector<ServiceEndpoint>& endpoints() const { return endpoints_; }
const std::set<std::string>& aliases() const { return aliases_; }
bool endpoints_crypto_ready() const { return endpoints_crypto_ready_; }
ResolveErrorInfo resolve_error_info() const { return resolve_error_info_; }
RequestPriority priority() const { return priority_; }
// These setters return `this&` to allow chaining.
FakeServiceEndpointResolution& CompleteStartSynchronously(int rv);
FakeServiceEndpointResolution& set_start_result(int start_result);
FakeServiceEndpointResolution& set_endpoints(
std::vector<ServiceEndpoint> endpoints);
FakeServiceEndpointResolution& add_endpoint(ServiceEndpoint endpoint);
FakeServiceEndpointResolution& set_aliases(std::set<std::string> aliases);
FakeServiceEndpointResolution& set_crypto_ready(bool endpoints_crypto_ready);
FakeServiceEndpointResolution& set_resolve_error_info(
ResolveErrorInfo resolve_error_info);
FakeServiceEndpointResolution& set_priority(RequestPriority priority);
private:
int start_result_ = ERR_IO_PENDING;
std::vector<ServiceEndpoint> endpoints_;
std::set<std::string> aliases_;
bool endpoints_crypto_ready_ = false;
ResolveErrorInfo resolve_error_info_;
RequestPriority priority_ = RequestPriority::IDLE;
};
// A fake ServiceEndpointRequest implementation that provides testing
// harnesses. See the comment of HostResolver::ServiceEndpointRequest for
// details.
class FakeServiceEndpointRequest : public HostResolver::ServiceEndpointRequest {
public:
FakeServiceEndpointRequest();
~FakeServiceEndpointRequest() override;
// Following setter methods return `this&` to allow chaining.
FakeServiceEndpointRequest& set_endpoints(
std::vector<ServiceEndpoint> endpoints);
FakeServiceEndpointRequest& add_endpoint(ServiceEndpoint endpoint);
FakeServiceEndpointRequest& set_aliases(std::set<std::string> aliases);
FakeServiceEndpointRequest& set_crypto_ready(bool endpoints_crypto_ready);
FakeServiceEndpointRequest& set_resolve_error_info(
ResolveErrorInfo resolve_error_info);
FakeServiceEndpointRequest& set_priority(RequestPriority priority);
// Make `this` complete synchronously when ServiceEndpointRequest::Start()
// is called.
FakeServiceEndpointRequest& CompleteStartSynchronously(int rv);
// Calls `delegate_->OnServiceEndpointsUpdated()`. Must not be used after
// calling CompleteStartSynchronously() or
// CallOnServiceEndpointRequestFinished()
FakeServiceEndpointRequest& CallOnServiceEndpointsUpdated();
// Calls `delegate_->OnServiceEndpointRequestFinished()`. Mut not be used
// after calling CompleteStartSynchronously().
FakeServiceEndpointRequest& CallOnServiceEndpointRequestFinished(int rv);
RequestPriority priority() const { return resolution_.priority(); }
// HostResolver::ServiceEndpointRequest methods:
int Start(Delegate* delegate) override;
base::span<const ServiceEndpoint> GetEndpointResults() override;
const std::set<std::string>& GetDnsAliasResults() override;
bool EndpointsCryptoReady() override;
ResolveErrorInfo GetResolveErrorInfo() override;
const HostCache::EntryStaleness* GetStaleInfo() const override;
bool IsStaleWhileRefresing() const override;
void ChangeRequestPriority(RequestPriority priority) override;
private:
friend class FakeServiceEndpointResolver;
raw_ptr<Delegate> delegate_;
FakeServiceEndpointResolution resolution_;
base::WeakPtrFactory<FakeServiceEndpointRequest> weak_ptr_factory_{this};
};
// A fake HostResolver that implements the ServiceEndpointRequest API using
// FakeServiceEndpointRequest.
class FakeServiceEndpointResolver : public HostResolver {
public:
FakeServiceEndpointResolver();
FakeServiceEndpointResolver(const FakeServiceEndpointResolver&) = delete;
FakeServiceEndpointResolver& operator=(const FakeServiceEndpointResolver&) =
delete;
~FakeServiceEndpointResolver() override;
// Creates a FakeServiceEndpointRequest that will be used for the next
// CreateServiceEndpointRequest() call. CreateServiceEndpointRequest()
// consumes the request. If you expect multiple CreateServiceEndpointRequest()
// calls, you need to do either:
// - Call this method as many times as you expect
// CreateServiceEndpointRequest()
// - Configure the default resolution result using
// ConfigureDefaultResolution().
base::WeakPtr<FakeServiceEndpointRequest> AddFakeRequest();
// Configures the default resolution result. It will be used when there are
// no requests in the request queue. Overrides the previous default result if
// existed.
FakeServiceEndpointResolution& ConfigureDefaultResolution();
// HostResolver methods:
void OnShutdown() override;
std::unique_ptr<ResolveHostRequest> CreateRequest(
url::SchemeHostPort host,
NetworkAnonymizationKey network_anonymization_key,
NetLogWithSource net_log,
std::optional<ResolveHostParameters> optional_parameters) override;
std::unique_ptr<ResolveHostRequest> CreateRequest(
const HostPortPair& host,
const NetworkAnonymizationKey& network_anonymization_key,
const NetLogWithSource& net_log,
const std::optional<ResolveHostParameters>& optional_parameters) override;
std::unique_ptr<ServiceEndpointRequest> CreateServiceEndpointRequest(
Host host,
NetworkAnonymizationKey network_anonymization_key,
NetLogWithSource net_log,
ResolveHostParameters parameters) override;
bool IsHappyEyeballsV3Enabled() const override;
private:
std::list<std::unique_ptr<FakeServiceEndpointRequest>> requests_;
std::optional<FakeServiceEndpointResolution> default_resolution_;
};
// A helper to build a ServiceEndpoint.
class ServiceEndpointBuilder {
public:
ServiceEndpointBuilder();
~ServiceEndpointBuilder();
ServiceEndpointBuilder& add_v4(std::string_view addr, uint16_t port = 80);
ServiceEndpointBuilder& add_v6(std::string_view addr, uint16_t port = 80);
ServiceEndpointBuilder& add_ip_endpoint(IPEndPoint ip_endpoint);
ServiceEndpointBuilder& set_alpns(std::vector<std::string> alpns);
ServiceEndpointBuilder& set_ech_config_list(
std::vector<uint8_t> ech_config_list);
ServiceEndpointBuilder& set_trust_anchor_ids(
std::vector<std::vector<uint8_t>> trust_anchor_ids);
ServiceEndpoint endpoint() const { return endpoint_; }
private:
ServiceEndpoint endpoint_;
};
class FakeStreamSocket : public MockClientSocket {
public:
static std::unique_ptr<FakeStreamSocket> CreateForSpdy();
FakeStreamSocket();
FakeStreamSocket(const FakeStreamSocket&) = delete;
FakeStreamSocket& operator=(const FakeStreamSocket&) = delete;
~FakeStreamSocket() override;
void set_is_connected(bool connected) { connected_ = connected; }
void set_is_idle(bool is_idle) { is_idle_ = is_idle; }
void set_was_ever_used(bool was_ever_used) { was_ever_used_ = was_ever_used; }
void set_peer_addr(IPEndPoint peer_addr) { peer_addr_ = peer_addr; }
void set_ssl_info(SSLInfo ssl_info) { ssl_info_ = std::move(ssl_info); }
// StreamSocket implementation:
int Read(IOBuffer* buf,
int buf_len,
CompletionOnceCallback callback) override;
int Write(IOBuffer* buf,
int buf_len,
CompletionOnceCallback callback,
const NetworkTrafficAnnotationTag& traffic_annotation) override;
int Connect(CompletionOnceCallback callback) override;
bool IsConnected() const override;
bool IsConnectedAndIdle() const override;
bool WasEverUsed() const override;
bool GetSSLInfo(SSLInfo* ssl_info) override;
// Simulates a situation where a connected socket disconnects after
// IsConnected() is called `count` times. Such situation could happen in the
// real world.
void DisconnectAfterIsConnectedCall(int count = 1);
private:
bool is_idle_ = true;
bool was_ever_used_ = false;
// When set to a positive value, every IsConnected() call decrements this
// counter. After this counter reached zero, IsConnected() uses
// `is_connected_override_`.
mutable int disconnect_after_is_connected_call_count_ = -1;
mutable std::optional<bool> is_connected_override_;
std::optional<SSLInfo> ssl_info_;
};
// A helper to create an HttpStreamKey.
class StreamKeyBuilder {
public:
explicit StreamKeyBuilder(std::string_view destination = "http://a.test")
: destination_(url::SchemeHostPort(GURL(destination))) {}
StreamKeyBuilder(const StreamKeyBuilder&) = delete;
StreamKeyBuilder& operator=(const StreamKeyBuilder&) = delete;
~StreamKeyBuilder() = default;
StreamKeyBuilder& from_key(const HttpStreamKey& key);
const url::SchemeHostPort& destination() const { return destination_; }
StreamKeyBuilder& set_destination(std::string_view destination) {
set_destination(url::SchemeHostPort(GURL(destination)));
return *this;
}
StreamKeyBuilder& set_destination(url::SchemeHostPort destination) {
destination_ = std::move(destination);
return *this;
}
StreamKeyBuilder& set_privacy_mode(PrivacyMode privacy_mode) {
privacy_mode_ = privacy_mode;
return *this;
}
HttpStreamKey Build() const;
private:
url::SchemeHostPort destination_;
PrivacyMode privacy_mode_ = PRIVACY_MODE_DISABLED;
SecureDnsPolicy secure_dns_policy_ = SecureDnsPolicy::kAllow;
bool disable_cert_network_fetches_ = true;
};
// An HttpStreamPool::Job::Delegate implementation for tests.
class TestJobDelegate : public HttpStreamPool::Job::Delegate {
public:
static inline constexpr std::string_view kDefaultDestination =
"https://www.example.org";
explicit TestJobDelegate(
std::optional<HttpStreamKey> stream_key = std::nullopt);
TestJobDelegate(const TestJobDelegate&) = delete;
TestJobDelegate& operator=(const TestJobDelegate&) = delete;
~TestJobDelegate() override;
TestJobDelegate& set_expected_protocol(NextProto expected_protocol) {
expected_protocol_ = expected_protocol;
return *this;
}
TestJobDelegate& set_quic_version(quic::ParsedQuicVersion quic_version) {
quic_version_ = quic_version;
return *this;
}
void CreateAndStartJob(HttpStreamPool& pool);
void CancelJob() { job_.reset(); }
int GetResult() { return result_future_.Get(); }
// HttpStreamPool::Job::Delegate implementations:
void OnStreamReady(HttpStreamPool::Job* job,
std::unique_ptr<HttpStream> stream,
NextProto negotiated_protocol) override;
RequestPriority priority() const override;
HttpStreamPool::RespectLimits respect_limits() const override;
const std::vector<SSLConfig::CertAndStatus>& allowed_bad_certs()
const override;
bool enable_ip_based_pooling_for_h2() const override;
bool enable_alternative_services() const override;
NextProtoSet allowed_alpns() const override;
const ProxyInfo& proxy_info() const override;
const NetLogWithSource& net_log() const override;
void OnStreamFailed(HttpStreamPool::Job* job,
int status,
const NetErrorDetails& net_error_details,
ResolveErrorInfo resolve_error_info) override;
void OnCertificateError(HttpStreamPool::Job* job,
int status,
const SSLInfo& ssl_info) override;
void OnNeedsClientAuth(HttpStreamPool::Job* job,
SSLCertRequestInfo* cert_info) override;
void OnPreconnectComplete(HttpStreamPool::Job* job, int status) override;
HttpStreamKey GetStreamKey() const { return key_builder_.Build(); }
NextProto negotiated_protocol() const { return negotiated_protocol_; }
private:
void SetResult(int result) { result_future_.SetValue(result); }
StreamKeyBuilder key_builder_;
NextProto expected_protocol_ = NextProto::kProtoUnknown;
quic::ParsedQuicVersion quic_version_ =
quic::ParsedQuicVersion::Unsupported();
std::vector<SSLConfig::CertAndStatus> allowed_bad_certs_;
ProxyInfo proxy_info_ = ProxyInfo::Direct();
NetLogWithSource net_log_;
std::unique_ptr<HttpStreamPool::Job> job_;
base::test::TestFuture<int> result_future_;
NextProto negotiated_protocol_ = NextProto::kProtoUnknown;
};
// Convert a ClientSocketPool::GroupId to an HttpStreamKey.
HttpStreamKey GroupIdToHttpStreamKey(const ClientSocketPool::GroupId& group_id);
// Wait for the `attempt_manager`'s completion.
void WaitForAttemptManagerComplete(
HttpStreamPool::AttemptManager* attempt_manager);
} // namespace net
#endif // NET_HTTP_HTTP_STREAM_POOL_TEST_UTIL_H_