diff options
author | davidben@chromium.org <davidben@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-04-02 00:45:49 +0000 |
---|---|---|
committer | davidben@chromium.org <davidben@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-04-02 00:45:49 +0000 |
commit | bb5ec78508daf2f667aac87369d519018729895c (patch) | |
tree | 09d8d9d447c2ab944428369234d543482f2c5fe4 | |
parent | 15754c107f29ec566651a0d26f2ee0a00549bb22 (diff) | |
download | chromium_src-bb5ec78508daf2f667aac87369d519018729895c.zip chromium_src-bb5ec78508daf2f667aac87369d519018729895c.tar.gz chromium_src-bb5ec78508daf2f667aac87369d519018729895c.tar.bz2 |
Make the network predictor HSTS-aware.
If preconnecting to an http URL on the HSTS list, switch to the https version.
Also apply the HSTS redirect before looking a URL up in referrers so
preconnecting subresources for, e.g., http://mail.google.com, picks up
subresources for https://mail.google.com.
BUG=344925
Review URL: https://codereview.chromium.org/219953002
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@261009 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | chrome/browser/net/predictor.cc | 53 | ||||
-rw-r--r-- | chrome/browser/net/predictor.h | 35 | ||||
-rw-r--r-- | chrome/browser/net/predictor_unittest.cc | 67 |
3 files changed, 152 insertions, 3 deletions
diff --git a/chrome/browser/net/predictor.cc b/chrome/browser/net/predictor.cc index b6b2b26..38e123d 100644 --- a/chrome/browser/net/predictor.cc +++ b/chrome/browser/net/predictor.cc @@ -40,6 +40,9 @@ #include "net/base/net_log.h" #include "net/dns/host_resolver.h" #include "net/dns/single_request_host_resolver.h" +#include "net/http/transport_security_state.h" +#include "net/ssl/ssl_config_service.h" +#include "net/url_request/url_request_context.h" #include "net/url_request/url_request_context_getter.h" using base::TimeDelta; @@ -269,10 +272,13 @@ Predictor::Predictor(bool preconnect_enabled) max_dns_queue_delay_( TimeDelta::FromMilliseconds(g_max_queueing_delay_ms)), host_resolver_(NULL), + transport_security_state_(NULL), + ssl_config_service_(NULL), preconnect_enabled_(preconnect_enabled), consecutive_omnibox_preconnect_count_(0), next_trim_time_(base::TimeTicks::Now() + - TimeDelta::FromHours(kDurationBetweenTrimmingsHours)) { + TimeDelta::FromHours(kDurationBetweenTrimmingsHours)), + observer_(NULL) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); } @@ -809,6 +815,11 @@ void Predictor::FinalizeInitializationOnIOThread( host_resolver_ = io_thread->globals()->host_resolver.get(); preconnect_usage_.reset(new PreconnectUsage()); + net::URLRequestContext* context = + url_request_context_getter_->GetURLRequestContext(); + transport_security_state_ = context->transport_security_state(); + ssl_config_service_ = context->ssl_config_service(); + // base::WeakPtrFactory instances need to be created and destroyed // on the same thread. The predictor lives on the IO thread and will die // from there so now that we're on the IO thread we need to properly @@ -984,15 +995,23 @@ void Predictor::PreconnectUrl(const GURL& url, } void Predictor::PreconnectUrlOnIOThread( - const GURL& url, + const GURL& original_url, const GURL& first_party_for_cookies, UrlInfo::ResolutionMotivation motivation, int count) { + // Skip the HSTS redirect. + GURL url = GetHSTSRedirectOnIOThread(original_url); + if (motivation == UrlInfo::MOUSE_OVER_MOTIVATED) RecordPreconnectTrigger(url); AdviseProxy(url, motivation, true /* is_preconnect */); + if (observer_) { + observer_->OnPreconnectUrl( + url, first_party_for_cookies, motivation, count); + } + PreconnectOnIOThread(url, first_party_for_cookies, motivation, @@ -1068,8 +1087,12 @@ enum SubresourceValue { SUBRESOURCE_VALUE_MAX }; -void Predictor::PrepareFrameSubresources(const GURL& url, +void Predictor::PrepareFrameSubresources(const GURL& original_url, const GURL& first_party_for_cookies) { + // Apply HSTS redirect early so it is taken into account when looking up + // subresources. + GURL url = GetHSTSRedirectOnIOThread(original_url); + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); DCHECK_EQ(url.GetWithEmptyPath(), url); Referrers::iterator it = referrers_.find(url); @@ -1289,6 +1312,30 @@ void Predictor::AdviseProxyOnIOThread(const GURL& url, proxy_advisor_->Advise(url, motivation, is_preconnect); } +GURL Predictor::GetHSTSRedirectOnIOThread(const GURL& url) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + + if (!transport_security_state_) + return url; + if (!url.SchemeIs("http")) + return url; + net::TransportSecurityState::DomainState domain_state; + if (!transport_security_state_->GetDomainState( + url.host(), + net::SSLConfigService::IsSNIAvailable(ssl_config_service_), + &domain_state)) { + return url; + } + if (!domain_state.ShouldUpgradeToSSL()) + return url; + + url_canon::Replacements<char> replacements; + const char kNewScheme[] = "https"; + replacements.SetScheme(kNewScheme, + url_parse::Component(0, strlen(kNewScheme))); + return url.ReplaceComponents(replacements); +} + // ---------------------- End IO methods. ------------------------------------- //----------------------------------------------------------------------------- diff --git a/chrome/browser/net/predictor.h b/chrome/browser/net/predictor.h index 04725e8..3234e17 100644 --- a/chrome/browser/net/predictor.h +++ b/chrome/browser/net/predictor.h @@ -46,6 +46,8 @@ class WaitableEvent; namespace net { class HostResolver; +class SSLConfigService; +class TransportSecurityState; class URLRequestContextGetter; } @@ -59,6 +61,17 @@ typedef chrome_common_net::UrlList UrlList; typedef chrome_common_net::NameList NameList; typedef std::map<GURL, UrlInfo> Results; +// An observer for testing. +class PredictorObserver { + public: + virtual ~PredictorObserver() {} + + virtual void OnPreconnectUrl(const GURL& original_url, + const GURL& first_party_for_cookies, + UrlInfo::ResolutionMotivation motivation, + int count) = 0; +}; + // Predictor is constructed during Profile construction (on the UI thread), // but it is destroyed on the IO thread when ProfileIOData goes away. All of // its core state and functionality happens on the IO thread. The only UI @@ -268,6 +281,11 @@ class Predictor { host_resolver_ = host_resolver; } // Used for testing. + void SetTransportSecurityState( + net::TransportSecurityState* transport_security_state) { + transport_security_state_ = transport_security_state; + } + // Used for testing. void SetProxyAdvisor(ProxyAdvisor* proxy_advisor) { proxy_advisor_.reset(proxy_advisor); } @@ -279,6 +297,10 @@ class Predictor { void SetShutdown(bool shutdown) { shutdown_ = shutdown; } + // Used for testing. + void SetObserver(PredictorObserver* observer) { + observer_ = observer; + } // Flag setting to use preconnection instead of just DNS pre-fetching. bool preconnect_enabled() const { @@ -481,6 +503,9 @@ class Predictor { UrlInfo::ResolutionMotivation motivation, bool is_preconnect); + // Applies the HSTS redirect for |url|, if any. + GURL GetHSTSRedirectOnIOThread(const GURL& url); + // ------------- End IO thread methods. scoped_ptr<InitialObserver> initial_observer_; @@ -520,6 +545,13 @@ class Predictor { // The host resolver we warm DNS entries for. net::HostResolver* host_resolver_; + // The TransportSecurityState instance we query HSTS redirects from. + net::TransportSecurityState* transport_security_state_; + + // The SSLConfigService we query SNI support from (used in querying HSTS + // redirects). + net::SSLConfigService* ssl_config_service_; + // Are we currently using preconnection, rather than just DNS resolution, for // subresources and omni-box search URLs. bool preconnect_enabled_; @@ -557,6 +589,9 @@ class Predictor { scoped_ptr<ProxyAdvisor> proxy_advisor_; + // An observer for testing. + PredictorObserver* observer_; + DISALLOW_COPY_AND_ASSIGN(Predictor); }; diff --git a/chrome/browser/net/predictor_unittest.cc b/chrome/browser/net/predictor_unittest.cc index fff3325..740e07d 100644 --- a/chrome/browser/net/predictor_unittest.cc +++ b/chrome/browser/net/predictor_unittest.cc @@ -21,6 +21,7 @@ #include "net/base/address_list.h" #include "net/base/winsock_init.h" #include "net/dns/mock_host_resolver.h" +#include "net/http/transport_security_state.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" @@ -697,6 +698,72 @@ TEST_F(PredictorTest, DiscardPredictorResults) { predictor.Shutdown(); } +class TestPredictorObserver : public PredictorObserver { + public: + // PredictorObserver implementation: + virtual void OnPreconnectUrl(const GURL& url, + const GURL& first_party_for_cookies, + UrlInfo::ResolutionMotivation motivation, + int count) OVERRIDE { + preconnected_urls_.push_back(url); + } + + std::vector<GURL> preconnected_urls_; +}; + +// Tests that preconnects apply the HSTS list. +TEST_F(PredictorTest, HSTSRedirect) { + const GURL kHttpUrl("http://example.com"); + const GURL kHttpsUrl("https://example.com"); + + const base::Time expiry = + base::Time::Now() + base::TimeDelta::FromSeconds(1000); + net::TransportSecurityState state; + state.AddHSTS(kHttpUrl.host(), expiry, false); + + Predictor predictor(true); + TestPredictorObserver observer; + predictor.SetObserver(&observer); + predictor.SetTransportSecurityState(&state); + + predictor.PreconnectUrl(kHttpUrl, GURL(), UrlInfo::OMNIBOX_MOTIVATED, 2); + ASSERT_EQ(1u, observer.preconnected_urls_.size()); + EXPECT_EQ(kHttpsUrl, observer.preconnected_urls_[0]); + + predictor.Shutdown(); +} + +// Tests that preconnecting a URL on the HSTS list preconnects the subresources +// for the SSL version. +TEST_F(PredictorTest, HSTSRedirectSubresources) { + const GURL kHttpUrl("http://example.com"); + const GURL kHttpsUrl("https://example.com"); + const GURL kSubresourceUrl("https://images.example.com"); + const double kUseRate = 23.4; + + const base::Time expiry = + base::Time::Now() + base::TimeDelta::FromSeconds(1000); + net::TransportSecurityState state; + state.AddHSTS(kHttpUrl.host(), expiry, false); + + Predictor predictor(true); + TestPredictorObserver observer; + predictor.SetObserver(&observer); + predictor.SetTransportSecurityState(&state); + + scoped_ptr<base::ListValue> referral_list(NewEmptySerializationList()); + AddToSerializedList( + kHttpsUrl, kSubresourceUrl, kUseRate, referral_list.get()); + predictor.DeserializeReferrers(*referral_list.get()); + + predictor.PreconnectUrlAndSubresources(kHttpUrl, GURL()); + ASSERT_EQ(2u, observer.preconnected_urls_.size()); + EXPECT_EQ(kHttpsUrl, observer.preconnected_urls_[0]); + EXPECT_EQ(kSubresourceUrl, observer.preconnected_urls_[1]); + + predictor.Shutdown(); +} + #if defined(OS_ANDROID) || defined(OS_IOS) // Tests for the predictor with a proxy advisor |