some spot refactoring
This commit is contained in:
parent
4b3563cacd
commit
2e536a2232
4 changed files with 326 additions and 299 deletions
|
|
@ -1,9 +1,17 @@
|
|||
import { WebRequest } from "webextension-polyfill";
|
||||
import {
|
||||
createSpotNetworkRequest,
|
||||
stopTrackingNetwork,
|
||||
startTrackingNetwork,
|
||||
SpotNetworkRequest,
|
||||
} from "../utils/networkTracking";
|
||||
import {
|
||||
isTokenExpired
|
||||
} from '../utils/jwt'
|
||||
|
||||
export default defineBackground(() => {
|
||||
const CHECK_INT = 60 * 1000;
|
||||
const PING_INT = 30 * 1000;
|
||||
const VER = '1.0.3';
|
||||
const VER = "1.0.7";
|
||||
|
||||
const messages = {
|
||||
popup: {
|
||||
|
|
@ -101,6 +109,12 @@ export default defineBackground(() => {
|
|||
stopped: "stopped",
|
||||
};
|
||||
|
||||
const defaultSettings = {
|
||||
openInNewTab: true,
|
||||
consoleLogs: true,
|
||||
networkLogs: true,
|
||||
ingestPoint: "https://app.openreplay.com",
|
||||
};
|
||||
const defaultSpotObj = {
|
||||
name: "",
|
||||
comment: "",
|
||||
|
|
@ -126,12 +140,7 @@ export default defineBackground(() => {
|
|||
let finalReady = false;
|
||||
let finalSpotObj: SpotObj = defaultSpotObj;
|
||||
let onStop: (() => void) | null = null;
|
||||
let settings = {
|
||||
openInNewTab: true,
|
||||
consoleLogs: true,
|
||||
networkLogs: true,
|
||||
ingestPoint: "https://app.openreplay.com",
|
||||
};
|
||||
let settings = defaultSettings;
|
||||
let recordingState = {
|
||||
activeTabId: null,
|
||||
area: null,
|
||||
|
|
@ -139,6 +148,8 @@ export default defineBackground(() => {
|
|||
audioPerm: 0,
|
||||
} as Record<string, any>;
|
||||
let jwtToken = "";
|
||||
let refreshInt: any;
|
||||
let pingInt: any;
|
||||
|
||||
function setJWTToken(token: string) {
|
||||
jwtToken = token;
|
||||
|
|
@ -147,11 +158,28 @@ export default defineBackground(() => {
|
|||
void browser.runtime.sendMessage({
|
||||
type: messages.popup.loginExist,
|
||||
});
|
||||
if (!refreshInt) {
|
||||
refreshInt = setInterval(() => {
|
||||
void refreshToken();
|
||||
}, CHECK_INT);
|
||||
}
|
||||
|
||||
if (!pingInt) {
|
||||
pingInt = setInterval(() => {
|
||||
void pingJWT();
|
||||
}, PING_INT);
|
||||
}
|
||||
} else {
|
||||
void browser.storage.local.remove("jwtToken");
|
||||
void browser.runtime.sendMessage({
|
||||
type: messages.popup.to.noLogin,
|
||||
});
|
||||
if (refreshInt) {
|
||||
clearInterval(refreshInt);
|
||||
}
|
||||
if (pingInt) {
|
||||
clearInterval(pingInt);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -167,21 +195,35 @@ export default defineBackground(() => {
|
|||
}
|
||||
|
||||
let slackChannels: { name: string; webhookId: number }[] = [];
|
||||
const refreshToken = async () => {
|
||||
const data = await browser.storage.local.get(["jwtToken", "settings"]);
|
||||
|
||||
void checkTokenValidity();
|
||||
browser.storage.local.get("settings").then(async (data: any) => {
|
||||
if (!data.settings) {
|
||||
void browser.storage.local.set({ settings });
|
||||
return;
|
||||
}
|
||||
settings = Object.assign(settings, data.settings);
|
||||
});
|
||||
|
||||
async function refreshToken() {
|
||||
const data = await browser.storage.local.get(["jwtToken", "settings"]);
|
||||
if (!data.settings) {
|
||||
await browser.storage.local.set({ defaultSettings });
|
||||
data.settings = defaultSettings;
|
||||
}
|
||||
if (!data.jwtToken) {
|
||||
setJWTToken("");
|
||||
}
|
||||
const { jwtToken, settings } = data;
|
||||
const ingest = safeApiUrl(settings.ingestPoint);
|
||||
const refreshUrl = safeApiUrl(`${ingest}/api`);
|
||||
const refreshUrl = `${safeApiUrl(settings.ingestPoint)}/api/spot/refresh`
|
||||
console.log(settings.ingestPoint, refreshUrl);
|
||||
if (!isTokenExpired(jwtToken) || !jwtToken) {
|
||||
if (refreshInt) {
|
||||
clearInterval(refreshInt);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
const resp = await fetch(`${refreshUrl}/spot/refresh`, {
|
||||
const resp = await fetch(refreshUrl, {
|
||||
method: "GET",
|
||||
headers: {
|
||||
Authorization: `Bearer ${jwtToken}`,
|
||||
|
|
@ -199,7 +241,7 @@ export default defineBackground(() => {
|
|||
const refreshedJwt = dataObj.jwt;
|
||||
setJWTToken(refreshedJwt);
|
||||
return true;
|
||||
};
|
||||
}
|
||||
|
||||
const fetchSlackChannels = async (token: string, ingest: string) => {
|
||||
await refreshToken();
|
||||
|
|
@ -217,51 +259,15 @@ export default defineBackground(() => {
|
|||
}));
|
||||
}
|
||||
};
|
||||
let refreshInt: any;
|
||||
let pingInt: any;
|
||||
browser.storage.local
|
||||
.get(["jwtToken", "settings"])
|
||||
.then(async (data: any) => {
|
||||
if (!data.settings) {
|
||||
void browser.storage.local.set({ settings });
|
||||
return;
|
||||
}
|
||||
settings = Object.assign(settings, data.settings);
|
||||
|
||||
if (!data.jwtToken) {
|
||||
console.error("No JWT token found in storage");
|
||||
void browser.runtime.sendMessage({
|
||||
type: messages.popup.to.noLogin,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const url = safeApiUrl(`${data.settings.ingestPoint}/api`);
|
||||
const ok = await refreshToken();
|
||||
if (ok) {
|
||||
fetchSlackChannels(data.jwtToken, url).catch((e) => {
|
||||
console.error(e);
|
||||
void refreshToken();
|
||||
});
|
||||
|
||||
if (!refreshInt) {
|
||||
refreshInt = setInterval(() => {
|
||||
void refreshToken();
|
||||
}, CHECK_INT);
|
||||
}
|
||||
|
||||
if (!pingInt) {
|
||||
pingInt = setInterval(() => {
|
||||
void pingJWT();
|
||||
}, PING_INT);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
async function pingJWT(): Promise<void> {
|
||||
const data = await browser.storage.local.get(["jwtToken", "settings"]);
|
||||
if (!data.settings) {
|
||||
return;
|
||||
await browser.storage.local.set({ defaultSettings });
|
||||
data.settings = defaultSettings;
|
||||
}
|
||||
if (!data.jwtToken) {
|
||||
setJWTToken("");
|
||||
}
|
||||
const { jwtToken, settings } = data;
|
||||
const ingest = safeApiUrl(settings.ingestPoint);
|
||||
|
|
@ -277,7 +283,7 @@ export default defineBackground(() => {
|
|||
method: "GET",
|
||||
headers: {
|
||||
Authorization: `Bearer ${jwtToken}`,
|
||||
'Ext-Version': VER
|
||||
"Ext-Version": VER,
|
||||
},
|
||||
});
|
||||
if (!r.ok) {
|
||||
|
|
@ -289,6 +295,30 @@ export default defineBackground(() => {
|
|||
}
|
||||
|
||||
let lastReq: Record<string, any> | null = null;
|
||||
|
||||
async function checkTokenValidity() {
|
||||
const data = await browser.storage.local.get("jwtToken");
|
||||
console.log(data)
|
||||
if (!data.jwtToken) {
|
||||
void browser.runtime.sendMessage({
|
||||
type: messages.popup.to.noLogin,
|
||||
});
|
||||
return;
|
||||
}
|
||||
const ok = await refreshToken();
|
||||
if (ok) {
|
||||
if (!refreshInt) {
|
||||
refreshInt = setInterval(() => {
|
||||
void refreshToken();
|
||||
}, CHECK_INT);
|
||||
}
|
||||
if (!pingInt) {
|
||||
pingInt = setInterval(() => {
|
||||
void pingJWT();
|
||||
}, PING_INT);
|
||||
}
|
||||
}
|
||||
}
|
||||
// @ts-ignore
|
||||
browser.runtime.onMessage.addListener((request, sender, respond) => {
|
||||
if (request.type === messages.content.from.contentReady) {
|
||||
|
|
@ -819,7 +849,7 @@ export default defineBackground(() => {
|
|||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
Authorization: `Bearer ${jwtToken}`,
|
||||
'Ext-Version': VER
|
||||
"Ext-Version": VER,
|
||||
},
|
||||
})
|
||||
.then((r) => {
|
||||
|
|
@ -916,208 +946,25 @@ export default defineBackground(() => {
|
|||
void browser.runtime.setUninstallURL("https://forms.gle/sMo8da2AvrPg5o7YA");
|
||||
browser.runtime.onInstalled.addListener(async ({ reason }) => {
|
||||
// Also fired on update and browser_update
|
||||
if (reason !== "install") return;
|
||||
|
||||
await browser.tabs.create({
|
||||
url: "https://www.openreplay.com/spot/welcome?ref=extension",
|
||||
active: true,
|
||||
});
|
||||
if (reason === "install") {
|
||||
await browser.tabs.create({
|
||||
url: "https://www.openreplay.com/spot/welcome?ref=extension",
|
||||
active: true,
|
||||
});
|
||||
}
|
||||
// in future:
|
||||
// const tabs = await browser.tabs.query({}) as chrome.tabs.Tab[]
|
||||
// for (const tab of tabs) {
|
||||
// if (tab.id) {
|
||||
// this will require more permissions, do we even want this?
|
||||
// void chrome.tabs.executeScript(tab.id, {file: "content"});
|
||||
// }
|
||||
// }
|
||||
await checkTokenValidity();
|
||||
await initializeOffscreenDocument();
|
||||
});
|
||||
void initializeOffscreenDocument();
|
||||
|
||||
type TrackedRequest = {
|
||||
statusCode: number;
|
||||
requestHeaders: Record<string, string>;
|
||||
responseHeaders: Record<string, string>;
|
||||
} & (
|
||||
| WebRequest.OnBeforeRequestDetailsType
|
||||
| WebRequest.OnBeforeSendHeadersDetailsType
|
||||
| WebRequest.OnCompletedDetailsType
|
||||
| WebRequest.OnErrorOccurredDetailsType
|
||||
| WebRequest.OnResponseStartedDetailsType
|
||||
);
|
||||
|
||||
interface SpotNetworkRequest {
|
||||
encodedBodySize: number;
|
||||
responseBodySize: number;
|
||||
duration: number;
|
||||
method: TrackedRequest["method"];
|
||||
type: string;
|
||||
time: TrackedRequest["timeStamp"];
|
||||
statusCode: number;
|
||||
error?: string;
|
||||
url: TrackedRequest["url"];
|
||||
fromCache: boolean;
|
||||
body: string;
|
||||
requestHeaders: Record<string, string>;
|
||||
responseHeaders: Record<string, string>;
|
||||
}
|
||||
const rawRequests: (TrackedRequest & {
|
||||
startTs: number;
|
||||
duration: number;
|
||||
})[] = [];
|
||||
function filterHeaders(headers: Record<string, string>) {
|
||||
const filteredHeaders: Record<string, string> = {};
|
||||
const privateHs = [
|
||||
"x-api-key",
|
||||
"www-authenticate",
|
||||
"x-csrf-token",
|
||||
"x-requested-with",
|
||||
"x-forwarded-for",
|
||||
"x-real-ip",
|
||||
"cookie",
|
||||
"authorization",
|
||||
"auth",
|
||||
"proxy-authorization",
|
||||
"set-cookie",
|
||||
];
|
||||
if (Array.isArray(headers)) {
|
||||
headers.forEach(({ name, value }) => {
|
||||
if (privateHs.includes(name.toLowerCase())) {
|
||||
return;
|
||||
} else {
|
||||
filteredHeaders[name] = value;
|
||||
}
|
||||
});
|
||||
} else {
|
||||
for (const [key, value] of Object.entries(headers)) {
|
||||
if (!privateHs.includes(key.toLowerCase())) {
|
||||
filteredHeaders[key] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
return filteredHeaders;
|
||||
}
|
||||
function createSpotNetworkRequest(
|
||||
trackedRequest: TrackedRequest,
|
||||
trackedTab?: number,
|
||||
) {
|
||||
if (trackedRequest.tabId === -1) {
|
||||
return;
|
||||
}
|
||||
if (trackedTab && trackedTab !== trackedRequest.tabId) {
|
||||
return;
|
||||
}
|
||||
if (
|
||||
["ping", "beacon", "image", "script", "font"].includes(
|
||||
trackedRequest.type,
|
||||
)
|
||||
) {
|
||||
if (!trackedRequest.statusCode || trackedRequest.statusCode < 400) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
const type = ["stylesheet", "script", "image", "media", "font"].includes(
|
||||
trackedRequest.type,
|
||||
)
|
||||
? "resource"
|
||||
: trackedRequest.type;
|
||||
|
||||
const requestHeaders = trackedRequest.requestHeaders
|
||||
? filterHeaders(trackedRequest.requestHeaders)
|
||||
: {};
|
||||
const responseHeaders = trackedRequest.responseHeaders
|
||||
? filterHeaders(trackedRequest.responseHeaders)
|
||||
: {};
|
||||
|
||||
const reqSize = trackedRequest.reqBody
|
||||
? trackedRequest.requestSize || trackedRequest.reqBody.length
|
||||
: 0;
|
||||
|
||||
const status = getRequestStatus(trackedRequest);
|
||||
const request: SpotNetworkRequest = {
|
||||
method: trackedRequest.method,
|
||||
type,
|
||||
body: trackedRequest.reqBody,
|
||||
requestHeaders,
|
||||
responseHeaders,
|
||||
time: trackedRequest.timeStamp,
|
||||
statusCode: status,
|
||||
error: trackedRequest.error,
|
||||
url: trackedRequest.url,
|
||||
fromCache: trackedRequest.fromCache || false,
|
||||
encodedBodySize: reqSize,
|
||||
responseBodySize: trackedRequest.responseSize,
|
||||
duration: trackedRequest.duration,
|
||||
};
|
||||
|
||||
return request;
|
||||
}
|
||||
|
||||
function modifyOnSpot(request: TrackedRequest) {
|
||||
const id = request.requestId;
|
||||
const index = rawRequests.findIndex((r) => r.requestId === id);
|
||||
const ts = Date.now();
|
||||
const start = rawRequests[index]?.startTs ?? ts;
|
||||
rawRequests[index] = {
|
||||
...rawRequests[index],
|
||||
...request,
|
||||
duration: ts - start,
|
||||
};
|
||||
}
|
||||
|
||||
const trackOnBefore = (
|
||||
details: WebRequest.OnBeforeRequestDetailsType & { reqBody: string },
|
||||
) => {
|
||||
if (details.method === "POST" && details.requestBody) {
|
||||
const requestBody = details.requestBody;
|
||||
if (requestBody.formData) {
|
||||
details.reqBody = JSON.stringify(requestBody.formData);
|
||||
} else if (requestBody.raw) {
|
||||
const raw = requestBody.raw[0]?.bytes;
|
||||
if (raw) {
|
||||
details.reqBody = new TextDecoder("utf-8").decode(raw);
|
||||
}
|
||||
}
|
||||
}
|
||||
rawRequests.push({ ...details, startTs: Date.now(), duration: 0 });
|
||||
};
|
||||
const trackOnCompleted = (details: WebRequest.OnCompletedDetailsType) => {
|
||||
modifyOnSpot(details);
|
||||
};
|
||||
const trackOnHeaders = (
|
||||
details: WebRequest.OnBeforeSendHeadersDetailsType,
|
||||
) => {
|
||||
modifyOnSpot(details);
|
||||
};
|
||||
const trackOnError = (details: WebRequest.OnErrorOccurredDetailsType) => {
|
||||
modifyOnSpot(details);
|
||||
};
|
||||
function startTrackingNetwork() {
|
||||
rawRequests.length = 0;
|
||||
browser.webRequest.onBeforeRequest.addListener(
|
||||
// @ts-ignore
|
||||
trackOnBefore,
|
||||
{ urls: ["<all_urls>"] },
|
||||
["requestBody"],
|
||||
);
|
||||
browser.webRequest.onBeforeSendHeaders.addListener(
|
||||
trackOnHeaders,
|
||||
{ urls: ["<all_urls>"] },
|
||||
["requestHeaders"],
|
||||
);
|
||||
browser.webRequest.onCompleted.addListener(
|
||||
trackOnCompleted,
|
||||
{
|
||||
urls: ["<all_urls>"],
|
||||
},
|
||||
["responseHeaders"],
|
||||
);
|
||||
browser.webRequest.onErrorOccurred.addListener(
|
||||
trackOnError,
|
||||
{
|
||||
urls: ["<all_urls>"],
|
||||
},
|
||||
["extraHeaders"],
|
||||
);
|
||||
}
|
||||
|
||||
function stopTrackingNetwork() {
|
||||
browser.webRequest.onBeforeRequest.removeListener(trackOnBefore);
|
||||
browser.webRequest.onCompleted.removeListener(trackOnCompleted);
|
||||
browser.webRequest.onErrorOccurred.removeListener(trackOnError);
|
||||
}
|
||||
|
||||
async function initializeOffscreenDocument() {
|
||||
const existingContexts = await browser.runtime.getContexts({});
|
||||
let recording = false;
|
||||
|
|
@ -1125,9 +972,8 @@ export default defineBackground(() => {
|
|||
const offscreenDocument = existingContexts.find(
|
||||
(c: { contextType: string }) => c.contextType === "OFFSCREEN_DOCUMENT",
|
||||
);
|
||||
|
||||
if (offscreenDocument) {
|
||||
await browser.offscreen.closeDocument()
|
||||
await browser.offscreen.closeDocument();
|
||||
}
|
||||
|
||||
await browser.offscreen.createDocument({
|
||||
|
|
@ -1153,22 +999,15 @@ export default defineBackground(() => {
|
|||
}
|
||||
let activeTab = activeTabs[0];
|
||||
const sendTo = message.activeTabId || activeTab.id!;
|
||||
if (!contentArmy[sendTo]) {
|
||||
let tries = 0;
|
||||
const exist = await new Promise((res) => {
|
||||
const interval = setInterval(() => {
|
||||
if (contentArmy[sendTo] || tries < 500) {
|
||||
clearInterval(interval);
|
||||
res(tries < 500);
|
||||
}
|
||||
}, 100);
|
||||
});
|
||||
if (!exist) throw new Error("Can't find required tab");
|
||||
let attempts = 0;
|
||||
while (!contentArmy[sendTo] && attempts < 100) {
|
||||
await new Promise((resolve) => setTimeout(resolve, 100));
|
||||
attempts++;
|
||||
}
|
||||
try {
|
||||
void browser.tabs.sendMessage(sendTo, message);
|
||||
} catch (e) {
|
||||
console.error("Sending to active tab", e, message);
|
||||
if (contentArmy[sendTo]) {
|
||||
await browser.tabs.sendMessage(sendTo, message);
|
||||
} else {
|
||||
console.error("Content script not ready in tab", sendTo);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1381,29 +1220,4 @@ export default defineBackground(() => {
|
|||
console.error("Error starting recording", e, activeTab, activeTabId);
|
||||
}
|
||||
}
|
||||
|
||||
const decodeJwt = (jwt: string): any => {
|
||||
const base64Url = jwt.split(".")[1];
|
||||
if (!base64Url) {
|
||||
return { exp: 0 };
|
||||
}
|
||||
const base64 = base64Url.replace("-", "+").replace("_", "/");
|
||||
return JSON.parse(atob(base64));
|
||||
};
|
||||
|
||||
const isTokenExpired = (token: string): boolean => {
|
||||
const decoded: any = decodeJwt(token);
|
||||
const currentTime = Date.now() / 1000;
|
||||
return decoded.exp < currentTime;
|
||||
};
|
||||
|
||||
function getRequestStatus(request: any): number {
|
||||
if (request.statusCode) {
|
||||
return request.statusCode;
|
||||
}
|
||||
if (request.error) {
|
||||
return 0;
|
||||
}
|
||||
return 200;
|
||||
}
|
||||
});
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
"name": "wxt-starter",
|
||||
"description": "manifest.json description",
|
||||
"private": true,
|
||||
"version": "1.0.6",
|
||||
"version": "1.0.7",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "wxt",
|
||||
|
|
|
|||
14
spot/utils/jwt.ts
Normal file
14
spot/utils/jwt.ts
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
export const decodeJwt = (jwt: string): any => {
|
||||
const base64Url = jwt.split(".")[1];
|
||||
if (!base64Url) {
|
||||
return { exp: 0 };
|
||||
}
|
||||
const base64 = base64Url.replace("-", "+").replace("_", "/");
|
||||
return JSON.parse(atob(base64));
|
||||
};
|
||||
|
||||
export const isTokenExpired = (token: string): boolean => {
|
||||
const decoded: any = decodeJwt(token);
|
||||
const currentTime = Date.now() / 1000;
|
||||
return decoded.exp < currentTime;
|
||||
};
|
||||
199
spot/utils/networkTracking.ts
Normal file
199
spot/utils/networkTracking.ts
Normal file
|
|
@ -0,0 +1,199 @@
|
|||
import { WebRequest } from "webextension-polyfill";
|
||||
export type TrackedRequest = {
|
||||
statusCode: number;
|
||||
requestHeaders: Record<string, string>;
|
||||
responseHeaders: Record<string, string>;
|
||||
} & (
|
||||
| WebRequest.OnBeforeRequestDetailsType
|
||||
| WebRequest.OnBeforeSendHeadersDetailsType
|
||||
| WebRequest.OnCompletedDetailsType
|
||||
| WebRequest.OnErrorOccurredDetailsType
|
||||
| WebRequest.OnResponseStartedDetailsType
|
||||
);
|
||||
|
||||
export interface SpotNetworkRequest {
|
||||
encodedBodySize: number;
|
||||
responseBodySize: number;
|
||||
duration: number;
|
||||
method: TrackedRequest["method"];
|
||||
type: string;
|
||||
time: TrackedRequest["timeStamp"];
|
||||
statusCode: number;
|
||||
error?: string;
|
||||
url: TrackedRequest["url"];
|
||||
fromCache: boolean;
|
||||
body: string;
|
||||
requestHeaders: Record<string, string>;
|
||||
responseHeaders: Record<string, string>;
|
||||
}
|
||||
const rawRequests: (TrackedRequest & {
|
||||
startTs: number;
|
||||
duration: number;
|
||||
})[] = [];
|
||||
function filterHeaders(headers: Record<string, string>) {
|
||||
const filteredHeaders: Record<string, string> = {};
|
||||
const privateHs = [
|
||||
"x-api-key",
|
||||
"www-authenticate",
|
||||
"x-csrf-token",
|
||||
"x-requested-with",
|
||||
"x-forwarded-for",
|
||||
"x-real-ip",
|
||||
"cookie",
|
||||
"authorization",
|
||||
"auth",
|
||||
"proxy-authorization",
|
||||
"set-cookie",
|
||||
];
|
||||
if (Array.isArray(headers)) {
|
||||
headers.forEach(({ name, value }) => {
|
||||
if (privateHs.includes(name.toLowerCase())) {
|
||||
return;
|
||||
} else {
|
||||
filteredHeaders[name] = value;
|
||||
}
|
||||
});
|
||||
} else {
|
||||
for (const [key, value] of Object.entries(headers)) {
|
||||
if (!privateHs.includes(key.toLowerCase())) {
|
||||
filteredHeaders[key] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
return filteredHeaders;
|
||||
}
|
||||
export function createSpotNetworkRequest(
|
||||
trackedRequest: TrackedRequest,
|
||||
trackedTab?: number,
|
||||
) {
|
||||
if (trackedRequest.tabId === -1) {
|
||||
return;
|
||||
}
|
||||
if (trackedTab && trackedTab !== trackedRequest.tabId) {
|
||||
return;
|
||||
}
|
||||
if (
|
||||
["ping", "beacon", "image", "script", "font"].includes(trackedRequest.type)
|
||||
) {
|
||||
if (!trackedRequest.statusCode || trackedRequest.statusCode < 400) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
const type = ["stylesheet", "script", "image", "media", "font"].includes(
|
||||
trackedRequest.type,
|
||||
)
|
||||
? "resource"
|
||||
: trackedRequest.type;
|
||||
|
||||
const requestHeaders = trackedRequest.requestHeaders
|
||||
? filterHeaders(trackedRequest.requestHeaders)
|
||||
: {};
|
||||
const responseHeaders = trackedRequest.responseHeaders
|
||||
? filterHeaders(trackedRequest.responseHeaders)
|
||||
: {};
|
||||
|
||||
const reqSize = trackedRequest.reqBody
|
||||
? trackedRequest.requestSize || trackedRequest.reqBody.length
|
||||
: 0;
|
||||
|
||||
const status = getRequestStatus(trackedRequest);
|
||||
const request: SpotNetworkRequest = {
|
||||
method: trackedRequest.method,
|
||||
type,
|
||||
body: trackedRequest.reqBody,
|
||||
requestHeaders,
|
||||
responseHeaders,
|
||||
time: trackedRequest.timeStamp,
|
||||
statusCode: status,
|
||||
error: trackedRequest.error,
|
||||
url: trackedRequest.url,
|
||||
fromCache: trackedRequest.fromCache || false,
|
||||
encodedBodySize: reqSize,
|
||||
responseBodySize: trackedRequest.responseSize,
|
||||
duration: trackedRequest.duration,
|
||||
};
|
||||
|
||||
return request;
|
||||
}
|
||||
|
||||
function modifyOnSpot(request: TrackedRequest) {
|
||||
const id = request.requestId;
|
||||
const index = rawRequests.findIndex((r) => r.requestId === id);
|
||||
const ts = Date.now();
|
||||
const start = rawRequests[index]?.startTs ?? ts;
|
||||
rawRequests[index] = {
|
||||
...rawRequests[index],
|
||||
...request,
|
||||
duration: ts - start,
|
||||
};
|
||||
}
|
||||
|
||||
const trackOnBefore = (
|
||||
details: WebRequest.OnBeforeRequestDetailsType & { reqBody: string },
|
||||
) => {
|
||||
if (details.method === "POST" && details.requestBody) {
|
||||
const requestBody = details.requestBody;
|
||||
if (requestBody.formData) {
|
||||
details.reqBody = JSON.stringify(requestBody.formData);
|
||||
} else if (requestBody.raw) {
|
||||
const raw = requestBody.raw[0]?.bytes;
|
||||
if (raw) {
|
||||
details.reqBody = new TextDecoder("utf-8").decode(raw);
|
||||
}
|
||||
}
|
||||
}
|
||||
rawRequests.push({ ...details, startTs: Date.now(), duration: 0 });
|
||||
};
|
||||
const trackOnCompleted = (details: WebRequest.OnCompletedDetailsType) => {
|
||||
modifyOnSpot(details);
|
||||
};
|
||||
const trackOnHeaders = (details: WebRequest.OnBeforeSendHeadersDetailsType) => {
|
||||
modifyOnSpot(details);
|
||||
};
|
||||
const trackOnError = (details: WebRequest.OnErrorOccurredDetailsType) => {
|
||||
modifyOnSpot(details);
|
||||
};
|
||||
export function startTrackingNetwork() {
|
||||
rawRequests.length = 0;
|
||||
browser.webRequest.onBeforeRequest.addListener(
|
||||
// @ts-ignore
|
||||
trackOnBefore,
|
||||
{ urls: ["<all_urls>"] },
|
||||
["requestBody"],
|
||||
);
|
||||
browser.webRequest.onBeforeSendHeaders.addListener(
|
||||
trackOnHeaders,
|
||||
{ urls: ["<all_urls>"] },
|
||||
["requestHeaders"],
|
||||
);
|
||||
browser.webRequest.onCompleted.addListener(
|
||||
trackOnCompleted,
|
||||
{
|
||||
urls: ["<all_urls>"],
|
||||
},
|
||||
["responseHeaders"],
|
||||
);
|
||||
browser.webRequest.onErrorOccurred.addListener(
|
||||
trackOnError,
|
||||
{
|
||||
urls: ["<all_urls>"],
|
||||
},
|
||||
["extraHeaders"],
|
||||
);
|
||||
}
|
||||
|
||||
export function stopTrackingNetwork() {
|
||||
browser.webRequest.onBeforeRequest.removeListener(trackOnBefore);
|
||||
browser.webRequest.onCompleted.removeListener(trackOnCompleted);
|
||||
browser.webRequest.onErrorOccurred.removeListener(trackOnError);
|
||||
}
|
||||
|
||||
function getRequestStatus(request: any): number {
|
||||
if (request.statusCode) {
|
||||
return request.statusCode;
|
||||
}
|
||||
if (request.error) {
|
||||
return 0;
|
||||
}
|
||||
return 200;
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue