Skip to content

Commit

Permalink
feat: Add support for proxying font URLs (#16796)
Browse files Browse the repository at this point in the history
  • Loading branch information
benjackwhite authored Sep 18, 2023
1 parent 70062eb commit 8449fa5
Show file tree
Hide file tree
Showing 6 changed files with 93 additions and 4 deletions.
1 change: 1 addition & 0 deletions frontend/src/lib/constants.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,7 @@ export const FEATURE_FLAGS = {
SURVEY_NPS_RESULTS: 'survey-nps-results', // owner: @liyiy
// owner: #team-monitoring
SESSION_RECORDING_ALLOW_V1_SNAPSHOTS: 'session-recording-allow-v1-snapshots',
SESSION_REPLAY_CORS_PROXY: 'session-replay-cors-proxy', // owner: #team-monitoring
HOGQL_INSIGHTS: 'hogql-insights', // owner: @mariusandra
WEBHOOKS_DENYLIST: 'webhooks-denylist', // owner: #team-pipeline
} as const
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`CorsPlugin should replace font urls in links 1`] = `"https://replay.ph-proxy.com/proxy?url=https://app.posthog.com/fonts/my-font.woff2?t=1234"`;

exports[`CorsPlugin should replace font urls in links 2`] = `"https://replay.ph-proxy.com/proxy?url=https://app.posthog.com/fonts/my-font.ttf"`;

exports[`CorsPlugin should replace font urls in stylesheets 1`] = `"@font-face { font-display: fallback; font-family: "Roboto Condensed"; font-weight: 400; font-style: normal; src: url("https://replay.ph-proxy.com/proxy?url=https://posthog.com/assets/fonts/roboto/roboto_condensed_reg-webfont.woff2?11012022") format("woff2"), url("https://replay.ph-proxy.com/proxy?url=https://posthog.com/assets/fonts/roboto/roboto_condensed_reg-webfont.woff?11012022")"`;
exports[`CorsPlugin should replace font urls in stylesheets 2`] = `"url("https://replay.ph-proxy.com/proxy?url=https://app.posthog.com/fonts/my-font.woff2")"`;
24 changes: 24 additions & 0 deletions frontend/src/scenes/session-recordings/player/rrweb/index.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { CorsPlugin } from '.'

describe('CorsPlugin', () => {
it.each([
`@font-face { font-display: fallback; font-family: "Roboto Condensed"; font-weight: 400; font-style: normal; src: url("https://posthog.com/assets/fonts/roboto/roboto_condensed_reg-webfont.woff2?11012022") format("woff2"), url("https://posthog.com/assets/fonts/roboto/roboto_condensed_reg-webfont.woff?11012022")`,
`url("https://app.posthog.com/fonts/my-font.woff2")`,
])('should replace font urls in stylesheets', (content: string) => {
expect(CorsPlugin._replaceFontCssUrls(content)).toMatchSnapshot()
})

it.each(['https://app.posthog.com/fonts/my-font.woff2?t=1234', 'https://app.posthog.com/fonts/my-font.ttf'])(
'should replace font urls in links',
(content: string) => {
expect(CorsPlugin._replaceFontUrl(content)).toMatchSnapshot()
}
)

it.each(['https://app.posthog.com/my-image.jpeg'])(
'should not replace non-font urls in links',
(content: string) => {
expect(CorsPlugin._replaceFontUrl(content)).toEqual(content)
}
)
})
38 changes: 38 additions & 0 deletions frontend/src/scenes/session-recordings/player/rrweb/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { ReplayPlugin, playerConfig } from 'rrweb/typings/types'

const PROXY_URL = 'https://replay.ph-proxy.com' as const

