From a51290933ac6be0395fe3cf6b7d42ca5b96d20d7 Mon Sep 17 00:00:00 2001 From: Timothy J Fontaine Date: Sat, 23 Mar 2013 20:37:31 -0700 Subject: [PATCH] split caching into its own library --- ChangeLog | 1 + README.md | 62 +--------------- dns.js | 9 --- lib/cache.js | 184 ------------------------------------------------ lib/memory.js | 93 ------------------------ lib/platform.js | 4 +- lib/utils.js | 86 ---------------------- package.json | 2 +- 8 files changed, 6 insertions(+), 435 deletions(-) delete mode 100644 lib/cache.js delete mode 100644 lib/memory.js diff --git a/ChangeLog b/ChangeLog index 1056b02..c7b2fa4 100644 --- a/ChangeLog +++ b/ChangeLog @@ -2,6 +2,7 @@ * Add SPF record type, same as TXT * Emit platform ready on windows (Oleg Elifantiev) * Split packet parsing into its own library + * Split caching into its own library 2013-03-03 Timothy J Fontaine * Assert missing fields diff --git a/README.md b/README.md index 8ffaaf8..fa526c8 100644 --- a/README.md +++ b/README.md @@ -30,7 +30,7 @@ var question = dns.Question({ type: 'A', }); -var start = new Date().getTime(); +var start = Date.now(); var req = dns.Request({ question: question, @@ -49,7 +49,7 @@ req.on('message', function (err, answer) { }); req.on('end', function () { - var delta = (new Date().getTime()) - start; + var delta = (Date.now()) - start; console.log('Finished processing request: ' + delta.toString() + 'ms'); }); @@ -269,61 +269,3 @@ Available Types: - `service` -- string - `regexp` -- string - `replacement` -- string - -Cache ------ - -If you perform a query on an A or AAAA type and it doesn't exist, the cache -will attempt to lookup a CNAME and then resolve that. - -The constructor takes an optional object with the following properties: - - * `store` -- implements the cache store model (optional, default MemoryStore) - -Methods: - - * `lookup(question, cb)` -- for a given question check the cache store for -existence - * `store(packet)` -- iterates over the resource records in a packet and sends -them to the cache store - * `purge()` -- clears the cache store of all entries - -MemoryStore / Cache store model -------------------------------- - -`MemoryStore(opts)` -- An in memory store based on a js object - -Methods: - - * `get(domain, key, cb)` - - `domain` is the holder under which keys will be applied, -`key` is the subdomain that is being queried for. -If you `get('example.com', 'www', cb)` you are really asking for `www.example.com`. - - `cb(err, results)` -- results is an object of types and array of answers - * `{ 1: [{address: '127.0.0.1', ttl: 300, type: 1, class: 1}] }` - * `set(domain, key, data, cb)` - - `domain` is the parent under which this key is stored. -`key` is the subdomain we are storing, `data` is an object of types with an array of answers. - * `set('example.com', 'www', {1: [{class:1, type:1, ttl:300, address:'127.0.0.1'}]}, cb)` - - `cb(err, data)` -- cb merely returns the data that was passed. - * `delete(domain[, key[, type]], cb)` -- delete all from a domain, a domain and key, -or a domain a key and a type. - -Lookup ------- - -Is a mechanism that given a store performs the common resolution pattern. - -Given `example.com` previous added to a store: - - * `www.example.com CNAME foo.bar.example.com.` - * `*.example.com A 127.0.0.1` - -A `Lookup(store, 'example.com', {name:'www.example.com', type:1}, cb)` -will resolve `www` to the CNAME and then search for `foo.bar.example.com` which -will return no results, and then search for `*.bar.example.com` which will also -return no results, and ultimately searches for `*.example.com` which will return -the desired record. - -Callback will be called with `(err, results)` where results is an array suitable -for use in `Packet.answer` diff --git a/dns.js b/dns.js index 1b7a8c6..8b207c4 100644 --- a/dns.js +++ b/dns.js @@ -100,12 +100,3 @@ exports.Question = function (opts) { return q; }; exports.Request = client.Request; - -exports.Cache = require('./lib/cache'); -exports.MemoryStore = require('./lib/memory').MemoryStore; - -var utils = require('./lib/utils'); - -exports.Lookup = utils.Lookup -exports.is_absolute = utils.is_absolute; -exports.ensure_absolute = utils.ensure_absolute; diff --git a/lib/cache.js b/lib/cache.js deleted file mode 100644 index fb28d7c..0000000 --- a/lib/cache.js +++ /dev/null @@ -1,184 +0,0 @@ -// Copyright 2012 Timothy J Fontaine -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE - -'use strict'; - -var MemoryStore = require('./memory').MemoryStore; -var utils = require('./utils'); -var Lookup = utils.Lookup; -var util = require('util'); -var Heap = require('binaryheap'); - -var MemoryStoreExpire = function (store, zone, opts) { - opts = opts || {}; - this._store = store; - this._zone = zone; - this._max_keys = opts.max_keys; - this._ttl = new Heap(true); -}; - -MemoryStoreExpire.prototype.get = function (domain, key, cb) { - var self = this; - this._store.get(domain, key, function (err, results) { - var i, j, type, record; - var nresults = {}; - var now = Date.now(); - - for (i in results) { - type = results[i]; - for (j in type) { - record = type[j]; - record.ttl = Math.round((record._ttl_expires - now) / 1000) - if (record.ttl > 0) { - if (!nresults[i]) { - nresults[i] = []; - } - nresults[i].push(record); - } else { - self._ttl.remove(record); - self._store.delete(self._zone, record.name, record.type, function () {}); - } - } - } - - cb(err, nresults); - }); -}; - -MemoryStoreExpire.prototype.set = function (domain, key, data, cb) { - var i, j, type, record, expires; - var self = this; - var now = Date.now(); - - for (i in data) { - type = data[i]; - for (j in type) { - record = type[j]; - expires = (record.ttl * 1000) + now; - record._ttl_expires = expires; - self._ttl.insert(record, expires); - } - } - - while (this._ttl.length > this._max_keys) { - var record = this._ttl.pop(); - this._store.delete(this._zone, record.name, record.type); - } - - this._store.set(domain, key, data, function (err, results) { - if (cb) - cb(err, results); - }); -}; - -MemoryStoreExpire.prototype.delete = function (domain, key, type, cb) { - if (!cb) { - cb = type; - type = undefined; - } - - var self = this; - - this._store.get(domain, utils.ensure_absolute(key), function (gerr, gresults) { - var i, j, ktype, record; - - for (i in gresults) { - ktype = gresults[i]; - for (j in ktype) { - record = ktype[j]; - self._ttl.remove(record); - } - } - - if (!gresults) { - if (cb) - cb(gerr, gresults); - return; - } - - self._store.delete(domain, key, type, function (err, results) { - if (cb) - cb(err, results); - }); - }); -}; - -var Cache = module.exports = function (opts) { - opts = opts || {}; - this._zone = '.' || opts.zone; - this._store = undefined; - this.purge = function () { - this._store = new MemoryStoreExpire(opts.store || new MemoryStore(), this._zone, opts); - } - this.purge(); -}; - -Cache.prototype.store = function (packet) { - var self = this; - var c = {}; - - function each(record) { - var r = c[record.name.toLowerCase()]; - var t; - - if (!r) - r = c[record.name.toLowerCase()] = {}; - - t = r[record.type]; - - if (!t) - t = r[record.type] = []; - - t.push(record); - } - - packet.answer.forEach(each); - packet.authority.forEach(each); - packet.additional.forEach(each); - - Object.keys(c).forEach(function (key) { - self._store.set(self._zone, utils.ensure_absolute(key), c[key]); - }); -}; - -Cache.prototype.lookup = function (question, cb) { - var self = this; - Lookup(this._store, this._zone, question, function (err, results) { - var i, record, found = false; - - for (i in results) { - record = results[i]; - if (record.type == question.type) { - found = true; - break; - } - } - - if (results && !found) { - self._store.delete(self._zone, utils.ensure_absolute(question.name)); - results.forEach(function (rr) { - self._store.delete(self._zone, utils.ensure_absolute(rr.name)); - }); - results = null; - } - - cb(results); - }); -}; diff --git a/lib/memory.js b/lib/memory.js deleted file mode 100644 index 8694de2..0000000 --- a/lib/memory.js +++ /dev/null @@ -1,93 +0,0 @@ -// Copyright 2012 Timothy J Fontaine -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - -'use strict'; - -var MemoryStore = exports.MemoryStore = function (opts) { - this._store = {}; -}; - -MemoryStore.prototype.get = function (domain, key, cb) { - var d = domain.toLowerCase(); - var k = key.toLowerCase(); - var result = this._store[d]; - - if (result) - result = result[k]; - - process.nextTick(function () { - cb(null, result); - }); -}; - -MemoryStore.prototype.set = function (domain, key, data, cb) { - var d = domain.toLowerCase(); - var k = key.toLowerCase(); - var result_domain = this._store[d]; - - if (!result_domain) - result_domain = this._store[d] = {}; - - result_domain[k] = data; - - if (cb) { - process.nextTick(function () { - cb(null, data); - }); - } -}; - -MemoryStore.prototype.delete = function (domain, key, type, cb) { - var d, k; - - if (!cb) { - cb = type; - type = undefined; - } - - if (!cb) { - cb = key; - type = undefined; - } - - d = this._store[domain.toLowerCase()]; - - if (d && key) - k = d[key.toLowerCase()]; - - if (domain && key && type) { - if (d && k) { - delete k[type]; - } - } else if (domain && key) { - if (d) { - delete d[k]; - } - } else if (domain) { - if (d) { - delete this._store[domain.toLowerCase()]; - } - } - - if (cb) { - process.nextTick(function () { - cb(null, domain); - }); - } -}; diff --git a/lib/platform.js b/lib/platform.js index d59ce1d..0462346 100644 --- a/lib/platform.js +++ b/lib/platform.js @@ -25,7 +25,7 @@ var fs = require('fs'), net = require('net'), os = require('os'), util = require('util'), - Cache = require('./cache'), + Cache = require('native-dns-cache'), consts = require('native-dns-packet').consts, path = require('path'), utils = require('./utils'); @@ -268,7 +268,7 @@ Platform.prototype._parseHosts = function(hostsfile) { }); Object.keys(toStore).forEach(function (key) { - self.hosts._store.set(self.hosts._zone, utils.ensure_absolute(key), toStore[key]); + self.hosts._store.set(self.hosts._zone, key, toStore[key]); }); self._hostsReady = true; self._checkReady(); diff --git a/lib/utils.js b/lib/utils.js index a0d748e..47bf497 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -197,89 +197,3 @@ exports.reverseIP = function(ip) { return reverseip; }; - -var is_absolute = exports.is_absolute = function (f) { - return f && /\.$/.test(f); -}; - -var ensure_absolute = exports.ensure_absolute = function (f) { - if (!is_absolute(f)) - return f += '.'; - return f; -}; - -var CNAME = require('native-dns-packet').consts.NAME_TO_QTYPE.CNAME; - -var Lookup = exports.Lookup = function (store, zone, question, cb) { - if (!(this instanceof Lookup)) - return new Lookup(store, zone, question, cb); - - this.store = store; - this.zone = zone; - this.cb = cb; - this.question = question; - this.results = []; - this.wildcard = undefined; - - this.name = ensure_absolute(question.name); - - this.store.get(this.zone, this.name, this.lookup.bind(this)); -}; - -Lookup.prototype.send = function (err) { - this.cb(err, this.results); -}; - -Lookup.prototype.lookup = function (err, results) { - var type, ret, name, self = this; - - if (err) - return this.send(err); - - if (!results) { - if (!this.wildcard) - this.wildcard = this.question.name; - - if (this.wildcard.toLowerCase() == this.zone.toLowerCase()) - return this.send(); - - name = this.wildcard = this.wildcard.split('.').splice(1).join('.'); - - // 'com.'.split('.').splice(1) will return empty string, we're at the end - if (!this.wildcard) - return this.send(); - - name = '*.' + name; - } else if (results[this.question.type]) { - type = this.question.type; - ret = results; - } else if (results[CNAME]) { - type = CNAME; - ret = results; - this.name = name = results[CNAME][0].data - } - - if (ret) { - ret = ret[type]; - ret.forEach(function (r) { - var rr, k; - - if (self.wildcard && /^\*/.test(r.name)) { - rr = {}; - for (k in r) { - rr[k] = r[k]; - } - rr.name = self.name; - } else { - rr = r; - } - - self.results.push(rr); - }); - } - - if (name) - this.store.get(this.zone, ensure_absolute(name), this.lookup.bind(this)); - else - this.send(); -}; diff --git a/package.json b/package.json index 9ac46a5..2253d7d 100644 --- a/package.json +++ b/package.json @@ -24,8 +24,8 @@ "test": "nodeunit test" }, "dependencies": { - "binaryheap": ">= 0.0.2", "ipaddr.js": ">= 0.1.1", + "native-dns-cache": ">= 0.0.1", "native-dns-packet": ">= 0.0.1" }, "devDependencies": {