mirror of
https://github.com/alibaba/anyproxy.git
synced 2025-08-04 21:39:04 +00:00
feat: refact the remaining files into TS
This commit is contained in:
15
lib/proxy/index.ts
Normal file
15
lib/proxy/index.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
import ProxyCore from './proxyCore';
|
||||
import ProxyServer from './proxyServer';
|
||||
import WebInterface from '../webInterface';
|
||||
import Recorder from '../recorder';
|
||||
import certMgr from '../certMgr';
|
||||
|
||||
|
||||
module.exports.ProxyCore = ProxyCore;
|
||||
module.exports.ProxyServer = ProxyServer;
|
||||
module.exports.ProxyRecorder = Recorder;
|
||||
module.exports.ProxyWebServer = WebInterface;
|
||||
module.exports.utils = {
|
||||
systemProxyMgr: require('../systemProxyMgr'),
|
||||
certMgr,
|
||||
};
|
||||
299
lib/proxy/proxyCore.ts
Normal file
299
lib/proxy/proxyCore.ts
Normal file
@@ -0,0 +1,299 @@
|
||||
'use strict';
|
||||
/*tslint:disable:max-line-length */
|
||||
|
||||
import * as http from 'http';
|
||||
import * as https from 'https';
|
||||
import * as async from 'async';
|
||||
import * as color from 'colorful';
|
||||
import * as events from 'events';
|
||||
import * as throttle from 'stream-throttle';
|
||||
import * as co from 'co';
|
||||
import certMgr from '../certMgr';
|
||||
import Recorder from '../recorder';
|
||||
import logUtil from '../log';
|
||||
import util from '../util';
|
||||
import RequestHandler from '../requestHandler';
|
||||
import wsServerMgr from '../wsServerMgr';
|
||||
import WebInterface from '../webInterface';
|
||||
|
||||
declare type TSyncTaskCb = (err: Error) => void;
|
||||
|
||||
const ThrottleGroup = throttle.ThrottleGroup;
|
||||
|
||||
const T_TYPE_HTTP = 'http';
|
||||
const T_TYPE_HTTPS = 'https';
|
||||
const DEFAULT_TYPE = T_TYPE_HTTP;
|
||||
|
||||
const PROXY_STATUS_INIT = 'INIT';
|
||||
const PROXY_STATUS_READY = 'READY';
|
||||
const PROXY_STATUS_CLOSED = 'CLOSED';
|
||||
|
||||
/**
|
||||
*
|
||||
* @class ProxyCore
|
||||
* @extends {events.EventEmitter}
|
||||
*/
|
||||
class ProxyCore extends events.EventEmitter {
|
||||
private socketIndex: number;
|
||||
private status: 'INIT' | 'READY' | 'CLOSED';
|
||||
private proxyPort: string;
|
||||
private proxyType: 'http' | 'https';
|
||||
protected proxyHostName: string;
|
||||
public recorder: Recorder;
|
||||
private httpProxyServer: http.Server | https.Server;
|
||||
protected webServerInstance: WebInterface;
|
||||
private requestHandler: RequestHandler;
|
||||
private proxyRule: AnyProxyRule;
|
||||
private socketPool: {
|
||||
[key: string]: http.IncomingMessage;
|
||||
};
|
||||
/**
|
||||
* Creates an instance of ProxyCore.
|
||||
* @memberOf ProxyCore
|
||||
*/
|
||||
constructor(config: AnyProxyConfig) {
|
||||
super();
|
||||
config = config || {};
|
||||
|
||||
this.status = PROXY_STATUS_INIT;
|
||||
this.proxyPort = config.port;
|
||||
this.proxyType = /https/i.test(config.type || DEFAULT_TYPE) ? T_TYPE_HTTPS : T_TYPE_HTTP;
|
||||
this.proxyHostName = config.hostname || 'localhost';
|
||||
this.recorder = config.recorder;
|
||||
|
||||
if (parseInt(process.versions.node.split('.')[0], 10) < 4) {
|
||||
throw new Error('node.js >= v4.x is required for anyproxy');
|
||||
} else if (config.forceProxyHttps && !certMgr.ifRootCAFileExists()) {
|
||||
logUtil.printLog('You can run `anyproxy-ca` to generate one root CA and then re-run this command');
|
||||
throw new Error('root CA not found. Please run `anyproxy-ca` to generate one first.');
|
||||
} else if (this.proxyType === T_TYPE_HTTPS && !config.hostname) {
|
||||
throw new Error('hostname is required in https proxy');
|
||||
} else if (!this.proxyPort) {
|
||||
throw new Error('proxy port is required');
|
||||
} else if (!this.recorder) {
|
||||
throw new Error('recorder is required');
|
||||
} else if (config.forceProxyHttps && config.rule && config.rule.beforeDealHttpsRequest) {
|
||||
logUtil.printLog('both "-i(--intercept)" and rule.beforeDealHttpsRequest are specified, the "-i" option will be ignored.', logUtil.T_WARN);
|
||||
config.forceProxyHttps = false;
|
||||
}
|
||||
|
||||
this.httpProxyServer = null;
|
||||
this.requestHandler = null;
|
||||
|
||||
// copy the rule to keep the original proxyRule independent
|
||||
this.proxyRule = config.rule || {};
|
||||
|
||||
if (config.silent) {
|
||||
logUtil.setPrintStatus(false);
|
||||
}
|
||||
|
||||
if (config.throttle) {
|
||||
logUtil.printLog('throttle :' + config.throttle + 'kb/s');
|
||||
const rate = parseInt(config.throttle, 10);
|
||||
if (rate < 1) {
|
||||
throw new Error('Invalid throttle rate value, should be positive integer');
|
||||
}
|
||||
global._throttle = new ThrottleGroup({ rate: 1024 * rate }); // rate - byte/sec
|
||||
}
|
||||
|
||||
// init recorder
|
||||
this.recorder = config.recorder;
|
||||
|
||||
// init request handler
|
||||
const RequestHandlerClass = (util.freshRequire('./requestHandler') as any).default;
|
||||
this.requestHandler = new RequestHandlerClass({
|
||||
wsIntercept: config.wsIntercept,
|
||||
httpServerPort: config.port, // the http server port for http proxy
|
||||
forceProxyHttps: !!config.forceProxyHttps,
|
||||
dangerouslyIgnoreUnauthorized: !!config.dangerouslyIgnoreUnauthorized,
|
||||
}, this.proxyRule, this.recorder);
|
||||
}
|
||||
|
||||
/**
|
||||
* manage all created socket
|
||||
* for each new socket, we put them to a map;
|
||||
* if the socket is closed itself, we remove it from the map
|
||||
* when the `close` method is called, we'll close the sockes before the server closed
|
||||
*
|
||||
* @param {Socket} the http socket that is creating
|
||||
* @returns undefined
|
||||
* @memberOf ProxyCore
|
||||
*/
|
||||
private handleExistConnections(socket: http.IncomingMessage): void {
|
||||
const self = this;
|
||||
self.socketIndex ++;
|
||||
const key = `socketIndex_${self.socketIndex}`;
|
||||
self.socketPool[key] = socket;
|
||||
|
||||
// if the socket is closed already, removed it from pool
|
||||
socket.on('close', () => {
|
||||
delete self.socketPool[key];
|
||||
});
|
||||
}
|
||||
/**
|
||||
* start the proxy server
|
||||
*
|
||||
* @returns ProxyCore
|
||||
*
|
||||
* @memberOf ProxyCore
|
||||
*/
|
||||
public start(): ProxyCore {
|
||||
const self = this;
|
||||
self.socketIndex = 0;
|
||||
self.socketPool = {};
|
||||
|
||||
if (self.status !== PROXY_STATUS_INIT) {
|
||||
throw new Error('server status is not PROXY_STATUS_INIT, can not run start()');
|
||||
}
|
||||
async.series(
|
||||
[
|
||||
// creat proxy server
|
||||
function(callback: TSyncTaskCb): void {
|
||||
if (self.proxyType === T_TYPE_HTTPS) {
|
||||
certMgr.getCertificate(self.proxyHostName, (err, keyContent, crtContent) => {
|
||||
if (err) {
|
||||
callback(err);
|
||||
} else {
|
||||
self.httpProxyServer = https.createServer({
|
||||
key: keyContent,
|
||||
cert: crtContent,
|
||||
}, self.requestHandler.userRequestHandler);
|
||||
callback(null);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
self.httpProxyServer = http.createServer(self.requestHandler.userRequestHandler);
|
||||
callback(null);
|
||||
}
|
||||
},
|
||||
|
||||
// handle CONNECT request for https over http
|
||||
function(callback: TSyncTaskCb): void {
|
||||
self.httpProxyServer.on('connect', self.requestHandler.connectReqHandler);
|
||||
|
||||
callback(null);
|
||||
},
|
||||
|
||||
function(callback: TSyncTaskCb): void {
|
||||
wsServerMgr.getWsServer({
|
||||
server: self.httpProxyServer,
|
||||
connHandler: self.requestHandler.wsHandler,
|
||||
});
|
||||
// remember all sockets, so we can destory them when call the method 'close';
|
||||
self.httpProxyServer.on('connection', (socket) => {
|
||||
self.handleExistConnections.call(self, socket);
|
||||
});
|
||||
callback(null);
|
||||
},
|
||||
|
||||
// start proxy server
|
||||
function(callback: TSyncTaskCb): void {
|
||||
self.httpProxyServer.listen(self.proxyPort);
|
||||
callback(null);
|
||||
},
|
||||
],
|
||||
|
||||
// final callback
|
||||
(err, result) => {
|
||||
if (!err) {
|
||||
const tipText = (self.proxyType === T_TYPE_HTTP ? 'Http' : 'Https') + ' proxy started on port ' + self.proxyPort;
|
||||
logUtil.printLog(color.green(tipText));
|
||||
|
||||
if (self.webServerInstance) {
|
||||
const webTip = 'web interface started on port ' + self.webServerInstance.webPort;
|
||||
logUtil.printLog(color.green(webTip));
|
||||
}
|
||||
|
||||
let ruleSummaryString = '';
|
||||
const ruleSummary = this.proxyRule.summary;
|
||||
if (ruleSummary) {
|
||||
co(function *(): Generator {
|
||||
if (typeof ruleSummary === 'string') {
|
||||
ruleSummaryString = ruleSummary;
|
||||
} else {
|
||||
ruleSummaryString = yield ruleSummary();
|
||||
}
|
||||
|
||||
logUtil.printLog(color.green(`Active rule is: ${ruleSummaryString}`));
|
||||
});
|
||||
}
|
||||
|
||||
self.status = PROXY_STATUS_READY;
|
||||
self.emit('ready');
|
||||
} else {
|
||||
const tipText = 'err when start proxy server :(';
|
||||
logUtil.printLog(color.red(tipText), logUtil.T_ERR);
|
||||
logUtil.printLog(err, logUtil.T_ERR);
|
||||
self.emit('error', {
|
||||
error: err,
|
||||
});
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* close the proxy server
|
||||
*
|
||||
* @returns ProxyCore
|
||||
*
|
||||
* @memberOf ProxyCore
|
||||
*/
|
||||
public close(): Promise<Error> {
|
||||
// clear recorder cache
|
||||
return new Promise((resolve) => {
|
||||
if (this.httpProxyServer) {
|
||||
// destroy conns & cltSockets when closing proxy server
|
||||
this.requestHandler.conns.forEach((conn, key) => {
|
||||
logUtil.printLog(`destorying https connection : ${key}`);
|
||||
conn.end();
|
||||
});
|
||||
|
||||
this.requestHandler.cltSockets.forEach((cltSocket, key) => {
|
||||
logUtil.printLog(`endding https cltSocket : ${key}`);
|
||||
cltSocket.end();
|
||||
});
|
||||
|
||||
// for (const connItem of this.requestHandler.conns) {
|
||||
// const key = connItem[0];
|
||||
// const conn = connItem[1];
|
||||
// logUtil.printLog(`destorying https connection : ${key}`);
|
||||
// conn.end();
|
||||
// }
|
||||
|
||||
// for (const cltSocketItem of this.requestHandler.cltSockets) {
|
||||
// const key = cltSocketItem[0];
|
||||
// const cltSocket = cltSocketItem[1];
|
||||
// logUtil.printLog(`endding https cltSocket : ${key}`);
|
||||
// cltSocket.end();
|
||||
// }
|
||||
|
||||
if (this.socketPool) {
|
||||
for (const key in this.socketPool) {
|
||||
this.socketPool[key].destroy();
|
||||
}
|
||||
}
|
||||
|
||||
this.httpProxyServer.close((error) => {
|
||||
if (error) {
|
||||
console.error(error);
|
||||
logUtil.printLog(`proxy server close FAILED : ${error.message}`, logUtil.T_ERR);
|
||||
} else {
|
||||
this.httpProxyServer = null;
|
||||
|
||||
this.status = PROXY_STATUS_CLOSED;
|
||||
logUtil.printLog(`proxy server closed at ${this.proxyHostName}:${this.proxyPort}`);
|
||||
}
|
||||
resolve(error);
|
||||
});
|
||||
} else {
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export default ProxyCore;
|
||||
87
lib/proxy/proxyServer.ts
Normal file
87
lib/proxy/proxyServer.ts
Normal file
@@ -0,0 +1,87 @@
|
||||
'use strict';
|
||||
|
||||
import ProxyCore from './proxyCore.js';
|
||||
import Recorder from '../recorder';
|
||||
import WebInterface from '../webInterface';
|
||||
import logUtil from '../log';
|
||||
|
||||
/**
|
||||
* start proxy server as well as recorder and webInterface
|
||||
*/
|
||||
class ProxyServer extends ProxyCore {
|
||||
public proxyWebinterfaceConfig: any;
|
||||
/**
|
||||
*
|
||||
* @param {object} config - config
|
||||
* @param {object} [config.webInterface] - config of the web interface
|
||||
* @param {boolean} [config.webInterface.enable=false] - if web interface is enabled
|
||||
* @param {number} [config.webInterface.webPort=8002] - http port of the web interface
|
||||
*/
|
||||
constructor(config: AnyProxyConfig) {
|
||||
// prepare a recorder
|
||||
const recorder = new Recorder();
|
||||
const configForCore = Object.assign({
|
||||
recorder,
|
||||
}, config);
|
||||
|
||||
super(configForCore);
|
||||
|
||||
this.proxyWebinterfaceConfig = config.webInterface;
|
||||
this.recorder = recorder;
|
||||
this.webServerInstance = null;
|
||||
}
|
||||
|
||||
public start(): ProxyServer {
|
||||
// start web interface if neeeded
|
||||
if (this.proxyWebinterfaceConfig && this.proxyWebinterfaceConfig.enable) {
|
||||
this.webServerInstance = new WebInterface(this.proxyWebinterfaceConfig, this.recorder);
|
||||
// start web server
|
||||
this.webServerInstance.start().then(() => {
|
||||
// start proxy core
|
||||
super.start();
|
||||
})
|
||||
.catch((e) => {
|
||||
this.emit('error', e);
|
||||
});
|
||||
} else {
|
||||
super.start();
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public close(): Promise<Error> {
|
||||
return new Promise((resolve, reject) => {
|
||||
super.close()
|
||||
.then((error) => {
|
||||
if (error) {
|
||||
resolve(error);
|
||||
}
|
||||
});
|
||||
|
||||
if (this.recorder) {
|
||||
logUtil.printLog('clearing cache file...');
|
||||
this.recorder.clear();
|
||||
}
|
||||
const tmpWebServer = this.webServerInstance;
|
||||
this.recorder = null;
|
||||
this.webServerInstance = null;
|
||||
if (tmpWebServer) {
|
||||
logUtil.printLog('closing webserver...');
|
||||
tmpWebServer.close((error) => {
|
||||
if (error) {
|
||||
console.error(error);
|
||||
logUtil.printLog(`proxy web server close FAILED: ${error.message}`, logUtil.T_ERR);
|
||||
} else {
|
||||
logUtil.printLog(`proxy web server closed at ${this.proxyHostName} : ${this.proxyWebinterfaceConfig.webPort}`);
|
||||
}
|
||||
|
||||
resolve(error);
|
||||
});
|
||||
} else {
|
||||
resolve(null);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export default ProxyServer;
|
||||
Reference in New Issue
Block a user