fix(middleware): consider localhost variations (#31603)
Since `localhost` is actually an alias for `127.0.0.1` that points to loopback, we should take that into consideration at `NextURL` when we handle local URLs. The implementation is based on [is-localhost-url](https://github.com/Kikobeats/is-localhost-url); I added some tests over local URLs variations present at the library to ensure other variations are working fine. Additionally, I refactor some things over the code to avoid doing the same twice and added some legibility that is always welcome when you are working with URLs stuff. closes https://github.com/vercel/next.js/issues/31533
This commit is contained in:
parent
9652bdd653
commit
f52211bad3
2 changed files with 27 additions and 6 deletions
|
@ -20,6 +20,9 @@ interface Options {
|
||||||
trailingSlash?: boolean
|
trailingSlash?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const REGEX_LOCALHOST_HOSTNAME =
|
||||||
|
/(?!^https?:\/\/)(127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}|::1)/
|
||||||
|
|
||||||
export class NextURL extends URL {
|
export class NextURL extends URL {
|
||||||
private _basePath: string
|
private _basePath: string
|
||||||
private _locale?: {
|
private _locale?: {
|
||||||
|
@ -33,11 +36,12 @@ export class NextURL extends URL {
|
||||||
private _options: Options
|
private _options: Options
|
||||||
private _url: URL
|
private _url: URL
|
||||||
|
|
||||||
constructor(url: string, options: Options = {}) {
|
constructor(input: string, options: Options = {}) {
|
||||||
super(formatRelative(url))
|
const url = createWHATWGURL(input)
|
||||||
|
super(url)
|
||||||
this._options = options
|
this._options = options
|
||||||
this._basePath = ''
|
this._basePath = ''
|
||||||
this._url = formatRelative(url)
|
this._url = url
|
||||||
this.analyzeUrl()
|
this.analyzeUrl()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -163,7 +167,7 @@ export class NextURL extends URL {
|
||||||
}
|
}
|
||||||
|
|
||||||
set href(url: string) {
|
set href(url: string) {
|
||||||
this._url = formatRelative(url)
|
this._url = createWHATWGURL(url)
|
||||||
this.analyzeUrl()
|
this.analyzeUrl()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -228,8 +232,13 @@ export class NextURL extends URL {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function formatRelative(url: string) {
|
function createWHATWGURL(url: string) {
|
||||||
return url.startsWith('/')
|
url = url.replace(REGEX_LOCALHOST_HOSTNAME, 'localhost')
|
||||||
|
return isRelativeURL(url)
|
||||||
? new URL(url.replace(/^\/+/, '/'), new URL('https://localhost'))
|
? new URL(url.replace(/^\/+/, '/'), new URL('https://localhost'))
|
||||||
: new URL(url)
|
: new URL(url)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function isRelativeURL(url: string) {
|
||||||
|
return url.startsWith('/')
|
||||||
|
}
|
||||||
|
|
|
@ -119,3 +119,15 @@ it('parses and formats the default locale', () => {
|
||||||
expect(url.locale).toEqual('fr')
|
expect(url.locale).toEqual('fr')
|
||||||
expect(url.toString()).toEqual('/root/fr/bar')
|
expect(url.toString()).toEqual('/root/fr/bar')
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('consider 127.0.0.1 and variations as localhost', () => {
|
||||||
|
const httpUrl = new NextURL('http://localhost:3000/hello')
|
||||||
|
expect(new NextURL('http://127.0.0.1:3000/hello')).toStrictEqual(httpUrl)
|
||||||
|
expect(new NextURL('http://127.0.1.0:3000/hello')).toStrictEqual(httpUrl)
|
||||||
|
expect(new NextURL('http://::1:3000/hello')).toStrictEqual(httpUrl)
|
||||||
|
|
||||||
|
const httpsUrl = new NextURL('https://localhost:3000/hello')
|
||||||
|
expect(new NextURL('https://127.0.0.1:3000/hello')).toStrictEqual(httpsUrl)
|
||||||
|
expect(new NextURL('https://127.0.1.0:3000/hello')).toStrictEqual(httpsUrl)
|
||||||
|
expect(new NextURL('https://::1:3000/hello')).toStrictEqual(httpsUrl)
|
||||||
|
})
|
Loading…
Reference in a new issue