verdaccio/lib/up-storage.js

264 lines
6.1 KiB
JavaScript
Raw Normal View History

var URL = require('url')
, request = require('request')
, UError = require('./error').UserError
, mystreams = require('./streams')
, Logger = require('./logger')
, utils = require('./utils')
2013-06-08 03:16:28 +02:00
2013-09-25 11:12:33 +02:00
//
// Implements Storage interface
// (same for storage.js, local-storage.js, up-storage.js)
//
2013-06-19 18:58:16 +02:00
function Storage(config, mainconfig) {
2013-10-26 14:18:36 +02:00
if (!(this instanceof Storage)) return new Storage(config)
this.config = config
this.is_alive = false
this.userAgent = mainconfig.user_agent
this.ca = config.ca
this.logger = Logger.logger.child({sub: 'out'})
2013-06-08 03:16:28 +02:00
2013-10-26 14:18:36 +02:00
this.url = URL.parse(this.config.url)
2013-06-19 18:58:16 +02:00
if (this.url.hostname === 'registry.npmjs.org') {
2013-10-26 14:18:36 +02:00
this.ca = this.ca || require('./npmsslkeys')
2013-06-20 15:41:07 +02:00
// npm registry is too slow working with ssl :(
/*if (this.config._autogenerated) {
2013-06-19 18:58:16 +02:00
// encrypt all the things!
2013-10-26 14:18:36 +02:00
this.url.protocol = 'https'
this.config.url = URL.format(this.url)
2013-06-20 15:41:07 +02:00
}*/
2013-06-08 03:16:28 +02:00
}
2013-06-19 18:58:16 +02:00
2013-10-26 14:18:36 +02:00
this.config.url = this.config.url.replace(/\/$/, '')
return this
2013-06-08 03:16:28 +02:00
}
2013-09-28 19:31:58 +02:00
Storage.prototype.request = function(options, cb) {
2013-10-22 11:31:48 +02:00
var self = this
2013-10-26 14:18:36 +02:00
, headers = options.headers || {}
2013-10-22 11:31:48 +02:00
headers.accept = headers.accept || 'application/json'
headers['user-agent'] = headers['user-agent'] || this.userAgent
2013-10-22 11:31:48 +02:00
var method = options.method || 'GET'
2013-10-26 14:18:36 +02:00
, uri = options.uri_full || (this.config.url + options.uri)
self.logger.info({
method: method,
headers: headers,
uri: uri,
2013-10-26 14:18:36 +02:00
}, "making request: '@{method} @{uri}'")
if (utils.is_object(options.json)) {
2013-10-26 14:18:36 +02:00
var json = JSON.stringify(options.json)
headers['content-type'] = headers['content-type'] || 'application/json'
}
2013-09-28 19:31:58 +02:00
var req = request({
url: uri,
method: method,
2013-09-28 19:31:58 +02:00
headers: headers,
body: json,
2013-09-28 19:31:58 +02:00
ca: this.ca,
}, function(err, res, body) {
2013-10-22 11:31:48 +02:00
var error
if (!err) {
2013-10-26 14:18:36 +02:00
var res_length = body.length
2013-10-22 11:31:48 +02:00
if (options.json && res.statusCode < 300) {
try {
2013-10-26 14:18:36 +02:00
body = JSON.parse(body)
2013-10-22 11:31:48 +02:00
} catch(_err) {
body = {}
err = _err
error = err.message
}
}
2013-10-22 11:31:48 +02:00
if (!err && utils.is_object(body)) {
if (body.error) {
2013-10-26 14:18:36 +02:00
error = body.error
}
}
} else {
2013-10-26 14:18:36 +02:00
error = err.message
}
2013-10-26 14:18:36 +02:00
var msg = '@{!status}, req: \'@{request.method} @{request.url}\''
if (error) {
2013-10-26 14:18:36 +02:00
msg += ', error: @{!error}'
} else {
2013-10-26 14:18:36 +02:00
msg += ', bytes: @{bytes.in}/@{bytes.out}'
}
self.logger.warn({
err: err,
request: {method: method, url: uri},
level: 35, // http
status: res != null ? res.statusCode : 'ERR',
error: error,
bytes: {
in: json ? json.length : 0,
out: res_length || 0,
}
2013-10-26 14:18:36 +02:00
}, msg)
if (cb) cb.apply(self, arguments)
})
req.on('response', function(res) {
2013-10-26 14:18:36 +02:00
self.status_check(true)
})
2013-09-28 19:31:58 +02:00
req.on('error', function() {
2013-10-26 14:18:36 +02:00
self.status_check(false)
})
return req
2013-09-28 19:31:58 +02:00
}
Storage.prototype.status_check = function(alive) {
if (arguments.length === 0) {
if (!this.is_alive && Math.abs(Date.now() - this.is_alive_time()) > 60*1000) {
2013-10-26 14:18:36 +02:00
return false
2013-09-28 19:31:58 +02:00
} else {
2013-10-26 14:18:36 +02:00
return true
2013-09-28 19:31:58 +02:00
}
} else {
2013-10-26 14:18:36 +02:00
this.is_alive = alive
this.is_alive_time = Date.now()
2013-09-28 19:31:58 +02:00
}
}
2013-06-19 18:58:16 +02:00
Storage.prototype.can_fetch_url = function(url) {
2013-10-26 14:18:36 +02:00
url = URL.parse(url)
2013-06-19 18:58:16 +02:00
return url.protocol === this.url.protocol
&& url.host === this.url.host
2013-06-20 15:41:07 +02:00
&& url.path.indexOf(this.url.path) === 0
2013-06-19 18:58:16 +02:00
}
2013-09-25 11:18:38 +02:00
Storage.prototype.add_package = function(name, metadata, callback) {
2013-09-28 19:31:58 +02:00
this.request({
uri: '/' + escape(name),
method: 'PUT',
json: metadata,
}, function(err, res, body) {
2013-10-26 14:18:36 +02:00
if (err) return callback(err)
if (!(res.statusCode >= 200 && res.statusCode < 300)) {
2013-10-26 14:18:36 +02:00
return callback(new Error('bad status code: ' + res.statusCode))
}
2013-10-26 14:18:36 +02:00
callback(null, body)
})
2013-09-25 11:18:38 +02:00
}
Storage.prototype.add_version = function(name, version, metadata, tag, callback) {
2013-09-28 19:31:58 +02:00
this.request({
uri: '/' + escape(name) + '/' + escape(version) + '/-tag/' + escape(tag),
method: 'PUT',
json: metadata,
}, function(err, res, body) {
2013-10-26 14:18:36 +02:00
if (err) return callback(err)
if (!(res.statusCode >= 200 && res.statusCode < 300)) {
2013-10-26 14:18:36 +02:00
return callback(new Error('bad status code: ' + res.statusCode))
}
2013-10-26 14:18:36 +02:00
callback(null, body)
})
2013-09-25 11:18:38 +02:00
}
Storage.prototype.add_tarball = function(name, filename) {
2013-10-26 14:18:36 +02:00
var stream = new mystreams.UploadTarballStream()
, self = this
2013-09-28 14:19:40 +02:00
2013-09-28 19:31:58 +02:00
var wstream = this.request({
uri: '/' + escape(name) + '/-/' + escape(filename) + '/whatever',
2013-09-28 14:19:40 +02:00
method: 'PUT',
headers: {
'content-type': 'application/octet-stream'
},
2013-10-26 14:18:36 +02:00
})
2013-09-28 14:19:40 +02:00
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,
2013-10-26 14:18:36 +02:00
}))
2013-09-28 14:19:40 +02:00
}
2013-10-26 14:18:36 +02:00
stream.emit('success')
})
2013-09-28 14:19:40 +02:00
wstream.on('error', function(err) {
2013-10-26 14:18:36 +02:00
stream.emit('error', err)
})
2013-09-28 14:19:40 +02:00
stream.abort = function() {
2013-09-28 14:37:24 +02:00
process.nextTick(function() {
if (wstream.req) {
2013-10-26 14:18:36 +02:00
wstream.req.abort()
2013-09-28 14:37:24 +02:00
}
2013-10-26 14:18:36 +02:00
})
}
stream.done = function() {}
stream.pipe(wstream)
2013-09-28 14:19:40 +02:00
2013-10-26 14:18:36 +02:00
return stream
2013-09-25 11:18:38 +02:00
}
2013-10-22 11:31:48 +02:00
Storage.prototype.get_package = function(name, etag, callback) {
if (etag) {
var headers = {
'if-none-match': etag
}
}
2013-09-28 19:31:58 +02:00
this.request({
uri: '/' + escape(name),
2013-06-08 03:16:28 +02:00
json: true,
2013-10-22 11:31:48 +02:00
headers: headers,
2013-06-08 03:16:28 +02:00
}, function(err, res, body) {
2013-10-26 14:18:36 +02:00
if (err) return callback(err)
2013-06-14 10:34:29 +02:00
if (res.statusCode === 404) {
return callback(new UError({
msg: 'package doesn\'t exist on uplink',
status: 404,
2013-10-26 14:18:36 +02:00
}))
2013-06-14 10:34:29 +02:00
}
2013-06-14 09:56:02 +02:00
if (!(res.statusCode >= 200 && res.statusCode < 300)) {
2013-10-26 14:18:36 +02:00
return callback(new Error('bad status code: ' + res.statusCode))
2013-06-14 09:56:02 +02:00
}
2013-10-26 14:18:36 +02:00
callback(null, body, res.headers.etag)
})
2013-06-08 03:16:28 +02:00
}
2013-06-20 15:41:07 +02:00
Storage.prototype.get_tarball = function(name, filename) {
2013-10-26 14:18:36 +02:00
return this.get_url(this.config.url + '/' + name + '/-/' + filename)
2013-06-20 15:41:07 +02:00
}
Storage.prototype.get_url = function(url) {
2013-10-26 14:18:36 +02:00
var stream = new mystreams.ReadTarballStream()
stream.abort = function() {}
2013-06-20 15:41:07 +02:00
2013-09-28 19:31:58 +02:00
var rstream = this.request({
uri_full: url,
2013-06-19 18:58:16 +02:00
encoding: null,
2013-10-26 14:18:36 +02:00
})
2013-06-20 15:41:07 +02:00
rstream.on('response', function(res) {
2013-06-19 18:58:16 +02:00
if (res.statusCode === 404) {
2013-06-20 15:41:07 +02:00
return stream.emit('error', new UError({
2013-06-19 18:58:16 +02:00
msg: 'file doesn\'t exist on uplink',
status: 404,
2013-10-26 14:18:36 +02:00
}))
2013-06-19 18:58:16 +02:00
}
if (!(res.statusCode >= 200 && res.statusCode < 300)) {
2013-06-20 15:41:07 +02:00
return stream.emit('error', new UError({
msg: 'bad uplink status code: ' + res.statusCode,
status: 500,
2013-10-26 14:18:36 +02:00
}))
2013-06-19 18:58:16 +02:00
}
2013-06-20 15:41:07 +02:00
2013-10-26 14:18:36 +02:00
rstream.pipe(stream)
})
2013-06-20 15:41:07 +02:00
rstream.on('error', function(err) {
2013-10-26 14:18:36 +02:00
stream.emit('error', err)
})
return stream
2013-06-19 18:58:16 +02:00
}
2013-10-26 14:18:36 +02:00
module.exports = Storage
2013-06-08 03:16:28 +02:00