Skip to content

Commit ec4010f

Browse files
committed
better getRequestPerformanceEntry
1 parent 147aba6 commit ec4010f

File tree

1 file changed

+99
-79
lines changed
  • packages/rrweb/src/plugins/network/record

1 file changed

+99
-79
lines changed

packages/rrweb/src/plugins/network/record/index.ts

Lines changed: 99 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -142,18 +142,38 @@ function initPerformanceObserver(
142142
};
143143
}
144144

145-
const getPerformanceEntryByUrl = (
145+
const getRequestPerformanceEntry = async (
146146
win: IWindow,
147147
initiatorType: string,
148148
url: string,
149-
) => {
149+
after?: number,
150+
before?: number,
151+
attempt = 0,
152+
): Promise<PerformanceEntry> => {
153+
if (attempt > 10) {
154+
throw new Error('Cannot find performance entry');
155+
}
150156
const urlPerformanceEntries = win.performance.getEntriesByName(url);
151-
return findLast(
157+
const performanceEntry = findLast(
152158
urlPerformanceEntries,
153159
(performanceEntry) =>
154160
isResourceTiming(performanceEntry) &&
155-
performanceEntry.initiatorType === initiatorType,
161+
performanceEntry.initiatorType === initiatorType &&
162+
(!after || performanceEntry.startTime >= after) &&
163+
(!before || performanceEntry.startTime <= before),
156164
);
165+
if (!performanceEntry) {
166+
await new Promise((resolve) => setTimeout(resolve, 50 * attempt));
167+
return getRequestPerformanceEntry(
168+
win,
169+
initiatorType,
170+
url,
171+
after,
172+
before,
173+
attempt + 1,
174+
);
175+
}
176+
return performanceEntry;
157177
};
158178

159179
function initXhrObserver(
@@ -191,7 +211,7 @@ function initXhrObserver(
191211
XMLHttpRequest.prototype,
192212
'open',
193213
(originalOpen: typeof XMLHttpRequest.prototype.open) => {
194-
return async function (
214+
return function (
195215
method: string,
196216
url: string | URL,
197217
async = true,
@@ -200,8 +220,9 @@ function initXhrObserver(
200220
) {
201221
const xhr = this as XMLHttpRequest;
202222
const req = new Request(url);
203-
let performanceEntry: PerformanceEntry | undefined;
204223
const networkRequest: Partial<NetworkRequest> = {};
224+
let after: number | undefined;
225+
let before: number | undefined;
205226
if (recordRequestHeaders) {
206227
networkRequest.requestHeaders = {};
207228
const originalSetRequestHeader = xhr.setRequestHeader.bind(xhr);
@@ -210,9 +231,9 @@ function initXhrObserver(
210231
return originalSetRequestHeader(header, value);
211232
};
212233
}
213-
if (recordRequestBody) {
214-
const originalSend = xhr.send.bind(xhr);
215-
xhr.send = (body) => {
234+
const originalSend = xhr.send.bind(xhr);
235+
xhr.send = (body) => {
236+
if (recordRequestBody) {
216237
if (body === undefined || body === null) {
217238
networkRequest.requestBody = null;
218239
} else {
@@ -223,64 +244,65 @@ function initXhrObserver(
223244
: undefined,
224245
);
225246
}
226-
return originalSend(body);
227-
};
228-
}
229-
await new Promise<void>((resolve) => {
230-
xhr.addEventListener('readystatechange', () => {
231-
if (xhr.readyState !== xhr.DONE) {
232-
return;
233-
}
234-
performanceEntry = getPerformanceEntryByUrl(
235-
win,
236-
'xmlhttprequest',
237-
req.url,
238-
);
239-
if (recordResponseHeaders) {
240-
networkRequest.responseHeaders = {};
241-
const rawHeaders = xhr.getAllResponseHeaders();
242-
const headers = rawHeaders.trim().split(/[\r\n]+/);
243-
headers.forEach((line) => {
244-
const parts = line.split(': ');
245-
const header = parts.shift();
246-
const value = parts.join(': ');
247-
if (header) {
248-
networkRequest.responseHeaders![header] = value;
249-
}
250-
});
251-
}
252-
if (recordResponseBody) {
253-
if (!xhr.response) {
254-
networkRequest.responseBody = null;
255-
} else {
256-
try {
257-
const objBody = JSON.parse(xhr.response as string) as object;
258-
networkRequest.responseBody = stringify(
259-
objBody,
260-
typeof recordResponseBody === 'object'
261-
? recordResponseBody
262-
: undefined,
263-
);
264-
} catch {
265-
networkRequest.responseBody = xhr.response as string;
266-
}
247+
}
248+
after = win.performance.now();
249+
return originalSend(body);
250+
};
251+
xhr.addEventListener('readystatechange', () => {
252+
if (xhr.readyState !== xhr.DONE) {
253+
return;
254+
}
255+
before = win.performance.now();
256+
if (recordResponseHeaders) {
257+
networkRequest.responseHeaders = {};
258+
const rawHeaders = xhr.getAllResponseHeaders();
259+
const headers = rawHeaders.trim().split(/[\r\n]+/);
260+
headers.forEach((line) => {
261+
const parts = line.split(': ');
262+
const header = parts.shift();
263+
const value = parts.join(': ');
264+
if (header) {
265+
networkRequest.responseHeaders![header] = value;
266+
}
267+
});
268+
}
269+
if (recordResponseBody) {
270+
if (!xhr.response) {
271+
networkRequest.responseBody = null;
272+
} else {
273+
try {
274+
const objBody = JSON.parse(xhr.response as string) as object;
275+
networkRequest.responseBody = stringify(
276+
objBody,
277+
typeof recordResponseBody === 'object'
278+
? recordResponseBody
279+
: undefined,
280+
);
281+
} catch {
282+
networkRequest.responseBody = xhr.response as string;
267283
}
268284
}
269-
resolve();
270-
});
271-
originalOpen(method, url, async, username, password);
272-
});
273-
if (performanceEntry) {
274-
cb({
275-
requests: [
276-
{
285+
}
286+
getRequestPerformanceEntry(
287+
win,
288+
'xmlhttprequest',
289+
req.url,
290+
after,
291+
before,
292+
)
293+
.then((performanceEntry) => {
294+
const request: NetworkRequest = {
277295
performanceEntry,
278296
requestMethod: req.method,
279297
...networkRequest,
280-
},
281-
],
282-
});
283-
}
298+
};
299+
cb({ requests: [request] });
300+
})
301+
.catch(() => {
302+
//
303+
});
304+
});
305+
originalOpen(method, url, async, username, password);
284306
};
285307
},
286308
);
@@ -323,8 +345,9 @@ function initFetchObserver(
323345
const originalFetch = win.fetch;
324346
const wrappedFetch: typeof fetch = async (url, init) => {
325347
const req = new Request(url, init);
326-
let performanceEntry: PerformanceEntry | undefined;
327348
const networkRequest: Partial<NetworkRequest> = {};
349+
let after: number | undefined;
350+
let before: number | undefined;
328351
try {
329352
if (recordRequestHeaders) {
330353
networkRequest.requestHeaders = {};
@@ -344,8 +367,9 @@ function initFetchObserver(
344367
);
345368
}
346369
}
370+
after = win.performance.now();
347371
const res = await originalFetch(req);
348-
performanceEntry = getPerformanceEntryByUrl(win, 'fetch', req.url);
372+
before = win.performance.now();
349373
if (recordResponseHeaders) {
350374
networkRequest.responseHeaders = {};
351375
res.headers.forEach((value, header) => {
@@ -371,23 +395,19 @@ function initFetchObserver(
371395
}
372396
}
373397
return res;
374-
} catch (cause) {
375-
if (!performanceEntry) {
376-
performanceEntry = getPerformanceEntryByUrl(win, 'fetch', req.url);
377-
}
378-
throw cause;
379398
} finally {
380-
if (performanceEntry) {
381-
cb({
382-
requests: [
383-
{
384-
performanceEntry,
385-
requestMethod: req.method,
386-
...networkRequest,
387-
},
388-
],
399+
getRequestPerformanceEntry(win, 'fetch', req.url, after, before)
400+
.then((performanceEntry) => {
401+
const request: NetworkRequest = {
402+
performanceEntry,
403+
requestMethod: req.method,
404+
...networkRequest,
405+
};
406+
cb({ requests: [request] });
407+
})
408+
.catch(() => {
409+
//
389410
});
390-
}
391411
}
392412
};
393413
wrappedFetch.prototype = {};

0 commit comments

Comments
 (0)