Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(321)

Side by Side Diff: components/data_reduction_proxy/core/common/data_reduction_proxy_headers.cc

Issue 2777823002: Bypass DRP if a redirect cycle is detected (Closed)
Patch Set: ryansturm comments Created 3 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2014 The Chromium Authors. All rights reserved. 1 // Copyright 2014 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 "components/data_reduction_proxy/core/common/data_reduction_proxy_heade rs.h" 5 #include "components/data_reduction_proxy/core/common/data_reduction_proxy_heade rs.h"
6 6
7 #include <stddef.h> 7 #include <stddef.h>
8 #include <stdint.h> 8 #include <stdint.h>
9 9
10 #include <string> 10 #include <string>
11 #include <utility> 11 #include <utility>
12 #include <vector> 12 #include <vector>
13 13
14 #include "base/rand_util.h" 14 #include "base/rand_util.h"
15 #include "base/strings/string_number_conversions.h" 15 #include "base/strings/string_number_conversions.h"
16 #include "base/strings/string_split.h" 16 #include "base/strings/string_split.h"
17 #include "base/strings/string_util.h" 17 #include "base/strings/string_util.h"
18 #include "base/time/time.h" 18 #include "base/time/time.h"
19 #include "components/data_reduction_proxy/core/common/data_reduction_proxy_event _creator.h" 19 #include "components/data_reduction_proxy/core/common/data_reduction_proxy_event _creator.h"
20 #include "components/data_reduction_proxy/core/common/data_reduction_proxy_param s.h" 20 #include "components/data_reduction_proxy/core/common/data_reduction_proxy_param s.h"
21 #include "net/http/http_response_headers.h" 21 #include "net/http/http_response_headers.h"
22 #include "net/http/http_status_code.h" 22 #include "net/http/http_status_code.h"
23 #include "net/url_request/url_request.h"
23 24
24 using base::StringPiece; 25 using base::StringPiece;
25 using base::TimeDelta; 26 using base::TimeDelta;
26 27
27 namespace { 28 namespace {
28 29
29 const char kChromeProxyHeader[] = "chrome-proxy"; 30 const char kChromeProxyHeader[] = "chrome-proxy";
30 const char kChromeProxyECTHeader[] = "chrome-proxy-ect"; 31 const char kChromeProxyECTHeader[] = "chrome-proxy-ect";
31 const char kChromeProxyAcceptTransformHeader[] = 32 const char kChromeProxyAcceptTransformHeader[] =
32 "chrome-proxy-accept-transform"; 33 "chrome-proxy-accept-transform";
(...skipping 66 matching lines...) Expand 10 before | Expand all | Expand 10 after
99 const std::string& transform_type) { 100 const std::string& transform_type) {
100 std::string header_value; 101 std::string header_value;
101 if (!headers.GetNormalizedHeader( 102 if (!headers.GetNormalizedHeader(
102 data_reduction_proxy::chrome_proxy_content_transform_header(), 103 data_reduction_proxy::chrome_proxy_content_transform_header(),
103 &header_value)) { 104 &header_value)) {
104 return false; 105 return false;
105 } 106 }
106 return IsPreviewTypeInHeaderValue(header_value, transform_type); 107 return IsPreviewTypeInHeaderValue(header_value, transform_type);
107 } 108 }
108 109
110 // Returns true if there is a cycle in |url_chain|.
111 bool HasURLRedirectCycle(const std::vector<GURL>& url_chain) {
112 if (url_chain.size() <= 1)
113 return false;
114
115 // If the last entry occurs earlier in the |url_chain|, then very likely there
116 // is a redirect cycle.
117 return std::find(url_chain.rbegin() + 1, url_chain.rend(),
118 url_chain.back()) != url_chain.rend();
119 }
120
109 } // namespace 121 } // namespace
110 122
111 namespace data_reduction_proxy { 123 namespace data_reduction_proxy {
112 124
113 const char* chrome_proxy_header() { 125 const char* chrome_proxy_header() {
114 return kChromeProxyHeader; 126 return kChromeProxyHeader;
115 } 127 }
116 128
117 const char* chrome_proxy_ect_header() { 129 const char* chrome_proxy_ect_header() {
118 return kChromeProxyECTHeader; 130 return kChromeProxyECTHeader;
(...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after
173 while (headers->EnumerateHeader(&iter, kChromeProxyHeader, &value)) { 185 while (headers->EnumerateHeader(&iter, kChromeProxyHeader, &value)) {
174 if (StartsWithActionPrefix(value, action_prefix)) { 186 if (StartsWithActionPrefix(value, action_prefix)) {
175 if (action_value) 187 if (action_value)
176 *action_value = value.substr(action_prefix.size() + 1); 188 *action_value = value.substr(action_prefix.size() + 1);
177 return true; 189 return true;
178 } 190 }
179 } 191 }
180 return false; 192 return false;
181 } 193 }
182 194
183 bool ParseHeadersAndSetBypassDuration(const net::HttpResponseHeaders* headers, 195 bool ParseHeadersAndSetBypassDuration(const net::HttpResponseHeaders& headers,
184 base::StringPiece action_prefix, 196 base::StringPiece action_prefix,
185 base::TimeDelta* bypass_duration) { 197 base::TimeDelta* bypass_duration) {
186 DCHECK(headers);
187 size_t iter = 0; 198 size_t iter = 0;
188 std::string value; 199 std::string value;
189 200
190 while (headers->EnumerateHeader(&iter, kChromeProxyHeader, &value)) { 201 while (headers.EnumerateHeader(&iter, kChromeProxyHeader, &value)) {
191 if (StartsWithActionPrefix(value, action_prefix)) { 202 if (StartsWithActionPrefix(value, action_prefix)) {
192 int64_t seconds; 203 int64_t seconds;
193 if (!base::StringToInt64( 204 if (!base::StringToInt64(
194 StringPiece(value).substr(action_prefix.size() + 1), &seconds) || 205 StringPiece(value).substr(action_prefix.size() + 1), &seconds) ||
195 seconds < 0) { 206 seconds < 0) {
196 continue; // In case there is a well formed instruction. 207 continue; // In case there is a well formed instruction.
197 } 208 }
198 if (seconds != 0) { 209 if (seconds != 0) {
199 *bypass_duration = TimeDelta::FromSeconds(seconds); 210 *bypass_duration = TimeDelta::FromSeconds(seconds);
200 } else { 211 } else {
201 // Server deferred to us to choose a duration. Default to a random 212 // Server deferred to us to choose a duration. Default to a random
202 // duration between one and five minutes. 213 // duration between one and five minutes.
203 *bypass_duration = GetDefaultBypassDuration(); 214 *bypass_duration = GetDefaultBypassDuration();
204 } 215 }
205 return true; 216 return true;
206 } 217 }
207 } 218 }
208 return false; 219 return false;
209 } 220 }
210 221
211 bool ParseHeadersForBypassInfo(const net::HttpResponseHeaders* headers, 222 bool ParseHeadersForBypassInfo(const net::HttpResponseHeaders& headers,
212 DataReductionProxyInfo* proxy_info) { 223 DataReductionProxyInfo* proxy_info) {
213 DCHECK(proxy_info); 224 DCHECK(proxy_info);
214 225
215 // Support header of the form Chrome-Proxy: bypass|block=<duration>, where 226 // Support header of the form Chrome-Proxy: bypass|block=<duration>, where
216 // <duration> is the number of seconds to wait before retrying 227 // <duration> is the number of seconds to wait before retrying
217 // the proxy. If the duration is 0, then the default proxy retry delay 228 // the proxy. If the duration is 0, then the default proxy retry delay
218 // (specified in |ProxyList::UpdateRetryInfoOnFallback|) will be used. 229 // (specified in |ProxyList::UpdateRetryInfoOnFallback|) will be used.
219 // 'bypass' instructs Chrome to bypass the currently connected data reduction 230 // 'bypass' instructs Chrome to bypass the currently connected data reduction
220 // proxy, whereas 'block' instructs Chrome to bypass all available data 231 // proxy, whereas 'block' instructs Chrome to bypass all available data
221 // reduction proxies. 232 // reduction proxies.
(...skipping 16 matching lines...) Expand all
238 proxy_info->mark_proxies_as_bad = true; 249 proxy_info->mark_proxies_as_bad = true;
239 proxy_info->bypass_action = BYPASS_ACTION_TYPE_BYPASS; 250 proxy_info->bypass_action = BYPASS_ACTION_TYPE_BYPASS;
240 return true; 251 return true;
241 } 252 }
242 253
243 // Lastly, look for 'block-once'. 'block-once' instructs Chrome to retry the 254 // Lastly, look for 'block-once'. 'block-once' instructs Chrome to retry the
244 // current request (if it's idempotent), bypassing all available data 255 // current request (if it's idempotent), bypassing all available data
245 // reduction proxies. Unlike 'block', 'block-once' does not cause data 256 // reduction proxies. Unlike 'block', 'block-once' does not cause data
246 // reduction proxies to be bypassed for an extended period of time; 257 // reduction proxies to be bypassed for an extended period of time;
247 // 'block-once' only affects the retry of the current request. 258 // 'block-once' only affects the retry of the current request.
248 if (headers->HasHeaderValue(kChromeProxyHeader, 259 if (headers.HasHeaderValue(kChromeProxyHeader, kChromeProxyActionBlockOnce)) {
249 kChromeProxyActionBlockOnce)) {
250 proxy_info->bypass_all = true; 260 proxy_info->bypass_all = true;
251 proxy_info->mark_proxies_as_bad = false; 261 proxy_info->mark_proxies_as_bad = false;
252 proxy_info->bypass_duration = TimeDelta(); 262 proxy_info->bypass_duration = TimeDelta();
253 proxy_info->bypass_action = BYPASS_ACTION_TYPE_BLOCK_ONCE; 263 proxy_info->bypass_action = BYPASS_ACTION_TYPE_BLOCK_ONCE;
254 return true; 264 return true;
255 } 265 }
256 266
257 return false; 267 return false;
258 } 268 }
259 269
260 bool HasDataReductionProxyViaHeader(const net::HttpResponseHeaders* headers, 270 bool HasDataReductionProxyViaHeader(const net::HttpResponseHeaders& headers,
261 bool* has_intermediary) { 271 bool* has_intermediary) {
262 static const size_t kVersionSize = 4; 272 static const size_t kVersionSize = 4;
263 static const char kDataReductionProxyViaValue[] = "Chrome-Compression-Proxy"; 273 static const char kDataReductionProxyViaValue[] = "Chrome-Compression-Proxy";
264 size_t iter = 0; 274 size_t iter = 0;
265 std::string value; 275 std::string value;
266 276
267 // Case-sensitive comparison of |value|. Assumes the received protocol and the 277 // Case-sensitive comparison of |value|. Assumes the received protocol and the
268 // space following it are always |kVersionSize| characters. E.g., 278 // space following it are always |kVersionSize| characters. E.g.,
269 // 'Via: 1.1 Chrome-Compression-Proxy' 279 // 'Via: 1.1 Chrome-Compression-Proxy'
270 while (headers->EnumerateHeader(&iter, "via", &value)) { 280 while (headers.EnumerateHeader(&iter, "via", &value)) {
271 if (base::StringPiece(value).substr( 281 if (base::StringPiece(value).substr(
272 kVersionSize, arraysize(kDataReductionProxyViaValue) - 1) == 282 kVersionSize, arraysize(kDataReductionProxyViaValue) - 1) ==
273 kDataReductionProxyViaValue) { 283 kDataReductionProxyViaValue) {
274 if (has_intermediary) 284 if (has_intermediary)
275 // We assume intermediary exists if there is another Via header after 285 // We assume intermediary exists if there is another Via header after
276 // the data reduction proxy's Via header. 286 // the data reduction proxy's Via header.
277 *has_intermediary = !(headers->EnumerateHeader(&iter, "via", &value)); 287 *has_intermediary = !(headers.EnumerateHeader(&iter, "via", &value));
278 return true; 288 return true;
279 } 289 }
280 } 290 }
281 291
282 return false; 292 return false;
283 } 293 }
284 294
285 DataReductionProxyBypassType GetDataReductionProxyBypassType( 295 DataReductionProxyBypassType GetDataReductionProxyBypassType(
286 const net::HttpResponseHeaders* headers, 296 const std::vector<GURL>& url_chain,
297 const net::HttpResponseHeaders& headers,
287 DataReductionProxyInfo* data_reduction_proxy_info) { 298 DataReductionProxyInfo* data_reduction_proxy_info) {
288 DCHECK(data_reduction_proxy_info); 299 DCHECK(data_reduction_proxy_info);
300
301 bool has_via_header = HasDataReductionProxyViaHeader(headers, nullptr);
302
303 if (has_via_header && HasURLRedirectCycle(url_chain)) {
304 data_reduction_proxy_info->bypass_all = true;
305 data_reduction_proxy_info->mark_proxies_as_bad = false;
306 data_reduction_proxy_info->bypass_duration = base::TimeDelta();
307 data_reduction_proxy_info->bypass_action = BYPASS_ACTION_TYPE_BLOCK_ONCE;
308 return BYPASS_EVENT_TYPE_URL_REDIRECT_CYCLE;
309 }
310
289 if (ParseHeadersForBypassInfo(headers, data_reduction_proxy_info)) { 311 if (ParseHeadersForBypassInfo(headers, data_reduction_proxy_info)) {
290 // A chrome-proxy response header is only present in a 502. For proper 312 // A chrome-proxy response header is only present in a 502. For proper
291 // reporting, this check must come before the 5xx checks below. 313 // reporting, this check must come before the 5xx checks below.
292 if (!data_reduction_proxy_info->mark_proxies_as_bad) 314 if (!data_reduction_proxy_info->mark_proxies_as_bad)
293 return BYPASS_EVENT_TYPE_CURRENT; 315 return BYPASS_EVENT_TYPE_CURRENT;
294 316
295 const TimeDelta& duration = data_reduction_proxy_info->bypass_duration; 317 const TimeDelta& duration = data_reduction_proxy_info->bypass_duration;
296 if (duration <= TimeDelta::FromSeconds(kShortBypassMaxSeconds)) 318 if (duration <= TimeDelta::FromSeconds(kShortBypassMaxSeconds))
297 return BYPASS_EVENT_TYPE_SHORT; 319 return BYPASS_EVENT_TYPE_SHORT;
298 if (duration <= TimeDelta::FromSeconds(kMediumBypassMaxSeconds)) 320 if (duration <= TimeDelta::FromSeconds(kMediumBypassMaxSeconds))
299 return BYPASS_EVENT_TYPE_MEDIUM; 321 return BYPASS_EVENT_TYPE_MEDIUM;
300 return BYPASS_EVENT_TYPE_LONG; 322 return BYPASS_EVENT_TYPE_LONG;
301 } 323 }
302 324
303 // If a bypass is triggered by any of the following cases, then the data 325 // If a bypass is triggered by any of the following cases, then the data
304 // reduction proxy should be bypassed for a random duration between 1 and 5 326 // reduction proxy should be bypassed for a random duration between 1 and 5
305 // minutes. 327 // minutes.
306 data_reduction_proxy_info->mark_proxies_as_bad = true; 328 data_reduction_proxy_info->mark_proxies_as_bad = true;
307 data_reduction_proxy_info->bypass_duration = GetDefaultBypassDuration(); 329 data_reduction_proxy_info->bypass_duration = GetDefaultBypassDuration();
308 330
309 // Fall back if a 500, 502 or 503 is returned. 331 // Fall back if a 500, 502 or 503 is returned.
310 if (headers->response_code() == net::HTTP_INTERNAL_SERVER_ERROR) 332 if (headers.response_code() == net::HTTP_INTERNAL_SERVER_ERROR)
311 return BYPASS_EVENT_TYPE_STATUS_500_HTTP_INTERNAL_SERVER_ERROR; 333 return BYPASS_EVENT_TYPE_STATUS_500_HTTP_INTERNAL_SERVER_ERROR;
312 if (headers->response_code() == net::HTTP_BAD_GATEWAY) 334 if (headers.response_code() == net::HTTP_BAD_GATEWAY)
313 return BYPASS_EVENT_TYPE_STATUS_502_HTTP_BAD_GATEWAY; 335 return BYPASS_EVENT_TYPE_STATUS_502_HTTP_BAD_GATEWAY;
314 if (headers->response_code() == net::HTTP_SERVICE_UNAVAILABLE) 336 if (headers.response_code() == net::HTTP_SERVICE_UNAVAILABLE)
315 return BYPASS_EVENT_TYPE_STATUS_503_HTTP_SERVICE_UNAVAILABLE; 337 return BYPASS_EVENT_TYPE_STATUS_503_HTTP_SERVICE_UNAVAILABLE;
316 // TODO(kundaji): Bypass if Proxy-Authenticate header value cannot be 338 // TODO(kundaji): Bypass if Proxy-Authenticate header value cannot be
317 // interpreted by data reduction proxy. 339 // interpreted by data reduction proxy.
318 if (headers->response_code() == net::HTTP_PROXY_AUTHENTICATION_REQUIRED && 340 if (headers.response_code() == net::HTTP_PROXY_AUTHENTICATION_REQUIRED &&
319 !headers->HasHeader("Proxy-Authenticate")) { 341 !headers.HasHeader("Proxy-Authenticate")) {
320 return BYPASS_EVENT_TYPE_MALFORMED_407; 342 return BYPASS_EVENT_TYPE_MALFORMED_407;
321 } 343 }
322 if (!HasDataReductionProxyViaHeader(headers, NULL) && 344 if (!has_via_header && (headers.response_code() != net::HTTP_NOT_MODIFIED)) {
323 (headers->response_code() != net::HTTP_NOT_MODIFIED)) {
324 // A Via header might not be present in a 304. Since the goal of a 304 345 // A Via header might not be present in a 304. Since the goal of a 304
325 // response is to minimize information transfer, a sender in general 346 // response is to minimize information transfer, a sender in general
326 // should not generate representation metadata other than Cache-Control, 347 // should not generate representation metadata other than Cache-Control,
327 // Content-Location, Date, ETag, Expires, and Vary. 348 // Content-Location, Date, ETag, Expires, and Vary.
328 349
329 // The proxy Via header might also not be present in a 4xx response. 350 // The proxy Via header might also not be present in a 4xx response.
330 // Separate this case from other responses that are missing the header. 351 // Separate this case from other responses that are missing the header.
331 if (headers->response_code() >= net::HTTP_BAD_REQUEST && 352 if (headers.response_code() >= net::HTTP_BAD_REQUEST &&
332 headers->response_code() < net::HTTP_INTERNAL_SERVER_ERROR) { 353 headers.response_code() < net::HTTP_INTERNAL_SERVER_ERROR) {
333 // At this point, any 4xx response that is missing the via header 354 // At this point, any 4xx response that is missing the via header
334 // indicates an issue that is scoped to only the current request, so only 355 // indicates an issue that is scoped to only the current request, so only
335 // bypass the data reduction proxy for the current request. 356 // bypass the data reduction proxy for the current request.
336 data_reduction_proxy_info->bypass_all = true; 357 data_reduction_proxy_info->bypass_all = true;
337 data_reduction_proxy_info->mark_proxies_as_bad = false; 358 data_reduction_proxy_info->mark_proxies_as_bad = false;
338 data_reduction_proxy_info->bypass_duration = TimeDelta(); 359 data_reduction_proxy_info->bypass_duration = TimeDelta();
339 return BYPASS_EVENT_TYPE_MISSING_VIA_HEADER_4XX; 360 return BYPASS_EVENT_TYPE_MISSING_VIA_HEADER_4XX;
340 } 361 }
341 362
342 // Missing the via header should not trigger bypass if the client is 363 // Missing the via header should not trigger bypass if the client is
(...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after
392 std::string value; 413 std::string value;
393 size_t iter = 0; 414 size_t iter = 0;
394 while (headers->EnumerateHeader(&iter, kChromeProxyHeader, &value)) { 415 while (headers->EnumerateHeader(&iter, kChromeProxyHeader, &value)) {
395 if (StartsWithActionPrefix(value, kChromeProxyActionFingerprintChromeProxy)) 416 if (StartsWithActionPrefix(value, kChromeProxyActionFingerprintChromeProxy))
396 continue; 417 continue;
397 values->push_back(std::move(value)); 418 values->push_back(std::move(value));
398 } 419 }
399 } 420 }
400 421
401 } // namespace data_reduction_proxy 422 } // namespace data_reduction_proxy
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698