feat: refact wsServer, wsServerMgr, ruleLoader, systemProxyMgr into TS

This commit is contained in:
砚然 2018-08-29 14:51:36 +08:00
parent 4497160284
commit bcb7451579
13 changed files with 199 additions and 155 deletions

View File

@ -3,7 +3,7 @@
*/ */
const ruleLoader = require('../dist/ruleLoader'); const ruleLoader = require('../dist/ruleLoader');
const logUtil = require('../dist/log'); const logUtil = require('../dist/log').default;
const AnyProxy = require('../dist/proxy'); const AnyProxy = require('../dist/proxy');
module.exports = function startServer(program) { module.exports = function startServer(program) {

View File

@ -9,11 +9,10 @@ import * as color from 'colorful';
import * as WebSocket from 'ws'; import * as WebSocket from 'ws';
import * as constants from 'constants'; import * as constants from 'constants';
import * as AsyncTask from 'async-task-mgr'; import * as AsyncTask from 'async-task-mgr';
import Recorder from './recorder';
import certMgr from './certMgr'; import certMgr from './certMgr';
import logUtil from './log'; import logUtil from './log';
import util from './util'; import util from './util';
import * as wsServerMgr from './wsServerMgr'; import wsServerMgr from './wsServerMgr';
import * as co from 'co'; import * as co from 'co';
// // manage https servers // // manage https servers
@ -31,8 +30,7 @@ import * as co from 'co';
// asyncTask = require('async-task-mgr'); // asyncTask = require('async-task-mgr');
declare type THttpsRequestHanlder = (req: http.IncomingMessage, userRes: http.ServerResponse) => void; declare type THttpsRequestHanlder = (req: http.IncomingMessage, userRes: http.ServerResponse) => void;
declare type TWsRequestHandler = declare type TWsRequestHandler = (wsClient: WebSocket, wsReq: http.IncomingMessage) => void;
(userRule: AnyProxyRule, recorder: Recorder, wsClient: WebSocket, wsReq: http.IncomingMessage) => void;
const createSecureContext = tls.createSecureContext || (crypto as any).createSecureContext; const createSecureContext = tls.createSecureContext || (crypto as any).createSecureContext;
// using sni to avoid multiple ports // using sni to avoid multiple ports

View File

@ -1,5 +1,5 @@
'use strict'; 'use strict';
/*tslint:disable:no-console */
import * as color from 'colorful'; import * as color from 'colorful';
import util from './util'; import util from './util';
@ -12,7 +12,7 @@ enum LogLevelMap {
rule_error = 2, rule_error = 2,
warn = 3, warn = 3,
debug = 4, debug = 4,
}; }
function setPrintStatus(status: boolean): void { function setPrintStatus(status: boolean): void {
ifPrint = !!status; ifPrint = !!status;
@ -22,7 +22,7 @@ function setLogLevel(level: string): void {
logLevel = parseInt(level, 10); logLevel = parseInt(level, 10);
} }
function printLog(content: string, type?: LogLevelMap) { function printLog(content: string, type?: LogLevelMap): void {
if (!ifPrint) { if (!ifPrint) {
return; return;
} }
@ -75,35 +75,34 @@ function printLog(content: string, type?: LogLevelMap) {
} }
} }
module.exports.printLog = printLog; function debug(content: string): void {
function debug (content): void {
printLog(content, LogLevelMap.debug); printLog(content, LogLevelMap.debug);
}; }
function info (content): void { function info(content: string): void {
printLog(content, LogLevelMap.tip); printLog(content, LogLevelMap.tip);
}; }
function warn (content) { function warn(content: string): void {
printLog(content, LogLevelMap.warn); printLog(content, LogLevelMap.warn);
}; }
function error (content) { function error(content: string): void {
printLog(content, LogLevelMap.system_error); printLog(content, LogLevelMap.system_error);
}; }
function ruleError (content) { function ruleError(content: string): void {
printLog(content, LogLevelMap.rule_error); printLog(content, LogLevelMap.rule_error);
}; }
module.exports.setPrintStatus = setPrintStatus; // module.exports.setPrintStatus = setPrintStatus;
module.exports.setLogLevel = setLogLevel; // module.exports.setLogLevel = setLogLevel;
module.exports.T_TIP = LogLevelMap.tip; // module.exports.T_TIP = LogLevelMap.tip;
module.exports.T_ERR = LogLevelMap.system_error; // module.exports.T_ERR = LogLevelMap.system_error;
module.exports.T_RULE_ERROR = LogLevelMap.rule_error; // module.exports.T_RULE_ERROR = LogLevelMap.rule_error;
module.exports.T_WARN = LogLevelMap.warn; // module.exports.T_WARN = LogLevelMap.warn;
module.exports.T_DEBUG = LogLevelMap.debug; // module.exports.T_DEBUG = LogLevelMap.debug;
// module.exports.printLog = printLog;
const LogUtil = { const LogUtil = {
setPrintStatus, setPrintStatus,
@ -121,5 +120,4 @@ const LogUtil = {
T_DEBUG: LogLevelMap.debug, T_DEBUG: LogLevelMap.debug,
}; };
exports.LogUtil = LogUtil;
export default LogUtil; export default LogUtil;

View File

@ -6,12 +6,12 @@ const http = require('http'),
color = require('colorful'), color = require('colorful'),
certMgr = require('./certMgr').default, certMgr = require('./certMgr').default,
Recorder = require('./recorder').default, Recorder = require('./recorder').default,
logUtil = require('./log'), logUtil = require('./log').default,
util = require('./util').default, util = require('./util').default,
events = require('events'), events = require('events'),
co = require('co'), co = require('co'),
WebInterface = require('./webInterface'), WebInterface = require('./webInterface'),
wsServerMgr = require('./wsServerMgr'), wsServerMgr = require('./wsServerMgr').default,
ThrottleGroup = require('stream-throttle').ThrottleGroup; ThrottleGroup = require('stream-throttle').ThrottleGroup;
// const memwatch = require('memwatch-next'); // const memwatch = require('memwatch-next');

View File

@ -414,7 +414,7 @@ class RequestHandler {
public wsIntercept: boolean; public wsIntercept: boolean;
public connectReqHandler: () => void; public connectReqHandler: () => void;
private userRequestHandler: () => void; private userRequestHandler: () => void;
private wsHandler: () => void; private 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

@ -1,9 +1,9 @@
'use strict'; 'use strict';
const proxyUtil = require('./util').default; import proxyUtil from './util';
const path = require('path'); import * as path from 'path';
const fs = require('fs'); import * as fs from 'fs';
const request = require('request'); import * as request from 'request';
const cachePath = proxyUtil.getAnyProxyPath('cache'); const cachePath = proxyUtil.getAnyProxyPath('cache');
@ -13,7 +13,7 @@ const cachePath = proxyUtil.getAnyProxyPath('cache');
* @param {any} url * @param {any} url
* @returns {string} cachePath * @returns {string} cachePath
*/ */
function cacheRemoteFile(url) { function cacheRemoteFile(url: string): Promise<string> {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
request(url, (error, response, body) => { request(url, (error, response, body) => {
if (error) { if (error) {
@ -39,7 +39,7 @@ function cacheRemoteFile(url) {
* @param {any} filePath * @param {any} filePath
* @returns module * @returns module
*/ */
function loadLocalPath(filePath) { function loadLocalPath(filePath: string): Promise<NodeJS.Module> {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const ruleFilePath = path.resolve(process.cwd(), filePath); const ruleFilePath = path.resolve(process.cwd(), filePath);
if (fs.existsSync(ruleFilePath)) { if (fs.existsSync(ruleFilePath)) {
@ -57,14 +57,14 @@ function loadLocalPath(filePath) {
* @param {any} urlOrPath * @param {any} urlOrPath
* @returns module * @returns module
*/ */
function requireModule(urlOrPath) { function requireModule(urlOrPath: string): Promise<NodeJS.Module> {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
if (/^http/i.test(urlOrPath)) { if (/^http/i.test(urlOrPath)) {
resolve(cacheRemoteFile(urlOrPath)); resolve(cacheRemoteFile(urlOrPath));
} else { } else {
resolve(urlOrPath); resolve(urlOrPath);
} }
}).then(localPath => loadLocalPath(localPath)); }).then((localPath: string) => loadLocalPath(localPath));
} }
module.exports = { module.exports = {

View File

@ -17,7 +17,7 @@ module.exports = {
* @param {buffer} requestDetail.response.body * @param {buffer} requestDetail.response.body
* @returns * @returns
*/ */
*beforeSendRequest(requestDetail) { *beforeSendRequest(requestDetail: AnyProxyRequestDetail): Generator {
return null; return null;
}, },
@ -28,7 +28,7 @@ module.exports = {
* @param {object} requestDetail * @param {object} requestDetail
* @param {object} responseDetail * @param {object} responseDetail
*/ */
*beforeSendResponse(requestDetail, responseDetail) { *beforeSendResponse(requestDetail: AnyProxyRequestDetail, responseDetail: AnyProxyReponseDetail): Generator {
return null; return null;
}, },
@ -40,7 +40,7 @@ module.exports = {
* @param {any} requestDetail * @param {any} requestDetail
* @returns * @returns
*/ */
*beforeDealHttpsRequest(requestDetail) { *beforeDealHttpsRequest(requestDetail: AnyProxyRequestDetail): Generator {
return null; return null;
}, },
@ -51,7 +51,7 @@ module.exports = {
* @param {any} error * @param {any} error
* @returns * @returns
*/ */
*onError(requestDetail, error) { *onError(requestDetail: AnyProxyRequestDetail, error: NodeJS.ErrnoException): Generator {
return null; return null;
}, },
@ -63,7 +63,7 @@ module.exports = {
* @param {any} error * @param {any} error
* @returns * @returns
*/ */
*onConnectError(requestDetail, error) { *onConnectError(requestDetail: AnyProxyRequestDetail, error: NodeJS.ErrnoException): Generator {
return null; return null;
}, },
}; };

View File

@ -1,12 +1,21 @@
'use strict' 'use strict';
/* tslint:disable:max-line-length */
import * as child_process from 'child_process';
import logUtil from './log';
const child_process = require('child_process'); declare interface IProxyManager {
networkType?: string;
getNetworkType?: () => string;
enableGlobalProxy?: (ip: string, port: string, proxyType: 'http' | 'https') => void;
disableGlobalProxy?: (proxyType: 'http' | 'https') => void;
getProxyState?: () => IExecScriptResult;
}
const networkTypes = ['Ethernet', 'Thunderbolt Ethernet', 'Wi-Fi']; const networkTypes = ['Ethernet', 'Thunderbolt Ethernet', 'Wi-Fi'];
function execSync(cmd) { function execSync(cmd: string): IExecScriptResult {
let stdout, let stdout;
status = 0; let status = 0;
try { try {
stdout = child_process.execSync(cmd); stdout = child_process.execSync(cmd);
} catch (err) { } catch (err) {
@ -16,7 +25,7 @@ function execSync(cmd) {
return { return {
stdout: stdout.toString(), stdout: stdout.toString(),
status status,
}; };
} }
@ -51,13 +60,12 @@ function execSync(cmd) {
* ------------------------------------------------------------------------ * ------------------------------------------------------------------------
*/ */
const macProxyManager = {}; const macProxyManager: IProxyManager = {
};
macProxyManager.getNetworkType = () => {
for (let i = 0; i < networkTypes.length; i++) {
const type = networkTypes[i],
result = execSync('networksetup -getwebproxy ' + type);
macProxyManager.getNetworkType = function(): string {
for (const type of networkTypes) {
const result = execSync('networksetup -getwebproxy ' + type);
if (result.status === 0) { if (result.status === 0) {
macProxyManager.networkType = type; macProxyManager.networkType = type;
return type; return type;
@ -70,7 +78,7 @@ macProxyManager.getNetworkType = () => {
macProxyManager.enableGlobalProxy = (ip, port, proxyType) => { macProxyManager.enableGlobalProxy = (ip, port, proxyType) => {
if (!ip || !port) { if (!ip || !port) {
console.log('failed to set global proxy server.\n ip and port are required.'); logUtil.warn('failed to set global proxy server.\n ip and port are required.');
return; return;
} }
@ -125,11 +133,11 @@ macProxyManager.getProxyState = () => {
* ------------------------------------------------------------------------ * ------------------------------------------------------------------------
*/ */
const winProxyManager = {}; const winProxyManager: IProxyManager = {};
winProxyManager.enableGlobalProxy = (ip, port) => { winProxyManager.enableGlobalProxy = (ip, port) => {
if (!ip && !port) { if (!ip && !port) {
console.log('failed to set global proxy server.\n ip and port are required.'); logUtil.warn('failed to set global proxy server.\n ip and port are required.');
return; return;
} }
@ -145,8 +153,12 @@ winProxyManager.enableGlobalProxy = (ip, port) => {
winProxyManager.disableGlobalProxy = () => execSync('reg add "HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings" /v ProxyEnable /t REG_DWORD /d 0 /f'); winProxyManager.disableGlobalProxy = () => execSync('reg add "HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings" /v ProxyEnable /t REG_DWORD /d 0 /f');
winProxyManager.getProxyState = () => '' winProxyManager.getProxyState = () => {
return {
status: -1,
};
};
winProxyManager.getNetworkType = () => '' winProxyManager.getNetworkType = () => '';
module.exports = /^win/.test(process.platform) ? winProxyManager : macProxyManager; module.exports = /^win/.test(process.platform) ? winProxyManager : macProxyManager;

View File

@ -15,12 +15,13 @@ import * as color from 'colorful';
import { Buffer } from 'buffer'; import { Buffer } from 'buffer';
import { execSync } from 'child_process'; import { execSync } from 'child_process';
import logUtil from './log'; import logUtil from './log';
import * as os from 'os';
import { IncomingHttpHeaders } from 'http';
const networkInterfaces = os.networkInterfaces();
const networkInterfaces = require('os').networkInterfaces();
// {"Content-Encoding":"gzip"} --> {"content-encoding":"gzip"} // {"Content-Encoding":"gzip"} --> {"content-encoding":"gzip"}
function lower_keys (obj: object): object { function lower_keys(obj: object): object {
for (const key in obj) { for (const key in obj) {
const val = obj[key]; const val = obj[key];
delete obj[key]; delete obj[key];
@ -29,15 +30,15 @@ function lower_keys (obj: object): object {
} }
return obj; return obj;
}; }
function merge (baseObj: object, extendObj: object): object { function merge(baseObj: object, extendObj: object): object {
for (const key in extendObj) { for (const key in extendObj) {
baseObj[key] = extendObj[key]; baseObj[key] = extendObj[key];
} }
return baseObj; return baseObj;
}; }
function getUserHome(): string { function getUserHome(): string {
return process.env.HOME || process.env.USERPROFILE; return process.env.HOME || process.env.USERPROFILE;
@ -51,7 +52,7 @@ function getAnyProxyHome(): string {
return home; return home;
} }
function getAnyProxyPath (pathName): string { function getAnyProxyPath(pathName: string): string {
const home = getAnyProxyHome(); const home = getAnyProxyHome();
const targetPath = path.join(home, pathName); const targetPath = path.join(home, pathName);
if (!fs.existsSync(targetPath)) { if (!fs.existsSync(targetPath)) {
@ -63,41 +64,41 @@ function getAnyProxyPath (pathName): string {
/** /**
* render替换 * render替换
*/ */
function simpleRender (str: string, object: object, regexp: RegExp) { function simpleRender(str: string, object: object, regexp: RegExp): string {
return String(str).replace(regexp || (/\{\{([^{}]+)\}\}/g), (match, name) => { return String(str).replace(regexp || (/\{\{([^{}]+)\}\}/g), (match, name) => {
if (match.charAt(0) === '\\') { if (match.charAt(0) === '\\') {
return match.slice(1); return match.slice(1);
} }
return (object[name] != null) ? object[name] : ''; return (object[name] != null) ? object[name] : '';
}); });
}; }
/** /**
* *
*/ */
function filewalker (root: string, cb: Function) { function filewalker(root: string, cb: (err: Error, result: any) => void): void {
root = root || process.cwd(); root = root || process.cwd();
const ret = { const ret = {
directory: [], directory: [],
file: [] file: [],
}; };
fs.readdir(root, (err, list) => { fs.readdir(root, (err, list) => {
if (list && list.length) { if (list && list.length) {
list.map((item) => { list.map((item) => {
const fullPath = path.join(root, item), const fullPath = path.join(root, item);
stat = fs.lstatSync(fullPath); const stat = fs.lstatSync(fullPath);
if (stat.isFile()) { if (stat.isFile()) {
ret.file.push({ ret.file.push({
name: item, name: item,
fullPath fullPath,
}); });
} else if (stat.isDirectory()) { } else if (stat.isDirectory()) {
ret.directory.push({ ret.directory.push({
name: item, name: item,
fullPath fullPath,
}); });
} }
}); });
@ -105,20 +106,20 @@ function filewalker (root: string, cb: Function) {
cb && cb.apply(null, [null, ret]); cb && cb.apply(null, [null, ret]);
}); });
}; }
/* /*
* content-type以及content-length等信息 * content-type以及content-length等信息
* useLocalResponse的时候会使用到 * useLocalResponse的时候会使用到
*/ */
function contentType (filepath: string): string { function contentType(filepath: string): string {
return mime.contentType(path.extname(filepath)) || ''; return mime.contentType(path.extname(filepath)) || '';
}; }
/* /*
* file的大小byte为单位 * file的大小byte为单位
*/ */
function contentLength (filepath: string): number { function contentLength(filepath: string): number {
try { try {
const stat = fs.statSync(filepath); const stat = fs.statSync(filepath);
return stat.size; return stat.size;
@ -127,30 +128,30 @@ function contentLength (filepath: string): number {
logUtil.printLog(color.red(e)); logUtil.printLog(color.red(e));
return 0; return 0;
} }
}; }
/* /*
* remove the cache before requiring, the path SHOULD BE RELATIVE TO UTIL.JS * remove the cache before requiring, the path SHOULD BE RELATIVE TO UTIL.JS
*/ */
function freshRequire (modulePath: string): NodeModule { function freshRequire(modulePath: string): NodeModule {
delete require.cache[require.resolve(modulePath)]; delete require.cache[require.resolve(modulePath)];
return require(modulePath); return require(modulePath);
}; }
/* /*
* format the date string * format the date string
* @param date Date or timestamp * @param date Date or timestamp
* @param formatter YYYYMMDDHHmmss * @param formatter YYYYMMDDHHmmss
*/ */
function formatDate (date: Date | number, formatter: string): string { function formatDate(date: Date | number, formatter: string): string {
let finalDate : Date; let finalDate: Date;
if (typeof date !== 'object') { if (typeof date !== 'object') {
finalDate = new Date(date); finalDate = new Date(date);
} else { } else {
finalDate = date; finalDate = date;
} }
const transform = function (value) { const transform = function(value: number): string {
return value < 10 ? '0' + value : value; return value < 10 ? '0' + value : '' + value;
}; };
return formatter.replace(/^YYYY|MM|DD|hh|mm|ss/g, (match) => { return formatter.replace(/^YYYY|MM|DD|hh|mm|ss/g, (match) => {
switch (match) { switch (match) {
@ -167,11 +168,10 @@ function formatDate (date: Date | number, formatter: string): string {
case 'ss': case 'ss':
return transform(finalDate.getSeconds()); return transform(finalDate.getSeconds());
default: default:
return '' return '';
} }
}); });
}; }
/** /**
* get headers(Object) from rawHeaders(Array) * get headers(Object) from rawHeaders(Array)
@ -179,9 +179,9 @@ function formatDate (date: Date | number, formatter: string): string {
*/ */
function getHeaderFromRawHeaders (rawHeaders: Array<string>) { function getHeaderFromRawHeaders(rawHeaders: string[]): IncomingHttpHeaders {
const headerObj = {}; const headerObj = {};
const _handleSetCookieHeader = function (key, value) { const handleSetCookieHeader = function(key: string, value: string): void {
if (headerObj[key].constructor === Array) { if (headerObj[key].constructor === Array) {
headerObj[key].push(value); headerObj[key].push(value);
} else { } else {
@ -204,7 +204,7 @@ function getHeaderFromRawHeaders (rawHeaders: Array<string>) {
// headers with same fields could be combined with comma. Ref: https://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.2 // headers with same fields could be combined with comma. Ref: https://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.2
// set-cookie should NOT be combined. Ref: https://tools.ietf.org/html/rfc6265 // set-cookie should NOT be combined. Ref: https://tools.ietf.org/html/rfc6265
if (key.toLowerCase() === 'set-cookie') { if (key.toLowerCase() === 'set-cookie') {
_handleSetCookieHeader(key, value); handleSetCookieHeader(key, value);
} else { } else {
headerObj[key] = headerObj[key] + ',' + value; headerObj[key] = headerObj[key] + ',' + value;
} }
@ -212,9 +212,9 @@ function getHeaderFromRawHeaders (rawHeaders: Array<string>) {
} }
} }
return headerObj; return headerObj;
}; }
function getAllIpAddress (): Array<string> { function getAllIpAddress(): string[] {
const allIp = []; const allIp = [];
Object.keys(networkInterfaces).map((nic) => { Object.keys(networkInterfaces).map((nic) => {
@ -226,7 +226,7 @@ function getAllIpAddress (): Array<string> {
}); });
return allIp.length ? allIp : ['127.0.0.1']; return allIp.length ? allIp : ['127.0.0.1'];
}; }
function deleteFolderContentsRecursive(dirPath: string, ifClearFolderItself: boolean): void { function deleteFolderContentsRecursive(dirPath: string, ifClearFolderItself: boolean): void {
if (!dirPath.trim() || dirPath === '/') { if (!dirPath.trim() || dirPath === '/') {
@ -254,7 +254,9 @@ function deleteFolderContentsRecursive(dirPath: string, ifClearFolderItself: boo
} catch (er) { } catch (er) {
if (process.platform === 'win32' && (er.code === 'ENOTEMPTY' || er.code === 'EBUSY' || er.code === 'EPERM')) { if (process.platform === 'win32' && (er.code === 'ENOTEMPTY' || er.code === 'EBUSY' || er.code === 'EPERM')) {
// Retry on windows, sometimes it takes a little time before all the files in the directory are gone // Retry on windows, sometimes it takes a little time before all the files in the directory are gone
if (Date.now() - start > 1000) throw er; if (Date.now() - start > 1000) {
throw er;
}
} else if (er.code === 'ENOENT') { } else if (er.code === 'ENOENT') {
break; break;
} else { } else {
@ -269,7 +271,7 @@ function deleteFolderContentsRecursive(dirPath: string, ifClearFolderItself: boo
} }
} }
function getFreePort (): Promise<number> { function getFreePort(): Promise<number> {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const server = require('net').createServer(); const server = require('net').createServer();
server.unref(); server.unref();
@ -283,7 +285,7 @@ function getFreePort (): Promise<number> {
}); });
} }
function collectErrorLog (error: any): string { function collectErrorLog(error: any): string {
if (error && error.code && error.toString()) { if (error && error.code && error.toString()) {
return error.toString(); return error.toString();
} else { } else {
@ -291,40 +293,41 @@ function collectErrorLog (error: any): string {
try { try {
const errorString = error.toString(); const errorString = error.toString();
if (errorString.indexOf('You may only yield a function') >= 0) { if (errorString.indexOf('You may only yield a function') >= 0) {
result = 'Function is not yieldable. Did you forget to provide a generator or promise in rule file ? \nFAQ http://anyproxy.io/4.x/#faq'; result = 'Function is not yieldable. Did you forget to provide a generator or promise in rule file ? '
+ '\nFAQ http://anyproxy.io/4.x/#faq';
} }
} catch (e) {} } catch (e) { logUtil.error(e.stack); }
return result return result;
} }
} }
function isFunc (source: object): boolean { function isFunc(source: object): boolean {
return source && Object.prototype.toString.call(source) === '[object Function]'; return source && Object.prototype.toString.call(source) === '[object Function]';
}; }
/** /**
* @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): number {
return Buffer.byteLength(content); return Buffer.byteLength(content);
}; }
/* /*
* identify whether the * identify whether the
*/ */
function isIpDomain (domain: string): boolean { function isIpDomain(domain: string): boolean {
if (!domain) { if (!domain) {
return false; return false;
} }
const ipReg = /^\d+?\.\d+?\.\d+?\.\d+?$/; const ipReg = /^\d+?\.\d+?\.\d+?\.\d+?$/;
return ipReg.test(domain); return ipReg.test(domain);
}; }
function execScriptSync (cmd: string): object { function execScriptSync(cmd: string): object {
let stdout, let stdout;
status = 0; let status = 0;
try { try {
stdout = execSync(cmd); stdout = execSync(cmd);
} catch (err) { } catch (err) {
@ -334,14 +337,13 @@ function execScriptSync (cmd: string): object {
return { return {
stdout: stdout.toString(), stdout: stdout.toString(),
status status,
}; };
}; }
function guideToHomePage (): void { function guideToHomePage(): void {
logUtil.info('Refer to http://anyproxy.io for more detail'); logUtil.info('Refer to http://anyproxy.io for more detail');
}; }
const Util = { const Util = {
lower_keys, lower_keys,

View File

@ -1,20 +1,43 @@
'use strict'; 'use strict';
//websocket server manager // websocket server manager
import * as WebSocket from 'ws';
import Recorder from './recorder';
import logUtil from './log';
import { Server } from 'http';
const WebSocketServer = require('ws').Server; declare interface IWsMessage {
const logUtil = require('./log'); type?: 'error' | 'body';
error?: string;
reqRef?: string;
content?: {
id: number;
body: string;
error?: string;
};
}
function resToMsg(msg, recorder, cb) { declare interface IWsServerConfig {
let result = {}, server: Server;
jsonData; }
declare interface IMultiMessageQueue {
type: 'updateMultiple' | 'updateLatestWsMsg';
content: AnyProxyRecorder.WsResourceInfo[] | AnyProxyRecorder.WsResourceInfo;
}
const WebSocketServer = WebSocket.Server;
function resToMsg(msg: string, recorder: Recorder, cb: (result: IWsMessage) => void): void {
let result: IWsMessage = {};
let jsonData;
try { try {
jsonData = JSON.parse(msg); jsonData = JSON.parse(msg);
} catch (e) { } catch (e) {
result = { result = {
type: 'error', type: 'error',
error: 'failed to parse your request : ' + e.toString() error: 'failed to parse your request : ' + e.toString(),
}; };
cb && cb(result); cb && cb(result);
return; return;
@ -31,12 +54,12 @@ function resToMsg(msg, recorder, cb) {
result.content = { result.content = {
id: null, id: null,
body: null, body: null,
error: err.toString() error: err.toString(),
}; };
} else { } else {
result.content = { result.content = {
id: jsonData.id, id: jsonData.id,
body: data.toString() body: data.toString(),
}; };
} }
cb && cb(result); cb && cb(result);
@ -46,10 +69,13 @@ function resToMsg(msg, recorder, cb) {
} }
} }
//config.server // config.server
class wsServer { class WsServer {
constructor(config, recorder) { private config: IWsServerConfig;
private recorder: Recorder;
private wss: WebSocket.Server;
constructor(config: IWsServerConfig, recorder: Recorder) {
if (!recorder) { if (!recorder) {
throw new Error('proxy recorder is required'); throw new Error('proxy recorder is required');
} else if (!config || !config.server) { } else if (!config || !config.server) {
@ -61,12 +87,12 @@ class wsServer {
self.recorder = recorder; self.recorder = recorder;
} }
start() { public start(): Promise<any> {
const self = this; const self = this;
const config = self.config; const config = self.config;
const recorder = self.recorder; const recorder = self.recorder;
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
//web socket interface // web socket interface
const wss = new WebSocketServer({ const wss = new WebSocketServer({
server: config.server, server: config.server,
clientTracking: true, clientTracking: true,
@ -83,12 +109,12 @@ class wsServer {
sendMultipleMessage(); sendMultipleMessage();
}, 50); }, 50);
function sendMultipleMessage(data) { function sendMultipleMessage(data?: AnyProxyRecorder.WsResourceInfo): void {
// if the flag goes to be true, and there are records to send // if the flag goes to be true, and there are records to send
if (broadcastFlag && messageQueue.length > 0) { if (broadcastFlag && messageQueue.length > 0) {
wss && wss.broadcast({ wss && broadcast({
type: 'updateMultiple', type: 'updateMultiple',
content: messageQueue content: messageQueue,
}); });
messageQueue = []; messageQueue = [];
broadcastFlag = false; broadcastFlag = false;
@ -97,7 +123,7 @@ class wsServer {
} }
} }
wss.broadcast = function (data) { function broadcast(data?: IMultiMessageQueue | string): void {
if (typeof data === 'object') { if (typeof data === 'object') {
try { try {
data = JSON.stringify(data); data = JSON.stringify(data);
@ -105,17 +131,17 @@ class wsServer {
console.error('==> errorr when do broadcast ', e, data); console.error('==> errorr when do broadcast ', e, data);
} }
} }
for (const client of wss.clients) { wss.clients.forEach(function(client: WebSocket): void {
try { try {
client.send(data); client.send(data);
} catch (e) { } catch (e) {
logUtil.printLog('websocket failed to send data, ' + e, logUtil.T_ERR); logUtil.printLog('websocket failed to send data, ' + e, logUtil.T_ERR);
} }
});
} }
};
wss.on('connection', (ws) => { wss.on('connection', (ws) => {
ws.on('message', (msg) => { ws.on('message', (msg: string) => {
resToMsg(msg, recorder, (res) => { resToMsg(msg, recorder, (res) => {
res && ws.send(JSON.stringify(res)); res && ws.send(JSON.stringify(res));
}); });
@ -130,28 +156,27 @@ class wsServer {
logUtil.printLog('websocket error, ' + e, logUtil.T_ERR); logUtil.printLog('websocket error, ' + e, logUtil.T_ERR);
}); });
wss.on('close', () => { }); wss.on('close', (err: Error) => { logUtil.error(err.stack); });
recorder.on('update', (data) => { recorder.on('update', (data) => {
try { try {
sendMultipleMessage(data); sendMultipleMessage(data);
} catch (e) { } catch (e) {
console.log('ws error'); logUtil.error('ws error');
console.log(e); logUtil.error(e.stack);
} }
}); });
recorder.on('updateLatestWsMsg', (data) => { recorder.on('updateLatestWsMsg', (data) => {
try { try {
// console.info('==> update latestMsg ', data); // console.info('==> update latestMsg ', data);
wss && wss.broadcast({ wss && broadcast({
type: 'updateLatestWsMsg', type: 'updateLatestWsMsg',
content: data content: data,
}); });
} catch (e) { } catch (e) {
logUtil.error(e.message); logUtil.error(e.message);
logUtil.error(e.stack); logUtil.error(e.stack);
console.error(e);
} }
}); });
@ -159,7 +184,7 @@ class wsServer {
}); });
} }
closeAll() { public closeAll(): Promise<any> {
const self = this; const self = this;
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
self.wss.close((e) => { self.wss.close((e) => {
@ -173,4 +198,4 @@ class wsServer {
} }
} }
module.exports = wsServer; module.exports = WsServer;

View File

@ -2,8 +2,9 @@
* manage the websocket server * manage the websocket server
* *
*/ */
const ws = require('ws'); import * as ws from 'ws';
const logUtil = require('./log.js'); import * as http from 'http';
import logUtil from './log.js';
const WsServer = ws.Server; const WsServer = ws.Server;
@ -13,9 +14,12 @@ const WsServer = ws.Server;
{string} config.server {string} config.server
{handler} config.handler {handler} config.handler
*/ */
function getWsServer(config) { function getWsServer(config: {
server: http.Server;
connHandler: (wsClient: ws, wsReq: http.IncomingMessage) => void;
}): ws.Server {
const wss = new WsServer({ const wss = new WsServer({
server: config.server server: config.server,
}); });
wss.on('connection', config.connHandler); wss.on('connection', config.connHandler);
@ -24,16 +28,19 @@ function getWsServer(config) {
headers.push('x-anyproxy-websocket:true'); headers.push('x-anyproxy-websocket:true');
}); });
wss.on('error', e => { wss.on('error', (e) => {
logUtil.error(`error in websocket proxy: ${e.message},\r\n ${e.stack}`); logUtil.error(`error in websocket proxy: ${e.message},\r\n ${e.stack}`);
console.error('error happened in proxy websocket:', e) console.error('error happened in proxy websocket:', e);
}); });
wss.on('close', e => { wss.on('close', (e) => {
console.error('==> closing the ws server'); console.error('==> closing the ws server');
}); });
return wss; return wss;
} }
module.exports.getWsServer = getWsServer; export default {
getWsServer,
};

View File

@ -13,6 +13,7 @@
"ordered-imports": false, "ordered-imports": false,
"only-arrow-functions": false, "only-arrow-functions": false,
"no-reference": false, "no-reference": false,
"forin": false,
"typedef": [ "typedef": [
true, true,
"call-signature", "call-signature",

1
typings/index.d.ts vendored
View File

@ -85,4 +85,5 @@ declare interface OneLevelObjectType {
declare interface IExecScriptResult { declare interface IExecScriptResult {
status: number; status: number;
stdout?: string;
} }