Add ServiceWorkerRouterEvaluator.
To evaluate the ServiceWorkerRouterRules, an evaluator has been implemented. Since it is used from both the browser process and the renderer processes for routing decision, it has been implemented in content/common. Bug: 1371756 Change-Id: If201d406a69ec635ba4f1ee2d6a0d04e5f4244e6 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4570363 Reviewed-by: Minoru Chikamune <chikamune@chromium.org> Reviewed-by: Rakina Zata Amni <rakina@chromium.org> Reviewed-by: Shunya Shishido <sisidovski@chromium.org> Commit-Queue: Yoshisato Yanagisawa <yyanagisawa@chromium.org> Reviewed-by: Jeremy Roman <jbroman@chromium.org> Cr-Commit-Position: refs/heads/main@{#1152299}
This commit is contained in:

committed by
Chromium LUCI CQ

parent
c319fec4c2
commit
84c89e84a4
@ -150,6 +150,8 @@ source_set("common") {
|
||||
"service_worker/race_network_request_url_loader_client.h",
|
||||
"service_worker/service_worker_resource_loader.cc",
|
||||
"service_worker/service_worker_resource_loader.h",
|
||||
"service_worker/service_worker_router_evaluator.cc",
|
||||
"service_worker/service_worker_router_evaluator.h",
|
||||
"set_process_title.cc",
|
||||
"set_process_title.h",
|
||||
"skia_utils.cc",
|
||||
|
3
content/common/service_worker/DEPS
Normal file
3
content/common/service_worker/DEPS
Normal file
@ -0,0 +1,3 @@
|
||||
include_rules = {
|
||||
"+third_party/liburlpattern",
|
||||
}
|
@ -0,0 +1,76 @@
|
||||
// Copyright 2023 The Chromium Authors
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "content/common/service_worker/service_worker_router_evaluator.h"
|
||||
|
||||
#include "third_party/liburlpattern/options.h"
|
||||
#include "third_party/liburlpattern/pattern.h"
|
||||
#include "third_party/re2/src/re2/re2.h"
|
||||
|
||||
namespace {
|
||||
|
||||
std::string ConvertToRegex(const blink::UrlPattern& url_pattern) {
|
||||
liburlpattern::Options options = {.delimiter_list = "/",
|
||||
.prefix_list = "/",
|
||||
.sensitive = true,
|
||||
.strict = false};
|
||||
liburlpattern::Pattern pattern(url_pattern.pathname, options, "[^/]+?");
|
||||
VLOG(3) << "regex string:" << pattern.GenerateRegexString();
|
||||
return pattern.GenerateRegexString();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace content {
|
||||
|
||||
ServiceWorkerRouterEvaluator::RouterRule::RouterRule()
|
||||
: url_patterns(RE2::Set(RE2::Options(), RE2::Anchor::UNANCHORED)) {}
|
||||
|
||||
ServiceWorkerRouterEvaluator::RouterRule::~RouterRule() = default;
|
||||
|
||||
ServiceWorkerRouterEvaluator::ServiceWorkerRouterEvaluator(
|
||||
blink::ServiceWorkerRouterRules rules)
|
||||
: rules_(std::move(rules)) {
|
||||
Compile();
|
||||
}
|
||||
ServiceWorkerRouterEvaluator::~ServiceWorkerRouterEvaluator() = default;
|
||||
|
||||
void ServiceWorkerRouterEvaluator::Compile() {
|
||||
for (const auto& r : rules_.rules) {
|
||||
std::unique_ptr<RouterRule> rule = absl::make_unique<RouterRule>();
|
||||
for (const auto& condition : r.conditions) {
|
||||
CHECK_EQ(condition.type,
|
||||
blink::ServiceWorkerRouterCondition::ConditionType::kUrlPattern);
|
||||
if (rule->url_patterns.Add(ConvertToRegex(*condition.url_pattern),
|
||||
nullptr) == -1) {
|
||||
// Failed to parse the regex.
|
||||
return;
|
||||
}
|
||||
}
|
||||
rule->url_pattern_length = r.conditions.size();
|
||||
if (!rule->url_patterns.Compile()) {
|
||||
// Failed to compile the regex.
|
||||
return;
|
||||
}
|
||||
rule->sources = r.sources;
|
||||
compiled_rules_.emplace_back(std::move(rule));
|
||||
}
|
||||
is_valid_ = true;
|
||||
}
|
||||
|
||||
std::vector<blink::ServiceWorkerRouterSource>
|
||||
ServiceWorkerRouterEvaluator::Evaluate(
|
||||
const network::ResourceRequest& request) const {
|
||||
for (const auto& rule : compiled_rules_) {
|
||||
std::vector<int> vec;
|
||||
if (rule->url_patterns.Match(request.url.path(), &vec) &&
|
||||
// ensure it matches all included patterns.
|
||||
vec.size() == rule->url_pattern_length) {
|
||||
return rule->sources;
|
||||
}
|
||||
}
|
||||
return std::vector<blink::ServiceWorkerRouterSource>();
|
||||
}
|
||||
|
||||
} // namespace content
|
@ -0,0 +1,48 @@
|
||||
// Copyright 2023 The Chromium Authors
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef CONTENT_COMMON_SERVICE_WORKER_SERVICE_WORKER_ROUTER_EVALUATOR_H_
|
||||
#define CONTENT_COMMON_SERVICE_WORKER_SERVICE_WORKER_ROUTER_EVALUATOR_H_
|
||||
|
||||
#include <utility>
|
||||
|
||||
#include "content/common/content_export.h"
|
||||
#include "services/network/public/cpp/resource_request.h"
|
||||
#include "third_party/blink/public/common/service_worker/service_worker_router_rule.h"
|
||||
#include "third_party/re2/src/re2/set.h"
|
||||
|
||||
namespace content {
|
||||
|
||||
class CONTENT_EXPORT ServiceWorkerRouterEvaluator {
|
||||
public:
|
||||
struct RouterRule {
|
||||
RouterRule();
|
||||
~RouterRule();
|
||||
|
||||
RE2::Set url_patterns;
|
||||
size_t url_pattern_length = 0;
|
||||
std::vector<blink::ServiceWorkerRouterSource> sources;
|
||||
};
|
||||
explicit ServiceWorkerRouterEvaluator(blink::ServiceWorkerRouterRules rules);
|
||||
~ServiceWorkerRouterEvaluator();
|
||||
|
||||
bool IsValid() { return is_valid_; }
|
||||
|
||||
// Returns an empty list if nothing matched.
|
||||
std::vector<blink::ServiceWorkerRouterSource> Evaluate(
|
||||
const network::ResourceRequest& request) const;
|
||||
|
||||
const blink::ServiceWorkerRouterRules& rules() const { return rules_; }
|
||||
|
||||
private:
|
||||
void Compile();
|
||||
|
||||
const blink::ServiceWorkerRouterRules rules_;
|
||||
std::vector<std::unique_ptr<RouterRule>> compiled_rules_;
|
||||
bool is_valid_ = false;
|
||||
};
|
||||
|
||||
} // namespace content
|
||||
|
||||
#endif // CONTENT_COMMON_SERVICE_WORKER_SERVICE_WORKER_ROUTER_EVALUATOR_H_
|
@ -0,0 +1,309 @@
|
||||
// Copyright 2023 The Chromium Authors
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "content/common/service_worker/service_worker_router_evaluator.h"
|
||||
|
||||
#include "base/strings/string_piece.h"
|
||||
#include "testing/gtest/include/gtest/gtest.h"
|
||||
#include "third_party/liburlpattern/parse.h"
|
||||
#include "third_party/liburlpattern/pattern.h"
|
||||
|
||||
namespace content {
|
||||
|
||||
namespace {
|
||||
|
||||
TEST(ServiceWorkerRouterEvaluator, EmptyRule) {
|
||||
blink::ServiceWorkerRouterRules rules;
|
||||
ServiceWorkerRouterEvaluator evaluator(rules);
|
||||
EXPECT_EQ(0U, evaluator.rules().rules.size());
|
||||
|
||||
ASSERT_TRUE(evaluator.IsValid());
|
||||
network::ResourceRequest request;
|
||||
request.method = "GET";
|
||||
request.url = GURL("https://example.com/");
|
||||
const auto sources = evaluator.Evaluate(request);
|
||||
EXPECT_TRUE(sources.empty());
|
||||
}
|
||||
|
||||
TEST(ServiceWorkerRouterEvaluator, SimpleMatch) {
|
||||
blink::ServiceWorkerRouterRules rules;
|
||||
{
|
||||
blink::ServiceWorkerRouterRule rule;
|
||||
{
|
||||
blink::ServiceWorkerRouterCondition condition;
|
||||
condition.type =
|
||||
blink::ServiceWorkerRouterCondition::ConditionType::kUrlPattern;
|
||||
blink::UrlPattern url_pattern;
|
||||
auto parse_result = liburlpattern::Parse(
|
||||
"/test/*",
|
||||
[](base::StringPiece input) { return std::string(input); });
|
||||
ASSERT_TRUE(parse_result.ok());
|
||||
url_pattern.pathname = parse_result.value().PartList();
|
||||
condition.url_pattern = url_pattern;
|
||||
rule.conditions.push_back(condition);
|
||||
}
|
||||
{
|
||||
blink::ServiceWorkerRouterSource source;
|
||||
source.type = blink::ServiceWorkerRouterSource::SourceType::kNetwork;
|
||||
source.network_source = blink::ServiceWorkerRouterNetworkSource{};
|
||||
rule.sources.push_back(source);
|
||||
}
|
||||
rules.rules.push_back(rule);
|
||||
}
|
||||
ASSERT_EQ(1U, rules.rules.size());
|
||||
|
||||
ServiceWorkerRouterEvaluator evaluator(rules);
|
||||
ASSERT_EQ(1U, evaluator.rules().rules.size());
|
||||
EXPECT_TRUE(evaluator.IsValid());
|
||||
|
||||
network::ResourceRequest request;
|
||||
request.method = "GET";
|
||||
request.url = GURL("https://example.com/test/page.html");
|
||||
const auto sources = evaluator.Evaluate(request);
|
||||
EXPECT_EQ(1U, sources.size());
|
||||
}
|
||||
|
||||
TEST(ServiceWorkerRouterEvaluator, SimpleExactMatch) {
|
||||
blink::ServiceWorkerRouterRules rules;
|
||||
{
|
||||
blink::ServiceWorkerRouterRule rule;
|
||||
{
|
||||
blink::ServiceWorkerRouterCondition condition;
|
||||
condition.type =
|
||||
blink::ServiceWorkerRouterCondition::ConditionType::kUrlPattern;
|
||||
blink::UrlPattern url_pattern;
|
||||
auto parse_result = liburlpattern::Parse(
|
||||
"/test/page.html",
|
||||
[](base::StringPiece input) { return std::string(input); });
|
||||
ASSERT_TRUE(parse_result.ok());
|
||||
url_pattern.pathname = parse_result.value().PartList();
|
||||
condition.url_pattern = url_pattern;
|
||||
rule.conditions.push_back(condition);
|
||||
}
|
||||
{
|
||||
blink::ServiceWorkerRouterSource source;
|
||||
source.type = blink::ServiceWorkerRouterSource::SourceType::kNetwork;
|
||||
source.network_source = blink::ServiceWorkerRouterNetworkSource{};
|
||||
rule.sources.push_back(source);
|
||||
}
|
||||
rules.rules.push_back(rule);
|
||||
}
|
||||
ASSERT_EQ(1U, rules.rules.size());
|
||||
|
||||
ServiceWorkerRouterEvaluator evaluator(rules);
|
||||
ASSERT_EQ(1U, evaluator.rules().rules.size());
|
||||
EXPECT_TRUE(evaluator.IsValid());
|
||||
|
||||
network::ResourceRequest request;
|
||||
request.method = "GET";
|
||||
request.url = GURL("https://example.com/test/page.html");
|
||||
const auto sources = evaluator.Evaluate(request);
|
||||
EXPECT_EQ(1U, sources.size());
|
||||
}
|
||||
|
||||
TEST(ServiceWorkerRouterEvaluator, NotMatchingCondition) {
|
||||
blink::ServiceWorkerRouterRules rules;
|
||||
{
|
||||
blink::ServiceWorkerRouterRule rule;
|
||||
{
|
||||
blink::ServiceWorkerRouterCondition condition;
|
||||
condition.type =
|
||||
blink::ServiceWorkerRouterCondition::ConditionType::kUrlPattern;
|
||||
blink::UrlPattern url_pattern;
|
||||
auto parse_result = liburlpattern::Parse(
|
||||
"/test/*",
|
||||
[](base::StringPiece input) { return std::string(input); });
|
||||
ASSERT_TRUE(parse_result.ok());
|
||||
url_pattern.pathname = parse_result.value().PartList();
|
||||
condition.url_pattern = url_pattern;
|
||||
rule.conditions.push_back(condition);
|
||||
}
|
||||
{
|
||||
blink::ServiceWorkerRouterSource source;
|
||||
source.type = blink::ServiceWorkerRouterSource::SourceType::kNetwork;
|
||||
source.network_source = blink::ServiceWorkerRouterNetworkSource{};
|
||||
rule.sources.push_back(source);
|
||||
}
|
||||
rules.rules.push_back(rule);
|
||||
}
|
||||
ASSERT_EQ(1U, rules.rules.size());
|
||||
|
||||
ServiceWorkerRouterEvaluator evaluator(rules);
|
||||
ASSERT_EQ(1U, evaluator.rules().rules.size());
|
||||
EXPECT_TRUE(evaluator.IsValid());
|
||||
|
||||
network::ResourceRequest request;
|
||||
request.method = "GET";
|
||||
request.url = GURL("https://example.com/notmatched/page.html");
|
||||
const auto sources = evaluator.Evaluate(request);
|
||||
EXPECT_EQ(0U, sources.size());
|
||||
}
|
||||
|
||||
TEST(ServiceWorkerRouterEvaluator, OneConditionMisMatch) {
|
||||
blink::ServiceWorkerRouterRules rules;
|
||||
{
|
||||
blink::ServiceWorkerRouterRule rule;
|
||||
{
|
||||
blink::ServiceWorkerRouterCondition condition;
|
||||
condition.type =
|
||||
blink::ServiceWorkerRouterCondition::ConditionType::kUrlPattern;
|
||||
blink::UrlPattern url_pattern;
|
||||
auto parse_result = liburlpattern::Parse(
|
||||
"/test/*",
|
||||
[](base::StringPiece input) { return std::string(input); });
|
||||
ASSERT_TRUE(parse_result.ok());
|
||||
url_pattern.pathname = parse_result.value().PartList();
|
||||
condition.url_pattern = url_pattern;
|
||||
rule.conditions.push_back(condition);
|
||||
}
|
||||
{
|
||||
blink::ServiceWorkerRouterCondition condition;
|
||||
condition.type =
|
||||
blink::ServiceWorkerRouterCondition::ConditionType::kUrlPattern;
|
||||
blink::UrlPattern url_pattern;
|
||||
auto parse_result = liburlpattern::Parse(
|
||||
"/notmatch/*",
|
||||
[](base::StringPiece input) { return std::string(input); });
|
||||
ASSERT_TRUE(parse_result.ok());
|
||||
url_pattern.pathname = parse_result.value().PartList();
|
||||
condition.url_pattern = url_pattern;
|
||||
rule.conditions.push_back(condition);
|
||||
}
|
||||
{
|
||||
blink::ServiceWorkerRouterSource source;
|
||||
source.type = blink::ServiceWorkerRouterSource::SourceType::kNetwork;
|
||||
source.network_source = blink::ServiceWorkerRouterNetworkSource{};
|
||||
rule.sources.push_back(source);
|
||||
}
|
||||
rules.rules.push_back(rule);
|
||||
}
|
||||
ASSERT_EQ(1U, rules.rules.size());
|
||||
|
||||
ServiceWorkerRouterEvaluator evaluator(rules);
|
||||
ASSERT_EQ(1U, evaluator.rules().rules.size());
|
||||
EXPECT_TRUE(evaluator.IsValid());
|
||||
|
||||
network::ResourceRequest request;
|
||||
request.method = "GET";
|
||||
request.url = GURL("https://example.com/test/page.html");
|
||||
const auto sources = evaluator.Evaluate(request);
|
||||
EXPECT_EQ(0U, sources.size());
|
||||
}
|
||||
|
||||
TEST(ServiceWorkerRouterEvaluator, AllConditionMatch) {
|
||||
blink::ServiceWorkerRouterRules rules;
|
||||
{
|
||||
blink::ServiceWorkerRouterRule rule;
|
||||
{
|
||||
blink::ServiceWorkerRouterCondition condition;
|
||||
condition.type =
|
||||
blink::ServiceWorkerRouterCondition::ConditionType::kUrlPattern;
|
||||
blink::UrlPattern url_pattern;
|
||||
auto parse_result = liburlpattern::Parse(
|
||||
"/test/*",
|
||||
[](base::StringPiece input) { return std::string(input); });
|
||||
ASSERT_TRUE(parse_result.ok());
|
||||
url_pattern.pathname = parse_result.value().PartList();
|
||||
condition.url_pattern = url_pattern;
|
||||
rule.conditions.push_back(condition);
|
||||
}
|
||||
{
|
||||
blink::ServiceWorkerRouterCondition condition;
|
||||
condition.type =
|
||||
blink::ServiceWorkerRouterCondition::ConditionType::kUrlPattern;
|
||||
blink::UrlPattern url_pattern;
|
||||
auto parse_result = liburlpattern::Parse(
|
||||
"*.html", [](base::StringPiece input) { return std::string(input); });
|
||||
ASSERT_TRUE(parse_result.ok());
|
||||
url_pattern.pathname = parse_result.value().PartList();
|
||||
condition.url_pattern = url_pattern;
|
||||
rule.conditions.push_back(condition);
|
||||
}
|
||||
{
|
||||
blink::ServiceWorkerRouterSource source;
|
||||
source.type = blink::ServiceWorkerRouterSource::SourceType::kNetwork;
|
||||
source.network_source = blink::ServiceWorkerRouterNetworkSource{};
|
||||
rule.sources.push_back(source);
|
||||
}
|
||||
rules.rules.push_back(rule);
|
||||
}
|
||||
ASSERT_EQ(1U, rules.rules.size());
|
||||
|
||||
ServiceWorkerRouterEvaluator evaluator(rules);
|
||||
ASSERT_EQ(1U, evaluator.rules().rules.size());
|
||||
EXPECT_TRUE(evaluator.IsValid());
|
||||
|
||||
network::ResourceRequest request;
|
||||
request.method = "GET";
|
||||
request.url = GURL("https://example.com/test/page.html");
|
||||
const auto sources = evaluator.Evaluate(request);
|
||||
EXPECT_EQ(1U, sources.size());
|
||||
}
|
||||
|
||||
TEST(ServiceWorkerRouterEvaluator, ChooseMatchedRoute) {
|
||||
blink::ServiceWorkerRouterRules rules;
|
||||
{
|
||||
blink::ServiceWorkerRouterRule rule;
|
||||
{
|
||||
blink::ServiceWorkerRouterCondition condition;
|
||||
condition.type =
|
||||
blink::ServiceWorkerRouterCondition::ConditionType::kUrlPattern;
|
||||
blink::UrlPattern url_pattern;
|
||||
auto parse_result = liburlpattern::Parse(
|
||||
"*.html", [](base::StringPiece input) { return std::string(input); });
|
||||
ASSERT_TRUE(parse_result.ok());
|
||||
url_pattern.pathname = parse_result.value().PartList();
|
||||
condition.url_pattern = url_pattern;
|
||||
rule.conditions.push_back(condition);
|
||||
}
|
||||
{
|
||||
blink::ServiceWorkerRouterSource source;
|
||||
source.type = blink::ServiceWorkerRouterSource::SourceType::kNetwork;
|
||||
source.network_source = blink::ServiceWorkerRouterNetworkSource{};
|
||||
rule.sources.push_back(source);
|
||||
rule.sources.push_back(source);
|
||||
}
|
||||
rules.rules.push_back(rule);
|
||||
}
|
||||
{
|
||||
blink::ServiceWorkerRouterRule rule;
|
||||
{
|
||||
blink::ServiceWorkerRouterCondition condition;
|
||||
condition.type =
|
||||
blink::ServiceWorkerRouterCondition::ConditionType::kUrlPattern;
|
||||
blink::UrlPattern url_pattern;
|
||||
auto parse_result = liburlpattern::Parse(
|
||||
"*.css", [](base::StringPiece input) { return std::string(input); });
|
||||
ASSERT_TRUE(parse_result.ok());
|
||||
url_pattern.pathname = parse_result.value().PartList();
|
||||
condition.url_pattern = url_pattern;
|
||||
rule.conditions.push_back(condition);
|
||||
}
|
||||
{
|
||||
blink::ServiceWorkerRouterSource source;
|
||||
source.type = blink::ServiceWorkerRouterSource::SourceType::kNetwork;
|
||||
source.network_source = blink::ServiceWorkerRouterNetworkSource{};
|
||||
rule.sources.push_back(source);
|
||||
rule.sources.push_back(source);
|
||||
rule.sources.push_back(source);
|
||||
rule.sources.push_back(source);
|
||||
}
|
||||
rules.rules.push_back(rule);
|
||||
}
|
||||
ASSERT_EQ(2U, rules.rules.size());
|
||||
|
||||
ServiceWorkerRouterEvaluator evaluator(rules);
|
||||
ASSERT_EQ(2U, evaluator.rules().rules.size());
|
||||
EXPECT_TRUE(evaluator.IsValid());
|
||||
|
||||
network::ResourceRequest request;
|
||||
request.method = "GET";
|
||||
request.url = GURL("https://example.com/top/test.css");
|
||||
const auto sources = evaluator.Evaluate(request);
|
||||
EXPECT_EQ(4U, sources.size());
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
} // namespace content
|
@ -2690,6 +2690,7 @@ test("content_unittests") {
|
||||
"../common/input/gesture_event_stream_validator_unittest.cc",
|
||||
"../common/input/touch_event_stream_validator_unittest.cc",
|
||||
"../common/pseudonymization_salt_unittest.cc",
|
||||
"../common/service_worker/service_worker_router_evaluator_unittest.cc",
|
||||
"../common/url_utils_unittest.cc",
|
||||
"../common/user_agent_unittest.cc",
|
||||
"../common/webid/identity_url_loader_throttle_unittest.cc",
|
||||
|
Reference in New Issue
Block a user