Skip to content

Commit

Permalink
fix: interceptor
Browse files Browse the repository at this point in the history
  • Loading branch information
Baghdasaryan committed May 12, 2024
1 parent 629136a commit f65e2da
Show file tree
Hide file tree
Showing 5 changed files with 98 additions and 58 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,23 @@ import { IRuleMetaData, PageType } from "@models/formFieldModel";
import { PostMessageAction } from "@models/postMessageActionModel";
import { NAMESPACE } from "@options/constant";
import { validateJSON } from "@/utils/validateJSON";
import { jsonParesString } from "@/utils/jsonParesString";
import { isPromise } from "@/utils/isPromise";

const jsonifyValidJSONString = (mightBeJSONString, doStrictCheck) => {
const defaultValue = doStrictCheck ? {} : mightBeJSONString;

if (typeof mightBeJSONString !== "string") {
return defaultValue;
}

try {
return JSON.parse(mightBeJSONString);
} catch (e) {}

return defaultValue;
};

const isContentTypeJSON = (contentType) => !!contentType?.includes("application/json");

((NAMESPACE) => {
window[NAMESPACE] = window[NAMESPACE] || {};
Expand Down Expand Up @@ -60,15 +76,18 @@ import { jsonParesString } from "@/utils/jsonParesString";

const matchedRule = getMatchedRuleByUrl(request.url);

if (["GET", "HEAD"].includes(request.method.toUpperCase()) || !matchedRule) {
if (
matchedRule?.pageType !== PageType.MODIFY_RESPONSE &&
(["GET", "HEAD"].includes(request.method.toUpperCase()) || !matchedRule)
) {
try {
return await getOriginalResponse();
} catch (error) {
return Promise.reject(error);
}
}

if (matchedRule.pageType === PageType.MODIFY_RESPONSE) {
if (matchedRule?.pageType === PageType.MODIFY_RESPONSE) {
const response = await getOriginalResponse();
const responseBody = response.headers.get("content-type")?.includes("application/json")
? await response.json()
Expand Down Expand Up @@ -109,7 +128,75 @@ import { jsonParesString } from "@/utils/jsonParesString";
}
};

// XMLHttpRequest interceptor
const onReadyStateChange = async function () {
const matchedRule = getMatchedRuleByUrl(this.requestURL);
if (!matchedRule) return;
if (this.readyState === this.HEADERS_RECEIVED || this.readyState === this.DONE) {
const responseType = this.responseType;
const contentType = this.getResponseHeader("content-type");

if (this.readyState === this.HEADERS_RECEIVED) {
try {
Object.defineProperty(this, "status", {
get: () => 200,
});
} catch (error) {}
}

if (this.readyState === this.DONE) {
let customResponse =
matchedRule?.pageType === PageType.MODIFY_RESPONSE
? new ExecuteCode("args", `return (${matchedRule.editorValue})(args);`)({ response: this.response })
: this.response;

// Convert customResponse back to rawText
// response.value is String and evaluator method might return string/object
if (isPromise(customResponse)) {
customResponse = await customResponse;
}

const isUnsupportedResponseType = responseType && !["json", "text"].includes(responseType);

if (isUnsupportedResponseType) {
customResponse = this.response;
}

if (
!isUnsupportedResponseType &&
typeof customResponse === "object" &&
!(customResponse instanceof Blob) &&
(responseType === "json" || isContentTypeJSON(contentType))
) {
customResponse = JSON.stringify(customResponse);
}

try {
Object.defineProperty(this, "response", {
get: function () {
if (responseType === "json") {
if (typeof customResponse === "object") {
return customResponse;
}

return jsonifyValidJSONString(customResponse);
}

return customResponse;
},
});

if (responseType === "" || responseType === "text") {
Object.defineProperty(this, "responseText", {
get: function () {
return customResponse;
},
});
}
} catch (error) {}
}
}
};

const XHR = XMLHttpRequest;
XMLHttpRequest = function () {
const xhr = new XHR();
Expand All @@ -121,70 +208,21 @@ import { jsonParesString } from "@/utils/jsonParesString";
XMLHttpRequest[key] = val;
});

const onReadyStateChange = async function () {
if (this.readyState === this.DONE) {
const responseData = this.response;
const matchedRule = getMatchedRuleByUrl(this.requestURL);
let returnedData = responseData;
if (matchedRule) {
// Execute custom function
let returnedData = matchedRule
? await new ExecuteCode("args", `return (${matchedRule.editorValue})(args);`)(args)
: responseData;

const requestHeaders = this.requestHeaders || {};
const isJson =
requestHeaders["Content-Type"]?.includes("json") ||
requestHeaders["Accept"]?.includes("json") ||
this.responseType === "json";

// Convert response back to string, Blob isn't necessary
if (isJson && !(returnedData instanceof Blob)) {
try {
returnedData = JSON.stringify(returnedData);
} catch (error) {}
}
}

const args = {
response: jsonParesString(responseData),
};

// Modify response
Object.defineProperty(this, "response", {
get: function () {
return returnedData;
},
});

// Modify responseText
if (this.responseType === "" || this.responseType === "text") {
Object.defineProperty(this, "responseText", {
get: function () {
return returnedData;
},
});
}
}
};

const open = XMLHttpRequest.prototype.open;
XMLHttpRequest.prototype.open = function (method, url) {
this.method = method;
this.requestURL = url;
this.requestURL = getAbsoluteUrl(url);
open.apply(this, arguments);
};

const send = XMLHttpRequest.prototype.send;
XMLHttpRequest.prototype.send = function (data) {
const matchedRule = getMatchedRuleByUrl(this.requestURL);
// Modify request body
const requestBody = matchedRule?.pageType === PageType.MODIFY_REQUEST_BODY ? matchedRule.editorValue : data;
this.requestData = requestBody;
send.call(this, requestBody);
};

const setRequestHeader = XMLHttpRequest.prototype.setRequestHeader;
let setRequestHeader = XMLHttpRequest.prototype.setRequestHeader;
XMLHttpRequest.prototype.setRequestHeader = function (header, value) {
this.requestHeaders = this.requestHeaders || {};
this.requestHeaders[header] = value;
Expand Down
2 changes: 1 addition & 1 deletion browser-extension/src/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
},
{
"matches": ["http://*/*", "https://*/*"],
"js": ["cotentScript/cotentScript.js"],
"js": ["intercept/intercept.js"],
"run_at": "document_start",
"all_frames": true,
"world": "MAIN"
Expand Down
2 changes: 1 addition & 1 deletion browser-extension/src/services/InjectCodeService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -241,7 +241,7 @@ class InjectCodeService extends BaseService {
window[NAMESPACE] = window[NAMESPACE] || {};
window[NAMESPACE].rules = rules;
window[NAMESPACE].runtimeId = runtimeId;
window[NAMESPACE].start();
window[NAMESPACE]?.start?.();
},
world: "MAIN",
args: [rules, NAMESPACE, chrome.runtime.id],
Expand Down
2 changes: 2 additions & 0 deletions browser-extension/src/utils/isPromise.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export const isPromise = (obj) =>
!!obj && (typeof obj === "object" || typeof obj === "function") && typeof obj.then === "function";
2 changes: 1 addition & 1 deletion browser-extension/webpack/webpack.common.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ const outputPath = path.join(__dirname, "../", `dist/${process.env.BROWSER}`);
module.exports = {
entry: {
serviceWorker: path.resolve(__dirname, "../src/serviceWorker", "serviceWorker.ts"),
cotentScript: path.resolve(__dirname, "../src/cotentScript", "cotentScript.ts"),
intercept: path.resolve(__dirname, "../src/cotentScript", "intercept.ts"),
recorderWidget: path.resolve(__dirname, "../src/cotentScript", "recorderWidget.ts"),
setupContentConfig: path.resolve(__dirname, "../src/cotentScript", "setupContentConfig.ts"),
recordSession: path.resolve(__dirname, "../src/recordSession", "recordSession.ts"),
Expand Down

0 comments on commit f65e2da

Please sign in to comment.