blob: 9291ef270fff781b2aba331c2da317007d91ffbf [file] [log] [blame]
// Copyright 2021 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 "services/network/public/cpp/link_header_parser.h"
#include "base/memory/scoped_refptr.h"
#include "net/http/http_response_headers.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "url/gurl.h"
namespace network {
namespace {
const GURL kBaseUrl = GURL("https://example.com/foo?bar=baz");
} // namespace
TEST(LinkHeaderParserTest, NoLinkHeader) {
auto headers =
base::MakeRefCounted<net::HttpResponseHeaders>("HTTP/2 200 OK\n");
std::vector<mojom::LinkHeaderPtr> parsed_headers =
ParseLinkHeaders(*headers, kBaseUrl);
ASSERT_EQ(parsed_headers.size(), 0UL);
}
TEST(LinkHeaderParserTest, InvalidValue) {
auto headers =
base::MakeRefCounted<net::HttpResponseHeaders>("HTTP/2 200 OK\n");
// The value is invalid because it misses a semicolon after `rel=preload`.
headers->AddHeader("link", "</script.js>; rel=preload as=script");
std::vector<mojom::LinkHeaderPtr> parsed_headers =
ParseLinkHeaders(*headers, kBaseUrl);
ASSERT_EQ(parsed_headers.size(), 0UL);
}
TEST(LinkHeaderParserTest, UndefinedAttribute) {
auto headers =
base::MakeRefCounted<net::HttpResponseHeaders>("HTTP/2 200 OK\n");
// `unknownattr` is not pre-defined.
headers->AddHeader("link",
"</style.css>; rel=preload; as=stylesheet; unknownattr");
std::vector<mojom::LinkHeaderPtr> parsed_headers =
ParseLinkHeaders(*headers, kBaseUrl);
ASSERT_EQ(parsed_headers.size(), 0UL);
}
TEST(LinkHeaderParserTest, UndefinedAttributeValue) {
auto headers =
base::MakeRefCounted<net::HttpResponseHeaders>("HTTP/2 200 OK\n");
headers->AddHeader("link", "</foo>; rel=preload; as=unknown-as");
std::vector<mojom::LinkHeaderPtr> parsed_headers =
ParseLinkHeaders(*headers, kBaseUrl);
ASSERT_EQ(parsed_headers.size(), 0UL);
}
TEST(LinkHeaderParserTest, UnknownMimeType) {
auto headers =
base::MakeRefCounted<net::HttpResponseHeaders>("HTTP/2 200 OK\n");
headers->AddHeader("link", "</foo>; rel=preload; type=unknown-type");
std::vector<mojom::LinkHeaderPtr> parsed_headers =
ParseLinkHeaders(*headers, kBaseUrl);
ASSERT_EQ(parsed_headers.size(), 0UL);
}
TEST(LinkHeaderParserTest, NoRelAttribute) {
auto headers =
base::MakeRefCounted<net::HttpResponseHeaders>("HTTP/2 200 OK\n");
// `rel` must be present.
headers->AddHeader("link", "</foo>");
std::vector<mojom::LinkHeaderPtr> parsed_headers =
ParseLinkHeaders(*headers, kBaseUrl);
ASSERT_EQ(parsed_headers.size(), 0UL);
}
TEST(LinkHeaderParserTest, AttributesAppearTwice) {
auto headers =
base::MakeRefCounted<net::HttpResponseHeaders>("HTTP/2 200 OK\n");
headers->AddHeader("link", "</foo>; rel=preload; rel=prefetch");
std::vector<mojom::LinkHeaderPtr> parsed_headers =
ParseLinkHeaders(*headers, kBaseUrl);
ASSERT_EQ(parsed_headers.size(), 1UL);
// The parser should use the first one.
EXPECT_EQ(parsed_headers[0]->rel, mojom::LinkRelAttribute::kPreload);
// TODO(crbug.com/1182567): Add tests for other attributes if the behavior is
// reasonable.
}
TEST(LinkHeaderParserTest, LinkAsAttribute) {
auto headers =
base::MakeRefCounted<net::HttpResponseHeaders>("HTTP/2 200 OK\n");
headers->AddHeader("link", "</foo>; rel=preload");
headers->AddHeader("link", "</font.woff2>; rel=preload; as=font");
headers->AddHeader("link", "</image.jpg>; rel=preload; as=image");
headers->AddHeader("link", "</script.js>; rel=preload; as=script");
headers->AddHeader("link", "</style.css>; rel=preload; as=stylesheet");
std::vector<mojom::LinkHeaderPtr> parsed_headers =
ParseLinkHeaders(*headers, kBaseUrl);
ASSERT_EQ(parsed_headers.size(), 5UL);
EXPECT_EQ(parsed_headers[0]->as, mojom::LinkAsAttribute::kUnspecified);
EXPECT_EQ(parsed_headers[1]->as, mojom::LinkAsAttribute::kFont);
EXPECT_EQ(parsed_headers[2]->as, mojom::LinkAsAttribute::kImage);
EXPECT_EQ(parsed_headers[3]->as, mojom::LinkAsAttribute::kScript);
EXPECT_EQ(parsed_headers[4]->as, mojom::LinkAsAttribute::kStyleSheet);
}
TEST(LinkHeaderParserTest, CrossOriginAttribute) {
auto headers =
base::MakeRefCounted<net::HttpResponseHeaders>("HTTP/2 200 OK\n");
headers->AddHeader("link", "<https://cross.example.com/>; rel=preload");
headers->AddHeader("link",
"<https://cross.example.com/>; rel=preload; crossorigin");
headers->AddHeader(
"link",
"<https://cross.example.com/>; rel=preload; crossorigin=anonymous");
headers->AddHeader(
"link",
"<https://cross.example.com/>; rel=preload; crossorigin=use-credentials");
std::vector<mojom::LinkHeaderPtr> parsed_headers =
ParseLinkHeaders(*headers, kBaseUrl);
ASSERT_EQ(parsed_headers.size(), 4UL);
EXPECT_EQ(parsed_headers[0]->cross_origin,
mojom::CrossOriginAttribute::kUnspecified);
EXPECT_EQ(parsed_headers[1]->cross_origin,
mojom::CrossOriginAttribute::kAnonymous);
EXPECT_EQ(parsed_headers[2]->cross_origin,
mojom::CrossOriginAttribute::kAnonymous);
EXPECT_EQ(parsed_headers[3]->cross_origin,
mojom::CrossOriginAttribute::kUseCredentials);
}
TEST(LinkHeaderParserTest, TwoHeaders) {
auto headers =
base::MakeRefCounted<net::HttpResponseHeaders>("HTTP/2 200 OK\n");
headers->AddHeader("link", "</image.jpg>; rel=preload; as=image");
headers->AddHeader("link",
"<https://cross.example.com/font.woff2>; rel=preload; "
"as=font; crossorigin; type=font/woff2");
std::vector<mojom::LinkHeaderPtr> parsed_headers =
ParseLinkHeaders(*headers, kBaseUrl);
ASSERT_EQ(parsed_headers.size(), 2UL);
EXPECT_EQ(parsed_headers[0]->href, kBaseUrl.Resolve("/image.jpg"));
EXPECT_EQ(parsed_headers[0]->rel, mojom::LinkRelAttribute::kPreload);
EXPECT_EQ(parsed_headers[0]->as, mojom::LinkAsAttribute::kImage);
EXPECT_EQ(parsed_headers[0]->cross_origin,
mojom::CrossOriginAttribute::kUnspecified);
EXPECT_FALSE(parsed_headers[0]->mime_type.has_value());
EXPECT_EQ(parsed_headers[1]->href,
GURL("https://cross.example.com/font.woff2"));
EXPECT_EQ(parsed_headers[1]->rel, mojom::LinkRelAttribute::kPreload);
EXPECT_EQ(parsed_headers[1]->as, mojom::LinkAsAttribute::kFont);
EXPECT_EQ(parsed_headers[1]->cross_origin,
mojom::CrossOriginAttribute::kAnonymous);
EXPECT_EQ(parsed_headers[1]->mime_type, "font/woff2");
}
TEST(LinkHeaderParserTest, UpperCaseCharacters) {
auto headers =
base::MakeRefCounted<net::HttpResponseHeaders>("HTTP/2 200 OK\n");
headers->AddHeader("link", "</image.jpg>; REL=preload; as=IMAGE");
headers->AddHeader("link",
"<https://cross.example.com/font.woff2>; rel=PRELOAD; "
"AS=font; CROSSORIGIN=USE-CREDENTIALS; TYPE=font/woff2");
std::vector<mojom::LinkHeaderPtr> parsed_headers =
ParseLinkHeaders(*headers, kBaseUrl);
ASSERT_EQ(parsed_headers.size(), 2UL);
EXPECT_EQ(parsed_headers[0]->href, kBaseUrl.Resolve("/image.jpg"));
EXPECT_EQ(parsed_headers[0]->rel, mojom::LinkRelAttribute::kPreload);
EXPECT_EQ(parsed_headers[0]->as, mojom::LinkAsAttribute::kImage);
EXPECT_EQ(parsed_headers[1]->href,
GURL("https://cross.example.com/font.woff2"));
EXPECT_EQ(parsed_headers[1]->rel, mojom::LinkRelAttribute::kPreload);
EXPECT_EQ(parsed_headers[1]->as, mojom::LinkAsAttribute::kFont);
EXPECT_EQ(parsed_headers[1]->cross_origin,
mojom::CrossOriginAttribute::kUseCredentials);
EXPECT_EQ(parsed_headers[1]->mime_type, "font/woff2");
}
} // namespace network