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
)) {
56 // TODO(bengr): Implement bypass for CONNECT tunnel.
57 if (data_reduction_proxy_type_info
.is_ssl
)
60 // Empty implies either that the request was served from cache or that
61 // request was served directly from the origin.
62 if (request
->proxy_server().IsEmpty())
65 if (data_reduction_proxy_type_info
.proxy_servers
.first
.is_empty())
68 // At this point, the response is expected to have the data reduction proxy
69 // via header, so detect and report cases where the via header is missing.
70 DataReductionProxyUsageStats::DetectAndRecordMissingViaHeaderResponseCode(
71 !data_reduction_proxy_type_info
.proxy_servers
.second
.is_empty(),
72 original_response_headers
);
74 DataReductionProxyTamperDetection::DetectAndReport(
75 original_response_headers
,
76 data_reduction_proxy_type_info
.proxy_servers
.first
.SchemeIsSecure());
78 DataReductionProxyInfo data_reduction_proxy_info
;
79 DataReductionProxyBypassType bypass_type
=
80 GetDataReductionProxyBypassType(original_response_headers
,
81 &data_reduction_proxy_info
);
83 if (bypass_type
== BYPASS_EVENT_TYPE_MISSING_VIA_HEADER_OTHER
&&
84 DataReductionProxyParams::
85 IsIncludedInRemoveMissingViaHeaderOtherBypassFieldTrial()) {
86 // Ignore MISSING_VIA_HEADER_OTHER proxy bypass events if the client is part
87 // of the field trial to remove these kinds of bypasses.
88 bypass_type
= BYPASS_EVENT_TYPE_MAX
;
91 if (proxy_bypass_type
)
92 *proxy_bypass_type
= bypass_type
;
93 if (bypass_type
== BYPASS_EVENT_TYPE_MAX
)
96 DCHECK(request
->context());
97 DCHECK(request
->context()->proxy_service());
98 net::ProxyServer proxy_server
;
99 SetProxyServerFromGURL(
100 data_reduction_proxy_type_info
.proxy_servers
.first
, &proxy_server
);
102 // Only record UMA if the proxy isn't already on the retry list.
103 const net::ProxyRetryInfoMap
& proxy_retry_info
=
104 request
->context()->proxy_service()->proxy_retry_info();
105 if (proxy_retry_info
.find(proxy_server
.ToURI()) == proxy_retry_info
.end()) {
106 DataReductionProxyUsageStats::RecordDataReductionProxyBypassInfo(
107 !data_reduction_proxy_type_info
.proxy_servers
.second
.is_empty(),
108 data_reduction_proxy_info
.bypass_all
,
113 if (data_reduction_proxy_info
.mark_proxies_as_bad
) {
114 MarkProxiesAsBadUntil(request
,
115 data_reduction_proxy_info
.bypass_duration
,
116 data_reduction_proxy_info
.bypass_all
,
117 data_reduction_proxy_type_info
.proxy_servers
);
120 // Only retry idempotent methods.
121 if (!IsRequestIdempotent(request
))
124 OverrideResponseAsRedirect(request
,
125 original_response_headers
,
126 override_response_headers
);
130 void OnResolveProxyHandler(const GURL
& url
,
132 const net::ProxyConfig
& data_reduction_proxy_config
,
133 const net::ProxyRetryInfoMap
& proxy_retry_info
,
134 const DataReductionProxyParams
* params
,
135 net::ProxyInfo
* result
) {
136 if (data_reduction_proxy_config
.is_valid() &&
137 result
->proxy_server().is_direct()) {
138 net::ProxyInfo data_reduction_proxy_info
;
139 data_reduction_proxy_config
.proxy_rules().Apply(
140 url
, &data_reduction_proxy_info
);
141 data_reduction_proxy_info
.DeprioritizeBadProxies(proxy_retry_info
);
142 result
->Use(data_reduction_proxy_info
);
145 if ((load_flags
& net::LOAD_BYPASS_DATA_REDUCTION_PROXY
) &&
146 DataReductionProxyParams::IsIncludedInCriticalPathBypassFieldTrial() &&
147 !result
->is_empty() &&
148 !result
->is_direct() &&
150 params
->IsDataReductionProxy(
151 result
->proxy_server().host_port_pair(), NULL
)) {
156 bool IsRequestIdempotent(const net::URLRequest
* request
) {
158 if (request
->method() == "GET" ||
159 request
->method() == "OPTIONS" ||
160 request
->method() == "HEAD" ||
161 request
->method() == "PUT" ||
162 request
->method() == "DELETE" ||
163 request
->method() == "TRACE")
168 void OverrideResponseAsRedirect(
169 net::URLRequest
* request
,
170 const net::HttpResponseHeaders
* original_response_headers
,
171 scoped_refptr
<net::HttpResponseHeaders
>* override_response_headers
) {
173 DCHECK(original_response_headers
);
174 DCHECK(override_response_headers
->get() == NULL
);
176 request
->SetLoadFlags(request
->load_flags() |
177 net::LOAD_DISABLE_CACHE
|
178 net::LOAD_BYPASS_PROXY
);
179 *override_response_headers
= new net::HttpResponseHeaders(
180 original_response_headers
->raw_headers());
181 (*override_response_headers
)->ReplaceStatusLine("HTTP/1.1 302 Found");
182 (*override_response_headers
)->RemoveHeader("Location");
183 (*override_response_headers
)->AddHeader("Location: " +
184 request
->url().spec());
185 std::string http_origin
;
186 const net::HttpRequestHeaders
& request_headers
=
187 request
->extra_request_headers();
188 if (request_headers
.GetHeader("Origin", &http_origin
)) {
189 // If this redirect is used in a cross-origin request, add CORS headers to
190 // make sure that the redirect gets through. Note that the destination URL
191 // is still subject to the usual CORS policy, i.e. the resource will only
192 // be available to web pages if the server serves the response with the
193 // required CORS response headers.
194 (*override_response_headers
)->AddHeader(
195 "Access-Control-Allow-Origin: " + http_origin
);
196 (*override_response_headers
)->AddHeader(
197 "Access-Control-Allow-Credentials: true");
199 // TODO(bengr): Should we pop_back the request->url_chain?
202 void MarkProxiesAsBadUntil(
203 net::URLRequest
* request
,
204 base::TimeDelta
& bypass_duration
,
206 const std::pair
<GURL
, GURL
>& data_reduction_proxies
) {
207 DCHECK(!data_reduction_proxies
.first
.is_empty());
208 // Synthesize a suitable |ProxyInfo| to add the proxies to the
209 // |ProxyRetryInfoMap| of the proxy service.
210 net::ProxyList proxy_list
;
211 net::ProxyServer primary
;
212 SetProxyServerFromGURL(data_reduction_proxies
.first
, &primary
);
213 if (primary
.is_valid())
214 proxy_list
.AddProxyServer(primary
);
215 net::ProxyServer fallback
;
217 if (!data_reduction_proxies
.second
.is_empty())
218 SetProxyServerFromGURL(data_reduction_proxies
.second
, &fallback
);
219 if (fallback
.is_valid())
220 proxy_list
.AddProxyServer(fallback
);
221 proxy_list
.AddProxyServer(net::ProxyServer::Direct());
223 net::ProxyInfo proxy_info
;
224 proxy_info
.UseProxyList(proxy_list
);
225 DCHECK(request
->context());
226 net::ProxyService
* proxy_service
= request
->context()->proxy_service();
227 DCHECK(proxy_service
);
229 proxy_service
->MarkProxiesAsBadUntil(proxy_info
,
235 } // namespace data_reduction_proxy