Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include <stddef.h> | 5 #include <stddef.h> |
| 6 #include <stdint.h> | 6 #include <stdint.h> |
| 7 | 7 |
| 8 #include <algorithm> | 8 #include <algorithm> |
| 9 #include <memory> | 9 #include <memory> |
| 10 | 10 |
| (...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 60 net::NetworkDelegate* delegate) { | 60 net::NetworkDelegate* delegate) { |
| 61 const char kPlainTextHeaders[] = | 61 const char kPlainTextHeaders[] = |
| 62 "HTTP/1.1 200 OK\n" | 62 "HTTP/1.1 200 OK\n" |
| 63 "Content-Type: text/plain\n" | 63 "Content-Type: text/plain\n" |
| 64 "Access-Control-Allow-Origin: *\n" | 64 "Access-Control-Allow-Origin: *\n" |
| 65 "\n"; | 65 "\n"; |
| 66 return new net::URLRequestTestJob(request, delegate, kPlainTextHeaders, "", | 66 return new net::URLRequestTestJob(request, delegate, kPlainTextHeaders, "", |
| 67 true); | 67 true); |
| 68 } | 68 } |
| 69 | 69 |
| 70 net::URLRequestJob* CreateRedirectRequestJob(std::string location, | |
| 71 net::URLRequest* request, | |
| 72 net::NetworkDelegate* delegate) { | |
| 73 char kPlainTextHeaders[] = | |
| 74 "HTTP/1.1 302 \n" | |
| 75 "Location: %s\n" | |
| 76 "Access-Control-Allow-Origin: *\n" | |
| 77 "\n"; | |
| 78 return new net::URLRequestTestJob( | |
| 79 request, delegate, | |
| 80 base::StringPrintf(kPlainTextHeaders, location.c_str()), "", true); | |
| 81 } | |
| 82 | |
| 70 // Override the test server to redirect requests matching some path. This is | 83 // Override the test server to redirect requests matching some path. This is |
| 71 // used because the predictor only learns simple redirects with a path of "/" | 84 // used because the predictor only learns simple redirects with a path of "/" |
| 72 std::unique_ptr<net::test_server::HttpResponse> RedirectForPathHandler( | 85 std::unique_ptr<net::test_server::HttpResponse> RedirectForPathHandler( |
| 73 const std::string& path, | 86 const std::string& path, |
| 74 const GURL& redirect_url, | 87 const GURL& redirect_url, |
| 75 const net::test_server::HttpRequest& request) { | 88 const net::test_server::HttpRequest& request) { |
| 76 if (request.GetURL().path() != path) | 89 if (request.GetURL().path() != path) |
| 77 return nullptr; | 90 return nullptr; |
| 78 std::unique_ptr<net::test_server::BasicHttpResponse> response( | 91 std::unique_ptr<net::test_server::BasicHttpResponse> response( |
| 79 new net::test_server::BasicHttpResponse); | 92 new net::test_server::BasicHttpResponse); |
| (...skipping 275 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 355 // two. | 368 // two. |
| 356 class CrossSitePredictorObserver | 369 class CrossSitePredictorObserver |
| 357 : public chrome_browser_net::PredictorObserver { | 370 : public chrome_browser_net::PredictorObserver { |
| 358 public: | 371 public: |
| 359 CrossSitePredictorObserver(const GURL& source_host, | 372 CrossSitePredictorObserver(const GURL& source_host, |
| 360 const GURL& cross_site_host) | 373 const GURL& cross_site_host) |
| 361 : source_host_(source_host), | 374 : source_host_(source_host), |
| 362 cross_site_host_(cross_site_host), | 375 cross_site_host_(cross_site_host), |
| 363 cross_site_learned_(0), | 376 cross_site_learned_(0), |
| 364 cross_site_preconnected_(0), | 377 cross_site_preconnected_(0), |
| 365 same_site_preconnected_(0) {} | 378 same_site_preconnected_(0), |
| 379 strict_(true) {} | |
| 366 | 380 |
| 367 void OnPreconnectUrl( | 381 void OnPreconnectUrl( |
| 368 const GURL& original_url, | 382 const GURL& original_url, |
| 369 const GURL& first_party_for_cookies, | 383 const GURL& first_party_for_cookies, |
| 370 chrome_browser_net::UrlInfo::ResolutionMotivation motivation, | 384 chrome_browser_net::UrlInfo::ResolutionMotivation motivation, |
| 371 int count) override { | 385 int count) override { |
| 372 base::AutoLock lock(lock_); | 386 base::AutoLock lock(lock_); |
| 373 if (original_url == cross_site_host_) { | 387 if (original_url == cross_site_host_) { |
| 374 cross_site_preconnected_ = std::max(cross_site_preconnected_, count); | 388 cross_site_preconnected_ = std::max(cross_site_preconnected_, count); |
| 375 } else if (original_url == source_host_) { | 389 } else if (original_url == source_host_) { |
| 376 same_site_preconnected_ = std::max(same_site_preconnected_, count); | 390 same_site_preconnected_ = std::max(same_site_preconnected_, count); |
| 377 } else { | 391 } else if (strict_) { |
| 378 ADD_FAILURE() << "Preconnected " << original_url | 392 ADD_FAILURE() << "Preconnected " << original_url |
| 379 << " when should only be preconnecting the source host: " | 393 << " when should only be preconnecting the source host: " |
| 380 << source_host_ | 394 << source_host_ |
| 381 << " or the cross site host: " << cross_site_host_; | 395 << " or the cross site host: " << cross_site_host_; |
| 382 } | 396 } |
| 383 } | 397 } |
| 384 | 398 |
| 385 void OnLearnFromNavigation(const GURL& referring_url, | 399 void OnLearnFromNavigation(const GURL& referring_url, |
| 386 const GURL& target_url) override { | 400 const GURL& target_url) override { |
| 387 base::AutoLock lock(lock_); | 401 base::AutoLock lock(lock_); |
| 388 // There are three possibilities: | 402 // There are three possibilities: |
| 389 // source => target | 403 // source => target |
| 390 // source => source | 404 // source => source |
| 391 // target => target | 405 // target => target |
| 392 if (referring_url == source_host_ && target_url == cross_site_host_) { | 406 if (referring_url == source_host_ && target_url == cross_site_host_) { |
| 393 cross_site_learned_++; | 407 cross_site_learned_++; |
| 394 } else if (referring_url == source_host_ && target_url == source_host_) { | 408 } else if (referring_url == source_host_ && target_url == source_host_) { |
| 395 // Same site learned. Branch retained for clarity. | 409 // Same site learned. Branch retained for clarity. |
| 396 } else if (!(referring_url == cross_site_host_ && | 410 } else if (strict_ && |
| 411 !(referring_url == cross_site_host_ && | |
| 397 target_url == cross_site_host_)) { | 412 target_url == cross_site_host_)) { |
| 398 ADD_FAILURE() << "Learned " << referring_url << " => " << target_url | 413 ADD_FAILURE() << "Learned " << referring_url << " => " << target_url |
| 399 << " when should only be learning the source host: " | 414 << " when should only be learning the source host: " |
| 400 << source_host_ | 415 << source_host_ |
| 401 << " or the cross site host: " << cross_site_host_; | 416 << " or the cross site host: " << cross_site_host_; |
| 402 } | 417 } |
| 403 } | 418 } |
| 404 | 419 |
| 405 void ResetCounts() { | 420 void ResetCounts() { |
| 406 base::AutoLock lock(lock_); | 421 base::AutoLock lock(lock_); |
| (...skipping 10 matching lines...) Expand all Loading... | |
| 417 int CrossSitePreconnected() { | 432 int CrossSitePreconnected() { |
| 418 base::AutoLock lock(lock_); | 433 base::AutoLock lock(lock_); |
| 419 return cross_site_preconnected_; | 434 return cross_site_preconnected_; |
| 420 } | 435 } |
| 421 | 436 |
| 422 int SameSitePreconnected() { | 437 int SameSitePreconnected() { |
| 423 base::AutoLock lock(lock_); | 438 base::AutoLock lock(lock_); |
| 424 return same_site_preconnected_; | 439 return same_site_preconnected_; |
| 425 } | 440 } |
| 426 | 441 |
| 442 // Optionally allows the object to observe preconnects / learning from other | |
| 443 // hosts. | |
| 444 void SetStrict(bool strict) { | |
| 445 base::AutoLock lock(lock_); | |
| 446 strict_ = strict; | |
| 447 } | |
| 448 | |
| 427 private: | 449 private: |
| 428 const GURL source_host_; | 450 const GURL source_host_; |
| 429 const GURL cross_site_host_; | 451 const GURL cross_site_host_; |
| 430 | 452 |
| 431 // Protects all following members. They are read and updated from different | 453 // Protects all following members. They are read and updated from different |
| 432 // threads. | 454 // threads. |
| 433 base::Lock lock_; | 455 base::Lock lock_; |
| 434 | 456 |
| 435 int cross_site_learned_; | 457 int cross_site_learned_; |
| 436 int cross_site_preconnected_; | 458 int cross_site_preconnected_; |
| 437 int same_site_preconnected_; | 459 int same_site_preconnected_; |
| 438 | 460 |
| 461 // This member can be set to optionally allow url learning other than from | |
| 462 // source => source, source => target, or target => target. It will also allow | |
| 463 // preconnects to other hosts. | |
| 464 bool strict_; | |
| 465 | |
| 439 DISALLOW_COPY_AND_ASSIGN(CrossSitePredictorObserver); | 466 DISALLOW_COPY_AND_ASSIGN(CrossSitePredictorObserver); |
| 440 }; | 467 }; |
| 441 | 468 |
| 442 } // namespace | 469 } // namespace |
| 443 | 470 |
| 444 namespace chrome_browser_net { | 471 namespace chrome_browser_net { |
| 445 | 472 |
| 446 class PredictorBrowserTest : public InProcessBrowserTest { | 473 class PredictorBrowserTest : public InProcessBrowserTest { |
| 447 public: | 474 public: |
| 448 PredictorBrowserTest() | 475 PredictorBrowserTest() |
| (...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 484 embedded_test_server()->RegisterRequestHandler( | 511 embedded_test_server()->RegisterRequestHandler( |
| 485 base::Bind(&RedirectForPathHandler, "/", | 512 base::Bind(&RedirectForPathHandler, "/", |
| 486 cross_site_test_server()->GetURL("/title1.html"))); | 513 cross_site_test_server()->GetURL("/title1.html"))); |
| 487 | 514 |
| 488 predictor()->SetPreconnectEnabledForTest(true); | 515 predictor()->SetPreconnectEnabledForTest(true); |
| 489 InstallPredictorObserver(embedded_test_server()->base_url(), | 516 InstallPredictorObserver(embedded_test_server()->base_url(), |
| 490 cross_site_test_server()->base_url()); | 517 cross_site_test_server()->base_url()); |
| 491 StartInterceptingCrossSiteOnUI(); | 518 StartInterceptingCrossSiteOnUI(); |
| 492 } | 519 } |
| 493 | 520 |
| 494 // Intercepts all requests to the specified host and returns a response with | 521 static void StartInterceptingHostWithCreateJobCallback( |
| 495 // an empty body. Needed to prevent requests from actually going to the test | 522 const GURL& url, |
| 496 // server, to avoid any races related to socket accounting. Note, the | 523 const MatchingPortRequestInterceptor::CreateJobCallback& callback) { |
| 497 // interceptor also looks at the port, to differentiate between the | |
| 498 // two test servers. | |
| 499 static void StartInterceptingHost(const GURL& url) { | |
| 500 DCHECK_CURRENTLY_ON(BrowserThread::IO); | 524 DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| 501 net::URLRequestFilter::GetInstance()->AddHostnameInterceptor( | 525 net::URLRequestFilter::GetInstance()->AddHostnameInterceptor( |
| 502 url.scheme(), url.host(), | 526 url.scheme(), url.host(), |
| 503 base::WrapUnique(new MatchingPortRequestInterceptor( | 527 base::WrapUnique(new MatchingPortRequestInterceptor( |
| 504 url.EffectiveIntPort(), base::Bind(&CreateEmptyBodyRequestJob)))); | 528 url.EffectiveIntPort(), callback))); |
| 505 } | 529 } |
| 506 | 530 |
| 507 static void StopInterceptingHost(const GURL& url) { | 531 static void StopInterceptingHost(const GURL& url) { |
| 508 DCHECK_CURRENTLY_ON(BrowserThread::IO); | 532 DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| 509 net::URLRequestFilter::GetInstance()->RemoveHostnameHandler(url.scheme(), | 533 net::URLRequestFilter::GetInstance()->RemoveHostnameHandler(url.scheme(), |
| 510 url.host()); | 534 url.host()); |
| 511 } | 535 } |
| 512 | 536 |
| 537 // Intercepts all requests to the specified host and returns a response with | |
| 538 // an empty body. Needed to prevent requests from actually going to the test | |
| 539 // server, to avoid any races related to socket accounting. Note, the | |
| 540 // interceptor also looks at the port, to differentiate between the | |
| 541 // two test servers. | |
| 513 void StartInterceptingCrossSiteOnUI() { | 542 void StartInterceptingCrossSiteOnUI() { |
| 514 DCHECK_CURRENTLY_ON(BrowserThread::UI); | 543 DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| 515 content::BrowserThread::PostTask( | 544 content::BrowserThread::PostTask( |
| 516 content::BrowserThread::IO, FROM_HERE, | 545 content::BrowserThread::IO, FROM_HERE, |
| 517 base::Bind(&PredictorBrowserTest::StartInterceptingHost, | 546 base::Bind( |
| 518 cross_site_test_server()->base_url())); | 547 &PredictorBrowserTest::StartInterceptingHostWithCreateJobCallback, |
| 548 cross_site_test_server()->base_url(), | |
| 549 base::Bind(&CreateEmptyBodyRequestJob))); | |
| 519 } | 550 } |
| 520 | 551 |
| 521 void StopInterceptingCrossSiteOnUI() { | 552 void StopInterceptingCrossSiteOnUI() { |
| 522 DCHECK_CURRENTLY_ON(BrowserThread::UI); | 553 DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| 523 content::BrowserThread::PostTask( | 554 content::BrowserThread::PostTask( |
| 524 content::BrowserThread::IO, FROM_HERE, | 555 content::BrowserThread::IO, FROM_HERE, |
| 525 base::Bind(&PredictorBrowserTest::StopInterceptingHost, | 556 base::Bind(&PredictorBrowserTest::StopInterceptingHost, |
| 526 cross_site_test_server()->base_url())); | 557 cross_site_test_server()->base_url())); |
| 527 } | 558 } |
| 528 | 559 |
| (...skipping 241 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 770 // embedded test servers have the same host_piece, an extra preconnect is | 801 // embedded test servers have the same host_piece, an extra preconnect is |
| 771 // issued. This results in ceil(2.33) + 1 = 4 preconnects. | 802 // issued. This results in ceil(2.33) + 1 = 4 preconnects. |
| 772 ui_test_utils::NavigateToURL(browser(), | 803 ui_test_utils::NavigateToURL(browser(), |
| 773 embedded_test_server()->GetURL("/title1.html")); | 804 embedded_test_server()->GetURL("/title1.html")); |
| 774 // Just check that predictor has initiated preconnects to the cross site test | 805 // Just check that predictor has initiated preconnects to the cross site test |
| 775 // server. It's tricky to reset the connections to the test server, and | 806 // server. It's tricky to reset the connections to the test server, and |
| 776 // sockets can be reused. | 807 // sockets can be reused. |
| 777 EXPECT_EQ(4, observer()->CrossSitePreconnected()); | 808 EXPECT_EQ(4, observer()->CrossSitePreconnected()); |
| 778 } | 809 } |
| 779 | 810 |
| 811 // 1. Navigate to A.com learning B.com | |
| 812 // 2. Navigate to B.com with subresource from C.com redirecting to A.com. | |
| 813 // 3. Assert that the redirect does not cause us to preconnect to B.com (via | |
| 814 // A.com). | |
| 815 IN_PROC_BROWSER_TEST_F(PredictorBrowserTest, DontPredictBasedOnSubresources) { | |
| 816 GURL redirector_url = GURL("http://redirector.com"); | |
| 817 | |
| 818 NavigateToCrossSiteHtmlUrl(1 /* num_cors */, "" /* file_suffix */); | |
| 819 EXPECT_EQ(1, observer()->CrossSiteLearned()); | |
| 820 EXPECT_EQ(0, observer()->CrossSitePreconnected()); | |
| 821 | |
| 822 EXPECT_EQ(0u, cross_site_connection_listener_->GetAcceptedSocketCount()); | |
| 823 | |
| 824 // Stop intercepting so that the test can actually navigate to the cross site | |
| 825 // server. | |
| 826 StopInterceptingCrossSiteOnUI(); | |
| 827 | |
| 828 // All requests with the redirector url as base url should redirect to the | |
| 829 // embedded_test_server_. | |
| 830 content::BrowserThread::PostTask( | |
| 831 content::BrowserThread::IO, FROM_HERE, | |
| 832 base::Bind( | |
| 833 &PredictorBrowserTest::StartInterceptingHostWithCreateJobCallback, | |
| 834 redirector_url, | |
| 835 base::Bind( | |
| 836 &CreateRedirectRequestJob, | |
| 837 embedded_test_server()->GetURL("/predictor/empty.js").spec()))); | |
| 838 | |
| 839 // Reduce the strictness, because the below logic causes the predictor to | |
| 840 // learn cross_site_test_server_ => redirector, as well as | |
| 841 // cross_site_test_server_ => embedded_test_server_ (via referrer header). | |
| 842 observer()->SetStrict(false); | |
| 843 | |
| 844 GURL redirecting_url = cross_site_test_server()->GetURL(base::StringPrintf( | |
|
mmenke
2016/05/25 18:16:04
The URL doesn't redirect, it requests a subresourc
Charlie Harrison
2016/05/25 18:45:15
Done.
| |
| 845 "/predictor/" | |
| 846 "predictor_cross_site.html?subresourceHost=%s&numCORSResources=1", | |
| 847 redirector_url.spec().c_str())); | |
| 848 ui_test_utils::NavigateToURL(browser(), redirecting_url); | |
| 849 bool result = false; | |
| 850 | |
| 851 int navigation_preconnects = observer()->CrossSitePreconnected(); | |
|
mmenke
2016/05/25 18:16:04
Should we call WaitForAcceptedConnectionsOnUI? Lo
Charlie Harrison
2016/05/25 18:45:15
I don't think we should here. We stopped blocking
| |
| 852 EXPECT_EQ(2, navigation_preconnects); | |
| 853 | |
| 854 EXPECT_TRUE(content::ExecuteScriptAndExtractBool( | |
| 855 browser()->tab_strip_model()->GetActiveWebContents(), | |
| 856 "startFetchesAndWaitForReply()", &result)); | |
| 857 EXPECT_TRUE(result); | |
| 858 | |
| 859 // The number of preconnects should not increase. Note that the predictor | |
| 860 // would preconnect 4 sockets if it were doing so based on learning. | |
| 861 EXPECT_EQ(navigation_preconnects, observer()->CrossSitePreconnected()); | |
| 862 } | |
| 863 | |
| 864 IN_PROC_BROWSER_TEST_F(PredictorBrowserTest, PredictBasedOnSubframeRedirect) { | |
| 865 // A test server is needed here because data url navigations with redirect | |
| 866 // interceptors don't interact well with the ResourceTiming API. | |
| 867 // TODO(csharrison): Possibly this is a bug in either net or Blink, and it | |
| 868 // might be worthwhile to investigate. | |
| 869 std::unique_ptr<net::EmbeddedTestServer> redirector = | |
| 870 base::WrapUnique(new net::EmbeddedTestServer()); | |
| 871 ASSERT_TRUE(redirector->Start()); | |
| 872 | |
| 873 NavigateToCrossSiteHtmlUrl(1 /* num_cors */, "" /* file_suffix */); | |
| 874 EXPECT_EQ(1, observer()->CrossSiteLearned()); | |
| 875 EXPECT_EQ(0u, cross_site_connection_listener_->GetAcceptedSocketCount()); | |
| 876 | |
| 877 redirector->RegisterRequestHandler( | |
| 878 base::Bind(&RedirectForPathHandler, "/", | |
| 879 embedded_test_server()->GetURL("/title1.html"))); | |
| 880 | |
| 881 // Note that the observer will see preconnects to the redirector, and the | |
| 882 // predictor will learn redirector->embedded_test_server. | |
| 883 observer()->SetStrict(false); | |
| 884 | |
| 885 NavigateToDataURLWithContent(base::StringPrintf( | |
| 886 "<iframe src='%s'></iframe>", redirector->base_url().spec().c_str())); | |
| 887 | |
|
mmenke
2016/05/25 18:16:04
WaitForAcceptedConnectionsOnUI?
Charlie Harrison
2016/05/25 18:45:15
We do the check after checking observer()->CrossSi
| |
| 888 EXPECT_EQ(4, observer()->CrossSitePreconnected()); | |
| 889 cross_site_connection_listener_->WaitForAcceptedConnectionsOnUI(4u); | |
| 890 } | |
| 891 | |
| 780 // Expect that the predictor correctly predicts subframe navigations. | 892 // Expect that the predictor correctly predicts subframe navigations. |
| 781 IN_PROC_BROWSER_TEST_F(PredictorBrowserTest, SubframeCrossSitePrediction) { | 893 IN_PROC_BROWSER_TEST_F(PredictorBrowserTest, SubframeCrossSitePrediction) { |
| 782 ui_test_utils::NavigateToURL( | 894 ui_test_utils::NavigateToURL( |
| 783 browser(), embedded_test_server()->GetURL( | 895 browser(), embedded_test_server()->GetURL( |
| 784 "/predictor/predictor_cross_site_subframe_nav.html")); | 896 "/predictor/predictor_cross_site_subframe_nav.html")); |
| 785 bool result = false; | 897 bool result = false; |
| 786 EXPECT_TRUE(content::ExecuteScriptAndExtractBool( | 898 EXPECT_TRUE(content::ExecuteScriptAndExtractBool( |
| 787 browser()->tab_strip_model()->GetActiveWebContents(), | 899 browser()->tab_strip_model()->GetActiveWebContents(), |
| 788 base::StringPrintf( | 900 base::StringPrintf( |
| 789 "navigateSubframe('%s')", | 901 "navigateSubframe('%s')", |
| (...skipping 459 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1249 // Second navigation to content with an img. | 1361 // Second navigation to content with an img. |
| 1250 std::string img_content = | 1362 std::string img_content = |
| 1251 "<img src=\"" + preconnect_url.spec() + "test.gif\">"; | 1363 "<img src=\"" + preconnect_url.spec() + "test.gif\">"; |
| 1252 NavigateToDataURLWithContent(img_content); | 1364 NavigateToDataURLWithContent(img_content); |
| 1253 connection_listener_->WaitUntilFirstConnectionRead(); | 1365 connection_listener_->WaitUntilFirstConnectionRead(); |
| 1254 EXPECT_EQ(2u, connection_listener_->GetAcceptedSocketCount()); | 1366 EXPECT_EQ(2u, connection_listener_->GetAcceptedSocketCount()); |
| 1255 EXPECT_EQ(1u, connection_listener_->GetReadSocketCount()); | 1367 EXPECT_EQ(1u, connection_listener_->GetReadSocketCount()); |
| 1256 } | 1368 } |
| 1257 | 1369 |
| 1258 } // namespace chrome_browser_net | 1370 } // namespace chrome_browser_net |
| OLD | NEW |