From a8dc1b2f7b814e0b02cfb0fcab1379f855934817 Mon Sep 17 00:00:00 2001 From: Alex Kocharin Date: Fri, 8 Aug 2014 05:08:41 +0400 Subject: [PATCH] option not to cache third party files ref #85 --- lib/local-storage.js | 111 ++++++++++++++++++++++++++------- lib/storage.js | 24 ++++--- package.yaml | 2 +- test/functional/config-1.yaml | 6 ++ test/functional/config-2.yaml | 4 ++ test/functional/index.js | 1 + test/functional/nullstorage.js | 78 +++++++++++++++++++++++ test/start.sh | 10 --- 8 files changed, 193 insertions(+), 43 deletions(-) create mode 100644 test/functional/nullstorage.js delete mode 100755 test/start.sh diff --git a/lib/local-storage.js b/lib/local-storage.js index 92b6fe095..646811fed 100644 --- a/lib/local-storage.js +++ b/lib/local-storage.js @@ -46,7 +46,12 @@ Storage.prototype._internal_error = function(err, file, message) { } Storage.prototype.add_package = function(name, metadata, callback) { - this.storage(name).create_json(info_file, get_boilerplate(name), function(err) { + var storage = this.storage(name) + if (!storage) return callback(new UError({ + status: 404, + message: 'this package cannot be added' + })) + storage.create_json(info_file, get_boilerplate(name), function(err) { if (err && err.code === 'EEXISTS') { return callback(new UError({ status: 409, @@ -60,7 +65,13 @@ Storage.prototype.add_package = function(name, metadata, callback) { Storage.prototype.remove_package = function(name, callback) { var self = this self.logger.info({name: name}, 'unpublishing @{name} (all)') - self.storage(name).read_json(info_file, function(err, data) { + + var storage = self.storage(name) + if (!storage) return callback(new UError({ + status: 404, + message: 'no such package available', + })) + storage.read_json(info_file, function(err, data) { if (err) { if (err.code === 'ENOENT') { return callback(new UError({ @@ -73,7 +84,7 @@ Storage.prototype.remove_package = function(name, callback) { } self._normalize_package(data) - self.storage(name).unlink(info_file, function(err) { + storage.unlink(info_file, function(err) { if (err) return callback(err) var files = Object.keys(data._attachments) @@ -82,14 +93,14 @@ Storage.prototype.remove_package = function(name, callback) { if (files.length === 0) return cb() var file = files.shift() - self.storage(name).unlink(file, function() { + storage.unlink(file, function() { unlinkNext(cb) }) } unlinkNext(function() { // try to unlink the directory, but ignore errors because it can fail - self.storage(name).rmdir('.', function(err) { + storage.rmdir('.', function(err) { callback(err) }) }) @@ -99,7 +110,13 @@ Storage.prototype.remove_package = function(name, callback) { Storage.prototype._read_create_package = function(name, callback) { var self = this - self.storage(name).read_json(info_file, function(err, data) { + var storage = self.storage(name) + if (!storage) { + var data = get_boilerplate(name) + self._normalize_package(data) + return callback(null, data) + } + storage.read_json(info_file, function(err, data) { // TODO: race condition if (err) { if (err.code === 'ENOENT') { @@ -293,7 +310,8 @@ Storage.prototype.remove_tarball = function(name, filename, revision, callback) } }, function(err) { if (err) return callback(err) - self.storage(name).unlink(filename, callback) + var storage = self.storage(name) + if (storage) storage.unlink(filename, callback) }) } @@ -305,6 +323,8 @@ Storage.prototype.add_tarball = function(name, filename) { , length = 0 , shasum = crypto.createHash('sha1') + stream.abort = stream.done = function(){} + stream._transform = function(data) { shasum.update(data) length += data.length @@ -313,13 +333,27 @@ Storage.prototype.add_tarball = function(name, filename) { var self = this if (name === info_file || name === '__proto__') { - stream.emit('error', new UError({ - status: 403, - message: 'can\'t use this filename' - })) + process.nextTick(function() { + stream.emit('error', new UError({ + status: 403, + message: 'can\'t use this filename' + })) + }) + return stream } - var wstream = this.storage(name).write_stream(filename) + var storage = self.storage(name) + if (!storage) { + process.nextTick(function() { + stream.emit('error', new UError({ + status: 404, + message: 'can\'t upload this package' + })) + }) + return stream + } + + var wstream = storage.write_stream(filename) wstream.on('error', function(err) { if (err.code === 'EEXISTS') { @@ -380,13 +414,25 @@ Storage.prototype.add_tarball = function(name, filename) { Storage.prototype.get_tarball = function(name, filename, callback) { assert(utils.validate_name(filename)) + var self = this var stream = new mystreams.ReadTarballStream() stream.abort = function() { - rstream.abort() + if (rstream) rstream.abort() } - var rstream = this.storage(name).read_stream(filename) + var storage = self.storage(name) + if (!storage) { + process.nextTick(function() { + stream.emit('error', new UError({ + status: 404, + message: 'no such file available', + })) + }) + return stream + } + + var rstream = storage.read_stream(filename) rstream.on('error', function(err) { if (err && err.code === 'ENOENT') { stream.emit('error', new UError({ @@ -412,8 +458,13 @@ Storage.prototype.get_package = function(name, options, callback) { if (typeof(options) === 'function') callback = options, options = {} var self = this + var storage = self.storage(name) + if (!storage) return callback(new UError({ + status: 404, + message: 'no such package available' + })) - self.storage(name).read_json(info_file, function(err, result) { + storage.read_json(info_file, function(err, result) { if (err) { if (err.code === 'ENOENT') { return callback(new UError({ @@ -433,13 +484,17 @@ Storage.prototype.get_recent_packages = function(startkey, callback) { var self = this var i = 0 var list = [] - fs.readdir(self.storage('').path, function(err, files) { + + var storage = self.storage('') + if (!storage) return callback(null, []) + + fs.readdir(storage.path, function(err, files) { if (err) return callback(null, []) var filesL = files.length files.forEach(function(file) { - fs.stat(self.storage(file).path, function(err, stats) { + fs.stat(storage.path, function(err, stats) { if (err) return callback(err) if (stats.mtime > startkey) { list.push({ @@ -474,7 +529,12 @@ Storage.prototype.get_recent_packages = function(startkey, callback) { // Storage.prototype.update_package = function(name, updateFn, _callback) { var self = this - self.storage(name).lock_and_read_json(info_file, function(err, fd, json) { + var storage = self.storage(name) + if (!storage) return callback(new UError({ + status: 404, + message: 'no such package available', + })) + storage.lock_and_read_json(info_file, function(err, fd, json) { function callback() { var _args = arguments if (fd) { @@ -526,16 +586,21 @@ Storage.prototype._write_package = function(name, json, callback) { var rev = json._rev.split('-') json._rev = ((+rev[0] || 0) + 1) + '-' + crypto.pseudoRandomBytes(8).toString('hex') - this.storage(name).write_json(info_file, json, callback) + var storage = this.storage(name) + if (!storage) return callback() + storage.write_json(info_file, json, callback) } Storage.prototype.storage = function(package) { + var path = this.config.get_package_setting(package, 'storage') + if (path == null) path = this.config.storage + if (path == null || path === false) { + this.logger.debug({name: package}, 'this package has no storage defined: @{name}') + return null + } return new Path_Wrapper( Path.join( - Path.resolve( - Path.dirname(this.config.self_path), - this.config.get_package_setting(package, 'storage') || this.config.storage - ), + Path.resolve(Path.dirname(this.config.self_path), path), package ) ) diff --git a/lib/storage.js b/lib/storage.js index 7b0a6b1e2..fe940bff2 100644 --- a/lib/storage.js +++ b/lib/storage.js @@ -250,26 +250,32 @@ Storage.prototype.get_tarball = function(name, filename) { } var savestream = self.local.add_tarball(name, filename) - savestream.on('error', function(err) { - savestream.abort() - stream.emit('error', err) - }) - savestream.on('open', function() { + function on_open() { var rstream2 = uplink.get_url(file.url) rstream2.on('error', function(err) { - savestream.abort() + if (savestream) savestream.abort() + savestream = null stream.emit('error', err) }) rstream2.on('end', function() { - savestream.done() + if (savestream) savestream.done() }) rstream2.on('content-length', function(v) { stream.emit('content-length', v) - savestream.emit('content-length', v) + if (savestream) savestream.emit('content-length', v) }) rstream2.pipe(stream) - rstream2.pipe(savestream) + if (savestream) rstream2.pipe(savestream) + } + + savestream.on('open', function() { + on_open() + }) + savestream.on('error', function() { + if (savestream) savestream.abort() + savestream = null + on_open() }) } } diff --git a/package.yaml b/package.yaml index 4c44d403b..4874d97d3 100644 --- a/package.yaml +++ b/package.yaml @@ -58,7 +58,7 @@ keywords: - server scripts: - test: mocha ./test/functional ./test/unit + test: mocha -R dot ./test/functional ./test/unit lint: eslint -c ./.eslint.yaml ./lib #prepublish: js-yaml package.yaml > package.json diff --git a/test/functional/config-1.yaml b/test/functional/config-1.yaml index b8b0c3530..9be3bfa98 100644 --- a/test/functional/config-1.yaml +++ b/test/functional/config-1.yaml @@ -37,6 +37,12 @@ packages: allow_publish: all proxy_access: express + 'test-nullstorage*': + allow_access: all + allow_publish: all + proxy_access: server2 + storage: false + 'baduplink': allow_access: all allow_publish: all diff --git a/test/functional/config-2.yaml b/test/functional/config-2.yaml index 2f38072fc..a55dd59c0 100644 --- a/test/functional/config-2.yaml +++ b/test/functional/config-2.yaml @@ -28,6 +28,10 @@ packages: allow_publish: test anonymous proxy_access: server1 + 'test-nullstorage*': + allow_access: all + allow_publish: all + '*': allow_access: test anonymous allow_publish: test anonymous diff --git a/test/functional/index.js b/test/functional/index.js index 1bdaad115..c3672698d 100644 --- a/test/functional/index.js +++ b/test/functional/index.js @@ -43,6 +43,7 @@ describe('Func', function() { require('./incomplete')() require('./mirror')() require('./newnpmreg')() + require('./nullstorage')() require('./race')() require('./racycrash')() require('./security')() diff --git a/test/functional/nullstorage.js b/test/functional/nullstorage.js new file mode 100644 index 000000000..4d199cdc2 --- /dev/null +++ b/test/functional/nullstorage.js @@ -0,0 +1,78 @@ +require('./lib/startup') + +var assert = require('assert') + , async = require('async') + , crypto = require('crypto') + +function readfile(x) { + return require('fs').readFileSync(__dirname + '/' + x) +} + +module.exports = function() { + var server = process.server + var server2 = process.server2 + + it('trying to fetch non-existent package / null storage', function(cb) { + server.get_package('test-nullstorage-nonexist', function(res, body) { + assert.equal(res.statusCode, 404) + assert(~body.error.indexOf('no such package')) + cb() + }) + }) + + describe('test-nullstorage on server2', function() { + before(server2.add_package.bind(server2, 'test-nullstorage2')) + + it('creating new package - server2', function(){/* test for before() */}) + + it('downloading non-existent tarball', function(cb) { + server.get_tarball('test-nullstorage2', 'blahblah', function(res, body) { + assert.equal(res.statusCode, 404) + assert(~body.error.indexOf('no such file')) + cb() + }) + }) + + describe('tarball', function() { + before(function(cb) { + server2.put_tarball('test-nullstorage2', 'blahblah', readfile('fixtures/binary'), function(res, body) { + assert.equal(res.statusCode, 201) + assert(body.ok) + cb() + }) + }) + + before(function(cb) { + var pkg = require('./lib/package')('test-nullstorage2') + pkg.dist.shasum = crypto.createHash('sha1').update(readfile('fixtures/binary')).digest('hex') + server2.put_version('test-nullstorage2', '0.0.1', pkg, function(res, body) { + assert.equal(res.statusCode, 201) + assert(~body.ok.indexOf('published')) + cb() + }) + }) + + it('uploading new tarball', function(){/* test for before() */}) + + it('downloading newly created tarball', function(cb) { + server.get_tarball('test-nullstorage2', 'blahblah', function(res, body) { + assert.equal(res.statusCode, 200) + assert.deepEqual(body, readfile('fixtures/binary').toString('utf8')) + cb() + }) + }) + + it('downloading newly created package', function(cb) { + server.get_package('test-nullstorage2', function(res, body) { + assert.equal(res.statusCode, 200) + assert.equal(body.name, 'test-nullstorage2') + assert.equal(body.versions['0.0.1'].name, 'test-nullstorage2') + assert.equal(body.versions['0.0.1'].dist.tarball, 'http://localhost:55551/test-nullstorage2/-/blahblah') + assert.deepEqual(body['dist-tags'], {latest: '0.0.1'}) + cb() + }) + }) + }) + }) +} + diff --git a/test/start.sh b/test/start.sh deleted file mode 100755 index 8f0da7d29..000000000 --- a/test/start.sh +++ /dev/null @@ -1,10 +0,0 @@ -#!/bin/sh - -CWD=$(pwd) -PATH='../node_modules/.bin':$PATH -TESTDIR=$(dirname $0) -cd $TESTDIR -mocha ./functional ./unit -TESTRES=$? -cd $CWD -exit $TESTRES