core,utils: move core/dns-cache.js to utils/, and refactor it

This commit is contained in:
Micooz 2018-02-05 14:23:26 +08:00
parent b66e14d1cb
commit b785e20612
9 changed files with 111 additions and 128 deletions

@ -1,55 +0,0 @@
import net from 'net';
import {DNSCache} from '../dns-cache';
describe('DNSCache#get', function () {
it('should return an ip address', async function () {
const dns = new DNSCache();
dns.clear();
expect(net.isIP(await dns.get('localhost'))).toBe(4);
expect(net.isIP(await dns.get('localhost'))).toBe(4);
expect(net.isIP(await dns.get('127.0.0.1'))).toBe(4);
});
it('should throw if fail to resolve', async function () {
const dns = new DNSCache();
dns.clear();
try {
await dns.get('xxx');
} catch (err) {
expect(err.message).toEqual('getaddrinfo ENOTFOUND xxx');
}
});
it('should remove from poll if expire', async function () {
const dns = new DNSCache({expire: 1e3});
dns.clear();
await dns.get('localhost');
dns._now = () => Date.now() + 1e3;
await dns.get('localhost');
expect(DNSCache._pool).toEqual({});
});
});
describe('DNSCache#_now', function () {
it('should return a timestamp', function () {
const dns = new DNSCache();
dns.clear();
const now = Date.now();
expect(dns._now()).toBeGreaterThanOrEqual(now);
});
});
describe('DNSCache#_put', function () {
it('poll should be empty', function () {
const dns = new DNSCache({expire: 0});
dns.clear();
dns._put('', '');
expect(DNSCache._pool).toEqual({});
});
});

