xref: /aosp_15_r20/external/cronet/net/quic/dedicated_web_transport_http3_client_test.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1 // Copyright 2021 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "net/quic/dedicated_web_transport_http3_client.h"
6 
7 #include <memory>
8 #include <string_view>
9 
10 #include "base/memory/raw_ptr.h"
11 #include "base/strings/strcat.h"
12 #include "build/build_config.h"
13 #include "net/base/schemeful_site.h"
14 #include "net/cert/mock_cert_verifier.h"
15 #include "net/dns/mock_host_resolver.h"
16 #include "net/proxy_resolution/configured_proxy_resolution_service.h"
17 #include "net/quic/crypto/proof_source_chromium.h"
18 #include "net/quic/quic_context.h"
19 #include "net/test/test_data_directory.h"
20 #include "net/test/test_with_task_environment.h"
21 #include "net/third_party/quiche/src/quiche/quic/test_tools/crypto_test_utils.h"
22 #include "net/third_party/quiche/src/quiche/quic/test_tools/quic_test_backend.h"
23 #include "net/tools/quic/quic_simple_server.h"
24 #include "net/tools/quic/quic_simple_server_socket.h"
25 #include "net/url_request/url_request_context.h"
26 #include "net/url_request/url_request_context_builder.h"
27 #include "testing/gmock/include/gmock/gmock.h"
28 #include "testing/gtest/include/gtest/gtest.h"
29 #include "url/gurl.h"
30 #include "url/origin.h"
31 
32 namespace net::test {
33 namespace {
34 
35 using ::quic::test::MemSliceFromString;
36 using ::testing::_;
37 using ::testing::DoAll;
38 using ::testing::Optional;
39 using ::testing::SaveArg;
40 
41 class MockVisitor : public WebTransportClientVisitor {
42  public:
43   MOCK_METHOD(void,
44               OnConnected,
45               (scoped_refptr<HttpResponseHeaders>),
46               (override));
47   MOCK_METHOD(void, OnConnectionFailed, (const WebTransportError&), (override));
48   MOCK_METHOD(void,
49               OnClosed,
50               (const std::optional<WebTransportCloseInfo>&),
51               (override));
52   MOCK_METHOD(void, OnError, (const WebTransportError&), (override));
53 
54   MOCK_METHOD0(OnIncomingBidirectionalStreamAvailable, void());
55   MOCK_METHOD0(OnIncomingUnidirectionalStreamAvailable, void());
56   MOCK_METHOD1(OnDatagramReceived, void(std::string_view));
57   MOCK_METHOD0(OnCanCreateNewOutgoingBidirectionalStream, void());
58   MOCK_METHOD0(OnCanCreateNewOutgoingUnidirectionalStream, void());
59   MOCK_METHOD1(OnDatagramProcessed, void(std::optional<quic::MessageStatus>));
60 };
61 
62 // A clock that only mocks out WallNow(), but uses real Now() and
63 // ApproximateNow().  Useful for certificate verification.
64 class TestWallClock : public quic::QuicClock {
65  public:
Now() const66   quic::QuicTime Now() const override {
67     return quic::QuicChromiumClock::GetInstance()->Now();
68   }
ApproximateNow() const69   quic::QuicTime ApproximateNow() const override {
70     return quic::QuicChromiumClock::GetInstance()->ApproximateNow();
71   }
WallNow() const72   quic::QuicWallTime WallNow() const override { return wall_now_; }
73 
set_wall_now(quic::QuicWallTime now)74   void set_wall_now(quic::QuicWallTime now) { wall_now_ = now; }
75 
76  private:
77   quic::QuicWallTime wall_now_ = quic::QuicWallTime::Zero();
78 };
79 
80 class TestConnectionHelper : public quic::QuicConnectionHelperInterface {
81  public:
GetClock() const82   const quic::QuicClock* GetClock() const override { return &clock_; }
GetRandomGenerator()83   quic::QuicRandom* GetRandomGenerator() override {
84     return quic::QuicRandom::GetInstance();
85   }
GetStreamSendBufferAllocator()86   quiche::QuicheBufferAllocator* GetStreamSendBufferAllocator() override {
87     return &allocator_;
88   }
89 
clock()90   TestWallClock& clock() { return clock_; }
91 
92  private:
93   TestWallClock clock_;
94   quiche::SimpleBufferAllocator allocator_;
95 };
96 
97 class DedicatedWebTransportHttp3Test : public TestWithTaskEnvironment {
98  public:
DedicatedWebTransportHttp3Test()99   DedicatedWebTransportHttp3Test() {
100     quic::QuicEnableVersion(quic::ParsedQuicVersion::RFCv1());
101     origin_ = url::Origin::Create(GURL{"https://example.org"});
102     anonymization_key_ =
103         NetworkAnonymizationKey::CreateSameSite(SchemefulSite(origin_));
104 
105     URLRequestContextBuilder builder;
106     builder.set_proxy_resolution_service(
107         ConfiguredProxyResolutionService::CreateDirect());
108 
109     auto cert_verifier = std::make_unique<MockCertVerifier>();
110     cert_verifier->set_default_result(OK);
111     builder.SetCertVerifier(std::move(cert_verifier));
112 
113     auto host_resolver = std::make_unique<MockHostResolver>();
114     host_resolver->rules()->AddRule("test.example.com", "127.0.0.1");
115     builder.set_host_resolver(std::move(host_resolver));
116 
117     auto helper = std::make_unique<TestConnectionHelper>();
118     helper_ = helper.get();
119     auto quic_context = std::make_unique<QuicContext>(std::move(helper));
120     quic_context->params()->supported_versions.clear();
121     // This is required to bypass the check that only allows known certificate
122     // roots in QUIC.
123     quic_context->params()->origins_to_force_quic_on.insert(
124         HostPortPair("test.example.com", 0));
125     builder.set_quic_context(std::move(quic_context));
126 
127     builder.set_net_log(NetLog::Get());
128     context_ = builder.Build();
129 
130     // By default, quit on error instead of waiting for RunLoop() to time out.
131     ON_CALL(visitor_, OnConnectionFailed(_))
132         .WillByDefault([this](const WebTransportError& error) {
133           LOG(ERROR) << "Connection failed: " << error;
134           run_loop_->Quit();
135         });
136     ON_CALL(visitor_, OnError(_))
137         .WillByDefault([this](const WebTransportError& error) {
138           LOG(ERROR) << "Connection error: " << error;
139           run_loop_->Quit();
140         });
141   }
142 
~DedicatedWebTransportHttp3Test()143   ~DedicatedWebTransportHttp3Test() override {
144     if (server_ != nullptr) {
145       server_->Shutdown();
146     }
147   }
148 
GetURL(const std::string & suffix)149   GURL GetURL(const std::string& suffix) {
150     return GURL{base::StrCat(
151         {"https://test.example.com:", base::NumberToString(port_), suffix})};
152   }
153 
StartServer(std::unique_ptr<quic::ProofSource> proof_source=nullptr)154   void StartServer(std::unique_ptr<quic::ProofSource> proof_source = nullptr) {
155     if (proof_source == nullptr) {
156       proof_source = quic::test::crypto_test_utils::ProofSourceForTesting();
157     }
158     backend_.set_enable_webtransport(true);
159     server_ = std::make_unique<QuicSimpleServer>(
160         std::move(proof_source), quic::QuicConfig(),
161         quic::QuicCryptoServerConfig::ConfigOptions(),
162         AllSupportedQuicVersions(), &backend_);
163     ASSERT_TRUE(server_->CreateUDPSocketAndListen(
164         quic::QuicSocketAddress(quic::QuicIpAddress::Any6(), /*port=*/0)));
165     port_ = server_->server_address().port();
166   }
167 
Run()168   void Run() {
169     run_loop_ = std::make_unique<base::RunLoop>();
170     run_loop_->Run();
171   }
172 
StopRunning()173   auto StopRunning() {
174     return [this]() { run_loop_->Quit(); };
175   }
176 
177  protected:
178   quic::test::QuicFlagSaver flags_;  // Save/restore all QUIC flag values.
179   std::unique_ptr<URLRequestContext> context_;
180   std::unique_ptr<DedicatedWebTransportHttp3Client> client_;
181   raw_ptr<TestConnectionHelper> helper_;  // Owned by |context_|.
182   ::testing::NiceMock<MockVisitor> visitor_;
183   std::unique_ptr<QuicSimpleServer> server_;
184   std::unique_ptr<base::RunLoop> run_loop_;
185   quic::test::QuicTestBackend backend_;
186 
187   int port_ = 0;
188   url::Origin origin_;
189   NetworkAnonymizationKey anonymization_key_;
190 };
191 
TEST_F(DedicatedWebTransportHttp3Test,Connect)192 TEST_F(DedicatedWebTransportHttp3Test, Connect) {
193   StartServer();
194   client_ = std::make_unique<DedicatedWebTransportHttp3Client>(
195       GetURL("/echo"), origin_, &visitor_, anonymization_key_, context_.get(),
196       WebTransportParameters());
197 
198   EXPECT_CALL(visitor_, OnConnected(_)).WillOnce(StopRunning());
199   client_->Connect();
200   Run();
201   ASSERT_TRUE(client_->session() != nullptr);
202 
203   client_->Close(std::nullopt);
204   EXPECT_CALL(visitor_, OnClosed(_)).WillOnce(StopRunning());
205   Run();
206 }
207 
208 // TODO(https://crbug.com/1288036): The test is flaky on Mac and iOS.
209 #if BUILDFLAG(IS_IOS) || BUILDFLAG(IS_MAC)
210 #define MAYBE_CloseTimeout DISABLED_CloseTimeout
211 #else
212 #define MAYBE_CloseTimeout CloseTimeout
213 #endif
TEST_F(DedicatedWebTransportHttp3Test,MAYBE_CloseTimeout)214 TEST_F(DedicatedWebTransportHttp3Test, MAYBE_CloseTimeout) {
215   StartServer();
216   client_ = std::make_unique<DedicatedWebTransportHttp3Client>(
217       GetURL("/echo"), origin_, &visitor_, anonymization_key_, context_.get(),
218       WebTransportParameters());
219 
220   EXPECT_CALL(visitor_, OnConnected(_)).WillOnce(StopRunning());
221   client_->Connect();
222   Run();
223   ASSERT_TRUE(client_->session() != nullptr);
224 
225   // Delete the server and put up a no-op socket in its place to simulate the
226   // traffic being dropped.  Note that this is normally not a supported way of
227   // shutting down a QuicServer, and will generate a lot of errors in the logs.
228   server_.reset();
229   IPEndPoint bind_address(IPAddress::IPv6AllZeros(), port_);
230   auto noop_socket =
231       std::make_unique<UDPServerSocket>(/*net_log=*/nullptr, NetLogSource());
232   noop_socket->AllowAddressReuse();
233   ASSERT_GE(noop_socket->Listen(bind_address), 0);
234 
235   client_->Close(std::nullopt);
236   EXPECT_CALL(visitor_, OnError(_)).WillOnce(StopRunning());
237   Run();
238 }
239 
TEST_F(DedicatedWebTransportHttp3Test,CloseReason)240 TEST_F(DedicatedWebTransportHttp3Test, CloseReason) {
241   StartServer();
242   client_ = std::make_unique<DedicatedWebTransportHttp3Client>(
243       GetURL("/session-close"), origin_, &visitor_, anonymization_key_,
244       context_.get(), WebTransportParameters());
245 
246   EXPECT_CALL(visitor_, OnConnected(_)).WillOnce(StopRunning());
247   client_->Connect();
248   Run();
249   ASSERT_TRUE(client_->session() != nullptr);
250 
251   quic::WebTransportStream* stream =
252       client_->session()->OpenOutgoingUnidirectionalStream();
253   ASSERT_TRUE(stream != nullptr);
254   EXPECT_TRUE(stream->Write("42 test error"));
255   EXPECT_TRUE(stream->SendFin());
256 
257   WebTransportCloseInfo close_info(42, "test error");
258   std::optional<WebTransportCloseInfo> received_close_info;
259   EXPECT_CALL(visitor_, OnClosed(_))
260       .WillOnce(DoAll(StopRunning(), SaveArg<0>(&received_close_info)));
261   Run();
262   EXPECT_THAT(received_close_info, Optional(close_info));
263 }
264 
265 }  // namespace
266 }  // namespace net::test
267