blob: 14ff2d7ec734d3e8bbeab7684e71586808ec6e2b [file] [log] [blame]
// 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 "fuchsia/engine/common/web_engine_url_loader_throttle.h"
#include "base/strings/string_util.h"
namespace {
// Returnns 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 ApplyAddHeaders(network::ResourceRequest* request,
const mojom::UrlRequestRewriteAddHeadersPtr& add_headers) {
request->headers.MergeFrom(add_headers->headers);
}
void ApplyRemoveHeader(
network::ResourceRequest* request,
const mojom::UrlRequestRewriteRemoveHeaderPtr& remove_header) {
base::Optional<std::string> query_pattern = remove_header->query_pattern;
if (query_pattern &&
request->url.query().find(query_pattern.value()) == std::string::npos) {
return;
}
request->headers.RemoveHeader(remove_header->header_name);
}
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.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 ApplyRewrite(network::ResourceRequest* request,
const mojom::UrlRequestRewritePtr& rewrite) {
switch (rewrite->which()) {
case mojom::UrlRequestRewrite::Tag::ADD_HEADERS:
ApplyAddHeaders(request, rewrite->get_add_headers());
break;
case mojom::UrlRequestRewrite::Tag::REMOVE_HEADER:
ApplyRemoveHeader(request, rewrite->get_remove_header());
break;
case mojom::UrlRequestRewrite::Tag::SUBSTITUTE_QUERY_PATTERN:
ApplySubstituteQueryPattern(request,
rewrite->get_substitute_query_pattern());
break;
case mojom::UrlRequestRewrite::Tag::REPLACE_URL:
ApplyReplaceUrl(request, rewrite->get_replace_url());
break;
}
}
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)) {
// |rule_host| starts with a wildcard (e.g. "*.test.xyz"). Check if
// |url_host| ends with ".test.xyz". The "." character is included here to
// prevent accidentally matching "notatest.xyz".
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;
}
void ApplyRule(network::ResourceRequest* request,
const mojom::UrlRequestRewriteRulePtr& 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;
}
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;
}
for (const auto& rewrite : rule->rewrites) {
ApplyRewrite(request, rewrite);
}
}
void ApplyRules(network::ResourceRequest* request,
const std::vector<mojom::UrlRequestRewriteRulePtr>& rules) {
for (const auto& rule : rules) {
ApplyRule(request, rule);
}
}
} // namespace
WebEngineURLLoaderThrottle::WebEngineURLLoaderThrottle(
CachedRulesProvider* cached_rules_provider)
: cached_rules_provider_(cached_rules_provider) {
DCHECK(cached_rules_provider);
}
WebEngineURLLoaderThrottle::~WebEngineURLLoaderThrottle() = default;
void WebEngineURLLoaderThrottle::DetachFromCurrentSequence() {}
void WebEngineURLLoaderThrottle::WillStartRequest(
network::ResourceRequest* request,
bool* defer) {
scoped_refptr<WebEngineURLLoaderThrottle::UrlRequestRewriteRules>
cached_rules = cached_rules_provider_->GetCachedRules();
// |cached_rules| may be empty if no rule was ever sent to WebEngine.
if (cached_rules)
ApplyRules(request, cached_rules->data);
*defer = false;
}
bool WebEngineURLLoaderThrottle::makes_unsafe_redirect() {
// WillStartRequest() does not make cross-scheme redirects.
return false;
}