Commit e1989420 authored by sclittle@chromium.org's avatar sclittle@chromium.org

Added support for 'Chrome-Proxy: block-once' header

Added support for a new instruction 'block-once' that, when present in the 'Chrome-Proxy' HTTP response header, causes data reduction proxies to be bypassed when retrying the current request (if it's idempotent), but does not cause data reduction proxies to be bypassed for future requests.

BUG=325328

Review URL: https://codereview.chromium.org/467823002

Cr-Commit-Position: refs/heads/master@{#289469}
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@289469 0039d316-1c4b-4281-b951-d872f2087c98
parent 07f3ea96
......@@ -90,10 +90,12 @@ bool MaybeBypassProxyAndPrepareToRetry(
bypass_type);
}
if (data_reduction_proxy_info.mark_proxies_as_bad) {
MarkProxiesAsBadUntil(request,
data_reduction_proxy_info.bypass_duration,
data_reduction_proxy_info.bypass_all,
data_reduction_proxy_type_info.proxy_servers);
}
// Only retry idempotent methods.
if (!IsRequestIdempotent(request))
......
......@@ -323,6 +323,15 @@ TEST_F(DataReductionProxyProtocolTest, OverrideResponseAsRedirect) {
"Via: 1.1 Chrome-Compression-Proxy\n"
"Location: http://www.google.com/\n"
},
{ "HTTP/1.1 200 0K\n"
"Chrome-Proxy: block-once\n"
"Via: 1.1 Chrome-Compression-Proxy\n",
"HTTP/1.1 302 Found\n"
"Chrome-Proxy: block-once\n"
"Via: 1.1 Chrome-Compression-Proxy\n"
"Location: http://www.google.com/\n"
},
{ "HTTP/1.1 302 Found\n"
"Location: http://foo.com/\n",
......@@ -577,7 +586,124 @@ TEST_F(DataReductionProxyProtocolTest, BypassLogic) {
true,
1,
BYPASS_EVENT_TYPE_SHORT
}
},
// Valid data reduction proxy response with a block-once message. It will be
// retried, and there will be no proxies on the retry list since block-once
// only affects the current request.
{ "GET",
"HTTP/1.1 200 OK\r\n"
"Server: proxy\r\n"
"Chrome-Proxy: block-once\r\n"
"Via: 1.1 Chrome-Compression-Proxy\r\n\r\n",
true,
0u,
true,
0,
BYPASS_EVENT_TYPE_CURRENT
},
// Same as above with the OPTIONS method, which is idempotent.
{ "OPTIONS",
"HTTP/1.1 200 OK\r\n"
"Server: proxy\r\n"
"Chrome-Proxy: block-once\r\n"
"Via: 1.1 Chrome-Compression-Proxy\r\n\r\n",
true,
0u,
true,
0,
BYPASS_EVENT_TYPE_CURRENT
},
// Same as above with the HEAD method, which is idempotent.
{ "HEAD",
"HTTP/1.1 200 OK\r\n"
"Server: proxy\r\n"
"Chrome-Proxy: block-once\r\n"
"Via: 1.1 Chrome-Compression-Proxy\r\n\r\n",
true,
0u,
false,
0,
BYPASS_EVENT_TYPE_CURRENT
},
// Same as above with the PUT method, which is idempotent.
{ "PUT",
"HTTP/1.1 200 OK\r\n"
"Server: proxy\r\n"
"Chrome-Proxy: block-once\r\n"
"Via: 1.1 Chrome-Compression-Proxy\r\n\r\n",
true,
0u,
true,
0,
BYPASS_EVENT_TYPE_CURRENT
},
// Same as above with the DELETE method, which is idempotent.
{ "DELETE",
"HTTP/1.1 200 OK\r\n"
"Server: proxy\r\n"
"Chrome-Proxy: block-once\r\n"
"Via: 1.1 Chrome-Compression-Proxy\r\n\r\n",
true,
0u,
true,
0,
BYPASS_EVENT_TYPE_CURRENT
},
// Same as above with the TRACE method, which is idempotent.
{ "TRACE",
"HTTP/1.1 200 OK\r\n"
"Server: proxy\r\n"
"Chrome-Proxy: block-once\r\n"
"Via: 1.1 Chrome-Compression-Proxy\r\n\r\n",
true,
0u,
true,
0,
BYPASS_EVENT_TYPE_CURRENT
},
// Valid data reduction proxy response with a block-once message. It will
// not be retried because the request is non-idempotent, and there will be
// no proxies on the retry list since block-once only affects the current
// request.
{ "POST",
"HTTP/1.1 200 OK\r\n"
"Server: proxy\r\n"
"Chrome-Proxy: block-once\r\n"
"Via: 1.1 Chrome-Compression-Proxy\r\n\r\n",
false,
0u,
true,
0,
BYPASS_EVENT_TYPE_CURRENT
},
// Valid data reduction proxy response with block and block-once messages.
// The block message will override the block-once message, so both proxies
// should be on the retry list when it completes.
{ "GET",
"HTTP/1.1 200 OK\r\n"
"Server: proxy\r\n"
"Chrome-Proxy: block=1, block-once\r\n"
"Via: 1.1 Chrome-Compression-Proxy\r\n\r\n",
true,
2u,
true,
1,
BYPASS_EVENT_TYPE_SHORT
},
// Valid data reduction proxy response with bypass and block-once messages.
// The bypass message will override the block-once message, so one proxy
// should be on the retry list when it completes.
{ "GET",
"HTTP/1.1 200 OK\r\n"
"Server: proxy\r\n"
"Chrome-Proxy: bypass=1, block-once\r\n"
"Via: 1.1 Chrome-Compression-Proxy\r\n\r\n",
true,
1u,
true,
1,
BYPASS_EVENT_TYPE_SHORT
},
};
std::string primary = proxy_params_->DefaultOrigin();
std::string fallback = proxy_params_->DefaultFallbackOrigin();
......
......@@ -23,6 +23,7 @@ namespace {
const char kChromeProxyHeader[] = "chrome-proxy";
const char kActionValueDelimiter = '=';
const char kChromeProxyActionBlockOnce[] = "block-once";
const char kChromeProxyActionBlock[] = "block";
const char kChromeProxyActionBypass[] = "bypass";
......@@ -42,6 +43,7 @@ base::TimeDelta GetDefaultBypassDuration() {
base::TimeDelta::FromMinutes(5).InMilliseconds());
return TimeDelta::FromMilliseconds(delta_ms);
}
} // namespace
namespace data_reduction_proxy {
......@@ -111,7 +113,7 @@ bool ParseHeadersAndSetBypassDuration(const net::HttpResponseHeaders* headers,
bool ParseHeadersAndSetProxyInfo(const net::HttpResponseHeaders* headers,
DataReductionProxyInfo* proxy_info) {
DCHECK(proxy_info);
proxy_info->bypass_all = false;
// Support header of the form Chrome-Proxy: bypass|block=<duration>, where
// <duration> is the number of seconds to wait before retrying
// the proxy. If the duration is 0, then the default proxy retry delay
......@@ -120,19 +122,37 @@ bool ParseHeadersAndSetProxyInfo(const net::HttpResponseHeaders* headers,
// proxy, whereas 'block' instructs Chrome to bypass all available data
// reduction proxies.
// 'block' takes precedence over 'bypass', so look for it first.
// 'block' takes precedence over 'bypass' and 'block-once', so look for it
// first.
// TODO(bengr): Reduce checks for 'block' and 'bypass' to a single loop.
if (ParseHeadersAndSetBypassDuration(
headers, kChromeProxyActionBlock, &proxy_info->bypass_duration)) {
proxy_info->bypass_all = true;
proxy_info->mark_proxies_as_bad = true;
return true;
}
// Next, look for 'bypass'.
if (ParseHeadersAndSetBypassDuration(
headers, kChromeProxyActionBypass, &proxy_info->bypass_duration)) {
proxy_info->bypass_all = false;
proxy_info->mark_proxies_as_bad = true;
return true;
}
// Lastly, look for 'block-once'. 'block-once' instructs Chrome to retry the
// current request (if it's idempotent), bypassing all available data
// reduction proxies. Unlike 'block', 'block-once' does not cause data
// reduction proxies to be bypassed for an extended period of time;
// 'block-once' only affects the retry of the current request.
if (headers->HasHeaderValue(kChromeProxyHeader,
kChromeProxyActionBlockOnce)) {
proxy_info->bypass_all = true;
proxy_info->mark_proxies_as_bad = false;
proxy_info->bypass_duration = TimeDelta();
return true;
}
return false;
}
......@@ -179,17 +199,23 @@ DataReductionProxyBypassType GetDataReductionProxyBypassType(
if (ParseHeadersAndSetProxyInfo(headers, data_reduction_proxy_info)) {
// A chrome-proxy response header is only present in a 502. For proper
// reporting, this check must come before the 5xx checks below.
if (!data_reduction_proxy_info->mark_proxies_as_bad)
return BYPASS_EVENT_TYPE_CURRENT;
const TimeDelta& duration = data_reduction_proxy_info->bypass_duration;
// bypass=0 means bypass for a random duration between 1 to 5 minutes
if (duration == TimeDelta())
return BYPASS_EVENT_TYPE_MEDIUM;
if (duration <= TimeDelta::FromSeconds(kShortBypassMaxSeconds))
return BYPASS_EVENT_TYPE_SHORT;
if (duration <= TimeDelta::FromSeconds(kMediumBypassMaxSeconds))
return BYPASS_EVENT_TYPE_MEDIUM;
return BYPASS_EVENT_TYPE_LONG;
}
// If a bypass is triggered by any of the following cases, then the data
// reduction proxy should be bypassed for a random duration between 1 and 5
// minutes.
data_reduction_proxy_info->mark_proxies_as_bad = true;
data_reduction_proxy_info->bypass_duration = GetDefaultBypassDuration();
// Fall back if a 500, 502 or 503 is returned.
if (headers->response_code() == net::HTTP_INTERNAL_SERVER_ERROR)
return BYPASS_EVENT_TYPE_STATUS_500_HTTP_INTERNAL_SERVER_ERROR;
......
......@@ -25,7 +25,6 @@ namespace data_reduction_proxy {
// name in metrics/histograms/histograms.xml.
enum DataReductionProxyBypassType {
// Bypass due to explicit instruction for the current request.
// Not yet supported.
BYPASS_EVENT_TYPE_CURRENT = 0,
// Bypass the proxy for less than one minute.
......@@ -64,13 +63,19 @@ enum DataReductionProxyBypassType {
// Contains instructions contained in the Chrome-Proxy header.
struct DataReductionProxyInfo {
DataReductionProxyInfo() : bypass_all(false) {}
DataReductionProxyInfo()
: bypass_all(false), mark_proxies_as_bad(false) {}
// True if Chrome should bypass all available data reduction proxies. False
// if only the currently connected data reduction proxy should be bypassed.
bool bypass_all;
// Amount of time to bypass the data reduction proxy or proxies.
// True iff Chrome should mark the data reduction proxy or proxies as bad for
// the period of time specified in |bypass_duration|.
bool mark_proxies_as_bad;
// Amount of time to bypass the data reduction proxy or proxies. This value is
// ignored if |mark_proxies_as_bad| is false.
base::TimeDelta bypass_duration;
};
......
......@@ -95,6 +95,14 @@ TEST_F(DataReductionProxyHeadersTest, GetDataReductionProxyActionValue) {
true,
"123",
},
{ "HTTP/1.1 200 OK\n"
"connection: proxy-bypass\n"
"Chrome-Proxy: block-once\n"
"Content-Length: 999\n",
"block-once",
false,
"",
},
};
for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) {
std::string headers(tests[i].headers);
......@@ -118,12 +126,14 @@ TEST_F(DataReductionProxyHeadersTest, GetProxyBypassInfo) {
bool expected_result;
int64 expected_retry_delay;
bool expected_bypass_all;
bool expected_mark_proxies_as_bad;
} tests[] = {
{ "HTTP/1.1 200 OK\n"
"Content-Length: 999\n",
false,
0,
false,
false,
},
{ "HTTP/1.1 200 OK\n"
"connection: keep-alive\n"
......@@ -131,6 +141,7 @@ TEST_F(DataReductionProxyHeadersTest, GetProxyBypassInfo) {
false,
0,
false,
false,
},
{ "HTTP/1.1 200 OK\n"
"connection: keep-alive\n"
......@@ -139,6 +150,7 @@ TEST_F(DataReductionProxyHeadersTest, GetProxyBypassInfo) {
true,
86400,
false,
true,
},
{ "HTTP/1.1 200 OK\n"
"connection: keep-alive\n"
......@@ -147,6 +159,7 @@ TEST_F(DataReductionProxyHeadersTest, GetProxyBypassInfo) {
false,
0,
false,
false,
},
{ "HTTP/1.1 200 OK\n"
"connection: keep-alive\n"
......@@ -155,6 +168,7 @@ TEST_F(DataReductionProxyHeadersTest, GetProxyBypassInfo) {
false,
0,
false,
false,
},
{ "HTTP/1.1 200 OK\n"
"connection: keep-alive\n"
......@@ -163,6 +177,7 @@ TEST_F(DataReductionProxyHeadersTest, GetProxyBypassInfo) {
false,
0,
false,
false,
},
{ "HTTP/1.1 200 OK\n"
"connection: keep-alive\n"
......@@ -171,6 +186,7 @@ TEST_F(DataReductionProxyHeadersTest, GetProxyBypassInfo) {
true,
86400,
false,
true,
},
{ "HTTP/1.1 200 OK\n"
"connection: keep-alive\n"
......@@ -179,6 +195,7 @@ TEST_F(DataReductionProxyHeadersTest, GetProxyBypassInfo) {
true,
86400,
false,
true,
},
{ "HTTP/1.1 200 OK\n"
"connection: keep-alive\n"
......@@ -188,6 +205,7 @@ TEST_F(DataReductionProxyHeadersTest, GetProxyBypassInfo) {
true,
3600,
false,
true,
},
{ "HTTP/1.1 200 OK\n"
"connection: keep-alive\n"
......@@ -196,6 +214,7 @@ TEST_F(DataReductionProxyHeadersTest, GetProxyBypassInfo) {
true,
3600,
false,
true,
},
{ "HTTP/1.1 200 OK\n"
"connection: keep-alive\n"
......@@ -204,6 +223,7 @@ TEST_F(DataReductionProxyHeadersTest, GetProxyBypassInfo) {
true,
86400,
false,
true,
},
{ "HTTP/1.1 200 OK\n"
"connection: keep-alive\n"
......@@ -213,6 +233,7 @@ TEST_F(DataReductionProxyHeadersTest, GetProxyBypassInfo) {
true,
86400,
false,
true,
},
{ "HTTP/1.1 200 OK\n"
"connection: keep-alive\n"
......@@ -221,6 +242,7 @@ TEST_F(DataReductionProxyHeadersTest, GetProxyBypassInfo) {
true,
3600,
true,
true,
},
{ "HTTP/1.1 200 OK\n"
"connection: keep-alive\n"
......@@ -229,6 +251,7 @@ TEST_F(DataReductionProxyHeadersTest, GetProxyBypassInfo) {
true,
3600,
true,
true,
},
{ "HTTP/1.1 200 OK\n"
"connection: proxy-bypass\n"
......@@ -237,6 +260,7 @@ TEST_F(DataReductionProxyHeadersTest, GetProxyBypassInfo) {
true,
86400,
false,
true,
},
{ "HTTP/1.1 200 OK\n"
"connection: proxy-bypass\n"
......@@ -245,6 +269,7 @@ TEST_F(DataReductionProxyHeadersTest, GetProxyBypassInfo) {
false,
0,
false,
false,
},
{ "HTTP/1.1 200 OK\n"
"connection: proxy-bypass\n"
......@@ -253,6 +278,80 @@ TEST_F(DataReductionProxyHeadersTest, GetProxyBypassInfo) {
false,
0,
false,
false,
},
{ "HTTP/1.1 200 OK\n"
"connection: keep-alive\n"
"Chrome-Proxy: block-once\n"
"Content-Length: 999\n",
true,
0,
true,
false,
},
{ "HTTP/1.1 200 OK\n"
"connection: keep-alive\n"
"Chrome-Proxy: block-once=\n"
"Content-Length: 999\n",
false,
0,
false,
false,
},
{ "HTTP/1.1 200 OK\n"
"connection: keep-alive\n"
"Chrome-Proxy: block-once=10\n"
"Content-Length: 999\n",
false,
0,
false,
false,
},
{ "HTTP/1.1 200 OK\n"
"connection: keep-alive\n"
"Chrome-Proxy: block-once, bypass=86400, block=3600\n"
"Content-Length: 999\n",
true,
3600,
true,
true,
},
{ "HTTP/1.1 200 OK\n"
"connection: keep-alive\n"
"Chrome-Proxy: block-once\n"
"Chrome-Proxy: bypass=86400, block=3600\n"
"Content-Length: 999\n",
true,
3600,
true,
true,
},
{ "HTTP/1.1 200 OK\n"
"connection: keep-alive\n"
"Chrome-Proxy: block-once, bypass=86400\n"
"Content-Length: 999\n",
true,
86400,
false,
true,
},
{ "HTTP/1.1 200 OK\n"
"connection: keep-alive\n"
"Chrome-Proxy: block-once, block=3600\n"
"Content-Length: 999\n",
true,
3600,
true,
true,
},
{ "HTTP/1.1 200 OK\n"
"connection: keep-alive\n"
"Chrome-Proxy: bypass=, block=, block-once\n"
"Content-Length: 999\n",
true,
0,
true,
false,
},
};
for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) {
......@@ -268,6 +367,8 @@ TEST_F(DataReductionProxyHeadersTest, GetProxyBypassInfo) {
data_reduction_proxy_info.bypass_duration.InSeconds());
EXPECT_EQ(tests[i].expected_bypass_all,
data_reduction_proxy_info.bypass_all);
EXPECT_EQ(tests[i].expected_mark_proxies_as_bad,
data_reduction_proxy_info.mark_proxies_as_bad);
}
}
......@@ -460,6 +561,11 @@ TEST_F(DataReductionProxyHeadersTest, GetDataReductionProxyBypassEventType) {
"Via: 1.1 Chrome-Compression-Proxy\n",
BYPASS_EVENT_TYPE_LONG,
},
{ "HTTP/1.1 200 OK\n"
"Chrome-Proxy: block-once\n"
"Via: 1.1 Chrome-Compression-Proxy\n",
BYPASS_EVENT_TYPE_CURRENT,
},
{ "HTTP/1.1 500 Internal Server Error\n"
"Via: 1.1 Chrome-Compression-Proxy\n",
BYPASS_EVENT_TYPE_STATUS_500_HTTP_INTERNAL_SERVER_ERROR,
......
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