Skip to content

Commit

Permalink
🐛 [RUM-2940] fix normalize URL for relative paths
Browse files Browse the repository at this point in the history
  • Loading branch information
BenoitZugmeyer committed Jan 22, 2024
1 parent 3bef13a commit 302df4c
Show file tree
Hide file tree
Showing 2 changed files with 34 additions and 66 deletions.
69 changes: 33 additions & 36 deletions packages/core/src/tools/utils/urlPolyfill.spec.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,18 @@
import { isFirefox } from '../../../test'
import { isIE } from './browserDetection'
import { getHash, getOrigin, getPathName, getSearch, isValidUrl, normalizeUrl, getLocationOrigin } from './urlPolyfill'
import { buildUrl, getPathName, isValidUrl, normalizeUrl } from './urlPolyfill'

// On firefox, URL host is empty for file URI: 'https://bugzilla.mozilla.org/show_bug.cgi?id=1578787'
const emptyURLHost = isFirefox()

describe('normalize url', () => {
it('should add origin to relative path', () => {
expect(normalizeUrl('/my/path')).toEqual(`${getLocationOrigin()}/my/path`)
it('should resolve absolute paths', () => {
expect(normalizeUrl('/my/path')).toEqual(`${location.origin}/my/path`)
})

it('should resolve relative paths', () => {
history.pushState({}, '', '/foo/')
expect(normalizeUrl('./my/path')).toEqual(`${location.origin}/foo/my/path`)
})

it('should add protocol to relative url', () => {
Expand All @@ -20,20 +28,15 @@ describe('normalize url', () => {
})

it('should keep file url unchanged', () => {
if (isFirefox()) {
// On firefox, URL host is empty for file URI: 'https://bugzilla.mozilla.org/show_bug.cgi?id=1578787'
expect(normalizeUrl('file://foo.com/my/path')).toEqual('file:///my/path')
} else {
expect(normalizeUrl('file://foo.com/my/path')).toEqual('file://foo.com/my/path')
}
expect(normalizeUrl('file://foo.com/my/path')).toEqual(emptyURLHost ? 'file:///my/path' : 'file://foo.com/my/path')
})
})

describe('isValidUrl', () => {
it('should ensure url is valid', () => {
expect(isValidUrl('http://www.datadoghq.com')).toBe(true)
expect(isValidUrl('http://www.datadoghq.com/foo/bar?a=b#hello')).toBe(true)
expect(isValidUrl('file://www.datadoghq.com')).toBe(true)
expect(isValidUrl('file:///www.datadoghq.com')).toBe(true)
expect(isValidUrl('/plop')).toBe(false)
expect(isValidUrl('')).toBe(false)
})
Expand All @@ -45,40 +48,34 @@ describe('isValidUrl', () => {
})
})

describe('getOrigin', () => {
it('should retrieve url origin', () => {
expect(getOrigin('http://www.datadoghq.com')).toBe('http://www.datadoghq.com')
expect(getOrigin('http://www.datadoghq.com/foo/bar?a=b#hello')).toBe('http://www.datadoghq.com')
expect(getOrigin('http://localhost:8080')).toBe('http://localhost:8080')
})

it('should retrieve file url origin', () => {
if (isIE()) {
// On IE, our origin fallback strategy contains the host
expect(getOrigin('file://foo.com/my/path')).toEqual('file://foo.com')
} else {
expect(getOrigin('file://foo.com/my/path')).toEqual('file://')
}
})
})

describe('getPathName', () => {
it('should retrieve url path name', () => {
expect(getPathName('http://www.datadoghq.com')).toBe('/')
expect(getPathName('http://www.datadoghq.com/foo/bar?a=b#hello')).toBe('/foo/bar')
expect(getPathName('file://foo/bar?a=b#hello')).toBe('/bar')
})
})

describe('getSearch', () => {
it('should retrieve url search', () => {
expect(getSearch('http://www.datadoghq.com')).toBe('')
expect(getSearch('http://www.datadoghq.com/foo/bar?a=b#hello')).toBe('?a=b')
describe('buildUrl', () => {
it('should normalize href for absolute URLs', () => {
expect(buildUrl('http://foo.com').href).toBe('http://foo.com/')
expect(buildUrl('http://foo.com:8080').href).toBe('http://foo.com:8080/')
expect(buildUrl('http://foo.com:80').href).toBe('http://foo.com/')
expect(buildUrl('https://foo.com:443').href).toBe('https://foo.com/')
expect(buildUrl('file://foo.com/my/path').href).toBe(emptyURLHost ? 'file:///my/path' : 'file://foo.com/my/path')
})
})

describe('getHash', () => {
it('should retrieve url hash', () => {
expect(getHash('http://www.datadoghq.com')).toBe('')
expect(getHash('http://www.datadoghq.com/foo/bar?a=b#hello')).toBe('#hello')
it('should normalize href for relative URLs', () => {
expect(buildUrl('./bar', 'http://foo.com').href).toBe('http://foo.com/bar')
expect(buildUrl('/bar', 'http://foo.com').href).toBe('http://foo.com/bar')

expect(buildUrl('./bar', 'http://foo.com/foo').href).toBe('http://foo.com/bar')
expect(buildUrl('/bar', 'http://foo.com/foo').href).toBe('http://foo.com/bar')

expect(buildUrl('./bar', 'http://foo.com/foo/').href).toBe('http://foo.com/foo/bar')
expect(buildUrl('/bar', 'http://foo.com/foo/').href).toBe('http://foo.com/bar')

expect(buildUrl('./bar', 'file://foo/faa').href).toBe(emptyURLHost ? 'file:///bar' : 'file://foo/bar')
expect(buildUrl('/bar', 'file://foo/faa').href).toBe(emptyURLHost || isIE() ? 'file:///bar' : 'file://foo/bar')
})
})
31 changes: 1 addition & 30 deletions packages/core/src/tools/utils/urlPolyfill.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { jsonStringify } from '../serialisation/jsonStringify'

export function normalizeUrl(url: string) {
return buildUrl(url, getLocationOrigin()).href
return buildUrl(url, location.href).href
}

export function isValidUrl(url: string) {
Expand All @@ -12,23 +12,11 @@ export function isValidUrl(url: string) {
}
}

export function getOrigin(url: string) {
return getLinkElementOrigin(buildUrl(url))
}

export function getPathName(url: string) {
const pathname = buildUrl(url).pathname
return pathname[0] === '/' ? pathname : `/${pathname}`
}

export function getSearch(url: string) {
return buildUrl(url).search
}

export function getHash(url: string) {
return buildUrl(url).hash
}

export function buildUrl(url: string, base?: string) {
const supportedURL = getSupportedUrl()
if (supportedURL) {
Expand Down Expand Up @@ -67,20 +55,3 @@ function getSupportedUrl(): typeof URL | undefined {
}
return isURLSupported ? originalURL : undefined
}

export function getLocationOrigin() {
return getLinkElementOrigin(window.location)
}

/**
* Fallback
* On IE HTMLAnchorElement origin is not supported: https://developer.mozilla.org/en-US/docs/Web/API/HTMLHyperlinkElementUtils/origin
* On Firefox window.location.origin is "null" for file: URIs: https://bugzilla.mozilla.org/show_bug.cgi?id=878297
*/
export function getLinkElementOrigin(element: Location | HTMLAnchorElement | URL) {
if (element.origin && element.origin !== 'null') {
return element.origin
}
const sanitizedHost = element.host.replace(/(:80|:443)$/, '')
return `${element.protocol}//${sanitizedHost}`
}

0 comments on commit 302df4c

Please sign in to comment.