Current File : //usr/lib/node_modules/bower/packages/bower-registry-client/lib/lookup.js |
var path = require('path');
var url = require('url');
var async = require('async');
var request = require('request');
var replay = require('request-replay');
var createError = require('./util/createError');
var Cache = require('./util/Cache');
function lookup(name, callback) {
var data;
var that = this;
var registry = this._config.registry.search;
var total = registry.length;
var index = 0;
// If no registry entries were passed..
if (!total) {
return callback();
}
// Lookup package in series in each registry
// endpoint until we got the data
async.doUntil(
function(next) {
var remote = url.parse(registry[index]);
var lookupCache = that._lookupCache[remote.host];
// If force flag is disabled we check the cache
if (!that._config.force) {
lookupCache.get(name, function(err, value) {
data = value;
// Don't proceed with making a request if we got an error,
// a value from the cache or if the offline flag is enabled
if (err || data || that._config.offline) {
return next(err);
}
doRequest.call(that, name, index, function(err, entry) {
if (err || !entry) {
return next(err);
}
data = entry;
// Store in cache
lookupCache.set(name, entry, getMaxAge(entry), next);
});
});
// Otherwise, we totally bypass the cache and
// make only the request
} else {
doRequest.call(that, name, index, function(err, entry) {
if (err || !entry) {
return next(err);
}
data = entry;
// Store in cache
lookupCache.set(name, entry, getMaxAge(entry), next);
});
}
},
function() {
// Until the data is unknown or there's still registries to test
return !!data || ++index === total;
},
function(err) {
// If some of the registry entries failed, error out
if (err) {
return callback(err);
}
callback(null, data);
}
);
}
function doRequest(name, index, callback) {
var req;
var msg;
var requestUrl =
this._config.registry.search[index] +
'/packages/' +
encodeURIComponent(name);
var remote = url.parse(requestUrl);
var headers = {};
var that = this;
if (this._config.userAgent) {
headers['User-Agent'] = this._config.userAgent;
}
req = replay(
request.get(
requestUrl,
{
headers: headers,
ca: this._config.ca.search[index],
strictSSL: this._config.strictSsl,
timeout: this._config.timeout,
json: true
},
function(err, response, body) {
// If there was an internal error (e.g. timeout)
if (err) {
return callback(
createError(
'Request to ' +
requestUrl +
' failed: ' +
err.message,
err.code
)
);
}
// If not found, try next
if (response.statusCode === 404) {
return callback();
}
// Abort if there was an error (range different than 2xx)
if (response.statusCode < 200 || response.statusCode > 299) {
return callback(
createError(
'Request to ' +
requestUrl +
' failed with ' +
response.statusCode,
'EINVRES'
)
);
}
// Validate response body, since we are expecting a JSON object
// If the server returns an invalid JSON, it's still a string
if (typeof body !== 'object') {
return callback(
createError(
'Response of request to ' +
requestUrl +
' is not a valid json',
'EINVRES'
)
);
}
var data;
if (body.url) {
data = {
type: 'alias',
url: body.url
};
}
callback(null, data);
}
)
);
if (this._logger) {
req.on('replay', function(replay) {
msg =
'Request to ' +
requestUrl +
' failed with ' +
replay.error.code +
', ';
msg += 'retrying in ' + (replay.delay / 1000).toFixed(1) + 's';
that._logger.warn('retry', msg);
});
}
}
function getMaxAge(entry) {
// If type is alias, make it 5 days
if (entry.type === 'alias') {
return 5 * 24 * 60 * 60 * 1000;
}
// Otherwise make it 5 minutes
return 5 * 60 * 60 * 1000;
}
function initCache() {
this._lookupCache = this._cache.lookup || {};
// Generate a cache instance for each registry endpoint
this._config.registry.search.forEach(function(registry) {
var cacheDir;
var host = url.parse(registry).host;
// Skip if there's a cache for the same host
if (this._lookupCache[host]) {
return;
}
if (this._config.cache) {
cacheDir = path.join(
this._config.cache,
encodeURIComponent(host),
'lookup'
);
}
this._lookupCache[host] = new Cache(cacheDir, {
max: 250,
// If offline flag is passed, we use stale entries from the cache
useStale: this._config.offline
});
}, this);
}
function clearCache(name, callback) {
var lookupCache = this._lookupCache;
var remotes = Object.keys(lookupCache);
if (typeof name === 'function') {
callback = name;
name = null;
}
if (name) {
async.forEach(
remotes,
function(remote, next) {
lookupCache[remote].del(name, next);
},
callback
);
} else {
async.forEach(
remotes,
function(remote, next) {
lookupCache[remote].clear(next);
},
callback
);
}
}
function resetCache() {
var remote;
for (remote in this._lookupCache) {
this._lookupCache[remote].reset();
}
}
lookup.initCache = initCache;
lookup.clearCache = clearCache;
lookup.resetCache = resetCache;
module.exports = lookup;