1 // Copyright 2019 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 "android_webview/browser/network_service/aw_url_loader_throttle.h"
6 
7 #include "android_webview/browser/aw_resource_context.h"
8 #include "android_webview/common/aw_features.h"
9 #include "base/feature_list.h"
10 #include "base/metrics/histogram_macros.h"
11 #include "base/strings/string_util.h"
12 #include "net/base/registry_controlled_domains/registry_controlled_domain.h"
13 #include "net/http/http_request_headers.h"
14 #include "net/url_request/redirect_info.h"
15 #include "services/network/public/cpp/resource_request.h"
16 
17 namespace android_webview {
18 
19 namespace {
20 
21 // These values are logged to UMA. Entries should not be renumbered and
22 // numeric values should never be reused. Please keep in sync with
23 // "WebViewExtraHeadersRedirect" in src/tools/metrics/histograms/enums.xml.
24 enum class ExtraHeadersRedirect {
25   kSameOrigin = 0,
26   kSameDomain = 1,
27   kCrossDomain = 2,
28   kMaxValue = kCrossDomain
29 };
30 
RecordExtraHeadersRedirectUMA(ExtraHeadersRedirect value)31 void RecordExtraHeadersRedirectUMA(ExtraHeadersRedirect value) {
32   UMA_HISTOGRAM_ENUMERATION("Android.WebView.ExtraHeadersRedirect", value);
33 }
34 
35 }  // namespace
36 
AwURLLoaderThrottle(AwResourceContext * aw_resource_context)37 AwURLLoaderThrottle::AwURLLoaderThrottle(AwResourceContext* aw_resource_context)
38     : aw_resource_context_(aw_resource_context) {}
39 
40 AwURLLoaderThrottle::~AwURLLoaderThrottle() = default;
41 
WillStartRequest(network::ResourceRequest * request,bool * defer)42 void AwURLLoaderThrottle::WillStartRequest(network::ResourceRequest* request,
43                                            bool* defer) {
44   AddExtraHeadersIfNeeded(request->url, &request->headers);
45   if (!added_headers_.empty()) {
46     original_origin_ = url::Origin::Create(request->url);
47   }
48 }
49 
WillRedirectRequest(net::RedirectInfo * redirect_info,const network::mojom::URLResponseHead & response_head,bool * defer,std::vector<std::string> * to_be_removed_request_headers,net::HttpRequestHeaders * modified_request_headers,net::HttpRequestHeaders * modified_cors_exempt_request_headers)50 void AwURLLoaderThrottle::WillRedirectRequest(
51     net::RedirectInfo* redirect_info,
52     const network::mojom::URLResponseHead& response_head,
53     bool* defer,
54     std::vector<std::string>* to_be_removed_request_headers,
55     net::HttpRequestHeaders* modified_request_headers,
56     net::HttpRequestHeaders* modified_cors_exempt_request_headers) {
57   bool same_origin_only = base::FeatureList::IsEnabled(
58       features::kWebViewExtraHeadersSameOriginOnly);
59   bool same_domain_only = base::FeatureList::IsEnabled(
60       features::kWebViewExtraHeadersSameDomainOnly);
61 
62   if (!added_headers_.empty()) {
63     bool is_same_origin =
64         original_origin_.CanBeDerivedFrom(redirect_info->new_url);
65     bool is_same_domain = net::registry_controlled_domains::SameDomainOrHost(
66         redirect_info->new_url, original_origin_,
67         net::registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES);
68 
69     if (is_same_origin) {
70       RecordExtraHeadersRedirectUMA(ExtraHeadersRedirect::kSameOrigin);
71     } else if (is_same_domain) {
72       RecordExtraHeadersRedirectUMA(ExtraHeadersRedirect::kSameDomain);
73     } else {
74       RecordExtraHeadersRedirectUMA(ExtraHeadersRedirect::kCrossDomain);
75     }
76 
77     if ((same_origin_only && !is_same_origin) ||
78         (same_domain_only && !is_same_domain)) {
79       // The headers we added must be removed.
80       to_be_removed_request_headers->insert(
81           to_be_removed_request_headers->end(),
82           std::make_move_iterator(added_headers_.begin()),
83           std::make_move_iterator(added_headers_.end()));
84       added_headers_.clear();
85     }
86   }
87 
88   if (!same_origin_only && !same_domain_only) {
89     // The original behaviour added more headers if the redirect target had
90     // previously been loaded with extra headers; this is weird/surprising, so
91     // it's skipped when either feature is enabled.
92     AddExtraHeadersIfNeeded(redirect_info->new_url, modified_request_headers);
93   }
94 }
95 
AddExtraHeadersIfNeeded(const GURL & url,net::HttpRequestHeaders * headers)96 void AwURLLoaderThrottle::AddExtraHeadersIfNeeded(
97     const GURL& url,
98     net::HttpRequestHeaders* headers) {
99   std::string extra_headers = aw_resource_context_->GetExtraHeaders(url);
100   if (extra_headers.empty())
101     return;
102 
103   net::HttpRequestHeaders temp_headers;
104   temp_headers.AddHeadersFromString(extra_headers);
105   for (net::HttpRequestHeaders::Iterator it(temp_headers); it.GetNext();) {
106     if (headers->HasHeader(it.name()))
107       continue;
108 
109     headers->SetHeader(it.name(), it.value());
110     added_headers_.push_back(it.name());
111   }
112 }
113 
114 }  // namespace android_webview
115