Socket
Socket
Sign inDemoInstall

send

Package Overview
Dependencies
5
Maintainers
2
Versions
62
Alerts
File Explorer

Advanced tools

Install Socket

Detect and block malicious and high-risk dependencies

Install

Comparing version 0.2.0 to 0.3.0

16

History.md
0.3.0 / 2014-04-24
==================
* Fix sending files with dots without root set
* Coerce option types
* Accept API options in options object
* Set etags to "weak"
* Include file path in etag
* Make "Can't set headers after they are sent." catchable
* Send full entity-body for multi range requests
* Default directory access to 403 when index disabled
* Support multiple index paths
* Support "If-Range" header
* Control whether to generate etags
* deps: mime@1.2.11
0.2.0 / 2014-01-29

@@ -3,0 +19,0 @@ ==================

144

lib/send.js

@@ -19,2 +19,4 @@

var upPathRegexp = /(?:^|[\\\/])\.\.(?:[\\\/]|$)/;
/**

@@ -64,8 +66,11 @@ * Expose `send`.

var self = this;
options = options || {};
this.req = req;
this.path = path;
this.options = options || {};
this.maxage(0);
this.hidden(false);
this.index('index.html');
this.options = options;
this.etag(('etag' in options) ? options.etag : true);
this.maxage(options.maxage);
this.hidden(options.hidden);
this.index(('index' in options) ? options.index : 'index.html');
if (options.root || options.from) this.root(options.root || options.from);
}

@@ -80,2 +85,17 @@

/**
* Enable or disable etag generation.
*
* @param {Boolean} val
* @return {SendStream}
* @api public
*/
SendStream.prototype.etag = function(val){
val = Boolean(val);
debug('etag %s', val);
this._etag = val;
return this;
};
/**
* Enable or disable "hidden" (dot) files.

@@ -89,2 +109,3 @@ *

SendStream.prototype.hidden = function(val){
val = Boolean(val);
debug('hidden %s', val);

@@ -96,6 +117,6 @@ this._hidden = val;

/**
* Set index `path`, set to a falsy
* Set index `paths`, set to a falsy
* value to disable index support.
*
* @param {String|Boolean} path
* @param {String|Boolean|Array} paths
* @return {SendStream}

@@ -105,5 +126,6 @@ * @api public

SendStream.prototype.index = function(path){
debug('index %s', path);
this._index = path;
SendStream.prototype.index = function index(paths){
var index = !paths ? [] : Array.isArray(paths) ? paths : [paths];
debug('index %j', index);
this._index = index;
return this;

@@ -122,2 +144,3 @@ };

SendStream.prototype.from = function(path){
path = String(path);
this._root = normalize(path);

@@ -136,2 +159,4 @@ return this;

SendStream.prototype.maxage = function(ms){
ms = Number(ms);
if (isNaN(ms)) ms = 0;
if (Infinity == ms) ms = 60 * 60 * 24 * 365 * 1000;

@@ -168,3 +193,3 @@ debug('max-age %d', ms);

SendStream.prototype.isMalicious = function(){
return !this._root && ~this.path.indexOf('..');
return !this._root && ~this.path.indexOf('..') && upPathRegexp.test(this.path);
};

@@ -236,2 +261,14 @@

/**
* 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

@@ -274,2 +311,19 @@ * responded with 2xx or 304 (see RFC 2616 section 14.2{5,6}).

/**
* 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`.

@@ -283,2 +337,3 @@ *

if (this.listeners('directory').length) return this.emit('directory');
if (this.hasTrailingSlash()) return this.error(403);
var res = this.res;

@@ -328,3 +383,6 @@ path += '/';

// index file support
if (this._index && this.hasTrailingSlash()) path += this._index;
if (this._index.length && this.hasTrailingSlash()) {
this.sendIndex(path);
return res;
}

@@ -357,4 +415,9 @@ debug('stat "%s"', path);

if (res._header) {
// impossible to send now
return this.headersAlreadySent();
}
// set header fields
this.setHeader(stat);
this.setHeader(path, stat);

@@ -382,4 +445,11 @@ // set content-type

// 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);

@@ -389,4 +459,6 @@ return this.error(416);

// valid (syntactically invalid ranges are treated as a regular response)
if (-2 != ranges) {
// valid (syntactically invalid/multiple ranges are treated as a regular response)
if (-2 != ranges && ranges.length === 1) {
debug('range %j', ranges);
options.start = offset + ranges[0].start;

@@ -417,2 +489,34 @@ options.end = offset + ranges[0].end;

/**
* 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.redirect(self.path);
}
var p = path + self._index[i];
debug('stat "%s"', p);
fs.stat(p, function(err, stat){
if (err) return next(err);
if (stat.isDirectory()) return self.redirect(self.path);
self.emit('file', p, stat);
self.send(p, stat);
});
}
if (!this.hasTrailingSlash()) path += '/';
next();
};
/**
* Stream `path` to the response.

@@ -477,5 +581,6 @@ *

/**
* Set reaponse header fields, most
* Set response header fields, most
* fields may be pre-defined.
*
* @param {String} path
* @param {Object} stat

@@ -485,9 +590,14 @@ * @api private

SendStream.prototype.setHeader = function(stat){
SendStream.prototype.setHeader = function setHeader(path, stat){
var res = this.res;
if (!res.getHeader('Accept-Ranges')) res.setHeader('Accept-Ranges', 'bytes');
if (!res.getHeader('ETag')) res.setHeader('ETag', utils.etag(stat));
if (!res.getHeader('Date')) res.setHeader('Date', new Date().toUTCString());
if (!res.getHeader('Cache-Control')) res.setHeader('Cache-Control', 'public, max-age=' + (this._maxage / 1000));
if (!res.getHeader('Last-Modified')) res.setHeader('Last-Modified', stat.mtime.toUTCString());
if (this._etag && !res.getHeader('ETag')) {
var etag = utils.etag(path, stat);
debug('etag %s', etag);
res.setHeader('ETag', etag);
}
};

15

lib/utils.js
/**
* Return an ETag in the form of `"<size>-<mtime>"`
* from the given `stat`.
* Module dependencies.
*/
var crc32 = require('buffer-crc32').unsigned;
/**
* Return a weak ETag from the given `path` and `stat`.
*
* @param {String} path
* @param {Object} stat

@@ -11,4 +17,5 @@ * @return {String}

exports.etag = function(stat) {
return '"' + stat.size + '-' + Number(stat.mtime) + '"';
exports.etag = function etag(path, stat) {
var tag = String(stat.mtime.getTime()) + ':' + String(stat.size) + ':' + path;
return 'W/"' + crc32(tag) + '"';
};

@@ -15,0 +22,0 @@

{
"name": "send",
"version": "0.2.0",
"version": "0.3.0",
"description": "Better streaming static file server with Range and conditional-GET support",

@@ -12,5 +12,6 @@ "keywords": [

"dependencies": {
"debug": "*",
"mime": "~1.2.9",
"buffer-crc32": "0.2.1",
"debug": "0.8.0",
"fresh": "~0.2.1",
"mime": "1.2.11",
"range-parser": "~1.0.0"

@@ -21,7 +22,7 @@ },

"should": "*",
"supertest": "0.0.1",
"supertest": "0.10.0",
"connect": "2.x"
},
"scripts": {
"test": "make test"
"test": "mocha --require should --reporter spec --bail"
},

@@ -33,2 +34,2 @@ "repository": {

"main": "index"
}
}

@@ -46,4 +46,3 @@ # send

// /www/example.com/public/*
send(req, url.parse(req.url).pathname)
.root('/www/example.com/public')
send(req, url.parse(req.url).pathname, {root: '/www/example.com/public'})
.on('error', error)

@@ -57,2 +56,26 @@ .on('directory', redirect)

### Options
#### etag
Enable or disable etag generation, defaults to true.
#### hidden
Enable or disable transfer of hidden files, defaults to false.
#### index
By default send supports "index.html" files, to disable this
set `false` or to supply a new index pass a string or an array
in preferred order.
#### maxage
Provide a max-age in milliseconds for http caching, defaults to 0.
#### root
Serve files relative to `path`.
### Events

@@ -66,2 +89,6 @@

### .etag(bool)
Enable or disable etag generation, defaults to true.
### .root(dir)

@@ -71,6 +98,7 @@

### .index(path)
### .index(paths)
By default send supports "index.html" files, to disable this
invoke `.index(false)` or to supply a new index pass a string.
invoke `.index(false)` or to supply a new index pass a string
or an array in preferred order.

@@ -77,0 +105,0 @@ ### .maxage(ms)

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc