Add NetworkIsolationKey to QuicSessionKey.
And don't pool QUIC requests with different NetworkIsolationKeys when
kPartitionConnectionsByNetworkIsolationKey is enabled.
BUG=963480
Change-Id: I41a3bf3f4f0a24a5bfc264de46fc28e1f880d46d
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1629227
Commit-Queue: Matt Menke <[email protected]>
Reviewed-by: Zhongyi Shi <[email protected]>
Cr-Commit-Position: refs/heads/master@{#666102}
diff --git a/net/quic/quic_network_transaction_unittest.cc b/net/quic/quic_network_transaction_unittest.cc
index 40c5d9d8..712f11a 100644
--- a/net/quic/quic_network_transaction_unittest.cc
+++ b/net/quic/quic_network_transaction_unittest.cc
@@ -17,8 +17,10 @@
#include "base/strings/string_piece.h"
#include "base/strings/stringprintf.h"
#include "base/test/metrics/histogram_tester.h"
+#include "base/test/scoped_feature_list.h"
#include "net/base/chunked_upload_data_stream.h"
#include "net/base/completion_once_callback.h"
+#include "net/base/features.h"
#include "net/base/ip_endpoint.h"
#include "net/base/mock_network_change_notifier.h"
#include "net/base/test_completion_callback.h"
@@ -8718,5 +8720,405 @@
EXPECT_TRUE(mock_quic_data.AllWriteDataConsumed());
}
+// Test that NetworkIsolationKey is respected by QUIC connections, when
+// kPartitionConnectionsByNetworkIsolationKey is enabled.
+TEST_P(QuicNetworkTransactionTest, NetworkIsolation) {
+ NetworkIsolationKey network_isolation_key1(
+ url::Origin::Create(GURL("http://origin1/")));
+ NetworkIsolationKey network_isolation_key2(
+ url::Origin::Create(GURL("http://origin2/")));
+
+ session_params_.origins_to_force_quic_on.insert(
+ HostPortPair::FromString("mail.example.org:443"));
+
+ // Whether to use an H2 proxy. When false, uses HTTPS H2 requests without a
+ // proxy, when true, uses HTTP requests over an H2 proxy. It's unnecessary to
+ // test tunneled HTTPS over an H2 proxy, since that path sets up H2 sessions
+ // the same way as the HTTP over H2 proxy case.
+ for (bool use_proxy : {false, true}) {
+ SCOPED_TRACE(use_proxy);
+
+ if (use_proxy) {
+ proxy_resolution_service_ =
+ ProxyResolutionService::CreateFixedFromPacResult(
+ "QUIC mail.example.org:443", TRAFFIC_ANNOTATION_FOR_TESTS);
+ } else {
+ proxy_resolution_service_ = ProxyResolutionService::CreateDirect();
+ }
+
+ GURL url1;
+ GURL url2;
+ GURL url3;
+ if (use_proxy) {
+ url1 = GURL("http://mail.example.org/1");
+ url2 = GURL("http://mail.example.org/2");
+ url3 = GURL("http://mail.example.org/3");
+ } else {
+ url1 = GURL("https://mail.example.org/1");
+ url2 = GURL("https://mail.example.org/2");
+ url3 = GURL("https://mail.example.org/3");
+ }
+
+ for (bool partition_connections : {false, true}) {
+ SCOPED_TRACE(partition_connections);
+
+ base::test::ScopedFeatureList feature_list;
+ if (partition_connections) {
+ feature_list.InitAndEnableFeature(
+ features::kPartitionConnectionsByNetworkIsolationKey);
+ } else {
+ feature_list.InitAndDisableFeature(
+ features::kPartitionConnectionsByNetworkIsolationKey);
+ }
+
+ // Reads and writes for the unpartitioned case, where only one socket is
+ // used.
+
+ session_params_.origins_to_force_quic_on.insert(
+ HostPortPair::FromString("mail.example.org:443"));
+
+ MockQuicData unpartitioned_mock_quic_data;
+ quic::QuicStreamOffset request_header_offset = 0;
+ quic::QuicStreamOffset response_header_offset = 0;
+ QuicTestPacketMaker client_maker1(
+ version_,
+ quic::QuicUtils::CreateRandomConnectionId(&random_generator_),
+ &clock_, kDefaultServerHostName, quic::Perspective::IS_CLIENT,
+ client_headers_include_h2_stream_dependency_);
+ QuicTestPacketMaker server_maker1(
+ version_,
+ quic::QuicUtils::CreateRandomConnectionId(&random_generator_),
+ &clock_, kDefaultServerHostName, quic::Perspective::IS_SERVER, false);
+
+ unpartitioned_mock_quic_data.AddWrite(
+ SYNCHRONOUS,
+ client_maker1.MakeInitialSettingsPacket(1, &request_header_offset));
+
+ unpartitioned_mock_quic_data.AddWrite(
+ SYNCHRONOUS,
+ client_maker1.MakeRequestHeadersPacketWithOffsetTracking(
+ 2, GetNthClientInitiatedBidirectionalStreamId(0), true, true,
+ ConvertRequestPriorityToQuicPriority(DEFAULT_PRIORITY),
+ GetRequestHeaders("GET", url1.scheme(), "/1"), 0,
+ &request_header_offset));
+ unpartitioned_mock_quic_data.AddRead(
+ ASYNC,
+ server_maker1.MakeResponseHeadersPacketWithOffsetTracking(
+ 1, GetNthClientInitiatedBidirectionalStreamId(0), false, false,
+ GetResponseHeaders("200 OK"), &response_header_offset));
+ unpartitioned_mock_quic_data.AddRead(
+ ASYNC, server_maker1.MakeDataPacket(
+ 2, GetNthClientInitiatedBidirectionalStreamId(0), false,
+ true, 0, ConstructDataHeader(1) + "1"));
+ unpartitioned_mock_quic_data.AddWrite(
+ SYNCHRONOUS, ConstructClientAckPacket(3, 2, 1, 1));
+
+ unpartitioned_mock_quic_data.AddWrite(
+ SYNCHRONOUS,
+ client_maker1.MakeRequestHeadersPacketWithOffsetTracking(
+ 4, GetNthClientInitiatedBidirectionalStreamId(1), false, true,
+ ConvertRequestPriorityToQuicPriority(DEFAULT_PRIORITY),
+ GetRequestHeaders("GET", url2.scheme(), "/2"), 0,
+ &request_header_offset));
+ unpartitioned_mock_quic_data.AddRead(
+ ASYNC,
+ server_maker1.MakeResponseHeadersPacketWithOffsetTracking(
+ 3, GetNthClientInitiatedBidirectionalStreamId(1), false, false,
+ GetResponseHeaders("200 OK"), &response_header_offset));
+ unpartitioned_mock_quic_data.AddRead(
+ ASYNC, server_maker1.MakeDataPacket(
+ 4, GetNthClientInitiatedBidirectionalStreamId(1), false,
+ true, 0, ConstructDataHeader(1) + "2"));
+ unpartitioned_mock_quic_data.AddWrite(
+ SYNCHRONOUS, ConstructClientAckAndConnectionClosePacket(5, 4, 3, 1));
+
+ unpartitioned_mock_quic_data.AddWrite(
+ SYNCHRONOUS,
+ client_maker1.MakeRequestHeadersPacketWithOffsetTracking(
+ 6, GetNthClientInitiatedBidirectionalStreamId(2), false, true,
+ ConvertRequestPriorityToQuicPriority(DEFAULT_PRIORITY),
+ GetRequestHeaders("GET", url3.scheme(), "/3"), 0,
+ &request_header_offset));
+ unpartitioned_mock_quic_data.AddRead(
+ ASYNC,
+ server_maker1.MakeResponseHeadersPacketWithOffsetTracking(
+ 5, GetNthClientInitiatedBidirectionalStreamId(2), false, false,
+ GetResponseHeaders("200 OK"), &response_header_offset));
+ unpartitioned_mock_quic_data.AddRead(
+ ASYNC, server_maker1.MakeDataPacket(
+ 6, GetNthClientInitiatedBidirectionalStreamId(2), false,
+ true, 0, ConstructDataHeader(1) + "3"));
+ unpartitioned_mock_quic_data.AddWrite(
+ SYNCHRONOUS, ConstructClientAckAndConnectionClosePacket(7, 6, 5, 1));
+
+ unpartitioned_mock_quic_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
+
+ // Reads and writes for the partitioned case, where two sockets are used.
+
+ MockQuicData partitioned_mock_quic_data1;
+ request_header_offset = 0;
+ response_header_offset = 0;
+ QuicTestPacketMaker client_maker2(
+ version_,
+ quic::QuicUtils::CreateRandomConnectionId(&random_generator_),
+ &clock_, kDefaultServerHostName, quic::Perspective::IS_CLIENT,
+ client_headers_include_h2_stream_dependency_);
+ QuicTestPacketMaker server_maker2(
+ version_,
+ quic::QuicUtils::CreateRandomConnectionId(&random_generator_),
+ &clock_, kDefaultServerHostName, quic::Perspective::IS_SERVER, false);
+
+ partitioned_mock_quic_data1.AddWrite(
+ SYNCHRONOUS,
+ client_maker2.MakeInitialSettingsPacket(1, &request_header_offset));
+
+ partitioned_mock_quic_data1.AddWrite(
+ SYNCHRONOUS,
+ client_maker2.MakeRequestHeadersPacketWithOffsetTracking(
+ 2, GetNthClientInitiatedBidirectionalStreamId(0), true, true,
+ ConvertRequestPriorityToQuicPriority(DEFAULT_PRIORITY),
+ GetRequestHeaders("GET", url1.scheme(), "/1"), 0,
+ &request_header_offset));
+ partitioned_mock_quic_data1.AddRead(
+ ASYNC,
+ server_maker2.MakeResponseHeadersPacketWithOffsetTracking(
+ 1, GetNthClientInitiatedBidirectionalStreamId(0), false, false,
+ GetResponseHeaders("200 OK"), &response_header_offset));
+ partitioned_mock_quic_data1.AddRead(
+ ASYNC, server_maker2.MakeDataPacket(
+ 2, GetNthClientInitiatedBidirectionalStreamId(0), false,
+ true, 0, ConstructDataHeader(1) + "1"));
+ partitioned_mock_quic_data1.AddWrite(
+ SYNCHRONOUS, client_maker2.MakeAckPacket(3, 2, 1, 1, true));
+
+ partitioned_mock_quic_data1.AddWrite(
+ SYNCHRONOUS,
+ client_maker2.MakeRequestHeadersPacketWithOffsetTracking(
+ 4, GetNthClientInitiatedBidirectionalStreamId(1), false, true,
+ ConvertRequestPriorityToQuicPriority(DEFAULT_PRIORITY),
+ GetRequestHeaders("GET", url3.scheme(), "/3"), 0,
+ &request_header_offset));
+ partitioned_mock_quic_data1.AddRead(
+ ASYNC,
+ server_maker2.MakeResponseHeadersPacketWithOffsetTracking(
+ 3, GetNthClientInitiatedBidirectionalStreamId(1), false, false,
+ GetResponseHeaders("200 OK"), &response_header_offset));
+ partitioned_mock_quic_data1.AddRead(
+ ASYNC, server_maker2.MakeDataPacket(
+ 4, GetNthClientInitiatedBidirectionalStreamId(1), false,
+ true, 0, ConstructDataHeader(1) + "3"));
+ partitioned_mock_quic_data1.AddWrite(
+ SYNCHRONOUS, client_maker2.MakeAckPacket(5, 4, 3, 1, true));
+
+ partitioned_mock_quic_data1.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
+
+ MockQuicData partitioned_mock_quic_data2;
+ request_header_offset = 0;
+ response_header_offset = 0;
+ QuicTestPacketMaker client_maker3(
+ version_,
+ quic::QuicUtils::CreateRandomConnectionId(&random_generator_),
+ &clock_, kDefaultServerHostName, quic::Perspective::IS_CLIENT,
+ client_headers_include_h2_stream_dependency_);
+ QuicTestPacketMaker server_maker3(
+ version_,
+ quic::QuicUtils::CreateRandomConnectionId(&random_generator_),
+ &clock_, kDefaultServerHostName, quic::Perspective::IS_SERVER, false);
+
+ partitioned_mock_quic_data2.AddWrite(
+ SYNCHRONOUS,
+ client_maker3.MakeInitialSettingsPacket(1, &request_header_offset));
+
+ partitioned_mock_quic_data2.AddWrite(
+ SYNCHRONOUS,
+ client_maker3.MakeRequestHeadersPacketWithOffsetTracking(
+ 2, GetNthClientInitiatedBidirectionalStreamId(0), true, true,
+ ConvertRequestPriorityToQuicPriority(DEFAULT_PRIORITY),
+ GetRequestHeaders("GET", url2.scheme(), "/2"), 0,
+ &request_header_offset));
+ partitioned_mock_quic_data2.AddRead(
+ ASYNC,
+ server_maker3.MakeResponseHeadersPacketWithOffsetTracking(
+ 1, GetNthClientInitiatedBidirectionalStreamId(0), false, false,
+ GetResponseHeaders("200 OK"), &response_header_offset));
+ partitioned_mock_quic_data2.AddRead(
+ ASYNC, server_maker3.MakeDataPacket(
+ 2, GetNthClientInitiatedBidirectionalStreamId(0), false,
+ true, 0, ConstructDataHeader(1) + "2"));
+ partitioned_mock_quic_data2.AddWrite(
+ SYNCHRONOUS, client_maker3.MakeAckPacket(3, 2, 1, 1, true));
+
+ partitioned_mock_quic_data2.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
+
+ if (partition_connections) {
+ partitioned_mock_quic_data1.AddSocketDataToFactory(&socket_factory_);
+ partitioned_mock_quic_data2.AddSocketDataToFactory(&socket_factory_);
+ } else {
+ unpartitioned_mock_quic_data.AddSocketDataToFactory(&socket_factory_);
+ }
+
+ CreateSession();
+
+ TestCompletionCallback callback;
+ HttpRequestInfo request1;
+ request1.method = "GET";
+ request1.url = GURL(url1);
+ request1.traffic_annotation =
+ net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS);
+ request1.network_isolation_key = network_isolation_key1;
+ HttpNetworkTransaction trans1(LOWEST, session_.get());
+ int rv = trans1.Start(&request1, callback.callback(), NetLogWithSource());
+ EXPECT_THAT(callback.GetResult(rv), IsOk());
+ std::string response_data1;
+ EXPECT_THAT(ReadTransaction(&trans1, &response_data1), IsOk());
+ EXPECT_EQ("1", response_data1);
+
+ HttpRequestInfo request2;
+ request2.method = "GET";
+ request2.url = GURL(url2);
+ request2.traffic_annotation =
+ net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS);
+ request2.network_isolation_key = network_isolation_key2;
+ HttpNetworkTransaction trans2(LOWEST, session_.get());
+ rv = trans2.Start(&request2, callback.callback(), NetLogWithSource());
+ EXPECT_THAT(callback.GetResult(rv), IsOk());
+ std::string response_data2;
+ EXPECT_THAT(ReadTransaction(&trans2, &response_data2), IsOk());
+ EXPECT_EQ("2", response_data2);
+
+ HttpRequestInfo request3;
+ request3.method = "GET";
+ request3.url = GURL(url3);
+ request3.traffic_annotation =
+ net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS);
+ request3.network_isolation_key = network_isolation_key1;
+ HttpNetworkTransaction trans3(LOWEST, session_.get());
+ rv = trans3.Start(&request3, callback.callback(), NetLogWithSource());
+ EXPECT_THAT(callback.GetResult(rv), IsOk());
+ std::string response_data3;
+ EXPECT_THAT(ReadTransaction(&trans3, &response_data3), IsOk());
+ EXPECT_EQ("3", response_data3);
+
+ if (partition_connections) {
+ EXPECT_TRUE(partitioned_mock_quic_data1.AllReadDataConsumed());
+ EXPECT_TRUE(partitioned_mock_quic_data1.AllWriteDataConsumed());
+ EXPECT_TRUE(partitioned_mock_quic_data2.AllReadDataConsumed());
+ EXPECT_TRUE(partitioned_mock_quic_data2.AllWriteDataConsumed());
+ } else {
+ EXPECT_TRUE(unpartitioned_mock_quic_data.AllReadDataConsumed());
+ EXPECT_TRUE(unpartitioned_mock_quic_data.AllWriteDataConsumed());
+ }
+ }
+ }
+}
+
+// Test that two requests to the same origin over QUIC tunnels use different
+// QUIC sessions if their NetworkIsolationKeys don't match, and
+// kPartitionConnectionsByNetworkIsolationKey is enabled.
+TEST_P(QuicNetworkTransactionTest, NetworkIsolationTunnel) {
+ base::test::ScopedFeatureList feature_list;
+ feature_list.InitAndEnableFeature(
+ features::kPartitionConnectionsByNetworkIsolationKey);
+
+ session_params_.enable_quic = true;
+ session_params_.enable_quic_proxies_for_https_urls = true;
+ proxy_resolution_service_ = ProxyResolutionService::CreateFixedFromPacResult(
+ "QUIC proxy.example.org:70", TRAFFIC_ANNOTATION_FOR_TESTS);
+
+ const char kGetRequest[] =
+ "GET / HTTP/1.1\r\n"
+ "Host: mail.example.org\r\n"
+ "Connection: keep-alive\r\n\r\n";
+ const char kGetResponse[] =
+ "HTTP/1.1 200 OK\r\n"
+ "Content-Length: 10\r\n\r\n";
+
+ MockQuicData mock_quic_data[2];
+
+ for (int index : {0, 1}) {
+ QuicTestPacketMaker client_maker(
+ version_, quic::QuicUtils::CreateRandomConnectionId(&random_generator_),
+ &clock_, kDefaultServerHostName, quic::Perspective::IS_CLIENT,
+ client_headers_include_h2_stream_dependency_);
+ QuicTestPacketMaker server_maker(
+ version_, quic::QuicUtils::CreateRandomConnectionId(&random_generator_),
+ &clock_, kDefaultServerHostName, quic::Perspective::IS_SERVER, false);
+
+ quic::QuicStreamOffset header_stream_offset = 0;
+ mock_quic_data[index].AddWrite(
+ SYNCHRONOUS,
+ client_maker.MakeInitialSettingsPacket(1, &header_stream_offset));
+
+ mock_quic_data[index].AddWrite(
+ SYNCHRONOUS,
+ client_maker.MakeRequestHeadersPacketWithOffsetTracking(
+ 2, GetNthClientInitiatedBidirectionalStreamId(0), true, false,
+ ConvertRequestPriorityToQuicPriority(
+ HttpProxyConnectJob::kH2QuicTunnelPriority),
+ ConnectRequestHeaders("mail.example.org:443"), 0,
+ &header_stream_offset));
+ mock_quic_data[index].AddRead(
+ ASYNC, server_maker.MakeResponseHeadersPacketWithOffsetTracking(
+ 1, GetNthClientInitiatedBidirectionalStreamId(0), false,
+ false, GetResponseHeaders("200 OK"), nullptr));
+
+ std::string header = ConstructDataHeader(strlen(kGetRequest));
+ if (version_.transport_version != quic::QUIC_VERSION_99) {
+ mock_quic_data[index].AddWrite(
+ SYNCHRONOUS,
+ client_maker.MakeAckAndDataPacket(
+ 3, false, GetNthClientInitiatedBidirectionalStreamId(0), 1, 1, 1,
+ false, 0, quic::QuicStringPiece(kGetRequest)));
+ } else {
+ mock_quic_data[index].AddWrite(
+ SYNCHRONOUS,
+ client_maker.MakeAckAndMultipleDataFramesPacket(
+ 3, false, GetNthClientInitiatedBidirectionalStreamId(0), 1, 1, 1,
+ false, 0, {header, std::string(kGetRequest)}));
+ }
+
+ std::string header2 = ConstructDataHeader(strlen(kGetResponse));
+ mock_quic_data[index].AddRead(
+ ASYNC, server_maker.MakeDataPacket(
+ 2, GetNthClientInitiatedBidirectionalStreamId(0), false,
+ false, 0, header2 + std::string(kGetResponse)));
+ mock_quic_data[index].AddRead(
+ SYNCHRONOUS, server_maker.MakeDataPacket(
+ 3, GetNthClientInitiatedBidirectionalStreamId(0),
+ false, false, strlen(kGetResponse) + header2.length(),
+ ConstructDataHeader(10) + std::string("0123456789")));
+ mock_quic_data[index].AddWrite(
+ SYNCHRONOUS, client_maker.MakeAckPacket(4, 3, 2, 1, true));
+ mock_quic_data[index].AddRead(SYNCHRONOUS,
+ ERR_IO_PENDING); // No more data to read
+
+ mock_quic_data[index].AddSocketDataToFactory(&socket_factory_);
+ }
+
+ socket_factory_.AddSSLSocketDataProvider(&ssl_data_);
+ SSLSocketDataProvider ssl_data2(ASYNC, OK);
+ socket_factory_.AddSSLSocketDataProvider(&ssl_data2);
+
+ CreateSession();
+
+ request_.url = GURL("https://mail.example.org/");
+ AddQuicAlternateProtocolMapping(MockCryptoClientStream::CONFIRM_HANDSHAKE);
+ HttpNetworkTransaction trans(DEFAULT_PRIORITY, session_.get());
+ RunTransaction(&trans);
+ CheckResponseData(&trans, "0123456789");
+
+ HttpRequestInfo request2;
+ request_.network_isolation_key =
+ NetworkIsolationKey(url::Origin::Create(GURL("http://origin1/")));
+ HttpNetworkTransaction trans2(DEFAULT_PRIORITY, session_.get());
+ RunTransaction(&trans2);
+ CheckResponseData(&trans2, "0123456789");
+
+ EXPECT_TRUE(mock_quic_data[0].AllReadDataConsumed());
+ EXPECT_TRUE(mock_quic_data[0].AllWriteDataConsumed());
+ EXPECT_TRUE(mock_quic_data[1].AllReadDataConsumed());
+ EXPECT_TRUE(mock_quic_data[1].AllWriteDataConsumed());
+}
+
} // namespace test
} // namespace net