123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203 |
- 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, {
- proxy: remote.protocol === 'https:' ? this._config.httpsProxy : this._config.proxy,
- 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;
|