Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions src/components/proxy-middleware/middlewares/rules_middleware.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ import {
} from "../helpers/proxy_ctx_helper";
import RuleProcessorHelper from "../helpers/rule_processor_helper";
import RuleActionProcessor from "../rule_action_processor";
import { PROXY_HANDLER_TYPE } from "../../../lib/proxy";
import { RULE_ACTION } from "../constants";
import process_modify_header_action from "../rule_action_processor/processors/modify_header_processor";
import * as Sentry from "@sentry/browser";

class RulesMiddleware {
constructor(is_active, ctx, rulesHelper) {
Expand Down Expand Up @@ -79,6 +83,24 @@ class RulesMiddleware {
return rule_actions;
};

_applyResponseHeaderRulesForRedirect = (ctx, destUrl) => {
const originalUrl = this.request_data.request_url;
const prevHandler = ctx.currentHandler;
try {
this._update_request_data({ request_url: destUrl }); // match response rules against the destination
this._init_response_data(ctx); // reads the fetched headers on ctx.serverToProxyResponse
ctx.currentHandler = PROXY_HANDLER_TYPE.ON_RESPONSE;
this._process_rules(true)
.filter((action) => action?.action === RULE_ACTION.MODIFY_HEADERS)
.forEach((action) => process_modify_header_action(action, ctx)); // mutates ctx.serverToProxyResponse.headers
} catch (e) {
Sentry.captureException(e); // degrade: serve the redirected response without the failed header mods
} finally {
ctx.currentHandler = prevHandler;
this._update_request_data({ request_url: originalUrl });
}
};

_update_action_result_objs = (action_result_objs = []) => {
if (action_result_objs) {
this.action_result_objs =
Expand All @@ -96,6 +118,9 @@ class RulesMiddleware {

this.on_request_actions = this._process_rules();

ctx.rq.applyResponseHeaderRulesForRedirect = (destUrl) =>
this._applyResponseHeaderRulesForRedirect(ctx, destUrl);

const { action_result_objs, continue_request } =
await this.rule_action_processor.process_actions(
this.on_request_actions,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,47 +1,33 @@
const axios = require("axios");
const parser = require("ua-parser-js");
import fs from "fs";
import * as Sentry from "@sentry/browser";
const mime = require('mime-types');

const handleMixedResponse = async (ctx, destinationUrl) => {
// Handling mixed response from safari
let user_agent_str = null;
user_agent_str = ctx?.clientToProxyRequest?.headers["user-agent"];
const user_agent = parser(user_agent_str)?.browser?.name;
const LOCAL_DOMAINS = ["localhost", "127.0.0.1"];

if (ctx.isSSL && destinationUrl.includes("http:")) {
if (
user_agent === "Safari" ||
!LOCAL_DOMAINS.some((domain) => destinationUrl.includes(domain))
Comment on lines -16 to -17

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why did we remove this check below?

) {
try {
const resp = await axios.get(destinationUrl, {
headers: {
"Cache-Control": "no-cache",
},
});

return {
status: true,
response_data: {
headers: { "Cache-Control": "no-cache" },
status_code: 200,
body: resp.data,
},
};
} catch (e) {
Sentry.captureException(e);
return {
status: true,
response_data: {
headers: { "Cache-Control": "no-cache" },
status_code: 502,
body: e.response ? e.response.data : null,
},
};
}
try {
const resp = await axios.get(destinationUrl, {
responseType: "arraybuffer", // never JSON-parse; binary-safe; axios still decompresses gzip
headers: { "Cache-Control": "no-cache" },
});
return {
status: true,
response_data: {
status_code: resp.status,
headers: resp.headers, // real upstream headers (content-encoding already stripped by axios)
body: resp.data, // Buffer
},
};
} catch (e) {
Sentry.captureException(e);
return {
status: true,
response_data: {
headers: { "Cache-Control": "no-cache" },
status_code: e.response ? e.response.status : 502,
body: e.response ? e.response.data : null,
},
Comment thread
coderabbitai[bot] marked this conversation as resolved.
};
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,11 @@ class RuleActionProcessor {

const status_code = action_result.post_process_data.status_code || 200;
const headers = action_result.post_process_data.headers || {};
let body = action_result.post_process_data.body || null;
let body = action_result.post_process_data.body;
if (body === undefined) body = null;

// console.log("Log", ctx.rq.original_request);
if(typeof(body) !== 'string') {
if (body !== null && !Buffer.isBuffer(body) && typeof body !== "string") {
body = JSON.stringify(body);
}

Expand Down Expand Up @@ -75,7 +76,7 @@ class RuleActionProcessor {

switch (rule_action.action) {
case RULE_ACTION.REDIRECT:
action_result = process_redirect_action(rule_action, ctx);
action_result = await process_redirect_action(rule_action, ctx);
break;
case RULE_ACTION.MODIFY_HEADERS:
action_result = process_modify_header_action(rule_action, ctx);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import handleMixedResponse from "../handle_mixed_response";
import {
build_action_processor_response,
build_post_process_data,
stripHopByHopHeaders,
} from "../utils";

// adding util to get origin header for handling cors
Expand Down Expand Up @@ -46,14 +47,20 @@ const process_redirect_action = async (action, ctx) => {
);

if (isMixedResponse) {
// Feed the fetched response into the response context, then run the existing
// response Modify Headers processor against the destination URL.
ctx.serverToProxyResponse = {
statusCode: response_data.status_code,
headers: { ...(response_data.headers || {}) },
};
if (typeof ctx.rq.applyResponseHeaderRulesForRedirect === "function") {
ctx.rq.applyResponseHeaderRulesForRedirect(new_url);
}
const headers = stripHopByHopHeaders(ctx.serverToProxyResponse.headers);
Comment on lines +56 to +59

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not the right fix as other type of modifications might be required too in future. This should via the same action -> processing pipeline somehow so that rule gets triggered automatically

return build_action_processor_response(
action,
true,
build_post_process_data(
response_data.status_code,
response_data.headers,
response_data.body
)
build_post_process_data(response_data.status_code, headers, response_data.body)
);
}

Expand Down
13 changes: 12 additions & 1 deletion src/components/proxy-middleware/rule_action_processor/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -59,4 +59,15 @@ export const getHost = (ctx) => {

export const get_file_contents = (file_path) => {
return fs.readFileSync(file_path, "utf-8");
}
};

export const stripHopByHopHeaders = (headers) => {
const out = { ...(headers || {}) };
for (const key of Object.keys(out)) {
const k = key.toLowerCase();
if (k === "content-length" || k === "transfer-encoding") {
delete out[key];
}
}
return out;
};
Loading