123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259 |
- // Copyright 2019 The Chromium Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style license that can be
- // found in the LICENSE file.
- #include "components/url_rewrite/common/url_loader_throttle.h"
- #include <string>
- #include "base/strings/string_split.h"
- #include "base/strings/string_util.h"
- #include "net/base/net_errors.h"
- #include "services/network/public/cpp/resource_request.h"
- #include "url/url_constants.h"
- namespace url_rewrite {
- namespace {
- // Returns a string representing the URL stripped of query and ref.
- std::string ClearUrlQueryRef(const GURL& url) {
- GURL::Replacements replacements;
- replacements.ClearQuery();
- replacements.ClearRef();
- return url.ReplaceComponents(replacements).spec();
- }
- void ApplySubstituteQueryPattern(
- network::ResourceRequest* request,
- const mojom::UrlRequestRewriteSubstituteQueryPatternPtr&
- substitute_query_pattern) {
- std::string url_query = request->url.query();
- base::ReplaceSubstringsAfterOffset(&url_query, 0,
- substitute_query_pattern->pattern,
- substitute_query_pattern->substitution);
- GURL::Replacements replacements;
- replacements.SetQueryStr(url_query);
- request->url = request->url.ReplaceComponents(replacements);
- }
- void ApplyReplaceUrl(network::ResourceRequest* request,
- const mojom::UrlRequestRewriteReplaceUrlPtr& replace_url) {
- if (!base::EndsWith(ClearUrlQueryRef(request->url),
- replace_url->url_ends_with, base::CompareCase::SENSITIVE))
- return;
- GURL new_url = replace_url->new_url;
- if (new_url.SchemeIs(url::kDataScheme)) {
- request->url = new_url;
- return;
- }
- if (new_url.has_scheme() &&
- new_url.scheme().compare(request->url.scheme()) != 0) {
- // No cross-scheme redirect allowed.
- return;
- }
- GURL::Replacements replacements;
- std::string host = new_url.host();
- replacements.SetHostStr(host);
- std::string port = new_url.port();
- replacements.SetPortStr(port);
- std::string path = new_url.path();
- replacements.SetPathStr(path);
- request->url = request->url.ReplaceComponents(replacements);
- }
- void ApplyRemoveHeader(
- network::ResourceRequest* request,
- const mojom::UrlRequestRewriteRemoveHeaderPtr& remove_header) {
- absl::optional<std::string> query_pattern = remove_header->query_pattern;
- if (query_pattern &&
- request->url.query().find(query_pattern.value()) == std::string::npos) {
- // Per the FIDL API, the header should be removed if there is no query
- // pattern or if the pattern matches. Neither is true here.
- return;
- }
- request->headers.RemoveHeader(remove_header->header_name);
- request->cors_exempt_headers.RemoveHeader(remove_header->header_name);
- }
- void ApplyAppendToQuery(
- network::ResourceRequest* request,
- const mojom::UrlRequestRewriteAppendToQueryPtr& append_to_query) {
- std::string url_query;
- if (request->url.has_query() && !request->url.query().empty())
- url_query = request->url.query() + "&";
- url_query += append_to_query->query;
- GURL::Replacements replacements;
- replacements.SetQueryStr(url_query);
- request->url = request->url.ReplaceComponents(replacements);
- }
- bool HostMatches(const base::StringPiece& url_host,
- const base::StringPiece& rule_host) {
- const base::StringPiece kWildcard("*.");
- if (base::StartsWith(rule_host, kWildcard, base::CompareCase::SENSITIVE)) {
- if (base::EndsWith(url_host, rule_host.substr(1),
- base::CompareCase::SENSITIVE)) {
- return true;
- }
- // Check |url_host| is exactly |rule_host| without the wildcard. i.e. if
- // |rule_host| is "*.test.xyz", check |url_host| is exactly "test.xyz".
- return base::CompareCaseInsensitiveASCII(url_host, rule_host.substr(2)) ==
- 0;
- }
- return base::CompareCaseInsensitiveASCII(url_host, rule_host) == 0;
- }
- // Returns true if the host and scheme filters defined in |rule| match
- // |request|.
- bool RuleFiltersMatchRequest(network::ResourceRequest* request,
- const mojom::UrlRequestRulePtr& rule) {
- const GURL& url = request->url;
- if (rule->hosts_filter) {
- bool found = false;
- for (const base::StringPiece host : rule->hosts_filter.value()) {
- if ((found = HostMatches(url.host(), host)))
- break;
- }
- if (!found)
- return false;
- }
- if (rule->schemes_filter) {
- bool found = false;
- for (const auto& scheme : rule->schemes_filter.value()) {
- if (url.scheme().compare(scheme) == 0) {
- found = true;
- break;
- }
- }
- if (!found)
- return false;
- }
- return true;
- }
- // Returns true if |request| is either allowed or left unblocked by any rules.
- bool IsRequestAllowed(network::ResourceRequest* request,
- const mojom::UrlRequestRewriteRulesPtr& rules) {
- for (const auto& rule : rules->rules) {
- if (rule->actions.size() != 1)
- continue;
- if (rule->actions[0]->which() != mojom::UrlRequestAction::Tag::kPolicy)
- continue;
- if (!RuleFiltersMatchRequest(request, rule))
- continue;
- switch (rule->actions[0]->get_policy()) {
- case mojom::UrlRequestAccessPolicy::kAllow:
- return true;
- case mojom::UrlRequestAccessPolicy::kDeny:
- return false;
- }
- }
- return true;
- }
- } // namespace
- URLLoaderThrottle::URLLoaderThrottle(
- scoped_refptr<UrlRequestRewriteRules> rules,
- IsHeaderCorsExemptCallback is_header_cors_exempt_callback)
- : rules_(std::move(rules)),
- is_header_cors_exempt_callback_(
- std::move(is_header_cors_exempt_callback)) {
- DCHECK(rules_);
- DCHECK(is_header_cors_exempt_callback_);
- }
- URLLoaderThrottle::~URLLoaderThrottle() = default;
- void URLLoaderThrottle::DetachFromCurrentSequence() {}
- void URLLoaderThrottle::WillStartRequest(network::ResourceRequest* request,
- bool* defer) {
- if (!IsRequestAllowed(request, rules_->data)) {
- delegate_->CancelWithError(net::ERR_ABORTED,
- "Resource load blocked by embedder policy.");
- return;
- }
- for (const auto& rule : rules_->data->rules)
- ApplyRule(request, rule);
- *defer = false;
- }
- bool URLLoaderThrottle::makes_unsafe_redirect() {
- // WillStartRequest() does not make cross-scheme redirects.
- return false;
- }
- void URLLoaderThrottle::ApplyRule(network::ResourceRequest* request,
- const mojom::UrlRequestRulePtr& rule) {
- if (!RuleFiltersMatchRequest(request, rule))
- return;
- for (const auto& rewrite : rule->actions)
- ApplyRewrite(request, rewrite);
- }
- void URLLoaderThrottle::ApplyRewrite(
- network::ResourceRequest* request,
- const mojom::UrlRequestActionPtr& rewrite) {
- switch (rewrite->which()) {
- case mojom::UrlRequestAction::Tag::kAddHeaders:
- ApplyAddHeaders(request, rewrite->get_add_headers());
- return;
- case mojom::UrlRequestAction::Tag::kRemoveHeader:
- ApplyRemoveHeader(request, rewrite->get_remove_header());
- return;
- case mojom::UrlRequestAction::Tag::kSubstituteQueryPattern:
- ApplySubstituteQueryPattern(request,
- rewrite->get_substitute_query_pattern());
- return;
- case mojom::UrlRequestAction::Tag::kReplaceUrl:
- ApplyReplaceUrl(request, rewrite->get_replace_url());
- return;
- case mojom::UrlRequestAction::Tag::kAppendToQuery:
- ApplyAppendToQuery(request, rewrite->get_append_to_query());
- return;
- case mojom::UrlRequestAction::Tag::kPolicy:
- // "Policy" is interpreted elsewhere; it is a no-op for rewriting.
- return;
- }
- NOTREACHED(); // Invalid enum value.
- }
- void URLLoaderThrottle::ApplyAddHeaders(
- network::ResourceRequest* request,
- const mojom::UrlRequestRewriteAddHeadersPtr& add_headers) {
- // Bucket each |header| into the regular/CORS-compliant header list or the
- // CORS-exempt header list.
- for (const auto& header : add_headers->headers) {
- if (request->headers.HasHeader(header->name) ||
- request->cors_exempt_headers.HasHeader(header->name)) {
- // Skip headers already present in the request at this point.
- continue;
- }
- if (is_header_cors_exempt_callback_.Run(header->name)) {
- request->cors_exempt_headers.SetHeader(header->name, header->value);
- } else {
- request->headers.SetHeader(header->name, header->value);
- }
- }
- }
- } // namespace url_rewrite
|