0
Fork 0
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:
Alex Kocharin 2013-11-24 21:07:18 +04:00
parent fecffa2a39
commit 13242c8237
4 changed files with 211 additions and 41 deletions

View file

@ -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)
})
})
})

View file

@ -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
View 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')
}

View file

@ -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