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