1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 |
|
8 |
|
9 |
|
10 |
|
11 |
|
12 |
|
13 |
|
14 |
|
15 |
|
16 |
|
17 |
|
18 |
|
19 |
|
20 |
|
21 | 'use strict';
|
22 |
|
23 | var MemoryStore = require('./memory').MemoryStore;
|
24 | var utils = require('./utils');
|
25 | var Lookup = utils.Lookup;
|
26 | var util = require('util');
|
27 | var Heap = require('binaryheap');
|
28 |
|
29 | var 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 |
|
37 | MemoryStoreExpire.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 |
|
65 | MemoryStoreExpire.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 |
|
91 | MemoryStoreExpire.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 |
|
123 | var 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 |
|
133 | Cache.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 |
|
161 | Cache.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 | };
|