• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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