Comparing version 5.0.1 to 5.1.0
@@ -34,27 +34,40 @@ 'use strict'; | ||
exports.handler = function (route, options) { | ||
internals.normalizePaths = function (basePath, paths) { | ||
const settings = Joi.attempt(options, internals.schema, 'Invalid directory handler options (' + route.path + ')'); | ||
Hoek.assert(route.path[route.path.length - 1] === '}', 'The route path for a directory handler must end with a parameter:', route.path); | ||
return paths.map((path) => { | ||
const paramName = /\w+/.exec(route.path.slice(route.path.lastIndexOf('{')))[0]; | ||
return Path.isAbsolute(path) ? path : Path.join(basePath, path); | ||
}); | ||
}; | ||
const normalize = (paths) => { | ||
const normalized = []; | ||
for (let i = 0; i < paths.length; ++i) { | ||
let path = paths[i]; | ||
internals.resolvePathOption = function (basePath, result) { | ||
if (!Path.isAbsolute(path)) { | ||
path = Path.join(route.settings.files.relativeTo, path); | ||
} | ||
if (result instanceof Error) { | ||
throw result; | ||
} | ||
normalized.push(path); | ||
} | ||
let paths; | ||
if (typeof result === 'string') { | ||
paths = [result]; | ||
} | ||
else if (Array.isArray(result)) { | ||
paths = result; | ||
} | ||
else { | ||
throw Boom.internal('Invalid path function'); | ||
} | ||
return normalized; | ||
}; | ||
return internals.normalizePaths(basePath, paths); | ||
}; | ||
const normalized = (Array.isArray(settings.path) ? normalize(settings.path) : []); // Array or function | ||
exports.handler = function (route, options) { | ||
const settings = Joi.attempt(options, internals.schema, 'Invalid directory handler options (' + route.path + ')'); | ||
Hoek.assert(route.path[route.path.length - 1] === '}', 'The route path for a directory handler must end with a parameter:', route.path); | ||
const paramName = /\w+/.exec(route.path.slice(route.path.lastIndexOf('{')))[0]; | ||
const basePath = route.settings.files.relativeTo; | ||
const normalized = (Array.isArray(settings.path) ? internals.normalizePaths(basePath, settings.path) : null); // Array or function | ||
const indexNames = (settings.index === true) ? ['index.html'] : (settings.index || []); | ||
@@ -66,20 +79,4 @@ | ||
let paths = normalized; | ||
if (typeof settings.path === 'function') { | ||
const result = settings.path.call(null, request); | ||
if (result instanceof Error) { | ||
throw result; | ||
} | ||
const paths = normalized || internals.resolvePathOption(basePath, settings.path.call(null, request)); | ||
if (Array.isArray(result)) { | ||
paths = normalize(result); | ||
} | ||
else if (typeof result === 'string') { | ||
paths = normalize([result]); | ||
} | ||
else { | ||
throw Boom.badImplementation('Invalid path function'); | ||
} | ||
} | ||
// Append parameter | ||
@@ -92,3 +89,3 @@ | ||
throw Boom.notFound(); | ||
throw Boom.notFound(null, {}); | ||
} | ||
@@ -122,81 +119,69 @@ | ||
// Not found | ||
// Handle Not found | ||
if (error.output.statusCode === 404) { | ||
if (settings.defaultExtension) { | ||
if (hasTrailingSlash) { | ||
path = path.slice(0, -1); | ||
} | ||
if (internals.isNotFound(error)) { | ||
if (!settings.defaultExtension) { | ||
throw error; | ||
} | ||
try { | ||
return await File.load(path + '.' + settings.defaultExtension, request, fileOptions); | ||
} | ||
catch (err) { | ||
Bounce.ignore(err, 'boom'); | ||
} | ||
if (hasTrailingSlash) { | ||
path = path.slice(0, -1); | ||
} | ||
return; | ||
return await File.load(path + '.' + settings.defaultExtension, request, fileOptions); | ||
} | ||
// Propagate non-directory errors | ||
// Handle Directory | ||
if (error.output.statusCode !== 403 || | ||
error.data !== 'EISDIR') { | ||
if (internals.isDirectory(error)) { | ||
if (settings.redirectToSlash !== false && // Defaults to true | ||
!request.server.settings.router.stripTrailingSlash && | ||
!hasTrailingSlash) { | ||
throw error; | ||
} | ||
return reply.redirect(resource + '/'); | ||
} | ||
// Directory | ||
for (const indexName of indexNames) { | ||
const indexFile = Path.join(path, indexName); | ||
try { | ||
return await File.load(indexFile, request, fileOptions); | ||
} | ||
catch (err) { | ||
Bounce.ignore(err, 'boom'); | ||
if (!indexNames.length && | ||
!settings.listing) { | ||
if (!internals.isNotFound(err)) { | ||
throw Boom.internal(indexName + ' is a directory', err); | ||
} | ||
throw Boom.forbidden(); | ||
} | ||
if (settings.redirectToSlash !== false && // Defaults to true | ||
!request.server.settings.router.stripTrailingSlash && | ||
!hasTrailingSlash) { | ||
return reply.redirect(resource + '/'); | ||
} | ||
for (let i = 0; i < indexNames.length; ++i) { | ||
const indexName = indexNames[i]; | ||
const indexFile = Path.join(path, indexName); | ||
try { | ||
// File loaded successfully | ||
return await File.load(indexFile, request, fileOptions); | ||
// Not found - try next | ||
} | ||
} | ||
catch (err) { | ||
Bounce.ignore(err, 'boom'); | ||
// Directory | ||
// None of the index files were found | ||
if (err.output.statusCode !== 404) { | ||
throw Boom.badImplementation(indexName + ' is a directory'); | ||
} | ||
if (settings.listing) { | ||
return internals.generateListing(Path.join(baseDir, path), resource, selection, hasTrailingSlash, settings, request); | ||
} | ||
} | ||
// None of the index files were found | ||
throw error; | ||
}; | ||
if (!settings.listing) { | ||
throw Boom.forbidden(); | ||
for (let i = 0; i < paths.length; ++i) { | ||
try { | ||
return await each(paths[i]); | ||
} | ||
catch (err) { | ||
Bounce.ignore(err, 'boom'); | ||
return await internals.generateListing(Path.join(baseDir, path), resource, selection, hasTrailingSlash, settings, request); | ||
}; | ||
// Propagate any non-404 errors | ||
for (let i = 0; i < paths.length; ++i) { | ||
const response = await each(paths[i]); | ||
if (response !== undefined) { | ||
return response; | ||
if (!internals.isNotFound(err) || | ||
i === paths.length - 1) { | ||
throw err; | ||
} | ||
} | ||
} | ||
throw Boom.notFound(); | ||
throw Boom.notFound(null, {}); | ||
}; | ||
@@ -252,1 +237,13 @@ | ||
}; | ||
internals.isNotFound = function (boom) { | ||
return boom.output.statusCode === 404; | ||
}; | ||
internals.isDirectory = function (boom) { | ||
return boom.output.statusCode === 403 && boom.data.code === 'EISDIR'; | ||
}; |
@@ -88,3 +88,3 @@ 'use strict'; | ||
Bounce.rethrow(err, 'system'); | ||
throw Boom.boomify(err, { message: 'Failed to hash file' }); | ||
throw Boom.boomify(err, { message: 'Failed to hash file', data: { path: response.source.path } }); | ||
} | ||
@@ -91,0 +91,0 @@ }; |
@@ -104,3 +104,3 @@ 'use strict'; | ||
if (path === null) { | ||
throw Boom.forbidden(null, 'EACCES'); | ||
throw Boom.forbidden(null, { code: 'EACCES' }); | ||
} | ||
@@ -154,9 +154,9 @@ | ||
if (extension) { | ||
const gzFile = new Fs.File(`${response.source.path}${extension}`); | ||
const precompressed = new Fs.File(`${response.source.path}${extension}`); | ||
let stat; | ||
try { | ||
stat = await gzFile.openStat('r'); | ||
stat = await precompressed.openStat('r'); | ||
} | ||
catch (err) { | ||
gzFile.close(); | ||
precompressed.close(); | ||
Bounce.ignore(err, 'boom'); | ||
@@ -167,3 +167,3 @@ } | ||
response.source.file.close(); | ||
response.source.file = gzFile; | ||
response.source.file = precompressed; | ||
@@ -170,0 +170,0 @@ response.bytes(stat.size); |
@@ -37,11 +37,14 @@ 'use strict'; | ||
catch (err) { | ||
const data = { path: this.path }; | ||
if (this.path.indexOf('\u0000') !== -1 || err.code === 'ENOENT') { | ||
throw Boom.notFound(); | ||
throw Boom.notFound(null, data); | ||
} | ||
if (err.code === 'EACCES' || err.code === 'EPERM') { | ||
throw Boom.forbidden(null, err.code); | ||
data.code = err.code; | ||
throw Boom.forbidden(null, data); | ||
} | ||
throw Boom.boomify(err, { message: 'Failed to open file' }); | ||
throw Boom.boomify(err, { message: 'Failed to open file', data }); | ||
} | ||
@@ -54,3 +57,3 @@ }; | ||
if (this.fd !== null) { | ||
exports.close(this.fd).then(null, Hoek.ignore); | ||
Bounce.background(exports.close(this.fd)); | ||
this.fd = null; | ||
@@ -69,3 +72,3 @@ } | ||
if (stat.isDirectory()) { | ||
throw Boom.forbidden(null, 'EISDIR'); | ||
throw Boom.forbidden(null, { code: 'EISDIR', path: this.path }); | ||
} | ||
@@ -79,3 +82,3 @@ | ||
Bounce.rethrow(err, ['boom', 'system']); | ||
throw Boom.boomify(err, { message: 'Failed to stat file' }); | ||
throw Boom.boomify(err, { message: 'Failed to stat file', data: { path: this.path } }); | ||
} | ||
@@ -82,0 +85,0 @@ }; |
@@ -21,2 +21,18 @@ 'use strict'; | ||
internals.fileMethod = function (path, responseOptions) { | ||
// Set correct confine value | ||
responseOptions = responseOptions || {}; | ||
if (typeof responseOptions.confine === 'undefined' || responseOptions.confine === true) { | ||
responseOptions.confine = '.'; | ||
} | ||
Hoek.assert(responseOptions.end === undefined || +responseOptions.start <= +responseOptions.end, 'options.start must be less than or equal to options.end'); | ||
return this.response(File.response(path, responseOptions, this.request)); | ||
}; | ||
exports.plugin = { | ||
@@ -35,17 +51,4 @@ pkg: require('../package.json'), | ||
server.decorate('handler', 'directory', Directory.handler); | ||
server.decorate('toolkit', 'file', function (path, responseOptions) { | ||
// Set correct confine value | ||
responseOptions = responseOptions || {}; | ||
if (typeof responseOptions.confine === 'undefined' || responseOptions.confine === true) { | ||
responseOptions.confine = '.'; | ||
} | ||
Hoek.assert(responseOptions.end === undefined || +responseOptions.start <= +responseOptions.end, 'options.start must be less than or equal to options.end'); | ||
return this.response(File.response(path, responseOptions, this.request)); | ||
}); | ||
server.decorate('toolkit', 'file', internals.fileMethod); | ||
} | ||
}; |
{ | ||
"name": "inert", | ||
"description": "Static file and directory handlers plugin for hapi.js", | ||
"version": "5.0.1", | ||
"version": "5.1.0", | ||
"repository": "https://github.com/hapijs/inert.git", | ||
@@ -18,3 +18,3 @@ "main": "lib/index.js", | ||
"peerDependencies": { | ||
"hapi": "17.x.x" | ||
"hapi": ">=17.0.0" | ||
}, | ||
@@ -21,0 +21,0 @@ "dependencies": { |
@@ -32,2 +32,3 @@ # inert | ||
- [The `directory` handler](#the-directory-handler) | ||
- [Errors](#errors) | ||
@@ -262,1 +263,14 @@ ## Examples | ||
not found. Defaults to no extension. | ||
### Errors | ||
Any file access errors are signalled using appropriate [Boom](https://github.com/hapijs/boom) | ||
errors. These are `Boom.notFound()` for missing or hidden files, and `Boom.forbidden()` for | ||
files that exist, but can't otherwise be accessed. | ||
The error can contain an `err.data.path` property, which is the path that the error failed for. | ||
This property *does not always exist* if the response was generated without a file system lookup, | ||
and for the directory handler it will be the last tested non-index path. | ||
If an unexpected configuration or processing errors occurs, `Boom.internal()` and `'system'` | ||
errors can also be thrown. |
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
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
35743
274
548