var URL = require('url'); var request = require('request'); var UError = require('./error').UserError; var mystreams = require('./streams'); // // 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.ua = mainconfig.user_agent; this.ca; this.url = URL.parse(this.config.url); if (this.url.hostname === 'registry.npmjs.org') { this.ca = require('./npmsslkeys'); // 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); }*/ } this.config.url = this.config.url.replace(/\/$/, ''); return this; } Storage.prototype.can_fetch_url = function(url) { url = URL.parse(url); return url.protocol === this.url.protocol && url.host === this.url.host && url.path.indexOf(this.url.path) === 0 } Storage.prototype.add_package = function(name, metadata, callback) { request({ url: this.config.url + '/' + escape(name), headers: { 'User-Agent': this.ua, }, method: 'PUT', ca: this.ca, json: metadata, }, function(err, res, body) { if (err) return callback(err); if (!(res.statusCode >= 200 && res.statusCode < 300)) { return callback(new Error('bad status code: ' + res.statusCode)); } callback(null, body); }); } Storage.prototype.add_version = function(name, version, metadata, tag, callback) { request({ url: this.config.url + '/' + escape(name) + '/' + escape(version) + '/-tag/' + escape(tag), headers: { 'User-Agent': this.ua, }, method: 'PUT', ca: this.ca, json: metadata, }, function(err, res, body) { if (err) return callback(err); if (!(res.statusCode >= 200 && res.statusCode < 300)) { return callback(new Error('bad status code: ' + res.statusCode)); } callback(null, body); }); } Storage.prototype.add_tarball = function(name, filename) { var stream = new mystreams.UploadTarballStream(); var self = this; var wstream = request({ uri: this.config.url + '/' + escape(name) + '/-/' + escape(filename) + '/whatever', method: 'PUT', headers: { 'User-Agent': this.ua, 'content-type': 'application/octet-stream' }, ca: this.ca, }); wstream.on('response', function(res) { if (!(res.statusCode >= 200 && res.statusCode < 300)) { return stream.emit('error', new UError({ msg: 'bad uplink status code: ' + res.statusCode, status: 500, })); } stream.emit('success'); }); wstream.on('error', function(err) { stream.emit('error', err); }); stream.abort = function() { process.nextTick(function() { if (wstream.req) { wstream.req.abort(); } }); }; stream.done = function() {}; stream.pipe(wstream); return stream; } Storage.prototype.get_package = function(name, callback) { request({ url: this.config.url + '/' + escape(name), json: true, headers: { 'User-Agent': this.ua, }, ca: this.ca, }, function(err, res, body) { if (err) return callback(err); if (res.statusCode === 404) { return callback(new UError({ msg: 'package doesn\'t exist on uplink', status: 404, })); } if (!(res.statusCode >= 200 && res.statusCode < 300)) { return callback(new Error('bad status code: ' + res.statusCode)); } callback(null, body); }); } Storage.prototype.get_tarball = function(name, filename) { return this.get_url(this.config.url + '/' + name + '/-/' + filename); } Storage.prototype.get_url = function(url) { var stream = new mystreams.ReadTarballStream(); stream.abort = function() {}; var rstream = request({ url: url, headers: { 'User-Agent': this.ua, }, ca: this.ca, encoding: null, }); 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, })); } rstream.pipe(stream); }); rstream.on('error', function(err) { stream.emit('error', err); }); return stream; } module.exports = Storage;