feat(tracker): track network requests in iframes
This commit is contained in:
parent
c1568b0929
commit
5ddb0bbad4
3 changed files with 60 additions and 13 deletions
|
|
@ -14,6 +14,8 @@ import type { Options as ObserverOptions } from './observer/top_observer.js'
|
||||||
import type { Options as SanitizerOptions } from './sanitizer.js'
|
import type { Options as SanitizerOptions } from './sanitizer.js'
|
||||||
import type { Options as LoggerOptions } from './logger.js'
|
import type { Options as LoggerOptions } from './logger.js'
|
||||||
import type { Options as SessOptions } from './session.js'
|
import type { Options as SessOptions } from './session.js'
|
||||||
|
import type { Options as NetworkOptions } from '../modules/network.js'
|
||||||
|
|
||||||
import type {
|
import type {
|
||||||
Options as WebworkerOptions,
|
Options as WebworkerOptions,
|
||||||
ToWorkerData,
|
ToWorkerData,
|
||||||
|
|
@ -75,6 +77,7 @@ type AppOptions = {
|
||||||
|
|
||||||
// @deprecated
|
// @deprecated
|
||||||
onStart?: StartCallback
|
onStart?: StartCallback
|
||||||
|
network?: NetworkOptions
|
||||||
} & WebworkerOptions &
|
} & WebworkerOptions &
|
||||||
SessOptions
|
SessOptions
|
||||||
|
|
||||||
|
|
@ -99,6 +102,7 @@ export default class App {
|
||||||
private readonly stopCallbacks: Array<() => any> = []
|
private readonly stopCallbacks: Array<() => any> = []
|
||||||
private readonly commitCallbacks: Array<CommitCallback> = []
|
private readonly commitCallbacks: Array<CommitCallback> = []
|
||||||
private readonly options: AppOptions
|
private readonly options: AppOptions
|
||||||
|
public readonly networkOptions?: NetworkOptions
|
||||||
private readonly revID: string
|
private readonly revID: string
|
||||||
private activityState: ActivityState = ActivityState.NotActive
|
private activityState: ActivityState = ActivityState.NotActive
|
||||||
private readonly version = 'TRACKER_VERSION' // TODO: version compatability check inside each plugin.
|
private readonly version = 'TRACKER_VERSION' // TODO: version compatability check inside each plugin.
|
||||||
|
|
@ -109,6 +113,7 @@ export default class App {
|
||||||
// } ?? maybe onStart is good
|
// } ?? maybe onStart is good
|
||||||
|
|
||||||
this.projectKey = projectKey
|
this.projectKey = projectKey
|
||||||
|
this.networkOptions = options.network
|
||||||
this.options = Object.assign(
|
this.options = Object.assign(
|
||||||
{
|
{
|
||||||
revID: '',
|
revID: '',
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,13 @@
|
||||||
import Observer from './observer.js'
|
import Observer from './observer.js'
|
||||||
import { CreateIFrameDocument } from '../messages.gen.js'
|
import { CreateIFrameDocument } from '../messages.gen.js'
|
||||||
|
import Network from '../../modules/network.js'
|
||||||
|
|
||||||
export default class IFrameObserver extends Observer {
|
export default class IFrameObserver extends Observer {
|
||||||
observe(iframe: HTMLIFrameElement) {
|
observe(iframe: HTMLIFrameElement) {
|
||||||
const doc = iframe.contentDocument
|
const doc = iframe.contentDocument
|
||||||
|
const iWindow = iframe.contentWindow
|
||||||
const hostID = this.app.nodes.getID(iframe)
|
const hostID = this.app.nodes.getID(iframe)
|
||||||
|
console.log(iframe)
|
||||||
if (!doc || hostID === undefined) {
|
if (!doc || hostID === undefined) {
|
||||||
return
|
return
|
||||||
} //log TODO common app.logger
|
} //log TODO common app.logger
|
||||||
|
|
@ -16,6 +19,7 @@ export default class IFrameObserver extends Observer {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
this.app.send(CreateIFrameDocument(hostID, docID))
|
this.app.send(CreateIFrameDocument(hostID, docID))
|
||||||
|
Network(this.app, this.app.networkOptions, iWindow!)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -92,7 +92,7 @@ export interface Options {
|
||||||
sanitizer?: Sanitizer
|
sanitizer?: Sanitizer
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function (app: App, opts: Partial<Options> = {}) {
|
export default function (app: App, opts: Partial<Options> = {}, customEnv?: Record<string, any>) {
|
||||||
const options: Options = Object.assign(
|
const options: Options = Object.assign(
|
||||||
{
|
{
|
||||||
failuresOnly: false,
|
failuresOnly: false,
|
||||||
|
|
@ -150,8 +150,11 @@ export default function (app: App, opts: Partial<Options> = {}) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ====== Fetch ====== */
|
/* ====== Fetch ====== */
|
||||||
const origFetch = window.fetch.bind(window) as WindowFetch
|
const origFetch = customEnv
|
||||||
window.fetch = (input, init = {}) => {
|
? (customEnv.fetch.bind(customEnv) as WindowFetch)
|
||||||
|
: (window.fetch.bind(window) as WindowFetch)
|
||||||
|
|
||||||
|
const trackFetch = (input: RequestInfo | URL, init: RequestInit = {}) => {
|
||||||
if (!(typeof input === 'string' || input instanceof URL) || app.isServiceURL(String(input))) {
|
if (!(typeof input === 'string' || input instanceof URL) || app.isServiceURL(String(input))) {
|
||||||
return origFetch(input, init)
|
return origFetch(input, init)
|
||||||
}
|
}
|
||||||
|
|
@ -237,12 +240,23 @@ export default function (app: App, opts: Partial<Options> = {}) {
|
||||||
return response
|
return response
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (customEnv) {
|
||||||
|
customEnv.fetch = trackFetch
|
||||||
|
} else {
|
||||||
|
window.fetch = trackFetch
|
||||||
|
}
|
||||||
/* ====== <> ====== */
|
/* ====== <> ====== */
|
||||||
|
|
||||||
/* ====== XHR ====== */
|
/* ====== XHR ====== */
|
||||||
const nativeOpen = XMLHttpRequest.prototype.open
|
|
||||||
XMLHttpRequest.prototype.open = function (initMethod, url) {
|
const nativeOpen = customEnv
|
||||||
const xhr = this
|
? customEnv.XMLHttpRequest.prototype.open
|
||||||
|
: XMLHttpRequest.prototype.open
|
||||||
|
|
||||||
|
function trackXMLHttpReqOpen(initMethod: string, url: string | URL) {
|
||||||
|
// @ts-ignore ??? this -> XMLHttpRequest
|
||||||
|
const xhr = this as XMLHttpRequest
|
||||||
setSessionTokenHeader((name, value) => xhr.setRequestHeader(name, value))
|
setSessionTokenHeader((name, value) => xhr.setRequestHeader(name, value))
|
||||||
|
|
||||||
let startTime = 0
|
let startTime = 0
|
||||||
|
|
@ -302,23 +316,47 @@ export default function (app: App, opts: Partial<Options> = {}) {
|
||||||
|
|
||||||
//TODO: handle error (though it has no Error API nor any useful information)
|
//TODO: handle error (though it has no Error API nor any useful information)
|
||||||
//xhr.addEventListener('error', (e) => {})
|
//xhr.addEventListener('error', (e) => {})
|
||||||
return nativeOpen.apply(this, arguments)
|
// @ts-ignore ??? this -> XMLHttpRequest
|
||||||
|
return nativeOpen.apply(this as XMLHttpRequest, arguments)
|
||||||
}
|
}
|
||||||
|
if (customEnv) {
|
||||||
|
customEnv.XMLHttpRequest.prototype.open = trackXMLHttpReqOpen.bind(customEnv)
|
||||||
|
} else {
|
||||||
|
XMLHttpRequest.prototype.open = trackXMLHttpReqOpen
|
||||||
|
}
|
||||||
|
|
||||||
const nativeSend = XMLHttpRequest.prototype.send
|
const nativeSend = XMLHttpRequest.prototype.send
|
||||||
XMLHttpRequest.prototype.send = function (body) {
|
function trackXHRSend(body: Document | XMLHttpRequestBodyInit | null | undefined) {
|
||||||
const rdo = getXHRRequestDataObject(this)
|
// @ts-ignore ??? this -> XMLHttpRequest
|
||||||
|
const rdo = getXHRRequestDataObject(this as XMLHttpRequest)
|
||||||
rdo.body = body
|
rdo.body = body
|
||||||
|
|
||||||
return nativeSend.apply(this, arguments)
|
// @ts-ignore ??? this -> XMLHttpRequest
|
||||||
|
return nativeSend.apply(this as XMLHttpRequest, arguments)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (customEnv) {
|
||||||
|
customEnv.XMLHttpRequest.prototype.send = trackXHRSend.bind(customEnv)
|
||||||
|
} else {
|
||||||
|
XMLHttpRequest.prototype.send = trackXHRSend
|
||||||
|
}
|
||||||
|
|
||||||
const nativeSetRequestHeader = XMLHttpRequest.prototype.setRequestHeader
|
const nativeSetRequestHeader = XMLHttpRequest.prototype.setRequestHeader
|
||||||
XMLHttpRequest.prototype.setRequestHeader = function (name, value) {
|
|
||||||
|
function trackSetReqHeader(name: string, value: string) {
|
||||||
if (!isHIgnored(name)) {
|
if (!isHIgnored(name)) {
|
||||||
const rdo = getXHRRequestDataObject(this)
|
// @ts-ignore ??? this -> XMLHttpRequest
|
||||||
|
const rdo = getXHRRequestDataObject(this as XMLHttpRequest)
|
||||||
rdo.headers[name] = value
|
rdo.headers[name] = value
|
||||||
}
|
}
|
||||||
|
// @ts-ignore ??? this -> XMLHttpRequest
|
||||||
|
return nativeSetRequestHeader.apply(this as XMLHttpRequest, arguments)
|
||||||
|
}
|
||||||
|
|
||||||
return nativeSetRequestHeader.apply(this, arguments)
|
if (customEnv) {
|
||||||
|
customEnv.XMLHttpRequest.prototype.setRequestHeader = trackSetReqHeader.bind(customEnv)
|
||||||
|
} else {
|
||||||
|
XMLHttpRequest.prototype.setRequestHeader = trackSetReqHeader
|
||||||
}
|
}
|
||||||
/* ====== <> ====== */
|
/* ====== <> ====== */
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue