-
Notifications
You must be signed in to change notification settings - Fork 7
/
index.js
179 lines (171 loc) · 4.61 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
'use strict';
const fs = require('fs');
const path = require('path');
const util = require('util');
const events = require('events');
const eachSeries = function (coll, iteratee, callback) {
const len = coll.length;
let index = 0;
const next = function (err) {
if (index === len || err) {
return callback(err);
}
iteratee(coll[index++], next);
};
next();
};
/*
Constructor - requires path to cache directory
*/
const NginxCache = function (directory) {
if (!(this instanceof NginxCache)) {
return new NginxCache(directory);
}
events.EventEmitter.call(this);
// Store cache directory
if (typeof directory === 'string') {
this.directory = directory;
} else {
throw new Error('Directory required');
}
return this;
};
util.inherits(NginxCache, events.EventEmitter);
/*
Find cache files where the key, usually the URL, matches the RegExp pattern
*/
NginxCache.prototype.find = function (pattern) {
const that = this;
process.nextTick(function () {
if (typeof pattern === 'object' && pattern instanceof RegExp) {
// Reset depth counter
that.depth = 0;
// Start recursion into directory tree
that._findDirectory(that.directory, pattern);
} else {
that.emit('error', new Error('Invalid RegExp'));
that.emit('finish');
}
});
return this;
};
/*
Look in a directory for cache files
*/
NginxCache.prototype._findDirectory = function (directory, pattern) {
const that = this;
// Increment depth counter
that.depth++;
// Get children
fs.readdir(directory, function (err, files) {
if (err) {
if (err.code === 'EACCES' && directory === that.directory) {
that.emit('error', new Error('Permission denied to read files in root of cache directory ' + directory));
} else {
that.emit('warn', err);
}
} else {
// Loop over children, limiting the number of files open at once
eachSeries(files, function (file, done) {
const child = path.join(directory, file);
fs.stat(child, function (err, stat) {
if (err) {
that.emit('warn', err);
} else {
if (stat.isFile()) {
that._findFile(child, pattern);
} else if (stat.isDirectory()) {
that._findDirectory(child, pattern);
}
}
done();
});
}, function () {
// Decrement depth counter
that.depth--;
if (that.depth === 0) {
that.emit('finish');
}
});
}
});
};
/*
Look in a cache file for pattern
*/
NginxCache.prototype._findFile = function (file, pattern) {
const that = this;
// Increment depth counter
that.depth++;
// Open read-only file descriptor
fs.open(file, 'r', function (err, fd) {
if (err) {
// The Nginx Cache Manager probably deleted the file
that.emit('warn', err);
} else {
// Read first 1024 bytes from file, enough for headers
const buffer = Buffer.alloc(1024);
fs.read(fd, buffer, 0, buffer.length, 0, function (err, bytesRead, data) {
if (err) {
that.emit('warn', err);
} else {
// Extract the cache key from the Buffer
const key = keyFromBuffer(data, bytesRead);
if (key) {
if (pattern.test(key)) {
// Found a match
that.emit('match', file, key);
}
} else {
that.emit('warn', new Error('Could not find headers at start of ' + file));
}
}
// Close file descriptor
fs.close(fd, function () { });
// Decrement depth counter
that.depth--;
if (that.depth === 0) {
that.emit('finish');
}
});
}
});
};
/*
Seek '\nKEY: {{key}}\n' in a Buffer and extract '{{key}}'
*/
const keyFromBuffer = function (buffer, length) {
let key = null;
// Seek position in Buffer
let pos = 0;
// Seek '\nKEY: ' for start
let keyStart = -1;
while (pos < (length - 7) && keyStart === -1) {
if (
buffer[pos] === 0x0a && // '\n'
buffer[pos + 1] === 0x4b && // 'K'
buffer[pos + 2] === 0x45 && // 'E'
buffer[pos + 3] === 0x59 && // 'Y'
buffer[pos + 4] === 0x3a && // ':'
buffer[pos + 5] === 0x20 // ' '
) {
keyStart = pos + 6;
}
pos++;
}
if (keyStart !== -1) {
// Seek '\n' for end
let keyEnd = -1;
while (pos < length && keyEnd === -1) {
if (buffer[pos] === 0x0a) {
keyEnd = pos;
}
pos++;
}
if (keyEnd !== -1) {
key = buffer.toString('utf8', keyStart, keyEnd);
}
}
return key;
};
module.exports = NginxCache;