diff --git a/bin/anyproxy b/bin/anyproxy index d373535..34f1cbf 100755 --- a/bin/anyproxy +++ b/bin/anyproxy @@ -9,7 +9,7 @@ const program = require('commander'), util = require('../dist/util').default, rootCACheck = require('./rootCaCheck'), startServer = require('./startServer'), - certMgr = require('../dist/certMgr'), + certMgr = require('../dist/certMgr').default, logUtil = require('../dist/log'); program diff --git a/lib/certMgr.js b/lib/certMgr.ts similarity index 62% rename from lib/certMgr.js rename to lib/certMgr.ts index 07e73e4..3b92420 100644 --- a/lib/certMgr.js +++ b/lib/certMgr.ts @@ -1,12 +1,20 @@ -'use strict' +'use strict'; -const EasyCert = require('node-easy-cert'); -const co = require('co'); -const os = require('os'); -const inquirer = require('inquirer'); +import * as EasyCert from 'node-easy-cert'; +import * as co from 'co'; +import * as os from 'os'; +import * as inquirer from 'inquirer'; +import util from './util'; +import logUtil from './log'; -const util = require('./util').default; -const logUtil = require('./log'); +declare interface ICertMgr { + ifRootCAFileExists?: boolean; + generateRootCA?: ( cb: (error: boolean, keyPath: string, crtPath: string) => void ) => void; + getCAStatus?: () => Generator; + trustRootCA?: () => Generator; + getRootCAFilePath?: () => string; + getCertificate?: (serverName: string, cb: (err: Error, key: string, crt: string) => void) => void; +} const options = { rootDirPath: util.getAnyProxyPath('certificates'), @@ -15,24 +23,24 @@ const options = { { name: 'countryName', value: 'CN' }, { name: 'organizationName', value: 'AnyProxy' }, { shortName: 'ST', value: 'SH' }, - { shortName: 'OU', value: 'AnyProxy SSL Proxy' } - ] + { shortName: 'OU', value: 'AnyProxy SSL Proxy' }, + ], }; const easyCert = new EasyCert(options); -const crtMgr = util.merge({}, easyCert); +const crtMgr: ICertMgr = util.merge({}, easyCert); // rename function crtMgr.ifRootCAFileExists = easyCert.isRootCAFileExists; -crtMgr.generateRootCA = function (cb) { +crtMgr.generateRootCA = function(cb: (error: boolean, keyPath: string, crtPath: string) => void): void { doGenerate(false); // set default common name of the cert - function doGenerate(overwrite) { + function doGenerate(overwrite: boolean): void { const rootOptions = { commonName: 'AnyProxy', - overwrite: !!overwrite + overwrite: !!overwrite, }; easyCert.generateRootCA(rootOptions, (error, keyPath, crtPath) => { @@ -41,10 +49,11 @@ crtMgr.generateRootCA = function (cb) { } }; -crtMgr.getCAStatus = function *() { - return co(function *() { +crtMgr.getCAStatus = function *(): Generator { + return co(function *(): Generator { const result = { exist: false, + trusted: undefined, }; const ifExist = easyCert.isRootCAFileExists(); if (!ifExist) { @@ -57,12 +66,12 @@ crtMgr.getCAStatus = function *() { return result; } }); -} +}; /** * trust the root ca by command */ -crtMgr.trustRootCA = function *() { +crtMgr.trustRootCA = function *(): Generator { const platform = os.platform(); const rootCAPath = crtMgr.getRootCAFilePath(); const trustInquiry = [ @@ -70,8 +79,8 @@ crtMgr.trustRootCA = function *() { type: 'list', name: 'trustCA', message: 'The rootCA is not trusted yet, install it to the trust store now?', - choices: ['Yes', "No, I'll do it myself"] - } + choices: ['Yes', 'No, I\'ll do it myself'], + }, ]; if (platform === 'darwin') { @@ -79,7 +88,8 @@ crtMgr.trustRootCA = function *() { if (answer.trustCA === 'Yes') { logUtil.info('About to trust the root CA, this may requires your password'); // https://ss64.com/osx/security-cert.html - const result = util.execScriptSync(`sudo security add-trusted-cert -d -k /Library/Keychains/System.keychain ${rootCAPath}`); + const result = + (util.execScriptSync(`sudo security add-trusted-cert -d -k /Library/Keychains/System.keychain ${rootCAPath}`) as IExecScriptResult); if (result.status === 0) { logUtil.info('Root CA install, you are ready to intercept the https now'); } else { @@ -98,6 +108,6 @@ crtMgr.trustRootCA = function *() { logUtil.info('You can install the root CA manually.'); } logUtil.info('The root CA file path is: ' + crtMgr.getRootCAFilePath()); -} +}; -module.exports = crtMgr; +export default crtMgr; diff --git a/lib/httpsServerMgr.js b/lib/httpsServerMgr.ts similarity index 59% rename from lib/httpsServerMgr.js rename to lib/httpsServerMgr.ts index 6ce6380..6d29c22 100644 --- a/lib/httpsServerMgr.js +++ b/lib/httpsServerMgr.ts @@ -1,25 +1,46 @@ -'use strict' +'use strict'; -//manage https servers -const async = require('async'), - https = require('https'), - tls = require('tls'), - crypto = require('crypto'), - color = require('colorful'), - certMgr = require('./certMgr'), - logUtil = require('./log'), - util = require('./util').default, - wsServerMgr = require('./wsServerMgr'), - co = require('co'), - constants = require('constants'), - asyncTask = require('async-task-mgr'); +import * as async from 'async'; +import * as https from 'https'; +import * as http from 'http'; +import * as tls from 'tls'; +import * as crypto from 'crypto'; +import * as color from 'colorful'; +import * as WebSocket from 'ws'; +import * as constants from 'constants'; +import * as AsyncTask from 'async-task-mgr'; +import Recorder from './recorder'; +import certMgr from './certMgr'; +import logUtil from './log'; +import util from './util'; +import * as wsServerMgr from './wsServerMgr'; +import * as co from 'co'; -const createSecureContext = tls.createSecureContext || crypto.createSecureContext; -//using sni to avoid multiple ports -function SNIPrepareCert(serverName, SNICallback) { - let keyContent, - crtContent, - ctx; +// // manage https servers +// const async = require('async'), +// https = require('https'), +// tls = require('tls'), +// crypto = require('crypto'), +// color = require('colorful'), +// certMgr = require('./certMgr'), +// logUtil = require('./log'), +// util = require('./util').default, +// wsServerMgr = require('./wsServerMgr'), +// co = require('co'), +// constants = require('constants'), +// asyncTask = require('async-task-mgr'); + +declare type THttpsRequestHanlder = (req: http.IncomingMessage, userRes: http.ServerResponse) => void; +declare type TWsRequestHandler = + (userRule: AnyProxyRule, recorder: Recorder, wsClient: WebSocket, wsReq: http.IncomingMessage) => void; + +const createSecureContext = tls.createSecureContext || (crypto as any).createSecureContext; +// using sni to avoid multiple ports +function SNIPrepareCert( + serverName: string, SNICallback: (error: Error, ctx: tls.SecureContext) => void): void { + let keyContent; + let crtContent; + let ctx; async.series([ (callback) => { @@ -37,13 +58,13 @@ function SNIPrepareCert(serverName, SNICallback) { try { ctx = createSecureContext({ key: keyContent, - cert: crtContent + cert: crtContent, }); callback(); } catch (e) { callback(e); } - } + }, ], (err) => { if (!err) { const tipText = 'proxy server for __NAME established'.replace('__NAME', serverName); @@ -56,18 +77,17 @@ function SNIPrepareCert(serverName, SNICallback) { }); } -//config.port - port to start https server -//config.handler - request handler - - /** * Create an https server * * @param {object} config - * @param {number} config.port - * @param {function} config.handler + * @param {number} config.port port to start https server + * @param {function} config.handler request handler */ -function createHttpsServer(config) { +function createHttpsServer(config: { + port: number; + handler: THttpsRequestHanlder; +}): Promise { if (!config || !config.port || !config.handler) { throw (new Error('please assign a port')); } @@ -78,7 +98,7 @@ function createHttpsServer(config) { secureOptions: constants.SSL_OP_NO_SSLv3 || constants.SSL_OP_NO_TLSv1, SNICallback: SNIPrepareCert, key: keyContent, - cert: crtContent + cert: crtContent, }, config.handler).listen(config.port); resolve(server); }); @@ -92,7 +112,11 @@ function createHttpsServer(config) { * @param @required {number} config.port the port to listen on * @param @required {function} handler the handler of each connect */ -function createIPHttpsServer(config) { +function createIPHttpsServer(config: { + port: number; + ip: string; + handler: THttpsRequestHanlder; +}): Promise { if (!config || !config.port || !config.handler) { throw (new Error('please assign a port')); } @@ -106,7 +130,7 @@ function createIPHttpsServer(config) { const server = https.createServer({ secureOptions: constants.SSL_OP_NO_SSLv3 || constants.SSL_OP_NO_TLSv1, key: keyContent, - cert: crtContent + cert: crtContent, }, config.handler).listen(config.port); resolve(server); @@ -122,26 +146,39 @@ function createIPHttpsServer(config) { * @param {function} config.handler handler to deal https request * */ -class httpsServerMgr { - constructor(config) { +class HttpsServerMgr { + private instanceDefaultHost: string; + private httpsAsyncTask: AsyncTask; + private handler: THttpsRequestHanlder; + private wsHandler: TWsRequestHandler; + constructor(config: { + handler: THttpsRequestHanlder; + wsHandler: TWsRequestHandler; + }) { if (!config || !config.handler) { throw new Error('handler is required'); } this.instanceDefaultHost = '127.0.0.1'; - this.httpsAsyncTask = new asyncTask(); + this.httpsAsyncTask = new AsyncTask(); this.handler = config.handler; - this.wsHandler = config.wsHandler + this.wsHandler = config.wsHandler; } - getSharedHttpsServer(hostname) { + public getSharedHttpsServer(hostname: string): Promise<{ + host: string; + port: number; + }> { // ip address will have a unique name const finalHost = util.isIpDomain(hostname) ? hostname : this.instanceDefaultHost; const self = this; - function prepareServer(callback) { + function prepareServer(callback: (e: Error, result?: { + host: string; + port: number; + }) => void): void { let instancePort; co(util.getFreePort) - .then(co.wrap(function *(port) { + .then(co.wrap(function *(port: number): Generator { instancePort = port; let httpsServer = null; @@ -150,18 +187,18 @@ class httpsServerMgr { httpsServer = yield createIPHttpsServer({ ip: hostname, port, - handler: self.handler + handler: self.handler, }); } else { httpsServer = yield createHttpsServer({ port, - handler: self.handler + handler: self.handler, }); } wsServerMgr.getWsServer({ server: httpsServer, - connHandler: self.wsHandler + connHandler: self.wsHandler, }); httpsServer.on('upgrade', (req, cltSocket, head) => { @@ -175,7 +212,7 @@ class httpsServerMgr { callback(null, result); return result; })) - .catch(e => { + .catch((e) => { callback(e); }); } @@ -194,4 +231,4 @@ class httpsServerMgr { } } -export default httpsServerMgr; +export default HttpsServerMgr; diff --git a/lib/proxy.js b/lib/proxy.js index 85c25bc..44062a0 100644 --- a/lib/proxy.js +++ b/lib/proxy.js @@ -4,7 +4,7 @@ const http = require('http'), https = require('https'), async = require('async'), color = require('colorful'), - certMgr = require('./certMgr'), + certMgr = require('./certMgr').default, Recorder = require('./recorder'), logUtil = require('./log'), util = require('./util').default, diff --git a/lib/requestHandler/UserReqHandler.ts b/lib/requestHandler/UserReqHandler.ts index d1be4e0..5f589d4 100644 --- a/lib/requestHandler/UserReqHandler.ts +++ b/lib/requestHandler/UserReqHandler.ts @@ -1,6 +1,6 @@ /// -declare interface ErrorResponse { +declare interface IErrorResponse { statusCode: number; header: OneLevelObjectType; body: string; @@ -201,7 +201,7 @@ function fetchRemoteResponse( /* * get error response for exception scenarios */ -function getErrorResponse(error: NodeJS.ErrnoException, fullUrl: string): ErrorResponse { +function getErrorResponse(error: NodeJS.ErrnoException, fullUrl: string): IErrorResponse { // default error response const errorResponse = { statusCode: 500, diff --git a/lib/webInterface.js b/lib/webInterface.js index 003da6d..80fd15c 100644 --- a/lib/webInterface.js +++ b/lib/webInterface.js @@ -10,7 +10,7 @@ const express = require('express'), events = require('events'), qrCode = require('qrcode-npm'), util = require('./util').default, - certMgr = require('./certMgr'), + certMgr = require('./certMgr').default, wsServer = require('./wsServer'), juicer = require('juicer'), ip = require('ip'), diff --git a/test/server/server.js b/test/server/server.js index fcab17f..b7bab33 100644 --- a/test/server/server.js +++ b/test/server/server.js @@ -4,7 +4,7 @@ const koaBody = require('koa-body'); const send = require('koa-send'); const path = require('path'); const https = require('https'); -const certMgr = require('../../lib/certMgr'); +const certMgr = require('../../lib/certMgr').default; const fs = require('fs'); const nurl = require('url'); const color = require('colorful'); diff --git a/typings/index.d.ts b/typings/index.d.ts index 12a6a93..abbcd08 100644 --- a/typings/index.d.ts +++ b/typings/index.d.ts @@ -78,4 +78,8 @@ declare interface AnyProxyReponseDetail { declare interface OneLevelObjectType { [key: string]: string | boolean | number +} + +declare interface IExecScriptResult { + status: number; } \ No newline at end of file