mirror of
https://github.com/verdaccio/verdaccio.git
synced 2025-02-17 23:45:29 -05:00
add proxy support, fix #13
This commit is contained in:
parent
fecffa2a39
commit
13242c8237
4 changed files with 211 additions and 41 deletions
116
lib/storage.js
116
lib/storage.js
|
@ -5,6 +5,7 @@ var async = require('async')
|
|||
, Proxy = require('./up-storage')
|
||||
, mystreams = require('./streams')
|
||||
, utils = require('./utils')
|
||||
, transaction = require('./transaction')
|
||||
|
||||
//
|
||||
// Implements Storage interface
|
||||
|
@ -45,57 +46,89 @@ function Storage(config) {
|
|||
Storage.prototype.add_package = function(name, metadata, callback) {
|
||||
var self = this
|
||||
|
||||
var uplinks = []
|
||||
for (var i in self.uplinks) {
|
||||
if (self.config.proxy_access(name, i)) {
|
||||
uplinks.push(self.uplinks[i])
|
||||
}
|
||||
}
|
||||
// NOTE:
|
||||
// - when we checking package for existance, we ask ALL uplinks
|
||||
// - when we publishing package, we only publish it to some of them
|
||||
// so all requests are necessary
|
||||
|
||||
async.map(uplinks, function(up, cb) {
|
||||
up.get_package(name, null, function(err, res) {
|
||||
cb(null, [err, res])
|
||||
check_package(function(err) {
|
||||
if (err) return callback(err)
|
||||
|
||||
publish_package(function(err) {
|
||||
if (err) return callback(err)
|
||||
callback()
|
||||
})
|
||||
}, function(err, results) {
|
||||
for (var i=0; i<results.length; i++) {
|
||||
// checking error
|
||||
// if uplink fails with a status other than 404, we report failure
|
||||
if (results[i][0] != null) {
|
||||
if (results[i][0].status !== 404) {
|
||||
return callback(new UError({
|
||||
status: 503,
|
||||
msg: 'one of the uplinks is down, refuse to publish'
|
||||
}))
|
||||
})
|
||||
|
||||
function check_package(cb) {
|
||||
self.get_package(name, function(err, results, err_results) {
|
||||
// something weird
|
||||
if (err && err.status !== 404) return cb(err)
|
||||
|
||||
for (var i=0; i<err_results.length; i++) {
|
||||
// checking error
|
||||
// if uplink fails with a status other than 404, we report failure
|
||||
if (err_results[i][0] != null) {
|
||||
if (err_results[i][0].status !== 404) {
|
||||
return cb(new UError({
|
||||
status: 503,
|
||||
msg: 'one of the uplinks is down, refuse to publish'
|
||||
}))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// checking package
|
||||
if (results[i][1] != null) {
|
||||
return callback(new UError({
|
||||
if (results) {
|
||||
return cb(new UError({
|
||||
status: 409,
|
||||
msg: 'this package is already present'
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
uplinks = []
|
||||
return cb()
|
||||
})
|
||||
}
|
||||
|
||||
function publish_package(cb) {
|
||||
var fw_uplinks = []
|
||||
for (var i in self.uplinks) {
|
||||
if (self.config.proxy_publish(name, i)) {
|
||||
uplinks.push(self.uplinks[i])
|
||||
fw_uplinks.push(self.uplinks[i])
|
||||
}
|
||||
}
|
||||
async.map(uplinks, function(up, cb) {
|
||||
up.add_package(name, metadata, cb)
|
||||
}, function(err, results) {
|
||||
if (err) {
|
||||
return callback(new UError({
|
||||
status: 503,
|
||||
msg: 'can\'t upload to one of the uplinks, refuse to publish'
|
||||
}))
|
||||
|
||||
transaction(
|
||||
fw_uplinks,
|
||||
function localAction(cb) {
|
||||
self.local.add_package(name, metadata, cb)
|
||||
},
|
||||
function localRollback(cb) {
|
||||
self.local.remove_package(name, cb)
|
||||
},
|
||||
function remoteAction(remote, cb) {
|
||||
remote.add_package(name, metadata, cb)
|
||||
},
|
||||
function remoteRollback(remote, cb) {
|
||||
remote.remove_package(name, cb)
|
||||
},
|
||||
function(err) {
|
||||
if (!err) {
|
||||
callback()
|
||||
|
||||
} else if (err.uplink === 'local') {
|
||||
return callback(err)
|
||||
|
||||
} else {
|
||||
// hide uplink error with general message
|
||||
return callback(new UError({
|
||||
status: 503,
|
||||
msg: 'can\'t upload to one of the uplinks, refuse to publish'
|
||||
}))
|
||||
}
|
||||
}
|
||||
self.local.add_package(name, metadata, callback)
|
||||
})
|
||||
})
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
|
@ -351,6 +384,9 @@ Storage.prototype.get_tarball = function(name, filename) {
|
|||
// Used storages: local && uplink (proxy_access)
|
||||
//
|
||||
Storage.prototype.get_package = function(name, callback) {
|
||||
// NOTE: callback(err, result, _uplink_errors)
|
||||
// _uplink_errors is an array of errors used internally
|
||||
// XXX: move it to another function maybe?
|
||||
var self = this
|
||||
|
||||
self.local.get_package(name, function(err, data) {
|
||||
|
@ -381,12 +417,12 @@ Storage.prototype.get_package = function(name, callback) {
|
|||
oldetag = result._uplinks[up.upname].etag
|
||||
|
||||
up.get_package(name, oldetag, function(err, up_res, etag) {
|
||||
if (err || !up_res) return cb()
|
||||
if (err || !up_res) return cb(null, [err || new Error('no data')])
|
||||
|
||||
try {
|
||||
utils.validate_metadata(up_res, name)
|
||||
} catch(err) {
|
||||
return cb()
|
||||
return cb(null, [err])
|
||||
}
|
||||
|
||||
result._uplinks[up.upname] = {
|
||||
|
@ -413,13 +449,13 @@ Storage.prototype.get_package = function(name, callback) {
|
|||
exists = true
|
||||
cb()
|
||||
})
|
||||
}, function(err) {
|
||||
}, function(err, uplink_errors) {
|
||||
if (err) return callback(err)
|
||||
if (!exists) {
|
||||
return callback(new UError({
|
||||
status: 404,
|
||||
msg: 'no such package available'
|
||||
}))
|
||||
}), null, uplink_errors)
|
||||
}
|
||||
|
||||
self.local.update_versions(name, result, function(err) {
|
||||
|
@ -429,7 +465,7 @@ Storage.prototype.get_package = function(name, callback) {
|
|||
for (var i in result) {
|
||||
if (!~whitelist.indexOf(i)) delete result[i]
|
||||
}
|
||||
callback(null, result)
|
||||
callback(null, result, uplink_errors)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
|
@ -29,10 +29,56 @@ function Storage(config, mainconfig) {
|
|||
}*/
|
||||
}
|
||||
|
||||
_setupProxy.call(this, this.url.hostname, config, mainconfig, this.url.protocol === 'https:')
|
||||
|
||||
this.config.url = this.config.url.replace(/\/$/, '')
|
||||
return this
|
||||
}
|
||||
|
||||
function _setupProxy(hostname, config, mainconfig, isHTTPS) {
|
||||
debugger;
|
||||
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<no_proxy.length; i++) {
|
||||
var no_proxy_item = no_proxy[i]
|
||||
if (no_proxy_item[0] !== '.') no_proxy_item = '.' + no_proxy_item
|
||||
if (hostname.lastIndexOf(no_proxy_item) === hostname.length - no_proxy_item.length) {
|
||||
if (this.proxy) {
|
||||
this.logger.debug({url: this.url.href, rule: no_proxy_item}, 'not using proxy for @{url}, excluded by @{rule} rule')
|
||||
this.proxy = false
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// if it's non-string (i.e. "false"), don't use it
|
||||
if (typeof(this.proxy) !== 'string') {
|
||||
delete this.proxy
|
||||
} else {
|
||||
this.logger.debug({url: this.url.href, proxy: this.proxy}, 'using proxy @{proxy} for @{url}')
|
||||
}
|
||||
}
|
||||
|
||||
Storage.prototype.request = function(options, cb) {
|
||||
var self = this
|
||||
, headers = options.headers || {}
|
||||
|
@ -58,6 +104,7 @@ Storage.prototype.request = function(options, cb) {
|
|||
headers: headers,
|
||||
body: json,
|
||||
ca: this.ca,
|
||||
proxy: this.proxy,
|
||||
}, function(err, res, body) {
|
||||
var error
|
||||
if (!err) {
|
||||
|
|
87
test/no_proxy.js
Normal file
87
test/no_proxy.js
Normal file
|
@ -0,0 +1,87 @@
|
|||
var assert = require('assert')
|
||||
, Storage = require('../lib/up-storage')
|
||||
|
||||
require('../lib/logger').setup()
|
||||
|
||||
function setup(host, config, mainconfig) {
|
||||
config.url = host
|
||||
return new Storage(config, mainconfig)
|
||||
}
|
||||
|
||||
exports['should work fine without proxy'] = function() {
|
||||
var x = setup('http://x/x', {}, {})
|
||||
assert.equal(x.proxy, null)
|
||||
}
|
||||
|
||||
exports['local config should take priority'] = function() {
|
||||
var x = setup('http://x/x', {http_proxy: '123'}, {http_proxy: '456'})
|
||||
assert.equal(x.proxy, '123')
|
||||
}
|
||||
|
||||
exports['no_proxy is invalid'] = function() {
|
||||
var x = setup('http://x/x', {http_proxy: '123', no_proxy: false}, {})
|
||||
assert.equal(x.proxy, '123')
|
||||
var x = setup('http://x/x', {http_proxy: '123', no_proxy: null}, {})
|
||||
assert.equal(x.proxy, '123')
|
||||
var x = setup('http://x/x', {http_proxy: '123', no_proxy: []}, {})
|
||||
assert.equal(x.proxy, '123')
|
||||
var x = setup('http://x/x', {http_proxy: '123', no_proxy: ''}, {})
|
||||
assert.equal(x.proxy, '123')
|
||||
}
|
||||
|
||||
exports['no_proxy - simple/include'] = function() {
|
||||
var x = setup('http://localhost', {http_proxy: '123'}, {no_proxy: 'localhost'})
|
||||
assert.equal(x.proxy, undefined)
|
||||
}
|
||||
|
||||
exports['no_proxy - simple/not'] = function() {
|
||||
var x = setup('http://localhost', {http_proxy: '123'}, {no_proxy: 'blah'})
|
||||
assert.equal(x.proxy, '123')
|
||||
}
|
||||
|
||||
exports['no_proxy - various, single string'] = function() {
|
||||
var x = setup('http://blahblah', {http_proxy: '123'}, {no_proxy: 'blah'})
|
||||
assert.equal(x.proxy, '123')
|
||||
var x = setup('http://blah.blah', {}, {http_proxy: '123', no_proxy: 'blah'})
|
||||
assert.equal(x.proxy, null)
|
||||
var x = setup('http://blahblah', {}, {http_proxy: '123', no_proxy: '.blah'})
|
||||
assert.equal(x.proxy, '123')
|
||||
var x = setup('http://blah.blah', {http_proxy: '123', no_proxy: '.blah'}, {})
|
||||
assert.equal(x.proxy, null)
|
||||
var x = setup('http://blah', {http_proxy: '123', no_proxy: '.blah'}, {})
|
||||
assert.equal(x.proxy, null)
|
||||
var x = setup('http://blahh', {http_proxy: '123', no_proxy: 'blah'}, {})
|
||||
assert.equal(x.proxy, '123')
|
||||
}
|
||||
|
||||
exports['no_proxy - various, array'] = function() {
|
||||
var x = setup('http://blahblah', {http_proxy: '123'}, {no_proxy: 'foo,bar,blah'})
|
||||
assert.equal(x.proxy, '123')
|
||||
var x = setup('http://blah.blah', {http_proxy: '123'}, {no_proxy: 'foo,bar,blah'})
|
||||
assert.equal(x.proxy, null)
|
||||
var x = setup('http://blah.foo', {http_proxy: '123'}, {no_proxy: 'foo,bar,blah'})
|
||||
assert.equal(x.proxy, null)
|
||||
var x = setup('http://foo.baz', {http_proxy: '123'}, {no_proxy: 'foo,bar,blah'})
|
||||
assert.equal(x.proxy, '123')
|
||||
var x = setup('http://blahblah', {http_proxy: '123'}, {no_proxy: ['foo','bar','blah']})
|
||||
assert.equal(x.proxy, '123')
|
||||
var x = setup('http://blah.blah', {http_proxy: '123'}, {no_proxy: ['foo','bar','blah']})
|
||||
assert.equal(x.proxy, null)
|
||||
}
|
||||
|
||||
exports['no_proxy - hostport'] = function() {
|
||||
var x = setup('http://localhost:80', {http_proxy: '123'}, {no_proxy: 'localhost'})
|
||||
assert.equal(x.proxy, null)
|
||||
var x = setup('http://localhost:8080', {http_proxy: '123'}, {no_proxy: 'localhost'})
|
||||
assert.equal(x.proxy, null)
|
||||
}
|
||||
|
||||
exports['no_proxy - secure'] = function() {
|
||||
var x = setup('https://something', {http_proxy: '123'}, {})
|
||||
assert.equal(x.proxy, null)
|
||||
var x = setup('https://something', {https_proxy: '123'}, {})
|
||||
assert.equal(x.proxy, '123')
|
||||
var x = setup('https://something', {http_proxy: '456', https_proxy: '123'}, {})
|
||||
assert.equal(x.proxy, '123')
|
||||
}
|
||||
|
|
@ -4,6 +4,6 @@ CWD=$(pwd)
|
|||
PATH='../node_modules/.bin':$PATH
|
||||
TESTDIR=$(dirname $0)
|
||||
cd $TESTDIR
|
||||
mocha -R list --ui exports ./tests.js
|
||||
mocha -R list --ui exports ./tests.js ./no_proxy.js
|
||||
cd $CWD
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue