Upstream version 10.38.220.0
[platform/framework/web/crosswalk.git] / src / components / data_reduction_proxy / browser / data_reduction_proxy_protocol.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/browser/data_reduction_proxy_protocol.h"
6
7 #include "base/memory/ref_counted.h"
8 #include "base/time/time.h"
9 #include "components/data_reduction_proxy/browser/data_reduction_proxy_params.h"
10 #include "components/data_reduction_proxy/browser/data_reduction_proxy_tamper_detection.h"
11 #include "components/data_reduction_proxy/browser/data_reduction_proxy_usage_stats.h"
12 #include "components/data_reduction_proxy/common/data_reduction_proxy_headers.h"
13 #include "net/base/load_flags.h"
14 #include "net/http/http_response_headers.h"
15 #include "net/proxy/proxy_config.h"
16 #include "net/proxy/proxy_info.h"
17 #include "net/proxy/proxy_list.h"
18 #include "net/proxy/proxy_retry_info.h"
19 #include "net/proxy/proxy_server.h"
20 #include "net/proxy/proxy_service.h"
21 #include "net/url_request/url_request.h"
22 #include "net/url_request/url_request_context.h"
23 #include "url/gurl.h"
24
25 namespace {
26
27 bool SetProxyServerFromGURL(const GURL& gurl,
28                             net::ProxyServer* proxy_server) {
29   DCHECK(proxy_server);
30   if (!gurl.SchemeIsHTTPOrHTTPS())
31     return false;
32   *proxy_server = net::ProxyServer(gurl.SchemeIs("http") ?
33                                        net::ProxyServer::SCHEME_HTTP :
34                                        net::ProxyServer::SCHEME_HTTPS,
35                                    net::HostPortPair::FromURL(gurl));
36   return true;
37 }
38
39 }  // namespace
40
41 namespace data_reduction_proxy {
42
43 bool MaybeBypassProxyAndPrepareToRetry(
44     const DataReductionProxyParams* data_reduction_proxy_params,
45     net::URLRequest* request,
46     const net::HttpResponseHeaders* original_response_headers,
47     scoped_refptr<net::HttpResponseHeaders>* override_response_headers,
48     DataReductionProxyBypassType* proxy_bypass_type) {
49   if (!data_reduction_proxy_params)
50     return false;
51   DataReductionProxyTypeInfo data_reduction_proxy_type_info;
52   if (!data_reduction_proxy_params->WasDataReductionProxyUsed(
53           request, &data_reduction_proxy_type_info)) {
54     return false;
55   }
56
57   // Empty implies either that the request was served from cache or that
58   // request was served directly from the origin.
59   if (request->proxy_server().IsEmpty())
60     return false;
61
62   if (data_reduction_proxy_type_info.proxy_servers.first.is_empty())
63     return false;
64
65   DataReductionProxyTamperDetection::DetectAndReport(
66       original_response_headers,
67       data_reduction_proxy_type_info.proxy_servers.first.SchemeIsSecure());
68
69   DataReductionProxyInfo data_reduction_proxy_info;
70   DataReductionProxyBypassType bypass_type =
71       GetDataReductionProxyBypassType(original_response_headers,
72                                       &data_reduction_proxy_info);
73   if (proxy_bypass_type)
74     *proxy_bypass_type = bypass_type;
75   if (bypass_type == BYPASS_EVENT_TYPE_MAX)
76     return false;
77
78   DCHECK(request->context());
79   DCHECK(request->context()->proxy_service());
80   net::ProxyServer proxy_server;
81   SetProxyServerFromGURL(
82       data_reduction_proxy_type_info.proxy_servers.first, &proxy_server);
83
84   // Only record UMA if the proxy isn't already on the retry list.
85   const net::ProxyRetryInfoMap& proxy_retry_info =
86       request->context()->proxy_service()->proxy_retry_info();
87   if (proxy_retry_info.find(proxy_server.ToURI()) == proxy_retry_info.end()) {
88     DataReductionProxyUsageStats::RecordDataReductionProxyBypassInfo(
89         !data_reduction_proxy_type_info.proxy_servers.second.is_empty(),
90         data_reduction_proxy_info.bypass_all,
91         proxy_server,
92         bypass_type);
93   }
94
95   if (data_reduction_proxy_info.mark_proxies_as_bad) {
96     MarkProxiesAsBadUntil(request,
97                           data_reduction_proxy_info.bypass_duration,
98                           data_reduction_proxy_info.bypass_all,
99                           data_reduction_proxy_type_info.proxy_servers);
100   }
101
102   // Only retry idempotent methods.
103   if (!IsRequestIdempotent(request))
104     return false;
105
106   OverrideResponseAsRedirect(request,
107                              original_response_headers,
108                              override_response_headers);
109   return true;
110 }
111
112 void OnResolveProxyHandler(const GURL& url,
113                            int load_flags,
114                            const net::ProxyConfig& data_reduction_proxy_config,
115                            const net::ProxyRetryInfoMap& proxy_retry_info,
116                            const DataReductionProxyParams* params,
117                            net::ProxyInfo* result) {
118   if (data_reduction_proxy_config.is_valid() &&
119       result->proxy_server().is_direct()) {
120     net::ProxyInfo data_reduction_proxy_info;
121     data_reduction_proxy_config.proxy_rules().Apply(
122         url, &data_reduction_proxy_info);
123     data_reduction_proxy_info.DeprioritizeBadProxies(proxy_retry_info);
124     result->Use(data_reduction_proxy_info);
125   }
126
127   if ((load_flags & net::LOAD_BYPASS_DATA_REDUCTION_PROXY) &&
128       DataReductionProxyParams::IsIncludedInCriticalPathBypassFieldTrial() &&
129       !result->is_empty() &&
130       !result->is_direct() &&
131       params &&
132       params->IsDataReductionProxy(
133           result->proxy_server().host_port_pair(), NULL)) {
134     result->UseDirect();
135   }
136 }
137
138 bool IsRequestIdempotent(const net::URLRequest* request) {
139   DCHECK(request);
140   if (request->method() == "GET" ||
141       request->method() == "OPTIONS" ||
142       request->method() == "HEAD" ||
143       request->method() == "PUT" ||
144       request->method() == "DELETE" ||
145       request->method() == "TRACE")
146     return true;
147   return false;
148 }
149
150 void OverrideResponseAsRedirect(
151     net::URLRequest* request,
152     const net::HttpResponseHeaders* original_response_headers,
153     scoped_refptr<net::HttpResponseHeaders>* override_response_headers) {
154   DCHECK(request);
155   DCHECK(original_response_headers);
156   DCHECK(override_response_headers->get() == NULL);
157
158   request->SetLoadFlags(request->load_flags() |
159                         net::LOAD_DISABLE_CACHE |
160                         net::LOAD_BYPASS_PROXY);
161   *override_response_headers = new net::HttpResponseHeaders(
162       original_response_headers->raw_headers());
163   (*override_response_headers)->ReplaceStatusLine("HTTP/1.1 302 Found");
164   (*override_response_headers)->RemoveHeader("Location");
165   (*override_response_headers)->AddHeader("Location: " +
166                                           request->url().spec());
167   std::string http_origin;
168   const net::HttpRequestHeaders& request_headers =
169       request->extra_request_headers();
170   if (request_headers.GetHeader("Origin", &http_origin)) {
171     // If this redirect is used in a cross-origin request, add CORS headers to
172     // make sure that the redirect gets through. Note that the destination URL
173     // is still subject to the usual CORS policy, i.e. the resource will only
174     // be available to web pages if the server serves the response with the
175     // required CORS response headers.
176     (*override_response_headers)->AddHeader(
177         "Access-Control-Allow-Origin: " + http_origin);
178     (*override_response_headers)->AddHeader(
179         "Access-Control-Allow-Credentials: true");
180   }
181   // TODO(bengr): Should we pop_back the request->url_chain?
182 }
183
184 void MarkProxiesAsBadUntil(
185     net::URLRequest* request,
186     base::TimeDelta& bypass_duration,
187     bool bypass_all,
188     const std::pair<GURL, GURL>& data_reduction_proxies) {
189   DCHECK(!data_reduction_proxies.first.is_empty());
190   // Synthesize a suitable |ProxyInfo| to add the proxies to the
191   // |ProxyRetryInfoMap| of the proxy service.
192   net::ProxyList proxy_list;
193   net::ProxyServer primary;
194   SetProxyServerFromGURL(data_reduction_proxies.first, &primary);
195   if (primary.is_valid())
196     proxy_list.AddProxyServer(primary);
197   net::ProxyServer fallback;
198   if (bypass_all) {
199     if (!data_reduction_proxies.second.is_empty())
200       SetProxyServerFromGURL(data_reduction_proxies.second, &fallback);
201     if (fallback.is_valid())
202       proxy_list.AddProxyServer(fallback);
203     proxy_list.AddProxyServer(net::ProxyServer::Direct());
204   }
205   net::ProxyInfo proxy_info;
206   proxy_info.UseProxyList(proxy_list);
207   DCHECK(request->context());
208   net::ProxyService* proxy_service = request->context()->proxy_service();
209   DCHECK(proxy_service);
210
211   proxy_service->MarkProxiesAsBadUntil(proxy_info,
212                                        bypass_duration,
213                                        fallback,
214                                        request->net_log());
215 }
216
217 }  // namespace data_reduction_proxy