diff --git a/lib/certMgr.ts b/lib/certMgr.ts index 3b92420..696f539 100644 --- a/lib/certMgr.ts +++ b/lib/certMgr.ts @@ -8,11 +8,13 @@ import util from './util'; import logUtil from './log'; declare interface ICertMgr { - ifRootCAFileExists?: boolean; generateRootCA?: ( cb: (error: boolean, keyPath: string, crtPath: string) => void ) => void; getCAStatus?: () => Generator; trustRootCA?: () => Generator; getRootCAFilePath?: () => string; + ifRootCAFileExists?: () => boolean; + isRootCAFileExists?: () => boolean; + getRootDirPath?: () => string; getCertificate?: (serverName: string, cb: (err: Error, key: string, crt: string) => void) => void; } diff --git a/lib/proxy/index.ts b/lib/proxy/index.ts new file mode 100644 index 0000000..710d8c4 --- /dev/null +++ b/lib/proxy/index.ts @@ -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, +}; diff --git a/lib/proxy.js b/lib/proxy/proxyCore.ts similarity index 56% rename from lib/proxy.js rename to lib/proxy/proxyCore.ts index 78a3549..5a2acb1 100644 --- a/lib/proxy.js +++ b/lib/proxy/proxyCore.ts @@ -1,41 +1,28 @@ 'use strict'; +/*tslint:disable:max-line-length */ -const http = require('http'), - https = require('https'), - async = require('async'), - color = require('colorful'), - certMgr = require('./certMgr').default, - Recorder = require('./recorder').default, - logUtil = require('./log').default, - util = require('./util').default, - events = require('events'), - co = require('co'), - WebInterface = require('./webInterface'), - wsServerMgr = require('./wsServerMgr').default, - ThrottleGroup = require('stream-throttle').ThrottleGroup; +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'; -// const memwatch = require('memwatch-next'); +declare type TSyncTaskCb = (err: Error) => void; -// setInterval(() => { -// console.log(process.memoryUsage()); -// const rss = Math.ceil(process.memoryUsage().rss / 1000 / 1000); -// console.log('Program is using ' + rss + ' mb of Heap.'); -// }, 1000); +const ThrottleGroup = throttle.ThrottleGroup; -// 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', - T_TYPE_HTTPS = 'https', - DEFAULT_TYPE = T_TYPE_HTTP; +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'; @@ -47,25 +34,24 @@ const PROXY_STATUS_CLOSED = 'CLOSED'; * @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. - * - * @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 */ - constructor(config) { + constructor(config: AnyProxyConfig) { super(); config = config || {}; @@ -114,12 +100,12 @@ class ProxyCore extends events.EventEmitter { this.recorder = config.recorder; // init request handler - const RequestHandler = util.freshRequire('./requestHandler').default; - this.requestHandler = new RequestHandler({ + 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 + dangerouslyIgnoreUnauthorized: !!config.dangerouslyIgnoreUnauthorized, }, this.proxyRule, this.recorder); } @@ -133,7 +119,7 @@ class ProxyCore extends events.EventEmitter { * @returns undefined * @memberOf ProxyCore */ - handleExistConnections(socket) { + private handleExistConnections(socket: http.IncomingMessage): void { const self = this; self.socketIndex ++; const key = `socketIndex_${self.socketIndex}`; @@ -151,7 +137,7 @@ class ProxyCore extends events.EventEmitter { * * @memberOf ProxyCore */ - start() { + public start(): ProxyCore { const self = this; self.socketIndex = 0; self.socketPool = {}; @@ -161,8 +147,8 @@ class ProxyCore extends events.EventEmitter { } async.series( [ - //creat proxy server - function (callback) { + // creat proxy server + function(callback: TSyncTaskCb): void { if (self.proxyType === T_TYPE_HTTPS) { certMgr.getCertificate(self.proxyHostName, (err, keyContent, crtContent) => { if (err) { @@ -170,7 +156,7 @@ class ProxyCore extends events.EventEmitter { } else { self.httpProxyServer = https.createServer({ key: keyContent, - cert: crtContent + cert: crtContent, }, self.requestHandler.userRequestHandler); callback(null); } @@ -181,17 +167,17 @@ class ProxyCore extends events.EventEmitter { } }, - //handle CONNECT request for https over http - function (callback) { + // handle CONNECT request for https over http + function(callback: TSyncTaskCb): void { self.httpProxyServer.on('connect', self.requestHandler.connectReqHandler); callback(null); }, - function (callback) { + function(callback: TSyncTaskCb): void { wsServerMgr.getWsServer({ server: self.httpProxyServer, - connHandler: self.requestHandler.wsHandler + connHandler: self.requestHandler.wsHandler, }); // remember all sockets, so we can destory them when call the method 'close'; self.httpProxyServer.on('connection', (socket) => { @@ -200,14 +186,14 @@ class ProxyCore extends events.EventEmitter { callback(null); }, - //start proxy server - function (callback) { + // start proxy server + function(callback: TSyncTaskCb): void { self.httpProxyServer.listen(self.proxyPort); callback(null); }, ], - //final callback + // final callback (err, result) => { if (!err) { const tipText = (self.proxyType === T_TYPE_HTTP ? 'Http' : 'Https') + ' proxy started on port ' + self.proxyPort; @@ -221,7 +207,7 @@ class ProxyCore extends events.EventEmitter { let ruleSummaryString = ''; const ruleSummary = this.proxyRule.summary; if (ruleSummary) { - co(function *() { + co(function *(): Generator { if (typeof ruleSummary === 'string') { ruleSummaryString = ruleSummary; } else { @@ -239,10 +225,10 @@ class ProxyCore extends events.EventEmitter { logUtil.printLog(color.red(tipText), logUtil.T_ERR); logUtil.printLog(err, logUtil.T_ERR); self.emit('error', { - error: err + error: err, }); } - } + }, ); return self; @@ -256,24 +242,34 @@ class ProxyCore extends events.EventEmitter { * * @memberOf ProxyCore */ - close() { + public close(): Promise { // clear recorder cache return new Promise((resolve) => { if (this.httpProxyServer) { // destroy conns & cltSockets when closing proxy server - for (const connItem of this.requestHandler.conns) { - const key = connItem[0]; - const conn = connItem[1]; + this.requestHandler.conns.forEach((conn, key) => { logUtil.printLog(`destorying https connection : ${key}`); conn.end(); - } + }); - for (const cltSocketItem of this.requestHandler.cltSockets) { - const key = cltSocketItem[0]; - const cltSocket = cltSocketItem[1]; + 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) { @@ -296,92 +292,8 @@ class ProxyCore extends events.EventEmitter { } else { 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; -module.exports.ProxyServer = ProxyServer; -module.exports.ProxyRecorder = Recorder; -module.exports.ProxyWebServer = WebInterface; -module.exports.utils = { - systemProxyMgr: require('./systemProxyMgr'), - certMgr, -}; +export default ProxyCore; diff --git a/lib/proxy/proxyServer.ts b/lib/proxy/proxyServer.ts new file mode 100644 index 0000000..fb41862 --- /dev/null +++ b/lib/proxy/proxyServer.ts @@ -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 { + 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; diff --git a/lib/requestHandler/index.ts b/lib/requestHandler/index.ts index e39e526..efcdba2 100644 --- a/lib/requestHandler/index.ts +++ b/lib/requestHandler/index.ts @@ -412,9 +412,11 @@ class RequestHandler { public dangerouslyIgnoreUnauthorized: boolean; public httpServerPort: string; public wsIntercept: boolean; + public conns: Map; + public cltSockets: Map; public connectReqHandler: () => void; - private userRequestHandler: () => void; - private wsHandler: (wsClient: WebSocket, wsReq: http.IncomingMessage) => void; + public userRequestHandler: () => void; + public wsHandler: (wsClient: WebSocket, wsReq: http.IncomingMessage) => void; private httpsServerMgr: HttpsServerMgr; /** * Creates an instance of RequestHandler. diff --git a/lib/util.ts b/lib/util.ts index 683a9d9..ac60517 100644 --- a/lib/util.ts +++ b/lib/util.ts @@ -309,7 +309,7 @@ function isFunc(source: object): boolean { * @param {object} content * @returns the size of the content */ -function getByteSize(content: Buffer): number { +function getByteSize(content: Buffer | string): number { return Buffer.byteLength(content); } diff --git a/lib/webInterface.js b/lib/webInterface.ts similarity index 66% rename from lib/webInterface.js rename to lib/webInterface.ts index ff04155..6730cd9 100644 --- a/lib/webInterface.js +++ b/lib/webInterface.ts @@ -2,22 +2,44 @@ const DEFAULT_WEB_PORT = 8002; // port for web interface -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'); +import * as express from 'express'; +import * as url from 'url'; +import * as bodyParser from 'body-parser'; +import * as fs from 'fs'; +import * as path from 'path'; +import * as events from 'events'; +import * as qrCode from 'qrcode-npm'; +import * as juicer from 'juicer'; +import * as ip from 'ip'; +import * as http from 'http'; +import * as compress from 'compression'; +import * as buffer from 'buffer'; +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 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 /** * @@ -25,18 +47,13 @@ const MAX_CONTENT_SIZE = 1024 * 2000; // 2000kb * @class webInterface * @extends {events.EventEmitter} */ -class webInterface extends events.EventEmitter { - - /** - * Creates an instance of webInterface. - * - * @param {object} config - * @param {number} config.webPort - * @param {object} recorder - * - * @memberOf webInterface - */ - constructor(config, recorder) { +class WebInterface extends events.EventEmitter { + public webPort: number; + private recorder: Recorder; + private app: Express.Application; + private server: http.Server; + private wsServer: WsServer; + constructor(config: AnyProxyWebInterfaceConfig, recorder: Recorder) { if (!recorder) { throw new Error('recorder is required for web interface'); } @@ -44,7 +61,7 @@ class webInterface extends events.EventEmitter { const self = this; self.webPort = config.webPort || DEFAULT_WEB_PORT; self.recorder = recorder; - self.config = config || {}; + // self.config = config || {}; self.app = this.getServer(); self.server = null; @@ -54,25 +71,25 @@ class webInterface extends events.EventEmitter { /** * get the express server */ - getServer() { + public getServer(): Express.Application { const self = this; const recorder = self.recorder; - const ipAddress = ip.address(), + const ipAddress = ip.address(); // userRule = proxyInstance.proxyRule, - webBasePath = 'web'; + const webBasePath = 'web'; let ruleSummary = ''; let customMenu = []; try { - ruleSummary = ''; //userRule.summary(); - customMenu = ''; // userRule._getCustomMenu(); - } catch (e) { } + ruleSummary = ''; // userRule.summary(); + customMenu = []; // userRule._getCustomMenu(); + } catch (e) { LogUtil.error(e.stack); } - const myAbsAddress = 'http://' + ipAddress + ':' + self.webPort + '/', - staticDir = path.join(__dirname, '../', webBasePath); + const myAbsAddress = 'http://' + ipAddress + ':' + self.webPort + '/'; + const staticDir = path.join(__dirname, '../', webBasePath); const app = express(); - app.use(compress()); //invoke gzip + app.use(compress()); // invoke gzip app.use((req, res, next) => { res.setHeader('note', 'THIS IS A REQUEST FROM ANYPROXY WEB INTERFACE'); return next(); @@ -97,7 +114,7 @@ class webInterface extends events.EventEmitter { res.json({}); } else if (result.mime) { if (query.raw === 'true') { - //TODO : cache query result + // TODO : cache query result res.type(result.mime).end(result.content); } else if (query.download === 'true') { res.setHeader('Content-disposition', `attachment; filename=${result.fileName}`); @@ -118,21 +135,21 @@ class webInterface extends events.EventEmitter { if (query && query.id) { recorder.getDecodedBody(parseInt(query.id, 10), (err, result) => { // 返回下载信息 - const _resDownload = function (isDownload) { + const resDownload = function(isDownload: boolean): void { isDownload = typeof isDownload === 'boolean' ? isDownload : true; res.json({ id: query.id, type: result.type, - method: result.meethod, + method: result.method, fileName: result.fileName, - ref: `/downloadBody?id=${query.id}&download=${isDownload}&raw=${!isDownload}` + ref: `/downloadBody?id=${query.id}&download=${isDownload}&raw=${!isDownload}`, }); }; // 返回内容 - const _resContent = () => { - if (util.getByteSize(result.content || '') > MAX_CONTENT_SIZE) { - _resDownload(true); + const resContent = () => { + if (util.getByteSize(result.content || Buffer.from('')) > MAX_CONTENT_SIZE) { + resDownload(true); return; } @@ -140,7 +157,7 @@ class webInterface extends events.EventEmitter { id: query.id, type: result.type, method: result.method, - resBody: result.content + resBody: result.content, }); }; @@ -151,14 +168,14 @@ class webInterface extends events.EventEmitter { result.mime.indexOf('text') === 0 || // deal with 'application/x-javascript' and 'application/javascript' result.mime.indexOf('javascript') > -1) { - _resContent(); + resContent(); } else if (result.type === 'image') { - _resDownload(false); + resDownload(false); } else { - _resDownload(true); + resDownload(true); } } else { - _resContent(); + resContent(); } }); } else { @@ -203,49 +220,51 @@ class webInterface extends events.EventEmitter { app.get('/fetchCrtFile', (req, res) => { res.setHeader('Access-Control-Allow-Origin', '*'); - const _crtFilePath = certMgr.getRootCAFilePath(); - if (_crtFilePath) { + const crtFilePath = certMgr.getRootCAFilePath(); + if (crtFilePath) { res.setHeader('Content-Type', 'application/x-x509-ca-cert'); res.setHeader('Content-Disposition', 'attachment; filename="rootCA.crt"'); - res.end(fs.readFileSync(_crtFilePath, { encoding: null })); + res.end(fs.readFileSync(crtFilePath, { encoding: null })); } else { res.setHeader('Content-Type', 'text/html'); res.end('can not file rootCA ,plase use anyproxy --root to generate one'); } }); - //make qr code + // make qr code app.get('/qr', (req, res) => { res.setHeader('Access-Control-Allow-Origin', '*'); - const qr = qrCode.qrcode(4, 'M'), - targetUrl = myAbsAddress; + const qr = qrCode.qrcode(4, 'M'); + const targetUrl = myAbsAddress; qr.addData(targetUrl); qr.make(); const qrImageTag = qr.createImgTag(4); - const resDom = ' __img
click or scan qr code to start client
'.replace(/__url/, targetUrl).replace(/__img/, qrImageTag); + const resDom = ' __img
click or scan qr code to start client
' + .replace(/__url/, targetUrl).replace(/__img/, qrImageTag); res.setHeader('Content-Type', 'text/html'); res.end(resDom); }); app.get('/api/getQrCode', (req, res) => { res.setHeader('Access-Control-Allow-Origin', '*'); - const qr = qrCode.qrcode(4, 'M'), - targetUrl = myAbsAddress + 'fetchCrtFile'; + const qr = qrCode.qrcode(4, 'M'); + const targetUrl = myAbsAddress + 'fetchCrtFile'; qr.addData(targetUrl); qr.make(); const qrImageTag = qr.createImgTag(4); - // resDom = ' __img
click or scan qr code to download rootCA.crt
'.replace(/__url/,targetUrl).replace(/__img/,qrImageTag); + // resDom = ' __img
click or scan qr code to download rootCA.crt
' + // .replace(/__url/,targetUrl).replace(/__img/,qrImageTag); // res.setHeader("Content-Type", "text/html"); // res.end(resDom); - const isRootCAFileExists = certMgr.isRootCAFileExists(); + const isRootCAFileExists = certMgr.ifRootCAFileExists(); res.json({ status: 'success', url: targetUrl, isRootCAFileExists, - qrImgDom: qrImageTag + qrImgDom: qrImageTag, }); }); @@ -254,7 +273,7 @@ class webInterface extends events.EventEmitter { res.setHeader('Access-Control-Allow-Origin', '*'); const rootCAExists = certMgr.isRootCAFileExists(); const rootDirPath = certMgr.getRootDirPath(); - const interceptFlag = false; //proxyInstance.getInterceptFlag(); TODO + const interceptFlag = false; // proxyInstance.getInterceptFlag(); TODO const globalProxyFlag = false; // TODO: proxyInstance.getGlobalProxyFlag(); res.json({ status: 'success', @@ -264,8 +283,8 @@ class webInterface extends events.EventEmitter { currentGlobalProxyFlag: globalProxyFlag, ruleSummary: ruleSummary || '', ipAddress: util.getAllIpAddress(), - port: '', //proxyInstance.proxyPort, // TODO - appVersion: packageJson.version + port: '', // proxyInstance.proxyPort, // TODO + appVersion: packageJson.version, }); }); @@ -276,23 +295,23 @@ class webInterface extends events.EventEmitter { certMgr.generateRootCA(() => { res.json({ status: 'success', - code: 'done' + code: 'done', }); }); } else { res.json({ status: 'success', - code: 'root_ca_exists' + code: 'root_ca_exists', }); } }); app.use((req, res, next) => { - const indexTpl = fs.readFileSync(path.join(staticDir, '/index.html'), { encoding: 'utf8' }), - opt = { + const indexTpl = fs.readFileSync(path.join(staticDir, '/index.html'), { encoding: 'utf8' }); + const opt = { rule: ruleSummary || '', customMenu: customMenu || [], - ipAddress: ipAddress || '127.0.0.1' + ipAddress: ipAddress || '127.0.0.1', }; if (url.parse(req.url).pathname === '/') { @@ -306,25 +325,25 @@ class webInterface extends events.EventEmitter { return app; } - start() { + public start(): Promise { const self = this; return new Promise((resolve, reject) => { - self.server = self.app.listen(self.webPort); - self.wsServer = new wsServer({ - server: self.server + self.server = (self.app as any).listen(self.webPort); + self.wsServer = new WsServer({ + server: self.server, }, self.recorder); self.wsServer.start(); resolve(); - }) + }); } - close() { + public close(cb: (error: Error) => void): void { this.server && this.server.close(); this.wsServer && this.wsServer.closeAll(); this.server = null; this.wsServer = null; - this.proxyInstance = null; + // this.proxyInstance = null; } } -module.exports = webInterface; +export default WebInterface; diff --git a/lib/wsServer.ts b/lib/wsServer.ts index d81ac63..bac5fe7 100644 --- a/lib/wsServer.ts +++ b/lib/wsServer.ts @@ -1,10 +1,11 @@ 'use strict'; -// websocket server manager +// websocket server manager, for the webinterface websocket handling import * as WebSocket from 'ws'; import Recorder from './recorder'; import logUtil from './log'; -import { Server } from 'http'; +import * as http from 'http'; +import * as https from 'https'; declare interface IWsMessage { type?: 'error' | 'body'; @@ -18,7 +19,8 @@ declare interface IWsMessage { } declare interface IWsServerConfig { - server: Server; + server: http.Server | https.Server; + connHandler?: (wsClient: WebSocket, wsReq: http.IncomingMessage) => void; } declare interface IMultiMessageQueue { @@ -198,4 +200,4 @@ class WsServer { } } -module.exports = WsServer; +export default WsServer; diff --git a/lib/wsServerMgr.ts b/lib/wsServerMgr.ts index fa04bf1..e735d9b 100644 --- a/lib/wsServerMgr.ts +++ b/lib/wsServerMgr.ts @@ -1,9 +1,10 @@ /** -* manage the websocket server +* manage the websocket server, for proxy target only * */ import * as ws from 'ws'; import * as http from 'http'; +import * as https from 'https'; import logUtil from './log.js'; const WsServer = ws.Server; @@ -15,7 +16,7 @@ const WsServer = ws.Server; {handler} config.handler */ function getWsServer(config: { - server: http.Server; + server: http.Server | https.Server; connHandler: (wsClient: ws, wsReq: http.IncomingMessage) => void; }): ws.Server { const wss = new WsServer({ diff --git a/package.json b/package.json index c17c4c4..0b1e66e 100644 --- a/package.json +++ b/package.json @@ -40,6 +40,7 @@ "ws": "^5.1.0" }, "devDependencies": { + "@types/express": "^4.16.0", "@types/node": "^8.10.21", "@types/ws": "^6.0.0", "antd": "^2.5.0", diff --git a/tslint.json b/tslint.json index f770284..66c3d62 100644 --- a/tslint.json +++ b/tslint.json @@ -14,6 +14,8 @@ "only-arrow-functions": false, "no-reference": false, "forin": false, + "member-ordering": false, + "max-classes-per-file": false, "typedef": [ true, "call-signature", diff --git a/typings/index.d.ts b/typings/index.d.ts index 5564e63..73c5a5f 100644 --- a/typings/index.d.ts +++ b/typings/index.d.ts @@ -7,17 +7,27 @@ declare module NodeJS { } } +declare interface AnyProxyWebInterfaceConfig { + webPort?: number; +} + declare interface AnyProxyConfig { - port: string; // 代理监听端口 - httpServerPort: string; // web server 的端口 - forceProxyHttps: boolean; - dangerouslyIgnoreUnauthorized: boolean; // 是否忽略https证书 - wsIntercept: boolean; // 是否代理websocket - chunkSizeThreshold: number; // throttle的配置 + port?: string; // port of the proxy server + httpServerPort?: string; // web server 的端口 + type?: 'http' | 'https'; // type of the proxy server + forceProxyHttps?: boolean; // proxy https also + dangerouslyIgnoreUnauthorized?: boolean; // should ignore + 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 { - summary?: string, + summary?: string | Function, beforeSendRequest?: Function, beforeSendResponse?: Function, beforeDealHttpsRequest?: Function, @@ -86,4 +96,9 @@ declare interface OneLevelObjectType { declare interface IExecScriptResult { status: number; stdout?: string; +} + +declare module "*.json" { + const value: any; + export default value; } \ No newline at end of file