Commit 224cdb1b authored by Robert Ogden's avatar Robert Ogden Committed by Commit Bot

Add server loadshedding behavior to HTTPS Previews

Adds in the loadshedding behavior according to the design doc. When the
Retry-After header is sent, use that. Otherwise, pick a random interval
from 1-5 minutes.

Also massages the manager API a bit to make testing easier.

Bug: 864650
Change-Id: I485223278a08ad2185c19116552cb934d50f90bf
Reviewed-on: https://chromium-review.googlesource.com/1180069
Commit-Queue: Robert Ogden <robertogden@chromium.org>
Reviewed-by: default avatarRyan Sturm <ryansturm@chromium.org>
Cr-Commit-Position: refs/heads/master@{#585092}
parent a9ede579
......@@ -12,9 +12,14 @@
#include "base/strings/string_number_conversions.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/simple_test_tick_clock.h"
#include "build/build_config.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/previews/previews_lite_page_decider.h"
#include "chrome/browser/previews/previews_lite_page_navigation_throttle.h"
#include "chrome/browser/previews/previews_service.h"
#include "chrome/browser/previews/previews_service_factory.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/test/base/in_process_browser_test.h"
#include "chrome/test/base/ui_test_utils.h"
......@@ -24,6 +29,7 @@
#include "components/optimization_guide/test_component_creator.h"
#include "components/previews/core/previews_features.h"
#include "content/public/test/browser_test_utils.h"
#include "net/http/http_request_headers.h"
#include "net/http/http_status_code.h"
#include "net/test/embedded_test_server/http_request.h"
#include "net/test/embedded_test_server/http_response.h"
......@@ -407,19 +413,20 @@ class PreviewsLitePageServerBrowserTest : public PreviewsBrowserTest {
const net::test_server::HttpRequest& request) {
std::unique_ptr<net::test_server::BasicHttpResponse> response(
new net::test_server::BasicHttpResponse);
std::string original_url;
std::string original_url_str;
// Ignore anything that's not a previews request with an unused status.
if (!PreviewsLitePageNavigationThrottle::GetOriginalURL(request.GetURL(),
&original_url)) {
if (!PreviewsLitePageNavigationThrottle::GetOriginalURL(
request.GetURL(), &original_url_str)) {
response->set_code(net::HttpStatusCode::HTTP_BAD_REQUEST);
return response;
}
GURL original_url = GURL(original_url_str);
std::string query_param;
std::string code_query_param;
int return_code = 0;
if (net::GetValueForKeyInQuery(GURL(original_url), "resp", &query_param))
base::StringToInt(query_param, &return_code);
if (net::GetValueForKeyInQuery(original_url, "resp", &code_query_param))
base::StringToInt(code_query_param, &return_code);
switch (return_code) {
case 200:
......@@ -436,24 +443,37 @@ class PreviewsLitePageServerBrowserTest : public PreviewsBrowserTest {
response->set_code(net::HTTP_SERVICE_UNAVAILABLE);
break;
default:
response->set_code(net::HTTP_BAD_REQUEST);
response->set_code(net::HTTP_OK);
break;
}
std::string headers_query_param;
if (net::GetValueForKeyInQuery(original_url, "headers",
&headers_query_param)) {
net::HttpRequestHeaders headers;
headers.AddHeadersFromString(headers_query_param);
net::HttpRequestHeaders::Iterator iter(headers);
while (iter.GetNext())
response->AddCustomHeader(iter.name(), iter.value());
}
return std::move(response);
}
GURL previews_server() { return previews_server_->base_url(); }
GURL http_lite_page_url(int return_code) {
GURL http_lite_page_url(int return_code, std::string* headers = nullptr) {
std::string query = "resp=" + base::IntToString(return_code);
if (headers)
query += "&headers=" + *headers;
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) {
GURL https_lite_page_url(int return_code, std::string* headers = nullptr) {
std::string query = "resp=" + base::IntToString(return_code);
if (headers)
query += "&headers=" + *headers;
GURL::Replacements replacements;
replacements.SetQuery(query.c_str(), url::Component(0, query.length()));
return https_url().ReplaceComponents(replacements);
......@@ -564,6 +584,54 @@ IN_PROC_BROWSER_TEST_F(PreviewsLitePageServerBrowserTest,
VerifyPreviewNotLoaded();
}
// 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_LitePagePreviewsLoadshed LitePagePreviewsLoadshed
#else
#define MAYBE_LitePagePreviewsLoadshed DISABLED_LitePagePreviewsLoadshed
#endif
IN_PROC_BROWSER_TEST_F(PreviewsLitePageServerBrowserTest,
MAYBE_LitePagePreviewsLoadshed) {
PreviewsService* previews_service =
PreviewsServiceFactory::GetForProfile(browser()->profile());
ASSERT_TRUE(previews_service);
PreviewsLitePageDecider* decider =
previews_service->previews_lite_page_decider();
ASSERT_TRUE(decider);
std::unique_ptr<base::SimpleTestTickClock> clock =
std::make_unique<base::SimpleTestTickClock>();
decider->SetClockForTesting(clock.get());
// Send a loadshed response. Client should not retry for a randomly chosen
// duration [1 min, 5 mins).
ui_test_utils::NavigateToURL(browser(), https_lite_page_url(503));
VerifyPreviewNotLoaded();
clock->Advance(base::TimeDelta::FromMinutes(1));
ui_test_utils::NavigateToURL(browser(), https_lite_page_url(200));
VerifyPreviewNotLoaded();
clock->Advance(base::TimeDelta::FromMinutes(4));
ui_test_utils::NavigateToURL(browser(), https_lite_page_url(200));
VerifyPreviewLoaded();
// Send a loadshed response with a specific time duration, 30 seconds, to
// retry after.
std::string headers = "Retry-After: 30";
ui_test_utils::NavigateToURL(browser(), https_lite_page_url(503, &headers));
VerifyPreviewNotLoaded();
ui_test_utils::NavigateToURL(browser(), https_lite_page_url(200));
VerifyPreviewNotLoaded();
clock->Advance(base::TimeDelta::FromSeconds(31));
ui_test_utils::NavigateToURL(browser(), https_lite_page_url(200));
VerifyPreviewLoaded();
}
class PreviewsLitePageServerDataSaverBrowserTest
: public PreviewsLitePageServerBrowserTest {
public:
......
......@@ -74,18 +74,19 @@ bool PreviewsLitePageDecider::IsDataSaverEnabled(
return drp_settings->IsDataReductionProxyEnabled();
}
void PreviewsLitePageDecider::SetServerUnavailableUntil(
base::TimeTicks retry_at) {
void PreviewsLitePageDecider::SetServerUnavailableFor(
base::TimeDelta retry_after) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
base::TimeTicks retry_at = clock_->NowTicks() + retry_after;
if (!retry_at_.has_value() || retry_at > retry_at_)
retry_at_ = retry_at;
}
bool PreviewsLitePageDecider::IsServerUnavailable(base::TimeTicks now) {
bool PreviewsLitePageDecider::IsServerUnavailable() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (!retry_at_.has_value())
return false;
bool server_loadshedding = retry_at_ > now;
bool server_loadshedding = retry_at_ > clock_->NowTicks();
if (!server_loadshedding)
retry_at_.reset();
return server_loadshedding;
......
......@@ -48,8 +48,8 @@ class PreviewsLitePageDecider
FRIEND_TEST_ALL_PREFIXES(PreviewsLitePageDeciderTest, TestSingleBypass);
// PreviewsLitePageNavigationThrottleManager:
void SetServerUnavailableUntil(base::TimeTicks retry_at) override;
bool IsServerUnavailable(base::TimeTicks now) override;
void SetServerUnavailableFor(base::TimeDelta retry_after) override;
bool IsServerUnavailable() override;
void AddSingleBypass(std::string url) override;
bool CheckSingleBypass(std::string url) override;
......
......@@ -21,27 +21,34 @@ class PreviewsLitePageDeciderTest : public testing::Test {
};
TEST_F(PreviewsLitePageDeciderTest, TestServerUnavailable) {
const base::TimeTicks kNow = base::TimeTicks::Now();
struct TestCase {
base::TimeTicks retry_at;
base::TimeTicks now;
base::TimeDelta set_available_after;
base::TimeDelta check_available_after;
bool want_is_unavailable;
};
const TestCase kTestCases[]{
{
kNow - base::TimeDelta::FromMinutes(1), kNow, false,
base::TimeDelta::FromMinutes(1), base::TimeDelta::FromMinutes(2),
false,
},
{
kNow + base::TimeDelta::FromMinutes(1), kNow, true,
base::TimeDelta::FromMinutes(2), base::TimeDelta::FromMinutes(1),
true,
},
};
for (const TestCase& test_case : kTestCases) {
std::unique_ptr<PreviewsLitePageDecider> decider =
std::make_unique<PreviewsLitePageDecider>();
decider->SetServerUnavailableUntil(test_case.retry_at);
EXPECT_EQ(decider->IsServerUnavailable(test_case.now),
test_case.want_is_unavailable);
std::unique_ptr<base::SimpleTestTickClock> clock =
std::make_unique<base::SimpleTestTickClock>();
decider->SetClockForTesting(clock.get());
decider->SetServerUnavailableFor(test_case.set_available_after);
EXPECT_TRUE(decider->IsServerUnavailable());
clock->Advance(test_case.check_available_after);
EXPECT_EQ(decider->IsServerUnavailable(), test_case.want_is_unavailable);
}
}
......
......@@ -4,8 +4,12 @@
#include "chrome/browser/previews/previews_lite_page_navigation_throttle.h"
#include <string>
#include "base/memory/weak_ptr.h"
#include "base/rand_util.h"
#include "base/strings/string_number_conversions.h"
#include "base/time/time.h"
#include "components/base32/base32.h"
#include "components/previews/core/previews_experiments.h"
#include "content/public/browser/browser_thread.h"
......@@ -18,6 +22,7 @@
#include "net/base/escape.h"
#include "net/base/url_util.h"
#include "net/http/http_status_code.h"
#include "net/http/http_util.h"
#include "ui/base/window_open_disposition.h"
#include "url/gurl.h"
......@@ -89,6 +94,9 @@ bool PreviewsLitePageNavigationThrottle::IsEligibleForPreview() const {
if (!navigation_handle()->IsInMainFrame())
return false;
if (manager_->IsServerUnavailable())
return false;
if (IsPreviewsDomain(navigation_handle()->GetURL()))
return false;
......@@ -224,6 +232,18 @@ PreviewsLitePageNavigationThrottle::WillProcessResponse() {
response_code == net::HTTP_TEMPORARY_REDIRECT)
return content::NavigationThrottle::PROCEED;
if (response_code == net::HTTP_SERVICE_UNAVAILABLE) {
std::string retry_after_header;
base::TimeDelta retry_after = base::TimeDelta::FromSeconds(
base::RandInt(60, previews::params::PreviewServerLoadshedMaxSeconds()));
if (response_headers->EnumerateHeader(nullptr, "retry-after",
&retry_after_header)) {
net::HttpUtil::ParseRetryAfterHeader(retry_after_header,
base::Time::Now(), &retry_after);
}
manager_->SetServerUnavailableFor(retry_after);
}
manager_->AddSingleBypass(original_url);
content::OpenURLParams url_params =
MakeOpenURLParams(navigation_handle(), GURL(original_url));
......
......@@ -15,12 +15,12 @@
class PreviewsLitePageNavigationThrottleManager {
public:
// Used to notify that the Previews Server should not be sent anymore requests
// until after the given time.
virtual void SetServerUnavailableUntil(base::TimeTicks retry_at) = 0;
// until after the given duration.
virtual void SetServerUnavailableFor(base::TimeDelta retry_after) = 0;
// Returns true if a Preview should not be triggered because the server is
// unavailable.
virtual bool IsServerUnavailable(base::TimeTicks now) = 0;
virtual bool IsServerUnavailable() = 0;
// Informs the manager that the given URL should be bypassed one time.
virtual void AddSingleBypass(std::string url) = 0;
......
......@@ -135,6 +135,12 @@ base::TimeDelta LitePagePreviewsSingleBypassDuration() {
60 * 5));
}
int PreviewServerLoadshedMaxSeconds() {
return base::GetFieldTrialParamByFeatureAsInt(
features::kLitePageServerPreviews, "loadshed_max_seconds",
5 * 60 /* 5 minutes */);
}
GURL GetLitePagePreviewsDomainURL() {
std::string variable_host_str = GetFieldTrialParamValueByFeature(
features::kLitePageServerPreviews, "previews_host");
......
......@@ -95,6 +95,9 @@ GURL GetLitePagePreviewsDomainURL();
// The duration of a single bypass for Lite Page Server Previews.
base::TimeDelta LitePagePreviewsSingleBypassDuration();
// The maximum number of seconds to loadshed the Previews server for.
int PreviewServerLoadshedMaxSeconds();
// The threshold of EffectiveConnectionType above which preview |type| will be
// triggered.
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