core,utils: move core/dns-cache.js to utils/, and refactor it
This commit is contained in:
parent
b66e14d1cb
commit
b785e20612
@ -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});
|
||||
}
|
||||
|
35
src/utils/__tests__/dns-cache.test.js
Normal file
35
src/utils/__tests__/dns-cache.test.js
Normal file
@ -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
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';
|
||||
|
Loading…
Reference in New Issue
Block a user