Net: Plumb CNAME aliases to URLResponseHead to expose to ad-tagging.
The overall project aims to expose CNAME aliases to the
SubresourceFilter to aid in ad tagging and blocking.
A previous CL added an accessor to net::HttpBasicState as well as one
to net::HttpStream.
This change continues the plumbing by adding a field to
net::HttpResponseInfo to store the alias list in the HttpCache and the
code to net::HttpNetworkTransaction and net::HttpCache::Transaction to
save the aliases to this field.
We moreover add the code to network::URLLoader to set the field
`dns_aliases` in network::mojom::URLResponseHead.
Relevant tests:
net:net_unittests
out/Default/net_unittests --gtest_filter=*Dns*Alias*
services/network:tests
out/Default/services_unittests --gtest_filter=*Dns*Alias*
Bug: 1151047
Change-Id: I9adebfcc5fa811ece4d55e70f3d624d495ca6d1c
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2569956
Reviewed-by: Matt Menke <[email protected]>
Reviewed-by: Maksim Orlovich <[email protected]>
Commit-Queue: Cammie Smith Barnes <[email protected]>
Cr-Commit-Position: refs/heads/master@{#839172}
diff --git a/net/http/http_cache_lookup_manager_unittest.cc b/net/http/http_cache_lookup_manager_unittest.cc
index 55302912..21162a33 100644
--- a/net/http/http_cache_lookup_manager_unittest.cc
+++ b/net/http/http_cache_lookup_manager_unittest.cc
@@ -63,6 +63,7 @@
"Last-Modified: Wed, 28 Nov 2007 00:40:09 GMT\n",
base::Time(),
"<html><body>Google Blah Blah</body></html>",
+ {},
TEST_MODE_NORMAL,
nullptr,
nullptr,
diff --git a/net/http/http_cache_transaction.cc b/net/http/http_cache_transaction.cc
index 27f9663..bb0b938 100644
--- a/net/http/http_cache_transaction.cc
+++ b/net/http/http_cache_transaction.cc
@@ -1889,6 +1889,7 @@
response_.unused_since_prefetch = new_response_->unused_since_prefetch;
response_.restricted_prefetch = new_response_->restricted_prefetch;
response_.ssl_info = new_response_->ssl_info;
+ response_.dns_aliases = new_response_->dns_aliases;
if (new_response_->vary_data.is_valid()) {
response_.vary_data = new_response_->vary_data;
} else if (response_.vary_data.is_valid()) {
diff --git a/net/http/http_cache_unittest.cc b/net/http/http_cache_unittest.cc
index d20774c..0c0a7c4 100644
--- a/net/http/http_cache_unittest.cc
+++ b/net/http/http_cache_unittest.cc
@@ -381,6 +381,7 @@
"Cache-Control: max-age=10000\n",
base::Time(),
"<html><body>Google Blah Blah</body></html>",
+ {},
TEST_MODE_SYNC_NET_START,
&FastTransactionServer::FastNoStoreHandler,
nullptr,
@@ -580,6 +581,7 @@
"Content-Length: 10\n",
base::Time(),
"rg: 40-49 ",
+ {},
TEST_MODE_NORMAL,
&RangeTransactionServer::RangeHandler,
nullptr,
@@ -13016,4 +13018,78 @@
Field(&Entry::value_uint64, Gt(0UL)))));
}
+TEST_F(HttpCacheTest, DnsAliasesNoRevalidation) {
+ MockHttpCache cache;
+ HttpResponseInfo response;
+ ScopedMockTransaction transaction(kSimpleGET_Transaction);
+ transaction.dns_aliases = {"alias1", "alias2"};
+
+ RunTransactionTestWithResponseInfo(cache.http_cache(), transaction,
+ &response);
+ EXPECT_FALSE(response.was_cached);
+ EXPECT_THAT(response.dns_aliases, testing::ElementsAre("alias1", "alias2"));
+
+ // The second request should be cached, and the response used without
+ // revalidation. Set the transaction alias list to empty to verify that the
+ // cached aliases are being used.
+ transaction.dns_aliases = {};
+ RunTransactionTestWithResponseInfo(cache.http_cache(), transaction,
+ &response);
+ EXPECT_TRUE(response.was_cached);
+ EXPECT_THAT(response.dns_aliases, testing::ElementsAre("alias1", "alias2"));
+}
+
+TEST_F(HttpCacheTest, NoDnsAliasesNoRevalidation) {
+ MockHttpCache cache;
+ HttpResponseInfo response;
+ ScopedMockTransaction transaction(kSimpleGET_Transaction);
+ transaction.dns_aliases = {};
+
+ RunTransactionTestWithResponseInfo(cache.http_cache(), transaction,
+ &response);
+ EXPECT_FALSE(response.was_cached);
+ EXPECT_TRUE(response.dns_aliases.empty());
+
+ // The second request should be cached, and the response used without
+ // revalidation. Set the transaction alias list to nonempty to verify that the
+ // cached aliases are being used.
+ transaction.dns_aliases = {"alias"};
+ RunTransactionTestWithResponseInfo(cache.http_cache(), transaction,
+ &response);
+ EXPECT_TRUE(response.was_cached);
+ EXPECT_TRUE(response.dns_aliases.empty());
+}
+
+TEST_F(HttpCacheTest, DnsAliasesRevalidation) {
+ MockHttpCache cache;
+ HttpResponseInfo response;
+ ScopedMockTransaction transaction(kTypicalGET_Transaction);
+ transaction.response_headers =
+ "Date: Wed, 28 Nov 2007 09:40:09 GMT\n"
+ "Last-Modified: Wed, 28 Nov 2007 00:40:09 GMT\n"
+ "Cache-Control: max-age=0\n";
+ transaction.dns_aliases = {"alias1", "alias2"};
+
+ RunTransactionTestWithResponseInfo(cache.http_cache(), transaction,
+ &response);
+ EXPECT_FALSE(response.was_cached);
+ EXPECT_THAT(response.dns_aliases, testing::ElementsAre("alias1", "alias2"));
+
+ // On the second request, the cache should be revalidated. Change the aliases
+ // to be sure that the new aliases are being used, and have the response be
+ // cached for next time.
+ transaction.response_headers = "Cache-Control: max-age=10000\n";
+ transaction.dns_aliases = {"alias3", "alias4"};
+ RunTransactionTestWithResponseInfo(cache.http_cache(), transaction,
+ &response);
+ EXPECT_FALSE(response.was_cached);
+ EXPECT_THAT(response.dns_aliases, testing::ElementsAre("alias3", "alias4"));
+
+ transaction.dns_aliases = {"alias5", "alias6"};
+ RunTransactionTestWithResponseInfo(cache.http_cache(), transaction,
+ &response);
+ EXPECT_TRUE(response.was_cached);
+ EXPECT_THAT(response.dns_aliases, testing::ElementsAre("alias3", "alias4"));
+}
+
} // namespace net
diff --git a/net/http/http_network_transaction.cc b/net/http/http_network_transaction.cc
index e526512..1a624bf 100644
--- a/net/http/http_network_transaction.cc
+++ b/net/http/http_network_transaction.cc
@@ -565,6 +565,7 @@
response_.alpn_negotiated_protocol =
NextProtoToString(stream_request_->negotiated_protocol());
response_.was_fetched_via_spdy = stream_request_->using_spdy();
+ response_.dns_aliases = stream_->GetDnsAliases();
SetProxyInfoInReponse(used_proxy_info, &response_);
OnIOComplete(OK);
}
diff --git a/net/http/http_network_transaction_unittest.cc b/net/http/http_network_transaction_unittest.cc
index df4fda9..29a5c811 100644
--- a/net/http/http_network_transaction_unittest.cc
+++ b/net/http/http_network_transaction_unittest.cc
@@ -6416,6 +6416,9 @@
EXPECT_EQ(100, response->headers->GetContentLength());
EXPECT_TRUE(HttpVersion(1, 1) == response->headers->GetHttpVersion());
+ // DNS aliases should be empty when using a proxy.
+ EXPECT_TRUE(response->dns_aliases.empty());
+
TransportInfo expected_transport;
expected_transport.type = TransportType::kProxied;
expected_transport.endpoint = IPEndPoint(IPAddress::IPv4Localhost(), 70);
@@ -6484,6 +6487,9 @@
ASSERT_TRUE(response->headers);
EXPECT_EQ("HTTP/1.1 200", response->headers->GetStatusLine());
+ // DNS aliases should be empty when using a proxy.
+ EXPECT_TRUE(response->dns_aliases.empty());
+
TransportInfo expected_transport;
expected_transport.type = TransportType::kProxied;
expected_transport.endpoint = IPEndPoint(IPAddress::IPv4Localhost(), 70);
@@ -23401,4 +23407,120 @@
trans_post_auth_bar.reset();
}
+TEST_F(HttpNetworkTransactionTest, RequestWithDnsAliases) {
+ // Create a request.
+ HttpRequestInfo request;
+ request.method = "GET";
+ request.url = GURL("http://www.example.org/");
+ request.traffic_annotation =
+ net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS);
+
+ // Add a rule with DNS aliases to the host resolver.
+ std::vector<std::string> aliases({"alias1", "alias2", "www.example.org"});
+ auto* rules = session_deps_.host_resolver->rules();
+ rules->AddIPLiteralRuleWithDnsAliases("www.example.org", "127.0.0.1",
+ std::move(aliases));
+ session_deps_.host_resolver->set_rules(rules);
+
+ // Create a HttpNetworkSession.
+ std::unique_ptr<HttpNetworkSession> session(CreateSession(&session_deps_));
+
+ // Create a transaction.
+ HttpNetworkTransaction trans(DEFAULT_PRIORITY, session.get());
+
+ // Prepare the expected data to be written and read. The client should send
+ // the request below.
+ MockWrite data_writes[] = {
+ MockWrite("GET / HTTP/1.1\r\n"
+ "Host: www.example.org\r\n"
+ "Connection: keep-alive\r\n\r\n"),
+ };
+
+ // The server should respond with the following.
+ MockRead data_reads[] = {
+ MockRead("HTTP/1.0 200 OK\r\n"),
+ MockRead("Content-Type: text/html; charset=iso-8859-1\r\n"),
+ MockRead("Content-Length: 100\r\n\r\n"),
+ MockRead(SYNCHRONOUS, OK),
+ };
+
+ StaticSocketDataProvider data(data_reads, data_writes);
+ session_deps_.socket_factory->AddSocketDataProvider(&data);
+ TestCompletionCallback callback;
+
+ // Start the transaction.
+ int rv = trans.Start(&request, callback.callback(), NetLogWithSource());
+ EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
+
+ // Wait for completion.
+ rv = callback.WaitForResult();
+ EXPECT_THAT(rv, IsOk());
+
+ // Get the response info.
+ const HttpResponseInfo* response = trans.GetResponseInfo();
+
+ // Verify that the alias list was stored in the response info as expected.
+ ASSERT_TRUE(response);
+ EXPECT_THAT(response->dns_aliases,
+ testing::ElementsAre("alias1", "alias2", "www.example.org"));
+}
+
+TEST_F(HttpNetworkTransactionTest, RequestWithNoAdditionalDnsAliases) {
+ // Create a request.
+ HttpRequestInfo request;
+ request.method = "GET";
+ request.url = GURL("http://www.example.org/");
+ request.traffic_annotation =
+ net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS);
+
+ // Add a rule without DNS aliases to the host resolver. The parameter is an
+ // empty vector.
+ std::vector<std::string> aliases;
+ auto* rules = session_deps_.host_resolver->rules();
+ rules->AddIPLiteralRuleWithDnsAliases("www.example.org", "127.0.0.1",
+ std::move(aliases));
+ session_deps_.host_resolver->set_rules(rules);
+
+ // Create a HttpNetworkSession.
+ std::unique_ptr<HttpNetworkSession> session(CreateSession(&session_deps_));
+
+ // Create a transaction.
+ HttpNetworkTransaction trans(DEFAULT_PRIORITY, session.get());
+
+ // Prepare the expected data to be written and read. The client should send
+ // the request below.
+ MockWrite data_writes[] = {
+ MockWrite("GET / HTTP/1.1\r\n"
+ "Host: www.example.org\r\n"
+ "Connection: keep-alive\r\n\r\n"),
+ };
+
+ // The server should respond with the following.
+ MockRead data_reads[] = {
+ MockRead("HTTP/1.0 200 OK\r\n"),
+ MockRead("Content-Type: text/html; charset=iso-8859-1\r\n"),
+ MockRead("Content-Length: 100\r\n\r\n"),
+ MockRead(SYNCHRONOUS, OK),
+ };
+
+ StaticSocketDataProvider data(data_reads, data_writes);
+ session_deps_.socket_factory->AddSocketDataProvider(&data);
+ TestCompletionCallback callback;
+
+ // Start the transaction.
+ int rv = trans.Start(&request, callback.callback(), NetLogWithSource());
+ EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
+
+ // Wait for completion.
+ rv = callback.WaitForResult();
+ EXPECT_THAT(rv, IsOk());
+
+ // Get the response info.
+ const HttpResponseInfo* response = trans.GetResponseInfo();
+
+ // Verify that the alias list was stored in the response info as expected.
+ ASSERT_TRUE(response);
+ EXPECT_THAT(response->dns_aliases, testing::ElementsAre("www.example.org"));
+}
+
} // namespace net
diff --git a/net/http/http_response_info.cc b/net/http/http_response_info.cc
index 711667b..78950484 100644
--- a/net/http/http_response_info.cc
+++ b/net/http/http_response_info.cc
@@ -114,6 +114,9 @@
// restricted in some way.
RESPONSE_INFO_RESTRICTED_PREFETCH = 1 << 26,
+ // This bit is set if the response has a nonempty `dns_aliases` entry.
+ RESPONSE_INFO_HAS_DNS_ALIASES = 1 << 27,
+
// TODO(darin): Add other bits to indicate alternate request methods.
// For now, we don't support storing those.
};
@@ -368,6 +371,20 @@
base::checked_cast<uint16_t>(peer_signature_algorithm);
}
+ // Read DNS aliases.
+ if (flags & RESPONSE_INFO_HAS_DNS_ALIASES) {
+ int num_aliases;
+ if (!iter.ReadInt(&num_aliases))
+ return false;
+
+ std::string alias;
+ for (int i = 0; i < num_aliases; i++) {
+ if (!iter.ReadString(&alias))
+ return false;
+ dns_aliases.push_back(alias);
+ }
+ }
+
return true;
}
@@ -409,6 +426,8 @@
flags |= RESPONSE_INFO_PKP_BYPASSED;
if (!stale_revalidate_timeout.is_null())
flags |= RESPONSE_INFO_HAS_STALENESS;
+ if (!dns_aliases.empty())
+ flags |= RESPONSE_INFO_HAS_DNS_ALIASES;
pickle->WriteInt(flags);
pickle->WriteInt64(request_time.ToInternalValue());
@@ -457,6 +476,12 @@
if (ssl_info.is_valid() && ssl_info.peer_signature_algorithm != 0)
pickle->WriteInt(ssl_info.peer_signature_algorithm);
+
+ if (!dns_aliases.empty()) {
+ pickle->WriteInt(dns_aliases.size());
+ for (const auto& alias : dns_aliases)
+ pickle->WriteString(alias);
+ }
}
bool HttpResponseInfo::DidUseQuic() const {
diff --git a/net/http/http_response_info.h b/net/http/http_response_info.h
index a056f4d..5ef989d 100644
--- a/net/http/http_response_info.h
+++ b/net/http/http_response_info.h
@@ -241,6 +241,11 @@
// The "Vary" header data for this response.
HttpVaryData vary_data;
+ // Any DNS aliases for the remote endpoint. The alias chain order is
+ // preserved in reverse, from canonical name (i.e. address record name)
+ // through to query name.
+ std::vector<std::string> dns_aliases;
+
static std::string ConnectionInfoToString(ConnectionInfo connection_info);
};
diff --git a/net/http/http_response_info_unittest.cc b/net/http/http_response_info_unittest.cc
index af3eb0c..43c8770a 100644
--- a/net/http/http_response_info_unittest.cc
+++ b/net/http/http_response_info_unittest.cc
@@ -12,6 +12,7 @@
#include "net/test/cert_test_util.h"
#include "net/test/ct_test_util.h"
#include "net/test/test_data_directory.h"
+#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace net {
@@ -225,6 +226,23 @@
restored_ssl3_response_info.InitFromPickle(ssl3_pickle, &truncated));
}
+// Test that `dns_aliases` is preserved.
+TEST_F(HttpResponseInfoTest, DnsAliases) {
+ response_info_.dns_aliases = {"alias1", "alias2", "alias3"};
+ net::HttpResponseInfo restored_response_info;
+ PickleAndRestore(response_info_, &restored_response_info);
+ EXPECT_THAT(restored_response_info.dns_aliases,
+ testing::ElementsAre("alias1", "alias2", "alias3"));
+}
+
+// Test that an empty `dns_aliases` is preserved and doesn't throw an error.
+TEST_F(HttpResponseInfoTest, EmptyDnsAliases) {
+ response_info_.dns_aliases = {};
+ net::HttpResponseInfo restored_response_info;
+ PickleAndRestore(response_info_, &restored_response_info);
+ EXPECT_TRUE(restored_response_info.dns_aliases.empty());
+}
+
} // namespace
} // namespace net
diff --git a/net/http/http_transaction_test_util.cc b/net/http/http_transaction_test_util.cc
index 01f0271e8..47d8b84 100644
--- a/net/http/http_transaction_test_util.cc
+++ b/net/http/http_transaction_test_util.cc
@@ -63,6 +63,7 @@
"Cache-Control: max-age=10000\n",
base::Time(),
"<html><body>Google Blah Blah</body></html>",
+ {},
TEST_MODE_NORMAL,
nullptr,
nullptr,
@@ -84,6 +85,7 @@
"",
base::Time(),
"<html><body>Google Blah Blah</body></html>",
+ {},
TEST_MODE_NORMAL,
nullptr,
nullptr,
@@ -106,6 +108,7 @@
"Last-Modified: Wed, 28 Nov 2007 00:40:09 GMT\n",
base::Time(),
"<html><body>Google Blah Blah</body></html>",
+ {},
TEST_MODE_NORMAL,
nullptr,
nullptr,
@@ -128,6 +131,7 @@
"Etag: \"foopy\"\n",
base::Time(),
"<html><body>Google Blah Blah</body></html>",
+ {},
TEST_MODE_NORMAL,
nullptr,
nullptr,
@@ -149,6 +153,7 @@
"Cache-Control: max-age=10000\n",
base::Time(),
"<html><body>Google Blah Blah</body></html>",
+ {},
TEST_MODE_NORMAL,
nullptr,
nullptr,
@@ -515,6 +520,7 @@
response_.ssl_info.cert = t->cert;
response_.ssl_info.cert_status = t->cert_status;
response_.ssl_info.connection_status = t->ssl_connection_status;
+ response_.dns_aliases = t->dns_aliases;
data_ = resp_data;
content_length_ = response_.headers->GetContentLength();
diff --git a/net/http/http_transaction_test_util.h b/net/http/http_transaction_test_util.h
index aa7be5c3..4c4f7af 100644
--- a/net/http/http_transaction_test_util.h
+++ b/net/http/http_transaction_test_util.h
@@ -83,6 +83,10 @@
// If |response_time| is unspecified, the current time will be used.
base::Time response_time;
const char* data;
+ // Any aliases for the requested URL, as read from DNS records. The alias
+ // chain order is preserved in reverse, from canonical name (i.e. address
+ // record name) through to query name.
+ std::vector<std::string> dns_aliases;
int test_mode;
MockTransactionHandler handler;
MockTransactionReadHandler read_handler;
diff --git a/net/quic/quic_network_transaction_unittest.cc b/net/quic/quic_network_transaction_unittest.cc
index 9ed12bc..1eb2de4 100644
--- a/net/quic/quic_network_transaction_unittest.cc
+++ b/net/quic/quic_network_transaction_unittest.cc
@@ -956,6 +956,9 @@
CheckResponseData(&trans, expected);
if (used_proxy) {
EXPECT_TRUE(trans.GetResponseInfo()->proxy_server.is_quic());
+
+ // DNS aliases should be empty when using a proxy.
+ EXPECT_TRUE(trans.GetResponseInfo()->dns_aliases.empty());
} else {
EXPECT_TRUE(trans.GetResponseInfo()->proxy_server.is_direct());
}
@@ -7819,6 +7822,9 @@
CheckResponseData(&trans, "0123456789");
EXPECT_TRUE(trans.GetResponseInfo()->proxy_server.is_quic());
+ // DNS aliases should be empty when using a proxy.
+ EXPECT_TRUE(trans.GetResponseInfo()->dns_aliases.empty());
+
// Causes MockSSLClientSocket to disconnect, which causes the underlying QUIC
// proxy socket to disconnect.
NetworkChangeNotifier::NotifyObserversOfIPAddressChangeForTests();
diff --git a/net/url_request/url_request_job_unittest.cc b/net/url_request/url_request_job_unittest.cc
index d4b985c..51942c4 100644
--- a/net/url_request/url_request_job_unittest.cc
+++ b/net/url_request/url_request_job_unittest.cc
@@ -87,6 +87,7 @@
transaction->response_headers = response_headers;
transaction->response_time = base::Time();
transaction->data = "hello";
+ transaction->dns_aliases = {};
transaction->test_mode = TEST_MODE_NORMAL;
transaction->handler = nullptr;
transaction->read_handler = nullptr;
@@ -113,6 +114,7 @@
"Content-Length: 30\n", // Intentionally wrong.
base::Time(),
"hello",
+ {},
TEST_MODE_NORMAL,
nullptr,
nullptr,
@@ -134,6 +136,7 @@
"Content-Length: +30\n", // Invalid
base::Time(),
"hello",
+ {},
TEST_MODE_NORMAL,
nullptr,
nullptr,
@@ -156,6 +159,7 @@
"Content-Length: 30\n", // Intentionally wrong.
base::Time(),
"",
+ {},
TEST_MODE_NORMAL,
&GZipServer,
nullptr,
@@ -178,6 +182,7 @@
"Content-Encoding: gzip\n",
base::Time(),
"",
+ {},
TEST_MODE_SLOW_READ,
&GZipHelloServer,
nullptr,
@@ -201,6 +206,7 @@
"Content-Length: 5\n",
base::Time(),
"hello",
+ {},
TEST_MODE_NORMAL,
nullptr,
nullptr,
@@ -222,6 +228,7 @@
"Content-Encoding: gzip\n",
base::Time(),
"",
+ {},
TEST_MODE_NORMAL,
nullptr,
nullptr,
@@ -244,6 +251,7 @@
"Content-Length: 21\n",
base::Time(),
"not a valid gzip body",
+ {},
TEST_MODE_NORMAL,
nullptr,
nullptr,
@@ -267,6 +275,7 @@
"Content-Length: 230\n", // Intentionally wrong.
base::Time(),
"",
+ {},
TEST_MODE_SLOW_READ,
&BrotliHelloServer,
nullptr,
diff --git a/net/url_request/url_request_unittest.cc b/net/url_request/url_request_unittest.cc
index 1560b089..a45042e5 100644
--- a/net/url_request/url_request_unittest.cc
+++ b/net/url_request/url_request_unittest.cc
@@ -101,6 +101,7 @@
#include "net/http/http_request_headers.h"
#include "net/http/http_response_headers.h"
#include "net/http/http_server_properties.h"
+#include "net/http/http_transaction_test_util.h"
#include "net/http/http_util.h"
#include "net/log/file_net_log_observer.h"
#include "net/log/net_log_event_type.h"
@@ -12390,4 +12391,54 @@
EXPECT_EQ("/auth-basic", r->auth_challenge_info()->path);
}
+class URLRequestDnsAliasTest : public TestWithTaskEnvironment {
+ protected:
+ URLRequestDnsAliasTest() : context_(true) {
+ context_.set_network_delegate(&network_delegate_);
+ context_.set_host_resolver(&host_resolver_);
+ context_.Init();
+ }
+
+ void SetUp() override { ASSERT_TRUE(test_server_.Start()); }
+
+ TestNetworkDelegate network_delegate_; // Must outlive URLRequest.
+ MockHostResolver host_resolver_;
+ TestURLRequestContext context_;
+ TestDelegate test_delegate_;
+ EmbeddedTestServer test_server_;
+};
+
+TEST_F(URLRequestDnsAliasTest, WithDnsAliases) {
+ GURL url(test_server_.GetURL("www.example.test", "/echo"));
+ std::vector<std::string> aliases({"alias1", "alias2", "host"});
+ host_resolver_.rules()->AddIPLiteralRuleWithDnsAliases(
+ "www.example.test", "127.0.0.1", std::move(aliases));
+
+ std::unique_ptr<URLRequest> request(context_.CreateRequest(
+ url, DEFAULT_PRIORITY, &test_delegate_, TRAFFIC_ANNOTATION_FOR_TESTS));
+
+ request->Start();
+
+ test_delegate_.RunUntilComplete();
+ EXPECT_THAT(test_delegate_.request_status(), IsOk());
+ EXPECT_THAT(request->response_info().dns_aliases,
+ testing::ElementsAre("alias1", "alias2", "host"));
+}
+
+TEST_F(URLRequestDnsAliasTest, NoAdditionalDnsAliases) {
+ GURL url(test_server_.GetURL("www.example.test", "/echo"));
+ host_resolver_.rules()->AddIPLiteralRuleWithDnsAliases(
+ "www.example.test", "127.0.0.1", {} /* dns_aliases */);
+
+ std::unique_ptr<URLRequest> request(context_.CreateRequest(
+ url, DEFAULT_PRIORITY, &test_delegate_, TRAFFIC_ANNOTATION_FOR_TESTS));
+
+ request->Start();
+
+ test_delegate_.RunUntilComplete();
+ EXPECT_THAT(test_delegate_.request_status(), IsOk());
+ EXPECT_THAT(request->response_info().dns_aliases,
+ testing::ElementsAre("www.example.test"));
+}
+
} // namespace net
diff --git a/services/network/url_loader.cc b/services/network/url_loader.cc
index 3f5b58a7..2f80592 100644
--- a/services/network/url_loader.cc
+++ b/services/network/url_loader.cc
@@ -145,6 +145,7 @@
response->auth_challenge_info = request->auth_challenge_info();
response->has_range_requested = request->extra_request_headers().HasHeader(
net::HttpRequestHeaders::kRange);
+ response->dns_aliases = request->response_info().dns_aliases;
}
// A subclass of net::UploadBytesElementReader which owns
diff --git a/services/network/url_loader_unittest.cc b/services/network/url_loader_unittest.cc
index 817637f8..8baa288 100644
--- a/services/network/url_loader_unittest.cc
+++ b/services/network/url_loader_unittest.cc
@@ -141,6 +141,10 @@
constexpr char kInsecureHost[] = "othersite.test";
+constexpr char kHostnameWithAliases[] = "www.example.test";
+
+constexpr char kHostnameWithoutAliases[] = "www.other.test";
+
static ResourceRequest CreateResourceRequest(const char* method,
const GURL& url) {
ResourceRequest request;
@@ -531,6 +535,8 @@
// the loopback address and will let us access |test_server_|.
scoped_refptr<net::RuleBasedHostResolverProc> mock_resolver_proc =
base::MakeRefCounted<net::RuleBasedHostResolverProc>(nullptr);
+ mock_resolver_proc->AddIPLiteralRuleWithDnsAliases(
+ kHostnameWithAliases, "127.0.0.1", {"alias1", "alias2", "host"});
mock_resolver_proc->AddRule("*", "127.0.0.1");
mock_host_resolver_ = std::make_unique<net::ScopedDefaultHostResolverProc>(
mock_resolver_proc.get());
@@ -6320,4 +6326,22 @@
EXPECT_FALSE(socket_data.socket());
}
+TEST_F(URLLoaderTest, WithDnsAliases) {
+ GURL url(test_server_.GetURL(kHostnameWithAliases, "/echo"));
+
+ EXPECT_EQ(net::OK, Load(url));
+ ASSERT_TRUE(client_.response_head());
+ EXPECT_THAT(client_.response_head()->dns_aliases,
+ testing::ElementsAre("alias1", "alias2", "host"));
+}
+
+TEST_F(URLLoaderTest, NoAdditionalDnsAliases) {
+ GURL url(test_server_.GetURL(kHostnameWithoutAliases, "/echo"));
+
+ EXPECT_EQ(net::OK, Load(url));
+ ASSERT_TRUE(client_.response_head());
+ EXPECT_THAT(client_.response_head()->dns_aliases,
+ testing::ElementsAre(kHostnameWithoutAliases));
+}
+
} // namespace network