123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265 |
- // Copyright 2021 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/dedicated_web_transport_http3_client.h"
- #include <memory>
- #include "base/memory/raw_ptr.h"
- #include "base/strings/strcat.h"
- #include "base/threading/thread_task_runner_handle.h"
- #include "build/build_config.h"
- #include "net/base/schemeful_site.h"
- #include "net/cert/mock_cert_verifier.h"
- #include "net/dns/mock_host_resolver.h"
- #include "net/proxy_resolution/configured_proxy_resolution_service.h"
- #include "net/quic/crypto/proof_source_chromium.h"
- #include "net/test/test_data_directory.h"
- #include "net/test/test_with_task_environment.h"
- #include "net/third_party/quiche/src/quiche/quic/test_tools/crypto_test_utils.h"
- #include "net/third_party/quiche/src/quiche/quic/test_tools/quic_test_backend.h"
- #include "net/tools/quic/quic_simple_server.h"
- #include "net/tools/quic/quic_simple_server_socket.h"
- #include "net/url_request/url_request_context.h"
- #include "net/url_request/url_request_context_builder.h"
- #include "testing/gmock/include/gmock/gmock.h"
- #include "testing/gtest/include/gtest/gtest.h"
- #include "url/gurl.h"
- #include "url/origin.h"
- namespace net::test {
- namespace {
- using ::quic::test::MemSliceFromString;
- using ::testing::_;
- using ::testing::DoAll;
- using ::testing::Optional;
- using ::testing::SaveArg;
- class MockVisitor : public WebTransportClientVisitor {
- public:
- MOCK_METHOD(void,
- OnConnected,
- (scoped_refptr<HttpResponseHeaders>),
- (override));
- MOCK_METHOD(void, OnConnectionFailed, (const WebTransportError&), (override));
- MOCK_METHOD(void,
- OnClosed,
- (const absl::optional<WebTransportCloseInfo>&),
- (override));
- MOCK_METHOD(void, OnError, (const WebTransportError&), (override));
- MOCK_METHOD0(OnIncomingBidirectionalStreamAvailable, void());
- MOCK_METHOD0(OnIncomingUnidirectionalStreamAvailable, void());
- MOCK_METHOD1(OnDatagramReceived, void(base::StringPiece));
- MOCK_METHOD0(OnCanCreateNewOutgoingBidirectionalStream, void());
- MOCK_METHOD0(OnCanCreateNewOutgoingUnidirectionalStream, void());
- MOCK_METHOD1(OnDatagramProcessed, void(absl::optional<quic::MessageStatus>));
- };
- // A clock that only mocks out WallNow(), but uses real Now() and
- // ApproximateNow(). Useful for certificate verification.
- class TestWallClock : public quic::QuicClock {
- public:
- quic::QuicTime Now() const override {
- return quic::QuicChromiumClock::GetInstance()->Now();
- }
- quic::QuicTime ApproximateNow() const override {
- return quic::QuicChromiumClock::GetInstance()->ApproximateNow();
- }
- quic::QuicWallTime WallNow() const override { return wall_now_; }
- void set_wall_now(quic::QuicWallTime now) { wall_now_ = now; }
- private:
- quic::QuicWallTime wall_now_ = quic::QuicWallTime::Zero();
- };
- class TestConnectionHelper : public quic::QuicConnectionHelperInterface {
- public:
- const quic::QuicClock* GetClock() const override { return &clock_; }
- quic::QuicRandom* GetRandomGenerator() override {
- return quic::QuicRandom::GetInstance();
- }
- quiche::QuicheBufferAllocator* GetStreamSendBufferAllocator() override {
- return &allocator_;
- }
- TestWallClock& clock() { return clock_; }
- private:
- TestWallClock clock_;
- quiche::SimpleBufferAllocator allocator_;
- };
- class DedicatedWebTransportHttp3Test : public TestWithTaskEnvironment {
- public:
- DedicatedWebTransportHttp3Test() {
- quic::QuicEnableVersion(quic::ParsedQuicVersion::RFCv1());
- origin_ = url::Origin::Create(GURL{"https://example.org"});
- isolation_key_ =
- NetworkIsolationKey(SchemefulSite(origin_), SchemefulSite(origin_));
- URLRequestContextBuilder builder;
- builder.set_proxy_resolution_service(
- ConfiguredProxyResolutionService::CreateDirect());
- auto cert_verifier = std::make_unique<MockCertVerifier>();
- cert_verifier->set_default_result(OK);
- builder.SetCertVerifier(std::move(cert_verifier));
- auto host_resolver = std::make_unique<MockHostResolver>();
- host_resolver->rules()->AddRule("test.example.com", "127.0.0.1");
- builder.set_host_resolver(std::move(host_resolver));
- auto helper = std::make_unique<TestConnectionHelper>();
- helper_ = helper.get();
- auto quic_context = std::make_unique<QuicContext>(std::move(helper));
- quic_context->params()->supported_versions.clear();
- // This is required to bypass the check that only allows known certificate
- // roots in QUIC.
- quic_context->params()->origins_to_force_quic_on.insert(
- HostPortPair("test.example.com", 0));
- builder.set_quic_context(std::move(quic_context));
- builder.set_net_log(NetLog::Get());
- context_ = builder.Build();
- // By default, quit on error instead of waiting for RunLoop() to time out.
- ON_CALL(visitor_, OnConnectionFailed(_))
- .WillByDefault([this](const WebTransportError& error) {
- LOG(ERROR) << "Connection failed: " << error;
- run_loop_->Quit();
- });
- ON_CALL(visitor_, OnError(_))
- .WillByDefault([this](const WebTransportError& error) {
- LOG(ERROR) << "Connection error: " << error;
- run_loop_->Quit();
- });
- }
- ~DedicatedWebTransportHttp3Test() override {
- if (server_ != nullptr) {
- server_->Shutdown();
- }
- }
- GURL GetURL(const std::string& suffix) {
- return GURL{base::StrCat(
- {"https://test.example.com:", base::NumberToString(port_), suffix})};
- }
- void StartServer(std::unique_ptr<quic::ProofSource> proof_source = nullptr) {
- if (proof_source == nullptr) {
- proof_source = quic::test::crypto_test_utils::ProofSourceForTesting();
- }
- backend_.set_enable_webtransport(true);
- server_ = std::make_unique<QuicSimpleServer>(
- std::move(proof_source), quic::QuicConfig(),
- quic::QuicCryptoServerConfig::ConfigOptions(),
- quic::AllSupportedVersions(), &backend_);
- ASSERT_TRUE(server_->CreateUDPSocketAndListen(
- quic::QuicSocketAddress(quic::QuicIpAddress::Any6(), /*port=*/0)));
- port_ = server_->server_address().port();
- }
- void Run() {
- run_loop_ = std::make_unique<base::RunLoop>();
- run_loop_->Run();
- }
- auto StopRunning() {
- return [this]() { run_loop_->Quit(); };
- }
- protected:
- quic::test::QuicFlagSaver flags_; // Save/restore all QUIC flag values.
- std::unique_ptr<URLRequestContext> context_;
- std::unique_ptr<DedicatedWebTransportHttp3Client> client_;
- raw_ptr<TestConnectionHelper> helper_; // Owned by |context_|.
- ::testing::NiceMock<MockVisitor> visitor_;
- std::unique_ptr<QuicSimpleServer> server_;
- std::unique_ptr<base::RunLoop> run_loop_;
- quic::test::QuicTestBackend backend_;
- int port_ = 0;
- url::Origin origin_;
- NetworkIsolationKey isolation_key_;
- };
- TEST_F(DedicatedWebTransportHttp3Test, Connect) {
- StartServer();
- client_ = std::make_unique<DedicatedWebTransportHttp3Client>(
- GetURL("/echo"), origin_, &visitor_, isolation_key_, context_.get(),
- WebTransportParameters());
- EXPECT_CALL(visitor_, OnConnected(_)).WillOnce(StopRunning());
- client_->Connect();
- Run();
- ASSERT_TRUE(client_->session() != nullptr);
- client_->Close(absl::nullopt);
- EXPECT_CALL(visitor_, OnClosed(_)).WillOnce(StopRunning());
- Run();
- }
- // TODO(https://crbug.com/1288036): The test is flaky on Mac and iOS.
- #if BUILDFLAG(IS_IOS) || BUILDFLAG(IS_MAC)
- #define MAYBE_CloseTimeout DISABLED_CloseTimeout
- #else
- #define MAYBE_CloseTimeout CloseTimeout
- #endif
- TEST_F(DedicatedWebTransportHttp3Test, MAYBE_CloseTimeout) {
- StartServer();
- client_ = std::make_unique<DedicatedWebTransportHttp3Client>(
- GetURL("/echo"), origin_, &visitor_, isolation_key_, context_.get(),
- WebTransportParameters());
- EXPECT_CALL(visitor_, OnConnected(_)).WillOnce(StopRunning());
- client_->Connect();
- Run();
- ASSERT_TRUE(client_->session() != nullptr);
- // Delete the server and put up a no-op socket in its place to simulate the
- // traffic being dropped. Note that this is normally not a supported way of
- // shutting down a QuicServer, and will generate a lot of errors in the logs.
- server_.reset();
- IPEndPoint bind_address(IPAddress::IPv6AllZeros(), port_);
- auto noop_socket =
- std::make_unique<UDPServerSocket>(/*net_log=*/nullptr, NetLogSource());
- noop_socket->AllowAddressReuse();
- ASSERT_GE(noop_socket->Listen(bind_address), 0);
- client_->Close(absl::nullopt);
- EXPECT_CALL(visitor_, OnError(_)).WillOnce(StopRunning());
- Run();
- }
- TEST_F(DedicatedWebTransportHttp3Test, CloseReason) {
- StartServer();
- client_ = std::make_unique<DedicatedWebTransportHttp3Client>(
- GetURL("/session-close"), origin_, &visitor_, isolation_key_,
- context_.get(), WebTransportParameters());
- EXPECT_CALL(visitor_, OnConnected(_)).WillOnce(StopRunning());
- client_->Connect();
- Run();
- ASSERT_TRUE(client_->session() != nullptr);
- quic::WebTransportStream* stream =
- client_->session()->OpenOutgoingUnidirectionalStream();
- ASSERT_TRUE(stream != nullptr);
- EXPECT_TRUE(stream->Write("42 test error"));
- EXPECT_TRUE(stream->SendFin());
- WebTransportCloseInfo close_info(42, "test error");
- absl::optional<WebTransportCloseInfo> received_close_info;
- EXPECT_CALL(visitor_, OnClosed(_))
- .WillOnce(DoAll(StopRunning(), SaveArg<0>(&received_close_info)));
- Run();
- EXPECT_THAT(received_close_info, Optional(close_info));
- }
- } // namespace
- } // namespace net::test
|