Skip to content

Commit

Permalink
bug: allow scheme-relative url in links
Browse files Browse the repository at this point in the history
  • Loading branch information
SethFalco committed Apr 6, 2022
1 parent 5a62701 commit 6ecc2c8
Show file tree
Hide file tree
Showing 3 changed files with 44 additions and 36 deletions.
16 changes: 10 additions & 6 deletions packages/next/shared/lib/router/router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -214,10 +214,14 @@ export function delBasePath(path: string): string {
*/
export function isLocalURL(url: string): boolean {
// prevent a hydration mismatch on href for url with anchor refs
if (url.startsWith('/') || url.startsWith('#') || url.startsWith('?'))
if (
url.match(/^\/(?!\/)/) !== null ||
url.startsWith('#') ||
url.startsWith('?')
)
return true
try {
// absolute urls can be local if they are on the same origin
// absolute and scheme-relative urls can be local if they are on the same origin
const locationOrigin = getLocationOrigin()
const resolved = new URL(url, locationOrigin)
return resolved.origin === locationOrigin && hasBasePath(resolved.pathname)
Expand Down Expand Up @@ -318,9 +322,9 @@ export function resolveHref(

// repeated slashes and backslashes in the URL are considered
// invalid and will never match a Next.js page/file
const urlProtoMatch = urlAsString.match(/^[a-zA-Z]{1,}:\/\//)
const urlAsStringNoProto = urlProtoMatch
? urlAsString.slice(urlProtoMatch[0].length)
const urlSchemeMatch = urlAsString.match(/^(?:[a-zA-Z]+:)?\/\//)
const urlAsStringNoProto = urlSchemeMatch
? urlAsString.slice(urlSchemeMatch[0].length)
: urlAsString

const urlParts = urlAsStringNoProto.split('?')
Expand All @@ -330,7 +334,7 @@ export function resolveHref(
`Invalid href passed to next/router: ${urlAsString}, repeated forward-slashes (//) or backslashes \\ are not valid in the href`
)
const normalizedUrl = normalizeRepeatedSlashes(urlAsStringNoProto)
urlAsString = (urlProtoMatch ? urlProtoMatch[0] : '') + normalizedUrl
urlAsString = (urlSchemeMatch ? urlSchemeMatch[0] : '') + normalizedUrl
}

// Return because it cannot be routed by the Next.js router
Expand Down
36 changes: 20 additions & 16 deletions test/integration/repeated-slashes/app/pages/invalid.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,43 +14,47 @@ export default function Invalid() {
return (
<>
<p id="invalid">invalid page</p>
<Link href="/another" as="//google.com">
<a id="page-with-as-slashes">to /another as //google.com</a>
<Link href="/another" as="/google.com//maps">
<a id="page-with-as-slashes">to /another as /google.com//maps</a>
</Link>
<br />

<Link href="//google.com">
<a id="href-with-slashes">to //google.com</a>
<Link href="/google.com//maps">
<a id="href-with-slashes">to /google.com//maps</a>
</Link>
<br />

<Link href="//google.com?hello=1">
<a id="href-with-slashes-query">to //google.com?hello=1</a>
<Link href="/google.com//maps?hello=1">
<a id="href-with-slashes-query">to /google.com//maps?hello=1</a>
</Link>
<br />

<Link href="//google.com#hello">
<a id="href-with-slashes-hash">to //google.com#hello</a>
<Link href="/google.com//maps#hello">
<a id="href-with-slashes-hash">to /google.com//maps#hello</a>
</Link>
<br />

<Link href="/another" as="\/\/google.com">
<a id="page-with-as-backslashes">to /another as \\/\\/google.com</a>
<Link href="/another" as="\/google.com\/\/maps">
<a id="page-with-as-backslashes">
to /another as \\/google.com\\/\\/maps
</a>
</Link>
<br />

<Link href="\/\/google.com">
<a id="href-with-backslashes">to \\/\\/google.com</a>
<Link href="\/google.com\/\/maps">
<a id="href-with-backslashes">to \\/google.com\\/\\/maps</a>
</Link>
<br />

<Link href="\/\/google.com?hello=1">
<a id="href-with-backslashes-query">to \\/\\/google.com?hello=1</a>
<Link href="\/google.com\/\/maps?hello=1">
<a id="href-with-backslashes-query">
to \\/google.com\\/\\/maps?hello=1
</a>
</Link>
<br />

<Link href="\/\/google.com#hello">
<a id="href-with-backslashes-hash">to \\/\\/google.com#hello</a>
<Link href="\/google.com\/\/maps#hello">
<a id="href-with-backslashes-hash">to \\/google.com\\/\\/maps#hello</a>
</Link>
<br />
</>
Expand Down
28 changes: 14 additions & 14 deletions test/integration/repeated-slashes/test/index.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -289,12 +289,12 @@ function runTests({ isDev = false, isExport = false, isPages404 = false }) {
`/invalid${isExport ? '.html' : ''}`
)
const invalidHrefs = [
'//google.com',
'//google.com?hello=1',
'//google.com#hello',
'\\/\\/google.com',
'\\/\\/google.com?hello=1',
'\\/\\/google.com#hello',
'/google.com//maps',
'/google.com//maps?hello=1',
'/google.com//maps#hello',
'\\/google.com\\/\\/maps',
'\\/google.com\\/\\/maps?hello=1',
'\\/google.com\\/\\/maps#hello',
]

for (const href of invalidHrefs) {
Expand All @@ -313,24 +313,24 @@ function runTests({ isDev = false, isExport = false, isPages404 = false }) {
{
page: '/another',
href: '/another',
as: '//google.com',
pathname: '/google.com',
as: '/google.com//maps',
pathname: '/google.com/maps',
},
{
page: isPages404 ? '/404' : '/_error',
href: '//google.com',
pathname: '/google.com',
href: '/google.com//maps',
pathname: '/google.com/maps',
},
{
page: isPages404 ? '/404' : '/_error',
href: '//google.com?hello=1',
pathname: '/google.com',
href: '/google.com//maps?hello=1',
pathname: '/google.com/maps',
search: '?hello=1',
},
{
page: isPages404 ? '/404' : '/_error',
href: '//google.com#hello',
pathname: '/google.com',
href: '/google.com//maps#hello',
pathname: '/google.com/maps',
hash: '#hello',
},
]) {
Expand Down

0 comments on commit 6ecc2c8

Please sign in to comment.