Upstream version 11.39.266.0
[platform/framework/web/crosswalk.git] / src / components / data_reduction_proxy / common / data_reduction_proxy_headers.cc
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
3 // found in the LICENSE file.
4
5 #include "components/data_reduction_proxy/common/data_reduction_proxy_headers.h"
6
7 #include <string>
8 #include <vector>
9
10 #include "base/rand_util.h"
11 #include "base/strings/string_number_conversions.h"
12 #include "base/strings/string_piece.h"
13 #include "base/strings/string_util.h"
14 #include "base/time/time.h"
15 #include "net/http/http_response_headers.h"
16 #include "net/http/http_status_code.h"
17
18 using base::StringPiece;
19 using base::TimeDelta;
20
21 namespace {
22
23 const char kChromeProxyHeader[] = "chrome-proxy";
24 const char kActionValueDelimiter = '=';
25
26 const char kChromeProxyActionBlockOnce[] = "block-once";
27 const char kChromeProxyActionBlock[] = "block";
28 const char kChromeProxyActionBypass[] = "bypass";
29
30 // Actions for tamper detection fingerprints.
31 const char kChromeProxyActionFingerprintChromeProxy[]   = "fcp";
32 const char kChromeProxyActionFingerprintVia[]           = "fvia";
33 const char kChromeProxyActionFingerprintOtherHeaders[]  = "foh";
34 const char kChromeProxyActionFingerprintContentLength[] = "fcl";
35
36 const int kShortBypassMaxSeconds = 59;
37 const int kMediumBypassMaxSeconds = 300;
38
39 // Returns a random bypass duration between 1 and 5 minutes.
40 base::TimeDelta GetDefaultBypassDuration() {
41   const int64 delta_ms =
42       base::RandInt(base::TimeDelta::FromMinutes(1).InMilliseconds(),
43                     base::TimeDelta::FromMinutes(5).InMilliseconds());
44   return TimeDelta::FromMilliseconds(delta_ms);
45 }
46
47 }  // namespace
48
49 namespace data_reduction_proxy {
50
51 bool GetDataReductionProxyActionValue(
52     const net::HttpResponseHeaders* headers,
53     const std::string& action_prefix,
54     std::string* action_value) {
55   DCHECK(headers);
56   DCHECK(!action_prefix.empty());
57   // A valid action does not include a trailing '='.
58   DCHECK(action_prefix[action_prefix.size() - 1] != kActionValueDelimiter);
59   void* iter = NULL;
60   std::string value;
61   std::string prefix = action_prefix + kActionValueDelimiter;
62
63   while (headers->EnumerateHeader(&iter, kChromeProxyHeader, &value)) {
64     if (value.size() > prefix.size()) {
65       if (LowerCaseEqualsASCII(value.begin(),
66                                value.begin() + prefix.size(),
67                                prefix.c_str())) {
68         if (action_value)
69           *action_value = value.substr(prefix.size());
70         return true;
71       }
72     }
73   }
74   return false;
75 }
76
77 bool ParseHeadersAndSetBypassDuration(const net::HttpResponseHeaders* headers,
78                                       const std::string& action_prefix,
79                                       base::TimeDelta* bypass_duration) {
80   DCHECK(headers);
81   DCHECK(!action_prefix.empty());
82   // A valid action does not include a trailing '='.
83   DCHECK(action_prefix[action_prefix.size() - 1] != kActionValueDelimiter);
84   void* iter = NULL;
85   std::string value;
86   std::string prefix = action_prefix + kActionValueDelimiter;
87
88   while (headers->EnumerateHeader(&iter, kChromeProxyHeader, &value)) {
89     if (value.size() > prefix.size()) {
90       if (LowerCaseEqualsASCII(value.begin(),
91                                value.begin() + prefix.size(),
92                                prefix.c_str())) {
93         int64 seconds;
94         if (!base::StringToInt64(
95                 StringPiece(value.begin() + prefix.size(), value.end()),
96                 &seconds) || seconds < 0) {
97           continue;  // In case there is a well formed instruction.
98         }
99         if (seconds != 0) {
100           *bypass_duration = TimeDelta::FromSeconds(seconds);
101         } else {
102           // Server deferred to us to choose a duration. Default to a random
103           // duration between one and five minutes.
104           *bypass_duration = GetDefaultBypassDuration();
105         }
106         return true;
107       }
108     }
109   }
110   return false;
111 }
112
113 bool ParseHeadersAndSetProxyInfo(const net::HttpResponseHeaders* headers,
114                                  DataReductionProxyInfo* proxy_info) {
115   DCHECK(proxy_info);
116
117   // Support header of the form Chrome-Proxy: bypass|block=<duration>, where
118   // <duration> is the number of seconds to wait before retrying
119   // the proxy. If the duration is 0, then the default proxy retry delay
120   // (specified in |ProxyList::UpdateRetryInfoOnFallback|) will be used.
121   // 'bypass' instructs Chrome to bypass the currently connected data reduction
122   // proxy, whereas 'block' instructs Chrome to bypass all available data
123   // reduction proxies.
124
125   // 'block' takes precedence over 'bypass' and 'block-once', so look for it
126   // first.
127   // TODO(bengr): Reduce checks for 'block' and 'bypass' to a single loop.
128   if (ParseHeadersAndSetBypassDuration(
129           headers, kChromeProxyActionBlock, &proxy_info->bypass_duration)) {
130     proxy_info->bypass_all = true;
131     proxy_info->mark_proxies_as_bad = true;
132     return true;
133   }
134
135   // Next, look for 'bypass'.
136   if (ParseHeadersAndSetBypassDuration(
137           headers, kChromeProxyActionBypass, &proxy_info->bypass_duration)) {
138     proxy_info->bypass_all = false;
139     proxy_info->mark_proxies_as_bad = true;
140     return true;
141   }
142
143   // Lastly, look for 'block-once'. 'block-once' instructs Chrome to retry the
144   // current request (if it's idempotent), bypassing all available data
145   // reduction proxies. Unlike 'block', 'block-once' does not cause data
146   // reduction proxies to be bypassed for an extended period of time;
147   // 'block-once' only affects the retry of the current request.
148   if (headers->HasHeaderValue(kChromeProxyHeader,
149                               kChromeProxyActionBlockOnce)) {
150     proxy_info->bypass_all = true;
151     proxy_info->mark_proxies_as_bad = false;
152     proxy_info->bypass_duration = TimeDelta();
153     return true;
154   }
155
156   return false;
157 }
158
159 bool HasDataReductionProxyViaHeader(const net::HttpResponseHeaders* headers,
160                                     bool* has_intermediary) {
161   const size_t kVersionSize = 4;
162   const char kDataReductionProxyViaValue[] = "Chrome-Compression-Proxy";
163   size_t value_len = strlen(kDataReductionProxyViaValue);
164   void* iter = NULL;
165   std::string value;
166
167   // Case-sensitive comparison of |value|. Assumes the received protocol and the
168   // space following it are always |kVersionSize| characters. E.g.,
169   // 'Via: 1.1 Chrome-Compression-Proxy'
170   while (headers->EnumerateHeader(&iter, "via", &value)) {
171     if (value.size() >= kVersionSize + value_len &&
172         !value.compare(kVersionSize, value_len, kDataReductionProxyViaValue)) {
173       if (has_intermediary)
174         // We assume intermediary exists if there is another Via header after
175         // the data reduction proxy's Via header.
176         *has_intermediary = !(headers->EnumerateHeader(&iter, "via", &value));
177       return true;
178     }
179   }
180
181   // TODO(bengr): Remove deprecated header value.
182   const char kDeprecatedDataReductionProxyViaValue[] =
183       "1.1 Chrome Compression Proxy";
184   iter = NULL;
185   while (headers->EnumerateHeader(&iter, "via", &value))
186     if (value == kDeprecatedDataReductionProxyViaValue) {
187       if (has_intermediary)
188         *has_intermediary = !(headers->EnumerateHeader(&iter, "via", &value));
189       return true;
190     }
191
192   return false;
193 }
194
195 DataReductionProxyBypassType GetDataReductionProxyBypassType(
196     const net::HttpResponseHeaders* headers,
197     DataReductionProxyInfo* data_reduction_proxy_info) {
198   DCHECK(data_reduction_proxy_info);
199   if (ParseHeadersAndSetProxyInfo(headers, data_reduction_proxy_info)) {
200     // A chrome-proxy response header is only present in a 502. For proper
201     // reporting, this check must come before the 5xx checks below.
202     if (!data_reduction_proxy_info->mark_proxies_as_bad)
203       return BYPASS_EVENT_TYPE_CURRENT;
204
205     const TimeDelta& duration = data_reduction_proxy_info->bypass_duration;
206     if (duration <= TimeDelta::FromSeconds(kShortBypassMaxSeconds))
207       return BYPASS_EVENT_TYPE_SHORT;
208     if (duration <= TimeDelta::FromSeconds(kMediumBypassMaxSeconds))
209       return BYPASS_EVENT_TYPE_MEDIUM;
210     return BYPASS_EVENT_TYPE_LONG;
211   }
212
213   // If a bypass is triggered by any of the following cases, then the data
214   // reduction proxy should be bypassed for a random duration between 1 and 5
215   // minutes.
216   data_reduction_proxy_info->mark_proxies_as_bad = true;
217   data_reduction_proxy_info->bypass_duration = GetDefaultBypassDuration();
218
219   // Fall back if a 500, 502 or 503 is returned.
220   if (headers->response_code() == net::HTTP_INTERNAL_SERVER_ERROR)
221     return BYPASS_EVENT_TYPE_STATUS_500_HTTP_INTERNAL_SERVER_ERROR;
222   if (headers->response_code() == net::HTTP_BAD_GATEWAY)
223     return BYPASS_EVENT_TYPE_STATUS_502_HTTP_BAD_GATEWAY;
224   if (headers->response_code() == net::HTTP_SERVICE_UNAVAILABLE)
225     return BYPASS_EVENT_TYPE_STATUS_503_HTTP_SERVICE_UNAVAILABLE;
226   // TODO(kundaji): Bypass if Proxy-Authenticate header value cannot be
227   // interpreted by data reduction proxy.
228   if (headers->response_code() == net::HTTP_PROXY_AUTHENTICATION_REQUIRED &&
229       !headers->HasHeader("Proxy-Authenticate")) {
230     return BYPASS_EVENT_TYPE_MALFORMED_407;
231   }
232   if (!HasDataReductionProxyViaHeader(headers, NULL) &&
233       (headers->response_code() != net::HTTP_NOT_MODIFIED)) {
234     // A Via header might not be present in a 304. Since the goal of a 304
235     // response is to minimize information transfer, a sender in general
236     // should not generate representation metadata other than Cache-Control,
237     // Content-Location, Date, ETag, Expires, and Vary.
238
239     // The proxy Via header might also not be present in a 4xx response.
240     // Separate this case from other responses that are missing the header.
241     if (headers->response_code() >= net::HTTP_BAD_REQUEST &&
242         headers->response_code() < net::HTTP_INTERNAL_SERVER_ERROR) {
243       return BYPASS_EVENT_TYPE_MISSING_VIA_HEADER_4XX;
244     }
245     return BYPASS_EVENT_TYPE_MISSING_VIA_HEADER_OTHER;
246   }
247   // There is no bypass event.
248   return BYPASS_EVENT_TYPE_MAX;
249 }
250
251 bool GetDataReductionProxyActionFingerprintChromeProxy(
252     const net::HttpResponseHeaders* headers,
253     std::string* chrome_proxy_fingerprint) {
254   return GetDataReductionProxyActionValue(
255       headers,
256       kChromeProxyActionFingerprintChromeProxy,
257       chrome_proxy_fingerprint);
258 }
259
260 bool GetDataReductionProxyActionFingerprintVia(
261     const net::HttpResponseHeaders* headers,
262     std::string* via_fingerprint) {
263   return GetDataReductionProxyActionValue(
264       headers,
265       kChromeProxyActionFingerprintVia,
266       via_fingerprint);
267 }
268
269 bool GetDataReductionProxyActionFingerprintOtherHeaders(
270     const net::HttpResponseHeaders* headers,
271     std::string* other_headers_fingerprint) {
272   return GetDataReductionProxyActionValue(
273       headers,
274       kChromeProxyActionFingerprintOtherHeaders,
275       other_headers_fingerprint);
276 }
277
278 bool GetDataReductionProxyActionFingerprintContentLength(
279     const net::HttpResponseHeaders* headers,
280     std::string* content_length_fingerprint) {
281   return GetDataReductionProxyActionValue(
282       headers,
283       kChromeProxyActionFingerprintContentLength,
284       content_length_fingerprint);
285 }
286
287 void GetDataReductionProxyHeaderWithFingerprintRemoved(
288     const net::HttpResponseHeaders* headers,
289     std::vector<std::string>* values) {
290   DCHECK(values);
291   std::string chrome_proxy_fingerprint_prefix = std::string(
292       kChromeProxyActionFingerprintChromeProxy) + kActionValueDelimiter;
293
294   std::string value;
295   void* iter = NULL;
296   while (headers->EnumerateHeader(&iter, kChromeProxyHeader, &value)) {
297     if (value.size() > chrome_proxy_fingerprint_prefix.size()) {
298       if (LowerCaseEqualsASCII(
299           value.begin(),
300           value.begin() + chrome_proxy_fingerprint_prefix.size(),
301           chrome_proxy_fingerprint_prefix.c_str())) {
302         continue;
303       }
304     }
305     values->push_back(value);
306   }
307 }
308
309 }  // namespace data_reduction_proxy