mirror of
https://github.com/alibaba/anyproxy.git
synced 2025-04-23 15:31:26 +00:00
parent
3deacd2387
commit
2516ea2d57
@ -5,7 +5,7 @@
|
|||||||
"browser": true,
|
"browser": true,
|
||||||
"node": true,
|
"node": true,
|
||||||
"es6": true,
|
"es6": true,
|
||||||
"jasmine": true
|
"jest": true
|
||||||
},
|
},
|
||||||
"globals": {
|
"globals": {
|
||||||
"React": true,
|
"React": true,
|
||||||
|
5
.travis.yml
Normal file
5
.travis.yml
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
language: node_js
|
||||||
|
node_js:
|
||||||
|
- 12
|
||||||
|
before_script:
|
||||||
|
- node ./bin/anyproxy-ca -g
|
@ -4,6 +4,7 @@ AnyProxy
|
|||||||
[![NPM version][npm-image]][npm-url]
|
[![NPM version][npm-image]][npm-url]
|
||||||
[![node version][node-image]][node-url]
|
[![node version][node-image]][node-url]
|
||||||
[![npm download][download-image]][download-url]
|
[![npm download][download-image]][download-url]
|
||||||
|
[](https://travis-ci.org/alibaba/anyproxy)
|
||||||
|
|
||||||
[npm-image]: https://img.shields.io/npm/v/anyproxy.svg?style=flat-square
|
[npm-image]: https://img.shields.io/npm/v/anyproxy.svg?style=flat-square
|
||||||
[npm-url]: https://npmjs.org/package/anyproxy
|
[npm-url]: https://npmjs.org/package/anyproxy
|
||||||
|
11
babel.config.js
Normal file
11
babel.config.js
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
if (process.env.NODE_ENV === 'test') {
|
||||||
|
module.exports = {};
|
||||||
|
} else {
|
||||||
|
module.exports = {
|
||||||
|
presets: [
|
||||||
|
'es2015',
|
||||||
|
'stage-0'
|
||||||
|
]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
@ -20,10 +20,10 @@ program
|
|||||||
.parse(process.argv);
|
.parse(process.argv);
|
||||||
|
|
||||||
function openFolderOfFile(filePath) {
|
function openFolderOfFile(filePath) {
|
||||||
const isWin = /^win/.test(process.platform);
|
const platform = process.platform;
|
||||||
if (isWin) {
|
if (/^win/.test(platform)) {
|
||||||
exec('start .', { cwd: path.dirname(filePath) });
|
exec('start .', { cwd: path.dirname(filePath) });
|
||||||
} else {
|
} else if (/darwin/.test(platform)) {
|
||||||
exec(`open -R ${filePath}`);
|
exec(`open -R ${filePath}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -33,7 +33,6 @@ function guideToGenrateCA() {
|
|||||||
if (!error) {
|
if (!error) {
|
||||||
const certDir = path.dirname(keyPath);
|
const certDir = path.dirname(keyPath);
|
||||||
console.log(`The cert is generated at ${certDir}. Please trust the ${color.bold('rootCA.crt')}.`);
|
console.log(`The cert is generated at ${certDir}. Please trust the ${color.bold('rootCA.crt')}.`);
|
||||||
// TODO: console.log('guide to install');
|
|
||||||
openFolderOfFile(crtPath);
|
openFolderOfFile(crtPath);
|
||||||
} else {
|
} else {
|
||||||
console.error('failed to generate rootCA', error);
|
console.error('failed to generate rootCA', error);
|
||||||
@ -44,7 +43,6 @@ function guideToGenrateCA() {
|
|||||||
function guideToTrustCA() {
|
function guideToTrustCA() {
|
||||||
const certPath = AnyProxy.utils.certMgr.getRootCAFilePath();
|
const certPath = AnyProxy.utils.certMgr.getRootCAFilePath();
|
||||||
if (certPath) {
|
if (certPath) {
|
||||||
// TODO: console.log('guide to install');
|
|
||||||
openFolderOfFile(certPath);
|
openFolderOfFile(certPath);
|
||||||
} else {
|
} else {
|
||||||
console.error('failed to get cert path');
|
console.error('failed to get cert path');
|
||||||
|
188
jest.config.js
Normal file
188
jest.config.js
Normal file
@ -0,0 +1,188 @@
|
|||||||
|
// For a detailed explanation regarding each configuration property, visit:
|
||||||
|
// https://jestjs.io/docs/en/configuration.html
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
// All imported modules in your tests should be mocked automatically
|
||||||
|
// automock: false,
|
||||||
|
|
||||||
|
// Stop running tests after `n` failures
|
||||||
|
// bail: 0,
|
||||||
|
|
||||||
|
// Respect "browser" field in package.json when resolving modules
|
||||||
|
// browser: false,
|
||||||
|
|
||||||
|
// The directory where Jest should store its cached dependency information
|
||||||
|
// cacheDirectory: "/private/var/folders/dn/t1cpcmtx6ng82d7qf8b02w7r0000gn/T/jest_dx",
|
||||||
|
|
||||||
|
// Automatically clear mock calls and instances between every test
|
||||||
|
clearMocks: true,
|
||||||
|
|
||||||
|
// Indicates whether the coverage information should be collected while executing the test
|
||||||
|
// collectCoverage: false,
|
||||||
|
|
||||||
|
// An array of glob patterns indicating a set of files for which coverage information should be collected
|
||||||
|
// collectCoverageFrom: null,
|
||||||
|
|
||||||
|
// The directory where Jest should output its coverage files
|
||||||
|
coverageDirectory: 'coverage',
|
||||||
|
|
||||||
|
// An array of regexp pattern strings used to skip coverage collection
|
||||||
|
// coveragePathIgnorePatterns: [
|
||||||
|
// "/node_modules/"
|
||||||
|
// ],
|
||||||
|
|
||||||
|
// A list of reporter names that Jest uses when writing coverage reports
|
||||||
|
// coverageReporters: [
|
||||||
|
// "json",
|
||||||
|
// "text",
|
||||||
|
// "lcov",
|
||||||
|
// "clover"
|
||||||
|
// ],
|
||||||
|
|
||||||
|
// An object that configures minimum threshold enforcement for coverage results
|
||||||
|
// coverageThreshold: null,
|
||||||
|
|
||||||
|
// A path to a custom dependency extractor
|
||||||
|
// dependencyExtractor: null,
|
||||||
|
|
||||||
|
// Make calling deprecated APIs throw helpful error messages
|
||||||
|
// errorOnDeprecated: false,
|
||||||
|
|
||||||
|
// Force coverage collection from ignored files using an array of glob patterns
|
||||||
|
// forceCoverageMatch: [],
|
||||||
|
|
||||||
|
// A path to a module which exports an async function that is triggered once before all test suites
|
||||||
|
// globalSetup: null,
|
||||||
|
|
||||||
|
// A path to a module which exports an async function that is triggered once after all test suites
|
||||||
|
// globalTeardown: null,
|
||||||
|
|
||||||
|
// A set of global variables that need to be available in all test environments
|
||||||
|
// globals: {},
|
||||||
|
|
||||||
|
// The maximum amount of workers used to run your tests. Can be specified as % or a number. E.g. maxWorkers: 10% will use 10% of your CPU amount + 1 as the maximum worker number. maxWorkers: 2 will use a maximum of 2 workers.
|
||||||
|
// maxWorkers: "50%",
|
||||||
|
|
||||||
|
// An array of directory names to be searched recursively up from the requiring module's location
|
||||||
|
// moduleDirectories: [
|
||||||
|
// "node_modules"
|
||||||
|
// ],
|
||||||
|
|
||||||
|
// An array of file extensions your modules use
|
||||||
|
// moduleFileExtensions: [
|
||||||
|
// "js",
|
||||||
|
// "json",
|
||||||
|
// "jsx",
|
||||||
|
// "ts",
|
||||||
|
// "tsx",
|
||||||
|
// "node"
|
||||||
|
// ],
|
||||||
|
|
||||||
|
// A map from regular expressions to module names that allow to stub out resources with a single module
|
||||||
|
// moduleNameMapper: {},
|
||||||
|
|
||||||
|
// An array of regexp pattern strings, matched against all module paths before considered 'visible' to the module loader
|
||||||
|
// modulePathIgnorePatterns: [],
|
||||||
|
|
||||||
|
// Activates notifications for test results
|
||||||
|
// notify: false,
|
||||||
|
|
||||||
|
// An enum that specifies notification mode. Requires { notify: true }
|
||||||
|
// notifyMode: "failure-change",
|
||||||
|
|
||||||
|
// A preset that is used as a base for Jest's configuration
|
||||||
|
// preset: null,
|
||||||
|
|
||||||
|
// Run tests from one or more projects
|
||||||
|
// projects: null,
|
||||||
|
|
||||||
|
// Use this configuration option to add custom reporters to Jest
|
||||||
|
// reporters: undefined,
|
||||||
|
|
||||||
|
// Automatically reset mock state between every test
|
||||||
|
// resetMocks: false,
|
||||||
|
|
||||||
|
// Reset the module registry before running each individual test
|
||||||
|
// resetModules: false,
|
||||||
|
|
||||||
|
// A path to a custom resolver
|
||||||
|
// resolver: null,
|
||||||
|
|
||||||
|
// Automatically restore mock state between every test
|
||||||
|
// restoreMocks: false,
|
||||||
|
|
||||||
|
// The root directory that Jest should scan for tests and modules within
|
||||||
|
// rootDir: null,
|
||||||
|
|
||||||
|
// A list of paths to directories that Jest should use to search for files in
|
||||||
|
// roots: [
|
||||||
|
// "<rootDir>"
|
||||||
|
// ],
|
||||||
|
|
||||||
|
// Allows you to use a custom runner instead of Jest's default test runner
|
||||||
|
// runner: "jest-runner",
|
||||||
|
|
||||||
|
// The paths to modules that run some code to configure or set up the testing environment before each test
|
||||||
|
// setupFiles: [],
|
||||||
|
|
||||||
|
// A list of paths to modules that run some code to configure or set up the testing framework before each test
|
||||||
|
// setupFilesAfterEnv: [],
|
||||||
|
|
||||||
|
// A list of paths to snapshot serializer modules Jest should use for snapshot testing
|
||||||
|
// snapshotSerializers: [],
|
||||||
|
|
||||||
|
// The test environment that will be used for testing
|
||||||
|
testEnvironment: 'node',
|
||||||
|
|
||||||
|
// Options that will be passed to the testEnvironment
|
||||||
|
// testEnvironmentOptions: {},
|
||||||
|
|
||||||
|
// Adds a location field to test results
|
||||||
|
// testLocationInResults: false,
|
||||||
|
|
||||||
|
// The glob patterns Jest uses to detect test files
|
||||||
|
// testMatch: [
|
||||||
|
// "**/__tests__/**/*.[jt]s?(x)",
|
||||||
|
// "**/?(*.)+(spec|test).[tj]s?(x)"
|
||||||
|
// ],
|
||||||
|
|
||||||
|
// An array of regexp pattern strings that are matched against all test paths, matched tests are skipped
|
||||||
|
// testPathIgnorePatterns: [
|
||||||
|
// "/node_modules/"
|
||||||
|
// ],
|
||||||
|
|
||||||
|
// The regexp pattern or array of patterns that Jest uses to detect test files
|
||||||
|
// testRegex: [],
|
||||||
|
|
||||||
|
// This option allows the use of a custom results processor
|
||||||
|
// testResultsProcessor: null,
|
||||||
|
|
||||||
|
// This option allows use of a custom test runner
|
||||||
|
// testRunner: "jasmine2",
|
||||||
|
|
||||||
|
// This option sets the URL for the jsdom environment. It is reflected in properties such as location.href
|
||||||
|
// testURL: "http://localhost",
|
||||||
|
|
||||||
|
// Setting this value to "fake" allows the use of fake timers for functions such as "setTimeout"
|
||||||
|
// timers: "real",
|
||||||
|
|
||||||
|
// A map from regular expressions to paths to transformers
|
||||||
|
// transform: null,
|
||||||
|
|
||||||
|
// An array of regexp pattern strings that are matched against all source file paths, matched files will skip transformation
|
||||||
|
// transformIgnorePatterns: [
|
||||||
|
// "/node_modules/"
|
||||||
|
// ],
|
||||||
|
|
||||||
|
// An array of regexp pattern strings that are matched against all modules before the module loader will automatically return a mock for them
|
||||||
|
// unmockedModulePathPatterns: undefined,
|
||||||
|
|
||||||
|
// Indicates whether each individual test should be reported during the run
|
||||||
|
// verbose: null,
|
||||||
|
|
||||||
|
// An array of regexp patterns that are matched against all source file paths before re-running tests in watch mode
|
||||||
|
// watchPathIgnorePatterns: [],
|
||||||
|
|
||||||
|
// Whether to use watchman for file crawling
|
||||||
|
// watchman: true,
|
||||||
|
};
|
@ -11,11 +11,11 @@ const async = require('async'),
|
|||||||
util = require('./util'),
|
util = require('./util'),
|
||||||
wsServerMgr = require('./wsServerMgr'),
|
wsServerMgr = require('./wsServerMgr'),
|
||||||
co = require('co'),
|
co = require('co'),
|
||||||
|
assert = require('assert'),
|
||||||
constants = require('constants'),
|
constants = require('constants'),
|
||||||
asyncTask = require('async-task-mgr');
|
asyncTask = require('async-task-mgr');
|
||||||
|
|
||||||
const createSecureContext = tls.createSecureContext || crypto.createSecureContext;
|
const createSecureContext = tls.createSecureContext || crypto.createSecureContext;
|
||||||
//using sni to avoid multiple ports
|
|
||||||
function SNIPrepareCert(serverName, SNICallback) {
|
function SNIPrepareCert(serverName, SNICallback) {
|
||||||
let keyContent,
|
let keyContent,
|
||||||
crtContent,
|
crtContent,
|
||||||
@ -59,7 +59,6 @@ function SNIPrepareCert(serverName, SNICallback) {
|
|||||||
//config.port - port to start https server
|
//config.port - port to start https server
|
||||||
//config.handler - request handler
|
//config.handler - request handler
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create an https server
|
* Create an https server
|
||||||
*
|
*
|
||||||
@ -73,44 +72,11 @@ function createHttpsServer(config) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return new Promise((resolve) => {
|
return new Promise((resolve) => {
|
||||||
certMgr.getCertificate('anyproxy_internal_https_server', (err, keyContent, crtContent) => {
|
const server = https.createServer({
|
||||||
const server = https.createServer({
|
secureOptions: constants.SSL_OP_NO_SSLv3 || constants.SSL_OP_NO_TLSv1,
|
||||||
secureOptions: constants.SSL_OP_NO_SSLv3 || constants.SSL_OP_NO_TLSv1,
|
SNICallback: SNIPrepareCert,
|
||||||
SNICallback: SNIPrepareCert,
|
}, config.handler).listen(config.port);
|
||||||
key: keyContent,
|
resolve(server);
|
||||||
cert: crtContent
|
|
||||||
}, config.handler).listen(config.port);
|
|
||||||
resolve(server);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* create an https server that serving on IP address
|
|
||||||
* @param @required {object} config
|
|
||||||
* @param @required {string} config.ip the IP address of the server
|
|
||||||
* @param @required {number} config.port the port to listen on
|
|
||||||
* @param @required {function} handler the handler of each connect
|
|
||||||
*/
|
|
||||||
function createIPHttpsServer(config) {
|
|
||||||
if (!config || !config.port || !config.handler) {
|
|
||||||
throw (new Error('please assign a port'));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!config.ip) {
|
|
||||||
throw (new Error('please assign an IP to create the https server'));
|
|
||||||
}
|
|
||||||
|
|
||||||
return new Promise((resolve) => {
|
|
||||||
certMgr.getCertificate(config.ip, (err, keyContent, crtContent) => {
|
|
||||||
const server = https.createServer({
|
|
||||||
secureOptions: constants.SSL_OP_NO_SSLv3 || constants.SSL_OP_NO_TLSv1,
|
|
||||||
key: keyContent,
|
|
||||||
cert: crtContent
|
|
||||||
}, config.handler).listen(config.port);
|
|
||||||
|
|
||||||
resolve(server);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -124,20 +90,20 @@ function createIPHttpsServer(config) {
|
|||||||
*/
|
*/
|
||||||
class httpsServerMgr {
|
class httpsServerMgr {
|
||||||
constructor(config) {
|
constructor(config) {
|
||||||
if (!config || !config.handler) {
|
assert(config, 'config is required');
|
||||||
throw new Error('handler is required');
|
assert(config.handler && config.wsHandler, 'handler and wsHandler are required');
|
||||||
}
|
assert(config.hostname, 'hostname is required');
|
||||||
this.instanceDefaultHost = '127.0.0.1';
|
this.hostname = config.hostname;
|
||||||
this.httpsAsyncTask = new asyncTask();
|
|
||||||
this.handler = config.handler;
|
this.handler = config.handler;
|
||||||
this.wsHandler = config.wsHandler
|
this.wsHandler = config.wsHandler;
|
||||||
|
this.httpsAsyncTask = new asyncTask();
|
||||||
|
this.asyncTaskName = `https_${Math.random()}`;
|
||||||
|
this.httpsServer = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
getSharedHttpsServer(hostname) {
|
getSharedHttpsServer() {
|
||||||
// ip address will have a unique name
|
|
||||||
const finalHost = util.isIpDomain(hostname) ? hostname : this.instanceDefaultHost;
|
|
||||||
|
|
||||||
const self = this;
|
const self = this;
|
||||||
|
const finalHost = self.hostname;
|
||||||
function prepareServer(callback) {
|
function prepareServer(callback) {
|
||||||
let instancePort;
|
let instancePort;
|
||||||
co(util.getFreePort)
|
co(util.getFreePort)
|
||||||
@ -145,19 +111,10 @@ class httpsServerMgr {
|
|||||||
instancePort = port;
|
instancePort = port;
|
||||||
let httpsServer = null;
|
let httpsServer = null;
|
||||||
|
|
||||||
// if ip address passed in, will create an IP http server
|
httpsServer = yield createHttpsServer({
|
||||||
if (util.isIpDomain(hostname)) {
|
port,
|
||||||
httpsServer = yield createIPHttpsServer({
|
handler: self.handler
|
||||||
ip: hostname,
|
});
|
||||||
port,
|
|
||||||
handler: self.handler
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
httpsServer = yield createHttpsServer({
|
|
||||||
port,
|
|
||||||
handler: self.handler
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
wsServerMgr.getWsServer({
|
wsServerMgr.getWsServer({
|
||||||
server: httpsServer,
|
server: httpsServer,
|
||||||
@ -168,6 +125,8 @@ class httpsServerMgr {
|
|||||||
logUtil.debug('will let WebSocket server to handle the upgrade event');
|
logUtil.debug('will let WebSocket server to handle the upgrade event');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
self.httpsServer = httpsServer;
|
||||||
|
|
||||||
const result = {
|
const result = {
|
||||||
host: finalHost,
|
host: finalHost,
|
||||||
port: instancePort,
|
port: instancePort,
|
||||||
@ -181,9 +140,7 @@ class httpsServerMgr {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
// each ip address will gain a unit task name,
|
self.httpsAsyncTask.addTask(self.asyncTaskName, prepareServer, (error, serverInfo) => {
|
||||||
// while the domain address will share a common task name
|
|
||||||
self.httpsAsyncTask.addTask(`createHttpsServer-${finalHost}`, prepareServer, (error, serverInfo) => {
|
|
||||||
if (error) {
|
if (error) {
|
||||||
reject(error);
|
reject(error);
|
||||||
} else {
|
} else {
|
||||||
@ -192,6 +149,10 @@ class httpsServerMgr {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
close() {
|
||||||
|
return this.httpsServer && this.httpsServer.close();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = httpsServerMgr;
|
module.exports = httpsServerMgr;
|
||||||
|
@ -129,7 +129,7 @@ function fetchRemoteResponse(protocol, options, reqData, config) {
|
|||||||
});
|
});
|
||||||
} else if (isServerDeflated && originContentLen) {
|
} else if (isServerDeflated && originContentLen) {
|
||||||
refactContentEncoding();
|
refactContentEncoding();
|
||||||
zlib.inflateRaw(serverResData, (err, buff) => {
|
zlib.inflate(serverResData, (err, buff) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
rejectParsing(err);
|
rejectParsing(err);
|
||||||
} else {
|
} else {
|
||||||
@ -689,7 +689,8 @@ class RequestHandler {
|
|||||||
|
|
||||||
reqHandlerCtx.httpsServerMgr = new HttpsServerMgr({
|
reqHandlerCtx.httpsServerMgr = new HttpsServerMgr({
|
||||||
handler: reqHandlerCtx.userRequestHandler,
|
handler: reqHandlerCtx.userRequestHandler,
|
||||||
wsHandler: reqHandlerCtx.wsHandler // websocket
|
wsHandler: reqHandlerCtx.wsHandler, // websocket
|
||||||
|
hostname: '127.0.0.1',
|
||||||
});
|
});
|
||||||
|
|
||||||
this.connectReqHandler = getConnectReqHandler.apply(reqHandlerCtx, [userRule, recorder, reqHandlerCtx.httpsServerMgr]);
|
this.connectReqHandler = getConnectReqHandler.apply(reqHandlerCtx, [userRule, recorder, reqHandlerCtx.httpsServerMgr]);
|
||||||
|
@ -59,6 +59,7 @@ class wsServer {
|
|||||||
const self = this;
|
const self = this;
|
||||||
self.config = config;
|
self.config = config;
|
||||||
self.recorder = recorder;
|
self.recorder = recorder;
|
||||||
|
self.checkBroadcastFlagTimer = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
start() {
|
start() {
|
||||||
@ -78,7 +79,7 @@ class wsServer {
|
|||||||
// the flat to indicate wheter to broadcast the record
|
// the flat to indicate wheter to broadcast the record
|
||||||
let broadcastFlag = true;
|
let broadcastFlag = true;
|
||||||
|
|
||||||
setInterval(() => {
|
self.checkBroadcastFlagTimer = setInterval(() => {
|
||||||
broadcastFlag = true;
|
broadcastFlag = true;
|
||||||
sendMultipleMessage();
|
sendMultipleMessage();
|
||||||
}, 50);
|
}, 50);
|
||||||
@ -161,6 +162,9 @@ class wsServer {
|
|||||||
|
|
||||||
closeAll() {
|
closeAll() {
|
||||||
const self = this;
|
const self = this;
|
||||||
|
if (self.checkBroadcastFlagTimer) {
|
||||||
|
clearInterval(self.checkBroadcastFlagTimer);
|
||||||
|
}
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
self.wss.close((e) => {
|
self.wss.close((e) => {
|
||||||
if (e) {
|
if (e) {
|
||||||
|
13
package.json
13
package.json
@ -40,10 +40,13 @@
|
|||||||
"ws": "^5.1.0"
|
"ws": "^5.1.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@babel/core": "^7.8.3",
|
||||||
|
"@babel/preset-env": "^7.8.3",
|
||||||
"antd": "^2.5.0",
|
"antd": "^2.5.0",
|
||||||
"autoprefixer": "^6.4.1",
|
"autoprefixer": "^6.4.1",
|
||||||
"babel-core": "^6.14.0",
|
"babel-core": "^6.14.0",
|
||||||
"babel-eslint": "^7.0.0",
|
"babel-eslint": "^7.0.0",
|
||||||
|
"babel-jest": "^24.9.0",
|
||||||
"babel-loader": "^6.2.5",
|
"babel-loader": "^6.2.5",
|
||||||
"babel-plugin-import": "^1.0.0",
|
"babel-plugin-import": "^1.0.0",
|
||||||
"babel-plugin-transform-runtime": "^6.15.0",
|
"babel-plugin-transform-runtime": "^6.15.0",
|
||||||
@ -61,11 +64,7 @@
|
|||||||
"eslint-plugin-react": "^7.4.0",
|
"eslint-plugin-react": "^7.4.0",
|
||||||
"extract-text-webpack-plugin": "^3.0.2",
|
"extract-text-webpack-plugin": "^3.0.2",
|
||||||
"file-loader": "^0.9.0",
|
"file-loader": "^0.9.0",
|
||||||
"jasmine": "^2.5.3",
|
"jest": "^24.9.0",
|
||||||
"koa": "^1.2.1",
|
|
||||||
"koa-body": "^1.4.0",
|
|
||||||
"koa-router": "^5.4.0",
|
|
||||||
"koa-send": "^3.2.0",
|
|
||||||
"less": "^2.7.1",
|
"less": "^2.7.1",
|
||||||
"less-loader": "^2.2.3",
|
"less-loader": "^2.2.3",
|
||||||
"node-simhash": "^0.1.0",
|
"node-simhash": "^0.1.0",
|
||||||
@ -86,15 +85,15 @@
|
|||||||
"svg-inline-loader": "^0.7.1",
|
"svg-inline-loader": "^0.7.1",
|
||||||
"tunnel": "^0.0.6",
|
"tunnel": "^0.0.6",
|
||||||
"url-loader": "^0.5.7",
|
"url-loader": "^0.5.7",
|
||||||
|
"urllib": "^2.34.2",
|
||||||
"webpack": "^3.10.0",
|
"webpack": "^3.10.0",
|
||||||
"worker-loader": "^0.7.1"
|
"worker-loader": "^0.7.1"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"prepublish": "npm run buildweb",
|
"prepublish": "npm run buildweb",
|
||||||
"test": "node test.js",
|
"test": "npx jest",
|
||||||
"lint": "eslint .",
|
"lint": "eslint .",
|
||||||
"testserver": "node test/server/startServer.js",
|
"testserver": "node test/server/startServer.js",
|
||||||
"testOutWeb": "jasmine test/spec_outweb/test_realweb_spec.js",
|
|
||||||
"buildweb": "NODE_ENV=production webpack --config web/webpack.config.js --colors",
|
"buildweb": "NODE_ENV=production webpack --config web/webpack.config.js --colors",
|
||||||
"webserver": "NODE_ENV=test webpack --config web/webpack.config.js --colors --watch",
|
"webserver": "NODE_ENV=test webpack --config web/webpack.config.js --colors --watch",
|
||||||
"doc:serve": "node build_scripts/prebuild-doc.js && gitbook serve ./docs-src ./docs --log debug",
|
"doc:serve": "node build_scripts/prebuild-doc.js && gitbook serve ./docs-src ./docs --log debug",
|
||||||
|
23
proxy.js
23
proxy.js
@ -14,25 +14,6 @@ const http = require('http'),
|
|||||||
wsServerMgr = require('./lib/wsServerMgr'),
|
wsServerMgr = require('./lib/wsServerMgr'),
|
||||||
ThrottleGroup = require('stream-throttle').ThrottleGroup;
|
ThrottleGroup = require('stream-throttle').ThrottleGroup;
|
||||||
|
|
||||||
// const memwatch = require('memwatch-next');
|
|
||||||
|
|
||||||
// setInterval(() => {
|
|
||||||
// console.log(process.memoryUsage());
|
|
||||||
// const rss = Math.ceil(process.memoryUsage().rss / 1000 / 1000);
|
|
||||||
// console.log('Program is using ' + rss + ' mb of Heap.');
|
|
||||||
// }, 1000);
|
|
||||||
|
|
||||||
// memwatch.on('stats', (info) => {
|
|
||||||
// console.log('gc !!');
|
|
||||||
// console.log(process.memoryUsage());
|
|
||||||
// const rss = Math.ceil(process.memoryUsage().rss / 1000 / 1000);
|
|
||||||
// console.log('GC !! Program is using ' + rss + ' mb of Heap.');
|
|
||||||
|
|
||||||
// // var heapUsed = Math.ceil(process.memoryUsage().heapUsed / 1000);
|
|
||||||
// // console.log("Program is using " + heapUsed + " kb of Heap.");
|
|
||||||
// // console.log(info);
|
|
||||||
// });
|
|
||||||
|
|
||||||
const T_TYPE_HTTP = 'http',
|
const T_TYPE_HTTP = 'http',
|
||||||
T_TYPE_HTTPS = 'https',
|
T_TYPE_HTTPS = 'https',
|
||||||
DEFAULT_TYPE = T_TYPE_HTTP;
|
DEFAULT_TYPE = T_TYPE_HTTP;
|
||||||
@ -273,6 +254,10 @@ class ProxyCore extends events.EventEmitter {
|
|||||||
cltSocket.end();
|
cltSocket.end();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.requestHandler.httpsServerMgr) {
|
||||||
|
this.requestHandler.httpsServerMgr.close();
|
||||||
|
}
|
||||||
|
|
||||||
if (this.socketPool) {
|
if (this.socketPool) {
|
||||||
for (const key in this.socketPool) {
|
for (const key in this.socketPool) {
|
||||||
this.socketPool[key].destroy();
|
this.socketPool[key].destroy();
|
||||||
|
16
test.js
16
test.js
@ -1,16 +0,0 @@
|
|||||||
const Jasmine = require('jasmine');
|
|
||||||
|
|
||||||
const jasmine = new Jasmine();
|
|
||||||
const util = require('./lib/util');
|
|
||||||
const path = require('path');
|
|
||||||
|
|
||||||
const testTmpPath = path.join(__dirname, './test/temp');
|
|
||||||
const configFilePath = path.join(__dirname, './test/jasmine.json');
|
|
||||||
// rm - rf./test / temp /
|
|
||||||
util.deleteFolderContentsRecursive(testTmpPath);
|
|
||||||
|
|
||||||
jasmine.loadConfigFile(configFilePath);
|
|
||||||
jasmine.configureDefaultReporter({
|
|
||||||
showColors: false
|
|
||||||
});
|
|
||||||
jasmine.execute();
|
|
57
test/__snapshots__/basic.spec.js.snap
Normal file
57
test/__snapshots__/basic.spec.js.snap
Normal file
File diff suppressed because one or more lines are too long
@ -1,29 +0,0 @@
|
|||||||
/*
|
|
||||||
* 用于放置所有header信息的测试数据
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
// Get 和 Post共有的header信息
|
|
||||||
/*eslint max-len: ["off"]*/
|
|
||||||
const CommonRequestHeader = {
|
|
||||||
Accept: 'application/json;charset=utf-8,text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8',
|
|
||||||
'Accept-Charset': 'utf-8',
|
|
||||||
'Accept-Encoding': 'gzip, deflate',
|
|
||||||
'Accept-Language': 'zh-CN',
|
|
||||||
'Accept-Datetime': 'Thu, 31 May 2007 20:35:00 GMT',
|
|
||||||
Authorization: 'Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==',
|
|
||||||
'Cache-Control': 'no-cache',
|
|
||||||
Connection: 'keep-alive',
|
|
||||||
Cookie: 'testCookie1=cookie1; testCookie2=cookie2',
|
|
||||||
'Content-Type': 'application/x-www-form-urlencoded',
|
|
||||||
Date: 'Tue, 15 Nov 1994 08:12:31 GMT',
|
|
||||||
Origin: 'http://localhost',
|
|
||||||
Pragma: 'no-cache',
|
|
||||||
some_thing: 'only_to_test_letter_case',
|
|
||||||
'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.116 Safari/537.36'
|
|
||||||
};
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
CommonRequestHeader
|
|
||||||
};
|
|
||||||
|
|
@ -1,3 +0,0 @@
|
|||||||
.test {
|
|
||||||
display: block;
|
|
||||||
}
|
|
Binary file not shown.
@ -1,4 +0,0 @@
|
|||||||
function test() {
|
|
||||||
console.info('This is nothing but a js file, to test the js download');
|
|
||||||
}
|
|
||||||
test();
|
|
@ -1,3 +0,0 @@
|
|||||||
{
|
|
||||||
'testkey': 'this is just a normal json file'
|
|
||||||
}
|
|
Binary file not shown.
Before Width: | Height: | Size: 103 KiB |
@ -1,14 +0,0 @@
|
|||||||
<?xml version="1.0" standalone="no"?>
|
|
||||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg">
|
|
||||||
<metadata>Copyright (C) 2016 by original authors @ fontello.com</metadata>
|
|
||||||
<defs>
|
|
||||||
<font id="fontello" horiz-adv-x="1000" >
|
|
||||||
<font-face font-family="fontello" font-weight="400" font-stretch="normal" units-per-em="1000" ascent="850" descent="-150" />
|
|
||||||
<missing-glyph horiz-adv-x="1000" />
|
|
||||||
<glyph glyph-name="glass" unicode="" d="M948 746q0-19-24-43l-353-353v-429h179q15 0 25-10t11-25-11-25-25-11h-500q-14 0-25 11t-11 25 11 25 25 10h179v429l-353 353q-24 24-24 43 0 13 10 21t21 9 24 3h786q13 0 24-3t21-9 10-21z" horiz-adv-x="1000" />
|
|
||||||
|
|
||||||
<glyph glyph-name="music" unicode="" d="M857 725v-625q0-28-19-50t-48-33-58-18-53-6-54 6-58 18-48 33-19 50 19 50 48 33 58 18 54 6q58 0 107-22v300l-429-132v-396q0-28-19-50t-48-33-58-18-53-6-54 6-58 18-48 33-19 50 19 50 48 34 58 17 54 6q58 0 107-21v539q0 17 10 32t28 20l464 142q7 3 16 3 22 0 38-16t15-38z" horiz-adv-x="857.1" />
|
|
||||||
</font>
|
|
||||||
</defs>
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 1.0 KiB |
Binary file not shown.
Binary file not shown.
Before Width: | Height: | Size: 45 KiB |
Binary file not shown.
Binary file not shown.
BIN
test/fixtures/image.png
vendored
Normal file
BIN
test/fixtures/image.png
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 7.0 KiB |
3
test/fixtures/someRule.js
vendored
Normal file
3
test/fixtures/someRule.js
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
module.exports = {
|
||||||
|
foo: 'bar',
|
||||||
|
};
|
@ -1,14 +0,0 @@
|
|||||||
{
|
|
||||||
"spec_dir": "test",
|
|
||||||
"spec_files": [
|
|
||||||
"spec_lib/*.js",
|
|
||||||
"spec_rule/*.js",
|
|
||||||
"spec_web/*.js"
|
|
||||||
],
|
|
||||||
"helpers": [
|
|
||||||
"../node_modules/babel-register/lib/node.js",
|
|
||||||
"../node_modules/babel-polyfill/dist/polyfill.js"
|
|
||||||
],
|
|
||||||
"stopSpecOnExpectationFailure": false,
|
|
||||||
"random": false
|
|
||||||
}
|
|
@ -1,35 +0,0 @@
|
|||||||
const proxyTester = require('proxy-eval'),
|
|
||||||
Buffer = require('buffer').Buffer,
|
|
||||||
express = require('express');
|
|
||||||
|
|
||||||
const app = express();
|
|
||||||
|
|
||||||
app.post('/', (req, res) => {
|
|
||||||
const bigBody = new Buffer(1024 * 1024 * 10);
|
|
||||||
res.send(bigBody); //10 mb
|
|
||||||
});
|
|
||||||
app.listen(3000);
|
|
||||||
|
|
||||||
function test() {
|
|
||||||
//test the basic availibility of proxy server
|
|
||||||
setTimeout(() => {
|
|
||||||
const testParam = {
|
|
||||||
proxy: 'http://127.0.0.1:8001/',
|
|
||||||
reqTimeout: 4500,
|
|
||||||
httpGetUrl: '',
|
|
||||||
httpPostUrl: 'http://127.0.0.1:3000/',
|
|
||||||
httpPostBody: '123',
|
|
||||||
httpsGetUrl: '',
|
|
||||||
httpsPostUrl: '',
|
|
||||||
httpsPostBody: ''
|
|
||||||
};
|
|
||||||
proxyTester.test(testParam, (results) => {
|
|
||||||
process.exit();
|
|
||||||
});
|
|
||||||
}, 1000);
|
|
||||||
}
|
|
||||||
|
|
||||||
setTimeout(() => {
|
|
||||||
test();
|
|
||||||
}, 3000);
|
|
||||||
|
|
17
test/lib/httpsServerMgr.spec.js
Normal file
17
test/lib/httpsServerMgr.spec.js
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
const httpsServerMgr = require('../../lib/httpsServerMgr');
|
||||||
|
|
||||||
|
describe('httpsServerMgr', () => {
|
||||||
|
it('get https server', async () => {
|
||||||
|
const serverMgr = new httpsServerMgr({
|
||||||
|
hostname: '127.0.0.1',
|
||||||
|
handler: () => {
|
||||||
|
console.log('this is handler');
|
||||||
|
},
|
||||||
|
wsHandler: () => {
|
||||||
|
console.log('this is handler');
|
||||||
|
},
|
||||||
|
});
|
||||||
|
await serverMgr.getSharedHttpsServer();
|
||||||
|
serverMgr.close();
|
||||||
|
});
|
||||||
|
});
|
@ -1,34 +1,25 @@
|
|||||||
/*
|
|
||||||
* test for rule replaceOption rule
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
const ruleLoader = require('../../lib/ruleLoader');
|
const ruleLoader = require('../../lib/ruleLoader');
|
||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
|
|
||||||
const localModulePath = path.join(__dirname, '../util/CommonUtil.js');
|
const localModulePath = path.join(__dirname, '../fixtures/someRule.js');
|
||||||
describe('rule loader', () => {
|
describe('ruleLoader', () => {
|
||||||
it('should successfully cache a remote file', done => {
|
it('should successfully cache a remote file', async () => {
|
||||||
ruleLoader.cacheRemoteFile('https://cdn.bootcss.com/lodash.js/4.16.4/lodash.min.js')
|
await ruleLoader.cacheRemoteFile('https://cdn.bootcss.com/lodash.js/4.16.4/lodash.min.js')
|
||||||
.then(filePath => {
|
.then(filePath => {
|
||||||
let content;
|
let content;
|
||||||
if (filePath) {
|
if (filePath) {
|
||||||
content = fs.readFileSync(filePath, { encoding: 'utf8' });
|
content = fs.readFileSync(filePath, { encoding: 'utf8' });
|
||||||
}
|
}
|
||||||
expect(content && content.length > 100).toBe(true);
|
expect(content && content.length > 100).toBe(true);
|
||||||
done();
|
});
|
||||||
})
|
|
||||||
.catch(done.fail);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should load a local module ../util/CommonUtil', done => {
|
it('should load a local module ../util/CommonUtil', async () => {
|
||||||
ruleLoader.loadLocalPath(localModulePath)
|
await ruleLoader.loadLocalPath(localModulePath)
|
||||||
.then(module => {
|
.then(module => {
|
||||||
expect(module.printLog).not.toBeUndefined();
|
expect(module.foo).not.toBeUndefined();
|
||||||
done();
|
});
|
||||||
})
|
|
||||||
.catch(done.fail);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should smart load a remote module', done => {
|
it('should smart load a remote module', done => {
|
||||||
@ -43,7 +34,7 @@ describe('rule loader', () => {
|
|||||||
it('should smart load a local module', done => {
|
it('should smart load a local module', done => {
|
||||||
ruleLoader.requireModule(localModulePath)
|
ruleLoader.requireModule(localModulePath)
|
||||||
.then(module => {
|
.then(module => {
|
||||||
expect(module.printLog).not.toBeUndefined();
|
expect(module.foo).not.toBeUndefined();
|
||||||
done();
|
done();
|
||||||
})
|
})
|
||||||
.catch(done.fail);
|
.catch(done.fail);
|
@ -1,17 +1,13 @@
|
|||||||
/*
|
|
||||||
* test for rule replaceOption rule
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
const util = require('../../lib/util');
|
const util = require('../../lib/util');
|
||||||
|
|
||||||
describe('utils', () => {
|
describe('utils', () => {
|
||||||
it('should get some free ports', done => {
|
it('getFreePort', async () => {
|
||||||
const count = 100;
|
const count = 100;
|
||||||
const tasks = [];
|
const tasks = [];
|
||||||
for (let i = 1; i <= count; i++) {
|
for (let i = 1; i <= count; i++) {
|
||||||
tasks.push(util.getFreePort());
|
tasks.push(util.getFreePort());
|
||||||
}
|
}
|
||||||
Promise.all(tasks)
|
await Promise.all(tasks)
|
||||||
.then((results) => {
|
.then((results) => {
|
||||||
// ensure ports are unique
|
// ensure ports are unique
|
||||||
const portMap = {};
|
const portMap = {};
|
||||||
@ -19,8 +15,6 @@ describe('utils', () => {
|
|||||||
portMap[portNumber] = true;
|
portMap[portNumber] = true;
|
||||||
});
|
});
|
||||||
expect(Object.keys(portMap).length).toEqual(count);
|
expect(Object.keys(portMap).length).toEqual(count);
|
||||||
done();
|
});
|
||||||
})
|
|
||||||
.catch(done.fail);
|
|
||||||
});
|
});
|
||||||
});
|
});
|
@ -1 +0,0 @@
|
|||||||
* this is a folder to save test reports *
|
|
50
test/rule/beforeDealHttpsRequest.spec.js
Normal file
50
test/rule/beforeDealHttpsRequest.spec.js
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
const fs = require('fs');
|
||||||
|
const path = require('path');
|
||||||
|
const { basicProxyRequest, proxyServerWithRule, } = require('../util.js');
|
||||||
|
|
||||||
|
const RULE_PAYLOAD = 'this is something in rule';
|
||||||
|
|
||||||
|
const rule = {
|
||||||
|
*beforeSendRequest(requestDetail) {
|
||||||
|
const requestOptions = requestDetail.requestOptions;
|
||||||
|
return {
|
||||||
|
requestOptions,
|
||||||
|
requestData: RULE_PAYLOAD,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
*beforeDealHttpsRequest(requestDetail) {
|
||||||
|
return requestDetail.host.indexOf('httpbin.org') >= 0;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
describe('Rule beforeDealHttpsRequest', () => {
|
||||||
|
let proxyServer;
|
||||||
|
let proxyPort;
|
||||||
|
let proxyHost;
|
||||||
|
|
||||||
|
beforeAll(async () => {
|
||||||
|
proxyServer = await proxyServerWithRule(rule);
|
||||||
|
proxyPort = proxyServer.proxyPort;
|
||||||
|
proxyHost = `http://localhost:${proxyPort}`;
|
||||||
|
});
|
||||||
|
|
||||||
|
afterAll(() => {
|
||||||
|
return proxyServer && proxyServer.close();
|
||||||
|
});
|
||||||
|
it('Should replace the https request body', async () => {
|
||||||
|
const url = 'https://httpbin.org/put';
|
||||||
|
const payloadStream = fs.createReadStream(path.resolve(__dirname, '../fixtures/image.png'));
|
||||||
|
const postHeaders = {
|
||||||
|
anyproxy_header: 'header_value',
|
||||||
|
};
|
||||||
|
|
||||||
|
await basicProxyRequest(proxyHost, 'PUT', url, postHeaders, {}, payloadStream).then((result) => {
|
||||||
|
const proxyRes = result.response;
|
||||||
|
const body = JSON.parse(result.body);
|
||||||
|
expect(proxyRes.statusCode).toBe(200);
|
||||||
|
expect(body.data).toEqual(RULE_PAYLOAD);
|
||||||
|
expect(body.url.indexOf('/put')).toBeGreaterThan(0);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
93
test/rule/beforeSendRequest.spec.js
Normal file
93
test/rule/beforeSendRequest.spec.js
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
const fs = require('fs');
|
||||||
|
const path = require('path');
|
||||||
|
const { basicProxyRequest, proxyServerWithRule, } = require('../util.js');
|
||||||
|
|
||||||
|
const RULE_PAYLOAD = 'this is something in rule';
|
||||||
|
const RULE_REPLACE_HEADER_KEY = 'rule_replace_header_key';
|
||||||
|
const RULE_REPLACE_HEADER_VALUE = 'rule_replace_header_value';
|
||||||
|
|
||||||
|
const rule = {
|
||||||
|
*beforeSendRequest(requestDetail) {
|
||||||
|
const reqUrl = requestDetail.url;
|
||||||
|
if (reqUrl.indexOf('/post') >= 0) {
|
||||||
|
const requestOptions = requestDetail.requestOptions;
|
||||||
|
requestOptions.path = '/put';
|
||||||
|
requestOptions.method = 'PUT';
|
||||||
|
return {
|
||||||
|
requestOptions,
|
||||||
|
requestData: RULE_PAYLOAD,
|
||||||
|
};
|
||||||
|
} else if (reqUrl.indexOf('/status/302') >= 0) {
|
||||||
|
return {
|
||||||
|
response: {
|
||||||
|
statusCode: 404,
|
||||||
|
header: {
|
||||||
|
[RULE_REPLACE_HEADER_KEY]: RULE_REPLACE_HEADER_VALUE,
|
||||||
|
'content-type': 'plain/text',
|
||||||
|
},
|
||||||
|
body: RULE_PAYLOAD
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} else if (reqUrl.indexOf('/should_be_replaced') >= 0) {
|
||||||
|
const requestOptions = requestDetail.requestOptions;
|
||||||
|
requestOptions.hostname = 'httpbin.org';
|
||||||
|
requestOptions.path = '/status/302';
|
||||||
|
requestOptions.port = '443';
|
||||||
|
return {
|
||||||
|
protocol: 'https',
|
||||||
|
requestOptions,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
describe('Rule replaceRequestData', () => {
|
||||||
|
let proxyServer;
|
||||||
|
let proxyPort;
|
||||||
|
let proxyHost;
|
||||||
|
|
||||||
|
beforeAll(async () => {
|
||||||
|
proxyServer = await proxyServerWithRule(rule);
|
||||||
|
proxyPort = proxyServer.proxyPort;
|
||||||
|
proxyHost = `http://localhost:${proxyPort}`;
|
||||||
|
});
|
||||||
|
|
||||||
|
afterAll(() => {
|
||||||
|
return proxyServer && proxyServer.close();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should replace the request data in proxy if the assertion is true', async () => {
|
||||||
|
const url = 'http://httpbin.org/post';
|
||||||
|
const payloadStream = fs.createReadStream(path.resolve(__dirname, '../fixtures/image.png'));
|
||||||
|
const postHeaders = {
|
||||||
|
anyproxy_header: 'header_value',
|
||||||
|
};
|
||||||
|
|
||||||
|
await basicProxyRequest(proxyHost, 'POST', url, postHeaders, {}, payloadStream).then((result) => {
|
||||||
|
const proxyRes = result.response;
|
||||||
|
const body = JSON.parse(result.body);
|
||||||
|
expect(proxyRes.statusCode).toBe(200);
|
||||||
|
expect(body.data).toEqual(RULE_PAYLOAD);
|
||||||
|
expect(body.url.indexOf('/put')).toBeGreaterThan(0);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should respond content specified in rule', async () => {
|
||||||
|
const url = 'http://httpbin.org/status/302';
|
||||||
|
await basicProxyRequest(proxyHost, 'GET', url).then((result) => {
|
||||||
|
const proxyRes = result.response;
|
||||||
|
const body = result.body;
|
||||||
|
expect(body).toBe(RULE_PAYLOAD);
|
||||||
|
expect(proxyRes.statusCode).toBe(404);
|
||||||
|
expect(proxyRes.headers[RULE_REPLACE_HEADER_KEY]).toBe(RULE_REPLACE_HEADER_VALUE);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should replace protocol and url', async () => {
|
||||||
|
const url = 'http://domain_not_exists.anyproxy.io/should_be_replaced';
|
||||||
|
await basicProxyRequest(proxyHost, 'GET', url).then((result) => {
|
||||||
|
const proxyRes = result.response;
|
||||||
|
expect(proxyRes.statusCode).toBe(302);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
45
test/rule/beforeSendResponse.js
Normal file
45
test/rule/beforeSendResponse.js
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
const { basicProxyRequest, proxyServerWithRule, } = require('../util.js');
|
||||||
|
|
||||||
|
const RULE_REPLACE_HEADER_KEY = 'rule_replace_header_key';
|
||||||
|
const RULE_REPLACE_HEADER_VALUE = 'rule_replace_header_value';
|
||||||
|
const RULE_REPLACE_BODY = 'RULE_REPLACE_BODY';
|
||||||
|
const rule = {
|
||||||
|
*beforeSendResponse(requestDetail, responseDetail) {
|
||||||
|
if (requestDetail.url.indexOf('/uuid') >= 0) {
|
||||||
|
const newResponse = responseDetail.response;
|
||||||
|
newResponse.header[RULE_REPLACE_HEADER_KEY] = RULE_REPLACE_HEADER_VALUE;
|
||||||
|
newResponse.body = RULE_REPLACE_BODY;
|
||||||
|
newResponse.statusCode = 502;
|
||||||
|
return {
|
||||||
|
response: newResponse,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
describe('Rule replaceResponseData', () => {
|
||||||
|
let proxyServer;
|
||||||
|
let proxyPort;
|
||||||
|
let proxyHost;
|
||||||
|
|
||||||
|
beforeAll(async () => {
|
||||||
|
proxyServer = await proxyServerWithRule(rule);
|
||||||
|
proxyPort = proxyServer.proxyPort;
|
||||||
|
proxyHost = `http://localhost:${proxyPort}`;
|
||||||
|
});
|
||||||
|
|
||||||
|
afterAll(() => {
|
||||||
|
return proxyServer && proxyServer.close();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Should replace the header and body', async () => {
|
||||||
|
const url = 'http://httpbin.org/uuid';
|
||||||
|
await basicProxyRequest(proxyHost, 'GET', url).then((result) => {
|
||||||
|
const proxyRes = result.response;
|
||||||
|
const body = result.body;
|
||||||
|
expect(proxyRes.statusCode).toBe(502);
|
||||||
|
expect(proxyRes.headers[RULE_REPLACE_HEADER_KEY]).toBe(RULE_REPLACE_HEADER_VALUE);
|
||||||
|
expect(body).toBe(RULE_REPLACE_BODY);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
60
test/rule/onError.spec.js
Normal file
60
test/rule/onError.spec.js
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
const { basicProxyRequest, proxyServerWithRule, } = require('../util.js');
|
||||||
|
|
||||||
|
const jestMockErrorFn = jest.fn();
|
||||||
|
const jestMockConnectErrorFn = jest.fn();
|
||||||
|
|
||||||
|
const ERROR_PAGE_IN_RULE = 'this is my error page';
|
||||||
|
const rule = {
|
||||||
|
onConnectError: jestMockConnectErrorFn,
|
||||||
|
*onError(requestDetail, error) {
|
||||||
|
jestMockErrorFn(requestDetail, error);
|
||||||
|
return {
|
||||||
|
response: {
|
||||||
|
statusCode: '200',
|
||||||
|
header: {},
|
||||||
|
body: ERROR_PAGE_IN_RULE,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
},
|
||||||
|
*beforeDealHttpsRequest(requestDetail) {
|
||||||
|
return requestDetail.host.indexOf('intercept') === 0;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
describe('Rule replaceResponseData', () => {
|
||||||
|
let proxyServer;
|
||||||
|
let proxyPort;
|
||||||
|
let proxyHost;
|
||||||
|
|
||||||
|
beforeAll(async () => {
|
||||||
|
proxyServer = await proxyServerWithRule(rule);
|
||||||
|
proxyPort = proxyServer.proxyPort;
|
||||||
|
proxyHost = `http://localhost:${proxyPort}`;
|
||||||
|
});
|
||||||
|
|
||||||
|
afterAll(() => {
|
||||||
|
return proxyServer && proxyServer.close();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should get error', async () => {
|
||||||
|
const url = 'https://intercept.anyproxy_not_exists.io/some_path';
|
||||||
|
const result = await basicProxyRequest(proxyHost, 'GET', url);
|
||||||
|
const proxyRes = result.response;
|
||||||
|
const body = result.body;
|
||||||
|
expect(proxyRes.statusCode).toBe(200);
|
||||||
|
expect(body).toBe(ERROR_PAGE_IN_RULE);
|
||||||
|
expect(jestMockErrorFn.mock.calls.length).toBe(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should get connec error', async () => {
|
||||||
|
const url = 'https://anyproxy_not_exists.io/do_not_intercept';
|
||||||
|
let e;
|
||||||
|
try {
|
||||||
|
await basicProxyRequest(proxyHost, 'GET', url);
|
||||||
|
} catch (err) {
|
||||||
|
e = err;
|
||||||
|
}
|
||||||
|
expect(e).not.toBeUndefined();
|
||||||
|
expect(jestMockConnectErrorFn.mock.calls.length).toBe(1);
|
||||||
|
});
|
||||||
|
});
|
@ -1,377 +0,0 @@
|
|||||||
const Koa = require('koa');
|
|
||||||
const KoaRouter = require('koa-router');
|
|
||||||
const koaBody = require('koa-body');
|
|
||||||
const send = require('koa-send');
|
|
||||||
const path = require('path');
|
|
||||||
const https = require('https');
|
|
||||||
const certMgr = require('../../lib/certMgr');
|
|
||||||
const fs = require('fs');
|
|
||||||
const nurl = require('url');
|
|
||||||
const color = require('colorful');
|
|
||||||
const WebSocketServer = require('ws').Server;
|
|
||||||
const tls = require('tls');
|
|
||||||
const crypto = require('crypto');
|
|
||||||
const stream = require('stream');
|
|
||||||
const brotli = require('brotli');
|
|
||||||
const zlib = require('zlib');
|
|
||||||
|
|
||||||
const createSecureContext = tls.createSecureContext || crypto.createSecureContext;
|
|
||||||
|
|
||||||
const DEFAULT_PORT = 3000;
|
|
||||||
const HTTPS_PORT = 3001;
|
|
||||||
const HTTPS_PORT2 = 3002; // start multiple https server
|
|
||||||
const UPLOAD_DIR = path.resolve(__dirname, '../temp');
|
|
||||||
const PROXY_KEY_PREFIX = 'proxy-';
|
|
||||||
|
|
||||||
function SNICertCallback(serverName, SNICallback) {
|
|
||||||
certMgr.getCertificate(serverName, (err, key, crt) => {
|
|
||||||
if (err) {
|
|
||||||
console.error('error happend in sni callback', err);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const ctx = createSecureContext({
|
|
||||||
key,
|
|
||||||
cert: crt
|
|
||||||
});
|
|
||||||
|
|
||||||
SNICallback(null, ctx);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function KoaServer() {
|
|
||||||
this.httpServer = null;
|
|
||||||
this.httpsServer = null;
|
|
||||||
this.requestRecordMap = {}; // store all request data to the map
|
|
||||||
const self = this;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* log the request info, write as
|
|
||||||
*/
|
|
||||||
this.logRequest = function *(next) {
|
|
||||||
const headers = this.request.headers;
|
|
||||||
let key = this.request.protocol + '://' + this.request.host + nurl.parse(this.request.url).pathname; // remove param to get clean key
|
|
||||||
|
|
||||||
// take proxy data with 'proxy-' + url
|
|
||||||
if (headers['via-proxy'] === 'true') {
|
|
||||||
key = PROXY_KEY_PREFIX + key;
|
|
||||||
}
|
|
||||||
|
|
||||||
printLog('log request with key :' + key);
|
|
||||||
let body = this.request.body;
|
|
||||||
body = typeof body === 'object' ? JSON.stringify(body) : body;
|
|
||||||
|
|
||||||
self.requestRecordMap[key] = {
|
|
||||||
headers,
|
|
||||||
body
|
|
||||||
};
|
|
||||||
yield next;
|
|
||||||
};
|
|
||||||
|
|
||||||
this.logWsRequest = function (wsReq) {
|
|
||||||
const headers = wsReq.headers;
|
|
||||||
const host = headers.host;
|
|
||||||
const isEncript = wsReq.connection && wsReq.connection.encrypted;
|
|
||||||
const protocol = isEncript ? 'wss' : 'ws';
|
|
||||||
let key = `${protocol}://${host}${wsReq.url}`;
|
|
||||||
// take proxy data with 'proxy-' + url
|
|
||||||
if (headers['via-proxy'] === 'true') {
|
|
||||||
key = PROXY_KEY_PREFIX + key;
|
|
||||||
}
|
|
||||||
|
|
||||||
self.requestRecordMap[key] = {
|
|
||||||
headers: wsReq.headers,
|
|
||||||
body: '',
|
|
||||||
messages: [],
|
|
||||||
}
|
|
||||||
|
|
||||||
return self.requestRecordMap[key];
|
|
||||||
};
|
|
||||||
|
|
||||||
this.start();
|
|
||||||
}
|
|
||||||
|
|
||||||
KoaServer.prototype.constructRouter = function () {
|
|
||||||
const router = KoaRouter();
|
|
||||||
router.post('/test/getuser', koaBody(), this.logRequest, function *(next) {
|
|
||||||
printLog('requesting post /test/getuser');
|
|
||||||
this.response.set('reqbody', JSON.stringify(this.request.body));
|
|
||||||
this.response.body = 'body_post_getuser';
|
|
||||||
});
|
|
||||||
|
|
||||||
router.get('/test', this.logRequest, function *(next) {
|
|
||||||
printLog('request in get: ' + JSON.stringify(this.request));
|
|
||||||
this.cookies.set('a1', 'a1value');
|
|
||||||
this.cookies.set('a2', 'a2value');
|
|
||||||
this.cookies.set('a3', 'a3value');
|
|
||||||
this.response.set('header1', 'cookie2=headervalue2');
|
|
||||||
|
|
||||||
this.response.body = 'something';
|
|
||||||
this.response.__req = this.request;
|
|
||||||
printLog('response in get:' + JSON.stringify(this.response));
|
|
||||||
});
|
|
||||||
|
|
||||||
router.get('/test/uselocal', this.logRequest, function *(next) {
|
|
||||||
printLog('request in get local:' + JSON.stringify(this.request));
|
|
||||||
this.response.body = 'something should be in local';
|
|
||||||
// this.response.__req = this.request;
|
|
||||||
printLog('response in get:' + JSON.stringify(this.response));
|
|
||||||
});
|
|
||||||
|
|
||||||
['png', 'webp', 'json', 'js', 'css', 'ttf', 'eot', 'svg', 'woff', 'woff2'].forEach(item => {
|
|
||||||
router.get(`/test/download/${item}`, this.logRequest, function *(next) {
|
|
||||||
yield send(this, `./data/test.${item}`, {
|
|
||||||
root: path.resolve(__dirname, '../')
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
router.get('/test/response/304', this.logRequest, function *(next) {
|
|
||||||
this.response.set('Content-Encoding', 'gzip');
|
|
||||||
this.status = 304;
|
|
||||||
});
|
|
||||||
|
|
||||||
router.get('/test/response/303', function *(next) {
|
|
||||||
printLog('now to redirect 303');
|
|
||||||
this.redirect('/test');
|
|
||||||
this.status = 303;
|
|
||||||
});
|
|
||||||
|
|
||||||
router.get('/test/response/302', function *(next) {
|
|
||||||
printLog('now to redirect 302');
|
|
||||||
this.redirect('/test');
|
|
||||||
});
|
|
||||||
|
|
||||||
router.get('/test/response/301', function *(next) {
|
|
||||||
printLog('now to redirect permanently');
|
|
||||||
this.redirect('/test');
|
|
||||||
this.status = 301;
|
|
||||||
});
|
|
||||||
|
|
||||||
const onFileBegin = function (name, file) {
|
|
||||||
if (!fs.existsSync(UPLOAD_DIR)) {
|
|
||||||
try {
|
|
||||||
fs.mkdirSync(UPLOAD_DIR, '0777');
|
|
||||||
} catch (e) {
|
|
||||||
console.log(e);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
file.name = 'test_upload_' + Date.now() + '.png';
|
|
||||||
const folder = path.dirname(file.path);
|
|
||||||
file.path = path.join(folder, file.name);
|
|
||||||
};
|
|
||||||
|
|
||||||
router.post('/test/upload/png',
|
|
||||||
this.logRequest,
|
|
||||||
koaBody({
|
|
||||||
multipart: true,
|
|
||||||
formidable: {
|
|
||||||
uploadDir: UPLOAD_DIR,
|
|
||||||
onFileBegin
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
function *(next) {
|
|
||||||
const file = this.request.body.files.file;
|
|
||||||
this.response.set('reqbody', JSON.stringify(this.request.body.fields));
|
|
||||||
this.response.body = file.path;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
router.put('/test/upload/putpng',
|
|
||||||
this.logRequest,
|
|
||||||
koaBody({
|
|
||||||
multipart: true,
|
|
||||||
formidable: {
|
|
||||||
uploadDir: UPLOAD_DIR,
|
|
||||||
onFileBegin
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
function *(next) {
|
|
||||||
const file = this.request.body.files.file;
|
|
||||||
this.response.body = file.path;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
router.put('/test/put', koaBody(), this.logRequest, function *(next) {
|
|
||||||
printLog('requesting put /test/put' + JSON.stringify(this.request));
|
|
||||||
this.response.body = 'something in put';
|
|
||||||
});
|
|
||||||
|
|
||||||
router.delete('/test/delete/:id', this.logRequest, function *(next) {
|
|
||||||
printLog('requesting delete /test/delete/:id' + JSON.stringify(this.params));
|
|
||||||
this.response.body = 'something in delete';
|
|
||||||
});
|
|
||||||
|
|
||||||
router.head('/test/head', this.logRequest, function *(next) {
|
|
||||||
printLog('requesting head /test/head');
|
|
||||||
this.response.body = ''; // the body will not be passed to response, in HEAD request
|
|
||||||
this.response.set('reqBody', 'head_request_contains_no_resbody');
|
|
||||||
});
|
|
||||||
|
|
||||||
router.options('/test/options', this.logRequest, function *(next) {
|
|
||||||
printLog('requesting options /test/options');
|
|
||||||
this.response.body = 'could_be_empty';
|
|
||||||
this.response.set('Allow', 'GET, HEAD, POST, OPTIONS');
|
|
||||||
});
|
|
||||||
|
|
||||||
router.get('/test/should_not_replace_option', this.logRequest, function *(next) {
|
|
||||||
this.response.body = 'the_option_that_not_be_replaced';
|
|
||||||
});
|
|
||||||
|
|
||||||
router.get('/test/should_replace_option', this.logRequest, function *(next) {
|
|
||||||
this.response.body = 'the_request_that_has_not_be_replaced';
|
|
||||||
});
|
|
||||||
|
|
||||||
router.get('/test/new_replace_option', this.logRequest, function *(next) {
|
|
||||||
this.response.body = 'the_new_replaced_option_page_content';
|
|
||||||
});
|
|
||||||
|
|
||||||
router.get('/test/normal_request1', this.logRequest, koaBody(), function *(next) {
|
|
||||||
printLog('requesting get /test/normal_request1');
|
|
||||||
this.response.body = 'body_normal_request1';
|
|
||||||
});
|
|
||||||
|
|
||||||
router.get('/test/normal_request2', this.logRequest, koaBody(), function *(next) {
|
|
||||||
printLog('requesting get /test/normal_request2');
|
|
||||||
this.response.body = 'body_normal_request2';
|
|
||||||
});
|
|
||||||
|
|
||||||
router.post('/test/normal_post_request1', koaBody(), this.logRequest, function *(next) {
|
|
||||||
printLog('requesting post /test/normal_post_request1');
|
|
||||||
this.response.body = 'body_normal_post_request1';
|
|
||||||
});
|
|
||||||
|
|
||||||
router.get('/big_response', this.logRequest, function *(next) {
|
|
||||||
const buf = new Buffer(1 * 1024 * 1024 * 1024); // 1GB
|
|
||||||
buf.fill(1);
|
|
||||||
printLog('request in get big response of 1GB');
|
|
||||||
this.response.type = 'application/octet-stream';
|
|
||||||
this.response.body = buf;
|
|
||||||
});
|
|
||||||
|
|
||||||
router.get('/test/brotli', this.logRequest, function *(next) {
|
|
||||||
this.status = 200;
|
|
||||||
this.response.set('Content-Encoding', 'br');
|
|
||||||
this.response.set('Content-Type', 'application/json');
|
|
||||||
const buf = new Buffer('{"type":"brotli","message":"This is a brotli encoding response, but it need to be a long string or the brotli module\'s compress result will be null"}');
|
|
||||||
this.response.body = Buffer.from(brotli.compress(buf));
|
|
||||||
});
|
|
||||||
|
|
||||||
router.get('/test/gzip', this.logRequest, function *(next) {
|
|
||||||
this.status = 200;
|
|
||||||
this.response.set('Content-Encoding', 'gzip');
|
|
||||||
this.response.set('Content-Type', 'application/json');
|
|
||||||
const bufStream = new stream.PassThrough();
|
|
||||||
bufStream.end(new Buffer('{"type":"gzip","message":"This is a gzip encoding response"}'));
|
|
||||||
this.response.body = bufStream.pipe(zlib.createGzip());
|
|
||||||
});
|
|
||||||
|
|
||||||
router.get('/test/deflate', this.logRequest, function *(next) {
|
|
||||||
this.status = 200;
|
|
||||||
this.response.set('Content-Encoding', 'deflate');
|
|
||||||
this.response.set('Content-Type', 'application/json');
|
|
||||||
this.response.body = zlib.deflateRawSync('{"type":"deflate","message":"This is a deflate encoding response"}');
|
|
||||||
});
|
|
||||||
|
|
||||||
return router;
|
|
||||||
};
|
|
||||||
|
|
||||||
KoaServer.prototype.createWsServer = function (httpServer) {
|
|
||||||
const wsServer = new WebSocketServer({
|
|
||||||
server: httpServer,
|
|
||||||
path: '/test/socket'
|
|
||||||
});
|
|
||||||
wsServer.on('connection', (ws, wsReq) => {
|
|
||||||
const logRecord = this.logWsRequest(wsReq);
|
|
||||||
|
|
||||||
ws.send(JSON.stringify({
|
|
||||||
type: 'initial',
|
|
||||||
content: 'default message'
|
|
||||||
}));
|
|
||||||
|
|
||||||
ws.on('message', message => {
|
|
||||||
printLog('message from request socket: ' + message);
|
|
||||||
this.handleRecievedMessage(ws, message);
|
|
||||||
logRecord.messages.push(message);
|
|
||||||
});
|
|
||||||
|
|
||||||
ws.on('error', e => console.error('error happened in websocket server', e));
|
|
||||||
})
|
|
||||||
};
|
|
||||||
|
|
||||||
KoaServer.prototype.getRequestRecord = function (key) {
|
|
||||||
return this.requestRecordMap[key] || null;
|
|
||||||
};
|
|
||||||
|
|
||||||
KoaServer.prototype.getProxyRequestRecord = function (key) {
|
|
||||||
key = PROXY_KEY_PREFIX + key;
|
|
||||||
return this.requestRecordMap[key] || null;
|
|
||||||
};
|
|
||||||
|
|
||||||
KoaServer.prototype.handleRecievedMessage = function (ws, message) {
|
|
||||||
const newMessage = {
|
|
||||||
type: 'onMessage',
|
|
||||||
content: message
|
|
||||||
};
|
|
||||||
ws.send(JSON.stringify(newMessage));
|
|
||||||
};
|
|
||||||
|
|
||||||
KoaServer.prototype.start = function () {
|
|
||||||
printLog('Starting the server...');
|
|
||||||
const router = this.constructRouter();
|
|
||||||
const self = this;
|
|
||||||
const app = Koa();
|
|
||||||
|
|
||||||
app.use(router.routes());
|
|
||||||
this.httpServer = app.listen(DEFAULT_PORT);
|
|
||||||
this.createWsServer(this.httpServer);
|
|
||||||
|
|
||||||
printLog('HTTP is now listening on port :' + DEFAULT_PORT);
|
|
||||||
|
|
||||||
certMgr.getCertificate('localhost', (error, keyContent, crtContent) => {
|
|
||||||
if (error) {
|
|
||||||
console.error('failed to create https server:', error);
|
|
||||||
} else {
|
|
||||||
self.httpsServer = https.createServer({
|
|
||||||
SNICallback: SNICertCallback,
|
|
||||||
key: keyContent,
|
|
||||||
cert: crtContent
|
|
||||||
}, app.callback());
|
|
||||||
|
|
||||||
// create wss server
|
|
||||||
this.createWsServer(self.httpsServer);
|
|
||||||
|
|
||||||
self.httpsServer.listen(HTTPS_PORT);
|
|
||||||
|
|
||||||
self.httpsServer2 = https.createServer({
|
|
||||||
key: keyContent,
|
|
||||||
cert: crtContent
|
|
||||||
}, app.callback());
|
|
||||||
|
|
||||||
self.httpsServer2.listen(HTTPS_PORT2);
|
|
||||||
|
|
||||||
printLog('HTTPS is now listening on port :' + HTTPS_PORT);
|
|
||||||
|
|
||||||
printLog('Server started successfully');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return this;
|
|
||||||
};
|
|
||||||
|
|
||||||
KoaServer.prototype.close = function () {
|
|
||||||
printLog('Closing server now...');
|
|
||||||
this.httpServer && this.httpServer.close();
|
|
||||||
this.httpsServer && this.httpsServer.close();
|
|
||||||
this.httpsServer2 && this.httpsServer2.close();
|
|
||||||
this.requestRecordMap = {};
|
|
||||||
printLog('Server closed successfully');
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
function printLog(content) {
|
|
||||||
console.log(color.cyan('[SERVER LOG]: ' + content));
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = KoaServer;
|
|
@ -1,3 +0,0 @@
|
|||||||
const Server = require('./server.js');
|
|
||||||
|
|
||||||
new Server();
|
|
@ -1,123 +0,0 @@
|
|||||||
/*
|
|
||||||
* test for rule replaceOption rule
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
const AnyProxy = require('../../proxy');
|
|
||||||
const {
|
|
||||||
proxyGet,
|
|
||||||
directGet,
|
|
||||||
generateUrl,
|
|
||||||
} = require('../util/HttpUtil.js');
|
|
||||||
const Server = require('../server/server.js');
|
|
||||||
|
|
||||||
describe('AnyProxy.proxyServer basic test', () => {
|
|
||||||
it('should successfully start a proxy server', done => {
|
|
||||||
const options = {
|
|
||||||
port: 8001,
|
|
||||||
rule: null,
|
|
||||||
webInterface: {
|
|
||||||
enable: true,
|
|
||||||
webPort: 8002
|
|
||||||
},
|
|
||||||
throttle: 10000,
|
|
||||||
forceProxyHttps: false,
|
|
||||||
silent: false
|
|
||||||
};
|
|
||||||
const proxyServer = new AnyProxy.ProxyServer(options);
|
|
||||||
proxyServer.on('ready', () => {
|
|
||||||
proxyServer.close();
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
proxyServer.on('error', done.fail);
|
|
||||||
proxyServer.start();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('AnyProxy.proxyServer high order test', () => {
|
|
||||||
let proxyServer;
|
|
||||||
let serverInstance;
|
|
||||||
beforeAll(done => {
|
|
||||||
// jasmine.DEFAULT_TIMEOUT_INTERVAL = 5000;
|
|
||||||
serverInstance = new Server();
|
|
||||||
|
|
||||||
const options = {
|
|
||||||
port: 8001,
|
|
||||||
rule: null,
|
|
||||||
webInterface: {
|
|
||||||
enable: true,
|
|
||||||
webPort: 8002,
|
|
||||||
},
|
|
||||||
throttle: 10000,
|
|
||||||
forceProxyHttps: false,
|
|
||||||
silent: false
|
|
||||||
};
|
|
||||||
proxyServer = new AnyProxy.ProxyServer(options);
|
|
||||||
proxyServer.on('ready', done);
|
|
||||||
proxyServer.start();
|
|
||||||
});
|
|
||||||
|
|
||||||
afterAll(() => {
|
|
||||||
proxyServer && proxyServer.close();
|
|
||||||
serverInstance && serverInstance.close();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should work as expected for domain host', done => {
|
|
||||||
// test if proxy server works
|
|
||||||
proxyGet('https://www.tmall.com', {}, {})
|
|
||||||
.then(res => {
|
|
||||||
expect(res && res.statusCode && res.statusCode === 200 && res.body.length > 300).toBe(true);
|
|
||||||
done();
|
|
||||||
})
|
|
||||||
.catch(done);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should work as expected for ip host', done => {
|
|
||||||
// test if proxy server works
|
|
||||||
proxyGet(generateUrl('https', '/test'), {}, {})
|
|
||||||
.then(res => {
|
|
||||||
expect(res && res.statusCode && res.statusCode === 200).toBe(true);
|
|
||||||
done();
|
|
||||||
})
|
|
||||||
.catch(done);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should start webinterface correctly', done => {
|
|
||||||
// test web interface
|
|
||||||
directGet('http://127.0.0.1:8002', {}, {})
|
|
||||||
.then(res => {
|
|
||||||
expect(res && res.statusCode && res.statusCode === 200 && res.body.length > 300).toBe(true);
|
|
||||||
done();
|
|
||||||
})
|
|
||||||
.catch(done);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should deal well with the gzip encoding response', done => {
|
|
||||||
proxyGet(generateUrl('https', '/test/gzip'), {}, {})
|
|
||||||
.then(res => {
|
|
||||||
expect(res && res.statusCode === 200).toBe(true);
|
|
||||||
expect(JSON.parse(res.body).type).toBe('gzip');
|
|
||||||
done();
|
|
||||||
})
|
|
||||||
.catch(done);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should deal well with the deflate encoding response', done => {
|
|
||||||
proxyGet(generateUrl('https', '/test/deflate'), {}, {})
|
|
||||||
.then(res => {
|
|
||||||
expect(res && res.statusCode === 200).toBe(true);
|
|
||||||
expect(JSON.parse(res.body).type).toBe('deflate');
|
|
||||||
done();
|
|
||||||
})
|
|
||||||
.catch(done);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should deal well with the brotli encoding response', done => {
|
|
||||||
proxyGet(generateUrl('https', '/test/brotli'), {}, {})
|
|
||||||
.then(res => {
|
|
||||||
expect(res && res.statusCode === 200).toBe(true);
|
|
||||||
expect(JSON.parse(res.body).type).toBe('brotli');
|
|
||||||
done();
|
|
||||||
})
|
|
||||||
.catch(done);
|
|
||||||
});
|
|
||||||
});
|
|
@ -1,30 +0,0 @@
|
|||||||
const WebInterface = require('../../lib/webInterface.js');
|
|
||||||
const Recorder = require('../../lib/recorder');
|
|
||||||
const { directGet } = require('../util/HttpUtil.js');
|
|
||||||
|
|
||||||
describe('WebInterface server', () => {
|
|
||||||
let webServer = null;
|
|
||||||
const webHost = 'http://127.0.0.1:8002'
|
|
||||||
|
|
||||||
beforeAll(() => {
|
|
||||||
const recorder = new Recorder();
|
|
||||||
webServer = new WebInterface({
|
|
||||||
webPort: 8002,
|
|
||||||
}, recorder);
|
|
||||||
});
|
|
||||||
|
|
||||||
afterAll(() => {
|
|
||||||
webServer.close();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should response qrcode string in /getQrCode', done => {
|
|
||||||
directGet(`${webHost}/api/getQrCode`)
|
|
||||||
.then(res => {
|
|
||||||
const body = JSON.parse(res.body);
|
|
||||||
expect(body.qrImgDom).toMatch('<img src="data:image/');
|
|
||||||
expect(body.url).toBe(`${webHost}/downloadCrt`);
|
|
||||||
done();
|
|
||||||
})
|
|
||||||
.catch(done);
|
|
||||||
});
|
|
||||||
});
|
|
@ -1,147 +0,0 @@
|
|||||||
|
|
||||||
/**
|
|
||||||
* use phantomjs to capture requests in real websites, then compare the directly-connected response with those through AnyProxy
|
|
||||||
*/
|
|
||||||
const fs = require('fs');
|
|
||||||
const ProxyServerUtil = require('../util/ProxyServerUtil.js');
|
|
||||||
const HttpUtil = require('../util/HttpUtil.js');
|
|
||||||
const path = require('path');
|
|
||||||
const { printLog, printError, printHilite, stringSimilarity } = require('../util/CommonUtil.js');
|
|
||||||
|
|
||||||
const reportPath = path.join(__dirname, '../report/');
|
|
||||||
|
|
||||||
const testUrls = ['https://www.taobao.com', 'https://www.baidu.com', 'https://www.tmall.com'];
|
|
||||||
|
|
||||||
let direcrtResponseSampleA = [];
|
|
||||||
let direcrtResponseSampleB = [];
|
|
||||||
let proxyResponse = [];
|
|
||||||
|
|
||||||
function test(url, requestHeaders = {}) {
|
|
||||||
describe('Test requests in real broswer', () => {
|
|
||||||
let proxyServer;
|
|
||||||
|
|
||||||
beforeAll((done) => {
|
|
||||||
jasmine.DEFAULT_TIMEOUT_INTERVAL = 200000;
|
|
||||||
printLog('Start server for ' + url);
|
|
||||||
|
|
||||||
proxyServer = ProxyServerUtil.defaultProxyServer();
|
|
||||||
setTimeout(() => {
|
|
||||||
done();
|
|
||||||
}, 2000);
|
|
||||||
});
|
|
||||||
|
|
||||||
afterAll(() => {
|
|
||||||
proxyServer && proxyServer.close();
|
|
||||||
printLog('Closed server for ' + url);
|
|
||||||
});
|
|
||||||
|
|
||||||
it(`Request towards ${url}`, (done) => {
|
|
||||||
HttpUtil.getRequestListFromPage(url).then((arr) => {
|
|
||||||
const directPromisesA = [];
|
|
||||||
const directPromisesB = [];
|
|
||||||
const proxyPromises = [];
|
|
||||||
arr.forEach((data, i) => {
|
|
||||||
const requestPath = data.url;
|
|
||||||
const headers = data.headers;
|
|
||||||
const method = data.method;
|
|
||||||
const params = data.method === 'POST' ? JSON.parse(data.postData) : {};
|
|
||||||
if (HttpUtil.isSupportedProtocol(requestPath)) {
|
|
||||||
directPromisesA.push(HttpUtil.directRequest(method, requestPath, params, headers));
|
|
||||||
directPromisesB.push(HttpUtil.directRequest(method, requestPath, params, headers));
|
|
||||||
proxyPromises.push(HttpUtil.proxyRequest(method, requestPath, params, headers));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
Promise.all(directPromisesA).then(responseArr => { direcrtResponseSampleA = responseArr }).then(() => {
|
|
||||||
Promise.all(directPromisesB).then(responseArr => { direcrtResponseSampleB = responseArr }).then(() => {
|
|
||||||
Promise.all(proxyPromises).then(responseArr => { proxyResponse = responseArr }).then(() => {
|
|
||||||
showResponseResult();
|
|
||||||
const { compareResult: TESTRESULT, errRecord } = compareResponses(url);
|
|
||||||
const reportFile = dealLogFile(errRecord, url, () => {
|
|
||||||
printHilite('====== COMPARE RESULT: ' + TESTRESULT.toString().toUpperCase() + ' ======');
|
|
||||||
!TESTRESULT && printHilite(`Check more details in ${reportFile}`);
|
|
||||||
// expect(TESTRESULT).toBe(true);
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.catch((err) => {
|
|
||||||
printError(err);
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.catch((err) => {
|
|
||||||
printError(err);
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.catch((err) => {
|
|
||||||
printError(err);
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
function compareResponses(curUrl) {
|
|
||||||
const errRecord = [];
|
|
||||||
if (direcrtResponseSampleA.length !== direcrtResponseSampleB.length || direcrtResponseSampleA.length !== proxyResponse.length) {
|
|
||||||
printError('compare fail: length not match');
|
|
||||||
return {
|
|
||||||
compareResult: false,
|
|
||||||
errRecord
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (let i = 0; i < proxyResponse.length; i++) {
|
|
||||||
const direcrtResponseInfoA = direcrtResponseSampleA[i];
|
|
||||||
const direcrtResponseInfoB = direcrtResponseSampleB[i];
|
|
||||||
const proxyResponseInfo = proxyResponse[i];
|
|
||||||
const { similarity: similarity_direct } = stringSimilarity(stringify(direcrtResponseInfoA.body), stringify(direcrtResponseInfoB.body));
|
|
||||||
const { similarity: similarity_proxy } = stringSimilarity(stringify(direcrtResponseInfoA.body), stringify(proxyResponseInfo.body));
|
|
||||||
if (similarity_direct !== similarity_proxy) {
|
|
||||||
let LogText = `Similarity from ${proxyResponseInfo.request.href} between direct samples and proxy is not equal : ${similarity_direct} | ${similarity_proxy}\n`;
|
|
||||||
printError(LogText);
|
|
||||||
LogText += `\n${stringify(direcrtResponseInfoA.body)}`;
|
|
||||||
LogText += '\n=============================================\n';
|
|
||||||
LogText += `${stringify(direcrtResponseInfoB.body)}`;
|
|
||||||
LogText += '\n=============================================\n';
|
|
||||||
LogText += `${stringify(proxyResponseInfo.body)}\n\n`;
|
|
||||||
errRecord.push(LogText);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
compareResult: errRecord.length === 0,
|
|
||||||
errRecord
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function stringify(data) {
|
|
||||||
return data ? data.replace(/\s+/g, '') : '';
|
|
||||||
}
|
|
||||||
|
|
||||||
function showResponseResult() {
|
|
||||||
if (direcrtResponseSampleA.length !== direcrtResponseSampleB.length || direcrtResponseSampleA.length !== proxyResponse.length) {
|
|
||||||
printError('compare fail: length not match');
|
|
||||||
}
|
|
||||||
proxyResponse.forEach((dataObj, i) => {
|
|
||||||
const direcrtResponseInfoA = direcrtResponseSampleA[i];
|
|
||||||
const direcrtResponseInfoB = direcrtResponseSampleB[i];
|
|
||||||
printLog(`Direct Sample A ${direcrtResponseInfoA.request.method}: ${direcrtResponseInfoA.request.href} ${direcrtResponseInfoA.statusCode}${direcrtResponseInfoA.statusMessage}`);
|
|
||||||
printLog(`Direct Sample B ${direcrtResponseInfoB.request.method}: ${direcrtResponseInfoB.request.href} ${direcrtResponseInfoB.statusCode}${direcrtResponseInfoB.statusMessage}`);
|
|
||||||
printLog(`PROXY ${dataObj.request.method}: ${dataObj.request.href} ${dataObj.statusCode}${dataObj.statusMessage}`);
|
|
||||||
})
|
|
||||||
console.log('\n');
|
|
||||||
}
|
|
||||||
|
|
||||||
function dealLogFile(dataObj = 'Log', url, cb) {
|
|
||||||
const filePath = reportPath + url.replace(/[^\u4E00-\u9FA5A-Za-z\s()()\d•·]/g, '_') + '.txt';
|
|
||||||
fs.writeFile(filePath, dataObj, (err) => {
|
|
||||||
if (err) throw err;
|
|
||||||
console.log('Log is saved!');
|
|
||||||
cb && cb();
|
|
||||||
});
|
|
||||||
return filePath;
|
|
||||||
}
|
|
||||||
|
|
||||||
testUrls.forEach((link) => {
|
|
||||||
test(link);
|
|
||||||
});
|
|
@ -1,42 +0,0 @@
|
|||||||
process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0';
|
|
||||||
const { printLog } = require('../util/CommonUtil.js');
|
|
||||||
const spawn = require('child_process').spawn;
|
|
||||||
const Server = require('../server/server.js');
|
|
||||||
|
|
||||||
const ProxyServerUtil = require('../util/ProxyServerUtil.js');
|
|
||||||
|
|
||||||
describe('Test request with big body', () => {
|
|
||||||
let proxyServer;
|
|
||||||
let serverInstance;
|
|
||||||
|
|
||||||
beforeAll((done) => {
|
|
||||||
jasmine.DEFAULT_TIMEOUT_INTERVAL = 200000;
|
|
||||||
printLog('Start server for no_rule_big_response');
|
|
||||||
|
|
||||||
serverInstance = new Server();
|
|
||||||
proxyServer = ProxyServerUtil.defaultProxyServer();
|
|
||||||
|
|
||||||
setTimeout(() => {
|
|
||||||
done();
|
|
||||||
}, 2000);
|
|
||||||
});
|
|
||||||
|
|
||||||
afterAll(() => {
|
|
||||||
serverInstance && serverInstance.close();
|
|
||||||
proxyServer && proxyServer.close();
|
|
||||||
printLog('Closed server for no_rule_spec');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should successfully get file', (done) => {
|
|
||||||
const isWin = /^win/.test(process.platform);
|
|
||||||
if (isWin) {
|
|
||||||
done();
|
|
||||||
} else {
|
|
||||||
const curl = spawn('curl', ['http://localhost:3000/big_response', '--proxy', 'http://127.0.0.1:8001', '-o', '/dev/null']);
|
|
||||||
curl.on('close', (code) => {
|
|
||||||
expect(code).toEqual(0);
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
@ -1,427 +0,0 @@
|
|||||||
|
|
||||||
process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0';
|
|
||||||
const path = require('path');
|
|
||||||
const fs = require('fs');
|
|
||||||
const Server = require('../server/server.js');
|
|
||||||
const
|
|
||||||
{
|
|
||||||
proxyGet,
|
|
||||||
proxyPost,
|
|
||||||
directGet,
|
|
||||||
directPost,
|
|
||||||
directUpload,
|
|
||||||
proxyUpload,
|
|
||||||
generateUrl,
|
|
||||||
proxyPut,
|
|
||||||
directPut,
|
|
||||||
proxyDelete,
|
|
||||||
directDelete,
|
|
||||||
directHead,
|
|
||||||
proxyHead,
|
|
||||||
directOptions,
|
|
||||||
proxyOptions,
|
|
||||||
proxyPutUpload,
|
|
||||||
directPutUpload
|
|
||||||
} = require('../util/HttpUtil.js');
|
|
||||||
const { CommonRequestHeader } = require('../data/headers.js');
|
|
||||||
const { isCommonResHeaderEqual, isCommonReqEqual, printLog } = require('../util/CommonUtil.js');
|
|
||||||
const streamEqual = require('stream-equal');
|
|
||||||
|
|
||||||
const ProxyServerUtil = require('../util/ProxyServerUtil.js');
|
|
||||||
|
|
||||||
testRequest('http');
|
|
||||||
testRequest('https');
|
|
||||||
testRequest('http', false);
|
|
||||||
testRequest('https', false);
|
|
||||||
|
|
||||||
// Test suites for http and https request
|
|
||||||
function testRequest(protocol = 'http', needWeb = true) {
|
|
||||||
function constructUrl(urlPath) {
|
|
||||||
return generateUrl(protocol, urlPath);
|
|
||||||
}
|
|
||||||
|
|
||||||
describe('Test request without proxy rules in protocol ' + protocol, () => {
|
|
||||||
let proxyServer;
|
|
||||||
let serverInstance;
|
|
||||||
|
|
||||||
beforeAll((done) => {
|
|
||||||
jasmine.DEFAULT_TIMEOUT_INTERVAL = 200000;
|
|
||||||
printLog('Start server for no_rule_spec');
|
|
||||||
|
|
||||||
serverInstance = new Server();
|
|
||||||
proxyServer = ProxyServerUtil.defaultProxyServer(needWeb);
|
|
||||||
setTimeout(() => {
|
|
||||||
done();
|
|
||||||
}, 2000);
|
|
||||||
});
|
|
||||||
|
|
||||||
afterAll(() => {
|
|
||||||
serverInstance && serverInstance.close();
|
|
||||||
proxyServer && proxyServer.close();
|
|
||||||
printLog('Closed server for no_rule_spec');
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
it('Get should work as direct without proxy rules', (done) => {
|
|
||||||
const url = constructUrl('/test');
|
|
||||||
const getParam = {
|
|
||||||
param: 'nothing'
|
|
||||||
};
|
|
||||||
|
|
||||||
proxyGet(url, getParam, CommonRequestHeader).then((proxyRes) => {
|
|
||||||
directGet(url, getParam, CommonRequestHeader).then(directRes => {
|
|
||||||
expect(proxyRes.statusCode).toEqual(200);
|
|
||||||
expect(isCommonResHeaderEqual(directRes.headers, proxyRes.headers, url)).toBe(true);
|
|
||||||
expect(isCommonReqEqual(url, serverInstance)).toBe(true);
|
|
||||||
expect(proxyRes.statusCode).toEqual(directRes.statusCode);
|
|
||||||
expect(directRes.body).toEqual(proxyRes.body);
|
|
||||||
|
|
||||||
done();
|
|
||||||
}, error => {
|
|
||||||
console.error('error happend in direct get:', error);
|
|
||||||
done.fail('error happend in direct get');
|
|
||||||
});
|
|
||||||
}, error => {
|
|
||||||
console.log('error happened in proxy get:', error);
|
|
||||||
done.fail('error happend in proxy get');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Post should work as direct without proxy rules', (done) => {
|
|
||||||
const url = constructUrl('/test/getuser');
|
|
||||||
const param = {
|
|
||||||
param: 'postnothing'
|
|
||||||
};
|
|
||||||
|
|
||||||
proxyPost(url, param, CommonRequestHeader).then(proxyRes => {
|
|
||||||
directPost(url, param, CommonRequestHeader).then(directRes => {
|
|
||||||
expect(proxyRes.statusCode).toEqual(200);
|
|
||||||
|
|
||||||
expect(isCommonResHeaderEqual(directRes.headers, proxyRes.headers, url)).toBe(true);
|
|
||||||
expect(proxyRes.statusCode).toEqual(directRes.statusCode);
|
|
||||||
expect(directRes.body).toEqual(proxyRes.body);
|
|
||||||
|
|
||||||
expect(isCommonReqEqual(url, serverInstance)).toBe(true);
|
|
||||||
|
|
||||||
done();
|
|
||||||
}, error => {
|
|
||||||
console.error('error in direct post:', error);
|
|
||||||
done.fail('error happend in direct post');
|
|
||||||
});
|
|
||||||
}, error => {
|
|
||||||
console.log('error happened in proxy post,', error);
|
|
||||||
done.fail('error happend in proxy post');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('PUT should work as direct without proxy rules', done => {
|
|
||||||
const url = constructUrl('/test/put');
|
|
||||||
const param = {
|
|
||||||
param: 'putsomething'
|
|
||||||
};
|
|
||||||
proxyPut(url, param, CommonRequestHeader).then(proxyRes => {
|
|
||||||
directPut(url, param, CommonRequestHeader).then(directRes => {
|
|
||||||
expect(directRes.statusCode).toEqual(200);
|
|
||||||
|
|
||||||
expect(isCommonResHeaderEqual(directRes.headers, proxyRes.headers, url)).toBe(true);
|
|
||||||
expect(directRes.statusCode).toEqual(proxyRes.statusCode);
|
|
||||||
expect(directRes.body).toEqual(proxyRes.body);
|
|
||||||
expect(isCommonReqEqual(url, serverInstance)).toBe(true);
|
|
||||||
|
|
||||||
done();
|
|
||||||
}, error => {
|
|
||||||
console.error('error happened in direct put', error);
|
|
||||||
done.fail('error happened in direct put');
|
|
||||||
});
|
|
||||||
}, error => {
|
|
||||||
console.error('error happened in proxy put', error);
|
|
||||||
done.fail('error happened in proxy put');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('DELETE rquest should work as direct without proxy rules', (done) => {
|
|
||||||
const url = constructUrl('/test/delete/123456');
|
|
||||||
|
|
||||||
proxyDelete(url, {}, CommonRequestHeader).then(proxyRes => {
|
|
||||||
directDelete(url, {}, CommonRequestHeader).then(directRes => {
|
|
||||||
expect(directRes.statusCode).toEqual(200);
|
|
||||||
|
|
||||||
expect(directRes.statusCode).toEqual(proxyRes.statusCode);
|
|
||||||
expect(isCommonResHeaderEqual(directRes.headers, proxyRes.headers, url)).toBe(true);
|
|
||||||
expect(directRes.body).toEqual(proxyRes.body);
|
|
||||||
expect(isCommonReqEqual(url, serverInstance)).toBe(true);
|
|
||||||
|
|
||||||
done();
|
|
||||||
}, error => {
|
|
||||||
console.error('error happened in direct delete :', error);
|
|
||||||
done.fail('error happened in direct delete');
|
|
||||||
});
|
|
||||||
}, error => {
|
|
||||||
console.error('error happened in proxy delete :', error);
|
|
||||||
done.fail('error happened in proxy delete');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('HEAD request should work as direct without proxy rules', (done) => {
|
|
||||||
const url = constructUrl('/test/head');
|
|
||||||
|
|
||||||
proxyHead(url, CommonRequestHeader)
|
|
||||||
.then(proxyRes => {
|
|
||||||
directHead(url, CommonRequestHeader)
|
|
||||||
.then(directRes => {
|
|
||||||
expect(directRes.statusCode).toEqual(200);
|
|
||||||
expect(directRes.body).toEqual('');
|
|
||||||
|
|
||||||
expect(directRes.statusCode).toEqual(proxyRes.statusCode);
|
|
||||||
expect(isCommonResHeaderEqual(directRes.headers, proxyRes.headers, url)).toBe(true);
|
|
||||||
expect(directRes.body).toEqual(proxyRes.body);
|
|
||||||
expect(isCommonReqEqual(url, serverInstance)).toBe(true);
|
|
||||||
|
|
||||||
done();
|
|
||||||
}, error => {
|
|
||||||
console.error('error happened in direct head request:', error);
|
|
||||||
done.fail('error happened in direct head request');
|
|
||||||
});
|
|
||||||
}, error => {
|
|
||||||
console.error('error happened in proxy head request:', error);
|
|
||||||
done.fail('error happened in proxy head request');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('OPTIONS request should work as direct without proxy rules', (done) => {
|
|
||||||
const url = constructUrl('/test/options');
|
|
||||||
|
|
||||||
proxyOptions(url, CommonRequestHeader)
|
|
||||||
.then(proxyRes => {
|
|
||||||
directOptions(url, CommonRequestHeader)
|
|
||||||
.then(directRes => {
|
|
||||||
expect(directRes.statusCode).toEqual(200);
|
|
||||||
expect(directRes.body).toEqual('could_be_empty');
|
|
||||||
|
|
||||||
expect(directRes.statusCode).toEqual(proxyRes.statusCode);
|
|
||||||
expect(isCommonResHeaderEqual(directRes.headers, proxyRes.headers, url)).toBe(true);
|
|
||||||
expect(directRes.body).toEqual(proxyRes.body);
|
|
||||||
expect(isCommonReqEqual(url, serverInstance)).toBe(true);
|
|
||||||
|
|
||||||
done();
|
|
||||||
}, error => {
|
|
||||||
console.error('error happened in direct options request:', error);
|
|
||||||
done.fail('error happened in direct options request');
|
|
||||||
});
|
|
||||||
}, error => {
|
|
||||||
console.error('error happened in proxy options request:', error);
|
|
||||||
done.fail('error happened in proxy options request');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('304 should work as direct without proxy rules', (done) => {
|
|
||||||
const url = constructUrl('/test/response/304');
|
|
||||||
|
|
||||||
proxyGet(url, CommonRequestHeader)
|
|
||||||
.then(proxyRes => {
|
|
||||||
directGet(url, CommonRequestHeader)
|
|
||||||
.then(directRes => {
|
|
||||||
expect(directRes.statusCode).toEqual(304);
|
|
||||||
expect(directRes.body).toEqual('');
|
|
||||||
|
|
||||||
expect(directRes.statusCode).toEqual(proxyRes.statusCode);
|
|
||||||
expect(isCommonResHeaderEqual(directRes.headers, proxyRes.headers, url)).toBe(true);
|
|
||||||
expect(directRes.body).toEqual(proxyRes.body);
|
|
||||||
expect(isCommonReqEqual(url, serverInstance)).toBe(true);
|
|
||||||
done();
|
|
||||||
}, error => {
|
|
||||||
console.error('error happened in direct 304 request:', error);
|
|
||||||
done.fail('error happened in direct 304 request');
|
|
||||||
});
|
|
||||||
}, error => {
|
|
||||||
console.error('error happened in proxy 304 request:', error);
|
|
||||||
done.fail('error happened in proxy 304 request');
|
|
||||||
});
|
|
||||||
})
|
|
||||||
|
|
||||||
describe('Response code should be honored as direct without proxy rules', () => {
|
|
||||||
[301, 302, 303].forEach(code => {
|
|
||||||
testRedirect(code);
|
|
||||||
});
|
|
||||||
|
|
||||||
function testRedirect(redirectCode) {
|
|
||||||
it(`${redirectCode} response should work as direct without proxy rules`, (done) => {
|
|
||||||
const url = constructUrl(`/test/response/${redirectCode}`);
|
|
||||||
|
|
||||||
proxyGet(url)
|
|
||||||
.then(proxyRes => {
|
|
||||||
directGet(url)
|
|
||||||
.then(directRes => {
|
|
||||||
expect(directRes.statusCode).toEqual(redirectCode);
|
|
||||||
expect(directRes.headers.location).toEqual(proxyRes.headers.location);
|
|
||||||
expect(directRes.statusCode).toEqual(proxyRes.statusCode);
|
|
||||||
expect(directRes.headers.location).toEqual('/test');
|
|
||||||
done();
|
|
||||||
}).catch(error => {
|
|
||||||
console.log(`error happened in direct ${redirectCode}:`, error);
|
|
||||||
done.fail(`error happened in direct ${redirectCode}`);
|
|
||||||
});
|
|
||||||
}).catch(error => {
|
|
||||||
console.log(`error happened in proxy ${redirectCode}:`, error);
|
|
||||||
done.fail(`error happened in proxy ${redirectCode}`);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('Test file download ', () => {
|
|
||||||
const testArray = [
|
|
||||||
{
|
|
||||||
url: constructUrl('/test/download/png'),
|
|
||||||
type: 'png',
|
|
||||||
contentType: 'image/png'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
url: constructUrl('/test/download/webp'),
|
|
||||||
type: 'WEBP',
|
|
||||||
contentType: 'image/webp'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
url: constructUrl('/test/download/json'),
|
|
||||||
type: 'JSON',
|
|
||||||
contentType: 'application/json; charset=utf-8'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
url: constructUrl('/test/download/css'),
|
|
||||||
type: 'CSS',
|
|
||||||
contentType: 'text/css; charset=utf-8'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
url: constructUrl('/test/download/ttf'),
|
|
||||||
type: 'TTF',
|
|
||||||
contentType: 'application/x-font-ttf'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
url: constructUrl('/test/download/eot'),
|
|
||||||
type: 'EOT',
|
|
||||||
contentType: 'application/vnd.ms-fontobject'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
url: constructUrl('/test/download/svg'),
|
|
||||||
type: 'SVG',
|
|
||||||
contentType: 'image/svg+xml'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
url: constructUrl('/test/download/woff'),
|
|
||||||
type: 'WOFF',
|
|
||||||
contentType: 'application/font-woff'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
url: constructUrl('/test/download/woff2'),
|
|
||||||
type: 'WOFF2',
|
|
||||||
contentType: 'application/font-woff2'
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
testArray.forEach(item => {
|
|
||||||
testFileDownload(item.url, item.type, item.contentType);
|
|
||||||
});
|
|
||||||
|
|
||||||
// 封装测试文件下载的测试工具类
|
|
||||||
function testFileDownload(url, filetype, contentType) {
|
|
||||||
const describe = `${filetype} file download without rules should be work as direct download`;
|
|
||||||
const param = {};
|
|
||||||
|
|
||||||
it(describe, (done) => {
|
|
||||||
proxyGet(url, param).then(proxyRes => {
|
|
||||||
directGet(url, param).then(directRes => {
|
|
||||||
expect(proxyRes.statusCode).toEqual(200);
|
|
||||||
|
|
||||||
expect(isCommonResHeaderEqual(directRes.headers, proxyRes.headers, url)).toBe(true);
|
|
||||||
expect(proxyRes.statusCode).toEqual(directRes.statusCode);
|
|
||||||
expect(proxyRes.body).toEqual(directRes.body);
|
|
||||||
expect(isCommonReqEqual(url, serverInstance)).toBe(true);
|
|
||||||
done();
|
|
||||||
}, error => {
|
|
||||||
console.error('error in direct get :', filetype, error);
|
|
||||||
done.fail(`error happend in direct get ${filetype}`);
|
|
||||||
});
|
|
||||||
}, error => {
|
|
||||||
console.error('error in proxy get :', filetype, error);
|
|
||||||
done.fail(`error happend in proxy get ${filetype}`);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('Test file upload', () => {
|
|
||||||
const formParams = {
|
|
||||||
param1: 'param_1',
|
|
||||||
param2: 'param2'
|
|
||||||
};
|
|
||||||
it('POST upload should be working', (done) => {
|
|
||||||
const url = constructUrl('/test/upload/png');
|
|
||||||
const filePath = path.resolve(__dirname, '../data/test.png');
|
|
||||||
|
|
||||||
proxyUpload(url, filePath, formParams)
|
|
||||||
.then(proxyRes => {
|
|
||||||
directUpload(url, filePath, formParams)
|
|
||||||
.then((directRes) => {
|
|
||||||
expect(isCommonResHeaderEqual(directRes.headers, proxyRes.headers, url)).toBe(true);
|
|
||||||
expect(isCommonReqEqual(url, serverInstance)).toBe(true);
|
|
||||||
assertReponse(proxyRes, directRes, filePath, done);
|
|
||||||
}, error => {
|
|
||||||
console.error('error in direct upload:', error);
|
|
||||||
done.fail('error in direct upload');
|
|
||||||
});
|
|
||||||
}, error => {
|
|
||||||
console.error('error in proxy upload:', error);
|
|
||||||
done.fail('error in proxy upload:');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('PUT upload should be working', (done) => {
|
|
||||||
const url = constructUrl('/test/upload/putpng');
|
|
||||||
const filePath = path.resolve(__dirname, '../data/test.png');
|
|
||||||
proxyPutUpload(url, filePath, formParams)
|
|
||||||
.then(proxyRes => {
|
|
||||||
directPutUpload(url, filePath, formParams)
|
|
||||||
.then((directRes) => {
|
|
||||||
expect(isCommonResHeaderEqual(directRes.headers, proxyRes.headers, url)).toBe(true);
|
|
||||||
|
|
||||||
assertReponse(proxyRes, directRes, filePath, done);
|
|
||||||
}, error => {
|
|
||||||
console.error('error in direct upload:', error);
|
|
||||||
done.fail('error in direct upload');
|
|
||||||
});
|
|
||||||
}, error => {
|
|
||||||
console.error('error in proxy upload:', error);
|
|
||||||
done.fail('error in proxy upload:');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
function assertReponse(proxyRes, directRes, originFilePath, done) {
|
|
||||||
expect(proxyRes.statusCode).toEqual(200);
|
|
||||||
|
|
||||||
expect(proxyRes.statusCode).toEqual(directRes.statusCode);
|
|
||||||
// expect(proxyRes.headers.reqbody).toEqual(directRes.headers.reqbody);
|
|
||||||
|
|
||||||
// the body will be the file path
|
|
||||||
const directUploadedStream = fs.createReadStream(directRes.body);
|
|
||||||
const proxyUploadedStream = fs.createReadStream(proxyRes.body);
|
|
||||||
const localFileStream = fs.createReadStream(originFilePath);
|
|
||||||
streamEqual(directUploadedStream, localFileStream)
|
|
||||||
.then(isLocalEqual => {
|
|
||||||
expect(isLocalEqual).toBe(true);
|
|
||||||
streamEqual(directUploadedStream, proxyUploadedStream)
|
|
||||||
.then(isUploadedEqual => {
|
|
||||||
expect(isUploadedEqual).toBe(true);
|
|
||||||
done();
|
|
||||||
}, error => {
|
|
||||||
console.error('error in comparing directUpload with proxy:\n', error);
|
|
||||||
done.fail('error in comparing directUpload with proxy');
|
|
||||||
});
|
|
||||||
done();
|
|
||||||
}, error => {
|
|
||||||
console.error('error in comparing directUpload with local:\n', error);
|
|
||||||
done.fail('error in comparing directUpload with local');
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
@ -1,134 +0,0 @@
|
|||||||
/*
|
|
||||||
* Test suites for WebSocket.
|
|
||||||
* ONLY TO ENSURE THE REQUEST WILL BE BYPASSED SUCCESSFULLY, WE HAVEN'T SUPPORTTED WEBSOCKET YET.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
const ProxyServerUtil = require('../util/ProxyServerUtil.js');
|
|
||||||
const { generateWsUrl, directWs, proxyWs } = require('../util/HttpUtil.js');
|
|
||||||
const Server = require('../server/server.js');
|
|
||||||
const { printLog, isArrayEqual, isCommonReqEqual } = require('../util/CommonUtil.js');
|
|
||||||
|
|
||||||
testWebsocket('ws');
|
|
||||||
testWebsocket('wss');
|
|
||||||
testWebsocket('ws', true);
|
|
||||||
testWebsocket('wss', true);
|
|
||||||
|
|
||||||
function testWebsocket(protocol, masked = false) {
|
|
||||||
describe('Test WebSocket in protocol : ' + protocol, () => {
|
|
||||||
const url = generateWsUrl(protocol, '/test/socket');
|
|
||||||
let serverInstance;
|
|
||||||
let proxyServer;
|
|
||||||
// the message to
|
|
||||||
const testMessageArray = [
|
|
||||||
'Send the message with default option1',
|
|
||||||
'Send the message with default option2',
|
|
||||||
'Send the message with default option3',
|
|
||||||
'Send the message with default option4'
|
|
||||||
];
|
|
||||||
|
|
||||||
const websocketHeaders = {
|
|
||||||
referer: 'https://www.anyproxy.io/websocket/test',
|
|
||||||
origin: 'www.anyproxy.io'
|
|
||||||
}
|
|
||||||
|
|
||||||
beforeAll((done) => {
|
|
||||||
jasmine.DEFAULT_TIMEOUT_INTERVAL = 200000;
|
|
||||||
printLog('Start server for no_rule_websocket_spec');
|
|
||||||
serverInstance = new Server();
|
|
||||||
|
|
||||||
proxyServer = ProxyServerUtil.defaultProxyServer();
|
|
||||||
|
|
||||||
setTimeout(() => {
|
|
||||||
done();
|
|
||||||
}, 2000);
|
|
||||||
});
|
|
||||||
|
|
||||||
afterAll(() => {
|
|
||||||
serverInstance && serverInstance.close();
|
|
||||||
proxyServer && proxyServer.close();
|
|
||||||
printLog('Closed server for no_rule_websocket_spec');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Default websocket option', done => {
|
|
||||||
const directMessages = []; // set the flag for direct message, compare when both direct and proxy got message
|
|
||||||
const proxyMessages = [];
|
|
||||||
let directResHeaders;
|
|
||||||
let proxyResHeaders;
|
|
||||||
|
|
||||||
const ws = directWs(url, websocketHeaders);
|
|
||||||
const proxyWsRef = proxyWs(url, websocketHeaders);
|
|
||||||
ws.on('open', () => {
|
|
||||||
ws.send(testMessageArray[0], masked);
|
|
||||||
for (let i = 1; i < testMessageArray.length; i++) {
|
|
||||||
setTimeout(() => {
|
|
||||||
ws.send(testMessageArray[i], masked);
|
|
||||||
}, 1000);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
proxyWsRef.on('open', () => {
|
|
||||||
try {
|
|
||||||
proxyWsRef.send(testMessageArray[0], masked);
|
|
||||||
for (let i = 1; i < testMessageArray.length; i++) {
|
|
||||||
setTimeout(() => {
|
|
||||||
proxyWsRef.send(testMessageArray[i], masked);
|
|
||||||
}, 1000);
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
console.error(e);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
ws.on('upgrade', (res) => {
|
|
||||||
directResHeaders = res.headers;
|
|
||||||
compareMessageIfReady();
|
|
||||||
});
|
|
||||||
|
|
||||||
proxyWsRef.on('upgrade', (res) => {
|
|
||||||
proxyResHeaders = res.headers;
|
|
||||||
compareMessageIfReady();
|
|
||||||
});
|
|
||||||
|
|
||||||
ws.on('message', (data, flag) => {
|
|
||||||
const message = JSON.parse(data);
|
|
||||||
if (message.type === 'onMessage') {
|
|
||||||
directMessages.push(message.content);
|
|
||||||
compareMessageIfReady();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
proxyWsRef.on('message', (data, flag) => {
|
|
||||||
const message = JSON.parse(data);
|
|
||||||
if (message.type === 'onMessage') {
|
|
||||||
proxyMessages.push(message.content);
|
|
||||||
compareMessageIfReady();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
ws.on('error', error => {
|
|
||||||
console.error('error happened in direct websocket:', error);
|
|
||||||
done.fail('Error happened in direct websocket');
|
|
||||||
});
|
|
||||||
|
|
||||||
proxyWsRef.on('error', error => {
|
|
||||||
console.error('error happened in proxy websocket:', error);
|
|
||||||
done.fail('Error happened in proxy websocket');
|
|
||||||
});
|
|
||||||
|
|
||||||
function compareMessageIfReady() {
|
|
||||||
const targetLen = testMessageArray.length;
|
|
||||||
if (directMessages.length === targetLen
|
|
||||||
&& proxyMessages.length === targetLen
|
|
||||||
&& directResHeaders && proxyResHeaders
|
|
||||||
) {
|
|
||||||
expect(isArrayEqual(directMessages, testMessageArray)).toBe(true);
|
|
||||||
expect(isArrayEqual(directMessages, proxyMessages)).toBe(true);
|
|
||||||
expect(directResHeaders['x-anyproxy-websocket']).toBeUndefined();
|
|
||||||
expect(proxyResHeaders['x-anyproxy-websocket']).toBe('true');
|
|
||||||
expect(isCommonReqEqual(url, serverInstance)).toBe(true);
|
|
||||||
done();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
@ -1,22 +0,0 @@
|
|||||||
/*
|
|
||||||
* add authToken parameter to the request data
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
module.exports = {
|
|
||||||
*beforeSendRequest(requestDetail) {
|
|
||||||
if (requestDetail.url.indexOf('/getuser') >= 0) {
|
|
||||||
let requestStr = requestDetail.requestData.toString();
|
|
||||||
try {
|
|
||||||
requestStr = JSON.stringify(Object.assign(JSON.parse(requestStr), {
|
|
||||||
authToken: 'auth_token_inrule'
|
|
||||||
}))
|
|
||||||
} catch (e) {
|
|
||||||
requestStr += '&authToken=auth_token_inrule';
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
requestOptions: requestDetail.requestOptions,
|
|
||||||
requestData: requestStr
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
@ -1,13 +0,0 @@
|
|||||||
//rule scheme :
|
|
||||||
module.exports = {
|
|
||||||
|
|
||||||
*beforeSendRequest(requestDetail) {
|
|
||||||
const newOption = requestDetail.requestOptions;
|
|
||||||
if (newOption.hostname === 'localhost' && newOption.path === '/test/should_replace_option') {
|
|
||||||
newOption.path = '/test/new_replace_option';
|
|
||||||
return {
|
|
||||||
requestOptions: newOption
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
@ -1,15 +0,0 @@
|
|||||||
//rule scheme :
|
|
||||||
module.exports = {
|
|
||||||
*summary() {
|
|
||||||
return 'The rule to replace request protocol';
|
|
||||||
},
|
|
||||||
|
|
||||||
*beforeSendRequest(requestDetail) {
|
|
||||||
const newConfig = {
|
|
||||||
protocol: 'http',
|
|
||||||
requestOptions: requestDetail.requestOptions
|
|
||||||
};
|
|
||||||
newConfig.requestOptions.port = 3000;
|
|
||||||
return newConfig;
|
|
||||||
}
|
|
||||||
};
|
|
@ -1,17 +0,0 @@
|
|||||||
//rule scheme : replace the reponse data
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
|
|
||||||
*beforeSendResponse(requestDetail, responseDetail) {
|
|
||||||
if (requestDetail.url.indexOf('/test/normal_request1') > -1) {
|
|
||||||
const newResponse = responseDetail.response;
|
|
||||||
|
|
||||||
const newDataStr = newResponse.body.toString() + '_hello_world!';
|
|
||||||
newResponse.body = newDataStr;
|
|
||||||
|
|
||||||
return {
|
|
||||||
response: newResponse
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
@ -1,30 +0,0 @@
|
|||||||
//rule scheme : remove the cache headers in response headers
|
|
||||||
module.exports = {
|
|
||||||
*summary() {
|
|
||||||
return 'The rule to remove the cache headers in response';
|
|
||||||
},
|
|
||||||
|
|
||||||
*beforeSendResponse(requestDetail, responseDetail) {
|
|
||||||
if (requestDetail.url.indexOf('/test/normal_request1') >= 0) {
|
|
||||||
const newResponse = responseDetail.response;
|
|
||||||
newResponse.header.replacedheaderkey = 'replacedHeader_value_in_rule';
|
|
||||||
|
|
||||||
return {
|
|
||||||
response: newResponse
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// replaceResponseHeader(req, res, header) {
|
|
||||||
// const d = Q.defer();
|
|
||||||
|
|
||||||
// header = Object.assign({}, header);
|
|
||||||
// if (req.url.indexOf('test/normal_request1') > -1) {
|
|
||||||
// header.replacedheaderkey = 'replacedHeader_value_in_rule';
|
|
||||||
// }
|
|
||||||
|
|
||||||
// d.resolve(header);
|
|
||||||
|
|
||||||
// return d.promise;
|
|
||||||
// }
|
|
||||||
};
|
|
@ -1,20 +0,0 @@
|
|||||||
//replace all the images with local one
|
|
||||||
module.exports = {
|
|
||||||
|
|
||||||
*summary() {
|
|
||||||
return 'replace the response status code.';
|
|
||||||
},
|
|
||||||
|
|
||||||
*beforeSendResponse(requestDetail, responseDetail) {
|
|
||||||
if (requestDetail.url.indexOf('/test/normal_request1') >= 0) {
|
|
||||||
const newResponse = responseDetail.response;
|
|
||||||
newResponse.statusCode = 302;
|
|
||||||
newResponse.header.location = 'www.taobao.com';
|
|
||||||
|
|
||||||
return {
|
|
||||||
response: newResponse
|
|
||||||
};
|
|
||||||
}
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
@ -1,19 +0,0 @@
|
|||||||
//rule scheme :
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
*summary() {
|
|
||||||
return 'Rule to intercept https request';
|
|
||||||
},
|
|
||||||
|
|
||||||
*beforeSendResponse(requestDetail, responseDetail) {
|
|
||||||
const newResponse = responseDetail.response;
|
|
||||||
newResponse.body = newResponse.body.toString() + '_hello_world';
|
|
||||||
return {
|
|
||||||
response: newResponse
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
*beforeDealHttpsRequest(requestDetail) {
|
|
||||||
return requestDetail.host.indexOf('localhost:3001') > -1;
|
|
||||||
}
|
|
||||||
};
|
|
@ -1,21 +0,0 @@
|
|||||||
/*
|
|
||||||
* Rule defination for shouldUseLocalResponse
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
const localData = 'handled_in_local_response';
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
*beforeSendRequest(requestDetail) {
|
|
||||||
if (requestDetail.url.indexOf('uselocal') > -1) {
|
|
||||||
return {
|
|
||||||
response: {
|
|
||||||
statusCode: 200,
|
|
||||||
header: {
|
|
||||||
'Via-Proxy-Local': 'true'
|
|
||||||
},
|
|
||||||
body: localData
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
@ -1,155 +0,0 @@
|
|||||||
/*
|
|
||||||
* test for rule replaceResponseStatus rule
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
const ProxyServerUtil = require('../util/ProxyServerUtil.js');
|
|
||||||
const { proxyGet } = require('../util/HttpUtil.js');
|
|
||||||
const { printLog } = require('../util/CommonUtil.js');
|
|
||||||
|
|
||||||
const domain_not_exists = 'not_exist.not_exist_anyproxy_io_domain.com';
|
|
||||||
|
|
||||||
let errorInRule = null;
|
|
||||||
const ruleNotDealError = {
|
|
||||||
*onError(requestDetail, error) {
|
|
||||||
errorInRule = error;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let errorInConnect = null;
|
|
||||||
const ruleDealConnectError = {
|
|
||||||
*onConnectError(requestDetail, error) {
|
|
||||||
errorInConnect = error;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const ERROR_PAGE_IN_RULE = 'this is my error page';
|
|
||||||
const ruleReturnAnErrorPage = {
|
|
||||||
*onError(requestDetail, error) {
|
|
||||||
return {
|
|
||||||
response: {
|
|
||||||
statusCode: '200',
|
|
||||||
header: {},
|
|
||||||
body: ERROR_PAGE_IN_RULE,
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
testWrapper('http');
|
|
||||||
testWrapper('https');
|
|
||||||
testHttpsConnect();
|
|
||||||
|
|
||||||
function testWrapper(protocol) {
|
|
||||||
describe('Rule should get an error in :' + protocol, () => {
|
|
||||||
let proxyServer;
|
|
||||||
// let serverInstance;
|
|
||||||
|
|
||||||
beforeAll((done) => {
|
|
||||||
jasmine.DEFAULT_TIMEOUT_INTERVAL = 50000;
|
|
||||||
printLog('Start server for rule_deal_error_spec');
|
|
||||||
errorInRule = null;
|
|
||||||
|
|
||||||
proxyServer = ProxyServerUtil.proxyServerWithRule(ruleNotDealError);
|
|
||||||
|
|
||||||
setTimeout(() => {
|
|
||||||
done();
|
|
||||||
}, 2000);
|
|
||||||
});
|
|
||||||
|
|
||||||
afterAll(() => {
|
|
||||||
// serverInstance && serverInstance.close();
|
|
||||||
proxyServer && proxyServer.close();
|
|
||||||
printLog('Close server for rule_deal_error_spec');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Should get a request error', done => {
|
|
||||||
const url = protocol + `://${domain_not_exists}`;
|
|
||||||
proxyGet(url)
|
|
||||||
.then(proxyRes => {
|
|
||||||
expect(proxyRes.statusCode).toEqual(500);
|
|
||||||
expect(proxyRes.headers['proxy-error']).toEqual('true');
|
|
||||||
expect(errorInRule).not.toBe(null);
|
|
||||||
done();
|
|
||||||
})
|
|
||||||
.catch(error => {
|
|
||||||
console.error('error happened in proxy get: ', error);
|
|
||||||
done.fail('error happened in proxy get');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('Rule should return a custom error page in :' + protocol, () => {
|
|
||||||
let proxyServer;
|
|
||||||
// let serverInstance;
|
|
||||||
|
|
||||||
beforeAll((done) => {
|
|
||||||
jasmine.DEFAULT_TIMEOUT_INTERVAL = 50000;
|
|
||||||
printLog('Start server for rule_deal_error_custom_error_page');
|
|
||||||
|
|
||||||
proxyServer = ProxyServerUtil.proxyServerWithRule(ruleReturnAnErrorPage);
|
|
||||||
|
|
||||||
setTimeout(() => {
|
|
||||||
done();
|
|
||||||
}, 2000);
|
|
||||||
});
|
|
||||||
|
|
||||||
afterAll(() => {
|
|
||||||
// serverInstance && serverInstance.close();
|
|
||||||
proxyServer && proxyServer.close();
|
|
||||||
printLog('Close server for rule_deal_error_custom_error_page');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Should get a request error', done => {
|
|
||||||
const url = protocol + `://${domain_not_exists}`;
|
|
||||||
proxyGet(url)
|
|
||||||
.then(proxyRes => {
|
|
||||||
expect(proxyRes.statusCode).toEqual(200);
|
|
||||||
expect(proxyRes.headers['proxy-error']).toBe(undefined);
|
|
||||||
expect(proxyRes.body).toEqual(ERROR_PAGE_IN_RULE);
|
|
||||||
done();
|
|
||||||
})
|
|
||||||
.catch(error => {
|
|
||||||
console.error('error happened in proxy get: ', error);
|
|
||||||
done.fail('error happened in proxy get');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function testHttpsConnect() {
|
|
||||||
describe('Rule should get a connect error', () => {
|
|
||||||
let proxyServer;
|
|
||||||
// let serverInstance;
|
|
||||||
|
|
||||||
beforeAll((done) => {
|
|
||||||
errorInConnect = null;
|
|
||||||
jasmine.DEFAULT_TIMEOUT_INTERVAL = 50000;
|
|
||||||
printLog('Start server for rule_deal_error_custom_error_page');
|
|
||||||
|
|
||||||
proxyServer = ProxyServerUtil.proxyServerWithRule(ruleDealConnectError, {
|
|
||||||
forceProxyHttps: false
|
|
||||||
});
|
|
||||||
|
|
||||||
setTimeout(() => {
|
|
||||||
done();
|
|
||||||
}, 2000);
|
|
||||||
});
|
|
||||||
|
|
||||||
afterAll(() => {
|
|
||||||
proxyServer && proxyServer.close();
|
|
||||||
printLog('Close server for rule_deal_error_custom_error_page');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Should get a request error', done => {
|
|
||||||
const url = `https://${domain_not_exists}`;
|
|
||||||
proxyGet(url)
|
|
||||||
.then(proxyRes => {
|
|
||||||
done.fail('should throw an error when requesting');
|
|
||||||
})
|
|
||||||
.catch(error => {
|
|
||||||
expect(errorInConnect).not.toBe(null);
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
@ -1,120 +0,0 @@
|
|||||||
/*
|
|
||||||
* test for rule replaceRequestData rule
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
const ProxyServerUtil = require('../util/ProxyServerUtil.js');
|
|
||||||
const { proxyPost, generateUrl, directPost, isViaProxy } = require('../util/HttpUtil.js');
|
|
||||||
const Server = require('../server/server.js');
|
|
||||||
const { printLog, isObjectEqual } = require('../util/CommonUtil.js');
|
|
||||||
|
|
||||||
const rule = require('./rule/rule_replace_request_data.js');
|
|
||||||
|
|
||||||
testWrapper('http');
|
|
||||||
testWrapper('https');
|
|
||||||
|
|
||||||
function testWrapper(protocol) {
|
|
||||||
describe('Rule replaceRequestData should be working in :' + protocol, () => {
|
|
||||||
let proxyServer;
|
|
||||||
let serverInstance;
|
|
||||||
|
|
||||||
beforeAll((done) => {
|
|
||||||
jasmine.DEFAULT_TIMEOUT_INTERVAL = 50000;
|
|
||||||
printLog('Start server for rule_replace_request_data_spec');
|
|
||||||
|
|
||||||
serverInstance = new Server();
|
|
||||||
proxyServer = ProxyServerUtil.proxyServerWithRule(rule);
|
|
||||||
|
|
||||||
setTimeout(() => {
|
|
||||||
done();
|
|
||||||
}, 2000);
|
|
||||||
});
|
|
||||||
|
|
||||||
afterAll(() => {
|
|
||||||
serverInstance && serverInstance.close();
|
|
||||||
proxyServer && proxyServer.close();
|
|
||||||
printLog('Close server for rule_replace_request_data_spec');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Should replace the request data in proxy if the assertion is true', done => {
|
|
||||||
const url = generateUrl(protocol, '/test/getuser');
|
|
||||||
const userName = 'username_test';
|
|
||||||
const param = {
|
|
||||||
username: userName
|
|
||||||
};
|
|
||||||
|
|
||||||
proxyPost(url, param)
|
|
||||||
.then(proxyRes => {
|
|
||||||
expect(proxyRes.statusCode).toEqual(200);
|
|
||||||
expect(proxyRes.body).toEqual('body_post_getuser');
|
|
||||||
const proxyRequest = serverInstance.getProxyRequestRecord(url);
|
|
||||||
const proxyReqBodyObj = JSON.parse(proxyRequest.body.toString());
|
|
||||||
|
|
||||||
expect(isViaProxy(proxyRequest)).toBe(true);
|
|
||||||
|
|
||||||
expect(proxyReqBodyObj.username).toEqual(userName);
|
|
||||||
expect(proxyReqBodyObj.authToken).toEqual('auth_token_inrule');
|
|
||||||
|
|
||||||
directPost(url, param)
|
|
||||||
.then(directRes => {
|
|
||||||
expect(directRes.statusCode).toEqual(200);
|
|
||||||
expect(directRes.body).toEqual(proxyRes.body);
|
|
||||||
|
|
||||||
|
|
||||||
const directRequest = serverInstance.getRequestRecord(url);
|
|
||||||
const directReqBodyObj = JSON.parse(directRequest.body.toString());
|
|
||||||
expect(isViaProxy(directRequest)).toBe(false);
|
|
||||||
|
|
||||||
expect(directReqBodyObj.username).toEqual(userName);
|
|
||||||
expect(directReqBodyObj.authToken).toBeUndefined();
|
|
||||||
done();
|
|
||||||
})
|
|
||||||
.catch(error => {
|
|
||||||
console.error('error happened in direct post: ', error);
|
|
||||||
done.fail('error happened in direct post');
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.catch(error => {
|
|
||||||
console.error('error happened in proxy post: ', error);
|
|
||||||
console.error(error);
|
|
||||||
console.error(error.stack);
|
|
||||||
done.fail('error happened in proxy post');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Should not replace the request data in proxy if the assertion is false', done => {
|
|
||||||
const url = generateUrl(protocol, '/test/normal_post_request1');
|
|
||||||
const userName = 'normal_username_test';
|
|
||||||
|
|
||||||
const param = {
|
|
||||||
username: userName
|
|
||||||
};
|
|
||||||
|
|
||||||
proxyPost(url, param)
|
|
||||||
.then(proxyRes => {
|
|
||||||
expect(proxyRes.statusCode).toEqual(200);
|
|
||||||
expect(proxyRes.body).toEqual('body_normal_post_request1');
|
|
||||||
const proxyReqRecord = serverInstance.getProxyRequestRecord(url);
|
|
||||||
const proxyReqBody = JSON.parse(proxyReqRecord.body);
|
|
||||||
expect(isObjectEqual(proxyReqBody, param, url)).toBe(true);
|
|
||||||
|
|
||||||
directPost(url, param)
|
|
||||||
.then(directRes => {
|
|
||||||
expect(directRes.statusCode).toEqual(proxyRes.statusCode);
|
|
||||||
expect(directRes.body).toEqual(proxyRes.body);
|
|
||||||
const directReqRecord = serverInstance.getRequestRecord(url);
|
|
||||||
const directReqBody = JSON.parse(directReqRecord.body);
|
|
||||||
expect(isObjectEqual(directReqBody, proxyReqBody, url)).toBe(true);
|
|
||||||
done();
|
|
||||||
})
|
|
||||||
.catch(error => {
|
|
||||||
console.error('error happened in direct post:', error);
|
|
||||||
done.fail('error happened in direct post');
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.catch(error => {
|
|
||||||
console.error('error happened in proxy post:', error);
|
|
||||||
done.fail('error happened in proxy post');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
@ -1,89 +0,0 @@
|
|||||||
/*
|
|
||||||
* test for rule replaceOption rule
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
const ProxyServerUtil = require('../util/ProxyServerUtil.js');
|
|
||||||
const { proxyGet, generateUrl, directGet } = require('../util/HttpUtil.js');
|
|
||||||
const Server = require('../server/server.js');
|
|
||||||
const { printLog } = require('../util/CommonUtil.js');
|
|
||||||
|
|
||||||
const rule = require('./rule/rule_replace_request_option.js');
|
|
||||||
|
|
||||||
testWrapper('http');
|
|
||||||
testWrapper('https');
|
|
||||||
|
|
||||||
function testWrapper(protocol) {
|
|
||||||
describe('Rule replaceRequestOption should be working in :' + protocol, () => {
|
|
||||||
let proxyServer;
|
|
||||||
let serverInstance;
|
|
||||||
|
|
||||||
beforeAll(done => {
|
|
||||||
jasmine.DEFAULT_TIMEOUT_INTERVAL = 50000;
|
|
||||||
printLog('Start server for rule_replace_request_option_spec');
|
|
||||||
|
|
||||||
serverInstance = new Server();
|
|
||||||
|
|
||||||
proxyServer = ProxyServerUtil.proxyServerWithRule(rule);
|
|
||||||
|
|
||||||
setTimeout(() => {
|
|
||||||
done();
|
|
||||||
}, 2000);
|
|
||||||
});
|
|
||||||
|
|
||||||
afterAll(() => {
|
|
||||||
serverInstance && serverInstance.close();
|
|
||||||
proxyServer && proxyServer.close();
|
|
||||||
printLog('Close server for rule_replace_request_option_spec');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Should replace request option if the assertion is true', done => {
|
|
||||||
const url = generateUrl(protocol, '/test/should_replace_option');
|
|
||||||
const replacedUrl = generateUrl(protocol, '/test/new_replace_option');
|
|
||||||
|
|
||||||
const token = 'replacedOption' + Date.now();
|
|
||||||
const directToken = 'notRepacedOption' + Date.now();
|
|
||||||
proxyGet(url, {}, { token })
|
|
||||||
.then(proxyRes => {
|
|
||||||
directGet(url, {}, { token: directToken })
|
|
||||||
.then(directRes => {
|
|
||||||
expect(proxyRes.statusCode).toEqual(200);
|
|
||||||
expect(proxyRes.body).toEqual('the_new_replaced_option_page_content');
|
|
||||||
|
|
||||||
const proxyRequestObj = serverInstance.getProxyRequestRecord(replacedUrl);
|
|
||||||
expect(proxyRequestObj.headers.token).toEqual(token);
|
|
||||||
expect(proxyRequestObj.headers['via-proxy']).toEqual('true');
|
|
||||||
|
|
||||||
expect(directRes.statusCode).toEqual(200);
|
|
||||||
expect(directRes.body).toEqual('the_request_that_has_not_be_replaced');
|
|
||||||
|
|
||||||
const directRequestObj = serverInstance.getRequestRecord(url);
|
|
||||||
expect(directRequestObj.headers.token).toEqual(directToken);
|
|
||||||
|
|
||||||
done();
|
|
||||||
}).catch(error => {
|
|
||||||
console.error('error happened in direct get for replaceOption rule: ', error);
|
|
||||||
done.fail('error happened when direct test replaceOption rule ');
|
|
||||||
});
|
|
||||||
}).catch(error => {
|
|
||||||
console.error('error happened in proxy get for replaceOption rule: ', error);
|
|
||||||
done.fail('error happened when proxy test replaceOption rule ');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Should not replace request option if the assertion is false', done => {
|
|
||||||
const url = generateUrl(protocol, '/test/should_not_replace_option');
|
|
||||||
|
|
||||||
proxyGet(url)
|
|
||||||
.then(proxyRes => {
|
|
||||||
done();
|
|
||||||
}, error => {
|
|
||||||
console.error('error happened in proxy get:', error);
|
|
||||||
done.fail('error happened in proxy get');
|
|
||||||
}).catch(error => {
|
|
||||||
console.error('error happend in syntax:', error);
|
|
||||||
done.fail('error happend in syntax');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
@ -1,112 +0,0 @@
|
|||||||
/*
|
|
||||||
* test for rule replaceOption rule
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
const ProxyServerUtil = require('../util/ProxyServerUtil.js');
|
|
||||||
const { proxyGet, generateUrl, directGet } = require('../util/HttpUtil.js');
|
|
||||||
const Server = require('../server/server.js');
|
|
||||||
const { printLog } = require('../util/CommonUtil.js');
|
|
||||||
|
|
||||||
const rule = require('./rule/rule_replace_request_protocol.js');
|
|
||||||
|
|
||||||
testWrapper();
|
|
||||||
|
|
||||||
function testWrapper() {
|
|
||||||
describe('Rule replaceRequestProtocol should be working', () => {
|
|
||||||
let proxyServer;
|
|
||||||
let serverInstance;
|
|
||||||
|
|
||||||
beforeAll((done) => {
|
|
||||||
jasmine.DEFAULT_TIMEOUT_INTERVAL = 50000;
|
|
||||||
printLog('Start server for rule_replace_request_protocol');
|
|
||||||
|
|
||||||
serverInstance = new Server();
|
|
||||||
|
|
||||||
proxyServer = ProxyServerUtil.proxyServerWithRule(rule);
|
|
||||||
|
|
||||||
setTimeout(() => {
|
|
||||||
done();
|
|
||||||
}, 2000);
|
|
||||||
});
|
|
||||||
|
|
||||||
afterAll(() => {
|
|
||||||
serverInstance && serverInstance.close();
|
|
||||||
proxyServer && proxyServer.close();
|
|
||||||
printLog('Close server for rule_replace_request_protocol');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Should replace request protocol in PROXY https request', done => {
|
|
||||||
const url = generateUrl('https', '/test/normal_request1');
|
|
||||||
const httpUrl = url.replace('https', 'http');
|
|
||||||
const token = 'proxy_request1_token_' + Date.now();
|
|
||||||
proxyGet(url, {}, { token })
|
|
||||||
.then(proxyRes => {
|
|
||||||
expect(proxyRes.body).toEqual('body_normal_request1');
|
|
||||||
|
|
||||||
// there should be no https url be requested in proxy, it should be http request
|
|
||||||
expect(serverInstance.getProxyRequestRecord(url)).toBe(null);
|
|
||||||
const httpRecord = serverInstance.getProxyRequestRecord(httpUrl);
|
|
||||||
expect(httpRecord.headers.token).toEqual(token);
|
|
||||||
expect(httpRecord.headers['via-proxy']).toEqual('true');
|
|
||||||
done();
|
|
||||||
})
|
|
||||||
.catch(error => {
|
|
||||||
console.error('Error happened in proxy the https request: ', error);
|
|
||||||
done.fail('error happened in proxy the https request');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Should not replace protocol in PROXY http request', done => {
|
|
||||||
const url = generateUrl('http', '/test/normal_request2');
|
|
||||||
const token = 'proxy_request2_token_' + Date.now();
|
|
||||||
proxyGet(url, {}, { token })
|
|
||||||
.then(proxyRes => {
|
|
||||||
expect(proxyRes.body).toEqual('body_normal_request2');
|
|
||||||
const requestRecord = serverInstance.getProxyRequestRecord(url);
|
|
||||||
expect(requestRecord).not.toBe(null);
|
|
||||||
expect(requestRecord.headers.token).toEqual(token);
|
|
||||||
expect(requestRecord.headers['via-proxy']).toEqual('true');
|
|
||||||
done();
|
|
||||||
})
|
|
||||||
.catch(error => {
|
|
||||||
console.error('error happened in proxy the http request: ', error);
|
|
||||||
done.fail('error happend in proxy the http request');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Should the direct request still be working with https', done => {
|
|
||||||
const url = generateUrl('https', '/test/normal_request1');
|
|
||||||
const token = 'direct_request1_token_' + Date.now();
|
|
||||||
directGet(url, {}, { token })
|
|
||||||
.then(directRes => {
|
|
||||||
expect(directRes.body).toEqual('body_normal_request1');
|
|
||||||
const requestRecord = serverInstance.getRequestRecord(url);
|
|
||||||
expect(requestRecord.headers.token).toEqual(token);
|
|
||||||
expect(requestRecord.headers['via-proxy']).toBeUndefined();
|
|
||||||
done();
|
|
||||||
})
|
|
||||||
.catch(error => {
|
|
||||||
console.error('error happened in direct https get:', error);
|
|
||||||
done.fail('error happened in direct https get');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Should the direct request still be working with http', done => {
|
|
||||||
const url = generateUrl('http', '/test/normal_request2');
|
|
||||||
const token = 'direct_request1_token_' + Date.now();
|
|
||||||
directGet(url, {}, { token })
|
|
||||||
.then(directRes => {
|
|
||||||
expect(directRes.body).toEqual('body_normal_request2');
|
|
||||||
const requestRecord = serverInstance.getRequestRecord(url);
|
|
||||||
expect(requestRecord.headers.token).toEqual(token);
|
|
||||||
expect(requestRecord.headers['via-proxy']).toBeUndefined();
|
|
||||||
done();
|
|
||||||
})
|
|
||||||
.catch(error => {
|
|
||||||
console.error('error happened in direct http get:', error);
|
|
||||||
done.fail('error happened in direct http get');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
@ -1,87 +0,0 @@
|
|||||||
/*
|
|
||||||
* test for rule replaceResponseData rule
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
const ProxyServerUtil = require('../util/ProxyServerUtil.js');
|
|
||||||
const { proxyGet, generateUrl, directGet } = require('../util/HttpUtil.js');
|
|
||||||
const Server = require('../server/server.js');
|
|
||||||
const { printLog } = require('../util/CommonUtil.js');
|
|
||||||
|
|
||||||
const rule = require('./rule/rule_replace_response_data.js');
|
|
||||||
|
|
||||||
testWrapper('http');
|
|
||||||
testWrapper('https');
|
|
||||||
|
|
||||||
function testWrapper(protocol) {
|
|
||||||
describe('Rule replaceResponseData should be working in :' + protocol, () => {
|
|
||||||
let proxyServer;
|
|
||||||
let serverInstance;
|
|
||||||
|
|
||||||
beforeAll((done) => {
|
|
||||||
jasmine.DEFAULT_TIMEOUT_INTERVAL = 50000;
|
|
||||||
printLog('Start server for rule_replace_response_data');
|
|
||||||
|
|
||||||
serverInstance = new Server();
|
|
||||||
|
|
||||||
proxyServer = ProxyServerUtil.proxyServerWithRule(rule);
|
|
||||||
|
|
||||||
setTimeout(() => {
|
|
||||||
done();
|
|
||||||
}, 2000);
|
|
||||||
});
|
|
||||||
|
|
||||||
afterAll(() => {
|
|
||||||
serverInstance && serverInstance.close();
|
|
||||||
proxyServer && proxyServer.close();
|
|
||||||
printLog('Close server for rule_replace_response_data');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Should replace the header in proxy if assertion is true', done => {
|
|
||||||
const url = generateUrl(protocol, '/test/normal_request1');
|
|
||||||
proxyGet(url)
|
|
||||||
.then(proxyRes => {
|
|
||||||
expect(proxyRes.statusCode).toEqual(200);
|
|
||||||
expect(proxyRes.body).toEqual('body_normal_request1_hello_world!');
|
|
||||||
|
|
||||||
directGet(url)
|
|
||||||
.then(directRes => {
|
|
||||||
expect(directRes.statusCode).toEqual(200);
|
|
||||||
expect(directRes.body).toEqual('body_normal_request1');
|
|
||||||
done();
|
|
||||||
})
|
|
||||||
.catch(error => {
|
|
||||||
console.error('error happened in direct get: ', error);
|
|
||||||
done.fail('error happened in direct get');
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.catch(error => {
|
|
||||||
console.error('error happened in proxy get: ', error);
|
|
||||||
done.fail('error happened in proxy get');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Should not replace the header in proxy if assertion is false', done => {
|
|
||||||
const url = generateUrl(protocol, '/test/normal_request2');
|
|
||||||
proxyGet(url)
|
|
||||||
.then(proxyRes => {
|
|
||||||
expect(proxyRes.statusCode).toEqual(200);
|
|
||||||
expect(proxyRes.body).toEqual('body_normal_request2');
|
|
||||||
|
|
||||||
directGet(url)
|
|
||||||
.then(directRes => {
|
|
||||||
expect(directRes.statusCode).toEqual(200);
|
|
||||||
expect(directRes.body).toEqual(proxyRes.body);
|
|
||||||
done();
|
|
||||||
})
|
|
||||||
.catch(error => {
|
|
||||||
console.error('error happened in direct get: ', error);
|
|
||||||
done.fail('error happened in direct get');
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.catch(error => {
|
|
||||||
console.error('error happened in proxy get: ', error);
|
|
||||||
done.fail('error happened in proxy get');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
@ -1,91 +0,0 @@
|
|||||||
/*
|
|
||||||
* test for rule replaceResponseHeader rule
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
const ProxyServerUtil = require('../util/ProxyServerUtil.js');
|
|
||||||
const { proxyGet, generateUrl, directGet } = require('../util/HttpUtil.js');
|
|
||||||
const Server = require('../server/server.js');
|
|
||||||
const { printLog } = require('../util/CommonUtil.js');
|
|
||||||
|
|
||||||
const rule = require('./rule/rule_replace_response_header.js');
|
|
||||||
|
|
||||||
testWrapper('http');
|
|
||||||
testWrapper('https');
|
|
||||||
|
|
||||||
function testWrapper(protocol) {
|
|
||||||
describe('Rule replaceResponseHeader should be working in :' + protocol, () => {
|
|
||||||
let proxyServer;
|
|
||||||
let serverInstance;
|
|
||||||
|
|
||||||
beforeAll((done) => {
|
|
||||||
jasmine.DEFAULT_TIMEOUT_INTERVAL = 50000;
|
|
||||||
printLog('Start server for rule_replace_response_header_spec');
|
|
||||||
|
|
||||||
serverInstance = new Server();
|
|
||||||
|
|
||||||
proxyServer = ProxyServerUtil.proxyServerWithRule(rule);
|
|
||||||
|
|
||||||
setTimeout(() => {
|
|
||||||
done();
|
|
||||||
}, 2000);
|
|
||||||
});
|
|
||||||
|
|
||||||
afterAll(() => {
|
|
||||||
serverInstance && serverInstance.close();
|
|
||||||
proxyServer && proxyServer.close();
|
|
||||||
printLog('Close server for rule_replace_response_header_spec');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Should replace the header in proxy if assertion is true', done => {
|
|
||||||
const url = generateUrl(protocol, '/test/normal_request1');
|
|
||||||
proxyGet(url)
|
|
||||||
.then(proxyRes => {
|
|
||||||
expect(proxyRes.statusCode).toEqual(200);
|
|
||||||
expect(proxyRes.headers.replacedheaderkey).toEqual('replacedHeader_value_in_rule');
|
|
||||||
expect(proxyRes.body).toEqual('body_normal_request1');
|
|
||||||
|
|
||||||
directGet(url)
|
|
||||||
.then(directRes => {
|
|
||||||
expect(directRes.statusCode).toEqual(200);
|
|
||||||
expect(directRes.headers.replacedheaderkey).toBeUndefined();
|
|
||||||
expect(directRes.body).toEqual(proxyRes.body);
|
|
||||||
done();
|
|
||||||
})
|
|
||||||
.catch(error => {
|
|
||||||
console.error('error happened in direct get: ', error);
|
|
||||||
done.fail('error happened in direct get');
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.catch(error => {
|
|
||||||
console.error('error happened in proxy get: ', error);
|
|
||||||
done.fail('error happened in proxy get');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Should not replace the header in proxy if the assertion is false', done => {
|
|
||||||
const url = generateUrl(protocol, '/test/normal_request2');
|
|
||||||
proxyGet(url)
|
|
||||||
.then(proxyRes => {
|
|
||||||
expect(proxyRes.statusCode).toEqual(200);
|
|
||||||
expect(proxyRes.headers.replacedheaderkey).toBeUndefined();
|
|
||||||
expect(proxyRes.body).toEqual('body_normal_request2');
|
|
||||||
|
|
||||||
directGet(url)
|
|
||||||
.then(directRes => {
|
|
||||||
expect(directRes.statusCode).toEqual(200);
|
|
||||||
expect(directRes.headers.replacedheaderkey).toBeUndefined();
|
|
||||||
expect(directRes.body).toEqual(proxyRes.body);
|
|
||||||
done();
|
|
||||||
})
|
|
||||||
.catch(error => {
|
|
||||||
console.error('error happened in direct get: ', error);
|
|
||||||
done.fail('error happened in direct get');
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.catch(error => {
|
|
||||||
console.error('error happened in proxy get: ', error);
|
|
||||||
done.fail('error happened in proxy get');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
@ -1,87 +0,0 @@
|
|||||||
/*
|
|
||||||
* test for rule replaceResponseStatus rule
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
const ProxyServerUtil = require('../util/ProxyServerUtil.js');
|
|
||||||
const { proxyGet, generateUrl, directGet } = require('../util/HttpUtil.js');
|
|
||||||
const Server = require('../server/server.js');
|
|
||||||
const { printLog } = require('../util/CommonUtil.js');
|
|
||||||
|
|
||||||
const rule = require('./rule/rule_replace_response_status_code.js');
|
|
||||||
|
|
||||||
testWrapper('http');
|
|
||||||
testWrapper('https');
|
|
||||||
|
|
||||||
function testWrapper(protocol) {
|
|
||||||
describe('Rule replaceResponseStatus should be working in :' + protocol, () => {
|
|
||||||
let proxyServer;
|
|
||||||
let serverInstance;
|
|
||||||
|
|
||||||
beforeAll((done) => {
|
|
||||||
jasmine.DEFAULT_TIMEOUT_INTERVAL = 50000;
|
|
||||||
printLog('Start server for rule_replace_response_status_code');
|
|
||||||
|
|
||||||
serverInstance = new Server();
|
|
||||||
|
|
||||||
proxyServer = ProxyServerUtil.proxyServerWithRule(rule);
|
|
||||||
|
|
||||||
setTimeout(() => {
|
|
||||||
done();
|
|
||||||
}, 2000);
|
|
||||||
});
|
|
||||||
|
|
||||||
afterAll(() => {
|
|
||||||
serverInstance && serverInstance.close();
|
|
||||||
proxyServer && proxyServer.close();
|
|
||||||
printLog('Close server for rule_replace_response_status_code');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Should replace the status code in proxy if assertion is true', done => {
|
|
||||||
const url = generateUrl(protocol, '/test/normal_request1');
|
|
||||||
proxyGet(url)
|
|
||||||
.then(proxyRes => {
|
|
||||||
expect(proxyRes.statusCode).toEqual(302);
|
|
||||||
expect(proxyRes.headers.location).toEqual('www.taobao.com');
|
|
||||||
|
|
||||||
directGet(url)
|
|
||||||
.then(directRes => {
|
|
||||||
expect(directRes.statusCode).toEqual(200);
|
|
||||||
expect(directRes.body).toEqual('body_normal_request1');
|
|
||||||
done();
|
|
||||||
})
|
|
||||||
.catch(error => {
|
|
||||||
console.error('error happened in direct get: ', error);
|
|
||||||
done.fail('error happened in direct get');
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.catch(error => {
|
|
||||||
console.error('error happened in proxy get: ', error);
|
|
||||||
done.fail('error happened in proxy get');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Should not replace the status code in proxy if assertion is false', done => {
|
|
||||||
const url = generateUrl(protocol, '/test/normal_request2');
|
|
||||||
proxyGet(url)
|
|
||||||
.then(proxyRes => {
|
|
||||||
expect(proxyRes.statusCode).toEqual(200);
|
|
||||||
expect(proxyRes.body).toEqual('body_normal_request2');
|
|
||||||
|
|
||||||
directGet(url)
|
|
||||||
.then(directRes => {
|
|
||||||
expect(directRes.statusCode).toEqual(200);
|
|
||||||
expect(directRes.body).toEqual('body_normal_request2');
|
|
||||||
done();
|
|
||||||
})
|
|
||||||
.catch(error => {
|
|
||||||
console.error('error happened in direct get: ', error);
|
|
||||||
done.fail('error happened in direct get');
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.catch(error => {
|
|
||||||
console.error('error happened in proxy get: ', error);
|
|
||||||
done.fail('error happened in proxy get');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
@ -1,87 +0,0 @@
|
|||||||
/*
|
|
||||||
* test for rule shouldInterceptHttpsReq rule
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
const ProxyServerUtil = require('../util/ProxyServerUtil.js');
|
|
||||||
const { proxyGet, directGet } = require('../util/HttpUtil.js');
|
|
||||||
const Server = require('../server/server.js');
|
|
||||||
const { printLog } = require('../util/CommonUtil.js');
|
|
||||||
|
|
||||||
const rule = require('./rule/rule_should_intercept_https_req.js');
|
|
||||||
|
|
||||||
testWrapper();
|
|
||||||
|
|
||||||
function testWrapper() {
|
|
||||||
describe('Rule shouldInterceptHttpsReq should be working', () => {
|
|
||||||
let proxyServer;
|
|
||||||
let serverInstance;
|
|
||||||
|
|
||||||
beforeAll(done => {
|
|
||||||
jasmine.DEFAULT_TIMEOUT_INTERVAL = 50000;
|
|
||||||
printLog('Start server for test_rule_should_intercept_https_req_spec');
|
|
||||||
|
|
||||||
serverInstance = new Server();
|
|
||||||
|
|
||||||
proxyServer = ProxyServerUtil.proxyServerWithRule(rule, {
|
|
||||||
forceProxyHttps: false
|
|
||||||
});
|
|
||||||
|
|
||||||
setTimeout(() => {
|
|
||||||
done();
|
|
||||||
}, 2000);
|
|
||||||
});
|
|
||||||
|
|
||||||
afterAll(() => {
|
|
||||||
serverInstance && serverInstance.close();
|
|
||||||
proxyServer && proxyServer.close();
|
|
||||||
printLog('Close server for test_rule_should_intercept_https_req_spec');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Should replace the header in proxy if assertion is true', done => {
|
|
||||||
const url = 'https://localhost:3001/test';
|
|
||||||
|
|
||||||
proxyGet(url)
|
|
||||||
.then(proxyRes => {
|
|
||||||
directGet(url)
|
|
||||||
.then(directRes => {
|
|
||||||
expect(proxyRes.statusCode).toEqual(200);
|
|
||||||
|
|
||||||
expect(directRes.statusCode).toEqual(200);
|
|
||||||
expect(directRes.body + '_hello_world').toEqual(proxyRes.body);
|
|
||||||
done();
|
|
||||||
})
|
|
||||||
.catch(error => {
|
|
||||||
console.error('error happened in direct get: ', error);
|
|
||||||
done.fail('error happened in direct get');
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.catch(error => {
|
|
||||||
console.error('error happened in proxy get: ', error);
|
|
||||||
done.fail('error happened in proxy get');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Should not replace the header in proxy if assertion is false', done => {
|
|
||||||
const url = 'https://localhost:3002/test';
|
|
||||||
proxyGet(url)
|
|
||||||
.then(proxyRes => {
|
|
||||||
expect(proxyRes.statusCode).toEqual(200);
|
|
||||||
|
|
||||||
directGet(url)
|
|
||||||
.then(directRes => {
|
|
||||||
expect(directRes.statusCode).toEqual(200);
|
|
||||||
expect(directRes.body.replace(/\s/g, '')).toEqual(proxyRes.body.replace(/\s/g, ''));
|
|
||||||
done();
|
|
||||||
})
|
|
||||||
.catch(error => {
|
|
||||||
console.error('error happened in direct get: ', error);
|
|
||||||
done.fail('error happened in direct get');
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.catch(error => {
|
|
||||||
console.error('error happened in proxy get: ', error);
|
|
||||||
done.fail('error happened in proxy get');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
@ -1,55 +0,0 @@
|
|||||||
/*
|
|
||||||
* test for rule shouldUseLocal
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
const ProxyServerUtil = require('../util/ProxyServerUtil.js');
|
|
||||||
const { proxyGet, generateUrl } = require('../util/HttpUtil.js');
|
|
||||||
const Server = require('../server/server.js');
|
|
||||||
const { printLog } = require('../util/CommonUtil.js');
|
|
||||||
|
|
||||||
const rule = require('./rule/rule_should_use_local_response.js');
|
|
||||||
|
|
||||||
const expectedLocalBody = 'handled_in_local_response';
|
|
||||||
|
|
||||||
testWrapper('http');
|
|
||||||
testWrapper('https');
|
|
||||||
|
|
||||||
function testWrapper(protocol) {
|
|
||||||
describe('Rule shouldUseLocalResponse should be working in :' + protocol, () => {
|
|
||||||
let proxyServer;
|
|
||||||
let serverInstance;
|
|
||||||
|
|
||||||
beforeAll((done) => {
|
|
||||||
jasmine.DEFAULT_TIMEOUT_INTERVAL = 50000;
|
|
||||||
printLog('Start server for rule_shouldUseLocalResponse_spec');
|
|
||||||
|
|
||||||
serverInstance = new Server();
|
|
||||||
|
|
||||||
proxyServer = ProxyServerUtil.proxyServerWithRule(rule);
|
|
||||||
|
|
||||||
setTimeout(() => {
|
|
||||||
done();
|
|
||||||
}, 2000);
|
|
||||||
});
|
|
||||||
|
|
||||||
afterAll(() => {
|
|
||||||
serverInstance && serverInstance.close();
|
|
||||||
proxyServer && proxyServer.close();
|
|
||||||
printLog('Close server for rule_shouldUseLocalResponse_spec');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Should use local response if the assertion is true', done => {
|
|
||||||
const url = generateUrl(protocol, '/test/uselocal');
|
|
||||||
proxyGet(url, {})
|
|
||||||
.then(res => {
|
|
||||||
expect(res.body).toEqual(expectedLocalBody);
|
|
||||||
expect(res.headers['via-proxy-local']).toEqual('true');
|
|
||||||
done();
|
|
||||||
}).catch((error) => {
|
|
||||||
console.log('error happened in proxy get for shouldUseLocal: ', error);
|
|
||||||
done.fail('error happened when test shouldUseLocal rule');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
80
test/util.js
Normal file
80
test/util.js
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
const request = require('request');
|
||||||
|
const assert = require('assert');
|
||||||
|
// TODO
|
||||||
|
const { freshRequire, getFreePort } = require('../lib/util.js');
|
||||||
|
|
||||||
|
function basicProxyRequest(proxyHost, method, url, headers, qs, payload) {
|
||||||
|
assert(method && url, 'method and url are required');
|
||||||
|
assert(proxyHost, 'proxyHost is required');
|
||||||
|
headers = Object.assign({
|
||||||
|
'via-anyproxy': 'true',
|
||||||
|
}, headers || {});
|
||||||
|
|
||||||
|
const requestOpt = {
|
||||||
|
method,
|
||||||
|
url,
|
||||||
|
headers,
|
||||||
|
followRedirect: false,
|
||||||
|
rejectUnauthorized: false,
|
||||||
|
qs,
|
||||||
|
proxy: proxyHost,
|
||||||
|
};
|
||||||
|
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const callback = (error, response, body) => {
|
||||||
|
if (error) {
|
||||||
|
reject(error);
|
||||||
|
} else {
|
||||||
|
resolve({
|
||||||
|
response,
|
||||||
|
body,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if (payload) {
|
||||||
|
payload.pipe(request(requestOpt, callback));
|
||||||
|
} else {
|
||||||
|
request(requestOpt, callback);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const DEFAULT_OPTIONS = {
|
||||||
|
type: 'http',
|
||||||
|
port: 8001,
|
||||||
|
webInterface: false,
|
||||||
|
wsIntercept: true,
|
||||||
|
// throttle: 10000, // optional, speed limit in kb/s
|
||||||
|
forceProxyHttps: true, // intercept https as well
|
||||||
|
dangerouslyIgnoreUnauthorized: true,
|
||||||
|
silent: false //optional, do not print anything into terminal. do not set it when you are still debugging.
|
||||||
|
};
|
||||||
|
|
||||||
|
async function proxyServerWithRule(rule, overrideConfig) {
|
||||||
|
const AnyProxy = freshRequire('../proxy.js');
|
||||||
|
const freeportA = await getFreePort();
|
||||||
|
const freeportB = await getFreePort();
|
||||||
|
const options = Object.assign(DEFAULT_OPTIONS, {
|
||||||
|
port: freeportA,
|
||||||
|
webInterface: {
|
||||||
|
enable: true,
|
||||||
|
webPort: freeportB,
|
||||||
|
}
|
||||||
|
}, overrideConfig || {});
|
||||||
|
options.rule = rule;
|
||||||
|
|
||||||
|
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const instance = new AnyProxy.ProxyServer(options);
|
||||||
|
instance.on('error', reject);
|
||||||
|
instance.on('ready', () => {
|
||||||
|
resolve(instance);
|
||||||
|
});
|
||||||
|
instance.start();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
basicProxyRequest,
|
||||||
|
proxyServerWithRule,
|
||||||
|
};
|
@ -1,291 +0,0 @@
|
|||||||
/**
|
|
||||||
*
|
|
||||||
* The utility class for test
|
|
||||||
*/
|
|
||||||
const color = require('colorful');
|
|
||||||
|
|
||||||
function _isDeepEqual(source, target) {
|
|
||||||
// if the objects are Array
|
|
||||||
if (Array.isArray(source) && Array.isArray(target)) {
|
|
||||||
if (source.length !== target.length) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
let _isEqual = true;
|
|
||||||
for (let i = 0; i < source.length; i++) {
|
|
||||||
if (!_isDeepEqual(source[i], target[i])) {
|
|
||||||
_isEqual = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return _isEqual;
|
|
||||||
}
|
|
||||||
|
|
||||||
// if the source and target are just object
|
|
||||||
if (typeof source === 'object' && typeof target === 'object') {
|
|
||||||
let _isEqual = true;
|
|
||||||
for (const key in source) {
|
|
||||||
if (!_isDeepEqual(source[key], target[key])) {
|
|
||||||
_isEqual = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return _isEqual;
|
|
||||||
}
|
|
||||||
|
|
||||||
return source === target;
|
|
||||||
}
|
|
||||||
/*
|
|
||||||
* Compare whether tow object are equal
|
|
||||||
*/
|
|
||||||
function isObjectEqual(source = {}, target = {}, url = '') {
|
|
||||||
source = Object.assign({}, source);
|
|
||||||
target = Object.assign({}, target);
|
|
||||||
let isEqual = true;
|
|
||||||
|
|
||||||
for (const key in source) {
|
|
||||||
isEqual = isEqual && _isDeepEqual(source[key], target[key]);
|
|
||||||
|
|
||||||
if (!isEqual) {
|
|
||||||
console.info('source object :', source);
|
|
||||||
console.info('target object :', target);
|
|
||||||
printError(`different key in isObjectEqual is: "${key}", source is "${source[key]}",
|
|
||||||
target is "${target[key]}" the url is ${url}`);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
delete source[key];
|
|
||||||
delete target[key];
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const key in target) {
|
|
||||||
isEqual = isEqual && source[key] === target[key];
|
|
||||||
|
|
||||||
if (!isEqual) {
|
|
||||||
console.info('source object :', source);
|
|
||||||
console.info('target object :', target);
|
|
||||||
printError(`different key in isObjectEqual is: "${key}", source is "${source[key]}",
|
|
||||||
target is "${target[key]}" the url is ${url}`);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
delete source[key];
|
|
||||||
delete target[key];
|
|
||||||
}
|
|
||||||
|
|
||||||
return isEqual;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Compare the header between direct with proxy
|
|
||||||
* Will exclude the header(s) which modified by proxy
|
|
||||||
*/
|
|
||||||
function isCommonResHeaderEqual(directHeaders, proxyHeaders, requestUrl) {
|
|
||||||
directHeaders = Object.assign({}, directHeaders);
|
|
||||||
proxyHeaders = Object.assign({}, proxyHeaders);
|
|
||||||
let isEqual = true;
|
|
||||||
const mustEqualFileds = []; // the fileds that have to be equal, or the assert will be failed
|
|
||||||
|
|
||||||
if (!/gzip/i.test(directHeaders['content-encoding'])) {
|
|
||||||
// if the content is gzipped, proxy will unzip and remove the header
|
|
||||||
mustEqualFileds.push('content-encoding');
|
|
||||||
}
|
|
||||||
mustEqualFileds.push('content-type');
|
|
||||||
mustEqualFileds.push('cache-control');
|
|
||||||
mustEqualFileds.push('allow');
|
|
||||||
|
|
||||||
// ensure the required fileds are same
|
|
||||||
mustEqualFileds.forEach(filedName => {
|
|
||||||
isEqual = directHeaders[filedName] === proxyHeaders[filedName];
|
|
||||||
delete directHeaders[filedName];
|
|
||||||
delete proxyHeaders[filedName];
|
|
||||||
});
|
|
||||||
|
|
||||||
// remained filed are good to be same, but are allowed to be different
|
|
||||||
// will warn out those different fileds
|
|
||||||
for (const key in directHeaders) {
|
|
||||||
if (!_isDeepEqual(directHeaders[key], proxyHeaders[key])) {
|
|
||||||
printWarn(`key "${key}" of two response headers are different in request "${requestUrl}" :
|
|
||||||
direct is: "${directHeaders[key]}", proxy is: "${proxyHeaders[key]}"`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return isEqual;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Compare the request between direct with proxy
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
function isCommonReqEqual(url, serverInstance) {
|
|
||||||
console.info('==> trying to get the url ', url);
|
|
||||||
try {
|
|
||||||
let isEqual = true;
|
|
||||||
|
|
||||||
const directReqObj = serverInstance.getRequestRecord(url);
|
|
||||||
const proxyReqObj = serverInstance.getProxyRequestRecord(url);
|
|
||||||
|
|
||||||
// ensure the proxy header is correct
|
|
||||||
isEqual = isEqual && proxyReqObj.headers['via-proxy'] === 'true';
|
|
||||||
delete proxyReqObj.headers['via-proxy'];
|
|
||||||
|
|
||||||
directReqObj.headers['content-type'] = trimFormContentType(directReqObj.headers['content-type']);
|
|
||||||
proxyReqObj.headers['content-type'] = trimFormContentType(proxyReqObj.headers['content-type']);
|
|
||||||
|
|
||||||
// avoid compare content-length header via proxy
|
|
||||||
delete directReqObj.headers['content-length'];
|
|
||||||
delete proxyReqObj.headers['content-length'];
|
|
||||||
delete directReqObj.headers['transfer-encoding'];
|
|
||||||
delete proxyReqObj.headers['transfer-encoding'];
|
|
||||||
|
|
||||||
// delete the headers that should not be passed by AnyProxy
|
|
||||||
delete directReqObj.headers.connection;
|
|
||||||
delete proxyReqObj.headers.connection;
|
|
||||||
|
|
||||||
// delete the headers related to websocket establishment
|
|
||||||
const directHeaderKeys = Object.keys(directReqObj.headers);
|
|
||||||
directHeaderKeys.forEach((key) => {
|
|
||||||
// if the key matchs 'sec-websocket', delete it
|
|
||||||
if (/sec-websocket/ig.test(key)) {
|
|
||||||
delete directReqObj.headers[key];
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const proxyHeaderKeys = Object.keys(proxyReqObj.headers);
|
|
||||||
proxyHeaderKeys.forEach((key) => {
|
|
||||||
// if the key matchs 'sec-websocaket', delete it
|
|
||||||
if (/sec-websocket/ig.test(key)) {
|
|
||||||
delete proxyReqObj.headers[key];
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
isEqual = isEqual && directReqObj.url === proxyReqObj.url;
|
|
||||||
isEqual = isEqual && isObjectEqual(directReqObj.headers, proxyReqObj.headers, url);
|
|
||||||
isEqual = isEqual && directReqObj.body === proxyReqObj.body;
|
|
||||||
return isEqual;
|
|
||||||
} catch (e) {
|
|
||||||
console.error(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* for multipart-form, the boundary will be different with each update, we trim it here
|
|
||||||
*/
|
|
||||||
function trimFormContentType(contentType = '') {
|
|
||||||
return contentType.replace(/boundary.*/, '');
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function printLog(content) {
|
|
||||||
console.log(color.blue('==LOG==: ' + content));
|
|
||||||
}
|
|
||||||
|
|
||||||
function printWarn(content) {
|
|
||||||
console.log(color.magenta('==WARN==: ' + content));
|
|
||||||
}
|
|
||||||
|
|
||||||
function printError(content) {
|
|
||||||
console.log(color.red('==ERROR==: ' + content));
|
|
||||||
}
|
|
||||||
|
|
||||||
function printHilite(content) {
|
|
||||||
console.log(color.yellow('==LOG==: ' + content));
|
|
||||||
}
|
|
||||||
|
|
||||||
function parseUrlQuery(string = '') {
|
|
||||||
const parameterArray = string.split('&');
|
|
||||||
const parsedObj = {};
|
|
||||||
parameterArray.forEach((parameter) => {
|
|
||||||
// 获取等号的位置
|
|
||||||
const indexOfEqual = parameter.indexOf('=');
|
|
||||||
const name = parameter.substr(0, indexOfEqual);
|
|
||||||
const value = parameter.substr(indexOfEqual + 1);
|
|
||||||
parsedObj[name] = value;
|
|
||||||
});
|
|
||||||
return parsedObj;
|
|
||||||
}
|
|
||||||
|
|
||||||
function stringSimilarity(a, b, precision = 2) {
|
|
||||||
let similarity = '0%';
|
|
||||||
let isCongruent = false;
|
|
||||||
if (a && b) {
|
|
||||||
const targetLen = Math.max(a.length, b.length);
|
|
||||||
targetLen > 1000 ?
|
|
||||||
similarity = simHasH(a, b) :
|
|
||||||
similarity = LevenshteinSimilarity(a, b);
|
|
||||||
isCongruent = similarity === 100;
|
|
||||||
similarity = similarity.toFixed(precision) + '%';
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
isCongruent,
|
|
||||||
similarity
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* simhash similarity
|
|
||||||
*/
|
|
||||||
function simHasH(a, b) {
|
|
||||||
const simhash = require('node-simhash');
|
|
||||||
return (simhash.compare(a, b) * 100);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Levenshtein Distance
|
|
||||||
*/
|
|
||||||
function LevenshteinSimilarity(a, b) {
|
|
||||||
let cost;
|
|
||||||
const maxLen = Math.max(a.length, b.length);
|
|
||||||
const minOfThree = (numa, numb, numc) => {
|
|
||||||
if (numa > numb) {
|
|
||||||
return numb > numc ? numc : numb;
|
|
||||||
} else {
|
|
||||||
return numa > numc ? numc : numa;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (a.length === 0) cost = b.length;
|
|
||||||
if (b.length === 0) cost = a.length;
|
|
||||||
|
|
||||||
if (a.length > b.length) {
|
|
||||||
const tmp = a;
|
|
||||||
a = b;
|
|
||||||
b = tmp;
|
|
||||||
}
|
|
||||||
|
|
||||||
const row = [];
|
|
||||||
for (let i = 0; i <= a.length; i++) {
|
|
||||||
row[i] = i;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (let i = 1; i <= b.length; i++) {
|
|
||||||
let prev = i;
|
|
||||||
for (let j = 1; j <= a.length; j++) {
|
|
||||||
let val;
|
|
||||||
if (b.charAt(i - 1) === a.charAt(j - 1)) {
|
|
||||||
val = row[j - 1];
|
|
||||||
} else {
|
|
||||||
val = minOfThree(row[j - 1] + 1, prev + 1, row[j] + 1);
|
|
||||||
}
|
|
||||||
row[j - 1] = prev;
|
|
||||||
prev = val;
|
|
||||||
}
|
|
||||||
row[a.length] = prev;
|
|
||||||
}
|
|
||||||
cost = row[a.length];
|
|
||||||
return ((maxLen - cost) / maxLen * 100);
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
isObjectEqual,
|
|
||||||
isCommonResHeaderEqual,
|
|
||||||
printLog,
|
|
||||||
printWarn,
|
|
||||||
printError,
|
|
||||||
printHilite,
|
|
||||||
isCommonReqEqual,
|
|
||||||
parseUrlQuery,
|
|
||||||
stringSimilarity,
|
|
||||||
isArrayEqual: _isDeepEqual
|
|
||||||
};
|
|
@ -1,373 +0,0 @@
|
|||||||
/* eslint prefer-arrow-callback: 0 */
|
|
||||||
/**
|
|
||||||
* An util to make the request out
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
const request = require('request');
|
|
||||||
const fs = require('fs');
|
|
||||||
const WebSocket = require('ws');
|
|
||||||
const tunnel = require('tunnel');
|
|
||||||
const stream = require('stream');
|
|
||||||
const nodeUrl = require('url');
|
|
||||||
|
|
||||||
const PROXY_HOST = 'http://localhost:8001';
|
|
||||||
const SOCKET_PROXY_HOST = 'http://localhost:8001';
|
|
||||||
|
|
||||||
const HTTP_SERVER_BASE = 'http://localhost:3000';
|
|
||||||
const HTTPS_SERVER_BASE = 'https://localhost:3001';
|
|
||||||
const WS_SERVER_BASE = 'ws://localhost:3000';
|
|
||||||
const WSS_SERVER_BASE = 'wss://localhost:3001';
|
|
||||||
const DEFAULT_CHUNK_COLLECT_THRESHOLD = 20 * 1024 * 1024; // about 20 mb
|
|
||||||
|
|
||||||
const SOCKE_PROXY_URL_OBJ = nodeUrl.parse(SOCKET_PROXY_HOST);
|
|
||||||
|
|
||||||
class commonStream extends stream.Readable {
|
|
||||||
constructor(config) {
|
|
||||||
super({
|
|
||||||
highWaterMark: DEFAULT_CHUNK_COLLECT_THRESHOLD * 5
|
|
||||||
});
|
|
||||||
}
|
|
||||||
_read(size) {}
|
|
||||||
}
|
|
||||||
|
|
||||||
function getHostFromUrl(url = '') {
|
|
||||||
const hostReg = /^(https{0,1}:\/\/)(\w+)/;
|
|
||||||
const match = url.match(hostReg);
|
|
||||||
|
|
||||||
return match && match[2] ? match[2] : '';
|
|
||||||
}
|
|
||||||
|
|
||||||
function getPortFromUrl(url = '') {
|
|
||||||
const portReg = /^https{0,1}:\/\/\w+(:(\d+)){0,1}/;
|
|
||||||
const match = url.match(portReg);
|
|
||||||
let port = match && match[2] ? match[2] : '';
|
|
||||||
|
|
||||||
if (!port) {
|
|
||||||
port = url.indexOf('https://') === 0 ? 443 : 80;
|
|
||||||
}
|
|
||||||
return port;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取url中的path
|
|
||||||
*/
|
|
||||||
function getPathFromUrl(url = '') {
|
|
||||||
const pathReg = /^https{0,1}:\/\/\w+(:\d+){0,1}(.+)/;
|
|
||||||
const match = url.match(pathReg);
|
|
||||||
const path = match && match[3] ? match[2] : url;
|
|
||||||
return path;
|
|
||||||
}
|
|
||||||
|
|
||||||
function proxyRequest(method = 'GET', url, params, headers = {}) {
|
|
||||||
return doRequest(method, url, params, headers, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* 直接请求到真实服务器,不经过代理服务器
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
function directRequest(method = 'GET', url, params, headers = {}) {
|
|
||||||
return doRequest(method, url, params, headers);
|
|
||||||
}
|
|
||||||
|
|
||||||
function directUpload(url, filepath, formParams = {}, headers = {}) {
|
|
||||||
return doUpload(url, 'POST', filepath, formParams, headers);
|
|
||||||
}
|
|
||||||
|
|
||||||
function proxyUpload(url, filepath, formParams = {}, headers = {}) {
|
|
||||||
return doUpload(url, 'POST', filepath, formParams, headers, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
function directPutUpload(url, filepath, formParams = {}, headers = {}) {
|
|
||||||
return doUpload(url, 'PUT', filepath, formParams, headers);
|
|
||||||
}
|
|
||||||
|
|
||||||
function proxyPutUpload(url, filepath, headers = {}) {
|
|
||||||
return doUpload(url, 'PUT', filepath, headers, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param params {String} json类型或file路径
|
|
||||||
* {Object} key-value形式
|
|
||||||
*/
|
|
||||||
function doRequest(method = 'GET', url, params, headers = {}, isProxy) {
|
|
||||||
headers = Object.assign({}, headers);
|
|
||||||
|
|
||||||
let reqStream = new commonStream();
|
|
||||||
const requestData = {
|
|
||||||
headers,
|
|
||||||
followRedirect: false,
|
|
||||||
rejectUnauthorized: false
|
|
||||||
};
|
|
||||||
|
|
||||||
if (isProxy) {
|
|
||||||
requestData.proxy = PROXY_HOST;
|
|
||||||
requestData.headers['via-proxy'] = 'true';
|
|
||||||
}
|
|
||||||
|
|
||||||
const streamReq = (resolve, reject) => {
|
|
||||||
requestData.headers['content-type'] = 'text/plain'; //otherwise, koa-body could not recognize
|
|
||||||
if (typeof params === 'string') {
|
|
||||||
fs.existsSync(params) ?
|
|
||||||
reqStream = fs.createReadStream(params) :
|
|
||||||
reqStream.push(params);
|
|
||||||
} else if (typeof params === 'object') {
|
|
||||||
reqStream.push(JSON.stringify(params));
|
|
||||||
}
|
|
||||||
reqStream.push(null);
|
|
||||||
reqStream.pipe(request[method.toLowerCase()](
|
|
||||||
url,
|
|
||||||
requestData,
|
|
||||||
(error, response, body) => {
|
|
||||||
if (error) {
|
|
||||||
reject(error);
|
|
||||||
} else {
|
|
||||||
resolve(response);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
))
|
|
||||||
}
|
|
||||||
const commonReq = (resolve, reject) => {
|
|
||||||
requestData.url = url;
|
|
||||||
requestData.method = method;
|
|
||||||
requestData.qs = params;
|
|
||||||
request(
|
|
||||||
requestData,
|
|
||||||
(error, response, body) => {
|
|
||||||
if (error) {
|
|
||||||
reject(error);
|
|
||||||
} else {
|
|
||||||
resolve(response);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
const requestTask = new Promise((resolve, reject) => {
|
|
||||||
if (method === 'POST' || method === 'PUT') {
|
|
||||||
streamReq(resolve, reject);
|
|
||||||
} else {
|
|
||||||
commonReq(resolve, reject);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return requestTask;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function doUpload(url, method, filepath, formParams, headers = {}, isProxy) {
|
|
||||||
let formData = {
|
|
||||||
file: fs.createReadStream(filepath)
|
|
||||||
};
|
|
||||||
|
|
||||||
formData = Object.assign({}, formData, formParams);
|
|
||||||
headers = Object.assign({}, headers);
|
|
||||||
|
|
||||||
const requestData = {
|
|
||||||
formData,
|
|
||||||
url,
|
|
||||||
method,
|
|
||||||
headers,
|
|
||||||
json: true,
|
|
||||||
rejectUnauthorized: false
|
|
||||||
};
|
|
||||||
|
|
||||||
if (isProxy) {
|
|
||||||
requestData.proxy = PROXY_HOST;
|
|
||||||
requestData.headers['via-proxy'] = 'true';
|
|
||||||
}
|
|
||||||
const requestTask = new Promise((resolve, reject) => {
|
|
||||||
request(
|
|
||||||
requestData,
|
|
||||||
(error, response, body) => {
|
|
||||||
if (error) {
|
|
||||||
reject(error);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
resolve(response);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
});
|
|
||||||
return requestTask;
|
|
||||||
}
|
|
||||||
|
|
||||||
function doWebSocket(url, headers = {}, isProxy) {
|
|
||||||
let ws;
|
|
||||||
if (isProxy) {
|
|
||||||
headers['via-proxy'] = 'true';
|
|
||||||
let agent = new tunnel.httpOverHttp({
|
|
||||||
proxy: {
|
|
||||||
hostname: SOCKE_PROXY_URL_OBJ.hostname,
|
|
||||||
port: SOCKE_PROXY_URL_OBJ.port
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
if (url.indexOf('wss') === 0) {
|
|
||||||
agent = new tunnel.httpsOverHttp({
|
|
||||||
rejectUnauthorized: false,
|
|
||||||
proxy: {
|
|
||||||
hostname: SOCKE_PROXY_URL_OBJ.hostname,
|
|
||||||
port: SOCKE_PROXY_URL_OBJ.port
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
ws = new WebSocket(url, {
|
|
||||||
agent,
|
|
||||||
rejectUnauthorized: false,
|
|
||||||
headers
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
ws = new WebSocket(url, {
|
|
||||||
rejectUnauthorized: false,
|
|
||||||
headers
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return ws;
|
|
||||||
}
|
|
||||||
|
|
||||||
function proxyGet(url, params, headers = {}) {
|
|
||||||
return proxyRequest('GET', url, params, headers);
|
|
||||||
}
|
|
||||||
|
|
||||||
function proxyPost(url, params, headers = {}) {
|
|
||||||
return proxyRequest('POST', url, params, headers);
|
|
||||||
}
|
|
||||||
|
|
||||||
function proxyPut(url, params, headers = {}) {
|
|
||||||
return proxyRequest('PUT', url, params, headers);
|
|
||||||
}
|
|
||||||
|
|
||||||
function proxyDelete(url, params, headers = {}) {
|
|
||||||
return proxyRequest('DELETE', url, params, headers);
|
|
||||||
}
|
|
||||||
|
|
||||||
function proxyHead(url, headers = {}) {
|
|
||||||
return proxyRequest('HEAD', url, {}, headers);
|
|
||||||
}
|
|
||||||
|
|
||||||
function proxyOptions(url, headers = {}) {
|
|
||||||
return proxyRequest('OPTIONS', url, {}, headers);
|
|
||||||
}
|
|
||||||
|
|
||||||
function directGet(url, params, headers = {}) {
|
|
||||||
return directRequest('GET', url, params, headers);
|
|
||||||
}
|
|
||||||
|
|
||||||
function directPost(url, params, headers = {}) {
|
|
||||||
return directRequest('POST', url, params, headers);
|
|
||||||
}
|
|
||||||
|
|
||||||
function directPut(url, params, headers = {}) {
|
|
||||||
return directRequest('PUT', url, params, headers);
|
|
||||||
}
|
|
||||||
|
|
||||||
function directDelete(url, params, headers = {}) {
|
|
||||||
return directRequest('DELETE', url, params, headers);
|
|
||||||
}
|
|
||||||
|
|
||||||
function directHead(url, headers = {}) {
|
|
||||||
return directRequest('HEAD', url, {}, headers);
|
|
||||||
}
|
|
||||||
|
|
||||||
function directOptions(url, headers = {}) {
|
|
||||||
return directRequest('OPTIONS', url, {}, headers);
|
|
||||||
}
|
|
||||||
|
|
||||||
function proxyWs(url, headers) {
|
|
||||||
return doWebSocket(url, headers, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
function directWs(url, headers) {
|
|
||||||
return doWebSocket(url, headers);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* generate the final url based on protocol and path
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
function generateUrl(protocol, urlPath) {
|
|
||||||
return protocol === 'http' ? HTTP_SERVER_BASE + urlPath : HTTPS_SERVER_BASE + urlPath;
|
|
||||||
}
|
|
||||||
|
|
||||||
function generateWsUrl(protocol, urlPath) {
|
|
||||||
return protocol === 'wss' ? WSS_SERVER_BASE + urlPath : WS_SERVER_BASE + urlPath;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* verify if the request data is a valid proxy request, by checking specified header
|
|
||||||
*/
|
|
||||||
function isViaProxy(req) {
|
|
||||||
return req.headers['via-proxy'] === 'true';
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* check if url is supported by request moudle
|
|
||||||
*/
|
|
||||||
function isSupportedProtocol(requestPath) {
|
|
||||||
return requestPath.indexOf('http://') === 0 || requestPath.indexOf('https://') === 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* collect all request data in one url
|
|
||||||
*/
|
|
||||||
function getRequestListFromPage(pageUrl, cb) {
|
|
||||||
let _ph;
|
|
||||||
let _page;
|
|
||||||
let _outObj;
|
|
||||||
const phantom = require('phantom');
|
|
||||||
console.log(`collecting requests from ${pageUrl}...`);
|
|
||||||
return phantom.create().then(ph => {
|
|
||||||
_ph = ph;
|
|
||||||
return _ph.createPage();
|
|
||||||
}).then(page => {
|
|
||||||
_page = page;
|
|
||||||
_outObj = _ph.createOutObject();
|
|
||||||
_outObj.urls = [];
|
|
||||||
page.property('onResourceRequested', function (requestData, networkRequest, out) {
|
|
||||||
out.urls.push(requestData);
|
|
||||||
}, _outObj);
|
|
||||||
return _page.open(pageUrl);
|
|
||||||
})
|
|
||||||
.then(status => _outObj.property('urls'))
|
|
||||||
.then(urls => {
|
|
||||||
_page.close();
|
|
||||||
_ph.exit();
|
|
||||||
return urls;
|
|
||||||
})
|
|
||||||
.catch((err) => {
|
|
||||||
console.log(`failed to collecting requests from ${pageUrl}`);
|
|
||||||
console.log(err);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
getHostFromUrl,
|
|
||||||
getPathFromUrl,
|
|
||||||
getPortFromUrl,
|
|
||||||
proxyGet,
|
|
||||||
proxyPost,
|
|
||||||
directGet,
|
|
||||||
directPost,
|
|
||||||
directUpload,
|
|
||||||
proxyUpload,
|
|
||||||
generateUrl,
|
|
||||||
proxyWs,
|
|
||||||
directWs,
|
|
||||||
generateWsUrl,
|
|
||||||
directPut,
|
|
||||||
proxyPut,
|
|
||||||
directDelete,
|
|
||||||
proxyDelete,
|
|
||||||
directHead,
|
|
||||||
proxyHead,
|
|
||||||
directOptions,
|
|
||||||
proxyOptions,
|
|
||||||
directPutUpload,
|
|
||||||
proxyPutUpload,
|
|
||||||
isViaProxy,
|
|
||||||
getRequestListFromPage,
|
|
||||||
directRequest,
|
|
||||||
proxyRequest,
|
|
||||||
isSupportedProtocol
|
|
||||||
};
|
|
@ -1,86 +0,0 @@
|
|||||||
/*
|
|
||||||
* Utility class for creating proxy server, used to create specfied proxy server
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
const util = require('../../lib/util.js');
|
|
||||||
|
|
||||||
const DEFAULT_OPTIONS = {
|
|
||||||
type: 'http',
|
|
||||||
port: 8001,
|
|
||||||
webInterface: {
|
|
||||||
enable: true,
|
|
||||||
webPort: 8002, // optional, port for web interface
|
|
||||||
},
|
|
||||||
wsIntercept: true,
|
|
||||||
throttle: 10000, // optional, speed limit in kb/s
|
|
||||||
forceProxyHttps: true, // intercept https as well
|
|
||||||
dangerouslyIgnoreUnauthorized: true,
|
|
||||||
silent: false //optional, do not print anything into terminal. do not set it when you are still debugging.
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @return An instance of proxy, could be closed by calling `instance.close()`
|
|
||||||
*/
|
|
||||||
function defaultProxyServer(webinterfaceEnable = true) {
|
|
||||||
const AnyProxy = util.freshRequire('../proxy.js');
|
|
||||||
|
|
||||||
const options = util.merge({}, DEFAULT_OPTIONS);
|
|
||||||
util.merge(options, {
|
|
||||||
webInterface: {
|
|
||||||
enable: webinterfaceEnable,
|
|
||||||
webPort: 8002
|
|
||||||
}
|
|
||||||
})
|
|
||||||
const instance = new AnyProxy.ProxyServer(options);
|
|
||||||
instance.on('error', e => {
|
|
||||||
console.log('server instance error', e);
|
|
||||||
});
|
|
||||||
instance.start();
|
|
||||||
return instance;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Create proxy server with rule
|
|
||||||
* @param rules
|
|
||||||
Object, the rule object which contains required intercept method
|
|
||||||
@return An instance of proxy, could be closed by calling `instance.close()`
|
|
||||||
*/
|
|
||||||
function proxyServerWithRule(rule, overrideConfig) {
|
|
||||||
const AnyProxy = util.freshRequire('../proxy.js');
|
|
||||||
|
|
||||||
const options = Object.assign({}, DEFAULT_OPTIONS, overrideConfig);
|
|
||||||
options.rule = rule;
|
|
||||||
|
|
||||||
const instance = new AnyProxy.ProxyServer(options);
|
|
||||||
instance.on('error', e => {
|
|
||||||
console.log('server instance error', e);
|
|
||||||
});
|
|
||||||
instance.start();
|
|
||||||
|
|
||||||
return instance;
|
|
||||||
}
|
|
||||||
|
|
||||||
function proxyServerWithoutHttpsIntercept(rule) {
|
|
||||||
const AnyProxy = util.freshRequire('../proxy.js');
|
|
||||||
|
|
||||||
const options = util.merge({}, DEFAULT_OPTIONS);
|
|
||||||
if (rule) {
|
|
||||||
options.rule = rule;
|
|
||||||
}
|
|
||||||
options.forceProxyHttps = false;
|
|
||||||
|
|
||||||
const instance = new AnyProxy.ProxyServer(options);
|
|
||||||
instance.on('error', e => {
|
|
||||||
console.log('server instance error', e);
|
|
||||||
});
|
|
||||||
instance.start();
|
|
||||||
return instance;
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
defaultProxyServer,
|
|
||||||
proxyServerWithoutHttpsIntercept,
|
|
||||||
proxyServerWithRule
|
|
||||||
};
|
|
27
test/web/webInterface.spec.js
Normal file
27
test/web/webInterface.spec.js
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
const WebInterface = require('../../lib/webInterface.js');
|
||||||
|
const Recorder = require('../../lib/recorder');
|
||||||
|
const urllib = require('urllib');
|
||||||
|
|
||||||
|
describe('WebInterface server', () => {
|
||||||
|
let webServer = null;
|
||||||
|
const webHost = 'http://127.0.0.1:8002'
|
||||||
|
|
||||||
|
beforeAll(async () => {
|
||||||
|
const recorder = new Recorder();
|
||||||
|
webServer = new WebInterface({
|
||||||
|
webPort: 8002,
|
||||||
|
}, recorder);
|
||||||
|
await webServer.start();
|
||||||
|
});
|
||||||
|
|
||||||
|
afterAll(async () => {
|
||||||
|
await webServer.close();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should response qrcode string in /getQrCode', async () => {
|
||||||
|
const response = await urllib.request(`${webHost}/api/getQrCode`);
|
||||||
|
const body = JSON.parse(response.res.data);
|
||||||
|
expect(body.qrImgDom).toMatch('<img src="data:image/');
|
||||||
|
expect(body.url).toBe(`${webHost}/downloadCrt`);
|
||||||
|
});
|
||||||
|
});
|
@ -1,7 +1,7 @@
|
|||||||
/**
|
/**
|
||||||
* AJAX操作工具类
|
* AJAX操作工具类
|
||||||
*/
|
*/
|
||||||
import PromiseUtil from './PromiseUtil';
|
import PromiseUtil from './promiseUtil';
|
||||||
export function getJSON(url, data) {
|
export function getJSON(url, data) {
|
||||||
const d = PromiseUtil.defer();
|
const d = PromiseUtil.defer();
|
||||||
fetch(url + serializeQuery(data))
|
fetch(url + serializeQuery(data))
|
||||||
@ -53,10 +53,10 @@ export function isApiSuccess (response) {
|
|||||||
return response.status === 'success';
|
return response.status === 'success';
|
||||||
}
|
}
|
||||||
|
|
||||||
const ApiUtil = {
|
const apiUtil = {
|
||||||
getJSON,
|
getJSON,
|
||||||
postJSON,
|
postJSON,
|
||||||
isApiSuccess
|
isApiSuccess
|
||||||
};
|
};
|
||||||
|
|
||||||
export default ApiUtil;
|
export default apiUtil;
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
/**
|
/**
|
||||||
* define all Constant variables here
|
* define all constant variables here
|
||||||
*/
|
*/
|
||||||
|
|
||||||
module.exports.MenuKeyMap = {
|
module.exports.MenuKeyMap = {
|
||||||
RECORD_FILTER: 'RECORD_FILTER',
|
RECORD_FILTER: 'RECORD_FILTER',
|
||||||
MAP_LOCAL: 'MAP_LOCAL',
|
MAP_LOCAL: 'MAP_LOCAL',
|
||||||
ROOT_CA: 'ROOT_CA'
|
ROOT_CA: 'ROOT_CA'
|
||||||
};
|
};
|
||||||
|
@ -39,3 +39,4 @@ export function initWs(wsPort = location.port, path = 'do-not-proxy') {
|
|||||||
export default {
|
export default {
|
||||||
initWs: initWs
|
initWs: initWs
|
||||||
};
|
};
|
||||||
|
|
||||||
|
62
web/src/common/apiUtil.js
Normal file
62
web/src/common/apiUtil.js
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
/**
|
||||||
|
* AJAX操作工具类
|
||||||
|
*/
|
||||||
|
import PromiseUtil from './promiseUtil';
|
||||||
|
export function getJSON(url, data) {
|
||||||
|
const d = PromiseUtil.defer();
|
||||||
|
fetch(url + serializeQuery(data))
|
||||||
|
.then((data) => {
|
||||||
|
d.resolve(data.json());
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
console.error(error);
|
||||||
|
d.reject(error);
|
||||||
|
});
|
||||||
|
return d.promise;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function postJSON(url, data) {
|
||||||
|
const d = PromiseUtil.defer();
|
||||||
|
fetch(url, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Accept': 'application/json',
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
},
|
||||||
|
body: JSON.stringify(data)
|
||||||
|
})
|
||||||
|
.then((data) => {
|
||||||
|
|
||||||
|
d.resolve(data.json());
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
console.error(error);
|
||||||
|
d.reject(error);
|
||||||
|
});
|
||||||
|
return d.promise;
|
||||||
|
}
|
||||||
|
|
||||||
|
function serializeQuery (data = {}) {
|
||||||
|
data['__t'] = Date.now();// disable the cache
|
||||||
|
const queryArray = [];
|
||||||
|
|
||||||
|
for (let key in data) {
|
||||||
|
queryArray.push(`${key}=${data[key]}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const queryStr = queryArray.join('&');
|
||||||
|
|
||||||
|
return queryStr ? '?' + queryStr : '';
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isApiSuccess (response) {
|
||||||
|
return response.status === 'success';
|
||||||
|
}
|
||||||
|
|
||||||
|
const apiUtil = {
|
||||||
|
getJSON,
|
||||||
|
postJSON,
|
||||||
|
isApiSuccess
|
||||||
|
};
|
||||||
|
|
||||||
|
export default apiUtil;
|
9
web/src/common/constant.js
Normal file
9
web/src/common/constant.js
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
/**
|
||||||
|
* define all constant variables here
|
||||||
|
*/
|
||||||
|
|
||||||
|
module.exports.MenuKeyMap = {
|
||||||
|
RECORD_FILTER: 'RECORD_FILTER',
|
||||||
|
MAP_LOCAL: 'MAP_LOCAL',
|
||||||
|
ROOT_CA: 'ROOT_CA'
|
||||||
|
};
|
@ -1,25 +1,27 @@
|
|||||||
export function curlify(recordDetail) {
|
module.exports = {
|
||||||
const headers = { ...recordDetail.reqHeader };
|
curlify(recordDetail) {
|
||||||
const acceptEncoding = headers['Accept-Encoding'] || headers['accept-encoding'];
|
const headers = { ...recordDetail.reqHeader };
|
||||||
// escape reserve character in url
|
const acceptEncoding = headers['Accept-Encoding'] || headers['accept-encoding'];
|
||||||
const url = recordDetail.url.replace(/([\[\]])/g, '\\$1');
|
// escape reserve character in url
|
||||||
const curlified = ['curl', `'${url}'`];
|
const url = recordDetail.url.replace(/([\[\]])/g, '\\$1');
|
||||||
|
const curlified = ['curl', `'${url}'`];
|
||||||
|
|
||||||
if (recordDetail.method.toUpperCase() !== 'GET') {
|
if (recordDetail.method.toUpperCase() !== 'GET') {
|
||||||
curlified.push('-X', recordDetail.method);
|
curlified.push('-X', recordDetail.method);
|
||||||
|
}
|
||||||
|
|
||||||
|
Object.keys(headers).forEach((key) => {
|
||||||
|
curlified.push('-H', `'${key}: ${headers[key]}'`);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (recordDetail.reqBody) {
|
||||||
|
curlified.push('-d', `'${recordDetail.reqBody}'`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (/deflate|gzip/.test(acceptEncoding)) {
|
||||||
|
curlified.push('--compressed');
|
||||||
|
}
|
||||||
|
|
||||||
|
return curlified.join(' ');
|
||||||
}
|
}
|
||||||
|
};
|
||||||
Object.keys(headers).forEach((key) => {
|
|
||||||
curlified.push('-H', `'${key}: ${headers[key]}'`);
|
|
||||||
});
|
|
||||||
|
|
||||||
if (recordDetail.reqBody) {
|
|
||||||
curlified.push('-d', `'${recordDetail.reqBody}'`);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (/deflate|gzip/.test(acceptEncoding)) {
|
|
||||||
curlified.push('--compressed');
|
|
||||||
}
|
|
||||||
|
|
||||||
return curlified.join(' ');
|
|
||||||
}
|
|
||||||
|
42
web/src/common/wsUtil.js
Normal file
42
web/src/common/wsUtil.js
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
/*
|
||||||
|
* Utility for websocket
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
import { message } from 'antd';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initiate a ws connection.
|
||||||
|
* The default path `do-not-proxy` means the ws do not need to be proxied.
|
||||||
|
* This is very important for AnyProxy‘s own server, such as WEB UI,
|
||||||
|
* and the websocket detail panel in it, to prevent a recursive proxy.
|
||||||
|
* @param {wsPort} wsPort the port of websocket
|
||||||
|
* @param {key} path the path of the ws url
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
export function initWs(wsPort = location.port, path = 'do-not-proxy') {
|
||||||
|
if(!WebSocket){
|
||||||
|
throw (new Error('WebSocket is not supported on this browser'));
|
||||||
|
}
|
||||||
|
|
||||||
|
const wsClient = new WebSocket(`ws://${location.hostname}:${wsPort}/${path}`);
|
||||||
|
|
||||||
|
wsClient.onerror = (error) => {
|
||||||
|
console.error(error);
|
||||||
|
message.error('error happened when setup websocket');
|
||||||
|
};
|
||||||
|
|
||||||
|
wsClient.onopen = (e) => {
|
||||||
|
console.info('websocket opened: ', e);
|
||||||
|
};
|
||||||
|
|
||||||
|
wsClient.onclose = (e) => {
|
||||||
|
console.info('websocket closed: ', e);
|
||||||
|
};
|
||||||
|
|
||||||
|
return wsClient;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
initWs: initWs
|
||||||
|
};
|
||||||
|
|
@ -10,8 +10,8 @@ import { connect } from 'react-redux';
|
|||||||
import { message, Button, Spin } from 'antd';
|
import { message, Button, Spin } from 'antd';
|
||||||
import ResizablePanel from 'component/resizable-panel';
|
import ResizablePanel from 'component/resizable-panel';
|
||||||
import { hideRootCA, updateIsRootCAExists } from 'action/globalStatusAction';
|
import { hideRootCA, updateIsRootCAExists } from 'action/globalStatusAction';
|
||||||
import { MenuKeyMap } from 'common/Constant';
|
import { MenuKeyMap } from 'common/constant';
|
||||||
import { getJSON, ajaxGet, postJSON } from 'common/ApiUtil';
|
import { getJSON, ajaxGet, postJSON } from 'common/apiUtil';
|
||||||
|
|
||||||
import Style from './download-root-ca.less';
|
import Style from './download-root-ca.less';
|
||||||
import CommonStyle from '../style/common.less';
|
import CommonStyle from '../style/common.less';
|
||||||
|
@ -7,8 +7,8 @@ import ClassBind from 'classnames/bind';
|
|||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import InlineSVG from 'svg-inline-react';
|
import InlineSVG from 'svg-inline-react';
|
||||||
import { message, Modal, Popover, Button } from 'antd';
|
import { message, Modal, Popover, Button } from 'antd';
|
||||||
import { getQueryParameter } from 'common/CommonUtil';
|
import { getQueryParameter } from 'common/commonUtil';
|
||||||
import { MenuKeyMap } from 'common/Constant';
|
import { MenuKeyMap } from 'common/constant';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
resumeRecording,
|
resumeRecording,
|
||||||
@ -27,7 +27,7 @@ const {
|
|||||||
RECORD_FILTER: RECORD_FILTER_MENU_KEY
|
RECORD_FILTER: RECORD_FILTER_MENU_KEY
|
||||||
} = MenuKeyMap;
|
} = MenuKeyMap;
|
||||||
|
|
||||||
import { getJSON } from 'common/ApiUtil';
|
import { getJSON } from 'common/apiUtil';
|
||||||
|
|
||||||
import Style from './header-menu.less';
|
import Style from './header-menu.less';
|
||||||
|
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
import React, { PropTypes } from 'react';
|
import React, { PropTypes } from 'react';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import InlineSVG from 'svg-inline-react';
|
import InlineSVG from 'svg-inline-react';
|
||||||
import { getQueryParameter } from 'common/CommonUtil';
|
import { getQueryParameter } from 'common/commonUtil';
|
||||||
|
|
||||||
import Style from './left-menu.less';
|
import Style from './left-menu.less';
|
||||||
import ClassBind from 'classnames/bind';
|
import ClassBind from 'classnames/bind';
|
||||||
@ -15,7 +15,7 @@ import {
|
|||||||
showRootCA
|
showRootCA
|
||||||
} from 'action/globalStatusAction';
|
} from 'action/globalStatusAction';
|
||||||
|
|
||||||
import { MenuKeyMap } from 'common/Constant';
|
import { MenuKeyMap } from 'common/constant';
|
||||||
|
|
||||||
const StyleBind = ClassBind.bind(Style);
|
const StyleBind = ClassBind.bind(Style);
|
||||||
const {
|
const {
|
||||||
|
@ -4,14 +4,12 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import React, { PropTypes } from 'react';
|
import React, { PropTypes } from 'react';
|
||||||
import ReactDOM from 'react-dom';
|
|
||||||
import ClassBind from 'classnames/bind';
|
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import { Tree, Form, Input, Button } from 'antd';
|
import { Tree, Form, Input, Button } from 'antd';
|
||||||
import ResizablePanel from 'component/resizable-panel';
|
import ResizablePanel from 'component/resizable-panel';
|
||||||
import PromiseUtil from 'common/PromiseUtil';
|
import PromiseUtil from 'common/promiseUtil';
|
||||||
import { fetchDirectory, hideMapLocal, fetchMappedConfig, updateRemoteMappedConfig } from 'action/globalStatusAction';
|
import { fetchDirectory, hideMapLocal, fetchMappedConfig, updateRemoteMappedConfig } from 'action/globalStatusAction';
|
||||||
import { MenuKeyMap } from 'common/Constant';
|
import { MenuKeyMap } from 'common/constant';
|
||||||
|
|
||||||
import Style from './map-local.less';
|
import Style from './map-local.less';
|
||||||
import CommonStyle from '../style/common.less';
|
import CommonStyle from '../style/common.less';
|
||||||
|
@ -10,7 +10,7 @@ import { connect } from 'react-redux';
|
|||||||
import { Input, Alert } from 'antd';
|
import { Input, Alert } from 'antd';
|
||||||
import ResizablePanel from 'component/resizable-panel';
|
import ResizablePanel from 'component/resizable-panel';
|
||||||
import { hideFilter, updateFilter } from 'action/globalStatusAction';
|
import { hideFilter, updateFilter } from 'action/globalStatusAction';
|
||||||
import { MenuKeyMap } from 'common/Constant';
|
import { MenuKeyMap } from 'common/constant';
|
||||||
|
|
||||||
import Style from './record-filter.less';
|
import Style from './record-filter.less';
|
||||||
import CommonStyle from '../style/common.less';
|
import CommonStyle from '../style/common.less';
|
||||||
|
@ -10,7 +10,7 @@ import clipboard from 'clipboard-js'
|
|||||||
import JsonViewer from 'component/json-viewer';
|
import JsonViewer from 'component/json-viewer';
|
||||||
import ModalPanel from 'component/modal-panel';
|
import ModalPanel from 'component/modal-panel';
|
||||||
import { hideRecordDetail } from 'action/recordAction';
|
import { hideRecordDetail } from 'action/recordAction';
|
||||||
import { selectText } from 'common/CommonUtil';
|
import { selectText } from 'common/commonUtil';
|
||||||
import { curlify } from 'common/curlUtil';
|
import { curlify } from 'common/curlUtil';
|
||||||
|
|
||||||
import Style from './record-detail.less';
|
import Style from './record-detail.less';
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import React, { PropTypes } from 'react';
|
import React, { PropTypes } from 'react';
|
||||||
import { formatDate } from 'common/CommonUtil';
|
import { formatDate } from 'common/commonUtil';
|
||||||
|
|
||||||
import Style from './record-row.less';
|
import Style from './record-row.less';
|
||||||
import CommonStyle from '../style/common.less';
|
import CommonStyle from '../style/common.less';
|
||||||
|
@ -5,8 +5,8 @@
|
|||||||
|
|
||||||
import React, { PropTypes } from 'react';
|
import React, { PropTypes } from 'react';
|
||||||
import { message, Button, Icon } from 'antd';
|
import { message, Button, Icon } from 'antd';
|
||||||
import { formatDate } from 'common/CommonUtil';
|
import { formatDate } from 'common/commonUtil';
|
||||||
import { initWs } from 'common/WsUtil';
|
import { initWs } from 'common/wsUtil';
|
||||||
import ClassBind from 'classnames/bind';
|
import ClassBind from 'classnames/bind';
|
||||||
|
|
||||||
import Style from './record-ws-message-detail.less';
|
import Style from './record-ws-message-detail.less';
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
import React, { PropTypes } from 'react';
|
import React, { PropTypes } from 'react';
|
||||||
import ReactDOM from 'react-dom';
|
import ReactDOM from 'react-dom';
|
||||||
import { Table } from 'antd';
|
import { Table } from 'antd';
|
||||||
import { formatDate } from 'common/CommonUtil';
|
import { formatDate } from 'common/commonUtil';
|
||||||
|
|
||||||
import Style from './table-panel.less';
|
import Style from './table-panel.less';
|
||||||
import ClassBind from 'classnames/bind';
|
import ClassBind from 'classnames/bind';
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
import React, { PropTypes } from 'react';
|
import React, { PropTypes } from 'react';
|
||||||
import { Icon } from 'antd';
|
import { Icon } from 'antd';
|
||||||
import { getQueryParameter } from 'common/CommonUtil';
|
import { getQueryParameter } from 'common/commonUtil';
|
||||||
|
|
||||||
import Style from './title-bar.less';
|
import Style from './title-bar.less';
|
||||||
|
|
||||||
|
@ -5,14 +5,14 @@
|
|||||||
|
|
||||||
import React, { PropTypes } from 'react';
|
import React, { PropTypes } from 'react';
|
||||||
import { message } from 'antd';
|
import { message } from 'antd';
|
||||||
import { initWs } from 'common/WsUtil';
|
import { initWs } from 'common/wsUtil';
|
||||||
import { updateWholeRequest } from 'action/recordAction';
|
import { updateWholeRequest } from 'action/recordAction';
|
||||||
import {
|
import {
|
||||||
updateShouldClearRecord,
|
updateShouldClearRecord,
|
||||||
updateShowNewRecordTip
|
updateShowNewRecordTip
|
||||||
} from 'action/globalStatusAction';
|
} from 'action/globalStatusAction';
|
||||||
const RecordWorker = require('worker-loader?inline!./record-worker.jsx');
|
const RecordWorker = require('worker-loader?inline!./record-worker.jsx');
|
||||||
import { getJSON } from 'common/ApiUtil';
|
import { getJSON } from 'common/apiUtil';
|
||||||
|
|
||||||
const myRecordWorker = new RecordWorker(window.URL.createObjectURL(new Blob([RecordWorker.toString()])));
|
const myRecordWorker = new RecordWorker(window.URL.createObjectURL(new Blob([RecordWorker.toString()])));
|
||||||
const fetchLatestLog = function () {
|
const fetchLatestLog = function () {
|
||||||
|
@ -6,8 +6,8 @@ import { LocaleProvider } from 'antd';
|
|||||||
import enUS from 'antd/lib/locale-provider/en_US';
|
import enUS from 'antd/lib/locale-provider/en_US';
|
||||||
import createSagaMiddleware from 'redux-saga';
|
import createSagaMiddleware from 'redux-saga';
|
||||||
import rootSaga from 'saga/rootSaga';
|
import rootSaga from 'saga/rootSaga';
|
||||||
import { MenuKeyMap } from 'common/Constant';
|
import { MenuKeyMap } from 'common/constant';
|
||||||
import { getQueryParameter } from 'common/CommonUtil';
|
import { getQueryParameter } from 'common/commonUtil';
|
||||||
|
|
||||||
import reducer from 'reducer/rootReducer';
|
import reducer from 'reducer/rootReducer';
|
||||||
import HeaderMenu from 'component/header-menu';
|
import HeaderMenu from 'component/header-menu';
|
||||||
|
@ -21,7 +21,7 @@ const defaultStatus = {
|
|||||||
mappedConfig:[] // configured map config
|
mappedConfig:[] // configured map config
|
||||||
};
|
};
|
||||||
|
|
||||||
import { MenuKeyMap } from 'common/Constant';
|
import { MenuKeyMap } from 'common/constant';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
STOP_RECORDING,
|
STOP_RECORDING,
|
||||||
|
@ -29,7 +29,7 @@ import {
|
|||||||
updateLocalGlobalProxyFlag
|
updateLocalGlobalProxyFlag
|
||||||
} from 'action/globalStatusAction';
|
} from 'action/globalStatusAction';
|
||||||
|
|
||||||
import { getJSON, postJSON, isApiSuccess } from 'common/ApiUtil';
|
import { getJSON, postJSON, isApiSuccess } from 'common/apiUtil';
|
||||||
|
|
||||||
function* doFetchRequestList() {
|
function* doFetchRequestList() {
|
||||||
const data = yield call(getJSON, '/latestLog');
|
const data = yield call(getJSON, '/latestLog');
|
||||||
|
Loading…
x
Reference in New Issue
Block a user