summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authordavidben@chromium.org <davidben@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-04-02 00:45:49 +0000
committerdavidben@chromium.org <davidben@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-04-02 00:45:49 +0000
commitbb5ec78508daf2f667aac87369d519018729895c (patch)
tree09d8d9d447c2ab944428369234d543482f2c5fe4
parent15754c107f29ec566651a0d26f2ee0a00549bb22 (diff)
downloadchromium_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.cc53
-rw-r--r--chrome/browser/net/predictor.h35
-rw-r--r--chrome/browser/net/predictor_unittest.cc67
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