feat: refact the remaining files into TS

This commit is contained in:
砚然 2018-08-31 17:40:30 +08:00
parent bcb7451579
commit 0385096857
12 changed files with 310 additions and 252 deletions

View File

@ -8,11 +8,13 @@ import util from './util';
import logUtil from './log'; import logUtil from './log';
declare interface ICertMgr { declare interface ICertMgr {
ifRootCAFileExists?: boolean;
generateRootCA?: ( cb: (error: boolean, keyPath: string, crtPath: string) => void ) => void; generateRootCA?: ( cb: (error: boolean, keyPath: string, crtPath: string) => void ) => void;
getCAStatus?: () => Generator; getCAStatus?: () => Generator;
trustRootCA?: () => Generator; trustRootCA?: () => Generator;
getRootCAFilePath?: () => string; getRootCAFilePath?: () => string;
ifRootCAFileExists?: () => boolean;
isRootCAFileExists?: () => boolean;
getRootDirPath?: () => string;
getCertificate?: (serverName: string, cb: (err: Error, key: string, crt: string) => void) => void; getCertificate?: (serverName: string, cb: (err: Error, key: string, crt: string) => void) => void;
} }

15
lib/proxy/index.ts Normal file
View 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,
};

View File

@ -1,41 +1,28 @@
'use strict'; 'use strict';
/*tslint:disable:max-line-length */
const http = require('http'), import * as http from 'http';
https = require('https'), import * as https from 'https';
async = require('async'), import * as async from 'async';
color = require('colorful'), import * as color from 'colorful';
certMgr = require('./certMgr').default, import * as events from 'events';
Recorder = require('./recorder').default, import * as throttle from 'stream-throttle';
logUtil = require('./log').default, import * as co from 'co';
util = require('./util').default, import certMgr from '../certMgr';
events = require('events'), import Recorder from '../recorder';
co = require('co'), import logUtil from '../log';
WebInterface = require('./webInterface'), import util from '../util';
wsServerMgr = require('./wsServerMgr').default, import RequestHandler from '../requestHandler';
ThrottleGroup = require('stream-throttle').ThrottleGroup; import wsServerMgr from '../wsServerMgr';
import WebInterface from '../webInterface';
// const memwatch = require('memwatch-next'); declare type TSyncTaskCb = (err: Error) => void;
// setInterval(() => { const ThrottleGroup = throttle.ThrottleGroup;
// 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) => { const T_TYPE_HTTP = 'http';
// console.log('gc !!'); const T_TYPE_HTTPS = 'https';
// console.log(process.memoryUsage()); const DEFAULT_TYPE = T_TYPE_HTTP;
// 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',
T_TYPE_HTTPS = 'https',
DEFAULT_TYPE = T_TYPE_HTTP;
const PROXY_STATUS_INIT = 'INIT'; const PROXY_STATUS_INIT = 'INIT';
const PROXY_STATUS_READY = 'READY'; const PROXY_STATUS_READY = 'READY';
@ -47,25 +34,24 @@ const PROXY_STATUS_CLOSED = 'CLOSED';
* @extends {events.EventEmitter} * @extends {events.EventEmitter}
*/ */
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. * Creates an instance of ProxyCore.
*
* @param {object} config - configs
* @param {number} config.port - port of the proxy server
* @param {object} [config.rule=null] - rule module to use
* @param {string} [config.type=http] - type of the proxy server, could be 'http' or 'https'
* @param {strign} [config.hostname=localhost] - host name of the proxy server, required when this is an https proxy
* @param {number} [config.throttle] - speed limit in kb/s
* @param {boolean} [config.forceProxyHttps=false] - if proxy all https requests
* @param {boolean} [config.silent=false] - if keep the console silent
* @param {boolean} [config.dangerouslyIgnoreUnauthorized=false] - if ignore unauthorized server response
* @param {object} [config.recorder] - recorder to use
* @param {boolean} [config.wsIntercept] - whether intercept websocket
*
* @memberOf ProxyCore * @memberOf ProxyCore
*/ */
constructor(config) { constructor(config: AnyProxyConfig) {
super(); super();
config = config || {}; config = config || {};
@ -114,12 +100,12 @@ class ProxyCore extends events.EventEmitter {
this.recorder = config.recorder; this.recorder = config.recorder;
// init request handler // init request handler
const RequestHandler = util.freshRequire('./requestHandler').default; const RequestHandlerClass = (util.freshRequire('./requestHandler') as any).default;
this.requestHandler = new RequestHandler({ this.requestHandler = new RequestHandlerClass({
wsIntercept: config.wsIntercept, wsIntercept: config.wsIntercept,
httpServerPort: config.port, // the http server port for http proxy httpServerPort: config.port, // the http server port for http proxy
forceProxyHttps: !!config.forceProxyHttps, forceProxyHttps: !!config.forceProxyHttps,
dangerouslyIgnoreUnauthorized: !!config.dangerouslyIgnoreUnauthorized dangerouslyIgnoreUnauthorized: !!config.dangerouslyIgnoreUnauthorized,
}, this.proxyRule, this.recorder); }, this.proxyRule, this.recorder);
} }
@ -133,7 +119,7 @@ class ProxyCore extends events.EventEmitter {
* @returns undefined * @returns undefined
* @memberOf ProxyCore * @memberOf ProxyCore
*/ */
handleExistConnections(socket) { private handleExistConnections(socket: http.IncomingMessage): void {
const self = this; const self = this;
self.socketIndex ++; self.socketIndex ++;
const key = `socketIndex_${self.socketIndex}`; const key = `socketIndex_${self.socketIndex}`;
@ -151,7 +137,7 @@ class ProxyCore extends events.EventEmitter {
* *
* @memberOf ProxyCore * @memberOf ProxyCore
*/ */
start() { public start(): ProxyCore {
const self = this; const self = this;
self.socketIndex = 0; self.socketIndex = 0;
self.socketPool = {}; self.socketPool = {};
@ -162,7 +148,7 @@ class ProxyCore extends events.EventEmitter {
async.series( async.series(
[ [
// creat proxy server // creat proxy server
function (callback) { function(callback: TSyncTaskCb): void {
if (self.proxyType === T_TYPE_HTTPS) { if (self.proxyType === T_TYPE_HTTPS) {
certMgr.getCertificate(self.proxyHostName, (err, keyContent, crtContent) => { certMgr.getCertificate(self.proxyHostName, (err, keyContent, crtContent) => {
if (err) { if (err) {
@ -170,7 +156,7 @@ class ProxyCore extends events.EventEmitter {
} else { } else {
self.httpProxyServer = https.createServer({ self.httpProxyServer = https.createServer({
key: keyContent, key: keyContent,
cert: crtContent cert: crtContent,
}, self.requestHandler.userRequestHandler); }, self.requestHandler.userRequestHandler);
callback(null); callback(null);
} }
@ -182,16 +168,16 @@ class ProxyCore extends events.EventEmitter {
}, },
// handle CONNECT request for https over http // handle CONNECT request for https over http
function (callback) { function(callback: TSyncTaskCb): void {
self.httpProxyServer.on('connect', self.requestHandler.connectReqHandler); self.httpProxyServer.on('connect', self.requestHandler.connectReqHandler);
callback(null); callback(null);
}, },
function (callback) { function(callback: TSyncTaskCb): void {
wsServerMgr.getWsServer({ wsServerMgr.getWsServer({
server: self.httpProxyServer, server: self.httpProxyServer,
connHandler: self.requestHandler.wsHandler connHandler: self.requestHandler.wsHandler,
}); });
// remember all sockets, so we can destory them when call the method 'close'; // remember all sockets, so we can destory them when call the method 'close';
self.httpProxyServer.on('connection', (socket) => { self.httpProxyServer.on('connection', (socket) => {
@ -201,7 +187,7 @@ class ProxyCore extends events.EventEmitter {
}, },
// start proxy server // start proxy server
function (callback) { function(callback: TSyncTaskCb): void {
self.httpProxyServer.listen(self.proxyPort); self.httpProxyServer.listen(self.proxyPort);
callback(null); callback(null);
}, },
@ -221,7 +207,7 @@ class ProxyCore extends events.EventEmitter {
let ruleSummaryString = ''; let ruleSummaryString = '';
const ruleSummary = this.proxyRule.summary; const ruleSummary = this.proxyRule.summary;
if (ruleSummary) { if (ruleSummary) {
co(function *() { co(function *(): Generator {
if (typeof ruleSummary === 'string') { if (typeof ruleSummary === 'string') {
ruleSummaryString = ruleSummary; ruleSummaryString = ruleSummary;
} else { } else {
@ -239,10 +225,10 @@ class ProxyCore extends events.EventEmitter {
logUtil.printLog(color.red(tipText), logUtil.T_ERR); logUtil.printLog(color.red(tipText), logUtil.T_ERR);
logUtil.printLog(err, logUtil.T_ERR); logUtil.printLog(err, logUtil.T_ERR);
self.emit('error', { self.emit('error', {
error: err error: err,
}); });
} }
} },
); );
return self; return self;
@ -256,24 +242,34 @@ class ProxyCore extends events.EventEmitter {
* *
* @memberOf ProxyCore * @memberOf ProxyCore
*/ */
close() { public close(): Promise<Error> {
// clear recorder cache // clear recorder cache
return new Promise((resolve) => { return new Promise((resolve) => {
if (this.httpProxyServer) { if (this.httpProxyServer) {
// destroy conns & cltSockets when closing proxy server // destroy conns & cltSockets when closing proxy server
for (const connItem of this.requestHandler.conns) { this.requestHandler.conns.forEach((conn, key) => {
const key = connItem[0];
const conn = connItem[1];
logUtil.printLog(`destorying https connection : ${key}`); logUtil.printLog(`destorying https connection : ${key}`);
conn.end(); conn.end();
} });
for (const cltSocketItem of this.requestHandler.cltSockets) { this.requestHandler.cltSockets.forEach((cltSocket, key) => {
const key = cltSocketItem[0];
const cltSocket = cltSocketItem[1];
logUtil.printLog(`endding https cltSocket : ${key}`); logUtil.printLog(`endding https cltSocket : ${key}`);
cltSocket.end(); 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) { if (this.socketPool) {
for (const key in this.socketPool) { for (const key in this.socketPool) {
@ -296,92 +292,8 @@ class ProxyCore extends events.EventEmitter {
} else { } else {
resolve(); resolve();
} }
})
}
}
/**
* start proxy server as well as recorder and webInterface
*/
class ProxyServer extends ProxyCore {
/**
*
* @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) {
// 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;
}
start() {
// 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();
}
}
close() {
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.webPort}`);
}
resolve(error);
})
} else {
resolve(null);
}
}); });
} }
} }
module.exports.ProxyCore = ProxyCore; export default ProxyCore;
module.exports.ProxyServer = ProxyServer;
module.exports.ProxyRecorder = Recorder;
module.exports.ProxyWebServer = WebInterface;
module.exports.utils = {
systemProxyMgr: require('./systemProxyMgr'),
certMgr,
};

87
lib/proxy/proxyServer.ts Normal file
View 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;

View File

@ -412,9 +412,11 @@ class RequestHandler {
public dangerouslyIgnoreUnauthorized: boolean; public dangerouslyIgnoreUnauthorized: boolean;
public httpServerPort: string; public httpServerPort: string;
public wsIntercept: boolean; public wsIntercept: boolean;
public conns: Map<string, net.Socket>;
public cltSockets: Map<string, net.Socket>;
public connectReqHandler: () => void; public connectReqHandler: () => void;
private userRequestHandler: () => void; public userRequestHandler: () => void;
private wsHandler: (wsClient: WebSocket, wsReq: http.IncomingMessage) => void; public wsHandler: (wsClient: WebSocket, wsReq: http.IncomingMessage) => void;
private httpsServerMgr: HttpsServerMgr; private httpsServerMgr: HttpsServerMgr;
/** /**
* Creates an instance of RequestHandler. * Creates an instance of RequestHandler.

View File

@ -309,7 +309,7 @@ function isFunc(source: object): boolean {
* @param {object} content * @param {object} content
* @returns the size of the content * @returns the size of the content
*/ */
function getByteSize(content: Buffer): number { function getByteSize(content: Buffer | string): number {
return Buffer.byteLength(content); return Buffer.byteLength(content);
} }

View File

@ -2,22 +2,44 @@
const DEFAULT_WEB_PORT = 8002; // port for web interface const DEFAULT_WEB_PORT = 8002; // port for web interface
const express = require('express'), import * as express from 'express';
url = require('url'), import * as url from 'url';
bodyParser = require('body-parser'), import * as bodyParser from 'body-parser';
fs = require('fs'), import * as fs from 'fs';
path = require('path'), import * as path from 'path';
events = require('events'), import * as events from 'events';
qrCode = require('qrcode-npm'), import * as qrCode from 'qrcode-npm';
util = require('./util').default, import * as juicer from 'juicer';
certMgr = require('./certMgr').default, import * as ip from 'ip';
wsServer = require('./wsServer'), import * as http from 'http';
juicer = require('juicer'), import * as compress from 'compression';
ip = require('ip'), import * as buffer from 'buffer';
compress = require('compression'); import util from './util';
import certMgr from './certMgr';
import WsServer from './wsServer';
import Recorder from './recorder';
import LogUtil from './log';
/*tslint:disable:no-var-requires*/
const packageJson = require('../package.json'); const packageJson = require('../package.json');
// const express = require('express'),
// url = require('url'),
// bodyParser = require('body-parser'),
// fs = require('fs'),
// path = require('path'),
// events = require('events'),
// qrCode = require('qrcode-npm'),
// util = require('./util').default,
// certMgr = require('./certMgr').default,
// wsServer = require('./wsServer'),
// juicer = require('juicer'),
// ip = require('ip'),
// compress = require('compression');
// const packageJson = require('../package.json');
const Buffer = buffer.Buffer;
const MAX_CONTENT_SIZE = 1024 * 2000; // 2000kb const MAX_CONTENT_SIZE = 1024 * 2000; // 2000kb
/** /**
* *
@ -25,18 +47,13 @@ const MAX_CONTENT_SIZE = 1024 * 2000; // 2000kb
* @class webInterface * @class webInterface
* @extends {events.EventEmitter} * @extends {events.EventEmitter}
*/ */
class webInterface extends events.EventEmitter { class WebInterface extends events.EventEmitter {
public webPort: number;
/** private recorder: Recorder;
* Creates an instance of webInterface. private app: Express.Application;
* private server: http.Server;
* @param {object} config private wsServer: WsServer;
* @param {number} config.webPort constructor(config: AnyProxyWebInterfaceConfig, recorder: Recorder) {
* @param {object} recorder
*
* @memberOf webInterface
*/
constructor(config, recorder) {
if (!recorder) { if (!recorder) {
throw new Error('recorder is required for web interface'); throw new Error('recorder is required for web interface');
} }
@ -44,7 +61,7 @@ class webInterface extends events.EventEmitter {
const self = this; const self = this;
self.webPort = config.webPort || DEFAULT_WEB_PORT; self.webPort = config.webPort || DEFAULT_WEB_PORT;
self.recorder = recorder; self.recorder = recorder;
self.config = config || {}; // self.config = config || {};
self.app = this.getServer(); self.app = this.getServer();
self.server = null; self.server = null;
@ -54,22 +71,22 @@ class webInterface extends events.EventEmitter {
/** /**
* get the express server * get the express server
*/ */
getServer() { public getServer(): Express.Application {
const self = this; const self = this;
const recorder = self.recorder; const recorder = self.recorder;
const ipAddress = ip.address(), const ipAddress = ip.address();
// userRule = proxyInstance.proxyRule, // userRule = proxyInstance.proxyRule,
webBasePath = 'web'; const webBasePath = 'web';
let ruleSummary = ''; let ruleSummary = '';
let customMenu = []; let customMenu = [];
try { try {
ruleSummary = ''; // userRule.summary(); ruleSummary = ''; // userRule.summary();
customMenu = ''; // userRule._getCustomMenu(); customMenu = []; // userRule._getCustomMenu();
} catch (e) { } } catch (e) { LogUtil.error(e.stack); }
const myAbsAddress = 'http://' + ipAddress + ':' + self.webPort + '/', const myAbsAddress = 'http://' + ipAddress + ':' + self.webPort + '/';
staticDir = path.join(__dirname, '../', webBasePath); const staticDir = path.join(__dirname, '../', webBasePath);
const app = express(); const app = express();
app.use(compress()); // invoke gzip app.use(compress()); // invoke gzip
@ -118,21 +135,21 @@ class webInterface extends events.EventEmitter {
if (query && query.id) { if (query && query.id) {
recorder.getDecodedBody(parseInt(query.id, 10), (err, result) => { recorder.getDecodedBody(parseInt(query.id, 10), (err, result) => {
// 返回下载信息 // 返回下载信息
const _resDownload = function (isDownload) { const resDownload = function(isDownload: boolean): void {
isDownload = typeof isDownload === 'boolean' ? isDownload : true; isDownload = typeof isDownload === 'boolean' ? isDownload : true;
res.json({ res.json({
id: query.id, id: query.id,
type: result.type, type: result.type,
method: result.meethod, method: result.method,
fileName: result.fileName, fileName: result.fileName,
ref: `/downloadBody?id=${query.id}&download=${isDownload}&raw=${!isDownload}` ref: `/downloadBody?id=${query.id}&download=${isDownload}&raw=${!isDownload}`,
}); });
}; };
// 返回内容 // 返回内容
const _resContent = () => { const resContent = () => {
if (util.getByteSize(result.content || '') > MAX_CONTENT_SIZE) { if (util.getByteSize(result.content || Buffer.from('')) > MAX_CONTENT_SIZE) {
_resDownload(true); resDownload(true);
return; return;
} }
@ -140,7 +157,7 @@ class webInterface extends events.EventEmitter {
id: query.id, id: query.id,
type: result.type, type: result.type,
method: result.method, method: result.method,
resBody: result.content resBody: result.content,
}); });
}; };
@ -151,14 +168,14 @@ class webInterface extends events.EventEmitter {
result.mime.indexOf('text') === 0 || result.mime.indexOf('text') === 0 ||
// deal with 'application/x-javascript' and 'application/javascript' // deal with 'application/x-javascript' and 'application/javascript'
result.mime.indexOf('javascript') > -1) { result.mime.indexOf('javascript') > -1) {
_resContent(); resContent();
} else if (result.type === 'image') { } else if (result.type === 'image') {
_resDownload(false); resDownload(false);
} else { } else {
_resDownload(true); resDownload(true);
} }
} else { } else {
_resContent(); resContent();
} }
}); });
} else { } else {
@ -203,11 +220,11 @@ class webInterface extends events.EventEmitter {
app.get('/fetchCrtFile', (req, res) => { app.get('/fetchCrtFile', (req, res) => {
res.setHeader('Access-Control-Allow-Origin', '*'); res.setHeader('Access-Control-Allow-Origin', '*');
const _crtFilePath = certMgr.getRootCAFilePath(); const crtFilePath = certMgr.getRootCAFilePath();
if (_crtFilePath) { if (crtFilePath) {
res.setHeader('Content-Type', 'application/x-x509-ca-cert'); res.setHeader('Content-Type', 'application/x-x509-ca-cert');
res.setHeader('Content-Disposition', 'attachment; filename="rootCA.crt"'); res.setHeader('Content-Disposition', 'attachment; filename="rootCA.crt"');
res.end(fs.readFileSync(_crtFilePath, { encoding: null })); res.end(fs.readFileSync(crtFilePath, { encoding: null }));
} else { } else {
res.setHeader('Content-Type', 'text/html'); res.setHeader('Content-Type', 'text/html');
res.end('can not file rootCA ,plase use <strong>anyproxy --root</strong> to generate one'); res.end('can not file rootCA ,plase use <strong>anyproxy --root</strong> to generate one');
@ -217,35 +234,37 @@ class webInterface extends events.EventEmitter {
// make qr code // make qr code
app.get('/qr', (req, res) => { app.get('/qr', (req, res) => {
res.setHeader('Access-Control-Allow-Origin', '*'); res.setHeader('Access-Control-Allow-Origin', '*');
const qr = qrCode.qrcode(4, 'M'), const qr = qrCode.qrcode(4, 'M');
targetUrl = myAbsAddress; const targetUrl = myAbsAddress;
qr.addData(targetUrl); qr.addData(targetUrl);
qr.make(); qr.make();
const qrImageTag = qr.createImgTag(4); const qrImageTag = qr.createImgTag(4);
const resDom = '<a href="__url"> __img <br> click or scan qr code to start client </a>'.replace(/__url/, targetUrl).replace(/__img/, qrImageTag); const resDom = '<a href="__url"> __img <br> click or scan qr code to start client </a>'
.replace(/__url/, targetUrl).replace(/__img/, qrImageTag);
res.setHeader('Content-Type', 'text/html'); res.setHeader('Content-Type', 'text/html');
res.end(resDom); res.end(resDom);
}); });
app.get('/api/getQrCode', (req, res) => { app.get('/api/getQrCode', (req, res) => {
res.setHeader('Access-Control-Allow-Origin', '*'); res.setHeader('Access-Control-Allow-Origin', '*');
const qr = qrCode.qrcode(4, 'M'), const qr = qrCode.qrcode(4, 'M');
targetUrl = myAbsAddress + 'fetchCrtFile'; const targetUrl = myAbsAddress + 'fetchCrtFile';
qr.addData(targetUrl); qr.addData(targetUrl);
qr.make(); qr.make();
const qrImageTag = qr.createImgTag(4); const qrImageTag = qr.createImgTag(4);
// resDom = '<a href="__url"> __img <br> click or scan qr code to download rootCA.crt </a>'.replace(/__url/,targetUrl).replace(/__img/,qrImageTag); // resDom = '<a href="__url"> __img <br> click or scan qr code to download rootCA.crt </a>'
// .replace(/__url/,targetUrl).replace(/__img/,qrImageTag);
// res.setHeader("Content-Type", "text/html"); // res.setHeader("Content-Type", "text/html");
// res.end(resDom); // res.end(resDom);
const isRootCAFileExists = certMgr.isRootCAFileExists(); const isRootCAFileExists = certMgr.ifRootCAFileExists();
res.json({ res.json({
status: 'success', status: 'success',
url: targetUrl, url: targetUrl,
isRootCAFileExists, isRootCAFileExists,
qrImgDom: qrImageTag qrImgDom: qrImageTag,
}); });
}); });
@ -265,7 +284,7 @@ class webInterface extends events.EventEmitter {
ruleSummary: ruleSummary || '', ruleSummary: ruleSummary || '',
ipAddress: util.getAllIpAddress(), ipAddress: util.getAllIpAddress(),
port: '', // proxyInstance.proxyPort, // TODO port: '', // proxyInstance.proxyPort, // TODO
appVersion: packageJson.version appVersion: packageJson.version,
}); });
}); });
@ -276,23 +295,23 @@ class webInterface extends events.EventEmitter {
certMgr.generateRootCA(() => { certMgr.generateRootCA(() => {
res.json({ res.json({
status: 'success', status: 'success',
code: 'done' code: 'done',
}); });
}); });
} else { } else {
res.json({ res.json({
status: 'success', status: 'success',
code: 'root_ca_exists' code: 'root_ca_exists',
}); });
} }
}); });
app.use((req, res, next) => { app.use((req, res, next) => {
const indexTpl = fs.readFileSync(path.join(staticDir, '/index.html'), { encoding: 'utf8' }), const indexTpl = fs.readFileSync(path.join(staticDir, '/index.html'), { encoding: 'utf8' });
opt = { const opt = {
rule: ruleSummary || '', rule: ruleSummary || '',
customMenu: customMenu || [], customMenu: customMenu || [],
ipAddress: ipAddress || '127.0.0.1' ipAddress: ipAddress || '127.0.0.1',
}; };
if (url.parse(req.url).pathname === '/') { if (url.parse(req.url).pathname === '/') {
@ -306,25 +325,25 @@ class webInterface extends events.EventEmitter {
return app; return app;
} }
start() { public start(): Promise<undefined> {
const self = this; const self = this;
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
self.server = self.app.listen(self.webPort); self.server = (self.app as any).listen(self.webPort);
self.wsServer = new wsServer({ self.wsServer = new WsServer({
server: self.server server: self.server,
}, self.recorder); }, self.recorder);
self.wsServer.start(); self.wsServer.start();
resolve(); resolve();
}) });
} }
close() { public close(cb: (error: Error) => void): void {
this.server && this.server.close(); this.server && this.server.close();
this.wsServer && this.wsServer.closeAll(); this.wsServer && this.wsServer.closeAll();
this.server = null; this.server = null;
this.wsServer = null; this.wsServer = null;
this.proxyInstance = null; // this.proxyInstance = null;
} }
} }
module.exports = webInterface; export default WebInterface;

View File

@ -1,10 +1,11 @@
'use strict'; 'use strict';
// websocket server manager // websocket server manager, for the webinterface websocket handling
import * as WebSocket from 'ws'; import * as WebSocket from 'ws';
import Recorder from './recorder'; import Recorder from './recorder';
import logUtil from './log'; import logUtil from './log';
import { Server } from 'http'; import * as http from 'http';
import * as https from 'https';
declare interface IWsMessage { declare interface IWsMessage {
type?: 'error' | 'body'; type?: 'error' | 'body';
@ -18,7 +19,8 @@ declare interface IWsMessage {
} }
declare interface IWsServerConfig { declare interface IWsServerConfig {
server: Server; server: http.Server | https.Server;
connHandler?: (wsClient: WebSocket, wsReq: http.IncomingMessage) => void;
} }
declare interface IMultiMessageQueue { declare interface IMultiMessageQueue {
@ -198,4 +200,4 @@ class WsServer {
} }
} }
module.exports = WsServer; export default WsServer;

View File

@ -1,9 +1,10 @@
/** /**
* manage the websocket server * manage the websocket server, for proxy target only
* *
*/ */
import * as ws from 'ws'; import * as ws from 'ws';
import * as http from 'http'; import * as http from 'http';
import * as https from 'https';
import logUtil from './log.js'; import logUtil from './log.js';
const WsServer = ws.Server; const WsServer = ws.Server;
@ -15,7 +16,7 @@ const WsServer = ws.Server;
{handler} config.handler {handler} config.handler
*/ */
function getWsServer(config: { function getWsServer(config: {
server: http.Server; server: http.Server | https.Server;
connHandler: (wsClient: ws, wsReq: http.IncomingMessage) => void; connHandler: (wsClient: ws, wsReq: http.IncomingMessage) => void;
}): ws.Server { }): ws.Server {
const wss = new WsServer({ const wss = new WsServer({

View File

@ -40,6 +40,7 @@
"ws": "^5.1.0" "ws": "^5.1.0"
}, },
"devDependencies": { "devDependencies": {
"@types/express": "^4.16.0",
"@types/node": "^8.10.21", "@types/node": "^8.10.21",
"@types/ws": "^6.0.0", "@types/ws": "^6.0.0",
"antd": "^2.5.0", "antd": "^2.5.0",

View File

@ -14,6 +14,8 @@
"only-arrow-functions": false, "only-arrow-functions": false,
"no-reference": false, "no-reference": false,
"forin": false, "forin": false,
"member-ordering": false,
"max-classes-per-file": false,
"typedef": [ "typedef": [
true, true,
"call-signature", "call-signature",

29
typings/index.d.ts vendored
View File

@ -7,17 +7,27 @@ declare module NodeJS {
} }
} }
declare interface AnyProxyWebInterfaceConfig {
webPort?: number;
}
declare interface AnyProxyConfig { declare interface AnyProxyConfig {
port: string; // 代理监听端口 port?: string; // port of the proxy server
httpServerPort: string; // web server 的端口 httpServerPort?: string; // web server 的端口
forceProxyHttps: boolean; type?: 'http' | 'https'; // type of the proxy server
dangerouslyIgnoreUnauthorized: boolean; // 是否忽略https证书 forceProxyHttps?: boolean; // proxy https also
wsIntercept: boolean; // 是否代理websocket dangerouslyIgnoreUnauthorized?: boolean; // should ignore
chunkSizeThreshold: number; // throttle的配置 wsIntercept?: boolean; // should proxy websocket
throttle?: string; // speed limit in kb/s
hostname?: string; // the hostname of this proxy, default to 'localhost'
recorder?: any; // A Recorder instance
silent?: boolean; // if keep the console silent
rule?: any; // rule module to use
webInterface?: AnyProxyWebInterfaceConfig;
} }
declare interface AnyProxyRule { declare interface AnyProxyRule {
summary?: string, summary?: string | Function,
beforeSendRequest?: Function, beforeSendRequest?: Function,
beforeSendResponse?: Function, beforeSendResponse?: Function,
beforeDealHttpsRequest?: Function, beforeDealHttpsRequest?: Function,
@ -87,3 +97,8 @@ declare interface IExecScriptResult {
status: number; status: number;
stdout?: string; stdout?: string;
} }
declare module "*.json" {
const value: any;
export default value;
}