OLD | NEW |
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 Loading... |
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 Loading... |
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 Loading... |
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 Loading... |
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 |
OLD | NEW |