From fd7447e1983049c5580593bb4c6d1fd8eb610e04 Mon Sep 17 00:00:00 2001 From: Manuel Bucher Date: Wed, 8 Jun 2022 14:33:12 +0000 Subject: [PATCH] Bug 1761252 - Parse anchor attribute in Link-header r=necko-reviewers,dragana,kershaw Differential Revision: https://phabricator.services.mozilla.com/D142442 --- netwerk/base/nsNetUtil.cpp | 15 +++++ netwerk/base/nsNetUtil.h | 3 + netwerk/protocol/http/EarlyHintPreloader.cpp | 3 +- netwerk/test/browser/103_preload_anchor.html | 6 ++ .../browser/103_preload_anchor.html^headers^ | 1 + ...preload_anchor.html^informationalResponse^ | 2 + netwerk/test/browser/browser.ini | 3 + netwerk/test/browser/browser_103_preload.js | 41 +++---------- netwerk/test/gtest/TestLinkHeader.cpp | 61 +++++++++++++++++++ 9 files changed, 99 insertions(+), 36 deletions(-) create mode 100644 netwerk/test/browser/103_preload_anchor.html create mode 100644 netwerk/test/browser/103_preload_anchor.html^headers^ create mode 100644 netwerk/test/browser/103_preload_anchor.html^informationalResponse^ diff --git a/netwerk/base/nsNetUtil.cpp b/netwerk/base/nsNetUtil.cpp index c25ae1659f2d..447acc7b4ea2 100644 --- a/netwerk/base/nsNetUtil.cpp +++ b/netwerk/base/nsNetUtil.cpp @@ -3403,6 +3403,21 @@ void LinkHeader::Reset() { mCrossOrigin.SetIsVoid(true); } +nsresult LinkHeader::NewResolveHref(nsIURI** aOutURI, nsIURI* aBaseURI) const { + if (mAnchor.IsEmpty()) { + // use the base uri + return NS_NewURI(aOutURI, mHref, nullptr, aBaseURI); + } + + // compute the anchored URI + nsCOMPtr anchoredURI; + nsresult rv = + NS_NewURI(getter_AddRefs(anchoredURI), mAnchor, nullptr, aBaseURI); + NS_ENSURE_SUCCESS(rv, rv); + + return NS_NewURI(aOutURI, mHref, nullptr, anchoredURI); +} + bool LinkHeader::operator==(const LinkHeader& rhs) const { return mHref == rhs.mHref && mRel == rhs.mRel && mTitle == rhs.mTitle && mIntegrity == rhs.mIntegrity && mSrcset == rhs.mSrcset && diff --git a/netwerk/base/nsNetUtil.h b/netwerk/base/nsNetUtil.h index 0993401b7892..effd85c1d16c 100644 --- a/netwerk/base/nsNetUtil.h +++ b/netwerk/base/nsNetUtil.h @@ -1012,6 +1012,9 @@ struct LinkHeader { LinkHeader(); void Reset(); + + nsresult NewResolveHref(nsIURI** aOutURI, nsIURI* aBaseURI) const; + bool operator==(const LinkHeader& rhs) const; }; diff --git a/netwerk/protocol/http/EarlyHintPreloader.cpp b/netwerk/protocol/http/EarlyHintPreloader.cpp index 084883d22ba6..b58351c644bc 100644 --- a/netwerk/protocol/http/EarlyHintPreloader.cpp +++ b/netwerk/protocol/http/EarlyHintPreloader.cpp @@ -162,8 +162,7 @@ void EarlyHintPreloader::MaybeCreateAndInsertPreload( nsCOMPtr uri; // use the base uri - NS_ENSURE_SUCCESS_VOID( - NS_NewURI(getter_AddRefs(uri), aHeader.mHref, nullptr, aBaseURI)); + NS_ENSURE_SUCCESS_VOID(aHeader.NewResolveHref(getter_AddRefs(uri), aBaseURI)); // Only make same origin preloads, the fromPrivateWindow is only read when // reportError is enabled, so setting both to false is safe. diff --git a/netwerk/test/browser/103_preload_anchor.html b/netwerk/test/browser/103_preload_anchor.html new file mode 100644 index 000000000000..ebb14ac61183 --- /dev/null +++ b/netwerk/test/browser/103_preload_anchor.html @@ -0,0 +1,6 @@ + + + + + + diff --git a/netwerk/test/browser/103_preload_anchor.html^headers^ b/netwerk/test/browser/103_preload_anchor.html^headers^ new file mode 100644 index 000000000000..9e23c73b7ffb --- /dev/null +++ b/netwerk/test/browser/103_preload_anchor.html^headers^ @@ -0,0 +1 @@ +Cache-Control: no-cache diff --git a/netwerk/test/browser/103_preload_anchor.html^informationalResponse^ b/netwerk/test/browser/103_preload_anchor.html^informationalResponse^ new file mode 100644 index 000000000000..1099062e15d7 --- /dev/null +++ b/netwerk/test/browser/103_preload_anchor.html^informationalResponse^ @@ -0,0 +1,2 @@ +HTTP 103 Early Hints +Link: ; rel=preload; as=image; anchor="/browser/" diff --git a/netwerk/test/browser/browser.ini b/netwerk/test/browser/browser.ini index ca8c81f44c12..3ce2faf8affb 100644 --- a/netwerk/test/browser/browser.ini +++ b/netwerk/test/browser/browser.ini @@ -44,6 +44,9 @@ support-files = 103_preload.html^headers^ no_103_preload.html no_103_preload.html^headers^ + 103_preload_anchor.html^informationalResponse^ + 103_preload_anchor.html^headers^ + 103_preload_anchor.html 103_preload_and_404.html^informationalResponse^ 103_preload_and_404.html^headers^ 103_preload_and_404.html diff --git a/netwerk/test/browser/browser_103_preload.js b/netwerk/test/browser/browser_103_preload.js index c9c5601cc6c1..7e9d957761d4 100644 --- a/netwerk/test/browser/browser_103_preload.js +++ b/netwerk/test/browser/browser_103_preload.js @@ -333,10 +333,8 @@ add_task(async function test_103_iframe() { Services.cache2.clear(); }); -// - testName is just there to be printed during Asserts when failing -// - asset is the asset type, see early_hint_asset_html.sjs for possible values -// - hinted: when true, the server reponds with "103 Early Hints"-header -async function test_hint_asset(testName, asset, hinted) { +// Test that anchors are parsed +add_task(async function test_103_anchor() { // reset the count let headers = new Headers(); headers.append("X-Early-Hint-Count-Start", ""); @@ -345,14 +343,13 @@ async function test_hint_asset(testName, asset, hinted) { { headers } ); - let requestUrl = `http://example.com/browser/netwerk/test/browser/early_hint_asset_html.sjs?as=${asset}&hinted=${ - hinted ? "1" : "0" - }`; + let anchorUri = + "http://example.com/browser/netwerk/test/browser/103_preload_anchor.html"; await BrowserTestUtils.withNewTab( { gBrowser, - url: requestUrl, + url: anchorUri, waitForLoad: true, }, async function() {} @@ -364,31 +361,7 @@ async function test_hint_asset(testName, asset, hinted) { await Assert.deepEqual( gotRequestCount, - hinted ? { hinted: 1, normal: 0 } : { hinted: 0, normal: 1 }, - `${testName} (${asset}): Unexpected amount of requests made` + { hinted: 1, normal: 0 }, + "test_103_anchor: Unexpected amount of requests made" ); -} - -// preload css -add_task(async function test_103_asset_style() { - await test_hint_asset("test_103_asset_hinted", "style", true); - await test_hint_asset("test_103_asset_normal", "style", false); -}); - -// preload javascript -add_task(async function test_103_asset_javascript() { - await test_hint_asset("test_103_asset_hinted", "script", true); - await test_hint_asset("test_103_asset_normal", "script", false); -}); - -// preload fetch -add_task(async function test_103_asset_fetch() { - await test_hint_asset("test_103_asset_hinted", "fetch", true); - await test_hint_asset("test_103_asset_normal", "fetch", false); -}); - -// preload font -add_task(async function test_103_asset_font() { - await test_hint_asset("test_103_asset_hinted", "font", true); - await test_hint_asset("test_103_asset_normal", "font", false); }); diff --git a/netwerk/test/gtest/TestLinkHeader.cpp b/netwerk/test/gtest/TestLinkHeader.cpp index e74583d3d0c5..403091ed21d4 100644 --- a/netwerk/test/gtest/TestLinkHeader.cpp +++ b/netwerk/test/gtest/TestLinkHeader.cpp @@ -243,3 +243,64 @@ const SimpleParseTestData simple_parse_tests[] = { INSTANTIATE_TEST_SUITE_P(TestLinkHeader, SimpleParseTest, testing::ValuesIn(simple_parse_tests)); + +// Test anchor + +struct AnchorTestData { + nsString baseURI; + // building the new anchor in combination with the baseURI + nsString anchor; + nsString href; + const char* resolved; +}; + +class AnchorTest : public ::testing::TestWithParam {}; + +const AnchorTestData anchor_tests[] = { + {u"http://example.com/path/to/index.html"_ns, u""_ns, u"page.html"_ns, + "http://example.com/path/to/page.html"}, + {u"http://example.com/path/to/index.html"_ns, + u"http://example.com/path/"_ns, u"page.html"_ns, + "http://example.com/path/page.html"}, + {u"http://example.com/path/to/index.html"_ns, + u"http://example.com/path/"_ns, u"/page.html"_ns, + "http://example.com/page.html"}, + {u"http://example.com/path/to/index.html"_ns, u".."_ns, u"page.html"_ns, + "http://example.com/path/page.html"}, + {u"http://example.com/path/to/index.html"_ns, u".."_ns, + u"from/page.html"_ns, "http://example.com/path/from/page.html"}, + {u"http://example.com/path/to/index.html"_ns, u"/hello/"_ns, + u"page.html"_ns, "http://example.com/hello/page.html"}, + {u"http://example.com/path/to/index.html"_ns, u"/hello"_ns, u"page.html"_ns, + "http://example.com/page.html"}, + {u"http://example.com/path/to/index.html"_ns, u"#necko"_ns, u"page.html"_ns, + "http://example.com/path/to/page.html"}, + {u"http://example.com/path/to/index.html"_ns, u"https://example.net/"_ns, + u"to/page.html"_ns, "https://example.net/to/page.html"}, +}; + +LinkHeader LinkHeaderFromHrefAndAnchor(nsAString const& aHref, + nsAString const& aAnchor) { + LinkHeader l; + l.mHref = aHref; + l.mAnchor = aAnchor; + return l; +} + +TEST_P(AnchorTest, Anchor) { + const AnchorTestData test = GetParam(); + + LinkHeader linkHeader = LinkHeaderFromHrefAndAnchor(test.href, test.anchor); + + nsCOMPtr baseURI; + ASSERT_TRUE(NS_SUCCEEDED(NS_NewURI(getter_AddRefs(baseURI), test.baseURI))); + + nsCOMPtr resolved; + ASSERT_TRUE(NS_SUCCEEDED( + linkHeader.NewResolveHref(getter_AddRefs(resolved), baseURI))); + + ASSERT_STREQ(resolved->GetSpecOrDefault().get(), test.resolved); +} + +INSTANTIATE_TEST_SUITE_P(TestLinkHeader, AnchorTest, + testing::ValuesIn(anchor_tests));