@ -8,8 +8,7 @@ import qs from 'qs';
import winston from 'winston';
import isPlainObject from 'lodash.isplainobject';
import {getPresetClassByName, IPresetAddressing} from '../presets';
import {isValidHostname, isValidPort, logger} from '../utils';
import {DNS_DEFAULT_EXPIRE} from './dns-cache';
import {DNSCache, isValidHostname, isValidPort, logger, DNS_DEFAULT_EXPIRE} from '../utils';
function loadFileSync(file) {
return fs.readFileSync(path.resolve(process.cwd(), file));
@ -55,6 +54,9 @@ export class Config {
global.__DNS__ = json.dns;
dns.setServers(json.dns);
}
// dns-cache
DNSCache.init(__DNS_EXPIRE__);
}
static initServer(server) {

@ -1,66 +0,0 @@
import dns from 'dns';
import net from 'net';
import {logger} from '../utils';
export const DNS_DEFAULT_EXPIRE = 3600000;
export class DNSCache {
static _pool = {};
_expire = DNS_DEFAULT_EXPIRE;
constructor({expire} = {}) {
if (expire !== undefined) {
this._expire = expire;
}
}
_now() {
return Date.now();
}
async _lookup(hostname) {
return new Promise((resolve, reject) => {
dns.lookup(hostname, function (err, address) {
if (err) {
reject(err);
} else {
resolve(address);
}
});
});
}
_put(hostname, address) {
if (this._expire > 0) {
const expire = this._now() + this._expire;
DNSCache._pool[hostname] = [address, expire];
}
}
async get(hostname) {
if (net.isIP(hostname)) {
return hostname;
}
let address = null;
if (DNSCache._pool[hostname] === undefined) {
address = await this._lookup(hostname);
this._put(hostname, address);
} else {
const [addr, expire] = DNSCache._pool[hostname];
const now = this._now();
if (now >= expire) {
delete DNSCache._pool[hostname];
}
logger.verbose(`[dns-cache] hit: hostname=${hostname} resolved=${addr} ttl=${expire - now}ms`);
address = addr;
}
return address;
}
clear() {
DNSCache._pool = {};
}
}

@ -1,6 +1,5 @@
export * from './balancer';
export * from './config';
export * from './dns-cache';
export * from './hub';
export * from './pipe';
export * from './middleware';

@ -1,6 +1,5 @@
import EventEmitter from 'events';
import {Pipe} from './pipe';
import {DNSCache} from './dns-cache';
import {PIPE_ENCODE, PIPE_DECODE} from '../constants';
import {logger} from '../utils';
@ -78,7 +77,6 @@ export class Relay extends EventEmitter {
this._pipe = this.createPipe(this._presets);
this._ctx = {
pipe: this._pipe,
dnsCache: new DNSCache({expire: __DNS_EXPIRE__}),
thisRelay: this,
...context,
};

@ -1,7 +1,7 @@
import net from 'net';
import {Inbound, Outbound} from './defs';
import {MAX_BUFFERED_SIZE, PIPE_ENCODE, PIPE_DECODE} from '../constants';
import {logger, getRandomInt} from '../utils';
import {DNSCache, logger, getRandomInt} from '../utils';
import {
CONNECT_TO_REMOTE,
CONNECTED_TO_REMOTE,
@ -369,7 +369,7 @@ export class TcpOutbound extends Outbound {
}
async _connect({host, port}) {
const ip = await this.ctx.dnsCache.get(host);
const ip = await DNSCache.get(host);
logger.info(`[${this.name}] [${this.remote}] connecting to tcp://${host}:${port} resolve=${ip}`);
return net.connect({host: ip, port});
}

@ -0,0 +1,35 @@
import net from 'net';
import {DNSCache} from '../dns-cache';
async function sleep(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
it('should return an ip address', async function () {
DNSCache.clear();
expect(net.isIP(await DNSCache.get('localhost'))).toBe(4);
expect(net.isIP(await DNSCache.get('localhost'))).toBe(4);
expect(net.isIP(await DNSCache.get('127.0.0.1'))).toBe(4);
});
it('should throw if fail to resolve', async function () {
try {
await DNSCache.get('xxx');
} catch (err) {
expect(err.message).toEqual('getaddrinfo ENOTFOUND xxx');
}
});
it('should remove from pool if expire', async function () {
DNSCache.init(100);
await DNSCache.get('localhost');
await sleep(100);
await DNSCache.get('localhost');
expect(DNSCache.pool).toEqual({});
});
it('pool should be empty', function () {
DNSCache.init(0);
DNSCache._put('', '');
expect(DNSCache.pool).toEqual({});
});

69
src/utils/dns-cache.js Normal file

@ -0,0 +1,69 @@
import dns from 'dns';
import net from 'net';
import {logger} from './logger';
async function lookup(hostname) {
return new Promise((resolve, reject) => {
dns.lookup(hostname, function (err, address) {
if (err) {
reject(err);
} else {
resolve(address);
}
});
});
}
function now() {
return Date.now();
}
export const DNS_DEFAULT_EXPIRE = 3600000;
export class DNSCache {
static pool = {
// <hostname>: [address, expire]
};
static expire = DNS_DEFAULT_EXPIRE;
static init(expire) {
if (typeof expire === 'number' && expire >= 0) {
DNSCache.expire = expire;
}
DNSCache.pool = {};
}
static async get(hostname) {
if (net.isIP(hostname)) {
return hostname;
}
let address = null;
if (!DNSCache.pool[hostname]) {
address = await lookup(hostname);
DNSCache._put(hostname, address);
} else {
const [addr, expire] = DNSCache.pool[hostname];
const _now = now();
if (_now >= expire) {
delete DNSCache.pool[hostname];
}
logger.verbose(`[dns-cache] hit: hostname=${hostname} resolved=${addr} ttl=${expire - _now}ms`);
address = addr;
}
return address;
}
static clear() {
DNSCache.pool = {};
}
static _put(hostname, address) {
if (DNSCache.expire > 0) {
const expire = now() + DNSCache.expire;
DNSCache.pool[hostname] = [address, expire];
}
}
}

@ -1,6 +1,7 @@
export * from './common';
export * from './crypto';
export * from './date';
export * from './dns-cache';
export * from './string';
export * from './validator';
export * from './advanced-buffer';