Commit abc94c12 authored by Robert Ogden's avatar Robert Ogden Committed by Commit Bot

Add response reactions to HTTPS Server Previews

Loads the original page on a 404 or 503 server response. Further actions
for these response types will be in follow up CLs.

Bug: 864656
Change-Id: I861685e0d19f5597ade3ed814e9847a3595d9e31
Reviewed-on: https://chromium-review.googlesource.com/1171799
Commit-Queue: Robert Ogden <robertogden@chromium.org>
Reviewed-by: default avatarRyan Sturm <ryansturm@chromium.org>
Cr-Commit-Position: refs/heads/master@{#583424}
parent 915ab7e9
...@@ -9,10 +9,12 @@ ...@@ -9,10 +9,12 @@
#include "base/metrics/field_trial_param_associator.h" #include "base/metrics/field_trial_param_associator.h"
#include "base/metrics/field_trial_params.h" #include "base/metrics/field_trial_params.h"
#include "base/run_loop.h" #include "base/run_loop.h"
#include "base/strings/string_number_conversions.h"
#include "base/test/metrics/histogram_tester.h" #include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_feature_list.h" #include "base/test/scoped_feature_list.h"
#include "build/build_config.h" #include "build/build_config.h"
#include "chrome/browser/browser_process.h" #include "chrome/browser/browser_process.h"
#include "chrome/browser/previews/previews_lite_page_navigation_throttle.h"
#include "chrome/browser/ui/browser.h" #include "chrome/browser/ui/browser.h"
#include "chrome/test/base/in_process_browser_test.h" #include "chrome/test/base/in_process_browser_test.h"
#include "chrome/test/base/ui_test_utils.h" #include "chrome/test/base/ui_test_utils.h"
...@@ -22,6 +24,7 @@ ...@@ -22,6 +24,7 @@
#include "components/optimization_guide/test_component_creator.h" #include "components/optimization_guide/test_component_creator.h"
#include "components/previews/core/previews_features.h" #include "components/previews/core/previews_features.h"
#include "content/public/test/browser_test_utils.h" #include "content/public/test/browser_test_utils.h"
#include "net/http/http_status_code.h"
#include "net/test/embedded_test_server/http_request.h" #include "net/test/embedded_test_server/http_request.h"
#include "net/test/embedded_test_server/http_response.h" #include "net/test/embedded_test_server/http_response.h"
...@@ -193,9 +196,9 @@ class PreviewsNoScriptBrowserTest : public PreviewsBrowserTest { ...@@ -193,9 +196,9 @@ class PreviewsNoScriptBrowserTest : public PreviewsBrowserTest {
}; };
// Previews InfoBar (which these tests triggers) does not work on Mac. // Previews InfoBar (which these tests triggers) does not work on Mac.
// See crbug.com/782322 for detail. // See https://crbug.com/782322 for detail.
// Also occasional flakes on win7 (crbug.com/789542). // Also occasional flakes on win7 (https://crbug.com/789542).
// Also occasional flakes on Linux Xenial (crbug.com/869781). // Also occasional flakes on Linux Xenial (https://crbug.com/869781).
#if defined(OS_ANDROID) #if defined(OS_ANDROID)
#define MAYBE_NoScriptPreviewsEnabled NoScriptPreviewsEnabled #define MAYBE_NoScriptPreviewsEnabled NoScriptPreviewsEnabled
#define MAYBE_NoScriptPreviewsEnabledHttpRedirectToHttps \ #define MAYBE_NoScriptPreviewsEnabledHttpRedirectToHttps \
...@@ -231,7 +234,8 @@ IN_PROC_BROWSER_TEST_F(PreviewsNoScriptBrowserTest, ...@@ -231,7 +234,8 @@ IN_PROC_BROWSER_TEST_F(PreviewsNoScriptBrowserTest,
EXPECT_FALSE(noscript_css_requested()); EXPECT_FALSE(noscript_css_requested());
} }
// Flaky in all platforms except Android. See crbug.com/803626 for detail. // Flaky in all platforms except Android. See https://crbug.com/803626 for
// detail.
#if defined(OS_ANDROID) #if defined(OS_ANDROID)
#define MAYBE_NoScriptPreviewsEnabledButNoTransformDirective \ #define MAYBE_NoScriptPreviewsEnabledButNoTransformDirective \
NoScriptPreviewsEnabledButNoTransformDirective NoScriptPreviewsEnabledButNoTransformDirective
...@@ -303,9 +307,9 @@ class PreviewsOptimizationGuideBrowserTest : public PreviewsBrowserTest { ...@@ -303,9 +307,9 @@ class PreviewsOptimizationGuideBrowserTest : public PreviewsBrowserTest {
}; };
// Previews InfoBar (which this test triggers) does not work on Mac. // Previews InfoBar (which this test triggers) does not work on Mac.
// See crbug.com/782322 for detail. // See https://crbug.com/782322 for detail.
// Also occasional flakes on win7 (crbug.com/789948) and Ubuntu 16.04 // Also occasional flakes on win7 (https://crbug.com/789948) and Ubuntu 16.04
// (crbug.com/831838) // (https://crbug.com/831838)
#if defined(OS_ANDROID) #if defined(OS_ANDROID)
#define MAYBE_NoScriptPreviewsEnabledByWhitelist \ #define MAYBE_NoScriptPreviewsEnabledByWhitelist \
NoScriptPreviewsEnabledByWhitelist NoScriptPreviewsEnabledByWhitelist
...@@ -348,8 +352,6 @@ IN_PROC_BROWSER_TEST_F(PreviewsOptimizationGuideBrowserTest, ...@@ -348,8 +352,6 @@ IN_PROC_BROWSER_TEST_F(PreviewsOptimizationGuideBrowserTest,
EXPECT_FALSE(noscript_css_requested()); EXPECT_FALSE(noscript_css_requested());
} }
static const std::string kTestPreviewsServer = "https://litepages.test.com/";
// This test class enables LitePageServerPreviews. // This test class enables LitePageServerPreviews.
class PreviewsLitePageServerBrowserTest : public PreviewsBrowserTest { class PreviewsLitePageServerBrowserTest : public PreviewsBrowserTest {
public: public:
...@@ -358,16 +360,23 @@ class PreviewsLitePageServerBrowserTest : public PreviewsBrowserTest { ...@@ -358,16 +360,23 @@ class PreviewsLitePageServerBrowserTest : public PreviewsBrowserTest {
~PreviewsLitePageServerBrowserTest() override {} ~PreviewsLitePageServerBrowserTest() override {}
void SetUp() override { void SetUp() override {
// Set up previews server with resource handler.
previews_server_.reset(
new net::EmbeddedTestServer(net::EmbeddedTestServer::TYPE_HTTPS));
previews_server_->RegisterRequestHandler(base::BindRepeating(
&PreviewsLitePageServerBrowserTest::HandleResourceRequest,
base::Unretained(this)));
ASSERT_TRUE(previews_server_->Start());
std::unique_ptr<base::FeatureList> feature_list(new base::FeatureList); std::unique_ptr<base::FeatureList> feature_list(new base::FeatureList);
{ {
// The trial and group names are dummy values. // The trial and group names are dummy values.
scoped_refptr<base::FieldTrial> trial = scoped_refptr<base::FieldTrial> trial =
base::FieldTrialList::CreateFieldTrial("TrialName1", "GroupName1"); base::FieldTrialList::CreateFieldTrial("TrialName1", "GroupName1");
std::map<std::string, std::string> feature_parameters = { std::map<std::string, std::string> feature_parameters = {
{"previews_host", kTestPreviewsServer}}; {"previews_host", previews_server().spec()}};
ASSERT_TRUE(base::FieldTrialParamAssociator::GetInstance() base::FieldTrialParamAssociator::GetInstance()->AssociateFieldTrialParams(
->AssociateFieldTrialParams("TrialName1", "GroupName1", "TrialName1", "GroupName1", feature_parameters);
feature_parameters));
feature_list->RegisterFieldTrialOverride( feature_list->RegisterFieldTrialOverride(
previews::features::kLitePageServerPreviews.name, previews::features::kLitePageServerPreviews.name,
...@@ -386,25 +395,98 @@ class PreviewsLitePageServerBrowserTest : public PreviewsBrowserTest { ...@@ -386,25 +395,98 @@ class PreviewsLitePageServerBrowserTest : public PreviewsBrowserTest {
PreviewsBrowserTest::SetUp(); PreviewsBrowserTest::SetUp();
} }
void SetUpCommandLine(base::CommandLine* cmd) override {
PreviewsBrowserTest::SetUpCommandLine(cmd);
// Resolve all localhost subdomains to plain localhost so that Chrome's Test
// DNS resolver doesn't get upset.
cmd->AppendSwitchASCII(
"host-rules", "MAP *.localhost 127.0.0.1, MAP *.127.0.0.1 127.0.0.1");
}
std::unique_ptr<net::test_server::HttpResponse> HandleResourceRequest(
const net::test_server::HttpRequest& request) {
std::unique_ptr<net::test_server::BasicHttpResponse> response(
new net::test_server::BasicHttpResponse);
std::string original_url;
// Ignore anything that's not a previews request with an unused status.
if (!PreviewsLitePageNavigationThrottle::GetOriginalURL(request.GetURL(),
&original_url)) {
response->set_code(net::HttpStatusCode::HTTP_BAD_REQUEST);
return response;
}
std::string query_param;
int return_code = 0;
if (net::GetValueForKeyInQuery(GURL(original_url), "resp", &query_param))
base::StringToInt(query_param, &return_code);
switch (return_code) {
case 200:
response->set_code(net::HTTP_OK);
break;
case 307:
response->set_code(net::HTTP_TEMPORARY_REDIRECT);
response->AddCustomHeader("Location", https_lite_page_url(200).spec());
break;
case 404:
response->set_code(net::HTTP_NOT_FOUND);
break;
case 503:
response->set_code(net::HTTP_SERVICE_UNAVAILABLE);
break;
default:
response->set_code(net::HTTP_BAD_REQUEST);
response->set_code(net::HTTP_OK);
break;
}
return std::move(response);
}
GURL previews_server() { return previews_server_->base_url(); }
GURL http_lite_page_url(int return_code) {
std::string query = "resp=" + base::IntToString(return_code);
GURL::Replacements replacements;
replacements.SetQuery(query.c_str(), url::Component(0, query.length()));
return http_url().ReplaceComponents(replacements);
}
GURL https_lite_page_url(int return_code) {
std::string query = "resp=" + base::IntToString(return_code);
GURL::Replacements replacements;
replacements.SetQuery(query.c_str(), url::Component(0, query.length()));
return https_url().ReplaceComponents(replacements);
}
GURL NavigatedURL() { GURL NavigatedURL() {
return browser()->tab_strip_model()->GetActiveWebContents()->GetURL(); return browser()->tab_strip_model()->GetActiveWebContents()->GetURL();
} }
void VerifyPreviewLoaded() { void VerifyPreviewLoaded() {
EXPECT_TRUE(NavigatedURL().DomainIs(GURL(kTestPreviewsServer).host())); const GURL navigated_url = NavigatedURL();
const GURL previews_host = previews_server();
EXPECT_TRUE(navigated_url.DomainIs(previews_host.host()) &&
navigated_url.EffectiveIntPort() ==
previews_host.EffectiveIntPort());
} }
void VerifyPreviewNotLoaded() { void VerifyPreviewNotLoaded() {
EXPECT_FALSE(NavigatedURL().DomainIs(GURL(kTestPreviewsServer).host())); const GURL navigated_url = NavigatedURL();
const GURL previews_host = previews_server();
EXPECT_FALSE(navigated_url.DomainIs(previews_host.host()) &&
navigated_url.EffectiveIntPort() ==
previews_host.EffectiveIntPort());
} }
private: private:
base::test::ScopedFeatureList scoped_feature_list_; base::test::ScopedFeatureList scoped_feature_list_;
std::unique_ptr<net::EmbeddedTestServer> previews_server_;
}; };
// Previews InfoBar (which these tests trigger) does not work on Mac. // Previews InfoBar (which these tests trigger) does not work on Mac.
// See crbug.com/782322 for detail. // See https://crbug.com/782322 for detail.
// Also occasional flakes on win7 (crbug.com/789542). // Also occasional flakes on win7 (https://crbug.com/789542).
#if defined(OS_ANDROID) || defined(OS_LINUX) #if defined(OS_ANDROID) || defined(OS_LINUX)
#define MAYBE_LitePagePreviewsTriggering LitePagePreviewsTriggering #define MAYBE_LitePagePreviewsTriggering LitePagePreviewsTriggering
#else #else
...@@ -414,15 +496,11 @@ class PreviewsLitePageServerBrowserTest : public PreviewsBrowserTest { ...@@ -414,15 +496,11 @@ class PreviewsLitePageServerBrowserTest : public PreviewsBrowserTest {
IN_PROC_BROWSER_TEST_F(PreviewsLitePageServerBrowserTest, IN_PROC_BROWSER_TEST_F(PreviewsLitePageServerBrowserTest,
MAYBE_LitePagePreviewsTriggering) { MAYBE_LitePagePreviewsTriggering) {
// Verify the preview is not triggered on HTTP pageloads. // Verify the preview is not triggered on HTTP pageloads.
ui_test_utils::NavigateToURL(browser(), http_url()); ui_test_utils::NavigateToURL(browser(), http_lite_page_url(200));
VerifyPreviewNotLoaded(); VerifyPreviewNotLoaded();
// Verify the preview is triggered on HTTPS pageloads. // Verify the preview is triggered on HTTPS pageloads.
ui_test_utils::NavigateToURL(browser(), https_url()); ui_test_utils::NavigateToURL(browser(), https_lite_page_url(200));
VerifyPreviewLoaded();
// Verify the preview is triggered when an HTTP page redirects to HTTPS.
ui_test_utils::NavigateToURL(browser(), redirect_url());
VerifyPreviewLoaded(); VerifyPreviewLoaded();
// Verify the preview is not triggered for POST navigations. // Verify the preview is not triggered for POST navigations.
...@@ -438,8 +516,8 @@ IN_PROC_BROWSER_TEST_F(PreviewsLitePageServerBrowserTest, ...@@ -438,8 +516,8 @@ IN_PROC_BROWSER_TEST_F(PreviewsLitePageServerBrowserTest,
VerifyPreviewNotLoaded(); VerifyPreviewNotLoaded();
// Verify the preview is not triggered when navigating to the previews server. // Verify the preview is not triggered when navigating to the previews server.
ui_test_utils::NavigateToURL(browser(), GURL(kTestPreviewsServer)); ui_test_utils::NavigateToURL(browser(), previews_server());
EXPECT_EQ(NavigatedURL(), GURL(kTestPreviewsServer)); EXPECT_EQ(NavigatedURL(), previews_server());
// Verify a subframe navigation does not trigger a preview. // Verify a subframe navigation does not trigger a preview.
const int starting_https_url_count = https_url_count(); const int starting_https_url_count = https_url_count();
...@@ -448,6 +526,44 @@ IN_PROC_BROWSER_TEST_F(PreviewsLitePageServerBrowserTest, ...@@ -448,6 +526,44 @@ IN_PROC_BROWSER_TEST_F(PreviewsLitePageServerBrowserTest,
EXPECT_EQ(https_url_count(), starting_https_url_count + 1); EXPECT_EQ(https_url_count(), starting_https_url_count + 1);
} }
// Previews InfoBar (which these tests trigger) does not work on Mac.
// See https://crbug.com/782322 for detail.
// Also occasional flakes on win7 (https://crbug.com/789542).
#if defined(OS_ANDROID) || defined(OS_LINUX)
#define MAYBE_LitePagePreviewsRedirect LitePagePreviewsRedirect
#else
#define MAYBE_LitePagePreviewsRedirect DISABLED_LitePagePreviewsRedirect
#endif
IN_PROC_BROWSER_TEST_F(PreviewsLitePageServerBrowserTest,
MAYBE_LitePagePreviewsRedirect) {
// Verify the preview is triggered when an HTTP page redirects to HTTPS.
ui_test_utils::NavigateToURL(browser(), redirect_url());
VerifyPreviewLoaded();
// Verify the preview is triggered when an HTTPS page redirects to HTTPS.
ui_test_utils::NavigateToURL(browser(), https_lite_page_url(307));
VerifyPreviewLoaded();
}
// Previews InfoBar (which these tests trigger) does not work on Mac.
// See https://crbug.com/782322 for detail.
// Also occasional flakes on win7 (https://crbug.com/789542).
#if defined(OS_ANDROID) || defined(OS_LINUX)
#define MAYBE_LitePagePreviewsResponse LitePagePreviewsResponse
#else
#define MAYBE_LitePagePreviewsResponse DISABLED_LitePagePreviewsResponse
#endif
IN_PROC_BROWSER_TEST_F(PreviewsLitePageServerBrowserTest,
MAYBE_LitePagePreviewsResponse) {
// Verify the preview is not triggered when the server responds with 404.
ui_test_utils::NavigateToURL(browser(), https_lite_page_url(404));
VerifyPreviewNotLoaded();
// Verify the preview is not triggered when the server responds with 503.
ui_test_utils::NavigateToURL(browser(), https_lite_page_url(503));
VerifyPreviewNotLoaded();
}
class PreviewsLitePageServerDataSaverBrowserTest class PreviewsLitePageServerDataSaverBrowserTest
: public PreviewsLitePageServerBrowserTest { : public PreviewsLitePageServerBrowserTest {
public: public:
...@@ -462,9 +578,17 @@ class PreviewsLitePageServerDataSaverBrowserTest ...@@ -462,9 +578,17 @@ class PreviewsLitePageServerDataSaverBrowserTest
} }
}; };
// Previews InfoBar (which these tests trigger) does not work on Mac.
// See https://crbug.com/782322 for detail.
// Also occasional flakes on win7 (https://crbug.com/789542).
#if defined(OS_ANDROID) || defined(OS_LINUX)
#define MAYBE_LitePagePreviewsDSTriggering LitePagePreviewsDSTriggering
#else
#define MAYBE_LitePagePreviewsDSTriggering DISABLED_LitePagePreviewsDSTriggering
#endif
IN_PROC_BROWSER_TEST_F(PreviewsLitePageServerDataSaverBrowserTest, IN_PROC_BROWSER_TEST_F(PreviewsLitePageServerDataSaverBrowserTest,
MAYBE_LitePagePreviewsTriggering) { MAYBE_LitePagePreviewsDSTriggering) {
// Verify the preview is not triggered on HTTPS pageloads without DataSaver. // Verify the preview is not triggered on HTTPS pageloads without DataSaver.
ui_test_utils::NavigateToURL(browser(), https_url()); ui_test_utils::NavigateToURL(browser(), https_lite_page_url(200));
VerifyPreviewNotLoaded(); VerifyPreviewNotLoaded();
} }
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
#include "chrome/browser/previews/previews_lite_page_decider.h" #include "chrome/browser/previews/previews_lite_page_decider.h"
#include "base/memory/ptr_util.h" #include "base/memory/ptr_util.h"
#include "base/time/default_tick_clock.h"
#include "chrome/browser/net/spdyproxy/data_reduction_proxy_chrome_settings.h" #include "chrome/browser/net/spdyproxy/data_reduction_proxy_chrome_settings.h"
#include "chrome/browser/net/spdyproxy/data_reduction_proxy_chrome_settings_factory.h" #include "chrome/browser/net/spdyproxy/data_reduction_proxy_chrome_settings_factory.h"
#include "chrome/browser/previews/previews_lite_page_navigation_throttle.h" #include "chrome/browser/previews/previews_lite_page_navigation_throttle.h"
...@@ -17,7 +18,8 @@ ...@@ -17,7 +18,8 @@
#include "content/public/browser/navigation_throttle.h" #include "content/public/browser/navigation_throttle.h"
#include "content/public/browser/web_contents.h" #include "content/public/browser/web_contents.h"
PreviewsLitePageDecider::PreviewsLitePageDecider() = default; PreviewsLitePageDecider::PreviewsLitePageDecider()
: clock_(base::DefaultTickClock::GetInstance()) {}
PreviewsLitePageDecider::~PreviewsLitePageDecider() = default; PreviewsLitePageDecider::~PreviewsLitePageDecider() = default;
...@@ -54,6 +56,10 @@ PreviewsLitePageDecider::MaybeCreateThrottleFor( ...@@ -54,6 +56,10 @@ PreviewsLitePageDecider::MaybeCreateThrottleFor(
return nullptr; return nullptr;
} }
void PreviewsLitePageDecider::SetClockForTesting(const base::TickClock* clock) {
clock_ = clock;
}
bool PreviewsLitePageDecider::IsDataSaverEnabled( bool PreviewsLitePageDecider::IsDataSaverEnabled(
content::NavigationHandle* handle) const { content::NavigationHandle* handle) const {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
...@@ -84,3 +90,34 @@ bool PreviewsLitePageDecider::IsServerUnavailable(base::TimeTicks now) { ...@@ -84,3 +90,34 @@ bool PreviewsLitePageDecider::IsServerUnavailable(base::TimeTicks now) {
retry_at_.reset(); retry_at_.reset();
return server_loadshedding; return server_loadshedding;
} }
void PreviewsLitePageDecider::AddSingleBypass(std::string url) {
// Garbage collect any old entries while looking for the one for |url|.
auto entry = single_bypass_.end();
for (auto iter = single_bypass_.begin(); iter != single_bypass_.end();
/* no increment */) {
if (iter->second < clock_->NowTicks()) {
iter = single_bypass_.erase(iter);
continue;
}
if (iter->first == url)
entry = iter;
++iter;
}
// Update the entry for |url|.
const base::TimeTicks ttl =
clock_->NowTicks() + base::TimeDelta::FromMinutes(5);
if (entry == single_bypass_.end()) {
single_bypass_.emplace(url, ttl);
return;
}
entry->second = ttl;
}
bool PreviewsLitePageDecider::CheckSingleBypass(std::string url) {
auto entry = single_bypass_.find(url);
if (entry == single_bypass_.end())
return false;
return entry->second >= clock_->NowTicks();
}
...@@ -5,13 +5,14 @@ ...@@ -5,13 +5,14 @@
#ifndef CHROME_BROWSER_PREVIEWS_PREVIEWS_LITE_PAGE_DECIDER_H_ #ifndef CHROME_BROWSER_PREVIEWS_PREVIEWS_LITE_PAGE_DECIDER_H_
#define CHROME_BROWSER_PREVIEWS_PREVIEWS_LITE_PAGE_DECIDER_H_ #define CHROME_BROWSER_PREVIEWS_PREVIEWS_LITE_PAGE_DECIDER_H_
#include <map>
#include <memory> #include <memory>
#include <unordered_map>
#include "base/gtest_prod_util.h" #include "base/gtest_prod_util.h"
#include "base/macros.h" #include "base/macros.h"
#include "base/optional.h" #include "base/optional.h"
#include "base/sequence_checker.h" #include "base/sequence_checker.h"
#include "base/time/tick_clock.h"
#include "base/time/time.h" #include "base/time/time.h"
#include "chrome/browser/previews/previews_lite_page_navigation_throttle_manager.h" #include "chrome/browser/previews/previews_lite_page_navigation_throttle_manager.h"
...@@ -35,20 +36,33 @@ class PreviewsLitePageDecider ...@@ -35,20 +36,33 @@ class PreviewsLitePageDecider
static std::unique_ptr<content::NavigationThrottle> MaybeCreateThrottleFor( static std::unique_ptr<content::NavigationThrottle> MaybeCreateThrottleFor(
content::NavigationHandle* handle); content::NavigationHandle* handle);
// Sets the internal clock for testing.
void SetClockForTesting(const base::TickClock* clock);
protected: protected:
// Virtual for testing. // Virtual for testing.
virtual bool IsDataSaverEnabled(content::NavigationHandle* handle) const; virtual bool IsDataSaverEnabled(content::NavigationHandle* handle) const;
private: private:
FRIEND_TEST_ALL_PREFIXES(PreviewsLitePageDeciderTest, TestServerUnavailable); FRIEND_TEST_ALL_PREFIXES(PreviewsLitePageDeciderTest, TestServerUnavailable);
FRIEND_TEST_ALL_PREFIXES(PreviewsLitePageDeciderTest, TestSingleBypass);
// PreviewsLitePageNavigationThrottleManager: // PreviewsLitePageNavigationThrottleManager:
void SetServerUnavailableUntil(base::TimeTicks retry_at) override; void SetServerUnavailableUntil(base::TimeTicks retry_at) override;
bool IsServerUnavailable(base::TimeTicks now) override; bool IsServerUnavailable(base::TimeTicks now) override;
void AddSingleBypass(std::string url) override;
bool CheckSingleBypass(std::string url) override;
// The time after which it is ok to send the server more preview requests. // The time after which it is ok to send the server more preview requests.
base::Optional<base::TimeTicks> retry_at_; base::Optional<base::TimeTicks> retry_at_;
// A map that tracks the time at which a URL will stop being bypassed.
std::unordered_map<std::string, base::TimeTicks> single_bypass_;
// The clock used for getting the current time ticks. Use |SetClockForTesting|
// in tests.
const base::TickClock* clock_;
SEQUENCE_CHECKER(sequence_checker_); SEQUENCE_CHECKER(sequence_checker_);
DISALLOW_COPY_AND_ASSIGN(PreviewsLitePageDecider); DISALLOW_COPY_AND_ASSIGN(PreviewsLitePageDecider);
......
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
#include <memory> #include <memory>
#include "base/test/scoped_task_environment.h" #include "base/test/scoped_task_environment.h"
#include "base/test/simple_test_tick_clock.h"
#include "testing/gtest/include/gtest/gtest.h" #include "testing/gtest/include/gtest/gtest.h"
class PreviewsLitePageDeciderTest : public testing::Test { class PreviewsLitePageDeciderTest : public testing::Test {
...@@ -43,3 +44,46 @@ TEST_F(PreviewsLitePageDeciderTest, TestServerUnavailable) { ...@@ -43,3 +44,46 @@ TEST_F(PreviewsLitePageDeciderTest, TestServerUnavailable) {
test_case.want_is_unavailable); test_case.want_is_unavailable);
} }
} }
TEST_F(PreviewsLitePageDeciderTest, TestSingleBypass) {
const std::string kUrl = "http://test.com";
struct TestCase {
std::string add_url;
base::TimeDelta clock_advance;
std::string check_url;
bool want_check;
};
const TestCase kTestCases[]{
{
kUrl, base::TimeDelta::FromMinutes(1), kUrl, true,
},
{
kUrl, base::TimeDelta::FromMinutes(6), kUrl, false,
},
{
"bad", base::TimeDelta::FromMinutes(1), kUrl, false,
},
{
"bad", base::TimeDelta::FromMinutes(6), kUrl, false,
},
{
kUrl, base::TimeDelta::FromMinutes(1), "bad", false,
},
{
kUrl, base::TimeDelta::FromMinutes(6), "bad", false,
},
};
for (const TestCase& test_case : kTestCases) {
std::unique_ptr<PreviewsLitePageDecider> decider =
std::make_unique<PreviewsLitePageDecider>();
std::unique_ptr<base::SimpleTestTickClock> clock =
std::make_unique<base::SimpleTestTickClock>();
decider->SetClockForTesting(clock.get());
decider->AddSingleBypass(test_case.add_url);
clock->Advance(test_case.clock_advance);
EXPECT_EQ(decider->CheckSingleBypass(test_case.check_url),
test_case.want_check);
}
}
...@@ -16,6 +16,9 @@ ...@@ -16,6 +16,9 @@
#include "content/public/common/referrer.h" #include "content/public/common/referrer.h"
#include "crypto/sha2.h" #include "crypto/sha2.h"
#include "net/base/escape.h" #include "net/base/escape.h"
#include "net/base/url_util.h"
#include "net/http/http_status_code.h"
#include "ui/base/window_open_disposition.h"
#include "url/gurl.h" #include "url/gurl.h"
namespace { namespace {
...@@ -26,6 +29,19 @@ bool IsPreviewsDomain(const GURL& url) { ...@@ -26,6 +29,19 @@ bool IsPreviewsDomain(const GURL& url) {
url.EffectiveIntPort() == previews_host.EffectiveIntPort(); url.EffectiveIntPort() == previews_host.EffectiveIntPort();
} }
content::OpenURLParams MakeOpenURLParams(content::NavigationHandle* handle,
GURL url) {
content::OpenURLParams url_params(
url, handle->GetReferrer(), WindowOpenDisposition::CURRENT_TAB,
handle->GetPageTransition(), handle->IsRendererInitiated());
// TODO(crbug.com/864652): Add chrome-proxy headers if this is a Preview.
url_params.redirect_chain = handle->GetRedirectChain();
url_params.frame_tree_node_id = handle->GetFrameTreeNodeId();
url_params.user_gesture = handle->HasUserGesture();
url_params.started_from_context_menu = handle->WasStartedFromContextMenu();
return url_params;
}
} // namespace } // namespace
class WebContentsLifetimeHelper class WebContentsLifetimeHelper
...@@ -79,6 +95,24 @@ bool PreviewsLitePageNavigationThrottle::IsEligibleForPreview() const { ...@@ -79,6 +95,24 @@ bool PreviewsLitePageNavigationThrottle::IsEligibleForPreview() const {
return true; return true;
} }
// static
bool PreviewsLitePageNavigationThrottle::GetOriginalURL(
GURL url,
std::string* original_url) {
if (!url.is_valid())
return false;
if (!IsPreviewsDomain(url))
return false;
std::string original_url_query_param;
if (!net::GetValueForKeyInQuery(url, "u", &original_url_query_param))
return false;
*original_url = original_url_query_param;
return true;
}
GURL PreviewsLitePageNavigationThrottle::GetPreviewsURL() const { GURL PreviewsLitePageNavigationThrottle::GetPreviewsURL() const {
GURL original_url = navigation_handle()->GetURL(); GURL original_url = navigation_handle()->GetURL();
DCHECK(original_url.is_valid()); DCHECK(original_url.is_valid());
...@@ -101,23 +135,8 @@ GURL PreviewsLitePageNavigationThrottle::GetPreviewsURL() const { ...@@ -101,23 +135,8 @@ GURL PreviewsLitePageNavigationThrottle::GetPreviewsURL() const {
} }
content::NavigationThrottle::ThrottleCheckResult content::NavigationThrottle::ThrottleCheckResult
PreviewsLitePageNavigationThrottle::MaybeNavigateToPreview() const { PreviewsLitePageNavigationThrottle::CreateNewNavigation(
if (!IsEligibleForPreview()) { content::OpenURLParams url_params) const {
return content::NavigationThrottle::PROCEED;
}
content::OpenURLParams url_params(GetPreviewsURL(),
navigation_handle()->GetReferrer(),
WindowOpenDisposition::CURRENT_TAB,
navigation_handle()->GetPageTransition(),
navigation_handle()->IsRendererInitiated());
// TODO(crbug.com/864652): Add chrome-proxy headers.
url_params.redirect_chain = navigation_handle()->GetRedirectChain();
url_params.frame_tree_node_id = navigation_handle()->GetFrameTreeNodeId();
url_params.user_gesture = navigation_handle()->HasUserGesture();
url_params.started_from_context_menu =
navigation_handle()->WasStartedFromContextMenu();
// The helper class and its weak pointer protect against the WebContents // The helper class and its weak pointer protect against the WebContents
// dying in-between the PostTask and its execution, resulting in a use after // dying in-between the PostTask and its execution, resulting in a use after
// free bug. Since the helper is a WebContentsUserData, it will be // free bug. Since the helper is a WebContentsUserData, it will be
...@@ -134,6 +153,26 @@ PreviewsLitePageNavigationThrottle::MaybeNavigateToPreview() const { ...@@ -134,6 +153,26 @@ PreviewsLitePageNavigationThrottle::MaybeNavigateToPreview() const {
return content::NavigationThrottle::CANCEL; return content::NavigationThrottle::CANCEL;
} }
content::NavigationThrottle::ThrottleCheckResult
PreviewsLitePageNavigationThrottle::TriggerPreview() const {
content::OpenURLParams url_params =
MakeOpenURLParams(navigation_handle(), GetPreviewsURL());
return CreateNewNavigation(url_params);
}
content::NavigationThrottle::ThrottleCheckResult
PreviewsLitePageNavigationThrottle::MaybeNavigateToPreview() const {
const GURL url = navigation_handle()->GetURL();
if (!IsEligibleForPreview())
return content::NavigationThrottle::PROCEED;
// Make sure we're not trying to bypass this navigation.
if (manager_->CheckSingleBypass(url.spec()))
return content::NavigationThrottle::PROCEED;
return TriggerPreview();
}
content::NavigationThrottle::ThrottleCheckResult content::NavigationThrottle::ThrottleCheckResult
PreviewsLitePageNavigationThrottle::WillStartRequest() { PreviewsLitePageNavigationThrottle::WillStartRequest() {
return MaybeNavigateToPreview(); return MaybeNavigateToPreview();
...@@ -146,12 +185,50 @@ PreviewsLitePageNavigationThrottle::WillRedirectRequest() { ...@@ -146,12 +185,50 @@ PreviewsLitePageNavigationThrottle::WillRedirectRequest() {
content::NavigationThrottle::ThrottleCheckResult content::NavigationThrottle::ThrottleCheckResult
PreviewsLitePageNavigationThrottle::WillFailRequest() { PreviewsLitePageNavigationThrottle::WillFailRequest() {
return content::NavigationThrottle::PROCEED; std::string original_url;
if (!GetOriginalURL(navigation_handle()->GetURL(), &original_url))
return content::NavigationThrottle::PROCEED;
// The Preview was triggered but there was some irrecoverable issue (like
// there is no network connection). Load the original page and let it go
// through the normal process for whatever error it is.
manager_->AddSingleBypass(original_url);
content::OpenURLParams url_params =
MakeOpenURLParams(navigation_handle(), GURL(original_url));
return CreateNewNavigation(url_params);
} }
content::NavigationThrottle::ThrottleCheckResult content::NavigationThrottle::ThrottleCheckResult
PreviewsLitePageNavigationThrottle::WillProcessResponse() { PreviewsLitePageNavigationThrottle::WillProcessResponse() {
return content::NavigationThrottle::PROCEED; const net::HttpResponseHeaders* response_headers =
navigation_handle()->GetResponseHeaders();
if (!response_headers)
return content::NavigationThrottle::PROCEED;
std::string original_url;
if (!GetOriginalURL(navigation_handle()->GetURL(), &original_url)) {
// Return early if this request was not for a Preview.
return content::NavigationThrottle::PROCEED;
}
// After this point, the given response is known to be for a Preview.
// The Previews server will only send the following response codes: 200, 307,
// 404, and 503. 200 and 307 should proceed as normal, 404 and 503 request the
// client to load the original page instead because the server is not capable
// of generating a lite page. All other response codes are treated as a 404.
const int response_code = response_headers->response_code();
if (response_code == net::HTTP_OK ||
response_code == net::HTTP_TEMPORARY_REDIRECT)
return content::NavigationThrottle::PROCEED;
manager_->AddSingleBypass(original_url);
content::OpenURLParams url_params =
MakeOpenURLParams(navigation_handle(), GURL(original_url));
return CreateNewNavigation(url_params);
} }
const char* PreviewsLitePageNavigationThrottle::GetNameForLogging() { const char* PreviewsLitePageNavigationThrottle::GetNameForLogging() {
......
...@@ -10,6 +10,10 @@ ...@@ -10,6 +10,10 @@
#include "content/public/browser/navigation_handle.h" #include "content/public/browser/navigation_handle.h"
#include "content/public/browser/navigation_throttle.h" #include "content/public/browser/navigation_throttle.h"
namespace content {
struct OpenURLParams;
}
// This class does the actual decision making about when to serve a Lite Page // This class does the actual decision making about when to serve a Lite Page
// Server Preview, and the legwork to trigger the Preview navigation. When a // Server Preview, and the legwork to trigger the Preview navigation. When a
// Preview is triggered, it will cancel the incoming navigation and PostTask a // Preview is triggered, it will cancel the incoming navigation and PostTask a
...@@ -22,6 +26,10 @@ class PreviewsLitePageNavigationThrottle : public content::NavigationThrottle { ...@@ -22,6 +26,10 @@ class PreviewsLitePageNavigationThrottle : public content::NavigationThrottle {
~PreviewsLitePageNavigationThrottle() override; ~PreviewsLitePageNavigationThrottle() override;
// Attempts to extract the original URL from the given Previews URL. Returns
// false if |url| is not a valid Preview URL.
static bool GetOriginalURL(GURL url, std::string* original_url);
private: private:
FRIEND_TEST_ALL_PREFIXES(PreviewsLitePageNavigationThrottleTest, FRIEND_TEST_ALL_PREFIXES(PreviewsLitePageNavigationThrottleTest,
TestGetPreviewsURL); TestGetPreviewsURL);
...@@ -44,6 +52,19 @@ class PreviewsLitePageNavigationThrottle : public content::NavigationThrottle { ...@@ -44,6 +52,19 @@ class PreviewsLitePageNavigationThrottle : public content::NavigationThrottle {
content::NavigationThrottle::ThrottleCheckResult MaybeNavigateToPreview() content::NavigationThrottle::ThrottleCheckResult MaybeNavigateToPreview()
const; const;
// Can be called by any of the content::NavigationThrottle implementation
// methods to create a new navigation with the give |content::OpenURLParams|.
// Returns the |content::NavigationThrottle::ThrottleCheckResult| for the
// implemented method to return.
content::NavigationThrottle::ThrottleCheckResult CreateNewNavigation(
content::OpenURLParams url_params) const;
// Can be called by any of the content::NavigationThrottle implementation
// methods to trigger the preview. Returns the
// |content::NavigationThrottle::ThrottleCheckResult| for the implemented
// method to return.
content::NavigationThrottle::ThrottleCheckResult TriggerPreview() const;
// content::NavigationThrottle implementation: // content::NavigationThrottle implementation:
content::NavigationThrottle::ThrottleCheckResult WillStartRequest() override; content::NavigationThrottle::ThrottleCheckResult WillStartRequest() override;
content::NavigationThrottle::ThrottleCheckResult WillRedirectRequest() content::NavigationThrottle::ThrottleCheckResult WillRedirectRequest()
......
...@@ -21,6 +21,13 @@ class PreviewsLitePageNavigationThrottleManager { ...@@ -21,6 +21,13 @@ class PreviewsLitePageNavigationThrottleManager {
// Returns true if a Preview should not be triggered because the server is // Returns true if a Preview should not be triggered because the server is
// unavailable. // unavailable.
virtual bool IsServerUnavailable(base::TimeTicks now) = 0; virtual bool IsServerUnavailable(base::TimeTicks now) = 0;
// Informs the manager that the given URL should be bypassed one time.
virtual void AddSingleBypass(std::string url) = 0;
// Queries the manager if the given URL should be bypassed one time, returning
// true if yes.
virtual bool CheckSingleBypass(std::string url) = 0;
}; };
#endif // CHROME_BROWSER_PREVIEWS_PREVIEWS_LITE_PAGE_NAVIGATION_THROTTLE_MANAGER_H_ #endif // CHROME_BROWSER_PREVIEWS_PREVIEWS_LITE_PAGE_NAVIGATION_THROTTLE_MANAGER_H_
...@@ -135,3 +135,48 @@ TEST_F(PreviewsLitePageNavigationThrottleTest, TestGetPreviewsURL) { ...@@ -135,3 +135,48 @@ TEST_F(PreviewsLitePageNavigationThrottleTest, TestGetPreviewsURL) {
SimulateWillProcessResponse(); SimulateWillProcessResponse();
CallDidFinishNavigation(); CallDidFinishNavigation();
} }
TEST_F(PreviewsLitePageNavigationThrottleTest, TestGetOriginalURL) {
struct TestCase {
std::string previews_host;
std::string original_url;
std::string previews_url;
bool want_ok;
};
const TestCase kTestCases[]{
// Use https://play.golang.org/p/HUM2HxmUTOW to compute |previews_url|.
{
"https://previews.host.com",
"https://original.host.com/path/path/path?query=yes",
"https://shta44dh4bi7rc6fnpjnkrtytwlabygjhk53v2trlot2wddylwua."
"previews.host.com/p?u="
"https%3A%2F%2Foriginal.host.com%2Fpath%2Fpath%2Fpath%3Fquery%3Dyes",
true,
},
{
"https://previews.host.com",
"http://original.host.com/path/path/path?query=yes",
"https://6p7dar4ju6r4ynz7x3pucmlcltuqsf7z5auhvckzln7voglkt56q."
"previews.host.com/p?u="
"http%3A%2F%2Foriginal.host.com%2Fpath%2Fpath%2Fpath%3Fquery%3Dyes",
true,
},
};
for (const TestCase& test_case : kTestCases) {
base::test::ScopedFeatureList scoped_feature_list;
scoped_feature_list.InitAndEnableFeatureWithParameters(
previews::features::kLitePageServerPreviews,
{{"previews_host", test_case.previews_host}});
std::string original_url;
bool got_ok = PreviewsLitePageNavigationThrottle::GetOriginalURL(
GURL(test_case.previews_url), &original_url);
EXPECT_EQ(got_ok, test_case.want_ok);
EXPECT_EQ(original_url, test_case.original_url);
}
// Boilerplate navigation to keep the test harness happy.
SimulateWillProcessResponse();
CallDidFinishNavigation();
}
...@@ -129,6 +129,12 @@ base::TimeDelta OfflinePreviewFreshnessDuration() { ...@@ -129,6 +129,12 @@ base::TimeDelta OfflinePreviewFreshnessDuration() {
"offline_preview_freshness_duration_in_days", 7)); "offline_preview_freshness_duration_in_days", 7));
} }
base::TimeDelta LitePagePreviewsSingleBypassDuration() {
return base::TimeDelta::FromSeconds(base::GetFieldTrialParamByFeatureAsInt(
features::kLitePageServerPreviews, "single_bypass_duration_in_seconds",
60 * 5));
}
GURL GetLitePagePreviewsDomainURL() { GURL GetLitePagePreviewsDomainURL() {
std::string variable_host_str = GetFieldTrialParamValueByFeature( std::string variable_host_str = GetFieldTrialParamValueByFeature(
features::kLitePageServerPreviews, "previews_host"); features::kLitePageServerPreviews, "previews_host");
......
...@@ -92,6 +92,9 @@ base::TimeDelta OfflinePreviewFreshnessDuration(); ...@@ -92,6 +92,9 @@ base::TimeDelta OfflinePreviewFreshnessDuration();
// The host for Lite Page server previews. // The host for Lite Page server previews.
GURL GetLitePagePreviewsDomainURL(); GURL GetLitePagePreviewsDomainURL();
// The duration of a single bypass for Lite Page Server Previews.
base::TimeDelta LitePagePreviewsSingleBypassDuration();
// The threshold of EffectiveConnectionType above which preview |type| will be // The threshold of EffectiveConnectionType above which preview |type| will be
// triggered. // triggered.
net::EffectiveConnectionType GetECTThresholdForPreview( net::EffectiveConnectionType GetECTThresholdForPreview(
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment