Home | History | Annotate | Download | only in browser
      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   // TODO(bengr): Implement bypass for CONNECT tunnel.
     57   if (data_reduction_proxy_type_info.is_ssl)
     58     return false;
     59 
     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())
     63     return false;
     64 
     65   if (data_reduction_proxy_type_info.proxy_servers.first.is_empty())
     66     return false;
     67 
     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);
     73 
     74   DataReductionProxyTamperDetection::DetectAndReport(
     75       original_response_headers,
     76       data_reduction_proxy_type_info.proxy_servers.first.SchemeIsSecure());
     77 
     78   DataReductionProxyInfo data_reduction_proxy_info;
     79   DataReductionProxyBypassType bypass_type =
     80       GetDataReductionProxyBypassType(original_response_headers,
     81                                       &data_reduction_proxy_info);
     82 
     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;
     89   }
     90 
     91   if (proxy_bypass_type)
     92     *proxy_bypass_type = bypass_type;
     93   if (bypass_type == BYPASS_EVENT_TYPE_MAX)
     94     return false;
     95 
     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);
    101 
    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,
    109         proxy_server,
    110         bypass_type);
    111   }
    112 
    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);
    118   }
    119 
    120   // Only retry idempotent methods.
    121   if (!IsRequestIdempotent(request))
    122     return false;
    123 
    124   OverrideResponseAsRedirect(request,
    125                              original_response_headers,
    126                              override_response_headers);
    127   return true;
    128 }
    129 
    130 void OnResolveProxyHandler(const GURL& url,
    131                            int load_flags,
    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     if (!data_reduction_proxy_info.proxy_server().is_direct())
    143       result->UseProxyList(data_reduction_proxy_info.proxy_list());
    144   }
    145 
    146   if ((load_flags & net::LOAD_BYPASS_DATA_REDUCTION_PROXY) &&
    147       DataReductionProxyParams::IsIncludedInCriticalPathBypassFieldTrial() &&
    148       !result->is_empty() &&
    149       !result->is_direct() &&
    150       params &&
    151       params->IsDataReductionProxy(
    152           result->proxy_server().host_port_pair(), NULL)) {
    153     result->UseDirect();
    154   }
    155 }
    156 
    157 bool IsRequestIdempotent(const net::URLRequest* request) {
    158   DCHECK(request);
    159   if (request->method() == "GET" ||
    160       request->method() == "OPTIONS" ||
    161       request->method() == "HEAD" ||
    162       request->method() == "PUT" ||
    163       request->method() == "DELETE" ||
    164       request->method() == "TRACE")
    165     return true;
    166   return false;
    167 }
    168 
    169 void OverrideResponseAsRedirect(
    170     net::URLRequest* request,
    171     const net::HttpResponseHeaders* original_response_headers,
    172     scoped_refptr<net::HttpResponseHeaders>* override_response_headers) {
    173   DCHECK(request);
    174   DCHECK(original_response_headers);
    175   DCHECK(override_response_headers->get() == NULL);
    176 
    177   request->SetLoadFlags(request->load_flags() |
    178                         net::LOAD_DISABLE_CACHE |
    179                         net::LOAD_BYPASS_PROXY);
    180   *override_response_headers = new net::HttpResponseHeaders(
    181       original_response_headers->raw_headers());
    182   (*override_response_headers)->ReplaceStatusLine("HTTP/1.1 302 Found");
    183   (*override_response_headers)->RemoveHeader("Location");
    184   (*override_response_headers)->AddHeader("Location: " +
    185                                           request->url().spec());
    186   std::string http_origin;
    187   const net::HttpRequestHeaders& request_headers =
    188       request->extra_request_headers();
    189   if (request_headers.GetHeader("Origin", &http_origin)) {
    190     // If this redirect is used in a cross-origin request, add CORS headers to
    191     // make sure that the redirect gets through. Note that the destination URL
    192     // is still subject to the usual CORS policy, i.e. the resource will only
    193     // be available to web pages if the server serves the response with the
    194     // required CORS response headers.
    195     (*override_response_headers)->AddHeader(
    196         "Access-Control-Allow-Origin: " + http_origin);
    197     (*override_response_headers)->AddHeader(
    198         "Access-Control-Allow-Credentials: true");
    199   }
    200   // TODO(bengr): Should we pop_back the request->url_chain?
    201 }
    202 
    203 void MarkProxiesAsBadUntil(
    204     net::URLRequest* request,
    205     base::TimeDelta& bypass_duration,
    206     bool bypass_all,
    207     const std::pair<GURL, GURL>& data_reduction_proxies) {
    208   DCHECK(!data_reduction_proxies.first.is_empty());
    209   // Synthesize a suitable |ProxyInfo| to add the proxies to the
    210   // |ProxyRetryInfoMap| of the proxy service.
    211   net::ProxyList proxy_list;
    212   net::ProxyServer primary;
    213   SetProxyServerFromGURL(data_reduction_proxies.first, &primary);
    214   if (primary.is_valid())
    215     proxy_list.AddProxyServer(primary);
    216   net::ProxyServer fallback;
    217   if (bypass_all) {
    218     if (!data_reduction_proxies.second.is_empty())
    219       SetProxyServerFromGURL(data_reduction_proxies.second, &fallback);
    220     if (fallback.is_valid())
    221       proxy_list.AddProxyServer(fallback);
    222     proxy_list.AddProxyServer(net::ProxyServer::Direct());
    223   }
    224   net::ProxyInfo proxy_info;
    225   proxy_info.UseProxyList(proxy_list);
    226   DCHECK(request->context());
    227   net::ProxyService* proxy_service = request->context()->proxy_service();
    228   DCHECK(proxy_service);
    229 
    230   proxy_service->MarkProxiesAsBadUntil(proxy_info,
    231                                        bypass_duration,
    232                                        fallback,
    233                                        request->net_log());
    234 }
    235 
    236 }  // namespace data_reduction_proxy
    237