@fastify/send
Advanced tools
Comparing version 2.0.1 to 2.1.0
@@ -11,14 +11,15 @@ 'use strict' | ||
function collapseLeadingSlashes (str) { | ||
let i = 0 | ||
for (i; i < str.length; i++) { | ||
if ( | ||
str[0] !== '/' || | ||
str[1] !== '/' | ||
) { | ||
return str | ||
} | ||
for (let i = 2, il = str.length; i < il; ++i) { | ||
if (str[i] !== '/') { | ||
break | ||
return str.slice(i - 1) | ||
} | ||
} | ||
return i > 1 | ||
? '/' + str.substr(i) | ||
: str | ||
} | ||
module.exports.collapseLeadingSlashes = collapseLeadingSlashes |
@@ -14,5 +14,4 @@ /*! | ||
function containsDotFile (parts) { | ||
for (let i = 0; i < parts.length; i++) { | ||
const part = parts[i] | ||
if (part.length > 1 && part[0] === '.') { | ||
for (let i = 0, il = parts.length; i < il; ++i) { | ||
if (parts[i].length !== 1 && parts[i][0] === '.') { | ||
return true | ||
@@ -24,2 +23,3 @@ } | ||
} | ||
exports.containsDotFile = containsDotFile | ||
module.exports.containsDotFile = containsDotFile |
'use strict' | ||
const utf8MimeTypeRE = /^text\/|^application\/(javascript|json)/ | ||
const isUtf8MimeType = utf8MimeTypeRE.test.bind(utf8MimeTypeRE) | ||
function isUtf8MimeType (value) { | ||
const len = value.length | ||
return ( | ||
(len > 21 && value.indexOf('application/javascript') === 0) || | ||
(len > 14 && value.indexOf('application/json') === 0) || | ||
(len > 5 && value.indexOf('text/') === 0) | ||
) | ||
} | ||
module.exports.isUtf8MimeType = isUtf8MimeType |
@@ -12,13 +12,18 @@ 'use strict' | ||
function normalizeList (val, name) { | ||
const list = [].concat(val || []) | ||
for (let i = 0; i < list.length; i++) { | ||
if (typeof list[i] !== 'string') { | ||
throw new TypeError(name + ' must be array of strings or false') | ||
if (typeof val === 'string') { | ||
return [val] | ||
} else if (val === false) { | ||
return [] | ||
} else if (Array.isArray(val)) { | ||
for (let i = 0, il = val.length; i < il; ++i) { | ||
if (typeof val[i] !== 'string') { | ||
throw new TypeError(name + ' must be array of strings or false') | ||
} | ||
} | ||
return val | ||
} else { | ||
throw new TypeError(name + ' must be array of strings or false') | ||
} | ||
return list | ||
} | ||
module.exports.normalizeList = normalizeList |
@@ -29,3 +29,3 @@ /*! | ||
const { normalizeList } = require('./normalizeList') | ||
const { parseRange } = require('./parseRange') | ||
const { parseBytesRange } = require('./parseBytesRange') | ||
const { parseTokenList } = require('./parseTokenList') | ||
@@ -75,2 +75,8 @@ const { setHeaders } = require('./setHeaders') | ||
const validDotFilesOptions = [ | ||
'allow', | ||
'ignore', | ||
'deny' | ||
] | ||
/** | ||
@@ -110,6 +116,6 @@ * Initialize a `SendStream` with the given `path`. | ||
this._dotfiles = opts.dotfiles !== undefined | ||
? opts.dotfiles | ||
: 'ignore' | ||
? validDotFilesOptions.indexOf(opts.dotfiles) | ||
: 1 // 'ignore' | ||
if (this._dotfiles !== 'ignore' && this._dotfiles !== 'allow' && this._dotfiles !== 'deny') { | ||
if (this._dotfiles === -1) { | ||
throw new TypeError('dotfiles option must be "allow", "deny", or "ignore"') | ||
@@ -138,3 +144,4 @@ } | ||
: Number(this._maxage) | ||
this._maxage = !isNaN(this._maxage) | ||
// eslint-disable-next-line no-self-compare | ||
this._maxage = this._maxage === this._maxage // fast path of isNaN(number) | ||
? Math.min(Math.max(0, this._maxage), MAX_MAXAGE) | ||
@@ -335,6 +342,8 @@ : 0 | ||
const unmodifiedSince = Date.parse(ifUnmodifiedSince) | ||
if (!Number.isNaN(unmodifiedSince)) { | ||
// eslint-disable-next-line no-self-compare | ||
if (unmodifiedSince === unmodifiedSince) { // fast path of isNaN(number) | ||
const lastModified = Date.parse(res.getHeader('Last-Modified')) | ||
if ( | ||
Number.isNaN(lastModified) || | ||
// eslint-disable-next-line no-self-compare | ||
lastModified !== lastModified ||// fast path of isNaN(number) | ||
lastModified > unmodifiedSince | ||
@@ -449,3 +458,4 @@ ) { | ||
const ifRangeTimestamp = Date.parse(ifRange) | ||
if (Number.isNaN(ifRangeTimestamp)) { | ||
// eslint-disable-next-line no-self-compare | ||
if (ifRangeTimestamp !== ifRangeTimestamp) { // fast path of isNaN(number) | ||
return false | ||
@@ -457,3 +467,7 @@ } | ||
return Number.isNaN(lastModified) || lastModified <= ifRangeTimestamp | ||
return ( | ||
// eslint-disable-next-line no-self-compare | ||
lastModified !== lastModified || // fast path of isNaN(number) | ||
lastModified <= ifRangeTimestamp | ||
) | ||
} | ||
@@ -558,12 +572,21 @@ | ||
// dotfile handling | ||
if (containsDotFile(parts)) { | ||
debug('%s dotfile "%s"', this._dotfiles, path) | ||
if ( | ||
( | ||
debug.enabled || // if debugging is enabled, then check for all cases to log allow case | ||
this._dotfiles !== 0 // if debugging is not enabled, then only check if 'deny' or 'ignore' is set | ||
) && | ||
containsDotFile(parts) | ||
) { | ||
switch (this._dotfiles) { | ||
case 'allow': | ||
/* istanbul ignore next: unreachable, because NODE_DEBUG can not be set after process is running */ | ||
case 0: // 'allow' | ||
debug('allow dotfile "%s"', path) | ||
break | ||
case 'deny': | ||
case 2: // 'deny' | ||
debug('deny dotfile "%s"', path) | ||
this.error(403) | ||
return res | ||
case 'ignore': | ||
case 1: // 'ignore' | ||
default: | ||
debug('ignore dotfile "%s"', path) | ||
this.error(404) | ||
@@ -597,3 +620,2 @@ return res | ||
const req = this.req | ||
let ranges = req.headers.range | ||
let offset = options.start || 0 | ||
@@ -636,36 +658,40 @@ | ||
// Range support | ||
if (this._acceptRanges && ranges !== undefined && BYTES_RANGE_REGEXP.test(ranges)) { | ||
// If-Range support | ||
if (!this.isRangeFresh()) { | ||
debug('range stale') | ||
ranges = -2 | ||
} else { | ||
// parse | ||
ranges = parseRange(len, ranges) | ||
} | ||
if (this._acceptRanges) { | ||
const rangeHeader = req.headers.range | ||
// unsatisfiable | ||
if (ranges === -1) { | ||
debug('range unsatisfiable') | ||
if ( | ||
rangeHeader !== undefined && | ||
BYTES_RANGE_REGEXP.test(rangeHeader) | ||
) { | ||
// If-Range support | ||
if (this.isRangeFresh()) { | ||
// parse | ||
const ranges = parseBytesRange(len, rangeHeader) | ||
// Content-Range | ||
res.setHeader('Content-Range', contentRange('bytes', len)) | ||
// unsatisfiable | ||
if (ranges.length === 0) { | ||
debug('range unsatisfiable') | ||
// 416 Requested Range Not Satisfiable | ||
return this.error(416, { | ||
headers: { 'Content-Range': res.getHeader('Content-Range') } | ||
}) | ||
} | ||
// Content-Range | ||
res.setHeader('Content-Range', contentRange('bytes', len)) | ||
// valid (syntactically invalid/multiple ranges are treated as a regular response) | ||
if (ranges !== -2 && ranges.length === 1) { | ||
debug('range %j', ranges) | ||
// 416 Requested Range Not Satisfiable | ||
return this.error(416, { | ||
headers: { 'Content-Range': res.getHeader('Content-Range') } | ||
}) | ||
// valid (syntactically invalid/multiple ranges are treated as a regular response) | ||
} else if (ranges.length === 1) { | ||
debug('range %j', ranges) | ||
// Content-Range | ||
res.statusCode = 206 | ||
res.setHeader('Content-Range', contentRange('bytes', len, ranges[0])) | ||
// Content-Range | ||
res.statusCode = 206 | ||
res.setHeader('Content-Range', contentRange('bytes', len, ranges[0])) | ||
// adjust for requested range | ||
offset += ranges[0].start | ||
len = ranges[0].end - ranges[0].start + 1 | ||
// adjust for requested range | ||
offset += ranges[0].start | ||
len = ranges[0].end - ranges[0].start + 1 | ||
} | ||
} else { | ||
debug('range stale') | ||
} | ||
} | ||
@@ -672,0 +698,0 @@ } |
{ | ||
"name": "@fastify/send", | ||
"description": "Better streaming static file server with Range and conditional-GET support", | ||
"version": "2.0.1", | ||
"version": "2.1.0", | ||
"author": "TJ Holowaychuk <tj@vision-media.ca>", | ||
@@ -14,3 +14,6 @@ "contributors": [ | ||
"license": "MIT", | ||
"repository": "fastify/send", | ||
"repository": { | ||
"type": "git", | ||
"url": "git+https://github.com/fastify/send.git" | ||
}, | ||
"keywords": [ | ||
@@ -37,3 +40,3 @@ "static", | ||
"tap": "^16.3.3", | ||
"tsd": "^0.25.0" | ||
"tsd": "^0.28.0" | ||
}, | ||
@@ -40,0 +43,0 @@ "scripts": { |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
No repository
Supply chain riskPackage does not have a linked source code repository. Without this field, a package will have no reference to the location of the source code use to generate the package.
Found 1 instance in 1 package
139025
57
3483
1
6