openreplay/spot/utils/networkDebuggerTracking.ts
2025-03-25 15:39:02 +01:00

106 lines
3.7 KiB
TypeScript

let requestMaps = {};
const potentialActiveTabs: Array<string | number> = [];
export function resetMap(tabId?: string) {
if (tabId) delete requestMaps[tabId];
else requestMaps = {};
}
export async function attachDebuggerToTab(tabId: string | number) {
if (requestMaps[tabId] && potentialActiveTabs.includes(tabId)) return;
await new Promise((resolve, reject) => {
chrome.debugger.attach({ tabId }, "1.3", () => {
if (chrome.runtime.lastError) return reject(`${chrome.runtime.lastError.message}, ${tabId}`);
if (!requestMaps[tabId]) requestMaps[tabId] = {};
potentialActiveTabs.push(tabId);
chrome.debugger.sendCommand({ tabId }, "Network.enable", {}, resolve);
});
chrome.debugger.onEvent.addListener(handleRequestIntercept);
});
}
export function stopDebugger(tabId?: string | number) {
if (tabId) {
chrome.debugger.detach({ tabId });
const index = potentialActiveTabs.indexOf(tabId);
if (index > -1) potentialActiveTabs.splice(index, 1);
} else {
potentialActiveTabs.forEach((tabId) => {
chrome.debugger.detach({ tabId });
});
potentialActiveTabs.length = 0;
}
}
const getType = (requestType: string) => {
switch (requestType.toLowerCase()) {
case "fetch":
case "xhr":
case "xmlhttprequest":
return 'xmlhttprequest'
default:
return requestType
}
}
function handleRequestIntercept(source, method, params) {
if (!source.tabId) return;
const tabId = source.tabId;
if (!requestMaps[tabId]) return;
if (params.request && params.request.method === "OPTIONS") return;
const reqId = `${tabId}_${params.requestId}`;
switch (method) {
case "Network.requestWillBeSent":
requestMaps[tabId][reqId] = {
encodedBodySize: 0,
responseBodySize: 0,
duration: 0,
method: params.request.method,
type: params.type ? getType(params.type) : "resource",
statusCode: 0,
url: params.request.url,
body: params.request.postData || "",
responseBody: "",
fromCache: false,
requestHeaders: params.request.headers || {},
responseHeaders: {},
timestamp: Date.now(),
time: Date.now(),
};
break;
case "Network.responseReceived":
if (!requestMaps[tabId][reqId]) return;
requestMaps[tabId][reqId].statusCode = params.response.status;
requestMaps[tabId][reqId].responseHeaders = params.response.headers || {};
if (params.response.fromDiskCache) requestMaps[tabId][reqId].fromCache = true;
break;
case "Network.dataReceived":
if (!requestMaps[tabId][reqId]) return;
requestMaps[tabId][reqId].encodedBodySize += params.dataLength;
break;
case "Network.loadingFinished":
if (!requestMaps[tabId][reqId]) return;
requestMaps[tabId][reqId].duration = Date.now() - requestMaps[tabId][reqId].timestamp;
requestMaps[tabId][reqId].responseBodySize = requestMaps[tabId][reqId].encodedBodySize;
chrome.debugger.sendCommand({ tabId }, "Network.getResponseBody", { requestId: params.requestId }, (res) => {
if (!res || res.error) {
requestMaps[tabId][reqId].error = res?.error || "Unknown";
} else {
requestMaps[tabId][reqId].responseBody = res.base64Encoded ? 'base64 payload' : res.body;
}
});
break;
case "Network.loadingFailed":
if (!requestMaps[tabId][reqId]) return;
requestMaps[tabId][reqId].error = params.errorText || "Unknown";
break;
}
}
export function getRequests(tabId?: string) {
if (tabId) {
return Object.values(requestMaps[tabId] || {});
}
return Object.values(requestMaps).reduce((acc, curr) => acc.concat(Object.values(curr)), []);
}