1 // Copyright 2024 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 <memory>
6
7 #include "base/strings/strcat.h"
8 #include "base/test/metrics/histogram_tester.h"
9 #include "net/base/network_anonymization_key.h"
10 #include "net/base/privacy_mode.h"
11 #include "net/base/proxy_chain.h"
12 #include "net/base/proxy_server.h"
13 #include "net/base/session_usage.h"
14 #include "net/cert/x509_certificate.h"
15 #include "net/quic/address_utils.h"
16 #include "net/quic/crypto/proof_verifier_chromium.h"
17 #include "net/quic/quic_context.h"
18 #include "net/quic/quic_http_stream.h"
19 #include "net/quic/quic_session_pool.h"
20 #include "net/quic/quic_session_pool_test_base.h"
21 #include "net/quic/quic_socket_data_provider.h"
22 #include "net/quic/quic_test_packet_maker.h"
23 #include "net/test/cert_test_util.h"
24 #include "net/test/test_data_directory.h"
25 #include "net/third_party/quiche/src/quiche/quic/core/quic_types.h"
26 #include "net/third_party/quiche/src/quiche/quic/core/quic_versions.h"
27 #include "net/third_party/quiche/src/quiche/quic/test_tools/quic_config_peer.h"
28 #include "net/third_party/quiche/src/quiche/quic/test_tools/quic_test_utils.h"
29 #include "testing/gtest/include/gtest/gtest.h"
30
31 namespace net::test {
32
33 class QuicSessionPoolProxyJobTest
34 : public QuicSessionPoolTestBase,
35 public ::testing::TestWithParam<quic::ParsedQuicVersion> {
36 protected:
QuicSessionPoolProxyJobTest()37 QuicSessionPoolProxyJobTest() : QuicSessionPoolTestBase(GetParam()) {}
38
MakePacketMaker(const std::string & host,quic::Perspective perspective,bool client_priority_uses_incremental=false,bool use_priority_header=false)39 test::QuicTestPacketMaker MakePacketMaker(
40 const std::string& host,
41 quic::Perspective perspective,
42 bool client_priority_uses_incremental = false,
43 bool use_priority_header = false) {
44 return test::QuicTestPacketMaker(
45 version_,
46 quic::QuicUtils::CreateRandomConnectionId(context_.random_generator()),
47 context_.clock(), host, perspective, client_priority_uses_incremental,
48 use_priority_header);
49 }
50
51 base::HistogramTester histogram_tester;
52 };
53
54 INSTANTIATE_TEST_SUITE_P(All,
55 QuicSessionPoolProxyJobTest,
56 ::testing::ValuesIn(AllSupportedQuicVersions()));
57
TEST_P(QuicSessionPoolProxyJobTest,CreateProxiedQuicSession)58 TEST_P(QuicSessionPoolProxyJobTest, CreateProxiedQuicSession) {
59 Initialize();
60
61 GURL url("https://www.example.org/");
62 GURL proxy(kProxy1Url);
63 auto origin = url::SchemeHostPort(url);
64 auto proxy_origin = url::SchemeHostPort(proxy);
65 auto nak = NetworkAnonymizationKey();
66
67 scoped_refptr<X509Certificate> cert(
68 ImportCertFromFile(GetTestCertsDirectory(), "wildcard.pem"));
69 ASSERT_TRUE(cert->VerifyNameMatch(origin.host()));
70 ASSERT_TRUE(cert->VerifyNameMatch(proxy_origin.host()));
71 ASSERT_FALSE(cert->VerifyNameMatch(kDifferentHostname));
72
73 ProofVerifyDetailsChromium verify_details;
74 verify_details.cert_verify_result.verified_cert = cert;
75 verify_details.cert_verify_result.is_issued_by_known_root = true;
76 crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details);
77
78 // QUIC proxies do not use priority header.
79 client_maker_.set_use_priority_header(false);
80
81 // Use a separate packet maker for the connection to the endpoint.
82 QuicTestPacketMaker endpoint_maker =
83 MakePacketMaker(kDefaultServerHostName, quic::Perspective::IS_CLIENT,
84 /*client_priority_uses_incremental=*/true,
85 /*use_priority_header=*/true);
86
87 const uint64_t stream_id = GetNthClientInitiatedBidirectionalStreamId(0);
88 QuicSocketDataProvider socket_data(version_);
89 socket_data.AddWrite("initial-settings", ConstructInitialSettingsPacket(1))
90 .Sync();
91 socket_data
92 .AddWrite("connect-udp",
93 ConstructConnectUdpRequestPacket(
94 2, stream_id, proxy.host(),
95 "/.well-known/masque/udp/www.example.org/443/", false))
96 .Sync();
97 socket_data.AddRead("server-settings", ConstructServerSettingsPacket(3));
98 socket_data.AddRead("ok-response",
99 ConstructOkResponsePacket(4, stream_id, true));
100 socket_data.AddWrite("ack",
101 client_maker_.Packet(3).AddAckFrame(3, 4, 3).Build());
102 socket_data.AddWrite("endpoint-initial-settings",
103 ConstructClientH3DatagramPacket(
104 4, stream_id, kConnectUdpContextId,
105 endpoint_maker.MakeInitialSettingsPacket(1)));
106 socket_factory_->AddSocketDataProvider(&socket_data);
107
108 auto proxy_chain = ProxyChain::ForIpProtection({
109 ProxyServer::FromSchemeHostAndPort(ProxyServer::SCHEME_QUIC,
110 proxy_origin.host(), 443),
111 });
112 EXPECT_TRUE(proxy_chain.IsValid());
113
114 RequestBuilder builder(this);
115 builder.destination = origin;
116 builder.proxy_chain = proxy_chain;
117 builder.http_user_agent_settings = &http_user_agent_settings_;
118 builder.url = url;
119
120 // Note: `builder` defaults to using the parameterized `version_` member,
121 // which we will assert here as a pre-condition for checking that the proxy
122 // session ignores this and uses RFCv1 instead.
123 ASSERT_EQ(builder.quic_version, version_);
124
125 EXPECT_EQ(ERR_IO_PENDING, builder.CallRequest());
126 ASSERT_EQ(OK, callback_.WaitForResult());
127 std::unique_ptr<HttpStream> stream = CreateStream(&builder.request);
128 EXPECT_TRUE(stream.get());
129 QuicChromiumClientSession* session =
130 GetActiveSession(origin, PRIVACY_MODE_DISABLED, nak, proxy_chain);
131 ASSERT_TRUE(session);
132
133 // The direct connection to the proxy has a max packet size 1350. The
134 // connection to the endpoint could use up to 1350 - (packet header = 38) -
135 // (quarter-stream-id = 1) - (context-id = 1), but this value is greater than
136 // the default maximum of 1250. We can only observe the largest datagram that
137 // could be sent to the endpoint, which would be 1250 - (packet header = 38) =
138 // 1212 bytes.
139 EXPECT_EQ(session->GetGuaranteedLargestMessagePayload(), 1212);
140
141 // Check that the session through the proxy uses the version from the request.
142 EXPECT_EQ(session->GetQuicVersion(), version_);
143
144 // Check that the session to the proxy is keyed by an empty NAK and always
145 // uses RFCv1.
146 QuicChromiumClientSession* proxy_session =
147 GetActiveSession(proxy_origin, PRIVACY_MODE_DISABLED, nak,
148 ProxyChain::ForIpProtection({}), SessionUsage::kProxy);
149 ASSERT_TRUE(proxy_session);
150 EXPECT_EQ(proxy_session->GetQuicVersion(), quic::ParsedQuicVersion::RFCv1());
151
152 stream.reset();
153
154 // Ensure the session finishes creating before proceeding.
155 RunUntilIdle();
156
157 EXPECT_TRUE(socket_data.AllDataConsumed());
158 histogram_tester.ExpectTotalCount(
159 "Net.HttpProxy.ConnectLatency.Http3.Quic.Success", 1);
160 }
161
TEST_P(QuicSessionPoolProxyJobTest,DoubleProxiedQuicSession)162 TEST_P(QuicSessionPoolProxyJobTest, DoubleProxiedQuicSession) {
163 base::test::ScopedFeatureList scoped_feature_list;
164 scoped_feature_list.InitWithFeatures(
165 {net::features::kPartitionConnectionsByNetworkIsolationKey},
166 {net::features::kPartitionProxyChains});
167 Initialize();
168
169 // Set up a connection via proxy1, to proxy2, to example.org, all using QUIC.
170 GURL url("https://www.example.org/");
171 GURL proxy1(kProxy1Url);
172 GURL proxy2(kProxy2Url);
173 auto origin = url::SchemeHostPort(url);
174 auto proxy1_origin = url::SchemeHostPort(proxy1);
175 auto proxy2_origin = url::SchemeHostPort(proxy2);
176 auto endpoint_nak =
177 NetworkAnonymizationKey::CreateSameSite(SchemefulSite(url));
178
179 scoped_refptr<X509Certificate> cert(
180 ImportCertFromFile(GetTestCertsDirectory(), "wildcard.pem"));
181 ASSERT_TRUE(cert->VerifyNameMatch(origin.host()));
182 ASSERT_TRUE(cert->VerifyNameMatch(proxy1_origin.host()));
183 ASSERT_FALSE(cert->VerifyNameMatch(kDifferentHostname));
184
185 ProofVerifyDetailsChromium verify_details;
186 verify_details.cert_verify_result.verified_cert = cert;
187 verify_details.cert_verify_result.is_issued_by_known_root = true;
188 crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details);
189
190 QuicSocketDataProvider socket_data(version_);
191 quic::QuicStreamId stream_id_0 =
192 GetNthClientInitiatedBidirectionalStreamId(0);
193 int to_proxy1_packet_num = 1;
194 QuicTestPacketMaker to_proxy1 =
195 MakePacketMaker(proxy1_origin.host(), quic::Perspective::IS_CLIENT,
196 /*client_priority_uses_incremental=*/true,
197 /*use_priority_header=*/false);
198 int from_proxy1_packet_num = 1;
199 QuicTestPacketMaker from_proxy1 =
200 MakePacketMaker(proxy1_origin.host(), quic::Perspective::IS_SERVER,
201 /*client_priority_uses_incremental=*/false,
202 /*use_priority_header=*/false);
203 int to_proxy2_packet_num = 1;
204 QuicTestPacketMaker to_proxy2 =
205 MakePacketMaker(proxy2_origin.host(), quic::Perspective::IS_CLIENT,
206 /*client_priority_uses_incremental=*/true,
207 /*use_priority_header=*/false);
208 int from_proxy2_packet_num = 1;
209 QuicTestPacketMaker from_proxy2 =
210 MakePacketMaker(proxy2_origin.host(), quic::Perspective::IS_SERVER,
211 /*client_priority_uses_incremental=*/false,
212 /*use_priority_header=*/false);
213 int to_endpoint_packet_num = 1;
214 QuicTestPacketMaker to_endpoint =
215 MakePacketMaker("www.example.org", quic::Perspective::IS_CLIENT,
216 /*client_priority_uses_incremental=*/true,
217 /*use_priority_header=*/true);
218
219 // The browser sends initial settings to proxy1.
220 socket_data.AddWrite(
221 "proxy1 initial settings",
222 to_proxy1.MakeInitialSettingsPacket(to_proxy1_packet_num++));
223
224 // The browser sends CONNECT-UDP request to proxy1.
225 socket_data
226 .AddWrite("proxy1 connect-udp",
227 ConstructConnectUdpRequestPacket(
228 to_proxy1, to_proxy1_packet_num++, stream_id_0,
229 proxy1_origin.host(),
230 base::StrCat({"/.well-known/masque/udp/",
231 proxy2_origin.host(), "/443/"}),
232 false))
233 .Sync();
234
235 // Proxy1 sends initial settings.
236 socket_data.AddRead(
237 "proxy1 server settings",
238 from_proxy1.MakeInitialSettingsPacket(from_proxy1_packet_num++));
239
240 // Proxy1 responds to the CONNECT.
241 socket_data.AddRead(
242 "proxy1 ok response",
243 ConstructOkResponsePacket(from_proxy1, from_proxy1_packet_num++,
244 stream_id_0, true));
245
246 // The browser ACKs the OK response packet.
247 socket_data.AddWrite(
248 "proxy1 ack ok",
249 ConstructAckPacket(to_proxy1, to_proxy1_packet_num++, 1, 2, 1));
250
251 // The browser sends initial settings and a CONNECT-UDP request to proxy2 via
252 // proxy1.
253 socket_data.AddWrite("proxy2 settings-and-request",
254 to_proxy1.Packet(to_proxy1_packet_num++)
255 .AddMessageFrame(ConstructH3Datagram(
256 stream_id_0, kConnectUdpContextId,
257 ConstructInitialSettingsPacket(
258 to_proxy2, to_proxy2_packet_num++)))
259 .AddMessageFrame(ConstructH3Datagram(
260 stream_id_0, kConnectUdpContextId,
261 ConstructConnectUdpRequestPacket(
262 to_proxy2, to_proxy2_packet_num++,
263 stream_id_0, proxy2_origin.host(),
264 base::StrCat({"/.well-known/masque/udp/",
265 origin.host(), "/443/"}),
266 false)))
267 .Build());
268
269 // Proxy2 sends initial settings and an OK response to the CONNECT request,
270 // via proxy1.
271 socket_data.AddRead(
272 "proxy2 server settings and ok response",
273 from_proxy1.Packet(from_proxy1_packet_num++)
274 .AddMessageFrame(
275 ConstructH3Datagram(stream_id_0, kConnectUdpContextId,
276 ConstructInitialSettingsPacket(
277 from_proxy2, from_proxy2_packet_num++)))
278 .AddMessageFrame(ConstructH3Datagram(
279 stream_id_0, kConnectUdpContextId,
280 ConstructOkResponsePacket(from_proxy2, from_proxy2_packet_num++,
281 stream_id_0, true)))
282 .Build());
283
284 // The browser ACK's the datagram from proxy1, and acks proxy2's OK response
285 // packet via proxy1.
286 socket_data.AddWrite("proxy2 acks",
287 to_proxy1.Packet(to_proxy1_packet_num++)
288 .AddAckFrame(1, 3, 1)
289 .AddMessageFrame(ConstructH3Datagram(
290 stream_id_0, kConnectUdpContextId,
291 to_proxy2.Packet(to_proxy2_packet_num++)
292 .AddAckFrame(1, 2, 1)
293 .Build()))
294 .Build());
295
296 // The browser sends initial settings to the endpoint, via proxy2, via proxy1.
297 socket_data.AddWrite(
298 "endpoint initial settings",
299 to_proxy1.Packet(to_proxy1_packet_num++)
300 .AddMessageFrame(ConstructH3Datagram(
301 stream_id_0, kConnectUdpContextId,
302 to_proxy2.Packet(to_proxy2_packet_num++)
303 .AddMessageFrame(ConstructH3Datagram(
304 stream_id_0, kConnectUdpContextId,
305 ConstructInitialSettingsPacket(to_endpoint,
306 to_endpoint_packet_num++)))
307 .Build()))
308 .Build());
309
310 socket_factory_->AddSocketDataProvider(&socket_data);
311
312 auto proxy_chain = ProxyChain::ForIpProtection({
313 ProxyServer::FromSchemeHostAndPort(ProxyServer::SCHEME_QUIC,
314 proxy1_origin.host(), 443),
315 ProxyServer::FromSchemeHostAndPort(ProxyServer::SCHEME_QUIC,
316 proxy2_origin.host(), 443),
317 });
318 EXPECT_TRUE(proxy_chain.IsValid());
319
320 RequestBuilder builder(this);
321 builder.destination = origin;
322 builder.proxy_chain = proxy_chain;
323 builder.http_user_agent_settings = &http_user_agent_settings_;
324 builder.network_anonymization_key = endpoint_nak;
325 builder.url = url;
326
327 // Note: `builder` defaults to using the parameterized `version_` member,
328 // which we will assert here as a pre-condition for checking that the proxy
329 // session ignores this and uses RFCv1 instead.
330 ASSERT_EQ(builder.quic_version, version_);
331
332 EXPECT_EQ(ERR_IO_PENDING, builder.CallRequest());
333 ASSERT_EQ(OK, callback_.WaitForResult());
334 std::unique_ptr<HttpStream> stream = CreateStream(&builder.request);
335 EXPECT_TRUE(stream.get());
336 QuicChromiumClientSession* session = GetActiveSession(
337 origin, PRIVACY_MODE_DISABLED, endpoint_nak, proxy_chain);
338 ASSERT_TRUE(session);
339
340 // The direct connection to the proxy has a max packet size 1350. The
341 // connection to the endpoint could use up to 1350 - (packet header = 38) -
342 // (quarter-stream-id = 1) - (context-id = 1), but this value is greater than
343 // the default maximum of 1250. We can only observe the largest datagram that
344 // could be sent to the endpoint, which would be 1250 - (packet header = 38) =
345 // 1212 bytes.
346 EXPECT_EQ(session->GetGuaranteedLargestMessagePayload(), 1212);
347
348 // Check that the session through the proxy uses the version from the request.
349 EXPECT_EQ(session->GetQuicVersion(), version_);
350
351 // Check that the session to proxy1 uses an empty NAK (due to
352 // !kPartitionProxyChains) and RFCv1.
353 auto proxy_nak = NetworkAnonymizationKey();
354 QuicChromiumClientSession* proxy1_session =
355 GetActiveSession(proxy1_origin, PRIVACY_MODE_DISABLED, proxy_nak,
356 ProxyChain::ForIpProtection({}), SessionUsage::kProxy);
357 ASSERT_TRUE(proxy1_session);
358 EXPECT_EQ(proxy1_session->quic_session_key().network_anonymization_key(),
359 proxy_nak);
360 EXPECT_EQ(proxy1_session->GetQuicVersion(), quic::ParsedQuicVersion::RFCv1());
361
362 // Check that the session to proxy2 uses the endpoint NAK and RFCv1.
363 QuicChromiumClientSession* proxy2_session = GetActiveSession(
364 proxy2_origin, PRIVACY_MODE_DISABLED, endpoint_nak,
365 ProxyChain::ForIpProtection({ProxyServer::FromSchemeHostAndPort(
366 ProxyServer::SCHEME_QUIC, proxy1_origin.host(), 443)}),
367 SessionUsage::kProxy);
368 ASSERT_TRUE(proxy2_session);
369 EXPECT_EQ(proxy2_session->quic_session_key().network_anonymization_key(),
370 endpoint_nak);
371 EXPECT_EQ(proxy2_session->GetQuicVersion(), quic::ParsedQuicVersion::RFCv1());
372
373 stream.reset();
374
375 // Ensure the session finishes creating before proceeding.
376 RunUntilIdle();
377
378 ASSERT_TRUE(socket_data.AllDataConsumed());
379
380 histogram_tester.ExpectTotalCount(
381 "Net.HttpProxy.ConnectLatency.Http3.Quic.Success", 1);
382 }
383
TEST_P(QuicSessionPoolProxyJobTest,PoolDeletedDuringSessionCreation)384 TEST_P(QuicSessionPoolProxyJobTest, PoolDeletedDuringSessionCreation) {
385 base::test::ScopedFeatureList scoped_feature_list;
386 scoped_feature_list.InitWithFeatures(
387 {net::features::kPartitionConnectionsByNetworkIsolationKey},
388 {net::features::kPartitionProxyChains});
389 Initialize();
390
391 // Set up a connection via proxy1, to proxy2, to example.org, all using QUIC.
392 GURL url("https://www.example.org/");
393 GURL proxy1(kProxy1Url);
394 GURL proxy2(kProxy2Url);
395 auto origin = url::SchemeHostPort(url);
396 auto proxy1_origin = url::SchemeHostPort(proxy1);
397 auto proxy2_origin = url::SchemeHostPort(proxy2);
398 auto endpoint_nak =
399 NetworkAnonymizationKey::CreateSameSite(SchemefulSite(url));
400
401 scoped_refptr<X509Certificate> cert(
402 ImportCertFromFile(GetTestCertsDirectory(), "wildcard.pem"));
403 ASSERT_TRUE(cert->VerifyNameMatch(origin.host()));
404 ASSERT_TRUE(cert->VerifyNameMatch(proxy1_origin.host()));
405 ASSERT_FALSE(cert->VerifyNameMatch(kDifferentHostname));
406
407 ProofVerifyDetailsChromium verify_details;
408 verify_details.cert_verify_result.verified_cert = cert;
409 verify_details.cert_verify_result.is_issued_by_known_root = true;
410 crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details);
411
412 auto proxy_chain = ProxyChain::ForIpProtection({
413 ProxyServer::FromSchemeHostAndPort(ProxyServer::SCHEME_QUIC,
414 proxy1_origin.host(), 443),
415 ProxyServer::FromSchemeHostAndPort(ProxyServer::SCHEME_QUIC,
416 proxy2_origin.host(), 443),
417 });
418 EXPECT_TRUE(proxy_chain.IsValid());
419
420 {
421 RequestBuilder builder(this);
422 builder.destination = origin;
423 builder.proxy_chain = proxy_chain;
424 builder.http_user_agent_settings = &http_user_agent_settings_;
425 builder.network_anonymization_key = endpoint_nak;
426 builder.url = url;
427
428 // Note: `builder` defaults to using the parameterized `version_` member,
429 // which we will assert here as a pre-condition for checking that the proxy
430 // session ignores this and uses RFCv1 instead.
431 ASSERT_EQ(builder.quic_version, version_);
432
433 EXPECT_EQ(ERR_IO_PENDING, builder.CallRequest());
434 // Drop the builder first, since it contains a raw pointer to the pool.
435 }
436
437 // Drop the QuicSessionPool, destroying all pending requests. This should not
438 // crash (see crbug.com/374777473).
439 factory_.reset();
440 }
441
TEST_P(QuicSessionPoolProxyJobTest,CreateProxySessionFails)442 TEST_P(QuicSessionPoolProxyJobTest, CreateProxySessionFails) {
443 Initialize();
444
445 GURL url("https://www.example.org/");
446 GURL proxy(kProxy1Url);
447 auto origin = url::SchemeHostPort(url);
448 auto proxy_origin = url::SchemeHostPort(proxy);
449
450 scoped_refptr<X509Certificate> cert(
451 ImportCertFromFile(GetTestCertsDirectory(), "wildcard.pem"));
452 ASSERT_TRUE(cert->VerifyNameMatch(origin.host()));
453 ASSERT_TRUE(cert->VerifyNameMatch(proxy_origin.host()));
454 ASSERT_FALSE(cert->VerifyNameMatch(kDifferentHostname));
455
456 ProofVerifyDetailsChromium verify_details;
457 verify_details.cert_verify_result.verified_cert = cert;
458 verify_details.cert_verify_result.is_issued_by_known_root = true;
459 crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details);
460
461 QuicSocketDataProvider socket_data(version_);
462 // Creation of underlying session fails immediately.
463 socket_data.AddWriteError("creation-fails", ERR_SOCKET_NOT_CONNECTED).Sync();
464 socket_factory_->AddSocketDataProvider(&socket_data);
465
466 auto proxy_chain = ProxyChain::ForIpProtection({
467 ProxyServer::FromSchemeHostAndPort(ProxyServer::SCHEME_QUIC,
468 proxy_origin.host(), 443),
469 });
470 EXPECT_TRUE(proxy_chain.IsValid());
471
472 RequestBuilder builder(this);
473 builder.destination = origin;
474 builder.proxy_chain = proxy_chain;
475 builder.http_user_agent_settings = &http_user_agent_settings_;
476 builder.url = url;
477 EXPECT_EQ(ERR_IO_PENDING, builder.CallRequest());
478 ASSERT_EQ(ERR_QUIC_HANDSHAKE_FAILED, callback_.WaitForResult());
479
480 EXPECT_TRUE(socket_data.AllDataConsumed());
481 }
482
TEST_P(QuicSessionPoolProxyJobTest,CreateSessionFails)483 TEST_P(QuicSessionPoolProxyJobTest, CreateSessionFails) {
484 Initialize();
485
486 GURL url("https://www.example.org/");
487 GURL proxy(kProxy1Url);
488 auto origin = url::SchemeHostPort(url);
489 auto proxy_origin = url::SchemeHostPort(proxy);
490
491 scoped_refptr<X509Certificate> cert(
492 ImportCertFromFile(GetTestCertsDirectory(), "wildcard.pem"));
493 ASSERT_TRUE(cert->VerifyNameMatch(origin.host()));
494 ASSERT_TRUE(cert->VerifyNameMatch(proxy_origin.host()));
495 ASSERT_FALSE(cert->VerifyNameMatch(kDifferentHostname));
496
497 ProofVerifyDetailsChromium verify_details;
498 verify_details.cert_verify_result.verified_cert = cert;
499 verify_details.cert_verify_result.is_issued_by_known_root = true;
500 crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details);
501
502 // QUIC proxies do not use priority header.
503 client_maker_.set_use_priority_header(false);
504
505 // Set up to accept socket creation, but not actually carry any packets.
506 QuicSocketDataProvider socket_data(version_);
507 socket_data.AddPause("nothing-happens");
508 socket_factory_->AddSocketDataProvider(&socket_data);
509
510 auto proxy_chain = ProxyChain::ForIpProtection({
511 ProxyServer::FromSchemeHostAndPort(ProxyServer::SCHEME_QUIC,
512 proxy_origin.host(), 443),
513 });
514 EXPECT_TRUE(proxy_chain.IsValid());
515
516 RequestBuilder builder(this);
517 builder.destination = origin;
518 builder.proxy_chain = proxy_chain;
519 builder.http_user_agent_settings = &http_user_agent_settings_;
520 builder.url = url;
521 EXPECT_EQ(ERR_IO_PENDING, builder.CallRequest());
522
523 // Set up the socket, but don't even finish writing initial settings.
524 RunUntilIdle();
525
526 // Oops, the session went away. This generates an error
527 // from `QuicSessionPool::CreateSessionOnProxyStream`.
528 factory_->CloseAllSessions(ERR_QUIC_HANDSHAKE_FAILED,
529 quic::QuicErrorCode::QUIC_INTERNAL_ERROR);
530
531 ASSERT_EQ(ERR_QUIC_HANDSHAKE_FAILED, callback_.WaitForResult());
532
533 // The direct connection was successful; the tunneled connection failed, but
534 // that is not measured by this metric.
535 histogram_tester.ExpectTotalCount(
536 "Net.HttpProxy.ConnectLatency.Http3.Quic.Success", 1);
537 histogram_tester.ExpectTotalCount(
538 "Net.HttpProxy.ConnectLatency.Http3.Quic.Error", 0);
539 }
540
541 // If the server in a proxied session provides an SPA, the client does not
542 // follow it.
TEST_P(QuicSessionPoolProxyJobTest,ProxiedQuicSessionWithServerPreferredAddressShouldNotMigrate)543 TEST_P(QuicSessionPoolProxyJobTest,
544 ProxiedQuicSessionWithServerPreferredAddressShouldNotMigrate) {
545 IPEndPoint server_preferred_address = IPEndPoint(IPAddress(1, 2, 3, 4), 123);
546 FLAGS_quic_enable_chaos_protection = false;
547 if (!quic_params_->allow_server_migration) {
548 quic_params_->connection_options.push_back(quic::kSPAD);
549 }
550 Initialize();
551
552 GURL url("https://www.example.org/");
553 GURL proxy(kProxy1Url);
554 auto origin = url::SchemeHostPort(url);
555 auto proxy_origin = url::SchemeHostPort(proxy);
556 auto nak = NetworkAnonymizationKey();
557
558 scoped_refptr<X509Certificate> cert(
559 ImportCertFromFile(GetTestCertsDirectory(), "wildcard.pem"));
560 ASSERT_TRUE(cert->VerifyNameMatch(origin.host()));
561 ASSERT_TRUE(cert->VerifyNameMatch(proxy_origin.host()));
562 ASSERT_FALSE(cert->VerifyNameMatch(kDifferentHostname));
563
564 ProofVerifyDetailsChromium verify_details;
565 verify_details.cert_verify_result.verified_cert = cert;
566 verify_details.cert_verify_result.is_issued_by_known_root = true;
567 crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details);
568
569 // Set the config for the _endpoint_ to send a preferred address.
570 quic::QuicConfig config;
571 config.SetIPv4AlternateServerAddressToSend(
572 ToQuicSocketAddress(server_preferred_address));
573 quic::test::QuicConfigPeer::SetPreferredAddressConnectionIdAndToken(
574 &config, kNewCID, quic::QuicUtils::GenerateStatelessResetToken(kNewCID));
575 crypto_client_stream_factory_.SetConfigForServerId(
576 quic::QuicServerId("www.example.org", 443), config);
577
578 // QUIC proxies do not use priority header.
579 client_maker_.set_use_priority_header(false);
580
581 // Use a separate packet maker for the connection to the endpoint.
582 QuicTestPacketMaker endpoint_maker(
583 version_,
584 quic::QuicUtils::CreateRandomConnectionId(context_.random_generator()),
585 context_.clock(), kDefaultServerHostName, quic::Perspective::IS_CLIENT,
586 /*client_priority_uses_incremental=*/true,
587 /*use_priority_header=*/true);
588
589 const uint64_t stream_id = GetNthClientInitiatedBidirectionalStreamId(0);
590 QuicSocketDataProvider socket_data(version_);
591 socket_data.AddWrite("initial-settings", ConstructInitialSettingsPacket(1))
592 .Sync();
593 socket_data
594 .AddWrite("connect-udp",
595 ConstructConnectUdpRequestPacket(
596 2, stream_id, proxy.host(),
597 "/.well-known/masque/udp/www.example.org/443/", false))
598 .Sync();
599 socket_data.AddRead("server-settings", ConstructServerSettingsPacket(3));
600 socket_data.AddRead("ok-response",
601 ConstructOkResponsePacket(4, stream_id, true));
602 socket_data.AddWrite("ack",
603 client_maker_.Packet(3).AddAckFrame(3, 4, 3).Build());
604 socket_data.AddWrite("datagram",
605 ConstructClientH3DatagramPacket(
606 4, stream_id, kConnectUdpContextId,
607 endpoint_maker.MakeInitialSettingsPacket(1)));
608 socket_factory_->AddSocketDataProvider(&socket_data);
609
610 // Create socket data which should never be consumed. A packet with a
611 // PathChallengeFrame written to this socket indicates that the client
612 // incorrectly tried to connect directly to the server at its alternate
613 // address.
614 QuicSocketDataProvider socket_data_alt_addr(version_);
615 socket_factory_->AddSocketDataProvider(&socket_data_alt_addr);
616
617 auto proxy_chain = ProxyChain::ForIpProtection({
618 ProxyServer::FromSchemeHostAndPort(ProxyServer::SCHEME_QUIC,
619 proxy_origin.host(), 443),
620 });
621 EXPECT_TRUE(proxy_chain.IsValid());
622
623 RequestBuilder builder(this);
624 builder.destination = origin;
625 builder.proxy_chain = proxy_chain;
626 builder.http_user_agent_settings = &http_user_agent_settings_;
627 builder.url = url;
628 EXPECT_EQ(ERR_IO_PENDING, builder.CallRequest());
629 ASSERT_EQ(OK, callback_.WaitForResult());
630 std::unique_ptr<HttpStream> stream = CreateStream(&builder.request);
631 EXPECT_TRUE(stream.get());
632 QuicChromiumClientSession* session =
633 GetActiveSession(origin, PRIVACY_MODE_DISABLED, nak, proxy_chain);
634 ASSERT_TRUE(session);
635
636 // Ensure the session finishes creating before proceeding.
637 RunUntilIdle();
638
639 // Double-check that no migration occurred, so the peer address is not the
640 // server's preferred address.
641 IPEndPoint peer_address = ToIPEndPoint(session->peer_address());
642 EXPECT_NE(peer_address, server_preferred_address);
643
644 EXPECT_TRUE(socket_data.AllDataConsumed());
645 }
646
647 } // namespace net::test
648