UNPKG

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
"use strict"; 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 };