Add new certificateProvider extension API.
[chromium-blink-merge.git] / chrome / browser / net / predictor_browsertest.cc
blob81280c082013f958a1e62d7d1a458d6260130b30
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include <stdint.h>
7 #include "base/base64.h"
8 #include "base/command_line.h"
9 #include "base/json/json_string_value_serializer.h"
10 #include "base/macros.h"
11 #include "base/prefs/pref_service.h"
12 #include "base/synchronization/lock.h"
13 #include "base/thread_task_runner_handle.h"
14 #include "chrome/browser/browser_process.h"
15 #include "chrome/browser/net/chrome_net_log.h"
16 #include "chrome/browser/net/predictor.h"
17 #include "chrome/browser/profiles/profile.h"
18 #include "chrome/browser/ui/browser.h"
19 #include "chrome/common/pref_names.h"
20 #include "chrome/test/base/in_process_browser_test.h"
21 #include "chrome/test/base/ui_test_utils.h"
22 #include "content/public/common/content_switches.h"
23 #include "content/public/test/test_utils.h"
24 #include "net/base/host_port_pair.h"
25 #include "net/base/ip_endpoint.h"
26 #include "net/base/net_errors.h"
27 #include "net/dns/host_resolver_proc.h"
28 #include "net/dns/mock_host_resolver.h"
29 #include "net/test/embedded_test_server/embedded_test_server.h"
30 #include "net/test/embedded_test_server/embedded_test_server_connection_listener.h"
31 #include "testing/gmock/include/gmock/gmock.h"
33 using content::BrowserThread;
34 using testing::HasSubstr;
36 namespace {
38 const char kBlinkPreconnectFeature[] = "LinkPreconnect";
39 const char kChromiumHostname[] = "chromium.org";
40 const char kInvalidLongHostname[] = "illegally-long-hostname-over-255-"
41 "characters-should-not-send-an-ipc-message-to-the-browser-"
42 "0000000000000000000000000000000000000000000000000000000000000000000000000"
43 "0000000000000000000000000000000000000000000000000000000000000000000000000"
44 "000000000000000000000000000000000000000000000000000000.org";
46 // Gets notified by the EmbeddedTestServer on incoming connections being
47 // accepted or read from, keeps track of them and exposes that info to
48 // the tests.
49 // A port being reused is currently considered an error. If a test
50 // needs to verify multiple connections are opened in sequence, that will need
51 // to be changed.
52 class ConnectionListener
53 : public net::test_server::EmbeddedTestServerConnectionListener {
54 public:
55 ConnectionListener() : task_runner_(base::ThreadTaskRunnerHandle::Get()) {}
57 ~ConnectionListener() override {}
59 // Get called from the EmbeddedTestServer thread to be notified that
60 // a connection was accepted.
61 void AcceptedSocket(
62 const net::test_server::StreamListenSocket& connection) override {
63 base::AutoLock lock(lock_);
64 uint16_t socket = GetPort(connection);
65 EXPECT_TRUE(sockets_.find(socket) == sockets_.end());
67 sockets_[socket] = SOCKET_ACCEPTED;
68 task_runner_->PostTask(FROM_HERE, accept_loop_.QuitClosure());
71 // Get called from the EmbeddedTestServer thread to be notified that
72 // a connection was read from.
73 void ReadFromSocket(
74 const net::test_server::StreamListenSocket& connection) override {
75 base::AutoLock lock(lock_);
76 uint16_t socket = GetPort(connection);
77 EXPECT_FALSE(sockets_.find(socket) == sockets_.end());
79 sockets_[socket] = SOCKET_READ_FROM;
80 task_runner_->PostTask(FROM_HERE, read_loop_.QuitClosure());
83 // Returns the number of sockets that were accepted by the server.
84 size_t GetAcceptedSocketCount() const {
85 base::AutoLock lock(lock_);
86 return sockets_.size();
89 // Returns the number of sockets that were read from by the server.
90 size_t GetReadSocketCount() const {
91 base::AutoLock lock(lock_);
92 size_t read_sockets = 0;
93 for (const auto& socket : sockets_) {
94 if (socket.second == SOCKET_READ_FROM)
95 ++read_sockets;
97 return read_sockets;
100 void WaitUntilFirstConnectionAccepted() { accept_loop_.Run(); }
102 void WaitUntilFirstConnectionRead() { read_loop_.Run(); }
104 private:
105 static uint16_t GetPort(
106 const net::test_server::StreamListenSocket& connection) {
107 // Get the remote port of the peer, since the local port will always be the
108 // port the test server is listening on. This isn't strictly correct - it's
109 // possible for multiple peers to connect with the same remote port but
110 // different remote IPs - but the tests here assume that connections to the
111 // test server (running on localhost) will always come from localhost, and
112 // thus the peer port is all thats needed to distinguish two connections.
113 // This also would be problematic if the OS reused ports, but that's not
114 // something to worry about for these tests.
115 net::IPEndPoint address;
116 EXPECT_EQ(net::OK, connection.GetPeerAddress(&address));
117 return address.port();
120 enum SocketStatus { SOCKET_ACCEPTED, SOCKET_READ_FROM };
122 typedef base::hash_map<uint16_t, SocketStatus> SocketContainer;
123 SocketContainer sockets_;
125 base::RunLoop accept_loop_;
126 base::RunLoop read_loop_;
127 scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
129 mutable base::Lock lock_;
131 DISALLOW_COPY_AND_ASSIGN(ConnectionListener);
134 // Records a history of all hostnames for which resolving has been requested,
135 // and immediately fails the resolution requests themselves.
136 class HostResolutionRequestRecorder : public net::HostResolverProc {
137 public:
138 HostResolutionRequestRecorder()
139 : HostResolverProc(NULL),
140 is_waiting_for_hostname_(false) {
143 int Resolve(const std::string& host,
144 net::AddressFamily address_family,
145 net::HostResolverFlags host_resolver_flags,
146 net::AddressList* addrlist,
147 int* os_error) override {
148 BrowserThread::PostTask(
149 BrowserThread::UI,
150 FROM_HERE,
151 base::Bind(&HostResolutionRequestRecorder::AddToHistory,
152 base::Unretained(this),
153 host));
154 return net::ERR_NAME_NOT_RESOLVED;
157 int RequestedHostnameCount() const {
158 return requested_hostnames_.size();
161 bool HasHostBeenRequested(const std::string& hostname) const {
162 DCHECK_CURRENTLY_ON(BrowserThread::UI);
163 return std::find(requested_hostnames_.begin(),
164 requested_hostnames_.end(),
165 hostname) != requested_hostnames_.end();
168 void WaitUntilHostHasBeenRequested(const std::string& hostname) {
169 DCHECK_CURRENTLY_ON(BrowserThread::UI);
170 DCHECK(!is_waiting_for_hostname_);
171 if (HasHostBeenRequested(hostname))
172 return;
173 waiting_for_hostname_ = hostname;
174 is_waiting_for_hostname_ = true;
175 content::RunMessageLoop();
178 private:
179 ~HostResolutionRequestRecorder() override {}
181 void AddToHistory(const std::string& hostname) {
182 DCHECK_CURRENTLY_ON(BrowserThread::UI);
183 requested_hostnames_.push_back(hostname);
184 if (is_waiting_for_hostname_ && waiting_for_hostname_ == hostname) {
185 is_waiting_for_hostname_ = false;
186 waiting_for_hostname_.clear();
187 base::MessageLoop::current()->Quit();
191 // The hostname which WaitUntilHostHasBeenRequested is currently waiting for
192 // to be requested.
193 std::string waiting_for_hostname_;
195 // Whether WaitUntilHostHasBeenRequested is waiting for a hostname to be
196 // requested and thus is running a nested message loop.
197 bool is_waiting_for_hostname_;
199 // A list of hostnames for which resolution has already been requested. Only
200 // to be accessed from the UI thread.
201 std::vector<std::string> requested_hostnames_;
203 DISALLOW_COPY_AND_ASSIGN(HostResolutionRequestRecorder);
206 } // namespace
208 namespace chrome_browser_net {
210 class PredictorBrowserTest : public InProcessBrowserTest {
211 public:
212 PredictorBrowserTest()
213 : startup_url_("http://host1:1"),
214 referring_url_("http://host2:1"),
215 target_url_("http://host3:1"),
216 host_resolution_request_recorder_(new HostResolutionRequestRecorder) {
219 protected:
220 void SetUpInProcessBrowserTestFixture() override {
221 scoped_host_resolver_proc_.reset(new net::ScopedDefaultHostResolverProc(
222 host_resolution_request_recorder_.get()));
223 InProcessBrowserTest::SetUpInProcessBrowserTestFixture();
226 void SetUpCommandLine(base::CommandLine* command_line) override {
227 command_line->AppendSwitch(
228 switches::kEnableExperimentalWebPlatformFeatures);
229 command_line->AppendSwitchASCII(
230 switches::kEnableBlinkFeatures, kBlinkPreconnectFeature);
233 void SetUpOnMainThread() override {
234 connection_listener_.reset(new ConnectionListener());
235 embedded_test_server()->SetConnectionListener(connection_listener_.get());
236 ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
239 void TearDownOnMainThread() override {
240 ASSERT_TRUE(embedded_test_server()->ShutdownAndWaitUntilComplete());
243 // Navigates to a data URL containing the given content, with a MIME type of
244 // text/html.
245 void NavigateToDataURLWithContent(const std::string& content) {
246 std::string encoded_content;
247 base::Base64Encode(content, &encoded_content);
248 std::string data_uri_content = "data:text/html;base64," + encoded_content;
249 ui_test_utils::NavigateToURL(browser(), GURL(data_uri_content));
252 void TearDownInProcessBrowserTestFixture() override {
253 InProcessBrowserTest::TearDownInProcessBrowserTestFixture();
254 scoped_host_resolver_proc_.reset();
257 void LearnAboutInitialNavigation(const GURL& url) {
258 Predictor* predictor = browser()->profile()->GetNetworkPredictor();
259 BrowserThread::PostTask(BrowserThread::IO,
260 FROM_HERE,
261 base::Bind(&Predictor::LearnAboutInitialNavigation,
262 base::Unretained(predictor),
263 url));
264 content::RunAllPendingInMessageLoop(BrowserThread::IO);
267 void LearnFromNavigation(const GURL& referring_url, const GURL& target_url) {
268 Predictor* predictor = browser()->profile()->GetNetworkPredictor();
269 BrowserThread::PostTask(BrowserThread::IO,
270 FROM_HERE,
271 base::Bind(&Predictor::LearnFromNavigation,
272 base::Unretained(predictor),
273 referring_url,
274 target_url));
275 content::RunAllPendingInMessageLoop(BrowserThread::IO);
278 void PrepareFrameSubresources(const GURL& url) {
279 Predictor* predictor = browser()->profile()->GetNetworkPredictor();
280 predictor->PredictFrameSubresources(url, GURL());
283 void GetListFromPrefsAsString(const char* list_path,
284 std::string* value_as_string) const {
285 PrefService* prefs = browser()->profile()->GetPrefs();
286 const base::ListValue* list_value = prefs->GetList(list_path);
287 JSONStringValueSerializer serializer(value_as_string);
288 serializer.Serialize(*list_value);
291 bool HasHostBeenRequested(const std::string& hostname) const {
292 return host_resolution_request_recorder_->HasHostBeenRequested(hostname);
295 void WaitUntilHostHasBeenRequested(const std::string& hostname) {
296 host_resolution_request_recorder_->WaitUntilHostHasBeenRequested(hostname);
299 int RequestedHostnameCount() const {
300 return host_resolution_request_recorder_->RequestedHostnameCount();
303 const GURL startup_url_;
304 const GURL referring_url_;
305 const GURL target_url_;
306 scoped_ptr<ConnectionListener> connection_listener_;
308 private:
309 scoped_refptr<HostResolutionRequestRecorder>
310 host_resolution_request_recorder_;
311 scoped_ptr<net::ScopedDefaultHostResolverProc> scoped_host_resolver_proc_;
314 IN_PROC_BROWSER_TEST_F(PredictorBrowserTest, PRE_ShutdownStartupCycle) {
315 // Prepare state that will be serialized on this shut-down and read on next
316 // start-up.
317 LearnAboutInitialNavigation(startup_url_);
318 LearnFromNavigation(referring_url_, target_url_);
321 IN_PROC_BROWSER_TEST_F(PredictorBrowserTest, ShutdownStartupCycle) {
322 // Make sure that the Preferences file is actually wiped of all DNS prefetch
323 // related data after start-up.
324 std::string cleared_startup_list;
325 std::string cleared_referral_list;
326 GetListFromPrefsAsString(prefs::kDnsPrefetchingStartupList,
327 &cleared_startup_list);
328 GetListFromPrefsAsString(prefs::kDnsPrefetchingHostReferralList,
329 &cleared_referral_list);
331 EXPECT_THAT(cleared_startup_list, Not(HasSubstr(startup_url_.host())));
332 EXPECT_THAT(cleared_referral_list, Not(HasSubstr(referring_url_.host())));
333 EXPECT_THAT(cleared_referral_list, Not(HasSubstr(target_url_.host())));
335 // But also make sure this data has been first loaded into the Predictor, by
336 // inspecting that the Predictor starts making the expected hostname requests.
337 PrepareFrameSubresources(referring_url_);
338 WaitUntilHostHasBeenRequested(startup_url_.host());
339 WaitUntilHostHasBeenRequested(target_url_.host());
342 // Flaky on Windows: http://crbug.com/469120
343 #if defined(OS_WIN)
344 #define MAYBE_DnsPrefetch DISABLED_DnsPrefetch
345 #else
346 #define MAYBE_DnsPrefetch DnsPrefetch
347 #endif
348 IN_PROC_BROWSER_TEST_F(PredictorBrowserTest, MAYBE_DnsPrefetch) {
349 ASSERT_TRUE(test_server()->Start());
350 int hostnames_requested_before_load = RequestedHostnameCount();
351 ui_test_utils::NavigateToURL(
352 browser(),
353 GURL(test_server()->GetURL("files/predictor/dns_prefetch.html")));
354 WaitUntilHostHasBeenRequested(kChromiumHostname);
355 ASSERT_FALSE(HasHostBeenRequested(kInvalidLongHostname));
356 ASSERT_EQ(hostnames_requested_before_load + 1, RequestedHostnameCount());
359 // Tests that preconnect warms up a socket connection to a test server.
360 // Note: This test uses a data URI to serve the preconnect hint, to make sure
361 // that the network stack doesn't just re-use its connection to the test server.
362 IN_PROC_BROWSER_TEST_F(PredictorBrowserTest, PreconnectNonCORS) {
363 GURL preconnect_url = embedded_test_server()->base_url();
364 std::string preconnect_content =
365 "<link rel=\"preconnect\" href=\"" + preconnect_url.spec() + "\">";
366 NavigateToDataURLWithContent(preconnect_content);
367 connection_listener_->WaitUntilFirstConnectionAccepted();
368 EXPECT_EQ(1u, connection_listener_->GetAcceptedSocketCount());
369 EXPECT_EQ(0u, connection_listener_->GetReadSocketCount());
372 // Tests that preconnect warms up a socket connection to a test server,
373 // and that that socket is later used when fetching a resource.
374 // Note: This test uses a data URI to serve the preconnect hint, to make sure
375 // that the network stack doesn't just re-use its connection to the test server.
376 IN_PROC_BROWSER_TEST_F(PredictorBrowserTest, PreconnectAndFetchNonCORS) {
377 GURL preconnect_url = embedded_test_server()->base_url();
378 // First navigation to content with a preconnect hint.
379 std::string preconnect_content =
380 "<link rel=\"preconnect\" href=\"" + preconnect_url.spec() + "\">";
381 NavigateToDataURLWithContent(preconnect_content);
382 connection_listener_->WaitUntilFirstConnectionAccepted();
383 EXPECT_EQ(1u, connection_listener_->GetAcceptedSocketCount());
384 EXPECT_EQ(0u, connection_listener_->GetReadSocketCount());
386 // Second navigation to content with an img.
387 std::string img_content =
388 "<img src=\"" + preconnect_url.spec() + "test.gif\">";
389 NavigateToDataURLWithContent(img_content);
390 connection_listener_->WaitUntilFirstConnectionRead();
391 EXPECT_EQ(1u, connection_listener_->GetAcceptedSocketCount());
392 EXPECT_EQ(1u, connection_listener_->GetReadSocketCount());
395 // Tests that preconnect warms up a CORS connection to a test
396 // server, and that socket is later used when fetching a CORS resource.
397 // Note: This test uses a data URI to serve the preconnect hint, to make sure
398 // that the network stack doesn't just re-use its connection to the test server.
399 IN_PROC_BROWSER_TEST_F(PredictorBrowserTest, PreconnectAndFetchCORS) {
400 GURL preconnect_url = embedded_test_server()->base_url();
401 // First navigation to content with a preconnect hint.
402 std::string preconnect_content = "<link rel=\"preconnect\" href=\"" +
403 preconnect_url.spec() + "\" crossorigin>";
404 NavigateToDataURLWithContent(preconnect_content);
405 connection_listener_->WaitUntilFirstConnectionAccepted();
406 EXPECT_EQ(1u, connection_listener_->GetAcceptedSocketCount());
407 EXPECT_EQ(0u, connection_listener_->GetReadSocketCount());
409 // Second navigation to content with a font.
410 std::string font_content = "<script>var font = new FontFace('FontA', 'url(" +
411 preconnect_url.spec() +
412 "test.woff2)');font.load();</script>";
413 NavigateToDataURLWithContent(font_content);
414 connection_listener_->WaitUntilFirstConnectionRead();
415 EXPECT_EQ(1u, connection_listener_->GetAcceptedSocketCount());
416 EXPECT_EQ(1u, connection_listener_->GetReadSocketCount());
419 // Tests that preconnect warms up a non-CORS connection to a test
420 // server, but that socket is not used when fetching a CORS resource.
421 // Note: This test uses a data URI to serve the preconnect hint, to make sure
422 // that the network stack doesn't just re-use its connection to the test server.
423 IN_PROC_BROWSER_TEST_F(PredictorBrowserTest, PreconnectNonCORSAndFetchCORS) {
424 GURL preconnect_url = embedded_test_server()->base_url();
425 // First navigation to content with a preconnect hint.
426 std::string preconnect_content =
427 "<link rel=\"preconnect\" href=\"" + preconnect_url.spec() + "\">";
428 NavigateToDataURLWithContent(preconnect_content);
429 connection_listener_->WaitUntilFirstConnectionAccepted();
430 EXPECT_EQ(1u, connection_listener_->GetAcceptedSocketCount());
431 EXPECT_EQ(0u, connection_listener_->GetReadSocketCount());
433 // Second navigation to content with a font.
434 std::string font_content = "<script>var font = new FontFace('FontA', 'url(" +
435 preconnect_url.spec() +
436 "test.woff2)');font.load();</script>";
437 NavigateToDataURLWithContent(font_content);
438 connection_listener_->WaitUntilFirstConnectionRead();
439 EXPECT_EQ(2u, connection_listener_->GetAcceptedSocketCount());
440 EXPECT_EQ(1u, connection_listener_->GetReadSocketCount());
443 // Tests that preconnect warms up a CORS connection to a test server,
444 // but that socket is not used when fetching a non-CORS resource.
445 // Note: This test uses a data URI to serve the preconnect hint, to make sure
446 // that the network stack doesn't just re-use its connection to the test server.
447 IN_PROC_BROWSER_TEST_F(PredictorBrowserTest, PreconnectCORSAndFetchNonCORS) {
448 GURL preconnect_url = embedded_test_server()->base_url();
449 // First navigation to content with a preconnect hint.
450 std::string preconnect_content = "<link rel=\"preconnect\" href=\"" +
451 preconnect_url.spec() + "\" crossorigin>";
452 NavigateToDataURLWithContent(preconnect_content);
453 connection_listener_->WaitUntilFirstConnectionAccepted();
454 EXPECT_EQ(1u, connection_listener_->GetAcceptedSocketCount());
455 EXPECT_EQ(0u, connection_listener_->GetReadSocketCount());
457 // Second navigation to content with an img.
458 std::string img_content =
459 "<img src=\"" + preconnect_url.spec() + "test.gif\">";
460 NavigateToDataURLWithContent(img_content);
461 connection_listener_->WaitUntilFirstConnectionRead();
462 EXPECT_EQ(2u, connection_listener_->GetAcceptedSocketCount());
463 EXPECT_EQ(1u, connection_listener_->GetReadSocketCount());
466 } // namespace chrome_browser_net