var URL = require('url') , request = require('request') , Stream = require('stream') , UError = require('./error').UserError , mystreams = require('./streams') , Logger = require('./logger') , utils = require('./utils') , encode = encodeURIComponent // // Implements Storage interface // (same for storage.js, local-storage.js, up-storage.js) // function Storage(config, mainconfig) { if (!(this instanceof Storage)) return new Storage(config) this.config = config this.is_alive = true this.userAgent = mainconfig.user_agent this.ca = config.ca this.logger = Logger.logger.child({sub: 'out'}) this.server_id = mainconfig.server_id this.maxage = (parseInt(this.config.maxage, 10) || 0) * 1000 this.url = URL.parse(this.config.url) if (this.url.hostname === 'registry.npmjs.org') { // npm registry is too slow working with ssl :( /*if (this.config._autogenerated) { // encrypt all the things! this.url.protocol = 'https' this.config.url = URL.format(this.url) }*/ } _setupProxy.call(this, this.url.hostname, config, mainconfig, this.url.protocol === 'https:') this.config.url = this.config.url.replace(/\/$/, '') if (isNaN(parseFloat(this.config.timeout)) || !isFinite(this.config.timeout)) { this.config.timeout = 30000 } return this } function _setupProxy(hostname, config, mainconfig, isHTTPS) { var no_proxy var proxy_key = isHTTPS ? 'https_proxy' : 'http_proxy' // get http_proxy and no_proxy configs if (proxy_key in config) { this.proxy = config[proxy_key] } else if (proxy_key in mainconfig) { this.proxy = mainconfig[proxy_key] } if ('no_proxy' in config) { no_proxy = config.no_proxy } else if ('no_proxy' in mainconfig) { no_proxy = mainconfig.no_proxy } // use wget-like algorithm to determine if proxy shouldn't be used if (hostname[0] !== '.') hostname = '.' + hostname if (typeof(no_proxy) === 'string' && no_proxy.length) { no_proxy = no_proxy.split(',') } if (Array.isArray(no_proxy)) { for (var i=0; i= 200 && res.statusCode < 300)) { return callback(new Error('bad status code: ' + res.statusCode)) } callback(null, body, res.headers.etag) }) } Storage.prototype.get_tarball = function(name, options, filename) { if (!options) options = {} return this.get_url(this.config.url + '/' + name + '/-/' + filename) } Storage.prototype.get_url = function(url) { var stream = new mystreams.ReadTarballStream() stream.abort = function() {} var current_length = 0, expected_length var rstream = this.request({ uri_full: url, encoding: null, headers: { Accept: 'application/octet-stream', }, }) rstream.on('response', function(res) { if (res.statusCode === 404) { return stream.emit('error', new UError({ msg: 'file doesn\'t exist on uplink', status: 404, })) } if (!(res.statusCode >= 200 && res.statusCode < 300)) { return stream.emit('error', new UError({ msg: 'bad uplink status code: ' + res.statusCode, status: 500, })) } if (res.headers['content-length']) { expected_length = res.headers['content-length'] stream.emit('content-length', res.headers['content-length']) } rstream.pipe(stream) }) rstream.on('error', function(err) { stream.emit('error', err) }) rstream.on('data', function(d) { current_length += d.length }) rstream.on('end', function(d) { if (d) current_length += d.length if (expected_length && current_length != expected_length) stream.emit('error', new Error('content length mismatch')) }) return stream } Storage.prototype._add_proxy_headers = function(req, headers) { if (req) { headers['X-Forwarded-For'] = ( (req && req.headers['x-forwarded-for']) ? req.headers['x-forwarded-for'] + ', ' : '' ) + req.connection.remoteAddress } // always attach Via header to avoid loops, even if we're not proxying headers['Via'] = (req && req.headers['via']) ? req.headers['via'] + ', ' : '' headers['Via'] += '1.1 ' + this.server_id + ' (Sinopia)' } module.exports = Storage