Skip to content

Commit

Permalink
Fix router not working on some protocol
Browse files Browse the repository at this point in the history
- Added support for protocols other than http and https
- Added test case

Fixes vercel#16456, vercel#16595
  • Loading branch information
Cow258 committed Aug 30, 2020
1 parent ce99436 commit 02beafe
Show file tree
Hide file tree
Showing 2 changed files with 122 additions and 14 deletions.
29 changes: 15 additions & 14 deletions packages/next/next-server/lib/router/utils/parse-relative-url.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,18 @@
import { getLocationOrigin } from '../../utils'

const DUMMY_BASE = new URL(
let DUMMY_BASE = new URL(
typeof window === 'undefined' ? 'http://n' : getLocationOrigin()
)

/**
* Refresh DUMMY_BASE for unit test
*/
export function refreshDummyBase() {
DUMMY_BASE = new URL(
typeof window === 'undefined' ? 'http://n' : getLocationOrigin()
)
}

/**
* Parses path-relative urls (e.g. `/hello/world?foo=bar`). If url isn't path-relative
* (e.g. `./hello`) then at least base must be.
Expand All @@ -12,19 +21,11 @@ const DUMMY_BASE = new URL(
*/
export function parseRelativeUrl(url: string, base?: string) {
const resolvedBase = base ? new URL(base, DUMMY_BASE) : DUMMY_BASE
const {
pathname,
searchParams,
search,
hash,
href,
origin,
protocol,
} = new URL(url, resolvedBase)
if (
origin !== DUMMY_BASE.origin ||
(protocol !== 'http:' && protocol !== 'https:')
) {
const { pathname, searchParams, search, hash, href, origin } = new URL(
url,
resolvedBase
)
if (origin !== DUMMY_BASE.origin) {
throw new Error('invariant: invalid relative URL')
}
return {
Expand Down
107 changes: 107 additions & 0 deletions test/unit/parse-relative-url.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
/* eslint-env jest */
import {
parseRelativeUrl,
refreshDummyBase,
} from 'next/dist/next-server/lib/router/utils/parse-relative-url'

// convenience function so tests can be aligned neatly
// and easy to eyeball
const check = (windowUrl, targetUrl, expected) => {
window.location = new URL(windowUrl)
refreshDummyBase()
if (typeof expected === 'string') {
expect(() => parseRelativeUrl(targetUrl)).toThrow(expected)
} else {
const parsedUrl = parseRelativeUrl(targetUrl)
expect(parsedUrl.pathname).toBe(expected.pathname)
expect(parsedUrl.search).toBe(expected.search)
expect(parsedUrl.hash).toBe(expected.hash)
}
}

describe('parseRelativeUrl', () => {
beforeAll(() => {
global.window = {
location: {},
}
})

afterAll(() => {
delete global.window
})

it('should parse relative url', () => {
check(
'http://example.com:3210/someA/pathB?fooC=barD#hashE',
'/someF/pathG?fooH=barI#hashJ',
{
pathname: '/someF/pathG',
search: '?fooH=barI',
hash: '#hashJ',
}
)
})

it('should parse relative url when start with dot', () => {
check(
'http://example.com:3210/someA/pathB?fooC=barD#hashE',
'./someF/pathG?fooH=barI#hashJ',
{
pathname: '/someF/pathG',
search: '?fooH=barI',
hash: '#hashJ',
}
)
})

it('should parse relative url on special protocol', () => {
check(
'ionic://localhost/someA/pathB?fooC=barD#hashE',
'/someF/pathG?fooH=barI#hashJ',
{
pathname: '/someF/pathG',
search: '?fooH=barI',
hash: '#hashJ',
}
)
check(
'file:///someA/pathB?fooC=barD#hashE',
'/someF/pathG?fooH=barI#hashJ',
{
pathname: '/someF/pathG',
search: '?fooH=barI',
hash: '#hashJ',
}
)
})

it('should throw when parsing the full url', () => {
check(
'http://example.com:3210/someA/pathB?fooC=barD#hashE',
'http://example.com:3210/someF/pathG?fooH=barI#hashJ',
{
pathname: '/someF/pathG',
search: '?fooH=barI',
hash: '#hashJ',
}
)
})

it('should throw when parsing the special prefix', () => {
check(
'http://example.com:3210/someA/pathB?fooC=barD#hashE',
'mailto:foo@example.com',
'invariant: invalid relative URL'
)
check(
'http://example.com:3210/someA/pathB?fooC=barD#hashE',
'tel:+123456789',
'invariant: invalid relative URL'
)
check(
'http://example.com:3210/someA/pathB?fooC=barD#hashE',
'sms:+123456789',
'invariant: invalid relative URL'
)
})
})

0 comments on commit 02beafe

Please sign in to comment.