diff --git a/lib/auth.js b/lib/auth.js index 2ac528d18..9a7639e49 100644 --- a/lib/auth.js +++ b/lib/auth.js @@ -1,6 +1,6 @@ var Path = require('path') , crypto = require('crypto') - , UError = require('./error').UserError + , Error = require('http-errors') , Logger = require('./logger') , assert = require('assert') @@ -65,10 +65,8 @@ function Auth(config) { }, adduser: function(user, password, cb) { - if (config.users && config.users[user]) return cb(new UError({ - status: 403, - message: 'this user already exists', - })) + if (config.users && config.users[user]) + return cb(Error[403]('this user already exists')) return cb() }, @@ -76,17 +74,11 @@ function Auth(config) { this.plugins.push({ authenticate: function(user, password, cb) { - return cb(new UError({ - status: 403, - message: 'bad username/password, access denied', - })) + return cb(Error[403]('bad username/password, access denied')) }, adduser: function(user, password, cb) { - return cb(new UError({ - status: 409, - message: 'registration is disabled', - })) + return cb(Error[409]('registration is disabled')) }, }) } diff --git a/lib/config.js b/lib/config.js index 24f564fe1..6028d329a 100644 --- a/lib/config.js +++ b/lib/config.js @@ -2,7 +2,7 @@ var assert = require('assert') , crypto = require('crypto') , Path = require('path') , minimatch = require('minimatch') - , UError = require('./error').UserError + , Error = require('http-errors') , LocalList = require('./local-list') , utils = require('./utils') diff --git a/lib/error.js b/lib/error.js deleted file mode 100644 index 49d17c699..000000000 --- a/lib/error.js +++ /dev/null @@ -1,62 +0,0 @@ -var util = require('util') - , utils = require('./utils') - -function parse_error_params(params, status, message) { - if (typeof(params) === 'string') { - return { - message: params, - status: status, - } - } else if (typeof(params) === 'number') { - return { - message: message, - status: params, - } - } else if (utils.is_object(params)) { - if (params.message == null) params.message = message - if (params.status == null) params.status = status - return params - } else { - return { - message: message, - status: status, - } - } -} - -/* - * Errors caused by malfunctioned code - */ -var AppError = function(params, constr) { - Error.captureStackTrace(this, constr || this) - params = parse_error_params(params, 500, 'Internal server error') - this.message = params.message - this.status = params.status -} -util.inherits(AppError, Error) -AppError.prototype.name = 'Application Error' - -/* - * Errors caused by wrong request - */ -var UserError = function(params, constr) { - params = parse_error_params(params, 404, 'The requested resource was not found') - this.message = params.message - this.status = params.status -} -util.inherits(UserError, Error) -UserError.prototype.name = 'User Error' - -/* - * Mimic filesystem errors - */ -var FSError = function(code) { - this.code = code -} -util.inherits(UserError, Error) -UserError.prototype.name = 'FS Error' - -module.exports.AppError = AppError -module.exports.UserError = UserError -module.exports.FSError = FSError - diff --git a/lib/index.js b/lib/index.js index 2d2c7f507..192fa7dd4 100644 --- a/lib/index.js +++ b/lib/index.js @@ -3,7 +3,7 @@ var express = require('express') , utils = require('./utils') , Storage = require('./storage') , Config = require('./config') - , UError = require('./error').UserError + , Error = require('http-errors') , Middleware = require('./middleware') , Logger = require('./logger') , Cats = require('./status-cats') @@ -39,15 +39,10 @@ module.exports = function(config_hash) { } else { var message = "can't "+action+" restricted package without auth, did you forget 'npm set always-auth true'?" } - next(new UError({ - status: 403, - message: message, - })) + next(Error[403](message)) } else { - next(new UError({ - status: 403, - message: 'user '+req.remote_user.name+' not allowed to '+action+' it' - })) + next(Error[403]('user ' + req.remote_user.name + + ' not allowed to ' + action + ' it')) } } } @@ -144,10 +139,7 @@ module.exports = function(config_hash) { } } - return next(new UError({ - status: 404, - message: 'version not found: ' + req.params.version - })) + return next(Error[404]('version not found: ' + req.params.version)) }) }) @@ -205,20 +197,14 @@ module.exports = function(config_hash) { }) } else { if (typeof(req.body.name) !== 'string' || typeof(req.body.password) !== 'string') { - return next(new UError({ - status: 400, - message: 'user/password is not found in request (npm issue?)', - })) + return next(Error[400]('user/password is not found in request (npm issue?)')) } auth.add_user(req.body.name, req.body.password, function(err) { if (err) { if (err.status < 500 && err.message === 'this user already exists') { // with npm registering is the same as logging in // so we replace message in case of conflict - return next(new UError({ - status: 409, - message: 'bad username/password, access denied' - })) + return next(Error[409]('bad username/password, access denied')) } return next(err) } @@ -251,20 +237,14 @@ module.exports = function(config_hash) { var name = req.params.package if (Object.keys(req.body).length == 1 && utils.is_object(req.body.users)) { - return next(new UError({ - // 501 status is more meaningful, but npm doesn't show error message for 5xx - status: 404, - message: 'npm star|unstar calls are not implemented', - })) + // 501 status is more meaningful, but npm doesn't show error message for 5xx + return next(Error[404]('npm star|unstar calls are not implemented')) } try { var metadata = utils.validate_metadata(req.body, name) } catch(err) { - return next(new UError({ - status: 422, - message: 'bad incoming package data', - })) + return next(Error[422]('bad incoming package data')) } if (req.params._rev) { @@ -298,10 +278,7 @@ module.exports = function(config_hash) { // npm is doing something strange again // if this happens in normal circumstances, report it as a bug - return next(new UError({ - status: 400, - message: 'unsupported registry call', - })) + return next(Error[400]('unsupported registry call')) } if (err && err.status != 409) return next(err) diff --git a/lib/local-fs.js b/lib/local-fs.js index 1c865e384..e6a0e91ae 100644 --- a/lib/local-fs.js +++ b/lib/local-fs.js @@ -2,7 +2,13 @@ var fs = require('fs') , Path = require('path') , mkdirp = require('mkdirp') , mystreams = require('./streams') - , FSError = require('./error').FSError + , Error = require('http-errors') + +function FSError(code) { + var err = Error(code) + err.code = code + return err +} try { var fsExt = require('fs-ext') @@ -44,7 +50,7 @@ function write_stream(name) { }) fs.exists(name, function(exists) { - if (exists) return stream.emit('error', new FSError('EEXISTS')) + if (exists) return stream.emit('error', FSError('EEXISTS')) var tmpname = name + '.tmp-'+String(Math.random()).replace(/^0\./, '') , file = fs.createWriteStream(tmpname) @@ -114,14 +120,14 @@ function read_stream(name, stream, callback) { function create(name, contents, callback) { fs.exists(name, function(exists) { - if (exists) return callback(new FSError('EEXISTS')) + if (exists) return callback(FSError('EEXISTS')) write(name, contents, callback) }) } function update(name, contents, callback) { fs.exists(name, function(exists) { - if (!exists) return callback(new FSError('ENOENT')) + if (!exists) return callback(FSError('ENOENT')) write(name, contents, callback) }) } @@ -167,7 +173,7 @@ function lock_and_read(name, callback) { var buffer = new Buffer(st.size) fs.read(fd, buffer, 0, st.size, null, function(err, bytesRead, buffer) { if (err) return callback(err) - if (bytesRead != st.size) return callback(new Error('st.size != bytesRead'), fd) + if (bytesRead != st.size) return callback(Error('st.size != bytesRead'), fd) callback(null, fd, buffer) }) diff --git a/lib/local-storage.js b/lib/local-storage.js index 741ee3157..0cd8e4423 100644 --- a/lib/local-storage.js +++ b/lib/local-storage.js @@ -3,7 +3,7 @@ var fs = require('fs') , crypto = require('crypto') , assert = require('assert') , fs_storage = require('./local-fs') - , UError = require('./error').UserError + , Error = require('http-errors') , utils = require('./utils') , mystreams = require('./streams') , Logger = require('./logger') @@ -41,26 +41,17 @@ Storage.prototype._internal_error = function(err, file, message) { this.logger.error( {err: err, file: file} , message + ' @{file}: @{!err.message}' ) - return new UError({ - status: 500, - message: 'internal server error' - }) + return Error[500]() } Storage.prototype.add_package = function(name, info, callback) { var self = this var storage = this.storage(name) - if (!storage) return callback(new UError({ - status: 404, - message: 'this package cannot be added' - })) + if (!storage) return callback(Error[404]('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, - message: 'this package is already present' - })) + return callback(Error[409]('this package is already present')) } var latest = info['dist-tags'].latest @@ -76,17 +67,12 @@ Storage.prototype.remove_package = function(name, callback) { self.logger.info({name: name}, 'unpublishing @{name} (all)') var storage = self.storage(name) - if (!storage) return callback(new UError({ - status: 404, - message: 'no such package available', - })) + if (!storage) return callback(Error[404]('no such package available')) + storage.read_json(info_file, function(err, data) { if (err) { if (err.code === 'ENOENT') { - return callback(new UError({ - status: 404, - message: 'no such package available', - })) + return callback(Error[404]('no such package available')) } else { return callback(err) } @@ -222,10 +208,7 @@ Storage.prototype.add_version = function(name, version, metadata, tag, callback) delete metadata.readme if (data.versions[version] != null) { - return cb(new UError({ - status: 409, - message: 'this version already present' - })) + return cb(Error[409]('this version already present')) } // if uploaded tarball has a different shasum, it's very likely that we have some kind of error @@ -234,10 +217,9 @@ Storage.prototype.add_version = function(name, version, metadata, tag, callback) if (utils.is_object(data._attachments[tarball])) { if (data._attachments[tarball].shasum != null && metadata.dist.shasum != null) { if (data._attachments[tarball].shasum != metadata.dist.shasum) { - return cb(new UError({ - status: 400, - message: 'shasum error, ' + data._attachments[tarball].shasum + ' != ' + metadata.dist.shasum, - })) + return cb(Error[400]('shasum error, ' + + data._attachments[tarball].shasum + + ' != ' + metadata.dist.shasum)) } } @@ -258,10 +240,7 @@ Storage.prototype.add_tags = function(name, tags, callback) { self.update_package(name, function updater(data, cb) { for (var t in tags) { if (data.versions[tags[t]] == null) { - return cb(new UError({ - status: 404, - message: "this version doesn't exist" - })) + return cb(Error[404]("this version doesn't exist")) } utils.tag_version(data, tags[t], t, self.config) @@ -280,10 +259,7 @@ Storage.prototype.change_package = function(name, metadata, revision, callback) var self = this if (!utils.is_object(metadata.versions) || !utils.is_object(metadata['dist-tags'])) { - return callback(new UError({ - status: 422, - message: 'bad data', - })) + return callback(Error[422]('bad data')) } self.update_package(name, function updater(data, cb) { @@ -316,10 +292,7 @@ Storage.prototype.remove_tarball = function(name, filename, revision, callback) delete data._attachments[filename] cb() } else { - cb(new UError({ - status: 404, - message: 'no such file available', - })) + cb(Error[404]('no such file available')) } }, function(err) { if (err) return callback(err) @@ -347,10 +320,7 @@ Storage.prototype.add_tarball = function(name, filename) { var self = this if (name === info_file || name === '__proto__') { process.nextTick(function() { - stream.emit('error', new UError({ - status: 403, - message: 'can\'t use this filename' - })) + stream.emit('error', Error[403]("can't use this filename")) }) return stream } @@ -358,10 +328,7 @@ Storage.prototype.add_tarball = function(name, filename) { var storage = self.storage(name) if (!storage) { process.nextTick(function() { - stream.emit('error', new UError({ - status: 404, - message: 'can\'t upload this package' - })) + stream.emit('error', Error[404]("can't upload this package")) }) return stream } @@ -370,10 +337,7 @@ Storage.prototype.add_tarball = function(name, filename) { wstream.on('error', function(err) { if (err.code === 'EEXISTS') { - stream.emit('error', new UError({ - status: 409, - message: 'this tarball is already present' - })) + stream.emit('error', Error[409]('this tarball is already present')) } else if (err.code === 'ENOENT') { // check if package exists to throw an appropriate message self.get_package(name, function(_err, res) { @@ -411,10 +375,7 @@ Storage.prototype.add_tarball = function(name, filename) { } stream.done = function() { if (!length) { - stream.emit('error', new UError({ - status: 422, - message: 'refusing to accept zero-length file' - })) + stream.emit('error', Error[422]('refusing to accept zero-length file')) wstream.abort() } else { wstream.done() @@ -440,7 +401,6 @@ Storage.prototype.get_readme = function(name, version, callback) { if (exists) { returnReadme(); } else { -debugger self.unpack_tarball(fileName, function(err) { returnReadme(); }); @@ -455,7 +415,7 @@ debugger callback(data) }) } -}; +} Storage.prototype.get_tarball = function(name, filename, callback) { assert(utils.validate_name(filename)) @@ -469,10 +429,7 @@ Storage.prototype.get_tarball = function(name, filename, callback) { var storage = self.storage(name) if (!storage) { process.nextTick(function() { - stream.emit('error', new UError({ - status: 404, - message: 'no such file available', - })) + stream.emit('error', Error[404]('no such file available')) }) return stream } @@ -480,10 +437,7 @@ Storage.prototype.get_tarball = function(name, filename, callback) { var rstream = storage.read_stream(filename) rstream.on('error', function(err) { if (err && err.code === 'ENOENT') { - stream.emit('error', new UError({ - status: 404, - message: 'no such file available', - })) + stream.emit('error', Error(404, 'no such file available')) } else { stream.emit('error', err) } @@ -504,18 +458,12 @@ Storage.prototype.get_package = function(name, options, callback) { var self = this var storage = self.storage(name) - if (!storage) return callback(new UError({ - status: 404, - message: 'no such package available' - })) + if (!storage) return callback(Error[404]('no such package available')) storage.read_json(info_file, function(err, result) { if (err) { if (err.code === 'ENOENT') { - return callback(new UError({ - status: 404, - message: 'no such package available' - })) + return callback(Error[404]('no such package available')) } else { return callback(self._internal_error(err, info_file, 'error reading')) } @@ -575,10 +523,7 @@ Storage.prototype.get_recent_packages = function(startkey, callback) { Storage.prototype.update_package = function(name, updateFn, _callback) { var self = this var storage = self.storage(name) - if (!storage) return _callback(new UError({ - status: 404, - message: 'no such package available', - })) + if (!storage) return _callback(Error[404]('no such package available')) storage.lock_and_read_json(info_file, function(err, fd, json) { function callback() { var _args = arguments @@ -594,15 +539,9 @@ Storage.prototype.update_package = function(name, updateFn, _callback) { if (err) { if (err.code === 'EAGAIN') { - return callback(new UError({ - status: 503, - message: 'resource temporarily unavailable' - })) + return callback(Error[503]('resource temporarily unavailable')) } else if (err.code === 'ENOENT') { - return callback(new UError({ - status: 404, - message: 'no such package available', - })) + return callback(Error[404]('no such package available')) } else { return callback(err) } diff --git a/lib/logger.js b/lib/logger.js index 5a093be29..753085ae8 100644 --- a/lib/logger.js +++ b/lib/logger.js @@ -1,4 +1,5 @@ var Logger = require('bunyan') + , Error = require('http-errors') , Stream = require('stream') , utils = require('./utils') @@ -49,7 +50,7 @@ module.exports.setup = function(logs) { } } } else { - throw new Error('wrong target type for a log') + throw Error('wrong target type for a log') } if (target.level === 'http') target.level = 35 diff --git a/lib/middleware.js b/lib/middleware.js index 60339a0ba..2e47657e9 100644 --- a/lib/middleware.js +++ b/lib/middleware.js @@ -1,6 +1,6 @@ var crypto = require('crypto') + , Error = require('http-errors') , utils = require('./utils') - , UError = require('./error').UserError , Logger = require('./logger') module.exports.validate_name = function validate_name(req, res, next, value, name) { @@ -10,20 +10,15 @@ module.exports.validate_name = function validate_name(req, res, next, value, nam } else if (utils.validate_name(value)) { next() } else { - next(new UError({ - status: 403, - message: 'invalid ' + name, - })) + next(Error[403]('invalid ' + name)) } } module.exports.media = function media(expect) { return function(req, res, next) { if (req.headers['content-type'] !== expect) { - next(new UError({ - status: 415, - message: 'wrong content-type, expect: '+expect+', got: '+req.headers['content-type'], - })) + next(Error[415]('wrong content-type, expect: ' + expect + + ', got: '+req.headers['content-type'])) } else { next() } @@ -47,10 +42,7 @@ module.exports.anti_loop = function(config) { for (var i=0; i= 200 && res.statusCode < 300)) { - var error = new Error('bad status code: ' + res.statusCode) - error.status = res.statusCode + var error = Error('bad status code: ' + res.statusCode) + error.remoteStatus = res.statusCode return callback(error) } callback(null, body, res.headers.etag) @@ -299,16 +296,10 @@ Storage.prototype.get_url = function(url) { rstream.on('response', function(res) { if (res.statusCode === 404) { - return stream.emit('error', new UError({ - message: 'file doesn\'t exist on uplink', - status: 404, - })) + return stream.emit('error', Error[404]("file doesn't exist on uplink")) } if (!(res.statusCode >= 200 && res.statusCode < 300)) { - return stream.emit('error', new UError({ - message: 'bad uplink status code: ' + res.statusCode, - status: 500, - })) + return stream.emit('error', Error('bad uplink status code: ' + res.statusCode)) } if (res.headers['content-length']) { expected_length = res.headers['content-length'] @@ -327,7 +318,7 @@ Storage.prototype.get_url = function(url) { 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')) + stream.emit('error', Error('content length mismatch')) }) return stream } diff --git a/package.yaml b/package.yaml index b4205dabe..caca258f5 100644 --- a/package.yaml +++ b/package.yaml @@ -28,6 +28,7 @@ dependencies: cookies: '>=0.5.0 <1.0.0-0' request: '>=2.31.0 <3.0.0-0' async: '>=0.9.0 <1.0.0-0' + http-errors: '~1.2.0' # ferver # 2.x and 3.x have the same interface semver: '>=2.2.1 <4.0.0-0'