UNPKG

4.88 kBJavaScriptView Raw
1// Copyright 2012 Timothy J Fontaine <tjfontaine@gmail.com>
2//
3// Permission is hereby granted, free of charge, to any person obtaining a copy
4// of this software and associated documentation files (the "Software"), to deal
5// in the Software without restriction, including without limitation the rights
6// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7// copies of the Software, and to permit persons to whom the Software is
8// furnished to do so, subject to the following conditions:
9//
10// The above copyright notice and this permission notice shall be included in
11// all copies or substantial portions of the Software.
12//
13// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19// THE SOFTWARE
20
21'use strict';
22
23var MemoryStore = require('./memory').MemoryStore;
24var utils = require('./utils');
25var Lookup = utils.Lookup;
26var util = require('util');
27var Heap = require('binaryheap');
28
29var MemoryStoreExpire = function (store, zone, opts) {
30 opts = opts || {};
31 this._store = store;
32 this._zone = zone;
33 this._max_keys = opts.max_keys;
34 this._ttl = new Heap(true);
35};
36
37MemoryStoreExpire.prototype.get = function (domain, key, cb) {
38 var self = this;
39 this._store.get(domain, key, function (err, results) {
40 var i, j, type, record;
41 var nresults = {};
42 var now = Date.now();
43
44 for (i in results) {
45 type = results[i];
46 for (j in type) {
47 record = type[j];
48 record.ttl = Math.round((record._ttl_expires - now) / 1000)
49 if (record.ttl > 0) {
50 if (!nresults[i]) {
51 nresults[i] = [];
52 }
53 nresults[i].push(record);
54 } else {
55 self._ttl.remove(record);
56 self._store.delete(self._zone, record.name, record.type, function () {});
57 }
58 }
59 }
60
61 cb(err, nresults);
62 });
63};
64
65MemoryStoreExpire.prototype.set = function (domain, key, data, cb) {
66 var i, j, type, record, expires;
67 var self = this;
68 var now = Date.now();
69
70 for (i in data) {
71 type = data[i];
72 for (j in type) {
73 record = type[j];
74 expires = (record.ttl * 1000) + now;
75 record._ttl_expires = expires;
76 self._ttl.insert(record, expires);
77 }
78 }
79
80 while (this._ttl.length > this._max_keys) {
81 var record = this._ttl.pop();
82 this._store.delete(this._zone, record.name, record.type);
83 }
84
85 this._store.set(domain, key, data, function (err, results) {
86 if (cb)
87 cb(err, results);
88 });
89};
90
91MemoryStoreExpire.prototype.delete = function (domain, key, type, cb) {
92 if (!cb) {
93 cb = type;
94 type = undefined;
95 }
96
97 var self = this;
98
99 this._store.get(domain, utils.ensure_absolute(key), function (gerr, gresults) {
100 var i, j, ktype, record;
101
102 for (i in gresults) {
103 ktype = gresults[i];
104 for (j in ktype) {
105 record = ktype[j];
106 self._ttl.remove(record);
107 }
108 }
109
110 if (!gresults) {
111 if (cb)
112 cb(gerr, gresults);
113 return;
114 }
115
116 self._store.delete(domain, key, type, function (err, results) {
117 if (cb)
118 cb(err, results);
119 });
120 });
121};
122
123var Cache = module.exports = function (opts) {
124 opts = opts || {};
125 this._zone = '.' || opts.zone;
126 this._store = undefined;
127 this.purge = function () {
128 this._store = new MemoryStoreExpire(opts.store || new MemoryStore(), this._zone, opts);
129 }
130 this.purge();
131};
132
133Cache.prototype.store = function (packet) {
134 var self = this;
135 var c = {};
136
137 function each(record) {
138 var r = c[record.name.toLowerCase()];
139 var t;
140
141 if (!r)
142 r = c[record.name.toLowerCase()] = {};
143
144 t = r[record.type];
145
146 if (!t)
147 t = r[record.type] = [];
148
149 t.push(record);
150 }
151
152 packet.answer.forEach(each);
153 packet.authority.forEach(each);
154 packet.additional.forEach(each);
155
156 Object.keys(c).forEach(function (key) {
157 self._store.set(self._zone, utils.ensure_absolute(key), c[key]);
158 });
159};
160
161Cache.prototype.lookup = function (question, cb) {
162 var self = this;
163 Lookup(this._store, this._zone, question, function (err, results) {
164 var i, record, found = false;
165
166 for (i in results) {
167 record = results[i];
168 if (record.type == question.type) {
169 found = true;
170 break;
171 }
172 }
173
174 if (results && !found) {
175 self._store.delete(self._zone, utils.ensure_absolute(question.name));
176 results.forEach(function (rr) {
177 self._store.delete(self._zone, utils.ensure_absolute(rr.name));
178 });
179 results = null;
180 }
181
182 cb(results);
183 });
184};