@requestly/requestly-proxy 1.0.4 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -13,6 +13,7 @@ exports.RULE_ACTION = {
13
13
  };
14
14
  exports.RQ_INTERCEPTED_CONTENT_TYPES = [
15
15
  "text/html",
16
+ "text/plain",
16
17
  "text/javascript",
17
18
  "application/javascript",
18
19
  "text/css",
@@ -0,0 +1,9 @@
1
+ export function shouldMakeExternalRequest(ctx: any, action: any): boolean;
2
+ export function makeExternalRequest(ctx: any, url: any): Promise<{
3
+ status: boolean;
4
+ responseData: {
5
+ headers: any;
6
+ status_code: any;
7
+ body: any;
8
+ };
9
+ }>;
@@ -0,0 +1,167 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
25
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
26
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
27
+ return new (P || (P = Promise))(function (resolve, reject) {
28
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
29
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
30
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
31
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
32
+ });
33
+ };
34
+ var __importDefault = (this && this.__importDefault) || function (mod) {
35
+ return (mod && mod.__esModule) ? mod : { "default": mod };
36
+ };
37
+ Object.defineProperty(exports, "__esModule", { value: true });
38
+ exports.makeExternalRequest = exports.shouldMakeExternalRequest = void 0;
39
+ const parser = require("ua-parser-js");
40
+ const Sentry = __importStar(require("@sentry/browser"));
41
+ const https_1 = __importDefault(require("https"));
42
+ const http_1 = __importDefault(require("http"));
43
+ const willCreateMixedResponse = (ctx, destinationUrl) => {
44
+ var _a, _b, _c, _d;
45
+ let user_agent_str = null;
46
+ user_agent_str = (_a = ctx === null || ctx === void 0 ? void 0 : ctx.clientToProxyRequest) === null || _a === void 0 ? void 0 : _a.headers["user-agent"];
47
+ console.log("handling mixed response. i got headers", (_b = ctx === null || ctx === void 0 ? void 0 : ctx.clientToProxyRequest) === null || _b === void 0 ? void 0 : _b.headers);
48
+ const user_agent = (_d = (_c = parser(user_agent_str)) === null || _c === void 0 ? void 0 : _c.browser) === null || _d === void 0 ? void 0 : _d.name;
49
+ const LOCAL_DOMAINS = ["localhost", "127.0.0.1"];
50
+ return ctx.isSSL && destinationUrl.includes("http") && (user_agent === "Safari" ||
51
+ !LOCAL_DOMAINS.some((domain) => destinationUrl.includes(domain)));
52
+ };
53
+ const canPreserveCookie = (ctx, destinationUrl) => {
54
+ /* DOES NOT WORK */
55
+ // CAN'T DIFFERENTIATE CALL TO TOP LEVEL DOCUMENT
56
+ // // had to create this because host and referer were sometimes same
57
+ // // but had additional prefixes like `/`
58
+ // // note: neither referer nor host have search params
59
+ // const areUrlsSame = (u1, u2) => {
60
+ // try {
61
+ // let u1obj = new URL(u1)
62
+ // let u2obj = new URL(u2)
63
+ // return u1obj.href === u2obj.href
64
+ // } catch {
65
+ // // when url objects were not properly formed
66
+ // return false
67
+ // }
68
+ // }
69
+ // const requestHeaders = ctx?.clientToProxyRequest?.headers;
70
+ // const referer = requestHeaders["referer"] || requestHeaders["Referer"]
71
+ // const origin = requestHeaders["origin"] || requestHeaders["Origin"]
72
+ // // cannot preserve cookie on request for toplevel document
73
+ // if (
74
+ // // navigating to the domain directly from the search bar
75
+ // !referer ||
76
+ // // when navigating using google search result
77
+ // // referrer is present but origin is not
78
+ // !origin || // bug: origin not passed by firefox in some cases
79
+ // !areUrlsSame(referer, origin)
80
+ // ) return false
81
+ return true;
82
+ };
83
+ const shouldMakeExternalRequest = (ctx, action) => {
84
+ return ((action.preserveCookie && canPreserveCookie(ctx, action.url)) ||
85
+ willCreateMixedResponse(ctx, url));
86
+ };
87
+ exports.shouldMakeExternalRequest = shouldMakeExternalRequest;
88
+ function makeRequest(requestOptions) {
89
+ return __awaiter(this, void 0, void 0, function* () {
90
+ let requestAgent = http_1.default;
91
+ if (requestOptions.url.includes("https"))
92
+ requestAgent = https_1.default;
93
+ requestOptions.headers["Cache-Control"] = "no-cache";
94
+ try {
95
+ const { data, response } = yield new Promise((resolve, reject) => {
96
+ // `.request` wasn't working well with the provided request options
97
+ // node only provides wrapper for get, hence did not implement other methods
98
+ if (requestOptions.method === "GET") {
99
+ let request = requestAgent.get(requestOptions.url, requestOptions, (res) => {
100
+ const dataBuffers = [];
101
+ res.on('data', (buffer) => {
102
+ dataBuffers.push(buffer);
103
+ });
104
+ res.on('end', () => {
105
+ resolve({ data: Buffer.concat(dataBuffers).toString(), response: res });
106
+ });
107
+ });
108
+ request.on('error', (error) => {
109
+ console.log("requestError", requestOptions);
110
+ console.error(error);
111
+ reject(error);
112
+ });
113
+ }
114
+ else {
115
+ // hack: to return an understandable response back to user
116
+ // @nsr fix: implement all other methods using some workaround of http.request bug
117
+ const errMsg = "Can only preserve cookies for get requests";
118
+ const customError = new Error(errMsg);
119
+ customError.response = { data: errMsg };
120
+ throw customError;
121
+ }
122
+ });
123
+ return { success: true, response, data };
124
+ }
125
+ catch (error) {
126
+ Sentry.captureException(error);
127
+ console.error(error);
128
+ return { success: false, error };
129
+ }
130
+ });
131
+ }
132
+ const makeExternalRequest = (ctx, url) => __awaiter(void 0, void 0, void 0, function* () {
133
+ const requestOptions = ctx.proxyToServerRequestOptions;
134
+ // can't pass all request options because they o
135
+ // verride some attrubutes of node http request options
136
+ // in the wrong way
137
+ let finalRequestOptions = {
138
+ headers: Object.assign({}, requestOptions.headers),
139
+ url,
140
+ method: requestOptions.method
141
+ };
142
+ if (url.includes("https")) {
143
+ finalRequestOptions = Object.assign(Object.assign({}, finalRequestOptions), { rejectUnauthorized: false, requestCert: true, agent: false, strictSSL: false });
144
+ }
145
+ const { success, error, response, data } = yield makeRequest(finalRequestOptions);
146
+ if (success) {
147
+ return {
148
+ status: true,
149
+ responseData: {
150
+ headers: Object.assign(Object.assign({}, response.headers), { "connection": "close", "Cache-Control": "no-cache" }),
151
+ status_code: response.statuscode,
152
+ body: data,
153
+ },
154
+ };
155
+ }
156
+ else {
157
+ return {
158
+ status: true,
159
+ responseData: {
160
+ headers: { "Cache-Control": "no-cache" },
161
+ status_code: 502,
162
+ body: error.response ? error.response.data : null,
163
+ },
164
+ };
165
+ }
166
+ });
167
+ exports.makeExternalRequest = makeExternalRequest;
@@ -0,0 +1,22 @@
1
+ export function handleMixedResponse(ctx: any, destinationUrl: any): Promise<{
2
+ status: boolean;
3
+ response_data: {
4
+ headers: any;
5
+ status_code: any;
6
+ body: any;
7
+ };
8
+ } | {
9
+ status: boolean;
10
+ response_data?: undefined;
11
+ }>;
12
+ export function handleServerSideRedirect(ctx: any, destinationUrl: any): Promise<{
13
+ status: boolean;
14
+ response_data: {
15
+ headers: any;
16
+ status_code: any;
17
+ body: any;
18
+ };
19
+ } | {
20
+ status: boolean;
21
+ response_data?: undefined;
22
+ }>;
@@ -0,0 +1,149 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
25
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
26
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
27
+ return new (P || (P = Promise))(function (resolve, reject) {
28
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
29
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
30
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
31
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
32
+ });
33
+ };
34
+ var __importDefault = (this && this.__importDefault) || function (mod) {
35
+ return (mod && mod.__esModule) ? mod : { "default": mod };
36
+ };
37
+ Object.defineProperty(exports, "__esModule", { value: true });
38
+ exports.handleServerSideRedirect = exports.handleMixedResponse = void 0;
39
+ const parser = require("ua-parser-js");
40
+ const Sentry = __importStar(require("@sentry/browser"));
41
+ const https_1 = __importDefault(require("https"));
42
+ function makeSameRequestToDifferentUrl(ctx, url) {
43
+ var _a;
44
+ return __awaiter(this, void 0, void 0, function* () {
45
+ try {
46
+ const requestOptions = {
47
+ headers: Object.assign(Object.assign({}, (_a = ctx === null || ctx === void 0 ? void 0 : ctx.clientToProxyRequest) === null || _a === void 0 ? void 0 : _a.headers), { "Cache-Control": "no-cache" }),
48
+ rejectUnauthorized: false,
49
+ requestCert: true,
50
+ agent: false,
51
+ strictSSL: false,
52
+ };
53
+ const { data, response } = yield new Promise((resolve, reject) => {
54
+ let request = https_1.default.get(url, requestOptions, (res) => {
55
+ const dataBuffers = [];
56
+ res.on('data', (buffer) => {
57
+ dataBuffers.push(buffer);
58
+ });
59
+ res.on('end', () => {
60
+ resolve({ data: Buffer.concat(dataBuffers).toString(), response: res });
61
+ });
62
+ });
63
+ request.on('error', (error) => {
64
+ console.error(error);
65
+ reject(error);
66
+ });
67
+ });
68
+ return { success: true, response, data };
69
+ }
70
+ catch (error) {
71
+ Sentry.captureException(error);
72
+ console.error(error);
73
+ return { success: false, error };
74
+ }
75
+ });
76
+ }
77
+ const willCreateMixedResponse = (ctx, destinationUrl) => {
78
+ var _a, _b, _c, _d;
79
+ let user_agent_str = null;
80
+ user_agent_str = (_a = ctx === null || ctx === void 0 ? void 0 : ctx.clientToProxyRequest) === null || _a === void 0 ? void 0 : _a.headers["user-agent"];
81
+ console.log("handling mixed response. i got headers", (_b = ctx === null || ctx === void 0 ? void 0 : ctx.clientToProxyRequest) === null || _b === void 0 ? void 0 : _b.headers);
82
+ const user_agent = (_d = (_c = parser(user_agent_str)) === null || _c === void 0 ? void 0 : _c.browser) === null || _d === void 0 ? void 0 : _d.name;
83
+ const LOCAL_DOMAINS = ["localhost", "127.0.0.1"];
84
+ return ctx.isSSL && (user_agent === "Safari" ||
85
+ !LOCAL_DOMAINS.some((domain) => destinationUrl.includes(domain)));
86
+ };
87
+ const handleMixedResponse = (ctx, destinationUrl) => __awaiter(void 0, void 0, void 0, function* () {
88
+ if (willCreateMixedResponse(ctx, destinationUrl)) {
89
+ const { success, error, response, data } = yield makeSameRequestToDifferentUrl(ctx, destinationUrl);
90
+ if (success) {
91
+ return {
92
+ status: true,
93
+ response_data: {
94
+ headers: Object.assign(Object.assign({}, response.headers), { "connection": "close", "Cache-Control": "no-cache" }),
95
+ status_code: response.statuscode,
96
+ body: data,
97
+ },
98
+ };
99
+ }
100
+ else {
101
+ return {
102
+ status: true,
103
+ response_data: {
104
+ headers: { "Cache-Control": "no-cache" },
105
+ status_code: 502,
106
+ body: error.response ? error.responserror.data : null,
107
+ },
108
+ };
109
+ }
110
+ }
111
+ return { status: false };
112
+ });
113
+ exports.handleMixedResponse = handleMixedResponse;
114
+ const canMakeServerSideRedirect = (ctx, destinationUrl) => {
115
+ // todo: no redirects for html documents
116
+ // content-type
117
+ // will be handled by mixed content case
118
+ if (destinationUrl.includes("http"))
119
+ return false;
120
+ return true;
121
+ };
122
+ const handleServerSideRedirect = (ctx, destinationUrl) => __awaiter(void 0, void 0, void 0, function* () {
123
+ if (canMakeServerSideRedirect(ctx, destinationUrl)) {
124
+ const { success, error, response, data } = yield makeSameRequestToDifferentUrl(ctx, destinationUrl);
125
+ if (success) {
126
+ return {
127
+ status: true,
128
+ response_data: {
129
+ headers: Object.assign(Object.assign({}, response.headers), { "connection": "close", "Cache-Control": "no-cache" }),
130
+ status_code: response.statuscode,
131
+ body: data,
132
+ },
133
+ };
134
+ }
135
+ else {
136
+ return {
137
+ status: true,
138
+ response_data: {
139
+ headers: { "Cache-Control": "no-cache" },
140
+ status_code: 502,
141
+ body: error.response ? error.responserror.data : null,
142
+ },
143
+ };
144
+ }
145
+ }
146
+ return { status: false };
147
+ });
148
+ exports.handleServerSideRedirect = handleServerSideRedirect;
149
+ // export default handleMixedResponse; // change exports
@@ -15,7 +15,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
15
15
  const proxy_1 = require("../../../../lib/proxy");
