diff options
author | yoav <yoav@yoav.ws> | 2015-07-28 23:43:34 -0700 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2015-07-29 06:44:10 +0000 |
commit | 9131644005a8e069c128512f5674e53fa4d651ba (patch) | |
tree | 6d81372c60f9d8e8cbdebc952ed6265289f909a7 | |
parent | 4eae9f8ae3a6366c43f61c0c6fd588bca7010bcd (diff) | |
download | chromium_src-9131644005a8e069c128512f5674e53fa4d651ba.zip chromium_src-9131644005a8e069c128512f5674e53fa4d651ba.tar.gz chromium_src-9131644005a8e069c128512f5674e53fa4d651ba.tar.bz2 |
Add cross origin to Blink-driven preconnect
In order to support <link rel=preconnect> for both anonymous and non-anonymous connection pools we need to add support for preconnect requests coming from Blink.
BUG=468005
Review URL: https://codereview.chromium.org/1131293004
Cr-Commit-Position: refs/heads/master@{#340854}
15 files changed, 160 insertions, 50 deletions
diff --git a/chrome/browser/net/preconnect.cc b/chrome/browser/net/preconnect.cc index dfa91a3..17673c3 100644 --- a/chrome/browser/net/preconnect.cc +++ b/chrome/browser/net/preconnect.cc @@ -8,6 +8,7 @@ #include "base/logging.h" #include "base/metrics/histogram.h" #include "content/public/browser/browser_thread.h" +#include "net/base/load_flags.h" #include "net/http/http_network_session.h" #include "net/http/http_request_info.h" #include "net/http/http_stream_factory.h" @@ -30,20 +31,18 @@ void PreconnectOnUIThread( net::URLRequestContextGetter* getter) { // Prewarm connection to Search URL. BrowserThread::PostTask( - BrowserThread::IO, - FROM_HERE, + BrowserThread::IO, FROM_HERE, base::Bind(&PreconnectOnIOThread, url, first_party_for_cookies, - motivation, count, make_scoped_refptr(getter))); + motivation, count, make_scoped_refptr(getter), true)); return; } - -void PreconnectOnIOThread( - const GURL& url, - const GURL& first_party_for_cookies, - UrlInfo::ResolutionMotivation motivation, - int count, - net::URLRequestContextGetter* getter) { +void PreconnectOnIOThread(const GURL& url, + const GURL& first_party_for_cookies, + UrlInfo::ResolutionMotivation motivation, + int count, + net::URLRequestContextGetter* getter, + bool allow_credentials) { if (!BrowserThread::CurrentlyOn(BrowserThread::IO)) { LOG(DFATAL) << "This must be run only on the IO thread."; return; @@ -71,6 +70,16 @@ void PreconnectOnIOThread( if (delegate->CanEnablePrivacyMode(url, first_party_for_cookies)) request_info.privacy_mode = net::PRIVACY_MODE_ENABLED; + // TODO(yoav): Fix this layering violation, since when credentials are not + // allowed we should turn on a flag indicating that, rather then turn on + // private mode, even if lower layers would treat both the same. + if (!allow_credentials) { + request_info.privacy_mode = net::PRIVACY_MODE_ENABLED; + request_info.load_flags = net::LOAD_DO_NOT_SEND_COOKIES | + net::LOAD_DO_NOT_SAVE_COOKIES | + net::LOAD_DO_NOT_SEND_AUTH_DATA; + } + // Translate the motivation from UrlRequest motivations to HttpRequest // motivations. switch (motivation) { diff --git a/chrome/browser/net/preconnect.h b/chrome/browser/net/preconnect.h index 6e8442c..72718c1 100644 --- a/chrome/browser/net/preconnect.h +++ b/chrome/browser/net/preconnect.h @@ -34,7 +34,8 @@ void PreconnectOnIOThread(const GURL& url, const GURL& first_party_for_cookies, UrlInfo::ResolutionMotivation motivation, int count, - net::URLRequestContextGetter* getter); + net::URLRequestContextGetter* getter, + bool allow_credentials); } // namespace chrome_browser_net diff --git a/chrome/browser/net/predictor.cc b/chrome/browser/net/predictor.cc index d18979a..c435a8c 100644 --- a/chrome/browser/net/predictor.cc +++ b/chrome/browser/net/predictor.cc @@ -87,6 +87,9 @@ const int Predictor::kMaxSpeculativeResolveQueueDelayMs = (kExpectedResolutionTimeMs * Predictor::kTypicalSpeculativeGroupSize) / Predictor::kMaxSpeculativeParallelResolves; +// The default value of the credentials flag when preconnecting. +static bool kAllowCredentialsOnPreconnectByDefault = true; + static int g_max_queueing_delay_ms = Predictor::kMaxSpeculativeResolveQueueDelayMs; static size_t g_max_parallel_resolves = @@ -265,8 +268,9 @@ void Predictor::AnticipateOmniboxUrl(const GURL& url, bool preconnectable) { return; // We've done a preconnect recently. last_omnibox_preconnect_ = now; const int kConnectionsNeeded = 1; - PreconnectUrl( - CanonicalizeUrl(url), GURL(), motivation, kConnectionsNeeded); + PreconnectUrl(CanonicalizeUrl(url), GURL(), motivation, + kAllowCredentialsOnPreconnectByDefault, + kConnectionsNeeded); return; // Skip pre-resolution, since we'll open a connection. } } else { @@ -305,8 +309,8 @@ void Predictor::PreconnectUrlAndSubresources(const GURL& url, UrlInfo::ResolutionMotivation motivation(UrlInfo::EARLY_LOAD_MOTIVATED); const int kConnectionsNeeded = 1; - PreconnectUrl(CanonicalizeUrl(url), first_party_for_cookies, - motivation, kConnectionsNeeded); + PreconnectUrl(CanonicalizeUrl(url), first_party_for_cookies, motivation, + kConnectionsNeeded, kAllowCredentialsOnPreconnectByDefault); PredictFrameSubresources(url.GetWithEmptyPath(), first_party_for_cookies); } @@ -840,19 +844,20 @@ void Predictor::SaveDnsPrefetchStateForNextStartupAndTrim( void Predictor::PreconnectUrl(const GURL& url, const GURL& first_party_for_cookies, UrlInfo::ResolutionMotivation motivation, + bool allow_credentials, int count) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI) || BrowserThread::CurrentlyOn(BrowserThread::IO)); if (BrowserThread::CurrentlyOn(BrowserThread::IO)) { - PreconnectUrlOnIOThread(url, first_party_for_cookies, motivation, count); + PreconnectUrlOnIOThread(url, first_party_for_cookies, motivation, + allow_credentials, count); } else { BrowserThread::PostTask( - BrowserThread::IO, - FROM_HERE, - base::Bind(&Predictor::PreconnectUrlOnIOThread, - base::Unretained(this), url, first_party_for_cookies, - motivation, count)); + BrowserThread::IO, FROM_HERE, + base::Bind(&Predictor::PreconnectUrlOnIOThread, base::Unretained(this), + url, first_party_for_cookies, motivation, allow_credentials, + count)); } } @@ -860,6 +865,7 @@ void Predictor::PreconnectUrlOnIOThread( const GURL& original_url, const GURL& first_party_for_cookies, UrlInfo::ResolutionMotivation motivation, + bool allow_credentials, int count) { // Skip the HSTS redirect. GURL url = GetHSTSRedirectOnIOThread(original_url); @@ -869,11 +875,8 @@ void Predictor::PreconnectUrlOnIOThread( url, first_party_for_cookies, motivation, count); } - PreconnectOnIOThread(url, - first_party_for_cookies, - motivation, - count, - url_request_context_getter_.get()); + PreconnectOnIOThread(url, first_party_for_cookies, motivation, count, + url_request_context_getter_.get(), allow_credentials); } void Predictor::PredictFrameSubresources(const GURL& url, @@ -941,7 +944,8 @@ void Predictor::PrepareFrameSubresources(const GURL& original_url, // provide a more carefully estimated preconnection count. if (preconnect_enabled_) { PreconnectUrlOnIOThread(url, first_party_for_cookies, - UrlInfo::SELF_REFERAL_MOTIVATED, 2); + UrlInfo::SELF_REFERAL_MOTIVATED, + kAllowCredentialsOnPreconnectByDefault, 2); } return; } @@ -966,7 +970,8 @@ void Predictor::PrepareFrameSubresources(const GURL& original_url, if (url.host() == future_url->first.host()) ++count; PreconnectUrlOnIOThread(future_url->first, first_party_for_cookies, - motivation, count); + motivation, + kAllowCredentialsOnPreconnectByDefault, count); } else if (connection_expectation > kDNSPreresolutionWorthyExpectedValue) { evalution = PRERESOLUTION; future_url->second.preresolution_increment(); diff --git a/chrome/browser/net/predictor.h b/chrome/browser/net/predictor.h index d1983e3..8601ff0 100644 --- a/chrome/browser/net/predictor.h +++ b/chrome/browser/net/predictor.h @@ -242,12 +242,16 @@ class Predictor { // May be called from either the IO or UI thread and will PostTask // to the IO thread if necessary. - void PreconnectUrl(const GURL& url, const GURL& first_party_for_cookies, - UrlInfo::ResolutionMotivation motivation, int count); + void PreconnectUrl(const GURL& url, + const GURL& first_party_for_cookies, + UrlInfo::ResolutionMotivation motivation, + bool allow_credentials, + int count); void PreconnectUrlOnIOThread(const GURL& url, const GURL& first_party_for_cookies, UrlInfo::ResolutionMotivation motivation, + bool allow_credentials, int count); // ------------- End IO thread methods. diff --git a/chrome/browser/net/predictor_browsertest.cc b/chrome/browser/net/predictor_browsertest.cc index c4ebc55..98cbab1 100644 --- a/chrome/browser/net/predictor_browsertest.cc +++ b/chrome/browser/net/predictor_browsertest.cc @@ -103,8 +103,16 @@ class ConnectionListener private: static uint16_t GetPort( const net::test_server::StreamListenSocket& connection) { + // Get the remote port of the peer, since the local port will always be the + // port the test server is listening on. This isn't strictly correct - it's + // possible for multiple peers to connect with the same remote port but + // different remote IPs - but the tests here assume that connections to the + // test server (running on localhost) will always come from localhost, and + // thus the peer port is all thats needed to distinguish two connections. + // This also would be problematic if the OS reused ports, but that's not + // something to worry about for these tests. net::IPEndPoint address; - EXPECT_EQ(net::OK, connection.GetLocalAddress(&address)); + EXPECT_EQ(net::OK, connection.GetPeerAddress(&address)); return address.port(); } @@ -350,7 +358,7 @@ IN_PROC_BROWSER_TEST_F(PredictorBrowserTest, MAYBE_DnsPrefetch) { // Tests that preconnect warms up a socket connection to a test server. // Note: This test uses a data URI to serve the preconnect hint, to make sure // that the network stack doesn't just re-use its connection to the test server. -IN_PROC_BROWSER_TEST_F(PredictorBrowserTest, Preconnect) { +IN_PROC_BROWSER_TEST_F(PredictorBrowserTest, PreconnectNonCORS) { GURL preconnect_url = embedded_test_server()->base_url(); std::string preconnect_content = "<link rel=\"preconnect\" href=\"" + preconnect_url.spec() + "\">"; @@ -364,7 +372,7 @@ IN_PROC_BROWSER_TEST_F(PredictorBrowserTest, Preconnect) { // and that that socket is later used when fetching a resource. // Note: This test uses a data URI to serve the preconnect hint, to make sure // that the network stack doesn't just re-use its connection to the test server. -IN_PROC_BROWSER_TEST_F(PredictorBrowserTest, PreconnectAndUse) { +IN_PROC_BROWSER_TEST_F(PredictorBrowserTest, PreconnectAndFetchNonCORS) { GURL preconnect_url = embedded_test_server()->base_url(); // First navigation to content with a preconnect hint. std::string preconnect_content = @@ -383,5 +391,76 @@ IN_PROC_BROWSER_TEST_F(PredictorBrowserTest, PreconnectAndUse) { EXPECT_EQ(1u, connection_listener_->GetReadSocketCount()); } +// Tests that preconnect warms up a CORS connection to a test +// server, and that socket is later used when fetching a CORS resource. +// Note: This test uses a data URI to serve the preconnect hint, to make sure +// that the network stack doesn't just re-use its connection to the test server. +IN_PROC_BROWSER_TEST_F(PredictorBrowserTest, PreconnectAndFetchCORS) { + GURL preconnect_url = embedded_test_server()->base_url(); + // First navigation to content with a preconnect hint. + std::string preconnect_content = "<link rel=\"preconnect\" href=\"" + + preconnect_url.spec() + "\" crossorigin>"; + NavigateToDataURLWithContent(preconnect_content); + connection_listener_->WaitUntilFirstConnectionAccepted(); + EXPECT_EQ(1u, connection_listener_->GetAcceptedSocketCount()); + EXPECT_EQ(0u, connection_listener_->GetReadSocketCount()); + + // Second navigation to content with a font. + std::string font_content = "<script>var font = new FontFace('FontA', 'url(" + + preconnect_url.spec() + + "test.woff2)');font.load();</script>"; + NavigateToDataURLWithContent(font_content); + connection_listener_->WaitUntilFirstConnectionRead(); + EXPECT_EQ(1u, connection_listener_->GetAcceptedSocketCount()); + EXPECT_EQ(1u, connection_listener_->GetReadSocketCount()); +} + +// Tests that preconnect warms up a non-CORS connection to a test +// server, but that socket is not used when fetching a CORS resource. +// Note: This test uses a data URI to serve the preconnect hint, to make sure +// that the network stack doesn't just re-use its connection to the test server. +IN_PROC_BROWSER_TEST_F(PredictorBrowserTest, PreconnectNonCORSAndFetchCORS) { + GURL preconnect_url = embedded_test_server()->base_url(); + // First navigation to content with a preconnect hint. + std::string preconnect_content = + "<link rel=\"preconnect\" href=\"" + preconnect_url.spec() + "\">"; + NavigateToDataURLWithContent(preconnect_content); + connection_listener_->WaitUntilFirstConnectionAccepted(); + EXPECT_EQ(1u, connection_listener_->GetAcceptedSocketCount()); + EXPECT_EQ(0u, connection_listener_->GetReadSocketCount()); + + // Second navigation to content with a font. + std::string font_content = "<script>var font = new FontFace('FontA', 'url(" + + preconnect_url.spec() + + "test.woff2)');font.load();</script>"; + NavigateToDataURLWithContent(font_content); + connection_listener_->WaitUntilFirstConnectionRead(); + EXPECT_EQ(2u, connection_listener_->GetAcceptedSocketCount()); + EXPECT_EQ(1u, connection_listener_->GetReadSocketCount()); +} + +// Tests that preconnect warms up a CORS connection to a test server, +// but that socket is not used when fetching a non-CORS resource. +// Note: This test uses a data URI to serve the preconnect hint, to make sure +// that the network stack doesn't just re-use its connection to the test server. +IN_PROC_BROWSER_TEST_F(PredictorBrowserTest, PreconnectCORSAndFetchNonCORS) { + GURL preconnect_url = embedded_test_server()->base_url(); + // First navigation to content with a preconnect hint. + std::string preconnect_content = "<link rel=\"preconnect\" href=\"" + + preconnect_url.spec() + "\" crossorigin>"; + NavigateToDataURLWithContent(preconnect_content); + connection_listener_->WaitUntilFirstConnectionAccepted(); + EXPECT_EQ(1u, connection_listener_->GetAcceptedSocketCount()); + EXPECT_EQ(0u, connection_listener_->GetReadSocketCount()); + + // Second navigation to content with an img. + std::string img_content = + "<img src=\"" + preconnect_url.spec() + "test.gif\">"; + NavigateToDataURLWithContent(img_content); + connection_listener_->WaitUntilFirstConnectionRead(); + EXPECT_EQ(2u, connection_listener_->GetAcceptedSocketCount()); + EXPECT_EQ(1u, connection_listener_->GetReadSocketCount()); +} + } // namespace chrome_browser_net diff --git a/chrome/browser/net/predictor_unittest.cc b/chrome/browser/net/predictor_unittest.cc index 76c7333..61460ae 100644 --- a/chrome/browser/net/predictor_unittest.cc +++ b/chrome/browser/net/predictor_unittest.cc @@ -731,7 +731,8 @@ TEST_F(PredictorTest, HSTSRedirect) { predictor.SetObserver(&observer); predictor.SetTransportSecurityState(&state); - predictor.PreconnectUrl(kHttpUrl, GURL(), UrlInfo::OMNIBOX_MOTIVATED, 2); + predictor.PreconnectUrl(kHttpUrl, GURL(), UrlInfo::OMNIBOX_MOTIVATED, true, + 2); ASSERT_EQ(1u, observer.preconnected_urls_.size()); EXPECT_EQ(kHttpsUrl, observer.preconnected_urls_[0]); diff --git a/chrome/browser/renderer_host/chrome_render_message_filter.cc b/chrome/browser/renderer_host/chrome_render_message_filter.cc index 69a785f..861d06d 100644 --- a/chrome/browser/renderer_host/chrome_render_message_filter.cc +++ b/chrome/browser/renderer_host/chrome_render_message_filter.cc @@ -113,7 +113,9 @@ void ChromeRenderMessageFilter::OnDnsPrefetch( predictor_->DnsPrefetchList(request.hostname_list); } -void ChromeRenderMessageFilter::OnPreconnect(const GURL& url, int count) { +void ChromeRenderMessageFilter::OnPreconnect(const GURL& url, + bool allow_credentials, + int count) { if (count < 1) { LOG(WARNING) << "NetworkHintsMsg_Preconnect IPC with invalid count: " << count; @@ -121,8 +123,9 @@ void ChromeRenderMessageFilter::OnPreconnect(const GURL& url, int count) { } if (predictor_ && url.is_valid() && url.has_host() && url.has_scheme() && url.SchemeIsHTTPOrHTTPS()) { - predictor_->PreconnectUrl( - url, GURL(), chrome_browser_net::UrlInfo::EARLY_LOAD_MOTIVATED, count); + predictor_->PreconnectUrl(url, GURL(), + chrome_browser_net::UrlInfo::EARLY_LOAD_MOTIVATED, + allow_credentials, count); } } diff --git a/chrome/browser/renderer_host/chrome_render_message_filter.h b/chrome/browser/renderer_host/chrome_render_message_filter.h index 3359d33..249435b 100644 --- a/chrome/browser/renderer_host/chrome_render_message_filter.h +++ b/chrome/browser/renderer_host/chrome_render_message_filter.h @@ -50,7 +50,7 @@ class ChromeRenderMessageFilter : public content::BrowserMessageFilter { ~ChromeRenderMessageFilter() override; void OnDnsPrefetch(const network_hints::LookupRequest& request); - void OnPreconnect(const GURL& url, int count); + void OnPreconnect(const GURL& url, bool allow_credentials, int count); void OnUpdatedCacheStats(const blink::WebCache::UsageStats& stats); void OnAllowDatabase(int render_frame_id, diff --git a/components/network_hints/common/network_hints_messages.h b/components/network_hints/common/network_hints_messages.h index c8c724c..8613061 100644 --- a/components/network_hints/common/network_hints_messages.h +++ b/components/network_hints/common/network_hints_messages.h @@ -42,7 +42,7 @@ IPC_MESSAGE_CONTROL1(NetworkHintsMsg_DNSPrefetch, // Request for preconnect to host providing resource specified by URL -IPC_MESSAGE_CONTROL2(NetworkHintsMsg_Preconnect, +IPC_MESSAGE_CONTROL3(NetworkHintsMsg_Preconnect, GURL /* preconnect target url */, - int /* number of connections */) - + bool /* Does connection have its credentials flag set */, + int /* number of connections */) diff --git a/components/network_hints/renderer/prescient_networking_dispatcher.cc b/components/network_hints/renderer/prescient_networking_dispatcher.cc index a5599c9..223af31 100644 --- a/components/network_hints/renderer/prescient_networking_dispatcher.cc +++ b/components/network_hints/renderer/prescient_networking_dispatcher.cc @@ -25,8 +25,13 @@ void PrescientNetworkingDispatcher::prefetchDNS( } void PrescientNetworkingDispatcher::preconnect(const blink::WebURL& url) { + preconnect_.Preconnect(url, true); +} + +void PrescientNetworkingDispatcher::preconnect(const blink::WebURL& url, + bool allow_credentials) { VLOG(2) << "Preconnect: " << url.string().utf8(); - preconnect_.Preconnect(url); + preconnect_.Preconnect(url, allow_credentials); } } // namespace network_hints diff --git a/components/network_hints/renderer/prescient_networking_dispatcher.h b/components/network_hints/renderer/prescient_networking_dispatcher.h index 3b2219e..1810aa0 100644 --- a/components/network_hints/renderer/prescient_networking_dispatcher.h +++ b/components/network_hints/renderer/prescient_networking_dispatcher.h @@ -19,8 +19,11 @@ class PrescientNetworkingDispatcher : public blink::WebPrescientNetworking { PrescientNetworkingDispatcher(); ~PrescientNetworkingDispatcher() override; - void prefetchDNS(const blink::WebString& hostname) override; - void preconnect(const blink::WebURL& url) override; + virtual void prefetchDNS(const blink::WebString& hostname) override; + // TODO(yoav): Remove the old version of the API + virtual void preconnect(const blink::WebURL& url); + virtual void preconnect(const blink::WebURL& url, + const bool allow_credentials) override; private: network_hints::RendererDnsPrefetch dns_prefetch_; diff --git a/components/network_hints/renderer/renderer_preconnect.cc b/components/network_hints/renderer/renderer_preconnect.cc index 41d8033..b353cb1 100644 --- a/components/network_hints/renderer/renderer_preconnect.cc +++ b/components/network_hints/renderer/renderer_preconnect.cc @@ -20,12 +20,12 @@ RendererPreconnect::RendererPreconnect() { RendererPreconnect::~RendererPreconnect() { } -void RendererPreconnect::Preconnect(const GURL &url) { +void RendererPreconnect::Preconnect(const GURL& url, bool allow_credentials) { if (!url.is_valid()) return; RenderThread::Get()->Send( - new NetworkHintsMsg_Preconnect(url, 1)); + new NetworkHintsMsg_Preconnect(url, allow_credentials, 1)); } } // namespace network_hints diff --git a/components/network_hints/renderer/renderer_preconnect.h b/components/network_hints/renderer/renderer_preconnect.h index adc0143..53a8e3d 100644 --- a/components/network_hints/renderer/renderer_preconnect.h +++ b/components/network_hints/renderer/renderer_preconnect.h @@ -22,14 +22,14 @@ namespace network_hints { // An internal interface to the network_hints component for efficiently sending -// DNS prefetch requests to the net stack. +// preconnect requests to the net stack. class RendererPreconnect { public: RendererPreconnect(); ~RendererPreconnect(); // Submit a preconnect request for a single connection. - void Preconnect(const GURL &url); + void Preconnect(const GURL& url, bool allow_credentials); private: diff --git a/net/test/embedded_test_server/stream_listen_socket.cc b/net/test/embedded_test_server/stream_listen_socket.cc index c28e87d..1056983 100644 --- a/net/test/embedded_test_server/stream_listen_socket.cc +++ b/net/test/embedded_test_server/stream_listen_socket.cc @@ -103,7 +103,7 @@ int StreamListenSocket::GetLocalAddress(IPEndPoint* address) const { return OK; } -int StreamListenSocket::GetPeerAddress(IPEndPoint* address) { +int StreamListenSocket::GetPeerAddress(IPEndPoint* address) const { SockaddrStorage storage; if (getpeername(socket_, storage.addr, &storage.addr_len)) { #if defined(OS_WIN) diff --git a/net/test/embedded_test_server/stream_listen_socket.h b/net/test/embedded_test_server/stream_listen_socket.h index 45478eb..02a8b98 100644 --- a/net/test/embedded_test_server/stream_listen_socket.h +++ b/net/test/embedded_test_server/stream_listen_socket.h @@ -77,7 +77,7 @@ class StreamListenSocket : virtual int GetLocalAddress(IPEndPoint* address) const; // Copies the peer address to |address|. Returns a network error code. // This method is virtual to support unit testing. - virtual int GetPeerAddress(IPEndPoint* address); + virtual int GetPeerAddress(IPEndPoint* address) const; static const int kSocketError; |