export const CorsPlugin: ReplayPlugin & {
_replaceFontCssUrls: (value: string) => string
_replaceFontUrl: (value: string) => string
} = {
_replaceFontCssUrls: (value: string): string => {
return value.replace(
/url\("(https:\/\/\S*(?:.eot|.woff2|.ttf|.woff)\S*)"\)/gi,
`url("${PROXY_URL}/proxy?url=$1")`
)
},

_replaceFontUrl: (value: string): string => {
return value.replace(/^(https:\/\/\S*(?:.eot|.woff2|.ttf|.woff)\S*)$/i, `${PROXY_URL}/proxy?url=$1`)
},

onBuild: (node) => {
if (node.nodeName === 'STYLE') {
const styleElement = node as HTMLStyleElement
styleElement.innerText = CorsPlugin._replaceFontCssUrls(styleElement.innerText)
}

if (node.nodeName === 'LINK') {
const linkElement = node as HTMLLinkElement
linkElement.href = CorsPlugin._replaceFontUrl(linkElement.href)
}
},
}

export const COMMON_REPLAYER_CONFIG: Partial<playerConfig> = {
triggerFocus: false,
insertStyleRules: [
`.ph-no-capture { background-image: url("data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAxNiAxNiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHJlY3Qgd2lkdGg9IjE2IiBoZWlnaHQ9IjE2IiBmaWxsPSJibGFjayIvPgo8cGF0aCBkPSJNOCAwSDE2TDAgMTZWOEw4IDBaIiBmaWxsPSIjMkQyRDJEIi8+CjxwYXRoIGQ9Ik0xNiA4VjE2SDhMMTYgOFoiIGZpbGw9IiMyRDJEMkQiLz4KPC9zdmc+Cg=="); }`,
],
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,12 @@ import { SessionRecordingPlayerExplorerProps } from './view-explorer/SessionReco
import { createExportedSessionRecording } from '../file-playback/sessionRecordingFilePlaybackLogic'
import { RefObject } from 'react'
import posthog from 'posthog-js'
import { COMMON_REPLAYER_CONFIG, CorsPlugin } from './rrweb'
import { now } from 'lib/dayjs'
import { ReplayPlugin } from 'rrweb/typings/types'
import { preflightLogic } from 'scenes/PreflightCheck/preflightLogic'
import { featureFlagLogic } from 'lib/logic/featureFlagLogic'
import { FEATURE_FLAGS } from 'lib/constants'

export const PLAYBACK_SPEEDS = [0.5, 1, 2, 3, 4, 8, 16]
export const ONE_FRAME_MS = 100 // We don't really have frames but this feels granular enough
Expand Down Expand Up @@ -102,6 +107,10 @@ export const sessionRecordingPlayerLogic = kea<sessionRecordingPlayerLogicType>(
['speed', 'skipInactivitySetting'],
userLogic,
['hasAvailableFeature'],
preflightLogic,
['preflight'],
featureFlagLogic,
['featureFlags'],
],
actions: [
sessionRecordingDataLogic(props),
Expand Down Expand Up @@ -471,16 +480,24 @@ export const sessionRecordingPlayerLogic = kea<sessionRecordingPlayerLogicType>(
return
}

const plugins: ReplayPlugin[] = []

// We don't want non-cloud products to talk to our proxy as it likely won't work, but we _do_ want local testing to work
if (
values.featureFlags[FEATURE_FLAGS.SESSION_REPLAY_CORS_PROXY] &&
(values.preflight?.cloud || window.location.hostname === 'localhost')
) {
plugins.push(CorsPlugin)
}

const replayer = new Replayer(values.sessionPlayerData.snapshotsByWindowId[windowId], {
root: values.rootFrame,
triggerFocus: false,
insertStyleRules: [
`.ph-no-capture { background-image: url("data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAxNiAxNiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHJlY3Qgd2lkdGg9IjE2IiBoZWlnaHQ9IjE2IiBmaWxsPSJibGFjayIvPgo8cGF0aCBkPSJNOCAwSDE2TDAgMTZWOEw4IDBaIiBmaWxsPSIjMkQyRDJEIi8+CjxwYXRoIGQ9Ik0xNiA4VjE2SDhMMTYgOFoiIGZpbGw9IiMyRDJEMkQiLz4KPC9zdmc+Cg=="); }`,
],
...COMMON_REPLAYER_CONFIG,
// these two settings are attempts to improve performance of running two Replayers at once
// the main player and a preview player
mouseTail: props.mode !== SessionRecordingPlayerMode.Preview,
useVirtualDom: false,
plugins,
})

actions.setPlayer({ replayer, windowId })
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit 8449fa5

Please sign in to comment.