16
16
  const proxy_ctx_helper_1 = require("../../helpers/proxy_ctx_helper");
17
17
  const modified_requests_pool_1 = __importDefault(require("../modified_requests_pool"));
18
- const handle_mixed_response_1 = __importDefault(require("../handle_mixed_response"));
18
+ const redirectHelper_1 = require("../../helpers/redirectHelper");
19
19
  const utils_1 = require("../utils");
20
20
  const { URL } = require("url");
21
21
  // adding util to get origin header for handling cors
@@ -41,9 +41,12 @@ const process_redirect_action = (action, ctx) => __awaiter(void 0, void 0, void
41
41
  else {
42
42
  modified_requests_pool_1.default.add(new_url);
43
43
  }
44
- const { status: isMixedResponse, response_data } = yield (0, handle_mixed_response_1.default)(ctx, new_url);
45
- if (isMixedResponse) {
46
- return (0, utils_1.build_action_processor_response)(action, true, (0, utils_1.build_post_process_data)(response_data.status_code, response_data.headers, response_data.body));
44
+ // handle mixed content and redirect with preserve cookie
45
+ if ((0, redirectHelper_1.shouldMakeExternalRequest)(ctx, action)) {
46
+ const { status: wasExternalRequestSuccessful, responseData } = yield (0, redirectHelper_1.makeExternalRequest)(ctx, new_url);
47
+ if (wasExternalRequestSuccessful) {
48
+ return (0, utils_1.build_action_processor_response)(action, true, (0, utils_1.build_post_process_data)(responseData.status_code, responseData.headers, responseData.body));
49
+ }
47
50
  }
48
51
  // If this is a pre-flight request, don't redirect it
49
52
  if ((0, proxy_ctx_helper_1.is_request_preflight)(ctx))
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@requestly/requestly-proxy",
3
- "version": "1.0.4",
3
+ "version": "1.1.0",
4
4
  "description": "Proxy that gives superpowers to all the Requestly clients",
5
5
  "main": "dist/index.js",
6
6
  "scripts": {