anyproxy/lib/util.ts

373 lines
8.9 KiB
TypeScript
Raw Normal View History

2018-07-10 22:01:06 +08:00
'use strict';
// (function (): void {
// const fs = require('fs'),
// path = require('path'),
// mime = require('mime-types'),
// color = require('colorful'),
// crypto = require('crypto'),
// Buffer = require('buffer').Buffer,
// logUtil = require('./log');
import * as fs from 'fs';
import * as path from 'path';
import * as mime from 'mime-types';
import * as color from 'colorful';
import { Buffer } from 'buffer';
import { execSync } from 'child_process';
2018-08-24 16:03:38 +08:00
import logUtil from './log';
2018-07-10 22:01:06 +08:00
2018-08-28 11:59:43 +08:00
const networkInterfaces = require('os').networkInterfaces();
2018-07-10 22:01:06 +08:00
2018-08-28 11:59:43 +08:00
// {"Content-Encoding":"gzip"} --> {"content-encoding":"gzip"}
function lower_keys (obj: object): object {
for (const key in obj) {
const val = obj[key];
delete obj[key];
2018-07-10 22:01:06 +08:00
2018-08-28 11:59:43 +08:00
obj[key.toLowerCase()] = val;
}
2018-07-10 22:01:06 +08:00
2018-08-28 11:59:43 +08:00
return obj;
};
2018-07-10 22:01:06 +08:00
2018-08-28 11:59:43 +08:00
function merge (baseObj: object, extendObj: object): object {
for (const key in extendObj) {
baseObj[key] = extendObj[key];
}
2018-07-10 22:01:06 +08:00
2018-08-28 11:59:43 +08:00
return baseObj;
};
2018-07-10 22:01:06 +08:00
2018-08-28 11:59:43 +08:00
function getUserHome(): string {
return process.env.HOME || process.env.USERPROFILE;
}
2018-07-10 22:01:06 +08:00
2018-08-28 11:59:43 +08:00
function getAnyProxyHome(): string {
const home = path.join(getUserHome(), '/.anyproxy/');
if (!fs.existsSync(home)) {
fs.mkdirSync(home);
2018-07-10 22:01:06 +08:00
}
2018-08-28 11:59:43 +08:00
return home;
}
2018-07-10 22:01:06 +08:00
2018-08-28 11:59:43 +08:00
function getAnyProxyPath (pathName): string {
const home = getAnyProxyHome();
const targetPath = path.join(home, pathName);
if (!fs.existsSync(targetPath)) {
fs.mkdirSync(targetPath);
2018-07-10 22:01:06 +08:00
}
2018-08-28 11:59:43 +08:00
return targetPath;
}
2018-07-10 22:01:06 +08:00
2018-08-28 11:59:43 +08:00
/**
* render替换
*/
function simpleRender (str: string, object: object, regexp: RegExp) {
return String(str).replace(regexp || (/\{\{([^{}]+)\}\}/g), (match, name) => {
if (match.charAt(0) === '\\') {
return match.slice(1);
}
return (object[name] != null) ? object[name] : '';
});
};
/**
*
*/
function filewalker (root: string, cb: Function) {
root = root || process.cwd();
const ret = {
directory: [],
file: []
2018-07-10 22:01:06 +08:00
};
2018-08-28 11:59:43 +08:00
fs.readdir(root, (err, list) => {
if (list && list.length) {
list.map((item) => {
const fullPath = path.join(root, item),
stat = fs.lstatSync(fullPath);
if (stat.isFile()) {
ret.file.push({
name: item,
fullPath
});
} else if (stat.isDirectory()) {
ret.directory.push({
name: item,
fullPath
});
}
});
}
2018-07-10 22:01:06 +08:00
2018-08-28 11:59:43 +08:00
cb && cb.apply(null, [null, ret]);
});
};
/*
* content-type以及content-length等信息
* useLocalResponse的时候会使用到
*/
function contentType (filepath: string): string {
return mime.contentType(path.extname(filepath)) || '';
};
/*
* file的大小byte为单位
*/
function contentLength (filepath: string): number {
try {
const stat = fs.statSync(filepath);
return stat.size;
} catch (e) {
logUtil.printLog(color.red('\nfailed to ready local file : ' + filepath));
logUtil.printLog(color.red(e));
return 0;
}
};
/*
* remove the cache before requiring, the path SHOULD BE RELATIVE TO UTIL.JS
*/
function freshRequire (modulePath: string): NodeModule {
delete require.cache[require.resolve(modulePath)];
return require(modulePath);
};
/*
* format the date string
* @param date Date or timestamp
* @param formatter YYYYMMDDHHmmss
*/
function formatDate (date: Date | number, formatter: string): string {
let finalDate : Date;
if (typeof date !== 'object') {
finalDate = new Date(date);
} else {
finalDate = date;
}
const transform = function (value) {
return value < 10 ? '0' + value : value;
2018-07-10 22:01:06 +08:00
};
2018-08-28 11:59:43 +08:00
return formatter.replace(/^YYYY|MM|DD|hh|mm|ss/g, (match) => {
switch (match) {
case 'YYYY':
return transform(finalDate.getFullYear());
case 'MM':
return transform(finalDate.getMonth() + 1);
case 'mm':
return transform(finalDate.getMinutes());
case 'DD':
return transform(finalDate.getDate());
case 'hh':
return transform(finalDate.getHours());
case 'ss':
return transform(finalDate.getSeconds());
default:
return ''
}
});
};
2018-07-10 22:01:06 +08:00
2018-08-28 11:59:43 +08:00
/**
* get headers(Object) from rawHeaders(Array)
* @param rawHeaders [key, value, key2, value2, ...]
2018-07-10 22:01:06 +08:00
2018-08-28 11:59:43 +08:00
*/
2018-07-10 22:01:06 +08:00
2018-08-28 11:59:43 +08:00
function getHeaderFromRawHeaders (rawHeaders: Array<string>) {
const headerObj = {};
const _handleSetCookieHeader = function (key, value) {
if (headerObj[key].constructor === Array) {
headerObj[key].push(value);
2018-07-10 22:01:06 +08:00
} else {
2018-08-28 11:59:43 +08:00
headerObj[key] = [headerObj[key], value];
2018-07-10 22:01:06 +08:00
}
};
2018-08-28 11:59:43 +08:00
if (!!rawHeaders) {
for (let i = 0; i < rawHeaders.length; i += 2) {
const key = rawHeaders[i];
let value = rawHeaders[i + 1];
2018-07-10 22:01:06 +08:00
2018-08-28 11:59:43 +08:00
if (typeof value === 'string') {
value = value.replace(/\0+$/g, ''); // 去除 \u0000的null字符串
2018-07-10 22:01:06 +08:00
}
2018-08-28 11:59:43 +08:00
if (!headerObj[key]) {
headerObj[key] = value;
} else {
// 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
if (key.toLowerCase() === 'set-cookie') {
_handleSetCookieHeader(key, value);
2018-07-10 22:01:06 +08:00
} else {
2018-08-28 11:59:43 +08:00
headerObj[key] = headerObj[key] + ',' + value;
2018-07-10 22:01:06 +08:00
}
}
}
2018-08-28 11:59:43 +08:00
}
return headerObj;
};
2018-07-10 22:01:06 +08:00
2018-08-28 11:59:43 +08:00
function getAllIpAddress (): Array<string> {
const allIp = [];
2018-07-10 22:01:06 +08:00
2018-08-28 11:59:43 +08:00
Object.keys(networkInterfaces).map((nic) => {
networkInterfaces[nic].filter((detail) => {
if (detail.family.toLowerCase() === 'ipv4') {
allIp.push(detail.address);
}
2018-07-10 22:01:06 +08:00
});
2018-08-28 11:59:43 +08:00
});
2018-07-10 22:01:06 +08:00
2018-08-28 11:59:43 +08:00
return allIp.length ? allIp : ['127.0.0.1'];
};
2018-07-10 22:01:06 +08:00
2018-08-28 11:59:43 +08:00
function deleteFolderContentsRecursive(dirPath: string, ifClearFolderItself: boolean): void {
if (!dirPath.trim() || dirPath === '/') {
throw new Error('can_not_delete_this_dir');
}
2018-07-10 22:01:06 +08:00
2018-08-28 11:59:43 +08:00
if (fs.existsSync(dirPath)) {
fs.readdirSync(dirPath).forEach((file) => {
const curPath = path.join(dirPath, file);
if (fs.lstatSync(curPath).isDirectory()) {
deleteFolderContentsRecursive(curPath, true);
} else { // delete all files
fs.unlinkSync(curPath);
}
});
2018-07-10 22:01:06 +08:00
2018-08-28 11:59:43 +08:00
if (ifClearFolderItself) {
try {
// ref: https://github.com/shelljs/shelljs/issues/49
const start = Date.now();
while (true) {
try {
fs.rmdirSync(dirPath);
break;
} catch (er) {
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
if (Date.now() - start > 1000) throw er;
} else if (er.code === 'ENOENT') {
2018-07-10 22:01:06 +08:00
break;
2018-08-28 11:59:43 +08:00
} else {
throw er;
2018-07-10 22:01:06 +08:00
}
}
}
2018-08-28 11:59:43 +08:00
} catch (e) {
throw new Error('could not remove directory (code ' + e.code + '): ' + dirPath);
2018-07-10 22:01:06 +08:00
}
}
}
2018-08-28 11:59:43 +08:00
}
2018-07-10 22:01:06 +08:00
2018-08-28 11:59:43 +08:00
function getFreePort (): Promise<number> {
return new Promise((resolve, reject) => {
const server = require('net').createServer();
server.unref();
server.on('error', reject);
server.listen(0, () => {
const port = server.address().port;
server.close(() => {
resolve(port);
2018-07-10 22:01:06 +08:00
});
});
2018-08-28 11:59:43 +08:00
});
}
2018-07-10 22:01:06 +08:00
2018-08-28 11:59:43 +08:00
function collectErrorLog (error: any): string {
if (error && error.code && error.toString()) {
return error.toString();
} else {
let result = [error, error.stack].join('\n');
try {
const errorString = error.toString();
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';
}
} catch (e) {}
return result
2018-07-10 22:01:06 +08:00
}
2018-08-28 11:59:43 +08:00
}
2018-07-10 22:01:06 +08:00
2018-08-28 11:59:43 +08:00
function isFunc (source: object): boolean {
return source && Object.prototype.toString.call(source) === '[object Function]';
};
/**
* @param {object} content
* @returns the size of the content
*/
function getByteSize (content: Buffer): number {
return Buffer.byteLength(content);
};
/*
* identify whether the
*/
function isIpDomain (domain: string): boolean {
if (!domain) {
return false;
}
const ipReg = /^\d+?\.\d+?\.\d+?\.\d+?$/;
return ipReg.test(domain);
};
function execScriptSync (cmd: string): object {
let stdout,
status = 0;
try {
stdout = execSync(cmd);
} catch (err) {
stdout = err.stdout;
status = err.status;
}
2018-08-28 11:59:43 +08:00
return {
stdout: stdout.toString(),
status
};
2018-08-28 11:59:43 +08:00
};
2018-08-28 11:59:43 +08:00
function guideToHomePage (): void {
logUtil.info('Refer to http://anyproxy.io for more detail');
};
2018-07-10 22:01:06 +08:00
const Util = {
lower_keys,
merge,
getUserHome,
contentType,
getAnyProxyPath,
getAnyProxyHome,
simpleRender,
filewalker,
contentLength,
freshRequire,
getHeaderFromRawHeaders,
getAllIpAddress,
getFreePort,
collectErrorLog,
isFunc,
isIpDomain,
getByteSize,
deleteFolderContentsRecursive,
execScriptSync,
guideToHomePage,
2018-08-28 11:59:43 +08:00
formatDate,
};
2018-07-10 22:01:06 +08:00
export default Util;
2018-08-28 11:59:43 +08:00