fast-geoip
Version:
A faster & low-memory replacement for geoip-lite, a node library that maps IPs to geographical information
126 lines (125 loc) • 4.03 kB
JavaScript
;
var fs = require("fs");
var path = require("path");
var utils = require("./utils");
var params = require('./params');
var cacheEnabled = false;
var ipCache = {};
var locationCache;
var DATA_DIR = path.join(path.dirname(__dirname), "data");
function enableCache() {
if (!cacheEnabled) {
locationCache = readFile("locations.json").then(function (data) {
cacheEnabled = true;
return data;
});
}
}
function readFile(filename) {
if (cacheEnabled && ipCache[filename] != undefined) {
return Promise.resolve(ipCache[filename]);
}
return new Promise(function (resolve, reject) {
fs.readFile(path.join(DATA_DIR, filename), function (err, data) {
if (err) {
reject(err);
}
else if (data == undefined) {
reject();
}
else {
var content = JSON.parse(data.toString());
resolve(content);
if (cacheEnabled) {
ipCache[filename] = content;
}
}
});
});
}
function readFileChunk(filename, offset, length) {
return new Promise(function (resolve, reject) {
fs.open(path.join(DATA_DIR, filename), 'r', function (err, fd) {
if (err)
reject(err);
var buf = Buffer.alloc == undefined ? new Buffer(length) : Buffer.alloc(length);
fs.read(fd, buf, 0, length, offset, function (err, _, buffer) {
fs.close(fd, function () { });
if (err)
reject(err);
resolve(JSON.parse(buffer.toString()));
});
});
});
}
function readLocationRecord(index) {
if (cacheEnabled) {
return locationCache.then(function (locations) {
return locations[index];
});
}
else {
return readFileChunk("locations.json", index * params.LOCATION_RECORD_SIZE + 1, params.LOCATION_RECORD_SIZE - 1);
}
}
function firstArrayItem(item) {
return item[0];
}
function getNextIp(data, index, currentNextIp, extractKey) {
if (index < (data.length - 1)) {
return extractKey(data[index + 1]);
}
else {
return currentNextIp;
}
}
function lookup4(stringifiedIp) {
var ip = utils.ipStr2Num(stringifiedIp);
var rootIndex;
var ipData;
var nextIp = utils.ipStr2Num("255.255.255.255");
return readFile("index.json")
.then(function (data) {
// IP cannot be NaN
if (Object.is(ip, NaN))
throw "IP cannot be NaN";
rootIndex = utils.binarySearch(data, ip, utils.identity);
if (rootIndex == -1) {
// Ip is not in the database, return empty object
throw "IP not found in the database";
}
nextIp = getNextIp(data, rootIndex, nextIp, utils.identity);
return readFile("i" + rootIndex + ".json");
})
.then(function (data) {
var index = utils.binarySearch(data, ip, utils.identity) + rootIndex * params.NUMBER_NODES_PER_MIDINDEX;
nextIp = getNextIp(data, index, nextIp, utils.identity);
return readFile(index + ".json");
}).then(function (data) {
var index = utils.binarySearch(data, ip, firstArrayItem);
ipData = data[index];
if (ipData[1] == null) {
throw "IP doesn't any region nor country associated";
}
nextIp = getNextIp(data, index, nextIp, firstArrayItem);
return readLocationRecord(ipData[1]);
}).then(function (data) {
return {
range: [ipData[0], nextIp],
country: data[0],
region: data[1],
eu: data[5],
timezone: data[4],
city: data[2],
ll: [ipData[2], ipData[3]],
metro: data[3],
area: ipData[4]
};
}).catch(function () {
return null;
});
}
module.exports = {
lookup: lookup4,
enableCache: enableCache
};