Commit c8187c73 authored by Kunihiko Sakamoto's avatar Kunihiko Sakamoto Committed by Commit Bot

Add SignedExchangeRequestMatcher::FindBestMatchingIndex

This will be used for variant matching of web bundles.

Bug: 1029406
Change-Id: I65d1851cd6b9b6d446cc35981d3e9472203ab857
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1948003Reviewed-by: default avatarTsuyoshi Horo <horo@chromium.org>
Reviewed-by: default avatarKinuko Yasuda <kinuko@chromium.org>
Commit-Queue: Kunihiko Sakamoto <ksakamoto@chromium.org>
Cr-Commit-Position: refs/heads/master@{#720868}
parent b3228771
......@@ -410,6 +410,11 @@ SignedExchangeRequestMatcher::FindBestMatchingVariantKey(
variant_key_list);
}
base::Optional<size_t> SignedExchangeRequestMatcher::FindBestMatchingIndex(
const std::string& variants) const {
return FindBestMatchingIndex(request_headers_, variants);
}
// Implements "Cache Behaviour" [1] when "stored-responses" is a singleton list
// containing a response that has "Variants" header whose value is |variants|.
// [1] https://httpwg.org/http-extensions/draft-ietf-httpbis-variants.html#cache
......@@ -608,4 +613,46 @@ SignedExchangeRequestMatcher::FindBestMatchingVariantKey(
return found_variant_key;
}
// static
base::Optional<size_t> SignedExchangeRequestMatcher::FindBestMatchingIndex(
const net::HttpRequestHeaders& request_headers,
const std::string& variants) {
auto parsed_variants = ParseVariants(variants);
if (!parsed_variants)
return base::nullopt;
size_t best_match_index = 0;
for (const auto& variant_axis : *parsed_variants) {
const std::string field_name = base::ToLowerASCII(variant_axis.first);
std::unique_ptr<ContentNegotiationAlgorithm> negotiation_algorithm =
GetContentNegotiationAlgorithm(field_name);
if (!negotiation_algorithm)
return base::nullopt;
base::Optional<std::string> request_value;
std::string header_value;
if (request_headers.GetHeader(field_name, &header_value))
request_value = header_value;
std::vector<std::string> sorted_values =
negotiation_algorithm->run(variant_axis.second, request_value);
if (sorted_values.empty())
return base::nullopt;
auto it = std::find(variant_axis.second.begin(), variant_axis.second.end(),
sorted_values.front());
if (it == variant_axis.second.end())
return base::nullopt;
size_t best_value_index = it - variant_axis.second.begin();
if (!base::CheckMul(best_match_index, variant_axis.second.size())
.AssignIfValid(&best_match_index)) {
return base::nullopt;
}
if (!base::CheckAdd(best_match_index, best_value_index)
.AssignIfValid(&best_match_index)) {
return base::nullopt;
}
}
return best_match_index;
}
} // namespace blink
......@@ -308,4 +308,49 @@ TEST(SignedExchangeRequestMatcherTest, FindBestMatchingVariantKey) {
}
}
}
TEST(SignedExchangeRequestMatcherTest, FindBestMatchingIndex) {
const struct TestCase {
const char* name;
std::string variants;
std::map<std::string, std::string> req_headers;
base::Optional<size_t> expected_result;
} cases[] = {
{"matching value",
"Accept;image/png;image/jpg",
{{"accept", "image/webp,image/jpg"}},
1 /* image/jpg */},
{"default value",
"Accept;image/xx;image/yy",
{{"accept", "image/webp,image/jpg"}},
0 /* image/xx */},
{"content type and language",
"Accept;image/png;image/jpg, Accept-Language;en;fr;ja",
{{"accept", "image/jpg"}, {"accept-language", "fr"}},
4 /* image/jpg, fr */},
{"language and content type",
"Accept-Language;en;fr;ja, Accept;image/png;image/jpg",
{{"accept", "image/jpg"}, {"accept-language", "fr"}},
3 /* fr, image/jpg */},
{"ill-formed variants",
"Accept",
{{"accept", "image/webp,image/jpg"}},
base::nullopt},
{"unknown field name",
"Unknown;foo;bar",
{{"Unknown", "foo"}},
base::nullopt},
};
for (const auto& c : cases) {
net::HttpRequestHeaders request_headers;
for (auto it = c.req_headers.begin(); it != c.req_headers.end(); ++it)
request_headers.SetHeader(it->first, it->second);
base::Optional<size_t> result =
SignedExchangeRequestMatcher::FindBestMatchingIndex(request_headers,
c.variants);
EXPECT_EQ(c.expected_result, result) << c.name;
}
}
} // namespace blink
......@@ -10,6 +10,7 @@
#include <vector>
#include "base/gtest_prod_util.h"
#include "base/optional.h"
#include "net/http/http_request_headers.h"
#include "third_party/blink/public/common/common_export.h"
#include "third_party/blink/public/common/http/structured_header.h"
......@@ -41,6 +42,11 @@ class BLINK_COMMON_EXPORT SignedExchangeRequestMatcher {
const std::string& variants,
const std::vector<std::string>& variant_keys_list) const;
// Returns the index of best matching variant key within the all possible
// key for |variants|, in lexicographic (row-major) order.
base::Optional<size_t> FindBestMatchingIndex(
const std::string& variants) const;
private:
net::HttpRequestHeaders request_headers_;
......@@ -56,10 +62,16 @@ class BLINK_COMMON_EXPORT SignedExchangeRequestMatcher {
const std::string& variants,
const std::vector<std::string>& variant_key_list);
static base::Optional<size_t> FindBestMatchingIndex(
const net::HttpRequestHeaders& request_headers,
const std::string& variants);
FRIEND_TEST_ALL_PREFIXES(SignedExchangeRequestMatcherTest, MatchRequest);
FRIEND_TEST_ALL_PREFIXES(SignedExchangeRequestMatcherTest, CacheBehavior);
FRIEND_TEST_ALL_PREFIXES(SignedExchangeRequestMatcherTest,
FindBestMatchingVariantKey);
FRIEND_TEST_ALL_PREFIXES(SignedExchangeRequestMatcherTest,
FindBestMatchingIndex);
};
} // namespace blink
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment