Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

inert

Package Overview
Dependencies
Maintainers
2
Versions
37
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

inert - npm Package Compare versions

Comparing version 3.2.1 to 4.0.0

107

lib/directory.js

@@ -0,10 +1,12 @@

'use strict';
// Load modules
var Fs = require('fs');
var Path = require('path');
var Boom = require('boom');
var Hoek = require('hoek');
var Items = require('items');
var Joi = require('joi');
var File = require('./file');
const Fs = require('fs');
const Path = require('path');
const Boom = require('boom');
const Hoek = require('hoek');
const Items = require('items');
const Joi = require('joi');
const File = require('./file');

@@ -14,3 +16,3 @@

var internals = {};
const internals = {};

@@ -32,12 +34,12 @@

var settings = Joi.attempt(options, internals.schema, 'Invalid directory handler options (' + route.path + ')');
Hoek.assert(route.path[route.path.length - 1] === '}', 'The route path must end with a parameter:', route.path);
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);
var normalize = function (paths) {
const normalize = (paths) => {
var normalized = [];
for (var i = 0, il = paths.length; i < il; ++i) {
var path = paths[i];
const normalized = [];
for (let i = 0; i < paths.length; ++i) {
let path = paths[i];
if (!Hoek.isAbsolutePath(path)) {
if (!Path.isAbsolute(path)) {
path = Path.join(route.settings.files.relativeTo, path);

@@ -52,13 +54,13 @@ }

var normalized = (Array.isArray(settings.path) ? normalize(settings.path) : []); // Array or function
const normalized = (Array.isArray(settings.path) ? normalize(settings.path) : []); // Array or function
var indexNames = (settings.index === true) ? ['index.html'] : (settings.index || []);
const indexNames = (settings.index === true) ? ['index.html'] : (settings.index || []);
// Declare handler
var handler = function (request, reply) {
const handler = (request, reply) => {
var paths = normalized;
let paths = normalized;
if (typeof settings.path === 'function') {
var result = settings.path.call(null, request);
const result = settings.path.call(null, request);
if (result instanceof Error) {

@@ -81,12 +83,3 @@ return reply(result);

var selection = null;
var lastParam = request.paramsArray[request.paramsArray.length - 1];
if (lastParam) {
if (lastParam.indexOf('..') !== -1) {
return reply(Boom.forbidden());
}
selection = lastParam;
}
const selection = request.paramsArray[request.paramsArray.length - 1];
if (selection &&

@@ -101,12 +94,18 @@ !settings.showHidden &&

var resource = request.path;
var hasTrailingSlash = (resource[resource.length - 1] === '/');
var fileOptions = { lookupCompressed: settings.lookupCompressed, etagMethod: settings.etagMethod };
const resource = request.path;
const hasTrailingSlash = resource.endsWith('/');
const fileOptions = {
confine: null,
lookupCompressed: settings.lookupCompressed,
etagMethod: settings.etagMethod
};
Items.serial(paths, function (path, nextPath) {
Items.serial(paths, (baseDir, nextPath) => {
path = Path.join(path, selection || '');
fileOptions.confine = baseDir;
File.load(path, request, fileOptions, function (response) {
let path = selection || '';
File.load(path, request, fileOptions, (response) => {
// File loaded successfully

@@ -120,3 +119,3 @@

var err = response;
const err = response;
if (err.output.statusCode === 404) {

@@ -131,3 +130,3 @@ if (!settings.defaultExtension) {

return File.load(path + '.' + settings.defaultExtension, request, fileOptions, function (extResponse) {
return File.load(path + '.' + settings.defaultExtension, request, fileOptions, (extResponse) => {

@@ -163,6 +162,6 @@ if (!extResponse.isBoom) {

Items.serial(indexNames, function (indexName, nextIndex) {
Items.serial(indexNames, (indexName, nextIndex) => {
var indexFile = Path.join(path, indexName);
File.load(indexFile, request, fileOptions, function (indexResponse) {
const indexFile = Path.join(path, indexName);
File.load(indexFile, request, fileOptions, (indexResponse) => {

@@ -177,3 +176,3 @@ // File loaded successfully

var err = indexResponse;
const err = indexResponse;
if (err.output.statusCode !== 404) {

@@ -188,3 +187,3 @@ return reply(Boom.badImplementation(indexName + ' is a directory'));

},
function (/* err */) {
(/* err */) => {

@@ -197,7 +196,7 @@ // None of the index files were found

return internals.generateListing(path, resource, selection, hasTrailingSlash, settings, request, reply);
return internals.generateListing(Path.join(baseDir, path), resource, selection, hasTrailingSlash, settings, request, reply);
});
});
},
function (/* err */) {
(/* err */) => {

@@ -214,3 +213,3 @@ return reply(Boom.notFound());

Fs.readdir(path, function (err, files) {
Fs.readdir(path, (err, files) => {

@@ -222,19 +221,19 @@ if (err) {

resource = decodeURIComponent(resource);
var display = Hoek.escapeHtml(resource);
var html = '<html><head><title>' + display + '</title></head><body><h1>Directory: ' + display + '</h1><ul>';
const display = Hoek.escapeHtml(resource);
let html = '<html><head><title>' + display + '</title></head><body><h1>Directory: ' + display + '</h1><ul>';
if (selection) {
var parent = resource.substring(0, resource.lastIndexOf('/', resource.length - (hasTrailingSlash ? 2 : 1))) + '/';
html += '<li><a href="' + internals.pathEncode(parent) + '">Parent Directory</a></li>';
const parent = resource.substring(0, resource.lastIndexOf('/', resource.length - (hasTrailingSlash ? 2 : 1))) + '/';
html = html + '<li><a href="' + internals.pathEncode(parent) + '">Parent Directory</a></li>';
}
for (var i = 0, il = files.length; i < il; ++i) {
for (let i = 0; i < files.length; ++i) {
if (settings.showHidden ||
!internals.isFileHidden(files[i])) {
html += '<li><a href="' + internals.pathEncode(resource + (selection && !hasTrailingSlash ? '/' : '') + files[i]) + '">' + Hoek.escapeHtml(files[i]) + '</a></li>';
html = html + '<li><a href="' + internals.pathEncode(resource + (selection && !hasTrailingSlash ? '/' : '') + files[i]) + '">' + Hoek.escapeHtml(files[i]) + '</a></li>';
}
}
html += '</ul></body></html>';
html = html + '</ul></body></html>';

@@ -248,3 +247,3 @@ return reply(request.generateResponse(html));

return /(^|[\\\/])\.([^\\\/]|[\\\/]?$)/.test(path); // Starts with a '.' or contains '/.' or '\.', and not followed by a '/' or '\' or end
return /(^|[\\\/])\.([^.\\\/]|\.[^\\\/])/.test(path); // Starts with a '.' or contains '/.' or '\.', which is not followed by a '/' or '\' or '.'
};

@@ -251,0 +250,0 @@

@@ -0,8 +1,10 @@

'use strict';
// Load modules
var Fs = require('fs');
var Crypto = require('crypto');
var Boom = require('boom');
var Hoek = require('hoek');
var LruCache = require('lru-cache');
const Fs = require('fs');
const Crypto = require('crypto');
const Boom = require('boom');
const Hoek = require('hoek');
const LruCache = require('lru-cache');

@@ -12,3 +14,3 @@

var internals = {};
const internals = {};

@@ -18,3 +20,3 @@

var etags = response.request.server.plugins.inert._etags;
const etags = response.request.server.plugins.inert._etags;
if (!etags) {

@@ -26,8 +28,8 @@ return next(null, null);

var path = response.source.path;
var cachekey = [path, stat.ino, stat.size, stat.mtime.getTime()].join('-');
const path = response.source.path;
const cachekey = [path, stat.ino, stat.size, stat.mtime.getTime()].join('-');
// The etag hashes the file contents in order to be consistent across distributed deployments
var cachedEtag = etags.get(cachekey);
const cachedEtag = etags.get(cachekey);
if (cachedEtag) {

@@ -37,5 +39,5 @@ return next(null, cachedEtag);

var pendings = response.request.server.plugins.inert._pendings;
var pendingsId = '+' + cachekey; // Prefix to avoid conflicts with JS internals (e.g. __proto__)
var nexts = pendings[pendingsId];
const pendings = response.request.server.plugins.inert._pendings;
const pendingsId = '+' + cachekey; // Prefix to avoid conflicts with JS internals (e.g. __proto__)
let nexts = pendings[pendingsId];
if (nexts) {

@@ -50,3 +52,3 @@ return nexts.push(next);

internals.hashFile(response, function (err, hash) {
internals.hashFile(response, (err, hash) => {

@@ -60,3 +62,3 @@ if (!err) {

delete pendings[pendingsId];
for (var i = 0, il = nexts.length; i < il; ++i) {
for (let i = 0; i < nexts.length; ++i) {
Hoek.nextTick(nexts[i])(err, hash);

@@ -70,9 +72,9 @@ }

var hash = Crypto.createHash('sha1');
const hash = Crypto.createHash('sha1');
hash.setEncoding('hex');
var fileStream = Fs.createReadStream(response.source.path, { fd: response.source.fd, autoClose: false });
const fileStream = Fs.createReadStream(response.source.path, { fd: response.source.fd, autoClose: false });
fileStream.pipe(hash);
var done = function (err) {
let done = function (err) {

@@ -95,4 +97,4 @@ if (err) {

var size = stat.size.toString(16);
var mtime = stat.mtime.getTime().toString(16);
const size = stat.size.toString(16);
const mtime = stat.mtime.getTime().toString(16);

@@ -105,3 +107,3 @@ return next(null, size + '-' + mtime);

var etagMethod = response.source.settings.etagMethod;
const etagMethod = response.source.settings.etagMethod;
if (etagMethod === false) {

@@ -111,3 +113,3 @@ return next();

var applyEtag = function (err, etag) {
const applyEtag = (err, etag) => {

@@ -114,0 +116,0 @@ if (err) {

@@ -0,10 +1,12 @@

'use strict';
// Load modules
var Fs = require('fs');
var Path = require('path');
var Ammo = require('ammo');
var Boom = require('boom');
var Hoek = require('hoek');
var Joi = require('joi');
var Etag = require('./etag');
const Fs = require('fs');
const Path = require('path');
const Ammo = require('ammo');
const Boom = require('boom');
const Hoek = require('hoek');
const Joi = require('joi');
const Etag = require('./etag');

@@ -14,3 +16,3 @@

var internals = {};
const internals = {};

@@ -23,2 +25,3 @@

path: Joi.alternatives(Joi.string(), Joi.func()).required(),
confine: Joi.alternatives(Joi.string(), Joi.boolean()).default(true),
filename: Joi.string(),

@@ -35,9 +38,10 @@ mode: Joi.string().valid('attachment', 'inline').allow(false),

var settings = Joi.attempt(options, internals.schema, 'Invalid file handler options (' + route.path + ')');
settings = (typeof options !== 'object' ? { path: options } : settings);
let settings = Joi.attempt(options, internals.schema, 'Invalid file handler options (' + route.path + ')');
settings = (typeof options !== 'object' ? { path: options, confine: '.' } : settings);
settings.confine = settings.confine === true ? '.' : settings.confine;
Hoek.assert(typeof settings.path !== 'string' || settings.path[settings.path.length - 1] !== '/', 'File path cannot end with a \'/\':', route.path);
var handler = function (request, reply) {
const handler = (request, reply) => {
var path = (typeof settings.path === 'function' ? settings.path(request) : settings.path);
const path = (typeof settings.path === 'function' ? settings.path(request) : settings.path);
return reply(exports.response(path, settings, request));

@@ -52,3 +56,3 @@ };

var response = exports.response(path, options, request, true);
const response = exports.response(path, options, request, true);
return internals.prepare(response, callback);

@@ -60,7 +64,19 @@ };

options = options || {};
Hoek.assert(!options.mode || ['attachment', 'inline'].indexOf(options.mode) !== -1, 'options.mode must be either false, attachment, or inline');
var source = {
path: Path.normalize(Hoek.isAbsolutePath(path) ? path : Path.join(request.route.settings.files.relativeTo, path)),
if (options.confine) {
const confineDir = Path.resolve(request.route.settings.files.relativeTo, options.confine);
path = Path.isAbsolute(path) ? Path.normalize(path) : Path.join(confineDir, path);
// Verify that resolved path is within confineDir
if (path.lastIndexOf(confineDir, 0) !== 0) {
path = null;
}
}
else {
path = Path.isAbsolute(path) ? Path.normalize(path) : Path.join(request.route.settings.files.relativeTo, path);
}
const source = {
path: path,
settings: options,

@@ -71,3 +87,3 @@ stat: null,

var prepare = _preloaded ? null : internals.prepare;
const prepare = _preloaded ? null : internals.prepare;

@@ -80,5 +96,13 @@ return request.generateResponse(source, { variety: 'file', marshal: internals.marshal, prepare: prepare, close: internals.close });

var path = response.source.path;
internals.openStat(path, 'r', function (err, fd, stat) {
const path = response.source.path;
if (path === null) {
return process.nextTick(() => {
return callback(Boom.forbidden(null, 'EACCES'));
});
}
internals.openStat(path, 'r', (err, fd, stat) => {
if (err) {

@@ -98,7 +122,7 @@ return callback(err);

if (response.source.settings.mode) {
var fileName = response.source.settings.filename || Path.basename(path);
const fileName = response.source.settings.filename || Path.basename(path);
response.header('content-disposition', response.source.settings.mode + '; filename=' + encodeURIComponent(fileName));
}
Etag.apply(response, stat, function (err) {
Etag.apply(response, stat, (err) => {

@@ -125,4 +149,4 @@ if (err) {

var gzFile = response.source.path + '.gz';
internals.openStat(gzFile, 'r', function (err, fd, stat) {
const gzFile = response.source.path + '.gz';
internals.openStat(gzFile, 'r', (err, fd, stat) => {

@@ -147,5 +171,5 @@ if (err) {

var request = response.request;
var length = response.headers['content-length'];
var range = null;
const request = response.request;
const length = response.headers['content-length'];
let range = null;

@@ -161,4 +185,4 @@ if (request.headers.range && length) {

var mime = request.server.mime.type(response.headers['content-type'] || 'application/octet-stream');
var encoding = (request.connection.settings.compression && mime.compressible && !response.headers['content-encoding'] ? request.info.acceptEncoding : null);
const mime = request.server.mime.type(response.headers['content-type'] || 'application/octet-stream');
const encoding = (request.connection.settings.compression && mime.compressible && !response.headers['content-encoding'] ? request.info.acceptEncoding : null);

@@ -169,5 +193,5 @@ if (encoding === 'identity' || !encoding) {

var ranges = Ammo.header(request.headers.range, length);
const ranges = Ammo.header(request.headers.range, length);
if (!ranges) {
var error = Boom.rangeNotSatisfiable();
const error = Boom.rangeNotSatisfiable();
error.output.headers['content-range'] = 'bytes */' + length;

@@ -199,5 +223,5 @@ return callback(error);

var options = { fd: response.source.fd, start: 0 };
const options = { fd: response.source.fd, start: 0 };
internals.addContentRange(response, function (err, range) {
internals.addContentRange(response, (err, range) => {

@@ -213,3 +237,3 @@ if (err) {

var fileStream = Fs.createReadStream(path, options);
const fileStream = Fs.createReadStream(path, options);
response.source.fd = null; // Claim descriptor

@@ -224,3 +248,3 @@

Fs.open(path, mode, function (err, fd) {
Fs.open(path, mode, (err, fd) => {

@@ -239,3 +263,3 @@ if (err) {

Fs.fstat(fd, function (err, stat) {
Fs.fstat(fd, (err, stat) => {

@@ -242,0 +266,0 @@ if (err) {

@@ -0,7 +1,9 @@

'use strict';
// Load modules
var Directory = require('./directory');
var Etag = require('./etag');
var File = require('./file');
var Hoek = require('hoek');
const Directory = require('./directory');
const Etag = require('./etag');
const File = require('./file');
const Hoek = require('hoek');

@@ -11,3 +13,3 @@

var internals = {
const internals = {
defaults: {

@@ -21,3 +23,3 @@ etagsCacheMaxSize: 1000

var settings = Hoek.applyToDefaults(internals.defaults, options);
const settings = Hoek.applyToDefaults(internals.defaults, options);

@@ -32,2 +34,9 @@ server.expose('_etags', settings.etagsCacheMaxSize ? new Etag.Cache(settings.etagsCacheMaxSize) : null);

// Set correct confine value
responseOptions = responseOptions || {};
if (typeof responseOptions.confine === 'undefined' || responseOptions.confine === true) {
responseOptions.confine = '.';
}
return this.response(File.response(path, responseOptions, this.request));

@@ -39,2 +48,3 @@ });

exports.register.attributes = {

@@ -41,0 +51,0 @@ pkg: require('../package.json'),

{
"name": "inert",
"description": "Static file and directory handlers plugin for hapi.js",
"version": "3.2.1",
"version": "4.0.0",
"repository": "git://github.com/hapijs/inert",

@@ -15,16 +15,16 @@ "main": "lib/index.js",

"engines": {
"node": ">=0.10.40"
"node": ">=4.0.0"
},
"dependencies": {
"ammo": "1.x.x",
"boom": "2.x.x",
"hoek": "2.x.x",
"items": "1.x.x",
"joi": "^6.7.x",
"lru-cache": "2.7.x"
"ammo": "2.x.x",
"boom": "3.x.x",
"hoek": "4.x.x",
"items": "2.x.x",
"joi": "8.x.x",
"lru-cache": "4.0.x"
},
"devDependencies": {
"code": "1.x.x",
"hapi": "9.x.x",
"lab": "6.x.x"
"code": "2.x.x",
"hapi": "13.x.x",
"lab": "10.x.x"
},

@@ -31,0 +31,0 @@ "scripts": {

@@ -42,7 +42,7 @@ # inert

```js
var Path = require('path');
var Hapi = require('hapi');
var Inert = require('inert');
const Path = require('path');
const Hapi = require('hapi');
const Inert = require('inert');
var server = new Hapi.Server({
const server = new Hapi.Server({
connections: {

@@ -58,3 +58,3 @@ routes: {

server.register(Inert, function () {});
server.register(Inert, () => {});

@@ -73,3 +73,3 @@ server.route({

server.start(function (err) {
server.start((err) => {

@@ -108,3 +108,3 @@ if (err) {

var path = 'plain.txt';
let path = 'plain.txt';
if (request.headers['x-magic'] === 'sekret') {

@@ -120,3 +120,3 @@ path = 'awesome.png';

var response = request.response;
const response = request.response;
if (response.isBoom &&

@@ -132,6 +132,2 @@ response.output.statusCode === 404) {

Note that paths for files served using the `reply.file()` handler are **NOT** guarded against
access outside the `files.relativeTo` directory, so be careful to guard against malevolent user
input.
## Usage

@@ -159,2 +155,6 @@

- `options` - optional settings:
- `confine` - serve file relative to this directory and returns `403 Forbidden` if the
`path` resolves outside the `confine` directory.
Defaults to `true` which uses the `relativeTo` route option as the `confine`.
Set to `false` to disable this security feature.
- `filename` - an optional filename to specify if sending a 'Content-Disposition' header,

@@ -192,2 +192,6 @@ defaults to the basename of `path`

- `path` - a path string or function as described above (required).
- `confine` - serve file relative to this directory and returns `403 Forbidden` if the
`path` resolves outside the `confine` directory.
Defaults to `true` which uses the `relativeTo` route option as the `confine`.
Set to `false` to disable this security feature.
- `filename` - an optional filename to specify if sending a 'Content-Disposition'

@@ -194,0 +198,0 @@ header, defaults to the basename of `path`

@@ -0,12 +1,14 @@

'use strict';
// Load modules
var Fs = require('fs');
var Os = require('os');
var Path = require('path');
var Boom = require('boom');
var Code = require('code');
var Hapi = require('hapi');
var Hoek = require('hoek');
var Inert = require('..');
var Lab = require('lab');
const Fs = require('fs');
const Os = require('os');
const Path = require('path');
const Boom = require('boom');
const Code = require('code');
const Hapi = require('hapi');
const Hoek = require('hoek');
const Inert = require('..');
const Lab = require('lab');

@@ -16,3 +18,3 @@

var internals = {};
const internals = {};

@@ -22,15 +24,15 @@

var lab = exports.lab = Lab.script();
var describe = lab.describe;
var it = lab.it;
var expect = Code.expect;
const lab = exports.lab = Lab.script();
const describe = lab.describe;
const it = lab.it;
const expect = Code.expect;
describe('directory', function () {
describe('directory', () => {
describe('handler()', function () {
describe('handler()', () => {
var provisionServer = function (connection, debug) {
const provisionServer = (connection, debug) => {
var server = new Hapi.Server({ debug: debug });
const server = new Hapi.Server({ debug: debug });
server.connection(connection || { routes: { files: { relativeTo: __dirname } }, router: { stripTrailingSlash: false } });

@@ -41,8 +43,8 @@ server.register(Inert, Hoek.ignore);

it('returns a 403 when no index exists and listing is disabled', function (done) {
it('returns a 403 when no index exists and listing is disabled', (done) => {
var server = provisionServer();
const server = provisionServer();
server.route({ method: 'GET', path: '/directory/{path*}', handler: { directory: { path: '.' } } }); // Use '.' to test path normalization
server.inject('/directory/', function (res) {
server.inject('/directory/', (res) => {

@@ -54,8 +56,8 @@ expect(res.statusCode).to.equal(403);

it('returns a 403 when requesting a path containing \'..\'', function (done) {
it('returns a 403 when requesting a path containing \'..\'', (done) => {
var server = provisionServer();
const server = provisionServer();
server.route({ method: 'GET', path: '/directory/{path*}', handler: { directory: { path: './' } } });
server.inject('/directory/..', function (res) {
server.inject('/directory/..', (res) => {

@@ -67,8 +69,8 @@ expect(res.statusCode).to.equal(403);

it('returns a 404 when requesting an unknown file within a directory', function (done) {
it('returns a 404 when requesting an unknown file within a directory', (done) => {
var server = provisionServer();
const server = provisionServer();
server.route({ method: 'GET', path: '/directory/{path*}', handler: { directory: { path: './' } } });
server.inject('/directory/xyz', function (res) {
server.inject('/directory/xyz', (res) => {

@@ -80,8 +82,8 @@ expect(res.statusCode).to.equal(404);

it('returns a file when requesting a file from the directory', function (done) {
it('returns a file when requesting a file from the directory', (done) => {
var server = provisionServer();
const server = provisionServer();
server.route({ method: 'GET', path: '/directory/{path*}', handler: { directory: { path: './' } } });
server.inject('/directory/directory.js', function (res) {
server.inject('/directory/directory.js', (res) => {

@@ -94,8 +96,8 @@ expect(res.statusCode).to.equal(200);

it('returns a file when requesting a file from multi directory setup', function (done) {
it('returns a file when requesting a file from multi directory setup', (done) => {
var server = provisionServer();
const server = provisionServer();
server.route({ method: 'GET', path: '/multiple/{path*}', handler: { directory: { path: ['./', '../'], listing: true } } });
server.inject('/multiple/package.json', function (res) {
server.inject('/multiple/package.json', (res) => {

@@ -108,5 +110,5 @@ expect(res.statusCode).to.equal(200);

it('returns a file when requesting a file from multi directory function response', function (done) {
it('returns a file when requesting a file from multi directory function response', (done) => {
var server = provisionServer();
const server = provisionServer();
server.route({

@@ -117,3 +119,3 @@ method: 'GET',

directory: {
path: function () {
path: () => {

@@ -127,3 +129,3 @@ return ['./', '../'];

server.inject('/multiple/package.json', function (res) {
server.inject('/multiple/package.json', (res) => {

@@ -136,8 +138,8 @@ expect(res.statusCode).to.equal(200);

it('returns the correct file when requesting a file from a child directory', function (done) {
it('returns the correct file when requesting a file from a child directory', (done) => {
var server = provisionServer();
const server = provisionServer();
server.route({ method: 'GET', path: '/directory/{path*}', handler: { directory: { path: './' } } });
server.inject('/directory/directory/index.html', function (res) {
server.inject('/directory/directory/index.html', (res) => {

@@ -150,8 +152,8 @@ expect(res.statusCode).to.equal(200);

it('returns the correct listing links when viewing top level path', function (done) {
it('returns the correct listing links when viewing top level path', (done) => {
var server = provisionServer();
const server = provisionServer();
server.route({ method: 'GET', path: '/{path*}', handler: { directory: { path: './', index: true, listing: true } } });
server.inject('/', function (res) {
server.inject('/', (res) => {

@@ -164,8 +166,8 @@ expect(res.statusCode).to.equal(200);

it('does not contain any double / when viewing sub path listing', function (done) {
it('does not contain any double / when viewing sub path listing', (done) => {
var server = provisionServer();
const server = provisionServer();
server.route({ method: 'GET', path: '/showindex/{path*}', handler: { directory: { path: './', index: true, listing: true } } });
server.inject('/showindex/', function (res) {
server.inject('/showindex/', (res) => {

@@ -178,8 +180,8 @@ expect(res.statusCode).to.equal(200);

it('has the correct link to sub folders when inside of a sub folder listing', function (done) {
it('has the correct link to sub folders when inside of a sub folder listing', (done) => {
var server = provisionServer();
const server = provisionServer();
server.route({ method: 'GET', path: '/showindex/{path*}', handler: { directory: { path: './', index: true, listing: true } } });
server.inject('/showindex/directory/subdir/', function (res) {
server.inject('/showindex/directory/subdir/', (res) => {

@@ -192,8 +194,8 @@ expect(res.statusCode).to.equal(200);

it('has the correct link to a sub folder with spaces when inside of a sub folder listing', function (done) {
it('has the correct link to a sub folder with spaces when inside of a sub folder listing', (done) => {
var server = provisionServer();
const server = provisionServer();
server.route({ method: 'GET', path: '/showindex/{path*}', handler: { directory: { path: './', index: true, listing: true } } });
server.inject('/showindex/directory/subdir/', function (res) {
server.inject('/showindex/directory/subdir/', (res) => {

@@ -206,8 +208,8 @@ expect(res.statusCode).to.equal(200);

it('has the correct link to a file when inside of a listing of a sub folder that is inside a subfolder with spaces', function (done) {
it('has the correct link to a file when inside of a listing of a sub folder that is inside a subfolder with spaces', (done) => {
var server = provisionServer();
const server = provisionServer();
server.route({ method: 'GET', path: '/showindex/{path*}', handler: { directory: { path: './', index: true, listing: true } } });
server.inject('/showindex/directory/subdir/sub%20subdir%3D/subsubsubdir/', function (res) {
server.inject('/showindex/directory/subdir/sub%20subdir%3D/subsubsubdir/', (res) => {

@@ -220,8 +222,8 @@ expect(res.statusCode).to.equal(200);

it('returns the correct file when requesting a file from a directory with spaces', function (done) {
it('returns the correct file when requesting a file from a directory with spaces', (done) => {
var server = provisionServer();
const server = provisionServer();
server.route({ method: 'GET', path: '/directory/{path*}', handler: { directory: { path: './', index: true, listing: true } } });
server.inject('/directory/directory/subdir/sub%20subdir%3D/test%24.json', function (res) {
server.inject('/directory/directory/subdir/sub%20subdir%3D/test%24.json', (res) => {

@@ -234,8 +236,8 @@ expect(res.statusCode).to.equal(200);

it('returns the correct file when requesting a file from a directory that its parent directory has spaces', function (done) {
it('returns the correct file when requesting a file from a directory that its parent directory has spaces', (done) => {
var server = provisionServer();
const server = provisionServer();
server.route({ method: 'GET', path: '/directory/{path*}', handler: { directory: { path: './', index: true, listing: true } } });
server.inject('/directory/directory/subdir/sub%20subdir%3D/subsubsubdir/test.txt', function (res) {
server.inject('/directory/directory/subdir/sub%20subdir%3D/subsubsubdir/test.txt', (res) => {

@@ -248,8 +250,8 @@ expect(res.statusCode).to.equal(200);

it('returns a 403 when index and listing are disabled', function (done) {
it('returns a 403 when index and listing are disabled', (done) => {
var server = provisionServer();
const server = provisionServer();
server.route({ method: 'GET', path: '/directoryx/{path*}', handler: { directory: { path: '../', index: false } } });
server.inject('/directoryx/', function (res) {
server.inject('/directoryx/', (res) => {

@@ -261,8 +263,8 @@ expect(res.statusCode).to.equal(403);

it('returns a list of files when listing is enabled', function (done) {
it('returns a list of files when listing is enabled', (done) => {
var server = provisionServer();
const server = provisionServer();
server.route({ method: 'GET', path: '/directorylist/{path*}', handler: { directory: { path: '../', listing: true } } });
server.inject('/directorylist/', function (res) {
server.inject('/directorylist/', (res) => {

@@ -275,8 +277,8 @@ expect(res.statusCode).to.equal(200);

it('returns a list of files for subdirectory', function (done) {
it('returns a list of files for subdirectory', (done) => {
var server = provisionServer();
const server = provisionServer();
server.route({ method: 'GET', path: '/directorylist/{path*}', handler: { directory: { path: '../', listing: true } } });
server.inject('/directorylist/test/', function (res) {
server.inject('/directorylist/test/', (res) => {

@@ -289,8 +291,8 @@ expect(res.statusCode).to.equal(200);

it('returns a list of files when listing is enabled and index disabled', function (done) {
it('returns a list of files when listing is enabled and index disabled', (done) => {
var server = provisionServer();
const server = provisionServer();
server.route({ method: 'GET', path: '/directorylistx/{path*}', handler: { directory: { path: '../', listing: true, index: false } } });
server.inject('/directorylistx/', function (res) {
server.inject('/directorylistx/', (res) => {

@@ -303,8 +305,8 @@ expect(res.statusCode).to.equal(200);

it('returns the "index.html" index file when found and default index enabled', function (done) {
it('returns the "index.html" index file when found and default index enabled', (done) => {
var server = provisionServer();
const server = provisionServer();
server.route({ method: 'GET', path: '/directoryIndex/{path*}', handler: { directory: { path: './directory/' } } });
server.inject('/directoryIndex/', function (res) {
server.inject('/directoryIndex/', (res) => {

@@ -317,8 +319,8 @@ expect(res.statusCode).to.equal(200);

it('returns the index file when found and single custom index file specified', function (done) {
it('returns the index file when found and single custom index file specified', (done) => {
var server = provisionServer();
const server = provisionServer();
server.route({ method: 'GET', path: '/directoryIndex/{path*}', handler: { directory: { path: './directory/', index: 'index.js' } } });
server.inject('/directoryIndex/', function (res) {
server.inject('/directoryIndex/', (res) => {

@@ -331,8 +333,8 @@ expect(res.statusCode).to.equal(200);

it('returns the first index file found when an array of index files is specified', function (done) {
it('returns the first index file found when an array of index files is specified', (done) => {
var server = provisionServer();
const server = provisionServer();
server.route({ method: 'GET', path: '/directoryIndex/{path*}', handler: { directory: { path: './directory/', index: ['default.html', 'index.js', 'non.existing'] } } });
server.inject('/directoryIndex/', function (res) {
server.inject('/directoryIndex/', (res) => {

@@ -345,8 +347,8 @@ expect(res.statusCode).to.equal(200);

it('returns a 403 when listing is disabled and a custom index file is specified but not found', function (done) {
it('returns a 403 when listing is disabled and a custom index file is specified but not found', (done) => {
var server = provisionServer();
const server = provisionServer();
server.route({ method: 'GET', path: '/directoryIndex/{path*}', handler: { directory: { path: './directory/', index: 'default.html' } } });
server.inject('/directoryIndex/', function (res) {
server.inject('/directoryIndex/', (res) => {

@@ -358,8 +360,8 @@ expect(res.statusCode).to.equal(403);

it('returns a 403 when listing is disabled and an array of index files is specified but none were found', function (done) {
it('returns a 403 when listing is disabled and an array of index files is specified but none were found', (done) => {
var server = provisionServer();
const server = provisionServer();
server.route({ method: 'GET', path: '/directoryIndex/{path*}', handler: { directory: { path: './directory/', index: ['default.html', 'non.existing'] } } });
server.inject('/directoryIndex/', function (res) {
server.inject('/directoryIndex/', (res) => {

@@ -371,8 +373,8 @@ expect(res.statusCode).to.equal(403);

it('returns the index when served from a hidden folder', function (done) {
it('returns the index when served from a hidden folder', (done) => {
var server = provisionServer();
const server = provisionServer();
server.route({ method: 'GET', path: '/{path*}', handler: { directory: { path: './directory/.dot' } } });
server.inject('/index.html', function (res) {
server.inject('/index.html', (res) => {

@@ -382,3 +384,3 @@ expect(res.statusCode).to.equal(200);

server.inject('/', function (res2) {
server.inject('/', (res2) => {

@@ -392,8 +394,8 @@ expect(res2.statusCode).to.equal(200);

it('returns listing when served from a hidden folder', function (done) {
it('returns listing when served from a hidden folder', (done) => {
var server = provisionServer();
const server = provisionServer();
server.route({ method: 'GET', path: '/{path*}', handler: { directory: { path: './directory/.dot', index: false, listing: true } } });
server.inject('/', function (res) {
server.inject('/', (res) => {

@@ -406,8 +408,8 @@ expect(res.statusCode).to.equal(200);

it('returns a 500 when index.html is a directory', function (done) {
it('returns a 500 when index.html is a directory', (done) => {
var server = provisionServer(null, false);
const server = provisionServer(null, false);
server.route({ method: 'GET', path: '/directoryIndex/{path*}', handler: { directory: { path: './directory/' } } });
server.inject('/directoryIndex/invalid/', function (res) {
server.inject('/directoryIndex/invalid/', (res) => {

@@ -419,8 +421,8 @@ expect(res.statusCode).to.equal(500);

it('returns a 500 when the custom index is a directory', function (done) {
it('returns a 500 when the custom index is a directory', (done) => {
var server = provisionServer(null, false);
const server = provisionServer(null, false);
server.route({ method: 'GET', path: '/directoryIndex/{path*}', handler: { directory: { path: './directory/', index: 'misc' } } });
server.inject('/directoryIndex/invalid/', function (res) {
server.inject('/directoryIndex/invalid/', (res) => {

@@ -432,5 +434,5 @@ expect(res.statusCode).to.equal(500);

it('returns the correct file when using a fn directory handler', function (done) {
it('returns the correct file when using a fn directory handler', (done) => {
var directoryFn = function (request) {
const directoryFn = (request) => {

@@ -440,6 +442,6 @@ return '../lib';

var server = provisionServer();
const server = provisionServer();
server.route({ method: 'GET', path: '/directoryfn/{path?}', handler: { directory: { path: directoryFn } } });
server.inject('/directoryfn/index.js', function (res) {
server.inject('/directoryfn/index.js', (res) => {

@@ -452,8 +454,8 @@ expect(res.statusCode).to.equal(200);

it('returns listing with hidden files when hidden files should be shown', function (done) {
it('returns listing with hidden files when hidden files should be shown', (done) => {
var server = provisionServer();
const server = provisionServer();
server.route({ method: 'GET', path: '/showhidden/{path*}', handler: { directory: { path: './', showHidden: true, listing: true } } });
server.inject('/showhidden/', function (res) {
server.inject('/showhidden/', (res) => {

@@ -465,8 +467,8 @@ expect(res.payload).to.contain('.hidden');

it('returns listing without hidden files when hidden files should not be shown', function (done) {
it('returns listing without hidden files when hidden files should not be shown', (done) => {
var server = provisionServer();
const server = provisionServer();
server.route({ method: 'GET', path: '/noshowhidden/{path*}', handler: { directory: { path: './', listing: true } } });
server.inject('/noshowhidden/', function (res) {
server.inject('/noshowhidden/', (res) => {

@@ -479,8 +481,8 @@ expect(res.payload).to.not.contain('.hidden');

it('returns a 404 response when requesting a hidden file when showHidden is disabled', function (done) {
it('returns a 404 response when requesting a hidden file when showHidden is disabled', (done) => {
var server = provisionServer();
const server = provisionServer();
server.route({ method: 'GET', path: '/noshowhidden/{path*}', handler: { directory: { path: './', listing: true } } });
server.inject('/noshowhidden/.hidden', function (res) {
server.inject('/noshowhidden/.hidden', (res) => {

@@ -492,12 +494,12 @@ expect(res.statusCode).to.equal(404);

it('returns a 404 response when requesting a file in a hidden directory when showHidden is disabled', function (done) {
it('returns a 404 response when requesting a file in a hidden directory when showHidden is disabled', (done) => {
var server = provisionServer();
const server = provisionServer();
server.route({ method: 'GET', path: '/noshowhidden/{path*}', handler: { directory: { path: './directory', listing: true } } });
server.inject('/noshowhidden/.dot/index.html', function (res) {
server.inject('/noshowhidden/.dot/index.html', (res) => {
expect(res.statusCode).to.equal(404);
server.inject('/noshowhidden/.dot/', function (res2) {
server.inject('/noshowhidden/.dot/', (res2) => {

@@ -510,8 +512,8 @@ expect(res2.statusCode).to.equal(404);

it('returns a 404 response when requesting a hidden directory listing when showHidden is disabled', function (done) {
it('returns a 404 response when requesting a hidden directory listing when showHidden is disabled', (done) => {
var server = provisionServer();
const server = provisionServer();
server.route({ method: 'GET', path: '/noshowhidden/{path*}', handler: { directory: { path: './directory', listing: true, index: false } } });
server.inject('/noshowhidden/.dot/', function (res) {
server.inject('/noshowhidden/.dot/', (res) => {

@@ -523,8 +525,8 @@ expect(res.statusCode).to.equal(404);

it('returns a file when requesting a hidden file when showHidden is enabled', function (done) {
it('returns a file when requesting a hidden file when showHidden is enabled', (done) => {
var server = provisionServer();
const server = provisionServer();
server.route({ method: 'GET', path: '/showhidden/{path*}', handler: { directory: { path: './', showHidden: true, listing: true } } });
server.inject('/showhidden/.hidden', function (res) {
server.inject('/showhidden/.hidden', (res) => {

@@ -536,8 +538,8 @@ expect(res.payload).to.contain('Ssssh!');

it('returns a a file when requesting a file in a hidden directory when showHidden is enabled', function (done) {
it('returns a a file when requesting a file in a hidden directory when showHidden is enabled', (done) => {
var server = provisionServer();
const server = provisionServer();
server.route({ method: 'GET', path: '/noshowhidden/{path*}', handler: { directory: { path: './directory', showHidden: true, listing: true } } });
server.inject('/noshowhidden/.dot/index.html', function (res) {
server.inject('/noshowhidden/.dot/index.html', (res) => {

@@ -547,3 +549,3 @@ expect(res.statusCode).to.equal(200);

server.inject('/noshowhidden/.dot/', function (res2) {
server.inject('/noshowhidden/.dot/', (res2) => {

@@ -557,8 +559,8 @@ expect(res2.statusCode).to.equal(200);

it('redirects to the same path with / appended if asking for a directory', function (done) {
it('redirects to the same path with / appended if asking for a directory', (done) => {
var server = provisionServer();
const server = provisionServer();
server.route({ method: 'GET', path: '/redirect/{path*}', handler: { directory: { path: './', index: true, listing: true } } });
server.inject('/redirect/directory/subdir', function (res) {
server.inject('/redirect/directory/subdir', (res) => {

@@ -571,8 +573,8 @@ expect(res.statusCode).to.equal(302);

it('does not redirect to the same path with / appended redirectToSlash disabled', function (done) {
it('does not redirect to the same path with / appended redirectToSlash disabled', (done) => {
var server = provisionServer();
const server = provisionServer();
server.route({ method: 'GET', path: '/redirect/{path*}', handler: { directory: { path: './', index: true, listing: true, redirectToSlash: false } } });
server.inject('http://example.com/redirect/directory/subdir', function (res) {
server.inject('http://example.com/redirect/directory/subdir', (res) => {

@@ -585,8 +587,8 @@ expect(res.statusCode).to.equal(200);

it('does not redirect to the same path with / appended when server stripTrailingSlash is true', function (done) {
it('does not redirect to the same path with / appended when server stripTrailingSlash is true', (done) => {
var server = provisionServer({ routes: { files: { relativeTo: __dirname } }, router: { stripTrailingSlash: true } });
const server = provisionServer({ routes: { files: { relativeTo: __dirname } }, router: { stripTrailingSlash: true } });
server.route({ method: 'GET', path: '/redirect/{path*}', handler: { directory: { path: './', index: true, listing: true } } });
server.inject('http://example.com/redirect/directory/subdir', function (res) {
server.inject('http://example.com/redirect/directory/subdir', (res) => {

@@ -599,8 +601,8 @@ expect(res.statusCode).to.equal(200);

it('ignores unused path params', function (done) {
it('ignores unused path params', (done) => {
var server = provisionServer();
const server = provisionServer();
server.route({ method: 'GET', path: '/{ignore}/4/{path*}', handler: { directory: { path: './' } } });
server.inject('/crap/4/file.js', function (res) {
server.inject('/crap/4/file.js', (res) => {

@@ -613,8 +615,8 @@ expect(res.statusCode).to.equal(200);

it('returns error when failing to prepare file response due to bad state', function (done) {
it('returns error when failing to prepare file response due to bad state', (done) => {
var server = provisionServer(null, false);
const server = provisionServer(null, false);
server.route({ method: 'GET', path: '/directory/{path*}', handler: { directory: { path: './' } } });
server.ext('onRequest', function (request, reply) {
server.ext('onRequest', (request, reply) => {

@@ -625,3 +627,3 @@ reply.state('bad', {});

server.inject('/directory/file.js', function (res) {
server.inject('/directory/file.js', (res) => {

@@ -633,9 +635,9 @@ expect(res.statusCode).to.equal(500);

it('returns error when listing fails due to directory read error', { parallel: false }, function (done) {
it('returns error when listing fails due to directory read error', { parallel: false }, (done) => {
var server = provisionServer();
const server = provisionServer();
server.route({ method: 'GET', path: '/directorylist/{path*}', handler: { directory: { path: '../', listing: true } } });
var orig = Fs.readdir;
Fs.readdir = function (path, callback) {
const orig = Fs.readdir;
Fs.readdir = (path, callback) => {

@@ -646,3 +648,3 @@ Fs.readdir = orig;

server.inject('/directorylist/', function (res) {
server.inject('/directorylist/', (res) => {

@@ -654,8 +656,8 @@ expect(res.statusCode).to.equal(500);

it('appends default extension', function (done) {
it('appends default extension', (done) => {
var server = provisionServer();
const server = provisionServer();
server.route({ method: 'GET', path: '/directory/{path*}', handler: { directory: { path: __dirname, defaultExtension: 'html' } } });
server.inject('/directory/directory/index', function (res) {
server.inject('/directory/directory/index', (res) => {

@@ -667,8 +669,8 @@ expect(res.statusCode).to.equal(200);

it('appends default extension when resource ends with /', function (done) {
it('appends default extension when resource ends with /', (done) => {
var server = provisionServer();
const server = provisionServer();
server.route({ method: 'GET', path: '/directory/{path*}', handler: { directory: { path: __dirname, defaultExtension: 'html' } } });
server.inject('/directory/directory/index/', function (res) {
server.inject('/directory/directory/index/', (res) => {

@@ -680,8 +682,8 @@ expect(res.statusCode).to.equal(200);

it('appends default extension and fails to find file', function (done) {
it('appends default extension and fails to find file', (done) => {
var server = provisionServer();
const server = provisionServer();
server.route({ method: 'GET', path: '/directory/{path*}', handler: { directory: { path: __dirname, defaultExtension: 'html' } } });
server.inject('/directory/directory/none', function (res) {
server.inject('/directory/directory/none', (res) => {

@@ -693,8 +695,8 @@ expect(res.statusCode).to.equal(404);

it('does not append default extension when directory exists', function (done) {
it('does not append default extension when directory exists', (done) => {
var server = provisionServer();
const server = provisionServer();
server.route({ method: 'GET', path: '/directory/{path*}', handler: { directory: { path: __dirname, defaultExtension: 'html' } } });
server.inject('/directory/directory', function (res) {
server.inject('/directory/directory', (res) => {

@@ -706,5 +708,5 @@ expect(res.statusCode).to.equal(302);

it('resolves path name from plugin using specified path', function (done) {
it('resolves path name from plugin using specified path', (done) => {
var plugin = function (server, options, next) {
const plugin = (server, options, next) => {

@@ -720,6 +722,6 @@ server.path(__dirname);

var server = provisionServer({ router: { stripTrailingSlash: false } });
server.register({ register: plugin }, {}, function () { });
const server = provisionServer({ router: { stripTrailingSlash: false } });
server.register({ register: plugin }, {}, () => { });
server.inject('/test/index.html', function (res) {
server.inject('/test/index.html', (res) => {

@@ -731,5 +733,5 @@ expect(res.statusCode).to.equal(200);

it('resolves path name from plugin using relative path', function (done) {
it('resolves path name from plugin using relative path', (done) => {
var plugin = function (server, options, next) {
const plugin = (server, options, next) => {

@@ -744,6 +746,6 @@ server.route({ method: 'GET', path: '/test/{path*}', config: { handler: { directory: { path: Path.join('.', 'test', 'directory'), index: false, listing: false } } } });

var server = provisionServer({ router: { stripTrailingSlash: false } });
server.register({ register: plugin }, {}, function () { });
const server = provisionServer({ router: { stripTrailingSlash: false } });
server.register({ register: plugin }, {}, () => { });
server.inject('/test/index.html', function (res) {
server.inject('/test/index.html', (res) => {

@@ -755,8 +757,8 @@ expect(res.statusCode).to.equal(200);

it('resolves root pathnames', function (done) {
it('resolves root pathnames', (done) => {
var server = provisionServer({ router: { stripTrailingSlash: false } });
const server = provisionServer({ router: { stripTrailingSlash: false } });
server.route({ method: 'GET', path: '/test/{path*}', handler: { directory: { path: Path.join(__dirname, 'directory') } } });
server.inject('/test/index.html', function (res) {
server.inject('/test/index.html', (res) => {

@@ -768,8 +770,8 @@ expect(res.statusCode).to.equal(200);

it('resolves relative pathnames', function (done) {
it('resolves relative pathnames', (done) => {
var server = provisionServer({ router: { stripTrailingSlash: false } });
const server = provisionServer({ router: { stripTrailingSlash: false } });
server.route({ method: 'GET', path: '/test/{path*}', handler: { directory: { path: Path.join('.', 'test', 'directory') } } });
server.inject('/test/index.html', function (res) {
server.inject('/test/index.html', (res) => {

@@ -781,5 +783,5 @@ expect(res.statusCode).to.equal(200);

it('returns error when path function returns error', function (done) {
it('returns error when path function returns error', (done) => {
var path = function () {
const path = () => {

@@ -789,6 +791,6 @@ return Boom.badRequest('Really?!');

var server = provisionServer();
const server = provisionServer();
server.route({ method: 'GET', path: '/test/{path*}', handler: { directory: { path: path } } });
server.inject('/test/index.html', function (res) {
server.inject('/test/index.html', (res) => {

@@ -801,5 +803,5 @@ expect(res.statusCode).to.equal(400);

it('returns error when path function returns invalid response', function (done) {
it('returns error when path function returns invalid response', (done) => {
var path = function () {
const path = () => {

@@ -809,6 +811,6 @@ return 5;

var server = provisionServer(null, false);
const server = provisionServer(null, false);
server.route({ method: 'GET', path: '/test/{path*}', handler: { directory: { path: path } } });
server.inject('/test/index.html', function (res) {
server.inject('/test/index.html', (res) => {

@@ -820,8 +822,8 @@ expect(res.statusCode).to.equal(500);

it('returns a gzipped file using precompressed file', function (done) {
it('returns a gzipped file using precompressed file', (done) => {
var server = provisionServer();
const server = provisionServer();
server.route({ method: 'GET', path: '/{p*}', handler: { directory: { path: './file', lookupCompressed: true } } });
server.inject({ url: '/image.png', headers: { 'accept-encoding': 'gzip' } }, function (res) {
server.inject({ url: '/image.png', headers: { 'accept-encoding': 'gzip' } }, (res) => {

@@ -831,3 +833,3 @@ expect(res.headers['content-type']).to.equal('image/png');

var content = Fs.readFileSync('./test/file/image.png.gz');
const content = Fs.readFileSync('./test/file/image.png.gz');
expect(res.headers['content-length']).to.equal(content.length);

@@ -839,8 +841,8 @@ expect(res.rawPayload.length).to.equal(content.length);

it('respects the etagMethod option', function (done) {
it('respects the etagMethod option', (done) => {
var server = provisionServer();
const server = provisionServer();
server.route({ method: 'GET', path: '/{p*}', handler: { directory: { path: './file', etagMethod: 'simple' } } });
server.inject('/image.png', function (res) {
server.inject('/image.png', (res) => {

@@ -852,8 +854,8 @@ expect(res.headers.etag).to.match(/^".+-.+"$/);

it('returns a 403 when missing file read permission', function (done) {
it('returns a 403 when missing file read permission', (done) => {
var filename = Hoek.uniqueFilename(Os.tmpDir());
const filename = Hoek.uniqueFilename(Os.tmpDir());
Fs.writeFileSync(filename, 'data');
var fd;
let fd;
if (process.platform === 'win32') {

@@ -863,10 +865,11 @@ // make a permissionless file by unlinking an open file

Fs.unlinkSync(filename);
} else {
}
else {
Fs.chmodSync(filename, 0);
}
var server = provisionServer();
const server = provisionServer();
server.route({ method: 'GET', path: '/test/{path*}', handler: { directory: { path: Path.dirname(filename) } } });
server.inject('/test/' + Path.basename(filename), function (res) {
server.inject('/test/' + Path.basename(filename), (res) => {

@@ -876,3 +879,4 @@ // cleanup

Fs.closeSync(fd);
} else {
}
else {
Fs.unlinkSync(filename);

@@ -886,16 +890,16 @@ }

it('returns error when a file open fails', function (done) {
it('returns error when a file open fails', (done) => {
var orig = Fs.open;
const orig = Fs.open;
Fs.open = function () { // can return EMFILE error
Fs.open = orig;
var callback = arguments[arguments.length - 1];
const callback = arguments[arguments.length - 1];
callback(new Error('failed'));
};
var server = provisionServer();
const server = provisionServer();
server.route({ method: 'GET', path: '/test/{path*}', handler: { directory: { path: './' } } });
server.inject('/test/fail', function (res) {
server.inject('/test/fail', (res) => {

@@ -907,8 +911,8 @@ expect(res.statusCode).to.equal(500);

it('returns a 404 for null byte paths', function (done) {
it('returns a 404 for null byte paths', (done) => {
var server = provisionServer();
const server = provisionServer();
server.route({ method: 'GET', path: '/{path*}', handler: { directory: { path: './' } } });
server.inject('/index%00.html', function (res) {
server.inject('/index%00.html', (res) => {

@@ -920,6 +924,6 @@ expect(res.statusCode).to.equal(404);

it('only stats the file system once when requesting a file', function (done) {
it('only stats the file system once when requesting a file', (done) => {
var orig = Fs.fstat;
var callCnt = 0;
const orig = Fs.fstat;
let callCnt = 0;
Fs.fstat = function () {

@@ -931,6 +935,6 @@

var server = provisionServer();
const server = provisionServer();
server.route({ method: 'GET', path: '/directory/{path*}', handler: { directory: { path: './' } } });
server.inject('/directory/directory.js', function (res) {
server.inject('/directory/directory.js', (res) => {

@@ -937,0 +941,0 @@ Fs.fstat = orig;

@@ -0,14 +1,16 @@

'use strict';
// Load modules
var ChildProcess = require('child_process');
var Fs = require('fs');
var Os = require('os');
var Path = require('path');
var Boom = require('boom');
var Code = require('code');
var Hapi = require('hapi');
var Hoek = require('hoek');
var Items = require('items');
var Inert = require('..');
var Lab = require('lab');
const ChildProcess = require('child_process');
const Fs = require('fs');
const Os = require('os');
const Path = require('path');
const Boom = require('boom');
const Code = require('code');
const Hapi = require('hapi');
const Hoek = require('hoek');
const Items = require('items');
const Inert = require('..');
const Lab = require('lab');

@@ -18,3 +20,3 @@

var internals = {};
const internals = {};

@@ -24,15 +26,15 @@

var lab = exports.lab = Lab.script();
var describe = lab.describe;
var it = lab.it;
var expect = Code.expect;
const lab = exports.lab = Lab.script();
const describe = lab.describe;
const it = lab.it;
const expect = Code.expect;
describe('file', function () {
describe('file', () => {
describe('handler()', function () {
describe('handler()', () => {
var provisionServer = function (connection, etagsCacheMaxSize) {
const provisionServer = (connection, etagsCacheMaxSize) => {
var server = new Hapi.Server();
const server = new Hapi.Server();
server.connection(connection || {});

@@ -43,8 +45,8 @@ server.register(etagsCacheMaxSize !== undefined ? { register: Inert, options: { etagsCacheMaxSize: etagsCacheMaxSize } } : Inert, Hoek.ignore);

it('returns a file in the response with the correct headers', function (done) {
it('returns a file in the response with the correct headers', (done) => {
var server = provisionServer({ routes: { files: { relativeTo: __dirname } } });
var handler = function (request, reply) {
const server = provisionServer({ routes: { files: { relativeTo: __dirname } } });
const handler = (request, reply) => {
reply.file('../package.json').code(499);
reply.file('package.json', { confine: '../' }).code(499);
};

@@ -54,3 +56,3 @@

server.inject('/file', function (res) {
server.inject('/file', (res) => {

@@ -66,8 +68,8 @@ expect(res.statusCode).to.equal(499);

it('returns a file using route relativeTo', function (done) {
it('returns a file using route relativeTo', (done) => {
var server = provisionServer();
var handler = function (request, reply) {
const server = provisionServer();
const handler = (request, reply) => {
reply.file('../package.json');
reply.file('../package.json', { confine: false });
};

@@ -77,3 +79,3 @@

server.inject('/file', function (res) {
server.inject('/file', (res) => {

@@ -86,8 +88,8 @@ expect(res.statusCode).to.equal(200);

it('returns a file in the response with the correct headers using cwd relative paths without content-disposition header', function (done) {
it('returns a file in the response with the correct headers using cwd relative paths without content-disposition header', (done) => {
var server = provisionServer();
const server = provisionServer();
server.route({ method: 'GET', path: '/', handler: { file: './package.json' } });
server.inject('/', function (res) {
server.inject('/', (res) => {

@@ -103,8 +105,8 @@ expect(res.statusCode).to.equal(200);

it('returns a file in the response with the inline content-disposition header when using route config', function (done) {
it('returns a file in the response with the inline content-disposition header when using route config', (done) => {
var server = provisionServer({ routes: { files: { relativeTo: './' } } });
const server = provisionServer({ routes: { files: { relativeTo: './' } } });
server.route({ method: 'GET', path: '/', handler: { file: { path: './package.json', mode: 'inline' } } });
server.inject('/', function (res) {
server.inject('/', (res) => {

@@ -120,8 +122,8 @@ expect(res.statusCode).to.equal(200);

it('returns a file in the response with the inline content-disposition header when using route config and overriding filename', function (done) {
it('returns a file in the response with the inline content-disposition header when using route config and overriding filename', (done) => {
var server = provisionServer({ routes: { files: { relativeTo: './' } } });
const server = provisionServer({ routes: { files: { relativeTo: './' } } });
server.route({ method: 'GET', path: '/', handler: { file: { path: './package.json', mode: 'inline', filename: 'attachment.json' } } });
server.inject('/', function (res) {
server.inject('/', (res) => {

@@ -137,8 +139,8 @@ expect(res.statusCode).to.equal(200);

it('returns a file in the response with the attachment content-disposition header when using route config', function (done) {
it('returns a file in the response with the attachment content-disposition header when using route config', (done) => {
var server = provisionServer();
const server = provisionServer();
server.route({ method: 'GET', path: '/', handler: { file: { path: './package.json', mode: 'attachment' } } });
server.inject('/', function (res) {
server.inject('/', (res) => {

@@ -154,8 +156,8 @@ expect(res.statusCode).to.equal(200);

it('returns a file in the response with the attachment content-disposition header when using route config and overriding filename', function (done) {
it('returns a file in the response with the attachment content-disposition header when using route config and overriding filename', (done) => {
var server = provisionServer();
const server = provisionServer();
server.route({ method: 'GET', path: '/', handler: { file: { path: './package.json', mode: 'attachment', filename: 'attachment.json' } } });
server.inject('/', function (res) {
server.inject('/', (res) => {

@@ -171,8 +173,8 @@ expect(res.statusCode).to.equal(200);

it('returns a file in the response without the content-disposition header when using route config mode false', function (done) {
it('returns a file in the response without the content-disposition header when using route config mode false', (done) => {
var server = provisionServer();
const server = provisionServer();
server.route({ method: 'GET', path: '/', handler: { file: { path: './package.json', mode: false } } });
server.inject('/', function (res) {
server.inject('/', (res) => {

@@ -188,8 +190,8 @@ expect(res.statusCode).to.equal(200);

it('returns a file with correct headers when using attachment mode', function (done) {
it('returns a file with correct headers when using attachment mode', (done) => {
var server = provisionServer({ routes: { files: { relativeTo: __dirname } } });
var handler = function (request, reply) {
const server = provisionServer({ routes: { files: { relativeTo: __dirname } } });
const handler = (request, reply) => {
reply.file(Path.join(__dirname, '..', 'package.json'), { mode: 'attachment' });
reply.file(Path.join(__dirname, '..', 'package.json'), { confine: '..', mode: 'attachment' });
};

@@ -199,3 +201,3 @@

server.inject('/file', function (res) {
server.inject('/file', (res) => {

@@ -211,8 +213,8 @@ expect(res.statusCode).to.equal(200);

it('returns a file with correct headers when using attachment mode and overriding the filename', function (done) {
it('returns a file with correct headers when using attachment mode and overriding the filename', (done) => {
var server = provisionServer({ routes: { files: { relativeTo: __dirname } } });
var handler = function (request, reply) {
const server = provisionServer({ routes: { files: { relativeTo: __dirname } } });
const handler = (request, reply) => {
reply.file(Path.join(__dirname, '..', 'package.json'), { mode: 'attachment', filename: 'attachment.json' });
reply.file(Path.join(__dirname, '..', 'package.json'), { confine: '..', mode: 'attachment', filename: 'attachment.json' });
};

@@ -222,3 +224,3 @@

server.inject('/file', function (res) {
server.inject('/file', (res) => {

@@ -234,8 +236,8 @@ expect(res.statusCode).to.equal(200);

it('returns a file with correct headers when using inline mode', function (done) {
it('returns a file with correct headers when using inline mode', (done) => {
var server = provisionServer({ routes: { files: { relativeTo: __dirname } } });
var handler = function (request, reply) {
const server = provisionServer({ routes: { files: { relativeTo: __dirname } } });
const handler = (request, reply) => {
reply.file(Path.join(__dirname, '..', 'package.json'), { mode: 'inline' });
reply.file(Path.join(__dirname, '..', 'package.json'), { confine: '..', mode: 'inline' });
};

@@ -245,3 +247,3 @@

server.inject('/file', function (res) {
server.inject('/file', (res) => {

@@ -257,8 +259,8 @@ expect(res.statusCode).to.equal(200);

it('returns a file with correct headers when using inline mode and overriding filename', function (done) {
it('returns a file with correct headers when using inline mode and overriding filename', (done) => {
var server = provisionServer({ routes: { files: { relativeTo: __dirname } } });
var handler = function (request, reply) {
const server = provisionServer({ routes: { files: { relativeTo: __dirname } } });
const handler = (request, reply) => {
reply.file(Path.join(__dirname, '..', 'package.json'), { mode: 'inline', filename: 'attachment.json' });
reply.file(Path.join(__dirname, '..', 'package.json'), { confine: '..', mode: 'inline', filename: 'attachment.json' });
};

@@ -268,3 +270,3 @@

server.inject('/file', function (res) {
server.inject('/file', (res) => {

@@ -280,9 +282,9 @@ expect(res.statusCode).to.equal(200);

it('returns a 404 when the file is not found', function (done) {
it('returns a 404 when the file is not found', (done) => {
var server = provisionServer({ routes: { files: { relativeTo: '/no/such/path/x1' } } });
const server = provisionServer({ routes: { files: { relativeTo: '/no/such/path/x1' } } });
server.route({ method: 'GET', path: '/filenotfound', handler: { file: 'nopes' } });
server.inject('/filenotfound', function (res) {
server.inject('/filenotfound', (res) => {

@@ -294,9 +296,9 @@ expect(res.statusCode).to.equal(404);

it('returns a 403 when the file is a directory', function (done) {
it('returns a 403 when the file is a directory', (done) => {
var server = provisionServer();
const server = provisionServer();
server.route({ method: 'GET', path: '/filefolder', handler: { file: 'lib' } });
server.inject('/filefolder', function (res) {
server.inject('/filefolder', (res) => {

@@ -308,8 +310,8 @@ expect(res.statusCode).to.equal(403);

it('returns a file using the built-in handler config', function (done) {
it('returns a file using the built-in handler config', (done) => {
var server = provisionServer({ routes: { files: { relativeTo: __dirname } } });
const server = provisionServer({ routes: { files: { relativeTo: Path.join(__dirname, '..') } } });
server.route({ method: 'GET', path: '/staticfile', handler: { file: Path.join(__dirname, '..', 'package.json') } });
server.inject('/staticfile', function (res) {
server.inject('/staticfile', (res) => {

@@ -324,13 +326,13 @@ expect(res.statusCode).to.equal(200);

it('returns a file using the file function with the built-in handler config', function (done) {
it('returns a file using the file function with the built-in handler config', (done) => {
var filenameFn = function (request) {
const filenameFn = (request) => {
return '../lib/' + request.params.file;
return './lib/' + request.params.file;
};
var server = provisionServer({ routes: { files: { relativeTo: __dirname } } });
const server = provisionServer({ routes: { files: { relativeTo: Path.join(__dirname, '..') } } });
server.route({ method: 'GET', path: '/filefn/{file}', handler: { file: filenameFn } });
server.inject('/filefn/index.js', function (res) {
server.inject('/filefn/index.js', (res) => {

@@ -345,8 +347,8 @@ expect(res.statusCode).to.equal(200);

it('returns a file in the response with the correct headers (relative path)', function (done) {
it('returns a file in the response with the correct headers (relative path)', (done) => {
var server = provisionServer({ routes: { files: { relativeTo: '.' } } });
var relativeHandler = function (request, reply) {
const server = provisionServer({ routes: { files: { relativeTo: '.' } } });
const relativeHandler = (request, reply) => {
reply.file('./package.json');
reply.file('./package.json', { confine: true });
};

@@ -356,3 +358,3 @@

server.inject('/relativefile', function (res) {
server.inject('/relativefile', (res) => {

@@ -367,8 +369,8 @@ expect(res.statusCode).to.equal(200);

it('returns a file using the built-in handler config (relative path)', function (done) {
it('returns a file using the built-in handler config (relative path)', (done) => {
var server = provisionServer({ routes: { files: { relativeTo: __dirname } } });
server.route({ method: 'GET', path: '/relativestaticfile', handler: { file: '../package.json' } });
const server = provisionServer({ routes: { files: { relativeTo: Path.join(__dirname, '..') } } });
server.route({ method: 'GET', path: '/relativestaticfile', handler: { file: './package.json' } });
server.inject('/relativestaticfile', function (res) {
server.inject('/relativestaticfile', (res) => {

@@ -383,8 +385,8 @@ expect(res.statusCode).to.equal(200);

it('returns a file with default mime type', function (done) {
it('returns a file with default mime type', (done) => {
var server = provisionServer();
const server = provisionServer();
server.route({ method: 'GET', path: '/', handler: { file: Path.join(__dirname, '..', 'LICENSE') } });
server.inject('/', function (res) {
server.inject('/', (res) => {

@@ -397,8 +399,8 @@ expect(res.statusCode).to.equal(200);

it('returns a file in the response with the correct headers using custom mime type', function (done) {
it('returns a file in the response with the correct headers using custom mime type', (done) => {
var server = provisionServer({ routes: { files: { relativeTo: __dirname } } });
var handler = function (request, reply) {
const server = provisionServer({ routes: { files: { relativeTo: __dirname } } });
const handler = (request, reply) => {
reply.file('../LICENSE').type('application/example');
reply.file('../LICENSE', { confine: false }).type('application/example');
};

@@ -408,3 +410,3 @@

server.inject('/file', function (res) {
server.inject('/file', (res) => {

@@ -417,10 +419,10 @@ expect(res.statusCode).to.equal(200);

it('handles multiple simultaneous requests', function (done) {
it('handles multiple simultaneous requests', (done) => {
var server = provisionServer();
const server = provisionServer();
server.route({ method: 'GET', path: '/file', handler: { file: Path.join(__dirname, '..', 'package.json') } });
Items.parallel(['/file', '/file'], function (req, next) {
Items.parallel(['/file', '/file'], (req, next) => {
server.inject(req, function (res) {
server.inject(req, (res) => {

@@ -435,8 +437,8 @@ expect(res.statusCode).to.equal(200);

it('does not cache etags', function (done) {
it('does not cache etags', (done) => {
var server = provisionServer({ routes: { files: { relativeTo: __dirname } } }, 0);
const server = provisionServer({ routes: { files: { relativeTo: __dirname } } }, 0);
server.route({ method: 'GET', path: '/note', handler: { file: './file/note.txt' } });
server.inject('/note', function (res) {
server.inject('/note', (res) => {

@@ -447,3 +449,3 @@ expect(res.statusCode).to.equal(200);

server.inject('/note', function (res2) {
server.inject('/note', (res2) => {

@@ -458,8 +460,8 @@ expect(res2.statusCode).to.equal(200);

it('does not return etag when etagMethod is false', function (done) {
it('does not return etag when etagMethod is false', (done) => {
var server = provisionServer({ routes: { files: { relativeTo: __dirname } } }, 0);
const server = provisionServer({ routes: { files: { relativeTo: __dirname } } }, 0);
server.route({ method: 'GET', path: '/note', handler: { file: { path: './file/note.txt', etagMethod: false } } });
server.inject('/note', function (res) {
server.inject('/note', (res) => {

@@ -470,3 +472,3 @@ expect(res.statusCode).to.equal(200);

server.inject('/note', function (res2) {
server.inject('/note', (res2) => {

@@ -481,5 +483,5 @@ expect(res2.statusCode).to.equal(200);

it('invalidates etags when file changes (simple)', function (done) {
it('invalidates etags when file changes (simple)', (done) => {
var server = provisionServer({ routes: { files: { relativeTo: __dirname } } });
const server = provisionServer({ routes: { files: { relativeTo: __dirname } } });

@@ -490,3 +492,3 @@ server.route({ method: 'GET', path: '/note', handler: { file: { path: './file/note.txt', etagMethod: 'simple' } } });

server.inject('/note', function (res1) {
server.inject('/note', (res1) => {

@@ -497,3 +499,3 @@ expect(res1.statusCode).to.equal(200);

var etag1 = res1.headers.etag;
const etag1 = res1.headers.etag;

@@ -505,3 +507,3 @@ expect(etag1.slice(0, 1)).to.equal('"');

server.inject({ url: '/note', headers: { 'if-none-match': etag1 } }, function (res2) {
server.inject({ url: '/note', headers: { 'if-none-match': etag1 } }, (res2) => {

@@ -513,3 +515,3 @@ expect(res2.statusCode).to.equal(304);

var fd1 = Fs.openSync(Path.join(__dirname, 'file', 'note.txt'), 'w');
const fd1 = Fs.openSync(Path.join(__dirname, 'file', 'note.txt'), 'w');
Fs.writeSync(fd1, new Buffer('Test'), 0, 4);

@@ -520,3 +522,3 @@ Fs.closeSync(fd1);

server.inject({ url: '/note', headers: { 'if-none-match': etag1 } }, function (res3) {
server.inject({ url: '/note', headers: { 'if-none-match': etag1 } }, (res3) => {

@@ -527,6 +529,6 @@ expect(res3.statusCode).to.equal(200);

var etag2 = res3.headers.etag;
const etag2 = res3.headers.etag;
expect(etag1).to.not.equal(etag2);
var fd2 = Fs.openSync(Path.join(__dirname, 'file', 'note.txt'), 'w');
const fd2 = Fs.openSync(Path.join(__dirname, 'file', 'note.txt'), 'w');
Fs.writeSync(fd2, new Buffer('Test1'), 0, 5);

@@ -537,3 +539,3 @@ Fs.closeSync(fd2);

server.inject({ url: '/note', headers: { 'if-none-match': etag2 } }, function (res4) {
server.inject({ url: '/note', headers: { 'if-none-match': etag2 } }, (res4) => {

@@ -544,7 +546,7 @@ expect(res4.statusCode).to.equal(200);

var etag3 = res4.headers.etag;
const etag3 = res4.headers.etag;
expect(etag1).to.not.equal(etag3);
expect(etag2).to.not.equal(etag3);
var fd3 = Fs.openSync(Path.join(__dirname, 'file', 'note.txt'), 'w');
const fd3 = Fs.openSync(Path.join(__dirname, 'file', 'note.txt'), 'w');
Fs.writeSync(fd3, new Buffer('Test'), 0, 4);

@@ -560,5 +562,5 @@ Fs.closeSync(fd3);

it('invalidates etags when file changes (hash)', function (done) {
it('invalidates etags when file changes (hash)', (done) => {
var server = provisionServer({ routes: { files: { relativeTo: __dirname } } });
const server = provisionServer({ routes: { files: { relativeTo: __dirname } } });

@@ -569,3 +571,3 @@ server.route({ method: 'GET', path: '/note', handler: { file: './file/note.txt' } });

server.inject('/note', function (res1) {
server.inject('/note', (res1) => {

@@ -576,3 +578,3 @@ expect(res1.statusCode).to.equal(200);

var etag1 = res1.headers.etag;
const etag1 = res1.headers.etag;

@@ -584,3 +586,3 @@ expect(etag1.slice(0, 1)).to.equal('"');

server.inject({ url: '/note', headers: { 'if-none-match': etag1 } }, function (res2) {
server.inject({ url: '/note', headers: { 'if-none-match': etag1 } }, (res2) => {

@@ -593,3 +595,3 @@ expect(res2.statusCode).to.equal(304);

Fs.unlinkSync(Path.join(__dirname, 'file', 'note.txt'));
var fd1 = Fs.openSync(Path.join(__dirname, 'file', 'note.txt'), 'w');
const fd1 = Fs.openSync(Path.join(__dirname, 'file', 'note.txt'), 'w');
Fs.writeSync(fd1, new Buffer('Test'), 0, 4);

@@ -600,3 +602,3 @@ Fs.closeSync(fd1);

server.inject('/note', function (res3) {
server.inject('/note', (res3) => {

@@ -607,6 +609,6 @@ expect(res3.statusCode).to.equal(200);

var etag2 = res3.headers.etag;
const etag2 = res3.headers.etag;
expect(etag1).to.equal(etag2);
var fd2 = Fs.openSync(Path.join(__dirname, 'file', 'note.txt'), 'w');
const fd2 = Fs.openSync(Path.join(__dirname, 'file', 'note.txt'), 'w');
Fs.writeSync(fd2, new Buffer('Test1'), 0, 5);

@@ -617,3 +619,3 @@ Fs.closeSync(fd2);

server.inject({ url: '/note', headers: { 'if-none-match': etag2 } }, function (res4) {
server.inject({ url: '/note', headers: { 'if-none-match': etag2 } }, (res4) => {

@@ -624,6 +626,6 @@ expect(res4.statusCode).to.equal(200);

var etag3 = res4.headers.etag;
const etag3 = res4.headers.etag;
expect(etag1).to.not.equal(etag3);
var fd3 = Fs.openSync(Path.join(__dirname, 'file', 'note.txt'), 'w');
const fd3 = Fs.openSync(Path.join(__dirname, 'file', 'note.txt'), 'w');
Fs.writeSync(fd3, new Buffer('Test'), 0, 4);

@@ -634,3 +636,3 @@ Fs.closeSync(fd3);

server.inject('/note', function (res5) {
server.inject('/note', (res5) => {

@@ -641,3 +643,3 @@ expect(res5.statusCode).to.equal(200);

var etag4 = res5.headers.etag;
const etag4 = res5.headers.etag;
expect(etag1).to.equal(etag4);

@@ -653,11 +655,11 @@

it('returns a 304 when the request has if-modified-since and the response has not been modified since (larger)', function (done) {
it('returns a 304 when the request has if-modified-since and the response has not been modified since (larger)', (done) => {
var server = provisionServer();
const server = provisionServer();
server.route({ method: 'GET', path: '/file', handler: { file: Path.join(__dirname, '..', 'package.json') } });
server.inject('/file', function (res1) {
server.inject('/file', (res1) => {
var last = new Date(Date.parse(res1.headers['last-modified']) + 1000);
server.inject({ url: '/file', headers: { 'if-modified-since': last.toUTCString() } }, function (res2) {
const last = new Date(Date.parse(res1.headers['last-modified']) + 1000);
server.inject({ url: '/file', headers: { 'if-modified-since': last.toUTCString() } }, (res2) => {

@@ -673,10 +675,10 @@ expect(res2.statusCode).to.equal(304);

it('returns a 304 when the request has if-modified-since and the response has not been modified since (equal)', function (done) {
it('returns a 304 when the request has if-modified-since and the response has not been modified since (equal)', (done) => {
var server = provisionServer();
const server = provisionServer();
server.route({ method: 'GET', path: '/file', handler: { file: Path.join(__dirname, '..', 'package.json') } });
server.inject('/file', function (res1) {
server.inject('/file', (res1) => {
server.inject({ url: '/file', headers: { 'if-modified-since': res1.headers['last-modified'] } }, function (res2) {
server.inject({ url: '/file', headers: { 'if-modified-since': res1.headers['last-modified'] } }, (res2) => {

@@ -692,9 +694,9 @@ expect(res2.statusCode).to.equal(304);

it('computes etag header for 304 response', function (done) {
it('computes etag header for 304 response', (done) => {
var server = provisionServer();
const server = provisionServer();
server.route({ method: 'GET', path: '/file', handler: { file: Path.join(__dirname, '..', 'package.json') } });
var future = new Date(Date.now() + 1000);
server.inject({ url: '/file', headers: { 'if-modified-since': future } }, function (res) {
const future = new Date(Date.now() + 1000);
server.inject({ url: '/file', headers: { 'if-modified-since': future } }, (res) => {

@@ -708,8 +710,8 @@ expect(res.statusCode).to.equal(304);

it('computes etag header for head response', function (done) {
it('computes etag header for head response', (done) => {
var server = provisionServer();
const server = provisionServer();
server.route({ method: 'GET', path: '/file', handler: { file: Path.join(__dirname, '..', 'package.json') } });
server.inject({ method: 'HEAD', url: '/file' }, function (res) {
server.inject({ method: 'HEAD', url: '/file' }, (res) => {

@@ -723,8 +725,8 @@ expect(res.statusCode).to.equal(200);

it('changes etag when content encoding is used', function (done) {
it('changes etag when content encoding is used', (done) => {
var server = provisionServer();
const server = provisionServer();
server.route({ method: 'GET', path: '/file', handler: { file: Path.join(__dirname, '..', 'package.json') } });
server.inject('/file', function (res1) {
server.inject('/file', (res1) => {

@@ -735,3 +737,3 @@ expect(res1.statusCode).to.equal(200);

server.inject({ url: '/file', headers: { 'accept-encoding': 'gzip' } }, function (res2) {
server.inject({ url: '/file', headers: { 'accept-encoding': 'gzip' } }, (res2) => {

@@ -748,5 +750,5 @@ expect(res2.statusCode).to.equal(200);

it('return a 500 on hashing errors', function (done) {
it('return a 500 on hashing errors', (done) => {
var server = provisionServer();
const server = provisionServer();
server.route({ method: 'GET', path: '/file', handler: { file: Path.join(__dirname, '..', 'package.json') } });

@@ -756,3 +758,3 @@

var orig = Fs.createReadStream;
const orig = Fs.createReadStream;
Fs.createReadStream = function (path, options) {

@@ -762,3 +764,3 @@

process.nextTick(function () {
process.nextTick(() => {

@@ -771,3 +773,3 @@ Fs.closeSync(options.fd);

server.inject('/file', function (res) {
server.inject('/file', (res) => {

@@ -779,5 +781,5 @@ expect(res.statusCode).to.equal(500);

it('handles multiple simultaneous request hashing errors', function (done) {
it('handles multiple simultaneous request hashing errors', (done) => {
var server = provisionServer();
const server = provisionServer();
server.route({ method: 'GET', path: '/file', handler: { file: Path.join(__dirname, '..', 'package.json') } });

@@ -787,3 +789,3 @@

var orig = Fs.createReadStream;
const orig = Fs.createReadStream;
Fs.createReadStream = function (path, options) {

@@ -793,3 +795,3 @@

process.nextTick(function () {
process.nextTick(() => {

@@ -802,7 +804,7 @@ Fs.closeSync(options.fd);

Items.parallel(['/file', '/file'], function (req, next) {
Items.parallel(['/file', '/file'], (req, next) => {
setImmediate(function () {
setImmediate(() => {
server.inject(req, function (res) {
server.inject(req, (res) => {

@@ -816,8 +818,8 @@ expect(res.statusCode).to.equal(500);

it('returns valid http date responses in last-modified header', function (done) {
it('returns valid http date responses in last-modified header', (done) => {
var server = provisionServer();
const server = provisionServer();
server.route({ method: 'GET', path: '/file', handler: { file: Path.join(__dirname, '..', 'package.json') } });
server.inject('/file', function (res) {
server.inject('/file', (res) => {

@@ -830,8 +832,8 @@ expect(res.statusCode).to.equal(200);

it('returns 200 if if-modified-since is invalid', function (done) {
it('returns 200 if if-modified-since is invalid', (done) => {
var server = provisionServer();
const server = provisionServer();
server.route({ method: 'GET', path: '/file', handler: { file: Path.join(__dirname, '..', 'package.json') } });
server.inject({ url: '/file', headers: { 'if-modified-since': 'some crap' } }, function (res) {
server.inject({ url: '/file', headers: { 'if-modified-since': 'some crap' } }, (res) => {

@@ -843,9 +845,9 @@ expect(res.statusCode).to.equal(200);

it('returns 200 if last-modified is invalid', function (done) {
it('returns 200 if last-modified is invalid', (done) => {
var server = provisionServer();
const server = provisionServer();
server.route({
method: 'GET',
path: '/',
handler: function (request, reply) {
handler: (request, reply) => {

@@ -856,3 +858,3 @@ reply('ok').header('last-modified', 'some crap');

server.inject({ url: '/', headers: { 'if-modified-since': 'Fri, 28 Mar 2014 22:52:39 GMT' } }, function (res2) {
server.inject({ url: '/', headers: { 'if-modified-since': 'Fri, 28 Mar 2014 22:52:39 GMT' } }, (res2) => {

@@ -864,15 +866,15 @@ expect(res2.statusCode).to.equal(200);

it('closes file handlers when not reading file stream', { skip: process.platform === 'win32' }, function (done) {
it('closes file handlers when not reading file stream', { skip: process.platform === 'win32' }, (done) => {
var server = provisionServer();
const server = provisionServer();
server.route({ method: 'GET', path: '/file', handler: { file: Path.join(__dirname, '..', 'package.json') } });
server.inject('/file', function (res1) {
server.inject('/file', (res1) => {
server.inject({ url: '/file', headers: { 'if-modified-since': res1.headers.date } }, function (res2) {
server.inject({ url: '/file', headers: { 'if-modified-since': res1.headers.date } }, (res2) => {
expect(res2.statusCode).to.equal(304);
var cmd = ChildProcess.spawn('lsof', ['-p', process.pid]);
var lsof = '';
cmd.stdout.on('data', function (buffer) {
const cmd = ChildProcess.spawn('lsof', ['-p', process.pid]);
let lsof = '';
cmd.stdout.on('data', (buffer) => {

@@ -882,7 +884,7 @@ lsof += buffer.toString();

cmd.stdout.on('end', function () {
cmd.stdout.on('end', () => {
var count = 0;
var lines = lsof.split('\n');
for (var i = 0, il = lines.length; i < il; ++i) {
let count = 0;
const lines = lsof.split('\n');
for (let i = 0; i < lines.length; ++i) {
count += !!lines[i].match(/package.json/);

@@ -900,9 +902,9 @@ }

it('closes file handlers when not using a manually open file stream', { skip: process.platform === 'win32' }, function (done) {
it('closes file handlers when not using a manually open file stream', { skip: process.platform === 'win32' }, (done) => {
var server = provisionServer();
const server = provisionServer();
server.route({
method: 'GET',
path: '/file',
handler: function (request, reply) {
handler: (request, reply) => {

@@ -913,10 +915,10 @@ reply(Fs.createReadStream(Path.join(__dirname, '..', 'package.json'))).header('etag', 'abc');

server.inject('/file', function (res1) {
server.inject('/file', (res1) => {
server.inject({ url: '/file', headers: { 'if-none-match': res1.headers.etag } }, function (res2) {
server.inject({ url: '/file', headers: { 'if-none-match': res1.headers.etag } }, (res2) => {
expect(res2.statusCode).to.equal(304);
var cmd = ChildProcess.spawn('lsof', ['-p', process.pid]);
var lsof = '';
cmd.stdout.on('data', function (buffer) {
const cmd = ChildProcess.spawn('lsof', ['-p', process.pid]);
let lsof = '';
cmd.stdout.on('data', (buffer) => {

@@ -926,7 +928,7 @@ lsof += buffer.toString();

cmd.stdout.on('end', function () {
cmd.stdout.on('end', () => {
var count = 0;
var lines = lsof.split('\n');
for (var i = 0, il = lines.length; i < il; ++i) {
let count = 0;
const lines = lsof.split('\n');
for (let i = 0; i < lines.length; ++i) {
count += !!lines[i].match(/package.json/);

@@ -944,8 +946,8 @@ }

it('returns a gzipped file in the response when the request accepts gzip', function (done) {
it('returns a gzipped file in the response when the request accepts gzip', (done) => {
var server = provisionServer({ routes: { files: { relativeTo: __dirname } } });
var handler = function (request, reply) {
const server = provisionServer({ routes: { files: { relativeTo: __dirname } } });
const handler = (request, reply) => {
reply.file(Path.join(__dirname, '..', 'package.json'));
reply.file(Path.join(__dirname, '..', 'package.json'), { confine: '..' });
};

@@ -955,3 +957,3 @@

server.inject({ url: '/file', headers: { 'accept-encoding': 'gzip' } }, function (res) {
server.inject({ url: '/file', headers: { 'accept-encoding': 'gzip' } }, (res) => {

@@ -967,6 +969,6 @@ expect(res.statusCode).to.equal(200);

it('returns a plain file when not compressible', function (done) {
it('returns a plain file when not compressible', (done) => {
var server = provisionServer({ routes: { files: { relativeTo: __dirname } } });
var handler = function (request, reply) {
const server = provisionServer({ routes: { files: { relativeTo: __dirname } } });
const handler = (request, reply) => {

@@ -978,3 +980,3 @@ reply.file(Path.join(__dirname, 'file', 'image.png'));

server.inject({ url: '/file', headers: { 'accept-encoding': 'gzip' } }, function (res) {
server.inject({ url: '/file', headers: { 'accept-encoding': 'gzip' } }, (res) => {

@@ -990,8 +992,8 @@ expect(res.statusCode).to.equal(200);

it('returns a deflated file in the response when the request accepts deflate', function (done) {
it('returns a deflated file in the response when the request accepts deflate', (done) => {
var server = provisionServer({ routes: { files: { relativeTo: __dirname } } });
var handler = function (request, reply) {
const server = provisionServer({ routes: { files: { relativeTo: __dirname } } });
const handler = (request, reply) => {
reply.file(Path.join(__dirname, '..', 'package.json'));
reply.file(Path.join(__dirname, '..', 'package.json'), { confine: '..' });
};

@@ -1001,3 +1003,3 @@

server.inject({ url: '/file', headers: { 'accept-encoding': 'deflate' } }, function (res) {
server.inject({ url: '/file', headers: { 'accept-encoding': 'deflate' } }, (res) => {

@@ -1013,10 +1015,10 @@ expect(res.statusCode).to.equal(200);

it('returns a gzipped file using precompressed file', function (done) {
it('returns a gzipped file using precompressed file', (done) => {
var content = Fs.readFileSync('./test/file/image.png.gz');
const content = Fs.readFileSync('./test/file/image.png.gz');
var server = provisionServer();
const server = provisionServer();
server.route({ method: 'GET', path: '/file', handler: { file: { path: './test/file/image.png', lookupCompressed: true } } });
server.inject({ url: '/file', headers: { 'accept-encoding': 'gzip' } }, function (res) {
server.inject({ url: '/file', headers: { 'accept-encoding': 'gzip' } }, (res) => {

@@ -1032,8 +1034,8 @@ expect(res.statusCode).to.equal(200);

it('returns a gzipped file when precompressed file not found', function (done) {
it('returns a gzipped file when precompressed file not found', (done) => {
var server = provisionServer();
const server = provisionServer();
server.route({ method: 'GET', path: '/file', handler: { file: { path: './test/file/note.txt', lookupCompressed: true } } });
server.inject({ url: '/file', headers: { 'accept-encoding': 'gzip' } }, function (res) {
server.inject({ url: '/file', headers: { 'accept-encoding': 'gzip' } }, (res) => {

@@ -1048,10 +1050,10 @@ expect(res.statusCode).to.equal(200);

it('returns a 304 when using precompressed file and if-modified-since set', function (done) {
it('returns a 304 when using precompressed file and if-modified-since set', (done) => {
var server = provisionServer();
const server = provisionServer();
server.route({ method: 'GET', path: '/file', handler: { file: { path: './test/file/image.png', lookupCompressed: true } } });
server.inject('/file', function (res1) {
server.inject('/file', (res1) => {
server.inject({ url: '/file', headers: { 'if-modified-since': res1.headers.date, 'accept-encoding': 'gzip' } }, function (res2) {
server.inject({ url: '/file', headers: { 'if-modified-since': res1.headers.date, 'accept-encoding': 'gzip' } }, (res2) => {

@@ -1064,8 +1066,8 @@ expect(res2.statusCode).to.equal(304);

it('ignores precompressed file when content-encoding not requested', function (done) {
it('ignores precompressed file when content-encoding not requested', (done) => {
var server = provisionServer();
const server = provisionServer();
server.route({ method: 'GET', path: '/file', handler: { file: { path: './test/file/image.png', lookupCompressed: true } } });
server.inject('/file', function (res) {
server.inject('/file', (res) => {

@@ -1080,8 +1082,8 @@ expect(res.statusCode).to.equal(200);

it('ignores precompressed file when connection compression is disabled', function (done) {
it('ignores precompressed file when connection compression is disabled', (done) => {
var server = provisionServer({ compression: false });
const server = provisionServer({ compression: false });
server.route({ method: 'GET', path: '/file', handler: { file: { path: './test/file/image.png', lookupCompressed: true } } });
server.inject({ url: '/file', headers: { 'accept-encoding': 'gzip' } }, function (res) {
server.inject({ url: '/file', headers: { 'accept-encoding': 'gzip' } }, (res) => {

@@ -1096,9 +1098,9 @@ expect(res.statusCode).to.equal(200);

it('does not throw an error when adding a route with a parameter and function path', function (done) {
it('does not throw an error when adding a route with a parameter and function path', (done) => {
var fn = function () {
const fn = () => {
var server = provisionServer();
server.route({ method: 'GET', path: '/fileparam/{path}', handler: { file: function () { } } });
server.route({ method: 'GET', path: '/filepathparam/{path}', handler: { file: { path: function () { } } } });
const server = provisionServer();
server.route({ method: 'GET', path: '/fileparam/{path}', handler: { file: () => { } } });
server.route({ method: 'GET', path: '/filepathparam/{path}', handler: { file: { path: () => { } } } });
};

@@ -1110,10 +1112,10 @@

it('responds correctly when file is removed while processing', function (done) {
it('responds correctly when file is removed while processing', (done) => {
var filename = Hoek.uniqueFilename(Os.tmpDir()) + '.package.json';
const filename = Hoek.uniqueFilename(Os.tmpDir()) + '.package.json';
Fs.writeFileSync(filename, 'data');
var server = provisionServer();
server.route({ method: 'GET', path: '/', handler: { file: filename } });
server.ext('onPreResponse', function (request, reply) {
const server = provisionServer();
server.route({ method: 'GET', path: '/', handler: { file: { path: filename, confine: false } } });
server.ext('onPreResponse', (request, reply) => {

@@ -1124,3 +1126,3 @@ Fs.unlinkSync(filename);

server.inject('/', function (res) {
server.inject('/', (res) => {

@@ -1132,12 +1134,12 @@ expect(res.statusCode).to.equal(200);

it('responds correctly when file is changed while processing', function (done) {
it('responds correctly when file is changed while processing', (done) => {
var filename = Hoek.uniqueFilename(Os.tmpDir()) + '.package.json';
const filename = Hoek.uniqueFilename(Os.tmpDir()) + '.package.json';
Fs.writeFileSync(filename, 'data');
var server = provisionServer();
server.route({ method: 'GET', path: '/', handler: { file: filename } });
server.ext('onPreResponse', function (request, reply) {
const server = provisionServer();
server.route({ method: 'GET', path: '/', handler: { file: { path: filename, confine: false } } });
server.ext('onPreResponse', (request, reply) => {
var tempfile = filename + '~';
const tempfile = filename + '~';
if (process.platform === 'win32') {

@@ -1148,3 +1150,4 @@ // workaround to replace open file without a permission error

Fs.unlinkSync(tempfile);
} else {
}
else {
// atomic file replace

@@ -1158,3 +1161,3 @@ Fs.writeFileSync(tempfile, 'database');

server.inject('/', function (res) {
server.inject('/', (res) => {

@@ -1170,12 +1173,12 @@ Fs.unlinkSync(filename);

it('does not marshal response on 304', function (done) {
it('does not marshal response on 304', (done) => {
var server = provisionServer();
const server = provisionServer();
server.route({ method: 'GET', path: '/file', handler: { file: Path.join(__dirname, '..', 'package.json') } });
server.inject('/file', function (res1) {
server.inject('/file', (res1) => {
server.ext('onPreResponse', function (request, reply) {
server.ext('onPreResponse', (request, reply) => {
request.response._marshall = function () {
request.response._marshall = () => {

@@ -1188,3 +1191,3 @@ throw new Error('not called');

server.inject({ url: '/file', headers: { 'if-modified-since': res1.headers.date } }, function (res2) {
server.inject({ url: '/file', headers: { 'if-modified-since': res1.headers.date } }, (res2) => {

@@ -1197,10 +1200,10 @@ expect(res2.statusCode).to.equal(304);

it('returns error when aborted while processing', function (done) {
it('returns error when aborted while processing', (done) => {
var filename = Hoek.uniqueFilename(Os.tmpDir()) + '.package.json';
const filename = Hoek.uniqueFilename(Os.tmpDir()) + '.package.json';
Fs.writeFileSync(filename, 'data');
var server = provisionServer();
const server = provisionServer();
server.route({ method: 'GET', path: '/', handler: { file: filename } });
server.ext('onPreResponse', function (request, reply) {
server.ext('onPreResponse', (request, reply) => {

@@ -1210,3 +1213,3 @@ reply(Boom.internal('crapping out'));

server.inject('/', function (res) {
server.inject('/', (res) => {

@@ -1218,8 +1221,8 @@ expect(res.statusCode).to.equal(500);

it('returns error when stat fails unexpectedly', function (done) {
it('returns error when stat fails unexpectedly', (done) => {
var filename = Hoek.uniqueFilename(Os.tmpDir()) + '.package.json';
const filename = Hoek.uniqueFilename(Os.tmpDir()) + '.package.json';
Fs.writeFileSync(filename, 'data');
var orig = Fs.fstat;
const orig = Fs.fstat;
Fs.fstat = function (fd, callback) { // can return EIO error

@@ -1232,6 +1235,6 @@

var server = provisionServer();
server.route({ method: 'GET', path: '/', handler: { file: filename } });
const server = provisionServer();
server.route({ method: 'GET', path: '/', handler: { file: { path: filename, confine: false } } });
server.inject('/', function (res) {
server.inject('/', (res) => {

@@ -1243,19 +1246,19 @@ expect(res.statusCode).to.equal(500);

it('returns error when open fails unexpectedly', function (done) {
it('returns error when open fails unexpectedly', (done) => {
var filename = Hoek.uniqueFilename(Os.tmpDir()) + '.package.json';
const filename = Hoek.uniqueFilename(Os.tmpDir()) + '.package.json';
Fs.writeFileSync(filename, 'data');
var orig = Fs.open;
const orig = Fs.open;
Fs.open = function () { // can return EMFILE error
Fs.open = orig;
var callback = arguments[arguments.length - 1];
const callback = arguments[arguments.length - 1];
callback(new Error('failed'));
};
var server = provisionServer();
server.route({ method: 'GET', path: '/', handler: { file: filename } });
const server = provisionServer();
server.route({ method: 'GET', path: '/', handler: { file: { path: filename, confine: false } } });
server.inject('/', function (res) {
server.inject('/', (res) => {

@@ -1267,8 +1270,8 @@ expect(res.statusCode).to.equal(500);

it('returns a 403 when missing file read permission', function (done) {
it('returns a 403 when missing file read permission', (done) => {
var filename = Hoek.uniqueFilename(Os.tmpDir()) + '.package.json';
const filename = Hoek.uniqueFilename(Os.tmpDir()) + '.package.json';
Fs.writeFileSync(filename, 'data');
var retainedFd;
let retainedFd;
if (process.platform === 'win32') {

@@ -1278,16 +1281,17 @@ // make a permissionless file by unlinking an open file

Fs.unlinkSync(filename);
} else {
}
else {
Fs.chmodSync(filename, 0);
}
var server = provisionServer();
const server = provisionServer();
server.route({ method: 'GET', path: '/', handler: { file: filename } });
server.inject('/', function (res1) {
server.inject('/', (res1) => {
var orig = Fs.open;
const orig = Fs.open;
Fs.open = function (path, mode, callback) { // fake alternate permission error
Fs.open = orig;
return Fs.open(path, mode, function (err, fd) {
return Fs.open(path, mode, (err, fd) => {

@@ -1298,3 +1302,4 @@ if (err) {

err.errno = -1;
} else if (err.code === 'EPERM') {
}
else if (err.code === 'EPERM') {
err.code = 'EACCES';

@@ -1309,3 +1314,3 @@ err.errno = -13;

server.inject('/', function (res2) {
server.inject('/', (res2) => {

@@ -1315,3 +1320,4 @@ // cleanup

Fs.closeSync(retainedFd);
} else {
}
else {
Fs.unlinkSync(filename);

@@ -1327,10 +1333,10 @@ }

describe('response range', function () {
describe('response range', () => {
it('returns a subset of a file (start)', function (done) {
it('returns a subset of a file (start)', (done) => {
var server = provisionServer();
const server = provisionServer();
server.route({ method: 'GET', path: '/file', handler: { file: { path: Path.join(__dirname, 'file/image.png') } } });
server.inject({ url: '/file', headers: { 'range': 'bytes=0-4' } }, function (res) {
server.inject({ url: '/file', headers: { 'range': 'bytes=0-4' } }, (res) => {

@@ -1346,8 +1352,8 @@ expect(res.statusCode).to.equal(206);

it('returns a subset of a file (middle)', function (done) {
it('returns a subset of a file (middle)', (done) => {
var server = provisionServer();
const server = provisionServer();
server.route({ method: 'GET', path: '/file', handler: { file: { path: Path.join(__dirname, 'file/image.png') } } });
server.inject({ url: '/file', headers: { 'range': 'bytes=1-5' } }, function (res) {
server.inject({ url: '/file', headers: { 'range': 'bytes=1-5' } }, (res) => {

@@ -1363,8 +1369,8 @@ expect(res.statusCode).to.equal(206);

it('returns a subset of a file (-to)', function (done) {
it('returns a subset of a file (-to)', (done) => {
var server = provisionServer();
const server = provisionServer();
server.route({ method: 'GET', path: '/file', handler: { file: { path: Path.join(__dirname, 'file/image.png') } } });
server.inject({ url: '/file', headers: { 'range': 'bytes=-5' } }, function (res) {
server.inject({ url: '/file', headers: { 'range': 'bytes=-5' } }, (res) => {

@@ -1380,8 +1386,8 @@ expect(res.statusCode).to.equal(206);

it('returns a subset of a file (from-)', function (done) {
it('returns a subset of a file (from-)', (done) => {
var server = provisionServer();
const server = provisionServer();
server.route({ method: 'GET', path: '/file', handler: { file: { path: Path.join(__dirname, 'file/image.png') } } });
server.inject({ url: '/file', headers: { 'range': 'bytes=42005-' } }, function (res) {
server.inject({ url: '/file', headers: { 'range': 'bytes=42005-' } }, (res) => {

@@ -1397,8 +1403,8 @@ expect(res.statusCode).to.equal(206);

it('returns a subset of a file (beyond end)', function (done) {
it('returns a subset of a file (beyond end)', (done) => {
var server = provisionServer();
const server = provisionServer();
server.route({ method: 'GET', path: '/file', handler: { file: { path: Path.join(__dirname, 'file/image.png') } } });
server.inject({ url: '/file', headers: { 'range': 'bytes=42005-42011' } }, function (res) {
server.inject({ url: '/file', headers: { 'range': 'bytes=42005-42011' } }, (res) => {

@@ -1414,10 +1420,10 @@ expect(res.statusCode).to.equal(206);

it('returns a subset of a file (if-range)', function (done) {
it('returns a subset of a file (if-range)', (done) => {
var server = provisionServer();
const server = provisionServer();
server.route({ method: 'GET', path: '/file', handler: { file: { path: Path.join(__dirname, 'file/image.png') } } });
server.inject('/file', function (res1) {
server.inject('/file', (res1) => {
server.inject({ url: '/file', headers: { 'range': 'bytes=42005-42011', 'if-range': res1.headers.etag } }, function (res2) {
server.inject({ url: '/file', headers: { 'range': 'bytes=42005-42011', 'if-range': res1.headers.etag } }, (res2) => {

@@ -1434,8 +1440,8 @@ expect(res2.statusCode).to.equal(206);

it('returns 200 on incorrect if-range', function (done) {
it('returns 200 on incorrect if-range', (done) => {
var server = provisionServer();
const server = provisionServer();
server.route({ method: 'GET', path: '/file', handler: { file: { path: Path.join(__dirname, 'file/image.png') } } });
server.inject({ url: '/file', headers: { 'range': 'bytes=42005-42011', 'if-range': 'abc' } }, function (res2) {
server.inject({ url: '/file', headers: { 'range': 'bytes=42005-42011', 'if-range': 'abc' } }, (res2) => {

@@ -1447,8 +1453,8 @@ expect(res2.statusCode).to.equal(200);

it('returns 416 on invalid range (unit)', function (done) {
it('returns 416 on invalid range (unit)', (done) => {
var server = provisionServer();
const server = provisionServer();
server.route({ method: 'GET', path: '/file', handler: { file: { path: Path.join(__dirname, 'file/image.png') } } });
server.inject({ url: '/file', headers: { 'range': 'horses=1-5' } }, function (res) {
server.inject({ url: '/file', headers: { 'range': 'horses=1-5' } }, (res) => {

@@ -1461,8 +1467,8 @@ expect(res.statusCode).to.equal(416);

it('returns 416 on invalid range (inversed)', function (done) {
it('returns 416 on invalid range (inversed)', (done) => {
var server = provisionServer();
const server = provisionServer();
server.route({ method: 'GET', path: '/file', handler: { file: { path: Path.join(__dirname, 'file/image.png') } } });
server.inject({ url: '/file', headers: { 'range': 'bytes=5-1' } }, function (res) {
server.inject({ url: '/file', headers: { 'range': 'bytes=5-1' } }, (res) => {

@@ -1475,8 +1481,8 @@ expect(res.statusCode).to.equal(416);

it('returns 416 on invalid range (format)', function (done) {
it('returns 416 on invalid range (format)', (done) => {
var server = provisionServer();
const server = provisionServer();
server.route({ method: 'GET', path: '/file', handler: { file: { path: Path.join(__dirname, 'file/image.png') } } });
server.inject({ url: '/file', headers: { 'range': 'bytes 1-5' } }, function (res) {
server.inject({ url: '/file', headers: { 'range': 'bytes 1-5' } }, (res) => {

@@ -1489,8 +1495,8 @@ expect(res.statusCode).to.equal(416);

it('returns 416 on invalid range (empty range)', function (done) {
it('returns 416 on invalid range (empty range)', (done) => {
var server = provisionServer();
const server = provisionServer();
server.route({ method: 'GET', path: '/file', handler: { file: { path: Path.join(__dirname, 'file/image.png') } } });
server.inject({ url: '/file', headers: { 'range': 'bytes=-' } }, function (res) {
server.inject({ url: '/file', headers: { 'range': 'bytes=-' } }, (res) => {

@@ -1503,8 +1509,8 @@ expect(res.statusCode).to.equal(416);

it('returns 200 on multiple ranges', function (done) {
it('returns 200 on multiple ranges', (done) => {
var server = provisionServer();
const server = provisionServer();
server.route({ method: 'GET', path: '/file', handler: { file: { path: Path.join(__dirname, 'file/image.png') } } });
server.inject({ url: '/file', headers: { 'range': 'bytes=1-5,7-10' } }, function (res) {
server.inject({ url: '/file', headers: { 'range': 'bytes=1-5,7-10' } }, (res) => {

@@ -1517,5 +1523,5 @@ expect(res.statusCode).to.equal(200);

it('reads partial file content for a non-compressible file', function (done) {
it('reads partial file content for a non-compressible file', (done) => {
var server = provisionServer();
const server = provisionServer();
server.route({ method: 'GET', path: '/file', handler: { file: { path: Path.join(__dirname, 'file/image.png'), etagMethod: false } } });

@@ -1525,4 +1531,4 @@

var createOptions;
var orig = Fs.createReadStream;
let createOptions;
const orig = Fs.createReadStream;
Fs.createReadStream = function (path, options) {

@@ -1536,3 +1542,3 @@

server.inject({ url: '/file', headers: { 'range': 'bytes=1-4', 'accept-encoding': 'gzip' } }, function (res) {
server.inject({ url: '/file', headers: { 'range': 'bytes=1-4', 'accept-encoding': 'gzip' } }, (res) => {

@@ -1549,8 +1555,8 @@ expect(res.statusCode).to.equal(206);

it('returns 200 when content-length is missing', function (done) {
it('returns 200 when content-length is missing', (done) => {
var server = provisionServer();
const server = provisionServer();
server.route({ method: 'GET', path: '/file', handler: { file: { path: Path.join(__dirname, 'file/image.png') } } });
server.ext('onPreResponse', function (request, reply) {
server.ext('onPreResponse', (request, reply) => {

@@ -1561,3 +1567,3 @@ delete request.response.headers['content-length'];

server.inject({ url: '/file', headers: { 'range': 'bytes=1-5' } }, function (res) {
server.inject({ url: '/file', headers: { 'range': 'bytes=1-5' } }, (res) => {

@@ -1570,7 +1576,7 @@ expect(res.statusCode).to.equal(200);

it('returns 200 for dynamically compressed responses', function (done) {
it('returns 200 for dynamically compressed responses', (done) => {
var server = provisionServer();
const server = provisionServer();
server.route({ method: 'GET', path: '/file', handler: { file: { path: Path.join(__dirname, 'file/note.txt'), lookupCompressed: false } } });
server.inject({ url: '/file', headers: { 'range': 'bytes=1-3', 'accept-encoding': 'gzip' } }, function (res) {
server.inject({ url: '/file', headers: { 'range': 'bytes=1-3', 'accept-encoding': 'gzip' } }, (res) => {

@@ -1586,7 +1592,7 @@ expect(res.statusCode).to.equal(200);

it('returns a subset of a file when compression is disabled', function (done) {
it('returns a subset of a file when compression is disabled', (done) => {
var server = provisionServer({ compression: false });
const server = provisionServer({ compression: false });
server.route({ method: 'GET', path: '/file', handler: { file: { path: Path.join(__dirname, 'file/note.txt'), lookupCompressed: false } } });
server.inject({ url: '/file', headers: { 'range': 'bytes=1-3', 'accept-encoding': 'gzip' } }, function (res) {
server.inject({ url: '/file', headers: { 'range': 'bytes=1-3', 'accept-encoding': 'gzip' } }, (res) => {

@@ -1601,7 +1607,7 @@ expect(res.statusCode).to.equal(206);

it('returns a subset of a file using precompressed file', function (done) {
it('returns a subset of a file using precompressed file', (done) => {
var server = provisionServer();
const server = provisionServer();
server.route({ method: 'GET', path: '/file', handler: { file: { path: Path.join(__dirname, 'file/image.png'), lookupCompressed: true } } });
server.inject({ url: '/file', headers: { 'range': 'bytes=10-18', 'accept-encoding': 'gzip' } }, function (res) {
server.inject({ url: '/file', headers: { 'range': 'bytes=10-18', 'accept-encoding': 'gzip' } }, (res) => {

@@ -1618,7 +1624,7 @@ expect(res.statusCode).to.equal(206);

it('returns a subset for dynamically compressed responses with "identity" encoding', function (done) {
it('returns a subset for dynamically compressed responses with "identity" encoding', (done) => {
var server = provisionServer();
const server = provisionServer();
server.route({ method: 'GET', path: '/file', handler: { file: { path: Path.join(__dirname, 'file/note.txt'), lookupCompressed: false } } });
server.inject({ url: '/file', headers: { 'range': 'bytes=1-3', 'accept-encoding': 'identity' } }, function (res) {
server.inject({ url: '/file', headers: { 'range': 'bytes=1-3', 'accept-encoding': 'identity' } }, (res) => {

@@ -1633,8 +1639,8 @@ expect(res.statusCode).to.equal(206);

it('returns a subset when content-type is missing', function (done) {
it('returns a subset when content-type is missing', (done) => {
var server = provisionServer();
const server = provisionServer();
server.route({ method: 'GET', path: '/file', handler: { file: { path: Path.join(__dirname, 'file/note.txt') } } });
server.ext('onPreResponse', function (request, reply) {
server.ext('onPreResponse', (request, reply) => {

@@ -1645,3 +1651,3 @@ delete request.response.headers['content-type'];

server.inject({ url: '/file', headers: { 'range': 'bytes=1-5' } }, function (res) {
server.inject({ url: '/file', headers: { 'range': 'bytes=1-5' } }, (res) => {

@@ -1658,8 +1664,8 @@ expect(res.statusCode).to.equal(206);

it('has not leaked file descriptors', { skip: process.platform === 'win32' }, function (done) {
it('has not leaked file descriptors', { skip: process.platform === 'win32' }, (done) => {
// validate that all descriptors has been closed
var cmd = ChildProcess.spawn('lsof', ['-p', process.pid]);
var lsof = '';
cmd.stdout.on('data', function (buffer) {
const cmd = ChildProcess.spawn('lsof', ['-p', process.pid]);
let lsof = '';
cmd.stdout.on('data', (buffer) => {

@@ -1669,7 +1675,7 @@ lsof += buffer.toString();

cmd.stdout.on('end', function () {
cmd.stdout.on('end', () => {
var count = 0;
var lines = lsof.split('\n');
for (var i = 0, il = lines.length; i < il; ++i) {
let count = 0;
const lines = lsof.split('\n');
for (let i = 0; i < lines.length; ++i) {
count += !!lines[i].match(/package.json/);

@@ -1676,0 +1682,0 @@ }

@@ -0,8 +1,11 @@

'use strict';
// Load modules
var Code = require('code');
var Hapi = require('hapi');
var Hoek = require('hoek');
var Inert = require('..');
var Lab = require('lab');
const Code = require('code');
const Hapi = require('hapi');
const Hoek = require('hoek');
const Inert = require('..');
const Lab = require('lab');
const Path = require('path');

@@ -12,3 +15,3 @@

var internals = {};
const internals = {};

@@ -18,13 +21,13 @@

var lab = exports.lab = Lab.script();
var describe = lab.describe;
var it = lab.it;
var expect = Code.expect;
const lab = exports.lab = Lab.script();
const describe = lab.describe;
const it = lab.it;
const expect = Code.expect;
describe('security', function () {
describe('security', () => {
var provisionServer = function () {
const provisionServer = () => {
var server = new Hapi.Server();
const server = new Hapi.Server();
server.connection({ routes: { files: { relativeTo: __dirname } } });

@@ -35,10 +38,10 @@ server.register(Inert, Hoek.ignore);

it('blocks path traversal to files outside of hosted directory is not allowed with null byte injection', function (done) {
it('blocks path traversal to files outside of hosted directory is not allowed with null byte injection', (done) => {
var server = provisionServer();
const server = provisionServer();
server.route({ method: 'GET', path: '/{path*}', handler: { directory: { path: './directory' } } });
server.inject('/%00/../security.js', function (res) {
server.inject('/%00/../security.js', (res) => {
expect(res.statusCode).to.equal(403);
expect(res.statusCode).to.equal(404);
done();

@@ -48,8 +51,8 @@ });

it('blocks path traversal to files outside of hosted directory is not allowed', function (done) {
it('blocks path traversal to files outside of hosted directory is not allowed', (done) => {
var server = provisionServer();
const server = provisionServer();
server.route({ method: 'GET', path: '/{path*}', handler: { directory: { path: './directory' } } });
server.inject('/../security.js', function (res) {
server.inject('/../security.js', (res) => {

@@ -61,8 +64,8 @@ expect(res.statusCode).to.equal(403);

it('blocks path traversal to files outside of hosted directory is not allowed with encoded slash', function (done) {
it('blocks path traversal to files outside of hosted directory is not allowed with encoded slash', (done) => {
var server = provisionServer();
const server = provisionServer();
server.route({ method: 'GET', path: '/{path*}', handler: { directory: { path: './directory' } } });
server.inject('/..%2Fsecurity.js', function (res) {
server.inject('/..%2Fsecurity.js', (res) => {

@@ -74,10 +77,10 @@ expect(res.statusCode).to.equal(403);

it('blocks path traversal to files outside of hosted directory is not allowed with double encoded slash', function (done) {
it('blocks path traversal to files outside of hosted directory is not allowed with double encoded slash', (done) => {
var server = provisionServer();
const server = provisionServer();
server.route({ method: 'GET', path: '/{path*}', handler: { directory: { path: './directory' } } });
server.inject('/..%252Fsecurity.js', function (res) {
server.inject('/..%252Fsecurity.js', (res) => {
expect(res.statusCode).to.equal(403);
expect(res.statusCode).to.equal(404);
done();

@@ -87,10 +90,10 @@ });

it('blocks path traversal to files outside of hosted directory is not allowed with unicode encoded slash', function (done) {
it('blocks path traversal to files outside of hosted directory is not allowed with unicode encoded slash', (done) => {
var server = provisionServer();
const server = provisionServer();
server.route({ method: 'GET', path: '/{path*}', handler: { directory: { path: './directory' } } });
server.inject('/..\u2216security.js', function (res) {
server.inject('/..\u2216security.js', (res) => {
expect(res.statusCode).to.equal(403);
expect(res.statusCode).to.equal(404);
done();

@@ -100,8 +103,8 @@ });

it('blocks null byte injection when serving a file', function (done) {
it('blocks null byte injection when serving a file', (done) => {
var server = provisionServer();
const server = provisionServer();
server.route({ method: 'GET', path: '/{path*}', handler: { directory: { path: './directory' } } });
server.inject('/index%00.html', function (res) {
server.inject('/index%00.html', (res) => {

@@ -112,2 +115,67 @@ expect(res.statusCode).to.equal(404);

});
it('blocks access to files outside of base directory for file handler', (done) => {
const server = provisionServer();
const secureHandler = { file: { confine: './directory', path: Path.join(__dirname, 'security.js') } };
server.route({ method: 'GET', path: '/secure', handler: secureHandler });
server.route({ method: 'GET', path: '/open', handler: Hoek.applyToDefaults(secureHandler, { file: { confine: false } }) });
server.inject('/secure', (res1) => {
expect(res1.statusCode).to.equal(403);
server.inject('/open', (res2) => {
expect(res2.statusCode).to.equal(200);
done();
});
});
});
it('blocks path traversal to files outside of base directory for file handler', (done) => {
const server = provisionServer();
server.route({ method: 'GET', path: '/file', handler: { file: { confine: './directory', path: '../security.js' } } });
server.inject('/file', (res) => {
expect(res.statusCode).to.equal(403);
done();
});
});
it('blocks access to files outside of base directory for reply.file()', (done) => {
const server = provisionServer();
const fileHandler = (request, reply) => {
reply.file(Path.join(__dirname, 'security.js'), { confine: Path.join(__dirname, 'directory') });
};
server.route({ method: 'GET', path: '/file', handler: fileHandler });
server.inject('/file', (res) => {
expect(res.statusCode).to.equal(403);
done();
});
});
it('blocks path traversal to files outside of base directory for reply.file()', (done) => {
const server = provisionServer();
const fileHandler = (request, reply) => {
reply.file('../security.js', { confine: Path.join(__dirname, 'directory') });
};
server.route({ method: 'GET', path: '/file', handler: fileHandler });
server.inject('/file', (res) => {
expect(res.statusCode).to.equal(403);
done();
});
});
});

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

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

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc