data_reduction_proxy_protocol.cc revision 6e8cce623b6e4fe0c9e4af605d675dd9d0338c38
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 25namespace { 26 27bool 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 41namespace data_reduction_proxy { 42 43bool 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 112void 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 138bool 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 150void 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 // TODO(bengr): Should we pop_back the request->url_chain? 168} 169 170void MarkProxiesAsBadUntil( 171 net::URLRequest* request, 172 base::TimeDelta& bypass_duration, 173 bool bypass_all, 174 const std::pair<GURL, GURL>& data_reduction_proxies) { 175 DCHECK(!data_reduction_proxies.first.is_empty()); 176 // Synthesize a suitable |ProxyInfo| to add the proxies to the 177 // |ProxyRetryInfoMap| of the proxy service. 178 net::ProxyList proxy_list; 179 net::ProxyServer primary; 180 SetProxyServerFromGURL(data_reduction_proxies.first, &primary); 181 if (primary.is_valid()) 182 proxy_list.AddProxyServer(primary); 183 net::ProxyServer fallback; 184 if (bypass_all) { 185 if (!data_reduction_proxies.second.is_empty()) 186 SetProxyServerFromGURL(data_reduction_proxies.second, &fallback); 187 if (fallback.is_valid()) 188 proxy_list.AddProxyServer(fallback); 189 proxy_list.AddProxyServer(net::ProxyServer::Direct()); 190 } 191 net::ProxyInfo proxy_info; 192 proxy_info.UseProxyList(proxy_list); 193 DCHECK(request->context()); 194 net::ProxyService* proxy_service = request->context()->proxy_service(); 195 DCHECK(proxy_service); 196 197 proxy_service->MarkProxiesAsBadUntil(proxy_info, 198 bypass_duration, 199 fallback, 200 request->net_log()); 201} 202 203} // namespace data_reduction_proxy 204