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.
5 #include "components/data_reduction_proxy/browser/data_reduction_proxy_protocol.h"
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"
27 bool SetProxyServerFromGURL(const GURL& gurl,
28 net::ProxyServer* proxy_server) {
30 if (!gurl.SchemeIsHTTPOrHTTPS())
32 *proxy_server = net::ProxyServer(gurl.SchemeIs("http") ?
33 net::ProxyServer::SCHEME_HTTP :
34 net::ProxyServer::SCHEME_HTTPS,
35 net::HostPortPair::FromURL(gurl));
41 namespace data_reduction_proxy {
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)
51 DataReductionProxyTypeInfo data_reduction_proxy_type_info;
52 if (!data_reduction_proxy_params->WasDataReductionProxyUsed(
53 request, &data_reduction_proxy_type_info)) {
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())
62 if (data_reduction_proxy_type_info.proxy_servers.first.is_empty())
65 DataReductionProxyTamperDetection::DetectAndReport(
66 original_response_headers,
67 data_reduction_proxy_type_info.proxy_servers.first.SchemeIsSecure());
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)
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);
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,
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);
102 // Only retry idempotent methods.
103 if (!IsRequestIdempotent(request))
106 OverrideResponseAsRedirect(request,
107 original_response_headers,
108 override_response_headers);
112 void OnResolveProxyHandler(const GURL& url,
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);
127 if ((load_flags & net::LOAD_BYPASS_DATA_REDUCTION_PROXY) &&
128 DataReductionProxyParams::IsIncludedInCriticalPathBypassFieldTrial() &&
129 !result->is_empty() &&
130 !result->is_direct() &&
132 params->IsDataReductionProxy(
133 result->proxy_server().host_port_pair(), NULL)) {
138 bool IsRequestIdempotent(const net::URLRequest* 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")
150 void OverrideResponseAsRedirect(
151 net::URLRequest* request,
152 const net::HttpResponseHeaders* original_response_headers,
153 scoped_refptr<net::HttpResponseHeaders>* override_response_headers) {
155 DCHECK(original_response_headers);
156 DCHECK(override_response_headers->get() == NULL);
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");
181 // TODO(bengr): Should we pop_back the request->url_chain?
184 void MarkProxiesAsBadUntil(
185 net::URLRequest* request,
186 base::TimeDelta& bypass_duration,
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;
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());
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);
211 proxy_service->MarkProxiesAsBadUntil(proxy_info,
217 } // namespace data_reduction_proxy