diff options
Diffstat (limited to 'vnfmarket/src/main/webapp/vnfmarket/node_modules/send/index.js')
-rw-r--r-- | vnfmarket/src/main/webapp/vnfmarket/node_modules/send/index.js | 829 |
1 files changed, 829 insertions, 0 deletions
diff --git a/vnfmarket/src/main/webapp/vnfmarket/node_modules/send/index.js b/vnfmarket/src/main/webapp/vnfmarket/node_modules/send/index.js new file mode 100644 index 00000000..a4e5af85 --- /dev/null +++ b/vnfmarket/src/main/webapp/vnfmarket/node_modules/send/index.js @@ -0,0 +1,829 @@ +/*! + * send + * Copyright(c) 2012 TJ Holowaychuk + * Copyright(c) 2014-2015 Douglas Christopher Wilson + * MIT Licensed + */ + +'use strict' + +/** + * Module dependencies. + * @private + */ + +var createError = require('http-errors') +var debug = require('debug')('send') +var deprecate = require('depd')('send') +var destroy = require('destroy') +var escapeHtml = require('escape-html') + , parseRange = require('range-parser') + , Stream = require('stream') + , mime = require('mime') + , fresh = require('fresh') + , path = require('path') + , fs = require('fs') + , normalize = path.normalize + , join = path.join +var etag = require('etag') +var EventEmitter = require('events').EventEmitter; +var ms = require('ms'); +var onFinished = require('on-finished') +var statuses = require('statuses') + +/** + * Variables. + */ +var extname = path.extname +var maxMaxAge = 60 * 60 * 24 * 365 * 1000; // 1 year +var resolve = path.resolve +var sep = path.sep +var toString = Object.prototype.toString +var upPathRegexp = /(?:^|[\\\/])\.\.(?:[\\\/]|$)/ + +/** + * Module exports. + * @public + */ + +module.exports = send +module.exports.mime = mime + +/** + * Shim EventEmitter.listenerCount for node.js < 0.10 + */ + +/* istanbul ignore next */ +var listenerCount = EventEmitter.listenerCount + || function(emitter, type){ return emitter.listeners(type).length; }; + +/** + * Return a `SendStream` for `req` and `path`. + * + * @param {object} req + * @param {string} path + * @param {object} [options] + * @return {SendStream} + * @public + */ + +function send(req, path, options) { + return new SendStream(req, path, options); +} + +/** + * Initialize a `SendStream` with the given `path`. + * + * @param {Request} req + * @param {String} path + * @param {object} [options] + * @private + */ + +function SendStream(req, path, options) { + var opts = options || {} + + this.options = opts + this.path = path + this.req = req + + this._etag = opts.etag !== undefined + ? Boolean(opts.etag) + : true + + this._dotfiles = opts.dotfiles !== undefined + ? opts.dotfiles + : 'ignore' + + if (this._dotfiles !== 'ignore' && this._dotfiles !== 'allow' && this._dotfiles !== 'deny') { + throw new TypeError('dotfiles option must be "allow", "deny", or "ignore"') + } + + this._hidden = Boolean(opts.hidden) + + if (opts.hidden !== undefined) { + deprecate('hidden: use dotfiles: \'' + (this._hidden ? 'allow' : 'ignore') + '\' instead') + } + + // legacy support + if (opts.dotfiles === undefined) { + this._dotfiles = undefined + } + + this._extensions = opts.extensions !== undefined + ? normalizeList(opts.extensions, 'extensions option') + : [] + + this._index = opts.index !== undefined + ? normalizeList(opts.index, 'index option') + : ['index.html'] + + this._lastModified = opts.lastModified !== undefined + ? Boolean(opts.lastModified) + : true + + this._maxage = opts.maxAge || opts.maxage + this._maxage = typeof this._maxage === 'string' + ? ms(this._maxage) + : Number(this._maxage) + this._maxage = !isNaN(this._maxage) + ? Math.min(Math.max(0, this._maxage), maxMaxAge) + : 0 + + this._root = opts.root + ? resolve(opts.root) + : null + + if (!this._root && opts.from) { + this.from(opts.from) + } +} + +/** + * Inherits from `Stream.prototype`. + */ + +SendStream.prototype.__proto__ = Stream.prototype; + +/** + * Enable or disable etag generation. + * + * @param {Boolean} val + * @return {SendStream} + * @api public + */ + +SendStream.prototype.etag = deprecate.function(function etag(val) { + val = Boolean(val); + debug('etag %s', val); + this._etag = val; + return this; +}, 'send.etag: pass etag as option'); + +/** + * Enable or disable "hidden" (dot) files. + * + * @param {Boolean} path + * @return {SendStream} + * @api public + */ + +SendStream.prototype.hidden = deprecate.function(function hidden(val) { + val = Boolean(val); + debug('hidden %s', val); + this._hidden = val; + this._dotfiles = undefined + return this; +}, 'send.hidden: use dotfiles option'); + +/** + * Set index `paths`, set to a falsy + * value to disable index support. + * + * @param {String|Boolean|Array} paths + * @return {SendStream} + * @api public + */ + +SendStream.prototype.index = deprecate.function(function index(paths) { + var index = !paths ? [] : normalizeList(paths, 'paths argument'); + debug('index %o', paths); + this._index = index; + return this; +}, 'send.index: pass index as option'); + +/** + * Set root `path`. + * + * @param {String} path + * @return {SendStream} + * @api public + */ + +SendStream.prototype.root = function(path){ + path = String(path); + this._root = resolve(path) + return this; +}; + +SendStream.prototype.from = deprecate.function(SendStream.prototype.root, + 'send.from: pass root as option'); + +SendStream.prototype.root = deprecate.function(SendStream.prototype.root, + 'send.root: pass root as option'); + +/** + * Set max-age to `maxAge`. + * + * @param {Number} maxAge + * @return {SendStream} + * @api public + */ + +SendStream.prototype.maxage = deprecate.function(function maxage(maxAge) { + maxAge = typeof maxAge === 'string' + ? ms(maxAge) + : Number(maxAge); + if (isNaN(maxAge)) maxAge = 0; + if (Infinity == maxAge) maxAge = 60 * 60 * 24 * 365 * 1000; + debug('max-age %d', maxAge); + this._maxage = maxAge; + return this; +}, 'send.maxage: pass maxAge as option'); + +/** + * Emit error with `status`. + * + * @param {number} status + * @param {Error} [error] + * @private + */ + +SendStream.prototype.error = function error(status, error) { + // emit if listeners instead of responding + if (listenerCount(this, 'error') !== 0) { + return this.emit('error', createError(error, status, { + expose: false + })) + } + + var res = this.res + var msg = statuses[status] + + // wipe all existing headers + res._headers = null + + // send basic response + res.statusCode = status + res.setHeader('Content-Type', 'text/plain; charset=UTF-8') + res.setHeader('Content-Length', Buffer.byteLength(msg)) + res.setHeader('X-Content-Type-Options', 'nosniff') + res.end(msg) +} + +/** + * Check if the pathname ends with "/". + * + * @return {Boolean} + * @api private + */ + +SendStream.prototype.hasTrailingSlash = function(){ + return '/' == this.path[this.path.length - 1]; +}; + +/** + * Check if this is a conditional GET request. + * + * @return {Boolean} + * @api private + */ + +SendStream.prototype.isConditionalGET = function(){ + return this.req.headers['if-none-match'] + || this.req.headers['if-modified-since']; +}; + +/** + * Strip content-* header fields. + * + * @private + */ + +SendStream.prototype.removeContentHeaderFields = function removeContentHeaderFields() { + var res = this.res + var headers = Object.keys(res._headers || {}) + + for (var i = 0; i < headers.length; i++) { + var header = headers[i] + if (header.substr(0, 8) === 'content-' && header !== 'content-location') { + res.removeHeader(header) + } + } +} + +/** + * Respond with 304 not modified. + * + * @api private + */ + +SendStream.prototype.notModified = function(){ + var res = this.res; + debug('not modified'); + this.removeContentHeaderFields(); + res.statusCode = 304; + res.end(); +}; + +/** + * Raise error that headers already sent. + * + * @api private + */ + +SendStream.prototype.headersAlreadySent = function headersAlreadySent(){ + var err = new Error('Can\'t set headers after they are sent.'); + debug('headers already sent'); + this.error(500, err); +}; + +/** + * Check if the request is cacheable, aka + * responded with 2xx or 304 (see RFC 2616 section 14.2{5,6}). + * + * @return {Boolean} + * @api private + */ + +SendStream.prototype.isCachable = function(){ + var res = this.res; + return (res.statusCode >= 200 && res.statusCode < 300) || 304 == res.statusCode; +}; + +/** + * Handle stat() error. + * + * @param {Error} error + * @private + */ + +SendStream.prototype.onStatError = function onStatError(error) { + switch (error.code) { + case 'ENAMETOOLONG': + case 'ENOENT': + case 'ENOTDIR': + this.error(404, error) + break + default: + this.error(500, error) + break + } +} + +/** + * Check if the cache is fresh. + * + * @return {Boolean} + * @api private + */ + +SendStream.prototype.isFresh = function(){ + return fresh(this.req.headers, this.res._headers); +}; + +/** + * Check if the range is fresh. + * + * @return {Boolean} + * @api private + */ + +SendStream.prototype.isRangeFresh = function isRangeFresh(){ + var ifRange = this.req.headers['if-range']; + + if (!ifRange) return true; + + return ~ifRange.indexOf('"') + ? ~ifRange.indexOf(this.res._headers['etag']) + : Date.parse(this.res._headers['last-modified']) <= Date.parse(ifRange); +}; + +/** + * Redirect to path. + * + * @param {string} path + * @private + */ + +SendStream.prototype.redirect = function redirect(path) { + if (listenerCount(this, 'directory') !== 0) { + this.emit('directory') + return + } + + if (this.hasTrailingSlash()) { + this.error(403) + return + } + + var loc = path + '/' + var msg = 'Redirecting to <a href="' + escapeHtml(loc) + '">' + escapeHtml(loc) + '</a>\n' + var res = this.res + + // redirect + res.statusCode = 301 + res.setHeader('Content-Type', 'text/html; charset=UTF-8') + res.setHeader('Content-Length', Buffer.byteLength(msg)) + res.setHeader('X-Content-Type-Options', 'nosniff') + res.setHeader('Location', loc) + res.end(msg) +} + +/** + * Pipe to `res. + * + * @param {Stream} res + * @return {Stream} res + * @api public + */ + +SendStream.prototype.pipe = function(res){ + var self = this + , args = arguments + , root = this._root; + + // references + this.res = res; + + // decode the path + var path = decode(this.path) + if (path === -1) return this.error(400) + + // null byte(s) + if (~path.indexOf('\0')) return this.error(400); + + var parts + if (root !== null) { + // malicious path + if (upPathRegexp.test(normalize('.' + sep + path))) { + debug('malicious path "%s"', path) + return this.error(403) + } + + // join / normalize from optional root dir + path = normalize(join(root, path)) + root = normalize(root + sep) + + // explode path parts + parts = path.substr(root.length).split(sep) + } else { + // ".." is malicious without "root" + if (upPathRegexp.test(path)) { + debug('malicious path "%s"', path) + return this.error(403) + } + + // explode path parts + parts = normalize(path).split(sep) + + // resolve the path + path = resolve(path) + } + + // dotfile handling + if (containsDotFile(parts)) { + var access = this._dotfiles + + // legacy support + if (access === undefined) { + access = parts[parts.length - 1][0] === '.' + ? (this._hidden ? 'allow' : 'ignore') + : 'allow' + } + + debug('%s dotfile "%s"', access, path) + switch (access) { + case 'allow': + break + case 'deny': + return this.error(403) + case 'ignore': + default: + return this.error(404) + } + } + + // index file support + if (this._index.length && this.path[this.path.length - 1] === '/') { + this.sendIndex(path); + return res; + } + + this.sendFile(path); + return res; +}; + +/** + * Transfer `path`. + * + * @param {String} path + * @api public + */ + +SendStream.prototype.send = function(path, stat){ + var len = stat.size; + var options = this.options + var opts = {} + var res = this.res; + var req = this.req; + var ranges = req.headers.range; + var offset = options.start || 0; + + if (res._header) { + // impossible to send now + return this.headersAlreadySent(); + } + + debug('pipe "%s"', path) + + // set header fields + this.setHeader(path, stat); + + // set content-type + this.type(path); + + // conditional GET support + if (this.isConditionalGET() + && this.isCachable() + && this.isFresh()) { + return this.notModified(); + } + + // adjust len to start/end options + len = Math.max(0, len - offset); + if (options.end !== undefined) { + var bytes = options.end - offset + 1; + if (len > bytes) len = bytes; + } + + // Range support + if (ranges) { + ranges = parseRange(len, ranges); + + // If-Range support + if (!this.isRangeFresh()) { + debug('range stale'); + ranges = -2; + } + + // unsatisfiable + if (-1 == ranges) { + debug('range unsatisfiable'); + res.setHeader('Content-Range', 'bytes */' + stat.size); + return this.error(416); + } + + // valid (syntactically invalid/multiple ranges are treated as a regular response) + if (-2 != ranges && ranges.length === 1) { + debug('range %j', ranges); + + // Content-Range + res.statusCode = 206; + res.setHeader('Content-Range', 'bytes ' + + ranges[0].start + + '-' + + ranges[0].end + + '/' + + len); + + offset += ranges[0].start; + len = ranges[0].end - ranges[0].start + 1; + } + } + + // clone options + for (var prop in options) { + opts[prop] = options[prop] + } + + // set read options + opts.start = offset + opts.end = Math.max(offset, offset + len - 1) + + // content-length + res.setHeader('Content-Length', len); + + // HEAD support + if ('HEAD' == req.method) return res.end(); + + this.stream(path, opts) +}; + +/** + * Transfer file for `path`. + * + * @param {String} path + * @api private + */ +SendStream.prototype.sendFile = function sendFile(path) { + var i = 0 + var self = this + + debug('stat "%s"', path); + fs.stat(path, function onstat(err, stat) { + if (err && err.code === 'ENOENT' + && !extname(path) + && path[path.length - 1] !== sep) { + // not found, check extensions + return next(err) + } + if (err) return self.onStatError(err) + if (stat.isDirectory()) return self.redirect(self.path) + self.emit('file', path, stat) + self.send(path, stat) + }) + + function next(err) { + if (self._extensions.length <= i) { + return err + ? self.onStatError(err) + : self.error(404) + } + + var p = path + '.' + self._extensions[i++] + + debug('stat "%s"', p) + fs.stat(p, function (err, stat) { + if (err) return next(err) + if (stat.isDirectory()) return next() + self.emit('file', p, stat) + self.send(p, stat) + }) + } +} + +/** + * Transfer index for `path`. + * + * @param {String} path + * @api private + */ +SendStream.prototype.sendIndex = function sendIndex(path){ + var i = -1; + var self = this; + + function next(err){ + if (++i >= self._index.length) { + if (err) return self.onStatError(err); + return self.error(404); + } + + var p = join(path, self._index[i]); + + debug('stat "%s"', p); + fs.stat(p, function(err, stat){ + if (err) return next(err); + if (stat.isDirectory()) return next(); + self.emit('file', p, stat); + self.send(p, stat); + }); + } + + next(); +}; + +/** + * Stream `path` to the response. + * + * @param {String} path + * @param {Object} options + * @api private + */ + +SendStream.prototype.stream = function(path, options){ + // TODO: this is all lame, refactor meeee + var finished = false; + var self = this; + var res = this.res; + var req = this.req; + + // pipe + var stream = fs.createReadStream(path, options); + this.emit('stream', stream); + stream.pipe(res); + + // response finished, done with the fd + onFinished(res, function onfinished(){ + finished = true; + destroy(stream); + }); + + // error handling code-smell + stream.on('error', function onerror(err){ + // request already finished + if (finished) return; + + // clean up stream + finished = true; + destroy(stream); + + // error + self.onStatError(err); + }); + + // end + stream.on('end', function onend(){ + self.emit('end'); + }); +}; + +/** + * Set content-type based on `path` + * if it hasn't been explicitly set. + * + * @param {String} path + * @api private + */ + +SendStream.prototype.type = function type(path) { + var res = this.res; + + if (res.getHeader('Content-Type')) return; + + var type = mime.lookup(path); + + if (!type) { + debug('no content-type'); + return; + } + + var charset = mime.charsets.lookup(type); + + debug('content-type %s', type); + res.setHeader('Content-Type', type + (charset ? '; charset=' + charset : '')); +}; + +/** + * Set response header fields, most + * fields may be pre-defined. + * + * @param {String} path + * @param {Object} stat + * @api private + */ + +SendStream.prototype.setHeader = function setHeader(path, stat){ + var res = this.res; + + this.emit('headers', res, path, stat); + + if (!res.getHeader('Accept-Ranges')) res.setHeader('Accept-Ranges', 'bytes'); + if (!res.getHeader('Cache-Control')) res.setHeader('Cache-Control', 'public, max-age=' + Math.floor(this._maxage / 1000)); + + if (this._lastModified && !res.getHeader('Last-Modified')) { + var modified = stat.mtime.toUTCString() + debug('modified %s', modified) + res.setHeader('Last-Modified', modified) + } + + if (this._etag && !res.getHeader('ETag')) { + var val = etag(stat) + debug('etag %s', val) + res.setHeader('ETag', val) + } +}; + +/** + * Determine if path parts contain a dotfile. + * + * @api private + */ + +function containsDotFile(parts) { + for (var i = 0; i < parts.length; i++) { + if (parts[i][0] === '.') { + return true + } + } + + return false +} + +/** + * decodeURIComponent. + * + * Allows V8 to only deoptimize this fn instead of all + * of send(). + * + * @param {String} path + * @api private + */ + +function decode(path) { + try { + return decodeURIComponent(path) + } catch (err) { + return -1 + } +} + +/** + * Normalize the index option into an array. + * + * @param {boolean|string|array} val + * @param {string} name + * @private + */ + +function normalizeList(val, name) { + var list = [].concat(val || []) + + for (var i = 0; i < list.length; i++) { + if (typeof list[i] !== 'string') { + throw new TypeError(name + ' must be array of strings or false') + } + } + + return list +} |