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 1.1.0 to 2.0.0

2

CONTRIBUTING.md

@@ -13,5 +13,5 @@ # How to contribute

* Fork the repository on GitHub.
* Fix the issue ensuring that your code follows the [style guide](https://github.com/hapijs/hapi/blob/master/docs/Style.md).
* Fix the issue ensuring that your code follows the [style guide](https://github.com/hapijs/contrib/blob/master/Style.md).
* Add tests for your new code ensuring that you have 100% code coverage (we can help you reach 100% but will not merge without it).
* Run `npm test` to generate a report of test coverage
* [Pull requests](http://help.github.com/send-pull-requests/) should be made to the [master branch](https://github.com/hapijs/inert/tree/master).

@@ -18,3 +18,3 @@ // Load modules

internals.schema = Joi.object({
path: Joi.alternatives(Joi.string(), Joi.array().includes(Joi.string()), Joi.func()).required(),
path: Joi.alternatives(Joi.array().includes(Joi.string()).single(), Joi.func()).required(),
index: Joi.boolean(),

@@ -34,10 +34,4 @@ listing: Joi.boolean(),

var settings = Hoek.clone(options); // options can be reused
var settings = Joi.validate(options, internals.schema).value;
// Normalize static string path
if (typeof settings.path === 'string') {
settings.path = [settings.path];
}
var normalize = function (paths) {

@@ -48,8 +42,5 @@

var path = paths[i];
if (path[path.length - 1] !== Path.sep) {
path += Path.sep;
}
if (!Hoek.isAbsolutePath(path)) {
path = Path.join(route.files.relativeTo, path);
path = Path.join(route.settings.files.relativeTo, path);
}

@@ -90,3 +81,3 @@

var selection = null;
var lastParam = request._paramsArray[request._paramsArray.length - 1];
var lastParam = request.paramsArray[request.paramsArray.length - 1];
if (lastParam) {

@@ -156,3 +147,3 @@ if (lastParam.indexOf('..') !== -1) {

if (settings.redirectToSlash !== false && // Defaults to true
!request.server.settings.router.stripTrailingSlash &&
!request.connection.settings.router.stripTrailingSlash &&
!hasTrailingSlash) {

@@ -237,3 +228,3 @@

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

@@ -240,0 +231,0 @@

@@ -10,3 +10,2 @@ // Load modules

var LruCache = require('lru-cache');
var Mimos = require('mimos');

@@ -23,3 +22,3 @@

Joi.object({
path: Joi.string().required(),
path: Joi.alternatives(Joi.string(), Joi.func()).required(),
filename: Joi.string(),

@@ -36,3 +35,3 @@ mode: Joi.string().valid('attachment', 'inline').allow(false),

Joi.assert(options, internals.schema, 'Invalid file handler options (' + route.path + ')');
var settings = (typeof options !== 'object' ? { path: options } : Hoek.clone(options)); // options can be reused
var settings = (typeof options !== 'object' ? { path: options } : Joi.validate(options, internals.schema).value);
Hoek.assert(typeof settings.path !== 'string' || settings.path[settings.path.length - 1] !== '/', 'File path cannot end with a \'/\':', route.path);

@@ -63,8 +62,9 @@

var source = {
path: Path.normalize(Hoek.isAbsolutePath(path) ? path : Path.join(request.route.files.relativeTo, path)),
path: Path.normalize(Hoek.isAbsolutePath(path) ? path : Path.join(request.route.settings.files.relativeTo, path)),
settings: options,
stat: null
stat: null,
fd: null
};
return request.generateResponse(source, { variety: 'file', marshall: internals.marshall, prepare: internals.prepare });
return request.generateResponse(source, { variety: 'file', marshal: internals.marshal, prepare: internals.prepare, close: internals.close });
};

@@ -75,22 +75,22 @@

internals.close(response); // Close any leftover descriptors from previous prepare call
var path = response.source.path;
Fs.stat(path, function (err, stat) {
internals.openStat(path, 'r', function (err, fd, stat) {
if (err) {
return callback(Boom.notFound());
return callback(err);
}
if (stat.isDirectory()) {
return callback(Boom.forbidden());
}
response.source.fd = fd;
response.bytes(stat.size);
if (!response.headers['content-type']) {
response.type(Mimos.path(path).type || 'application/octet-stream');
response.type(response.request.server.mime.path(path).type || 'application/octet-stream');
}
response._header('last-modified', stat.mtime.toUTCString());
response.header('last-modified', stat.mtime.toUTCString());
if (response.request.server._etags) {
var etags = response.request.server.plugins.inert._etags;
if (etags) {

@@ -103,3 +103,3 @@ // Use stat info for an LRU cache key.

var cachedEtag = response.request.server._etags.get(cachekey);
var cachedEtag = etags.get(cachekey);
if (cachedEtag) {

@@ -118,3 +118,3 @@ response.etag(cachedEtag, { vary: true });

var etag = hash.digest('hex');
response.request.server._etags.set(cachekey, etag);
etags.set(cachekey, etag);
});

@@ -126,3 +126,3 @@ }

var fileName = response.source.settings.filename || Path.basename(path);
response._header('content-disposition', response.source.settings.mode + '; filename=' + encodeURIComponent(fileName));
response.header('content-disposition', response.source.settings.mode + '; filename=' + encodeURIComponent(fileName));
}

@@ -135,3 +135,3 @@

internals.marshall = function (response, callback) {
internals.marshal = function (response, next) {

@@ -141,19 +141,20 @@ if (!response.source.settings.lookupCompressed ||

return internals.openStream(response, response.source.path, callback);
return next(null, internals.openStream(response, response.source.path));
}
var gzFile = response.source.path + '.gz';
Fs.stat(gzFile, function (err, stat) {
internals.openStat(gzFile, 'r', function (err, fd, stat) {
if (err ||
stat.isDirectory()) {
return internals.openStream(response, response.source.path, callback);
if (err) {
return next(null, internals.openStream(response, response.source.path));
}
internals.close(response);
response.source.fd = fd;
response.bytes(stat.size);
response._header('content-encoding', 'gzip');
response.header('content-encoding', 'gzip');
response.vary('accept-encoding');
return internals.openStream(response, gzFile, callback);
return next(null, internals.openStream(response, gzFile));
});

@@ -163,23 +164,47 @@ };

internals.openStream = function (response, path, callback) {
internals.openStream = function (response, path) {
var fileStream = Fs.createReadStream(path);
Hoek.assert(response.source.fd !== null, 'file descriptor must be set');
var onError = function (err) {
var fileStream = Fs.createReadStream(path, { fd: response.source.fd });
response.source.fd = null; // Claim descriptor
return fileStream;
};
fileStream.removeListener('open', onOpen);
return callback(err);
};
var onOpen = function () {
internals.openStat = function (path, mode, callback) {
fileStream.removeListener('error', onError);
return callback(null, fileStream);
};
Fs.open(path, mode, function(err, fd) {
fileStream.once('error', onError);
fileStream.once('open', onOpen);
if (err) {
return callback(Boom.notFound());
}
Fs.fstat(fd, function(err, stat) {
if (err) {
Fs.close(fd, Hoek.ignore);
return callback(Boom.wrap(err, null, 'failed to stat file'));
}
if (stat.isDirectory()) {
Fs.close(fd, Hoek.ignore);
return callback(Boom.forbidden());
}
return callback(null, fd, stat);
});
});
};
internals.close = function (response) {
if (response.source.fd !== null) {
Fs.close(response.source.fd, Hoek.ignore);
response.source.fd = null;
}
};
exports.Etags = LruCache;

@@ -12,3 +12,19 @@ // Load modules

exports.file = File;
exports.directory = Directory;
exports.register = function (server, options, next) {
server.expose('_etags', server.settings.files.etagsCacheMaxSize ? new File.Etags(server.settings.files.etagsCacheMaxSize) : null);
server.handler('file', File.handler);
server.handler('directory', Directory.handler);
server.decorate('reply', 'file', function (path, options) {
return this.response(File.response(path, options, this.request));
});
return next();
};
exports.register.attributes = {
pkg: require('../package.json')
};
{
"name": "inert",
"description": "Static file and directory handlers for hapi.js",
"version": "1.1.0",
"description": "Static file and directory handlers plugin for hapi.js",
"version": "2.0.0",
"repository": "git://github.com/hapijs/inert",

@@ -11,3 +11,4 @@ "main": "index",

"handler",
"hapi"
"hapi",
"plugin"
],

@@ -21,9 +22,9 @@ "engines": {

"items": "1.x.x",
"joi": "^4.7.x",
"mimos": "1.x.x",
"joi": "5.x.x",
"lru-cache": "2.5.x"
},
"devDependencies": {
"hapi": "7.x.x",
"lab": "4.x.x"
"code": "1.x.x",
"hapi": "8.x.x",
"lab": "5.x.x"
},

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

#inert
Static file and directory handlers for hapi.js.
Static file and directory handlers plugin for hapi.js.
[![Build Status](https://secure.travis-ci.org/hapijs/inert.png)](http://travis-ci.org/hapijs/inert)
Lead Maintainer - [Eran Hammer](https://github.com/hueniverse)
Lead Maintainer - [Gil Pedersen](https://github.com/kanongil)

@@ -6,3 +6,5 @@ // Load modules

var Boom = require('boom');
var Code = require('code');
var Hapi = require('hapi');
var Hoek = require('hoek');
var Inert = require('..');

@@ -22,627 +24,670 @@ var Lab = require('lab');

var it = lab.it;
var expect = Lab.expect;
var expect = Code.expect;
describe('handler()', function () {
describe('directory', function () {
var count = 0;
var provisionServer = function (options, name) {
describe('handler()', function () {
var server = new Hapi.Server(name || 'domain' + (++count).toString(), options);
server.handler('directoryTest', Inert.directory.handler);
return server;
};
var provisionServer = function (connection, debug) {
it('returns a 403 when no index exists and listing is disabled', function (done) {
var server = new Hapi.Server({ minimal: true, debug: debug });
server.connection(connection || { routes: { files: { relativeTo: __dirname } }, router: { stripTrailingSlash: false } });
server.register(Inert, Hoek.ignore);
return server;
};
var server = provisionServer({ files: { relativeTo: __dirname } });
server.route({ method: 'GET', path: '/directory/{path*}', handler: { directoryTest: { path: '.' } } }); // Use '.' to test path normalization
it('returns a 403 when no index exists and listing is disabled', function (done) {
server.inject('/directory/', function (res) {
var server = provisionServer();
server.route({ method: 'GET', path: '/directory/{path*}', handler: { directory: { path: '.' } } }); // Use '.' to test path normalization
expect(res.statusCode).to.equal(403);
done();
server.inject('/directory/', function (res) {
expect(res.statusCode).to.equal(403);
done();
});
});
});
it('returns a 403 when requesting a path containing \'..\'', function (done) {
it('returns a 403 when requesting a path containing \'..\'', function (done) {
var server = provisionServer({ files: { relativeTo: __dirname } });
server.route({ method: 'GET', path: '/directory/{path*}', handler: { directoryTest: { path: './' } } });
var server = provisionServer();
server.route({ method: 'GET', path: '/directory/{path*}', handler: { directory: { path: './' } } });
server.inject('/directory/..', function (res) {
server.inject('/directory/..', function (res) {
expect(res.statusCode).to.equal(403);
done();
expect(res.statusCode).to.equal(403);
done();
});
});
});
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', function (done) {
var server = provisionServer({ files: { relativeTo: __dirname } });
server.route({ method: 'GET', path: '/directory/{path*}', handler: { directoryTest: { path: './' } } });
var server = provisionServer();
server.route({ method: 'GET', path: '/directory/{path*}', handler: { directory: { path: './' } } });
server.inject('/directory/xyz', function (res) {
server.inject('/directory/xyz', function (res) {
expect(res.statusCode).to.equal(404);
done();
expect(res.statusCode).to.equal(404);
done();
});
});
});
it('returns a file when requesting a file from the directory', function (done) {
it('returns a file when requesting a file from the directory', function (done) {
var server = provisionServer({ files: { relativeTo: __dirname } });
server.route({ method: 'GET', path: '/directory/{path*}', handler: { directoryTest: { path: './' } } });
var server = provisionServer();
server.route({ method: 'GET', path: '/directory/{path*}', handler: { directory: { path: './' } } });
server.inject('/directory/directory.js', function (res) {
server.inject('/directory/directory.js', function (res) {
expect(res.statusCode).to.equal(200);
expect(res.payload).to.contain('hapi');
done();
expect(res.statusCode).to.equal(200);
expect(res.payload).to.contain('hapi');
done();
});
});
});
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', function (done) {
var server = provisionServer({ files: { relativeTo: __dirname } });
server.route({ method: 'GET', path: '/multiple/{path*}', handler: { directoryTest: { path: ['./', '../'], listing: true } } });
var 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', function (res) {
expect(res.statusCode).to.equal(200);
expect(res.payload).to.contain('name": "inert"');
done();
expect(res.statusCode).to.equal(200);
expect(res.payload).to.contain('name": "inert"');
done();
});
});
});
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', function (done) {
var server = provisionServer({ files: { relativeTo: __dirname } });
server.route({ method: 'GET', path: '/multiple/{path*}', handler: { directoryTest: { path: function () { return ['./', '../']; }, listing: true } } });
var server = provisionServer();
server.route({ method: 'GET', path: '/multiple/{path*}', handler: { directory: { path: function () { return ['./', '../']; }, listing: true } } });
server.inject('/multiple/package.json', function (res) {
server.inject('/multiple/package.json', function (res) {
expect(res.statusCode).to.equal(200);
expect(res.payload).to.contain('name": "inert"');
done();
expect(res.statusCode).to.equal(200);
expect(res.payload).to.contain('name": "inert"');
done();
});
});
});
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', function (done) {
var server = provisionServer({ files: { relativeTo: __dirname } });
server.route({ method: 'GET', path: '/directory/{path*}', handler: { directoryTest: { path: './' } } });
var 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', function (res) {
expect(res.statusCode).to.equal(200);
expect(res.payload).to.contain('test');
done();
expect(res.statusCode).to.equal(200);
expect(res.payload).to.contain('test');
done();
});
});
});
it('returns the correct listing links when viewing top level path', function (done) {
it('returns the correct listing links when viewing top level path', function (done) {
var server = provisionServer({ files: { relativeTo: __dirname } });
server.route({ method: 'GET', path: '/{path*}', handler: { directoryTest: { path: './', index: true, listing: true } } });
var server = provisionServer();
server.route({ method: 'GET', path: '/{path*}', handler: { directory: { path: './', index: true, listing: true } } });
server.inject('/', function (res) {
server.inject('/', function (res) {
expect(res.statusCode).to.equal(200);
expect(res.payload).to.contain('href="/file.js"');
done();
expect(res.statusCode).to.equal(200);
expect(res.payload).to.contain('href="/file.js"');
done();
});
});
});
it('does not contain any double / when viewing sub path listing', function (done) {
it('does not contain any double / when viewing sub path listing', function (done) {
var server = provisionServer({ files: { relativeTo: __dirname } });
server.route({ method: 'GET', path: '/showindex/{path*}', handler: { directoryTest: { path: './', index: true, listing: true } } });
var server = provisionServer();
server.route({ method: 'GET', path: '/showindex/{path*}', handler: { directory: { path: './', index: true, listing: true } } });
server.inject('/showindex/', function (res) {
server.inject('/showindex/', function (res) {
expect(res.statusCode).to.equal(200);
expect(res.payload).to.not.contain('//');
done();
expect(res.statusCode).to.equal(200);
expect(res.payload).to.not.contain('//');
done();
});
});
});
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', function (done) {
var server = provisionServer({ files: { relativeTo: __dirname } });
server.route({ method: 'GET', path: '/showindex/{path*}', handler: { directoryTest: { path: './', index: true, listing: true } } });
var 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/', function (res) {
expect(res.statusCode).to.equal(200);
expect(res.payload).to.contain('href="/showindex/directory/subdir/subsubdir"');
done();
expect(res.statusCode).to.equal(200);
expect(res.payload).to.contain('href="/showindex/directory/subdir/subsubdir"');
done();
});
});
});
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', function (done) {
var server = provisionServer({ files: { relativeTo: __dirname } });
server.route({ method: 'GET', path: '/showindex/{path*}', handler: { directoryTest: { path: './', index: true, listing: true } } });
var 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/', function (res) {
expect(res.statusCode).to.equal(200);
expect(res.payload).to.contain('href="/showindex/directory/subdir/sub%20subdir%3D"');
done();
expect(res.statusCode).to.equal(200);
expect(res.payload).to.contain('href="/showindex/directory/subdir/sub%20subdir%3D"');
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', 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', function (done) {
var server = provisionServer({ files: { relativeTo: __dirname } });
server.route({ method: 'GET', path: '/showindex/{path*}', handler: { directoryTest: { path: './', index: true, listing: true } } });
var 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/', function (res) {
expect(res.statusCode).to.equal(200);
expect(res.payload).to.contain('href="/showindex/directory/subdir/sub%20subdir%3D/subsubsubdir/test.txt"');
done();
expect(res.statusCode).to.equal(200);
expect(res.payload).to.contain('href="/showindex/directory/subdir/sub%20subdir%3D/subsubsubdir/test.txt"');
done();
});
});
});
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', function (done) {
var server = provisionServer({ files: { relativeTo: __dirname } });
server.route({ method: 'GET', path: '/directory/{path*}', handler: { directoryTest: { path: './', index: true, listing: true } } });
var 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', function (res) {
expect(res.statusCode).to.equal(200);
expect(res.payload).to.equal('{"test":"test"}');
done();
expect(res.statusCode).to.equal(200);
expect(res.payload).to.equal('{"test":"test"}');
done();
});
});
});
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', function (done) {
var server = provisionServer({ files: { relativeTo: __dirname } });
server.route({ method: 'GET', path: '/directory/{path*}', handler: { directoryTest: { path: './', index: true, listing: true } } });
var 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', function (res) {
expect(res.statusCode).to.equal(200);
expect(res.payload).to.equal('test');
done();
expect(res.statusCode).to.equal(200);
expect(res.payload).to.equal('test');
done();
});
});
});
it('returns a 403 when index and listing are disabled', function (done) {
it('returns a 403 when index and listing are disabled', function (done) {
var server = provisionServer({ files: { relativeTo: __dirname } });
server.route({ method: 'GET', path: '/directoryx/{path*}', handler: { directoryTest: { path: '../', index: false } } });
var server = provisionServer();
server.route({ method: 'GET', path: '/directoryx/{path*}', handler: { directory: { path: '../', index: false } } });
server.inject('/directoryx/', function (res) {
server.inject('/directoryx/', function (res) {
expect(res.statusCode).to.equal(403);
done();
expect(res.statusCode).to.equal(403);
done();
});
});
});
it('returns a list of files when listing is enabled', function (done) {
it('returns a list of files when listing is enabled', function (done) {
var server = provisionServer({ files: { relativeTo: __dirname } });
server.route({ method: 'GET', path: '/directorylist/{path*}', handler: { directoryTest: { path: '../', listing: true } } });
var server = provisionServer();
server.route({ method: 'GET', path: '/directorylist/{path*}', handler: { directory: { path: '../', listing: true } } });
server.inject('/directorylist/', function (res) {
server.inject('/directorylist/', function (res) {
expect(res.statusCode).to.equal(200);
expect(res.payload).to.contain('package.json');
done();
expect(res.statusCode).to.equal(200);
expect(res.payload).to.contain('package.json');
done();
});
});
});
it('returns a list of files for subdirectory', function (done) {
it('returns a list of files for subdirectory', function (done) {
var server = provisionServer({ files: { relativeTo: __dirname } });
server.route({ method: 'GET', path: '/directorylist/{path*}', handler: { directoryTest: { path: '../', listing: true } } });
var server = provisionServer();
server.route({ method: 'GET', path: '/directorylist/{path*}', handler: { directory: { path: '../', listing: true } } });
server.inject('/directorylist/test/', function (res) {
server.inject('/directorylist/test/', function (res) {
expect(res.statusCode).to.equal(200);
expect(res.payload).to.contain('directory.js');
done();
expect(res.statusCode).to.equal(200);
expect(res.payload).to.contain('directory.js');
done();
});
});
});
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', function (done) {
var server = provisionServer({ files: { relativeTo: __dirname } });
server.route({ method: 'GET', path: '/directorylistx/{path*}', handler: { directoryTest: { path: '../', listing: true, index: false } } });
var server = provisionServer();
server.route({ method: 'GET', path: '/directorylistx/{path*}', handler: { directory: { path: '../', listing: true, index: false } } });
server.inject('/directorylistx/', function (res) {
server.inject('/directorylistx/', function (res) {
expect(res.statusCode).to.equal(200);
expect(res.payload).to.contain('package.json');
done();
expect(res.statusCode).to.equal(200);
expect(res.payload).to.contain('package.json');
done();
});
});
});
it('returns the index when found', function (done) {
it('returns the index when found', function (done) {
var server = provisionServer({ files: { relativeTo: __dirname } });
server.route({ method: 'GET', path: '/directoryIndex/{path*}', handler: { directoryTest: { path: './directory/' } } });
var server = provisionServer();
server.route({ method: 'GET', path: '/directoryIndex/{path*}', handler: { directory: { path: './directory/' } } });
server.inject('/directoryIndex/', function (res) {
server.inject('/directoryIndex/', function (res) {
expect(res.statusCode).to.equal(200);
expect(res.payload).to.contain('<p>test</p>');
done();
expect(res.statusCode).to.equal(200);
expect(res.payload).to.contain('<p>test</p>');
done();
});
});
});
it('returns the index when found in hidden folder', function (done) {
it('returns the index when served from a hidden folder', function (done) {
var server = provisionServer({ files: { relativeTo: __dirname } });
server.route({ method: 'GET', path: '/{path*}', handler: { directoryTest: { path: './directory/.dot' } } });
var server = provisionServer();
server.route({ method: 'GET', path: '/{path*}', handler: { directory: { path: './directory/.dot' } } });
server.inject('/index.html', function (res) {
server.inject('/index.html', function (res) {
expect(res.statusCode).to.equal(200);
expect(res.payload).to.contain('<p>test</p>');
expect(res.statusCode).to.equal(200);
expect(res.payload).to.contain('<p>test</p>');
server.inject('/', function (res) {
expect(res.statusCode).to.equal(200);
expect(res.payload).to.contain('<p>test</p>');
done();
});
});
});
it('returns listing when served from a hidden folder', function (done) {
var server = provisionServer();
server.route({ method: 'GET', path: '/{path*}', handler: { directory: { path: './directory/.dot', index: false, listing: true } } });
server.inject('/', function (res) {
expect(res.statusCode).to.equal(200);
expect(res.payload).to.contain('<p>test</p>');
expect(res.payload).to.contain('index.html');
done();
});
});
});
it('returns listing when found in hidden folder', function (done) {
it('returns a 500 when index.html is a directory', function (done) {
var server = provisionServer({ files: { relativeTo: __dirname } });
server.route({ method: 'GET', path: '/{path*}', handler: { directoryTest: { path: './directory/.dot', index: false, listing: true } } });
var server = provisionServer(null, false);
server.route({ method: 'GET', path: '/directoryIndex/{path*}', handler: { directory: { path: './directory/' } } });
server.inject('/', function (res) {
server.inject('/directoryIndex/invalid/', function (res) {
expect(res.statusCode).to.equal(200);
expect(res.payload).to.contain('index.html');
done();
expect(res.statusCode).to.equal(500);
done();
});
});
});
it('returns a 500 when index.html is a directory', function (done) {
it('returns the correct file when using a fn directory handler', function (done) {
var server = provisionServer({ files: { relativeTo: __dirname }, debug: false });
server.route({ method: 'GET', path: '/directoryIndex/{path*}', handler: { directoryTest: { path: './directory/' } } });
var directoryFn = function (request) {
server.inject('/directoryIndex/invalid/', function (res) {
return '../lib';
};
expect(res.statusCode).to.equal(500);
done();
});
});
var server = provisionServer();
server.route({ method: 'GET', path: '/directoryfn/{path?}', handler: { directory: { path: directoryFn } } });
it('returns the correct file when using a fn directory handler', function (done) {
server.inject('/directoryfn/index.js', function (res) {
var directoryFn = function (request) {
expect(res.statusCode).to.equal(200);
expect(res.payload).to.contain('export');
done();
});
});
return '../lib';
};
it('returns listing with hidden files when hidden files should be shown', function (done) {
var server = provisionServer({ files: { relativeTo: __dirname } });
server.route({ method: 'GET', path: '/directoryfn/{path?}', handler: { directoryTest: { path: directoryFn } } });
var server = provisionServer();
server.route({ method: 'GET', path: '/showhidden/{path*}', handler: { directory: { path: './', showHidden: true, listing: true } } });
server.inject('/directoryfn/index.js', function (res) {
server.inject('/showhidden/', function (res) {
expect(res.statusCode).to.equal(200);
expect(res.payload).to.contain('export');
done();
expect(res.payload).to.contain('.hidden');
done();
});
});
});
it('returns listing with hidden files when hidden files should be shown', function (done) {
it('returns listing without hidden files when hidden files should not be shown', function (done) {
var server = provisionServer({ files: { relativeTo: __dirname } });
server.route({ method: 'GET', path: '/showhidden/{path*}', handler: { directoryTest: { path: './', showHidden: true, listing: true } } });
var server = provisionServer();
server.route({ method: 'GET', path: '/noshowhidden/{path*}', handler: { directory: { path: './', listing: true } } });
server.inject('/showhidden/', function (res) {
server.inject('/noshowhidden/', function (res) {
expect(res.payload).to.contain('.hidden');
done();
expect(res.payload).to.not.contain('.hidden');
expect(res.payload).to.contain('directory.js');
done();
});
});
});
it('returns listing without hidden files when hidden files should not be shown', function (done) {
it('returns a 404 response when requesting a hidden file when showHidden is disabled', function (done) {
var server = provisionServer({ files: { relativeTo: __dirname } });
server.route({ method: 'GET', path: '/noshowhidden/{path*}', handler: { directoryTest: { path: './', listing: true } } });
var server = provisionServer();
server.route({ method: 'GET', path: '/noshowhidden/{path*}', handler: { directory: { path: './', listing: true } } });
server.inject('/noshowhidden/', function (res) {
server.inject('/noshowhidden/.hidden', function (res) {
expect(res.payload).to.not.contain('.hidden');
expect(res.payload).to.contain('directory.js');
done();
expect(res.statusCode).to.equal(404);
done();
});
});
});
it('returns a 404 response when requesting a hidden file when showHidden is disabled', function (done) {
it('returns a 404 response when requesting a file in a hidden directory when showHidden is disabled', function (done) {
var server = provisionServer({ files: { relativeTo: __dirname } });
server.route({ method: 'GET', path: '/noshowhidden/{path*}', handler: { directoryTest: { path: './', listing: true } } });
var server = provisionServer();
server.route({ method: 'GET', path: '/noshowhidden/{path*}', handler: { directory: { path: './directory', listing: true } } });
server.inject('/noshowhidden/.hidden', function (res) {
server.inject('/noshowhidden/.dot/index.html', function (res) {
expect(res.statusCode).to.equal(404);
done();
expect(res.statusCode).to.equal(404);
server.inject('/noshowhidden/.dot/', function (res) {
expect(res.statusCode).to.equal(404);
done();
});
});
});
});
it('returns a file when requesting a hidden file when showHidden is enabled', function (done) {
it('returns a 404 response when requesting a hidden directory listing when showHidden is disabled', function (done) {
var server = provisionServer({ files: { relativeTo: __dirname } });
server.route({ method: 'GET', path: '/showhidden/{path*}', handler: { directoryTest: { path: './', showHidden: true, listing: true } } });
var server = provisionServer();
server.route({ method: 'GET', path: '/noshowhidden/{path*}', handler: { directory: { path: './directory', listing: true, index: false } } });
server.inject('/showhidden/.hidden', function (res) {
server.inject('/noshowhidden/.dot/', function (res) {
expect(res.payload).to.contain('Ssssh!\n');
done();
expect(res.statusCode).to.equal(404);
done();
});
});
});
it('redirects to the same path with / appended if asking for a directory', function (done) {
it('returns a file when requesting a hidden file when showHidden is enabled', function (done) {
var server = provisionServer({ files: { relativeTo: __dirname } });
server.route({ method: 'GET', path: '/redirect/{path*}', handler: { directoryTest: { path: './', index: true, listing: true } } });
var server = provisionServer();
server.route({ method: 'GET', path: '/showhidden/{path*}', handler: { directory: { path: './', showHidden: true, listing: true } } });
server.inject('http://example.com/redirect/directory/subdir', function (res) {
server.inject('/showhidden/.hidden', function (res) {
expect(res.statusCode).to.equal(302);
expect(res.headers.location).to.equal('http://example.com/redirect/directory/subdir/');
done();
expect(res.payload).to.contain('Ssssh!');
done();
});
});
});
it('does not redirect to the same path with / appended redirectToSlash disabled', function (done) {
it('returns a a file when requesting a file in a hidden directory when showHidden is enabled', function (done) {
var server = provisionServer({ files: { relativeTo: __dirname } });
server.route({ method: 'GET', path: '/redirect/{path*}', handler: { directoryTest: { path: './', index: true, listing: true, redirectToSlash: false } } });
var server = provisionServer();
server.route({ method: 'GET', path: '/noshowhidden/{path*}', handler: { directory: { path: './directory', showHidden: true, listing: true } } });
server.inject('http://example.com/redirect/directory/subdir', function (res) {
server.inject('/noshowhidden/.dot/index.html', function (res) {
expect(res.statusCode).to.equal(200);
expect(res.result).to.contain('<html>');
done();
expect(res.statusCode).to.equal(200);
expect(res.payload).to.contain('test');
server.inject('/noshowhidden/.dot/', function (res) {
expect(res.statusCode).to.equal(200);
expect(res.payload).to.contain('test');
done();
});
});
});
});
it('does not redirect to the same path with / appended when server stripTrailingSlash is true', function (done) {
it('redirects to the same path with / appended if asking for a directory', function (done) {
var server = provisionServer({ files: { relativeTo: __dirname }, router: { stripTrailingSlash: true } });
server.route({ method: 'GET', path: '/redirect/{path*}', handler: { directoryTest: { path: './', index: true, listing: true } } });
var server = provisionServer();
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('/redirect/directory/subdir', function (res) {
expect(res.statusCode).to.equal(200);
expect(res.result).to.contain('<html>');
done();
expect(res.statusCode).to.equal(302);
expect(res.headers.location).to.equal('/redirect/directory/subdir/');
done();
});
});
});
it('ignores unused path params', function (done) {
it('does not redirect to the same path with / appended redirectToSlash disabled', function (done) {
var server = provisionServer({ files: { relativeTo: __dirname } });
server.route({ method: 'GET', path: '/{ignore}/4/{path*}', handler: { directoryTest: { path: './' } } });
var server = provisionServer();
server.route({ method: 'GET', path: '/redirect/{path*}', handler: { directory: { path: './', index: true, listing: true, redirectToSlash: false } } });
server.inject('/crap/4/file.js', function (res) {
server.inject('http://example.com/redirect/directory/subdir', function (res) {
expect(res.statusCode).to.equal(200);
expect(res.payload).to.contain('hapi');
done();
expect(res.statusCode).to.equal(200);
expect(res.result).to.contain('<html>');
done();
});
});
});
it('returns error when failing to prepare file response due to bad state', function (done) {
it('does not redirect to the same path with / appended when server stripTrailingSlash is true', function (done) {
var server = provisionServer({ files: { relativeTo: __dirname }, debug: false });
server.route({ method: 'GET', path: '/directory/{path*}', handler: { directoryTest: { path: './' } } });
var server = provisionServer({ routes: { files: { relativeTo: __dirname } }, router: { stripTrailingSlash: true } });
server.route({ method: 'GET', path: '/redirect/{path*}', handler: { directory: { path: './', index: true, listing: true } } });
server.ext('onRequest', function (request, reply) {
server.inject('http://example.com/redirect/directory/subdir', function (res) {
reply.state('bad', {});
reply();
expect(res.statusCode).to.equal(200);
expect(res.result).to.contain('<html>');
done();
});
});
server.inject('/directory/file.js', function (res) {
it('ignores unused path params', function (done) {
expect(res.statusCode).to.equal(500);
done();
var server = provisionServer();
server.route({ method: 'GET', path: '/{ignore}/4/{path*}', handler: { directory: { path: './' } } });
server.inject('/crap/4/file.js', function (res) {
expect(res.statusCode).to.equal(200);
expect(res.payload).to.contain('hapi');
done();
});
});
});
it('returns error when listing fails due to directory read error', { parallel: false }, function (done) {
it('returns error when failing to prepare file response due to bad state', function (done) {
var server = provisionServer({ files: { relativeTo: __dirname } });
server.route({ method: 'GET', path: '/directorylist/{path*}', handler: { directoryTest: { path: '../', listing: true } } });
var server = provisionServer(null, false);
server.route({ method: 'GET', path: '/directory/{path*}', handler: { directory: { path: './' } } });
var orig = Fs.readdir;
Fs.readdir = function (path, callback) { Fs.readdir = orig; callback(new Error('Simulated Directory Error')); };
server.inject('/directorylist/', function (res) {
server.ext('onRequest', function (request, reply) {
expect(res.statusCode).to.equal(500);
done();
reply.state('bad', {});
return reply.continue();
});
server.inject('/directory/file.js', function (res) {
expect(res.statusCode).to.equal(500);
done();
});
});
});
it('appends default extension', function (done) {
it('returns error when listing fails due to directory read error', { parallel: false }, function (done) {
var server = provisionServer();
server.route({ method: 'GET', path: '/directory/{path*}', handler: { directoryTest: { path: __dirname, defaultExtension: 'html' } } });
var server = provisionServer();
server.route({ method: 'GET', path: '/directorylist/{path*}', handler: { directory: { path: '../', listing: true } } });
server.inject('/directory/directory/index', function (res) {
var orig = Fs.readdir;
Fs.readdir = function (path, callback) { Fs.readdir = orig; callback(new Error('Simulated Directory Error')); };
server.inject('/directorylist/', function (res) {
expect(res.statusCode).to.equal(200);
done();
expect(res.statusCode).to.equal(500);
done();
});
});
});
it('appends default extension when resource ends with /', function (done) {
it('appends default extension', function (done) {
var server = provisionServer();
server.route({ method: 'GET', path: '/directory/{path*}', handler: { directoryTest: { path: __dirname, defaultExtension: 'html' } } });
var 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', function (res) {
expect(res.statusCode).to.equal(200);
done();
expect(res.statusCode).to.equal(200);
done();
});
});
});
it('appends default extension and fails to find file', function (done) {
it('appends default extension when resource ends with /', function (done) {
var server = provisionServer();
server.route({ method: 'GET', path: '/directory/{path*}', handler: { directoryTest: { path: __dirname, defaultExtension: 'html' } } });
var 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/index/', function (res) {
expect(res.statusCode).to.equal(404);
done();
expect(res.statusCode).to.equal(200);
done();
});
});
});
it('does not append default extension when directory exists', function (done) {
it('appends default extension and fails to find file', function (done) {
var server = provisionServer();
server.route({ method: 'GET', path: '/directory/{path*}', handler: { directoryTest: { path: __dirname, defaultExtension: 'html' } } });
var 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/none', function (res) {
expect(res.statusCode).to.equal(302);
done();
expect(res.statusCode).to.equal(404);
done();
});
});
});
it('resolves windows path name from pack using root path', { skip: process.platform !== 'win32' }, function (done) {
it('does not append default extension when directory exists', function (done) {
// Note: This uses a root path of where the test file lives (to simulate requiring a pack), which is why the
// directory handler path is ./directory instead of ./test/directory like the other test below
var server = provisionServer();
server.route({ method: 'GET', path: '/directory/{path*}', handler: { directory: { path: __dirname, defaultExtension: 'html' } } });
var pack = {
name: 'directory test',
version: '1.0',
path: __dirname,
register: function (plugin, options, next) {
server.inject('/directory/directory', function (res) {
plugin.route({ method: 'GET', path: '/test/{path*}', config: { handler: { directoryTest: { path: './directory', index: false, listing: false } } } });
expect(res.statusCode).to.equal(302);
done();
});
});
it('resolves path name from plugin using specified path', function (done) {
var plugin = function (server, options, next) {
server.path(__dirname);
server.route({ method: 'GET', path: '/test/{path*}', config: { handler: { directory: { path: Path.join('.', 'directory'), index: false, listing: false } } } });
return next();
}
};
};
plugin.attributes = {
name: 'directory test',
version: '1.0'
};
var server = provisionServer();
server.pack.register(pack, {}, function () { });
var server = provisionServer({ router: { stripTrailingSlash: false } });
server.register({ register: plugin }, {}, function () { });
server.inject('/test/index.html', function (res) {
server.inject('/test/index.html', function (res) {
expect(res.statusCode).to.equal(200);
done();
expect(res.statusCode).to.equal(200);
done();
});
});
});
it('resolves windows path name from pack using relative path', { skip: process.platform !== 'win32' }, function (done) {
it('resolves path name from plugin using relative path', function (done) {
// Note: This simulates a pack which is not "required", and therefore doesn't have a path set. This will use
// the process' root directory then, so the directory handler needs to specify the path relative to that
var pack = {
name: 'directory test',
version: '1.0',
register: function (plugin, options, next) {
plugin.route({ method: 'GET', path: '/test/{path*}', config: { handler: { directoryTest: { path: './test/directory', index: false, listing: false } } } });
var plugin = function (server, options, next) {
server.route({ method: 'GET', path: '/test/{path*}', config: { handler: { directory: { path: Path.join('.', 'test', 'directory'), index: false, listing: false } } } });
return next();
}
};
};
plugin.attributes = {
name: 'directory test',
version: '1.0'
};
var server = provisionServer();
server.pack.register(pack, {}, function () { });
var server = provisionServer({ router: { stripTrailingSlash: false } });
server.register({ register: plugin }, {}, function () { });
server.inject('/test/index.html', function (res) {
server.inject('/test/index.html', function (res) {
expect(res.statusCode).to.equal(200);
done();
expect(res.statusCode).to.equal(200);
done();
});
});
});
it('resolves root pathnames on windows', { skip: process.platform !== 'win32' }, function (done) {
it('resolves root pathnames', function (done) {
var server = provisionServer();
server.route({ method: 'GET', path: '/test/{path*}', handler: { directoryTest: { path: Path.join(__dirname, 'directory') } } });
var 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', function (res) {
expect(res.statusCode).to.equal(200);
done();
expect(res.statusCode).to.equal(200);
done();
});
});
});
it('resolves relative pathnames on windows', { skip: process.platform !== 'win32' }, function (done) {
it('resolves relative pathnames', function (done) {
var server = provisionServer();
server.route({ method: 'GET', path: '/test/{path*}', handler: { directoryTest: { path: './test/directory' } } });
var 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', function (res) {
expect(res.statusCode).to.equal(200);
done();
expect(res.statusCode).to.equal(200);
done();
});
});
});
it('returns error when path function returns error', function (done) {
it('returns error when path function returns error', function (done) {
var path = function () {
var path = function () {
return Boom.badRequest('Really?!');
};
return Boom.badRequest('Really?!');
};
var server = provisionServer();
server.route({ method: 'GET', path: '/test/{path*}', handler: { directoryTest: { path: path } } });
var 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', function (res) {
expect(res.statusCode).to.equal(400);
expect(res.result.message).to.equal('Really?!');
done();
expect(res.statusCode).to.equal(400);
expect(res.result.message).to.equal('Really?!');
done();
});
});
});
it('returns error when path function returns invalid response', function (done) {
it('returns error when path function returns invalid response', function (done) {
var path = function () {
var path = function () {
return 5;
};
return 5;
};
var server = provisionServer({ debug: false });
server.route({ method: 'GET', path: '/test/{path*}', handler: { directoryTest: { path: path } } });
var 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', function (res) {
expect(res.statusCode).to.equal(500);
done();
expect(res.statusCode).to.equal(500);
done();
});
});
});
it('returns a gzipped file using precompressed file', function (done) {
it('returns a gzipped file using precompressed file', function (done) {
var content = Fs.readFileSync('./test/file/image.png.gz');
var server = provisionServer();
server.route({ method: 'GET', path: '/{p*}', handler: { directory: { path: './file', lookupCompressed: true } } });
var server = provisionServer();
server.route({ method: 'GET', path: '/{p*}', handler: { directoryTest: { path: './test/file', lookupCompressed: true } } });
server.inject({ url: '/image.png', headers: { 'accept-encoding': 'gzip' } }, function (res) {
server.inject({ url: '/image.png', headers: { 'accept-encoding': 'gzip' } }, function (res) {
expect(res.headers['content-type']).to.equal('image/png');
expect(res.headers['content-encoding']).to.equal('gzip');
expect(res.headers['content-type']).to.equal('image/png');
expect(res.headers['content-encoding']).to.equal('gzip');
expect(res.headers['content-length']).to.equal(content.length);
expect(res.payload.length).to.equal(content.length);
done();
var content = Fs.readFileSync('./test/file/image.png.gz');
expect(res.headers['content-length']).to.equal(content.length);
expect(res.payload.length).to.equal(content.length);
done();
});
});
});
});

@@ -8,2 +8,3 @@ // Load modules

var Boom = require('boom');
var Code = require('code');
var Hapi = require('hapi');

@@ -25,355 +26,351 @@ var Hoek = require('hoek');

var it = lab.it;
var expect = Lab.expect;
var expect = Code.expect;
describe('handler()', function () {
describe('file', function () {
var count = 0;
var provisionServer = function (options, name) {
describe('handler()', function () {
var server = new Hapi.Server(name || 'domain' + (++count).toString(), options);
server.handler('fileTest', Inert.file.handler);
return server;
};
var provisionServer = function (relativeTo, etagsCacheMaxSize) {
it('returns a file in the response with the correct headers', function (done) {
var server = new Hapi.Server({ files: { etagsCacheMaxSize: etagsCacheMaxSize }, minimal: true });
server.connection({ routes: { files: { relativeTo: relativeTo } } });
server.register(Inert, Hoek.ignore);
return server;
};
var server = provisionServer({ files: { relativeTo: __dirname } });
var handler = function (request, reply) {
it('returns a file in the response with the correct headers', function (done) {
reply(Inert.file.response('../package.json', null, request)).code(499);
};
var server = provisionServer(__dirname);
var handler = function (request, reply) {
server.route({ method: 'GET', path: '/file', handler: handler });
reply.file('../package.json').code(499);
};
server.inject('/file', function (res) {
server.route({ method: 'GET', path: '/file', handler: handler });
expect(res.statusCode).to.equal(499);
expect(res.payload).to.contain('hapi');
expect(res.headers['content-type']).to.equal('application/json; charset=utf-8');
expect(res.headers['content-length']).to.exist;
expect(res.headers['content-disposition']).to.not.exist;
done();
server.inject('/file', function (res) {
expect(res.statusCode).to.equal(499);
expect(res.payload).to.contain('hapi');
expect(res.headers['content-type']).to.equal('application/json; charset=utf-8');
expect(res.headers['content-length']).to.exist();
expect(res.headers['content-disposition']).to.not.exist();
done();
});
});
});
it('returns a file using route relativeTo', function (done) {
it('returns a file using route relativeTo', function (done) {
var server = provisionServer();
var handler = function (request, reply) {
var server = provisionServer();
var handler = function (request, reply) {
reply(Inert.file.response('../package.json', null, request));
};
reply.file('../package.json');
};
server.route({ method: 'GET', path: '/file', handler: handler, config: { files: { relativeTo: __dirname } } });
server.route({ method: 'GET', path: '/file', handler: handler, config: { files: { relativeTo: __dirname } } });
server.inject('/file', function (res) {
server.inject('/file', function (res) {
expect(res.payload).to.contain('hapi');
done();
expect(res.payload).to.contain('hapi');
done();
});
});
});
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', function (done) {
var server = provisionServer();
server.route({ method: 'GET', path: '/', handler: { fileTest: './package.json' } });
var server = provisionServer();
server.route({ method: 'GET', path: '/', handler: { file: './package.json' } });
server.inject('/', function (res) {
server.inject('/', function (res) {
expect(res.payload).to.contain('hapi');
expect(res.headers['content-type']).to.equal('application/json; charset=utf-8');
expect(res.headers['content-length']).to.exist;
expect(res.headers['content-disposition']).to.not.exist;
done();
expect(res.payload).to.contain('hapi');
expect(res.headers['content-type']).to.equal('application/json; charset=utf-8');
expect(res.headers['content-length']).to.exist();
expect(res.headers['content-disposition']).to.not.exist();
done();
});
});
});
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', function (done) {
var server = provisionServer({ files: { relativeTo: './' } });
server.route({ method: 'GET', path: '/', handler: { fileTest: { path: './package.json', mode: 'inline' } } });
var server = provisionServer('./');
server.route({ method: 'GET', path: '/', handler: { file: { path: './package.json', mode: 'inline' } } });
server.inject('/', function (res) {
server.inject('/', function (res) {
expect(res.payload).to.contain('hapi');
expect(res.headers['content-type']).to.equal('application/json; charset=utf-8');
expect(res.headers['content-length']).to.exist;
expect(res.headers['content-disposition']).to.equal('inline; filename=package.json');
done();
expect(res.payload).to.contain('hapi');
expect(res.headers['content-type']).to.equal('application/json; charset=utf-8');
expect(res.headers['content-length']).to.exist();
expect(res.headers['content-disposition']).to.equal('inline; filename=package.json');
done();
});
});
});
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', function (done) {
var server = provisionServer({ files: { relativeTo: './' } });
server.route({ method: 'GET', path: '/', handler: { fileTest: { path: './package.json', mode: 'inline', filename: 'attachment.json' } } });
var server = provisionServer('./');
server.route({ method: 'GET', path: '/', handler: { file: { path: './package.json', mode: 'inline', filename: 'attachment.json' } } });
server.inject('/', function (res) {
server.inject('/', function (res) {
expect(res.payload).to.contain('hapi');
expect(res.headers['content-type']).to.equal('application/json; charset=utf-8');
expect(res.headers['content-length']).to.exist;
expect(res.headers['content-disposition']).to.equal('inline; filename=attachment.json');
done();
expect(res.payload).to.contain('hapi');
expect(res.headers['content-type']).to.equal('application/json; charset=utf-8');
expect(res.headers['content-length']).to.exist();
expect(res.headers['content-disposition']).to.equal('inline; filename=attachment.json');
done();
});
});
});
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', function (done) {
var server = provisionServer();
server.route({ method: 'GET', path: '/', handler: { fileTest: { path: './package.json', mode: 'attachment' } } });
var server = provisionServer();
server.route({ method: 'GET', path: '/', handler: { file: { path: './package.json', mode: 'attachment' } } });
server.inject('/', function (res) {
server.inject('/', function (res) {
expect(res.payload).to.contain('hapi');
expect(res.headers['content-type']).to.equal('application/json; charset=utf-8');
expect(res.headers['content-length']).to.exist;
expect(res.headers['content-disposition']).to.equal('attachment; filename=package.json');
done();
expect(res.payload).to.contain('hapi');
expect(res.headers['content-type']).to.equal('application/json; charset=utf-8');
expect(res.headers['content-length']).to.exist();
expect(res.headers['content-disposition']).to.equal('attachment; filename=package.json');
done();
});
});
});
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', function (done) {
var server = provisionServer();
server.route({ method: 'GET', path: '/', handler: { fileTest: { path: './package.json', mode: 'attachment', filename: 'attachment.json' } } });
var server = provisionServer();
server.route({ method: 'GET', path: '/', handler: { file: { path: './package.json', mode: 'attachment', filename: 'attachment.json' } } });
server.inject('/', function (res) {
server.inject('/', function (res) {
expect(res.payload).to.contain('hapi');
expect(res.headers['content-type']).to.equal('application/json; charset=utf-8');
expect(res.headers['content-length']).to.exist;
expect(res.headers['content-disposition']).to.equal('attachment; filename=attachment.json');
done();
expect(res.payload).to.contain('hapi');
expect(res.headers['content-type']).to.equal('application/json; charset=utf-8');
expect(res.headers['content-length']).to.exist();
expect(res.headers['content-disposition']).to.equal('attachment; filename=attachment.json');
done();
});
});
});
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', function (done) {
var server = provisionServer();
server.route({ method: 'GET', path: '/', handler: { fileTest: { path: './package.json', mode: false } } });
var server = provisionServer();
server.route({ method: 'GET', path: '/', handler: { file: { path: './package.json', mode: false } } });
server.inject('/', function (res) {
server.inject('/', function (res) {
expect(res.payload).to.contain('hapi');
expect(res.headers['content-type']).to.equal('application/json; charset=utf-8');
expect(res.headers['content-length']).to.exist;
expect(res.headers['content-disposition']).to.not.exist;
done();
expect(res.payload).to.contain('hapi');
expect(res.headers['content-type']).to.equal('application/json; charset=utf-8');
expect(res.headers['content-length']).to.exist();
expect(res.headers['content-disposition']).to.not.exist();
done();
});
});
});
it('returns a file with correct headers when using attachment mode', function (done) {
it('returns a file with correct headers when using attachment mode', function (done) {
var server = provisionServer({ files: { relativeTo: __dirname } });
var handler = function (request, reply) {
var server = provisionServer(__dirname);
var handler = function (request, reply) {
reply(Inert.file.response(__dirname + '/../package.json', { mode: 'attachment' }, request));
};
reply.file(Path.join(__dirname, '..', 'package.json'), { mode: 'attachment' });
};
server.route({ method: 'GET', path: '/file', handler: handler });
server.route({ method: 'GET', path: '/file', handler: handler });
server.inject('/file', function (res) {
server.inject('/file', function (res) {
expect(res.payload).to.contain('hapi');
expect(res.headers['content-type']).to.equal('application/json; charset=utf-8');
expect(res.headers['content-length']).to.exist;
expect(res.headers['content-disposition']).to.equal('attachment; filename=package.json');
done();
expect(res.payload).to.contain('hapi');
expect(res.headers['content-type']).to.equal('application/json; charset=utf-8');
expect(res.headers['content-length']).to.exist();
expect(res.headers['content-disposition']).to.equal('attachment; filename=package.json');
done();
});
});
});
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', function (done) {
var server = provisionServer({ files: { relativeTo: __dirname } });
var handler = function (request, reply) {
var server = provisionServer(__dirname);
var handler = function (request, reply) {
reply(Inert.file.response(__dirname + '/../package.json', { mode: 'attachment', filename: 'attachment.json' }, request));
};
reply.file(Path.join(__dirname, '..', 'package.json'), { mode: 'attachment', filename: 'attachment.json' });
};
server.route({ method: 'GET', path: '/file', handler: handler });
server.route({ method: 'GET', path: '/file', handler: handler });
server.inject('/file', function (res) {
server.inject('/file', function (res) {
expect(res.payload).to.contain('hapi');
expect(res.headers['content-type']).to.equal('application/json; charset=utf-8');
expect(res.headers['content-length']).to.exist;
expect(res.headers['content-disposition']).to.equal('attachment; filename=attachment.json');
done();
expect(res.payload).to.contain('hapi');
expect(res.headers['content-type']).to.equal('application/json; charset=utf-8');
expect(res.headers['content-length']).to.exist();
expect(res.headers['content-disposition']).to.equal('attachment; filename=attachment.json');
done();
});
});
});
it('returns a file with correct headers when using inline mode', function (done) {
it('returns a file with correct headers when using inline mode', function (done) {
var server = provisionServer({ files: { relativeTo: __dirname } });
var handler = function (request, reply) {
var server = provisionServer(__dirname);
var handler = function (request, reply) {
reply(Inert.file.response(__dirname + '/../package.json', { mode: 'inline' }, request));
};
reply.file(Path.join(__dirname, '..', 'package.json'), { mode: 'inline' });
};
server.route({ method: 'GET', path: '/file', handler: handler });
server.route({ method: 'GET', path: '/file', handler: handler });
server.inject('/file', function (res) {
server.inject('/file', function (res) {
expect(res.payload).to.contain('hapi');
expect(res.headers['content-type']).to.equal('application/json; charset=utf-8');
expect(res.headers['content-length']).to.exist;
expect(res.headers['content-disposition']).to.equal('inline; filename=package.json');
done();
expect(res.payload).to.contain('hapi');
expect(res.headers['content-type']).to.equal('application/json; charset=utf-8');
expect(res.headers['content-length']).to.exist();
expect(res.headers['content-disposition']).to.equal('inline; filename=package.json');
done();
});
});
});
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', function (done) {
var server = provisionServer({ files: { relativeTo: __dirname } });
var handler = function (request, reply) {
var server = provisionServer(__dirname);
var handler = function (request, reply) {
reply(Inert.file.response(__dirname + '/../package.json', { mode: 'inline', filename: 'attachment.json' }, request));
};
reply.file(Path.join(__dirname, '..', 'package.json'), { mode: 'inline', filename: 'attachment.json' });
};
server.route({ method: 'GET', path: '/file', handler: handler });
server.route({ method: 'GET', path: '/file', handler: handler });
server.inject('/file', function (res) {
server.inject('/file', function (res) {
expect(res.payload).to.contain('hapi');
expect(res.headers['content-type']).to.equal('application/json; charset=utf-8');
expect(res.headers['content-length']).to.exist;
expect(res.headers['content-disposition']).to.equal('inline; filename=attachment.json');
done();
expect(res.payload).to.contain('hapi');
expect(res.headers['content-type']).to.equal('application/json; charset=utf-8');
expect(res.headers['content-length']).to.exist();
expect(res.headers['content-disposition']).to.equal('inline; filename=attachment.json');
done();
});
});
});
it('returns a 404 when the file is not found', function (done) {
it('returns a 404 when the file is not found', function (done) {
var server = provisionServer({ files: { relativeTo: '/no/such/path/x1' } });
var server = provisionServer('/no/such/path/x1');
server.route({ method: 'GET', path: '/filenotfound', handler: { fileTest: 'nopes' } });
server.route({ method: 'GET', path: '/filenotfound', handler: { file: 'nopes' } });
server.inject('/filenotfound', function (res) {
server.inject('/filenotfound', function (res) {
expect(res.statusCode).to.equal(404);
done();
expect(res.statusCode).to.equal(404);
done();
});
});
});
it('returns a 403 when the file is a directory', function (done) {
it('returns a 403 when the file is a directory', function (done) {
var server = provisionServer();
var server = provisionServer();
server.route({ method: 'GET', path: '/filefolder', handler: { fileTest: 'lib' } });
server.route({ method: 'GET', path: '/filefolder', handler: { file: 'lib' } });
server.inject('/filefolder', function (res) {
server.inject('/filefolder', function (res) {
expect(res.statusCode).to.equal(403);
done();
expect(res.statusCode).to.equal(403);
done();
});
});
});
it('returns a file using the build-in handler config', function (done) {
it('returns a file using the build-in handler config', function (done) {
var server = provisionServer({ files: { relativeTo: __dirname } });
server.route({ method: 'GET', path: '/staticfile', handler: { fileTest: __dirname + '/../package.json' } });
var server = provisionServer(__dirname);
server.route({ method: 'GET', path: '/staticfile', handler: { file: Path.join(__dirname, '..', 'package.json') } });
server.inject('/staticfile', function (res) {
server.inject('/staticfile', function (res) {
expect(res.payload).to.contain('hapi');
expect(res.headers['content-type']).to.equal('application/json; charset=utf-8');
expect(res.headers['content-length']).to.exist;
done();
expect(res.payload).to.contain('hapi');
expect(res.headers['content-type']).to.equal('application/json; charset=utf-8');
expect(res.headers['content-length']).to.exist();
done();
});
});
});
it('returns a file using the file function with the build-in handler config', function (done) {
it('returns a file using the file function with the build-in handler config', function (done) {
var filenameFn = function (request) {
var filenameFn = function (request) {
return '../' + request.params.file;
};
return '../' + request.params.file;
};
var server = provisionServer({ files: { relativeTo: __dirname } });
server.route({ method: 'GET', path: '/filefn/{file}', handler: { fileTest: filenameFn } });
var server = provisionServer(__dirname);
server.route({ method: 'GET', path: '/filefn/{file}', handler: { file: filenameFn } });
server.inject('/filefn/index.js', function (res) {
server.inject('/filefn/index.js', function (res) {
expect(res.payload).to.contain('./lib');
expect(res.headers['content-type']).to.equal('application/javascript; charset=utf-8');
expect(res.headers['content-length']).to.exist;
done();
expect(res.payload).to.contain('./lib');
expect(res.headers['content-type']).to.equal('application/javascript; charset=utf-8');
expect(res.headers['content-length']).to.exist();
done();
});
});
});
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)', function (done) {
var server = provisionServer({ files: { relativeTo: '.' } });
var relativeHandler = function (request, reply) {
var server = provisionServer('.');
var relativeHandler = function (request, reply) {
reply(Inert.file.response('./package.json', null, request));
};
reply.file('./package.json');
};
server.route({ method: 'GET', path: '/relativefile', handler: relativeHandler });
server.route({ method: 'GET', path: '/relativefile', handler: relativeHandler });
server.inject('/relativefile', function (res) {
server.inject('/relativefile', function (res) {
expect(res.payload).to.contain('hapi');
expect(res.headers['content-type']).to.equal('application/json; charset=utf-8');
expect(res.headers['content-length']).to.exist;
done();
expect(res.payload).to.contain('hapi');
expect(res.headers['content-type']).to.equal('application/json; charset=utf-8');
expect(res.headers['content-length']).to.exist();
done();
});
});
});
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)', function (done) {
var server = provisionServer({ files: { relativeTo: __dirname } });
server.route({ method: 'GET', path: '/relativestaticfile', handler: { fileTest: '../package.json' } });
var server = provisionServer(__dirname);
server.route({ method: 'GET', path: '/relativestaticfile', handler: { file: '../package.json' } });
server.inject('/relativestaticfile', function (res) {
server.inject('/relativestaticfile', function (res) {
expect(res.payload).to.contain('hapi');
expect(res.headers['content-type']).to.equal('application/json; charset=utf-8');
expect(res.headers['content-length']).to.exist;
done();
expect(res.payload).to.contain('hapi');
expect(res.headers['content-type']).to.equal('application/json; charset=utf-8');
expect(res.headers['content-length']).to.exist();
done();
});
});
});
it('returns a file with default mime type', function (done) {
it('returns a file with default mime type', function (done) {
var server = provisionServer();
server.route({ method: 'GET', path: '/', handler: { fileTest: __dirname + '/../Makefile' } });
var server = provisionServer();
server.route({ method: 'GET', path: '/', handler: { file: Path.join(__dirname, '..', 'Makefile') } });
server.inject('/', function (res) {
server.inject('/', function (res) {
expect(res.headers['content-type']).to.equal('application/octet-stream');
done();
expect(res.headers['content-type']).to.equal('application/octet-stream');
done();
});
});
});
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', function (done) {
var server = provisionServer({ files: { relativeTo: __dirname } });
var handler = function (request, reply) {
var server = provisionServer(__dirname);
var handler = function (request, reply) {
reply(Inert.file.response('../Makefile', null, request)).type('application/example');
};
reply.file('../Makefile').type('application/example');
};
server.route({ method: 'GET', path: '/file', handler: handler });
server.route({ method: 'GET', path: '/file', handler: handler });
server.inject('/file', function (res) {
server.inject('/file', function (res) {
expect(res.headers['content-type']).to.equal('application/example');
done();
expect(res.headers['content-type']).to.equal('application/example');
done();
});
});
});
it('does not cache etags', function (done) {
it('does not cache etags', function (done) {
var server = provisionServer({ files: { relativeTo: __dirname, etagsCacheMaxSize: 0 } });
server.route({ method: 'GET', path: '/note', handler: { fileTest: './file/note.txt' } });
var server = provisionServer(__dirname, 0);
server.route({ method: 'GET', path: '/note', handler: { file: './file/note.txt' } });
server.inject('/note', function (res) {
expect(res.statusCode).to.equal(200);
expect(res.result).to.equal('Test');
expect(res.headers.etag).to.not.exist;
server.inject('/note', function (res) {

@@ -383,113 +380,120 @@

expect(res.result).to.equal('Test');
expect(res.headers.etag).to.not.exist;
done();
expect(res.headers.etag).to.not.exist();
server.inject('/note', function (res) {
expect(res.statusCode).to.equal(200);
expect(res.result).to.equal('Test');
expect(res.headers.etag).to.not.exist();
done();
});
});
});
});
it('invalidates etags when file changes', function (done) {
it('invalidates etags when file changes', function (done) {
var server = provisionServer({ files: { relativeTo: __dirname } });
var server = provisionServer(__dirname);
server.route({ method: 'GET', path: '/note', handler: { fileTest: './file/note.txt' } });
server.route({ method: 'GET', path: '/note', handler: { file: './file/note.txt' } });
// No etag, never requested
// No etag, never requested
server.inject('/note', function (res1) {
server.inject('/note', function (res1) {
expect(res1.statusCode).to.equal(200);
expect(res1.result).to.equal('Test');
expect(res1.headers.etag).to.not.exist;
expect(res1.statusCode).to.equal(200);
expect(res1.result).to.equal('Test');
expect(res1.headers.etag).to.not.exist();
// No etag, previously requested
// No etag, previously requested
server.inject('/note', function (res2) {
server.inject('/note', function (res2) {
expect(res2.statusCode).to.equal(200);
expect(res2.result).to.equal('Test');
expect(res2.headers.etag).to.exist;
expect(res2.statusCode).to.equal(200);
expect(res2.result).to.equal('Test');
expect(res2.headers.etag).to.exist();
var etag1 = res2.headers.etag;
var etag1 = res2.headers.etag;
expect(etag1.slice(0, 1)).to.equal('"');
expect(etag1.slice(-1)).to.equal('"');
expect(etag1.slice(0, 1)).to.equal('"');
expect(etag1.slice(-1)).to.equal('"');
// etag
// etag
server.inject({ url: '/note', headers: { 'if-none-match': etag1 } }, function (res3) {
server.inject({ url: '/note', headers: { 'if-none-match': etag1 } }, function (res3) {
expect(res3.statusCode).to.equal(304);
expect(res3.headers).to.not.have.property('content-length');
expect(res3.headers).to.not.have.property('etag');
expect(res3.headers).to.not.have.property('last-modified');
expect(res3.statusCode).to.equal(304);
expect(res3.headers).to.not.include('content-length');
expect(res3.headers).to.include('etag');
expect(res3.headers).to.include('last-modified');
var fd = Fs.openSync(__dirname + '/file/note.txt', 'w');
Fs.writeSync(fd, new Buffer('Test'), 0, 4);
Fs.closeSync(fd);
var fd = Fs.openSync(Path.join(__dirname, 'file', 'note.txt'), 'w');
Fs.writeSync(fd, new Buffer('Test'), 0, 4);
Fs.closeSync(fd);
// etag after file modified, content unchanged
// etag after file modified, content unchanged
server.inject({ url: '/note', headers: { 'if-none-match': etag1 } }, function (res4) {
server.inject({ url: '/note', headers: { 'if-none-match': etag1 } }, function (res4) {
expect(res4.statusCode).to.equal(200);
expect(res4.result).to.equal('Test');
expect(res4.headers.etag).to.not.exist;
expect(res4.statusCode).to.equal(200);
expect(res4.result).to.equal('Test');
expect(res4.headers.etag).to.not.exist();
// No etag, previously requested
// No etag, previously requested
server.inject({ url: '/note' }, function (res5) {
server.inject({ url: '/note' }, function (res5) {
expect(res5.statusCode).to.equal(200);
expect(res5.result).to.equal('Test');
expect(res5.headers.etag).to.exist;
expect(res5.statusCode).to.equal(200);
expect(res5.result).to.equal('Test');
expect(res5.headers.etag).to.exist();
var etag2 = res5.headers.etag;
expect(etag1).to.equal(etag2);
var etag2 = res5.headers.etag;
expect(etag1).to.equal(etag2);
var fd = Fs.openSync(__dirname + '/file/note.txt', 'w');
Fs.writeSync(fd, new Buffer('Test1'), 0, 5);
Fs.closeSync(fd);
var fd = Fs.openSync(Path.join(__dirname, 'file', 'note.txt'), 'w');
Fs.writeSync(fd, new Buffer('Test1'), 0, 5);
Fs.closeSync(fd);
// etag after file modified, content changed
// etag after file modified, content changed
server.inject({ url: '/note', headers: { 'if-none-match': etag2 } }, function (res6) {
server.inject({ url: '/note', headers: { 'if-none-match': etag2 } }, function (res6) {
expect(res6.statusCode).to.equal(200);
expect(res6.result).to.equal('Test1');
expect(res6.headers.etag).to.not.exist;
expect(res6.statusCode).to.equal(200);
expect(res6.result).to.equal('Test1');
expect(res6.headers.etag).to.not.exist();
// No etag, previously requested
// No etag, previously requested
server.inject('/note', function (res7) {
server.inject('/note', function (res7) {
expect(res7.statusCode).to.equal(200);
expect(res7.result).to.equal('Test1');
expect(res7.headers.etag).to.exist;
expect(res7.statusCode).to.equal(200);
expect(res7.result).to.equal('Test1');
expect(res7.headers.etag).to.exist();
var etag3 = res7.headers.etag;
expect(etag1).to.not.equal(etag3);
var etag3 = res7.headers.etag;
expect(etag1).to.not.equal(etag3);
var fd = Fs.openSync(__dirname + '/file/note.txt', 'w');
Fs.writeSync(fd, new Buffer('Test'), 0, 4);
Fs.closeSync(fd);
var fd = Fs.openSync(Path.join(__dirname, 'file', 'note.txt'), 'w');
Fs.writeSync(fd, new Buffer('Test'), 0, 4);
Fs.closeSync(fd);
// No etag, content restored
// No etag, content restored
server.inject('/note', function (res8) {
server.inject('/note', function (res8) {
expect(res8.statusCode).to.equal(200);
expect(res8.result).to.equal('Test');
expect(res8.statusCode).to.equal(200);
expect(res8.result).to.equal('Test');
// No etag, previously requested
// No etag, previously requested
server.inject('/note', function (res9) {
server.inject('/note', function (res9) {
expect(res9.statusCode).to.equal(200);
expect(res9.result).to.equal('Test');
expect(res9.headers.etag).to.exist;
expect(res9.statusCode).to.equal(200);
expect(res9.result).to.equal('Test');
expect(res9.headers.etag).to.exist();
var etag4 = res9.headers.etag;
expect(etag1).to.equal(etag4);
var etag4 = res9.headers.etag;
expect(etag1).to.equal(etag4);
done();
done();
});
});

@@ -504,360 +508,461 @@ });

});
});
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)', function (done) {
var server = provisionServer();
server.route({ method: 'GET', path: '/file', handler: { fileTest: __dirname + '/../package.json' } });
var server = provisionServer();
server.route({ method: 'GET', path: '/file', handler: { file: Path.join(__dirname, '..', 'package.json') } });
server.inject('/file', function (res1) {
server.inject('/file', function (res1) {
var last = new Date(Date.parse(res1.headers['last-modified']) + 1000);
server.inject({ url: '/file', headers: { 'if-modified-since': last.toUTCString() } }, function (res2) {
var last = new Date(Date.parse(res1.headers['last-modified']) + 1000);
server.inject({ url: '/file', headers: { 'if-modified-since': last.toUTCString() } }, function (res2) {
expect(res2.statusCode).to.equal(304);
expect(res2.headers).to.not.have.property('content-length');
expect(res2.headers).to.not.have.property('etag');
expect(res2.headers).to.not.have.property('last-modified');
done();
expect(res2.statusCode).to.equal(304);
expect(res2.headers).to.not.include('content-length');
expect(res2.headers).to.include('etag');
expect(res2.headers).to.include('last-modified');
done();
});
});
});
});
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)', function (done) {
var server = provisionServer();
server.route({ method: 'GET', path: '/file', handler: { fileTest: __dirname + '/../package.json' } });
var server = provisionServer();
server.route({ method: 'GET', path: '/file', handler: { file: Path.join(__dirname, '..', 'package.json') } });
server.inject('/file', function (res1) {
server.inject('/file', function (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'] } }, function (res2) {
expect(res2.statusCode).to.equal(304);
expect(res2.headers).to.not.have.property('content-length');
expect(res2.headers).to.not.have.property('etag');
expect(res2.headers).to.not.have.property('last-modified');
done();
expect(res2.statusCode).to.equal(304);
expect(res2.headers).to.not.include('content-length');
expect(res2.headers).to.include('etag');
expect(res2.headers).to.include('last-modified');
done();
});
});
});
});
it('retains etag header on head', function (done) {
it('retains etag header on head', function (done) {
var server = provisionServer();
server.route({ method: 'GET', path: '/file', handler: { fileTest: __dirname + '/../package.json' } });
var server = provisionServer();
server.route({ method: 'GET', path: '/file', handler: { file: Path.join(__dirname, '..', 'package.json') } });
server.inject('/file', function (res1) {
server.inject('/file', function (res1) {
server.inject({ method: 'HEAD', url: '/file' }, function (res2) {
server.inject({ method: 'HEAD', url: '/file' }, function (res2) {
expect(res2.statusCode).to.equal(200);
expect(res2.headers).to.have.property('etag');
expect(res2.headers).to.have.property('last-modified');
done();
expect(res2.statusCode).to.equal(200);
expect(res2.headers).to.include('etag');
expect(res2.headers).to.include('last-modified');
done();
});
});
});
});
it('changes etag when content encoding is used', function (done) {
it('changes etag when content encoding is used', function (done) {
var server = provisionServer();
server.route({ method: 'GET', path: '/file', handler: { fileTest: __dirname + '/../package.json' } });
var server = provisionServer();
server.route({ method: 'GET', path: '/file', handler: { file: Path.join(__dirname, '..', 'package.json') } });
server.inject('/file', function (res1) {
server.inject('/file', function (res1) {
server.inject('/file', function (res2) {
server.inject('/file', function (res2) {
expect(res2.statusCode).to.equal(200);
expect(res2.headers).to.have.property('etag');
expect(res2.headers).to.have.property('last-modified');
expect(res2.statusCode).to.equal(200);
expect(res2.headers).to.include('etag');
expect(res2.headers).to.include('last-modified');
server.inject({ url: '/file', headers: { 'accept-encoding': 'gzip' } }, function (res3) {
server.inject({ url: '/file', headers: { 'accept-encoding': 'gzip' } }, function (res3) {
expect(res3.statusCode).to.equal(200);
expect(res3.headers.vary).to.equal('accept-encoding');
expect(res3.headers.etag).to.not.equal(res2.headers.etag);
expect(res3.headers.etag).to.contain(res2.headers.etag.slice(0, -1) + '-');
expect(res3.headers['last-modified']).to.equal(res2.headers['last-modified']);
done();
expect(res3.statusCode).to.equal(200);
expect(res3.headers.vary).to.equal('accept-encoding');
expect(res3.headers.etag).to.not.equal(res2.headers.etag);
expect(res3.headers.etag).to.contain(res2.headers.etag.slice(0, -1) + '-');
expect(res3.headers['last-modified']).to.equal(res2.headers['last-modified']);
done();
});
});
});
});
});
it('returns valid http date responses in last-modified header', function (done) {
it('returns valid http date responses in last-modified header', function (done) {
var server = provisionServer();
server.route({ method: 'GET', path: '/file', handler: { fileTest: __dirname + '/../package.json' } });
var server = provisionServer();
server.route({ method: 'GET', path: '/file', handler: { file: Path.join(__dirname, '..', 'package.json') } });
server.inject('/file', function (res) {
server.inject('/file', function (res) {
expect(res.statusCode).to.equal(200);
expect(res.headers['last-modified']).to.equal(Fs.statSync(__dirname + '/../package.json').mtime.toUTCString());
done();
expect(res.statusCode).to.equal(200);
expect(res.headers['last-modified']).to.equal(Fs.statSync(Path.join(__dirname, '..', 'package.json')).mtime.toUTCString());
done();
});
});
});
it('returns 200 if if-modified-since is invalid', function (done) {
it('returns 200 if if-modified-since is invalid', function (done) {
var server = provisionServer();
server.route({ method: 'GET', path: '/file', handler: { fileTest: __dirname + '/../package.json' } });
var 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' } }, function (res) {
expect(res.statusCode).to.equal(200);
done();
expect(res.statusCode).to.equal(200);
done();
});
});
});
it('returns 200 if last-modified is invalid', function (done) {
it('returns 200 if last-modified is invalid', function (done) {
var server = provisionServer();
server.route({ method: 'GET', path: '/', handler: function (request, reply) { reply('ok').header('last-modified', 'some crap'); } });
var server = provisionServer();
server.route({ method: 'GET', path: '/', handler: function (request, reply) { 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' } }, function (res2) {
expect(res2.statusCode).to.equal(200);
done();
expect(res2.statusCode).to.equal(200);
done();
});
});
});
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' }, function (done) {
var server = provisionServer();
server.route({ method: 'GET', path: '/file', handler: { fileTest: __dirname + '/../package.json' } });
var server = provisionServer();
server.route({ method: 'GET', path: '/file', handler: { file: Path.join(__dirname, '..', 'package.json') } });
server.inject('/file', function (res1) {
server.inject('/file', function (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 } }, function (res2) {
expect(res2.statusCode).to.equal(304);
var cmd = ChildProcess.spawn('lsof', ['-p', process.pid]);
var lsof = '';
cmd.stdout.on('data', function (buffer) {
expect(res2.statusCode).to.equal(304);
var cmd = ChildProcess.spawn('lsof', ['-p', process.pid]);
var lsof = '';
cmd.stdout.on('data', function (buffer) {
lsof += buffer.toString();
});
lsof += buffer.toString();
});
cmd.stdout.on('end', function () {
cmd.stdout.on('end', function () {
var count = 0;
var lines = lsof.split('\n');
for (var i = 0, il = lines.length; i < il; ++i) {
count += !!lines[i].match(/package.json/);
}
var count = 0;
var lines = lsof.split('\n');
for (var i = 0, il = lines.length; i < il; ++i) {
count += !!lines[i].match(/package.json/);
}
expect(count).to.equal(0);
done();
expect(count).to.equal(0);
done();
});
cmd.stdin.end();
});
cmd.stdin.end();
});
});
});
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' }, function (done) {
var server = provisionServer();
server.route({ method: 'GET', path: '/file', handler: function (request, reply) { reply(Fs.createReadStream(__dirname + '/../package.json')).header('etag', 'abc'); } });
var server = provisionServer();
server.route({ method: 'GET', path: '/file', handler: function (request, reply) { reply(Fs.createReadStream(Path.join(__dirname, '..', 'package.json'))).header('etag', 'abc'); } });
server.inject('/file', function (res1) {
server.inject('/file', function (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 } }, function (res2) {
expect(res2.statusCode).to.equal(304);
var cmd = ChildProcess.spawn('lsof', ['-p', process.pid]);
var lsof = '';
cmd.stdout.on('data', function (buffer) {
expect(res2.statusCode).to.equal(304);
var cmd = ChildProcess.spawn('lsof', ['-p', process.pid]);
var lsof = '';
cmd.stdout.on('data', function (buffer) {
lsof += buffer.toString();
});
lsof += buffer.toString();
});
cmd.stdout.on('end', function () {
cmd.stdout.on('end', function () {
var count = 0;
var lines = lsof.split('\n');
for (var i = 0, il = lines.length; i < il; ++i) {
count += !!lines[i].match(/package.json/);
}
var count = 0;
var lines = lsof.split('\n');
for (var i = 0, il = lines.length; i < il; ++i) {
count += !!lines[i].match(/package.json/);
}
expect(count).to.equal(0);
done();
expect(count).to.equal(0);
done();
});
cmd.stdin.end();
});
cmd.stdin.end();
});
});
});
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', function (done) {
var server = provisionServer({ files: { relativeTo: __dirname } });
var handler = function (request, reply) {
var server = provisionServer(__dirname);
var handler = function (request, reply) {
reply(Inert.file.response(__dirname + '/../package.json', null, request));
};
reply.file(Path.join(__dirname, '..', 'package.json'));
};
server.route({ method: 'GET', path: '/file', handler: handler });
server.route({ method: 'GET', path: '/file', handler: handler });
server.inject({ url: '/file', headers: { 'accept-encoding': 'gzip' } }, function (res) {
server.inject({ url: '/file', headers: { 'accept-encoding': 'gzip' } }, function (res) {
expect(res.headers['content-type']).to.equal('application/json; charset=utf-8');
expect(res.headers['content-encoding']).to.equal('gzip');
expect(res.headers['content-length']).to.not.exist;
expect(res.payload).to.exist;
done();
expect(res.headers['content-type']).to.equal('application/json; charset=utf-8');
expect(res.headers['content-encoding']).to.equal('gzip');
expect(res.headers['content-length']).to.not.exist();
expect(res.payload).to.exist();
done();
});
});
});
it('returns a plain file when not compressible', function (done) {
it('returns a plain file when not compressible', function (done) {
var server = provisionServer({ files: { relativeTo: __dirname } });
var handler = function (request, reply) {
var server = provisionServer(__dirname);
var handler = function (request, reply) {
reply(Inert.file.response(__dirname + '/file/image.png', null, request));
};
reply.file(Path.join(__dirname, 'file', 'image.png'));
};
server.route({ method: 'GET', path: '/file', handler: handler });
server.route({ method: 'GET', path: '/file', handler: handler });
server.inject({ url: '/file', headers: { 'accept-encoding': 'gzip' } }, function (res) {
server.inject({ url: '/file', headers: { 'accept-encoding': 'gzip' } }, function (res) {
expect(res.headers['content-type']).to.equal('image/png');
expect(res.headers['content-encoding']).to.not.exist;
expect(res.headers['content-length']).to.equal(42010);
expect(res.payload).to.exist;
done();
expect(res.headers['content-type']).to.equal('image/png');
expect(res.headers['content-encoding']).to.not.exist();
expect(res.headers['content-length']).to.equal(42010);
expect(res.payload).to.exist();
done();
});
});
});
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', function (done) {
var server = provisionServer({ files: { relativeTo: __dirname } });
var handler = function (request, reply) {
var server = provisionServer(__dirname);
var handler = function (request, reply) {
reply(Inert.file.response(__dirname + '/../package.json', null, request));
};
reply.file(Path.join(__dirname, '..', 'package.json'));
};
server.route({ method: 'GET', path: '/file', handler: handler });
server.route({ method: 'GET', path: '/file', handler: handler });
server.inject({ url: '/file', headers: { 'accept-encoding': 'deflate' } }, function (res) {
server.inject({ url: '/file', headers: { 'accept-encoding': 'deflate' } }, function (res) {
expect(res.headers['content-type']).to.equal('application/json; charset=utf-8');
expect(res.headers['content-encoding']).to.equal('deflate');
expect(res.headers['content-length']).to.not.exist;
expect(res.payload).to.exist;
done();
expect(res.headers['content-type']).to.equal('application/json; charset=utf-8');
expect(res.headers['content-encoding']).to.equal('deflate');
expect(res.headers['content-length']).to.not.exist();
expect(res.payload).to.exist();
done();
});
});
});
it('returns a gzipped file using precompressed file', function (done) {
it('returns a gzipped file using precompressed file', function (done) {
var content = Fs.readFileSync('./test/file/image.png.gz');
var content = Fs.readFileSync('./test/file/image.png.gz');
var server = provisionServer();
server.route({ method: 'GET', path: '/file', handler: { fileTest: { path: './test/file/image.png', lookupCompressed: true } } });
var 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' } }, function (res) {
expect(res.headers['content-type']).to.equal('image/png');
expect(res.headers['content-encoding']).to.equal('gzip');
expect(res.headers['content-length']).to.equal(content.length);
expect(res.payload.length).to.equal(content.length);
done();
expect(res.headers['content-type']).to.equal('image/png');
expect(res.headers['content-encoding']).to.equal('gzip');
expect(res.headers['content-length']).to.equal(content.length);
expect(res.payload.length).to.equal(content.length);
done();
});
});
});
it('returns a gzipped file when precompressed file not found', function (done) {
it('returns a gzipped file when precompressed file not found', function (done) {
var server = provisionServer();
server.route({ method: 'GET', path: '/file', handler: { fileTest: { path: './test/file/note.txt', lookupCompressed: true } } });
var 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' } }, function (res) {
expect(res.headers['content-encoding']).to.equal('gzip');
expect(res.headers['content-length']).to.not.exist;
expect(res.payload).to.exist;
done();
expect(res.headers['content-encoding']).to.equal('gzip');
expect(res.headers['content-length']).to.not.exist();
expect(res.payload).to.exist();
done();
});
});
});
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', function (done) {
var server = provisionServer();
server.route({ method: 'GET', path: '/file', handler: { fileTest: { path: './test/file/image.png', lookupCompressed: true } } });
var 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', function (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' } }, function (res2) {
expect(res2.statusCode).to.equal(304);
expect(res2.statusCode).to.equal(304);
done();
});
});
});
it('ignores precompressed file when content-encoding not requested', function (done) {
var server = provisionServer();
server.route({ method: 'GET', path: '/file', handler: { file: { path: './test/file/image.png', lookupCompressed: true } } });
server.inject('/file', function (res) {
expect(res.headers['content-type']).to.equal('image/png');
expect(res.payload).to.exist();
done();
});
});
});
it('ignores precompressed file when content-encoding not requested', function (done) {
it('does not throw an error when adding a route with a parameter and function path', function (done) {
var server = provisionServer();
server.route({ method: 'GET', path: '/file', handler: { fileTest: { path: './test/file/image.png', lookupCompressed: true } } });
var fn = function () {
server.inject('/file', function (res) {
var server = provisionServer();
server.route({ method: 'GET', path: '/fileparam/{path}', handler: { file: function () { } } });
server.route({ method: 'GET', path: '/filepathparam/{path}', handler: { file: { path: function () { } } } });
};
expect(res.headers['content-type']).to.equal('image/png');
expect(res.payload).to.exist;
expect(fn).to.not.throw();
done();
});
});
it('does not throw an error when adding a route with a parameter and function path', function (done) {
it('responds correctly when file is removed while processing', function (done) {
var fn = function () {
var filename = Hoek.uniqueFilename(Os.tmpDir()) + '.package.json';
Fs.writeFileSync(filename, 'data');
var server = provisionServer(0, { files: { relativeTo: __dirname } });
server.route({ method: 'GET', path: '/fileparam/{path}', handler: { fileTest: function () { } } });
};
var server = provisionServer();
server.route({ method: 'GET', path: '/', handler: { file: filename } });
server.ext('onPreResponse', function (request, reply) {
expect(fn).to.not.throw(Error);
done();
});
Fs.unlinkSync(filename);
return reply.continue();
});
it('returns error when file is removed before stream is opened', function (done) {
server.inject('/', function (res) {
var filename = Hoek.uniqueFilename(Os.tmpDir());
Fs.writeFileSync(filename, 'data');
expect(res.statusCode).to.equal(200);
done();
});
});
var server = provisionServer();
server.route({ method: 'GET', path: '/', handler: { fileTest: filename } });
server.ext('onPreResponse', function (request, reply) {
it('responds correctly when file is changed while processing', function (done) {
Fs.unlinkSync(filename);
reply();
var 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) {
var tempfile = filename + '~';
if (process.platform === 'win32') {
// workaround to replace open file without a permission error
Fs.renameSync(filename, tempfile);
Fs.writeFileSync(filename, 'database');
Fs.unlinkSync(tempfile);
} else {
// atomic file replace
Fs.writeFileSync(tempfile, 'database');
Fs.renameSync(tempfile, filename);
}
return reply.continue();
});
server.inject('/', function (res) {
Fs.unlinkSync(filename);
expect(res.statusCode).to.equal(200);
expect(res.headers['content-length']).to.equal(4);
expect(res.payload).to.equal('data');
done();
});
});
server.inject('/', function (res) {
it('does not open file stream on 304', function (done) {
expect(res.statusCode).to.equal(500);
done();
var server = provisionServer();
server.route({ method: 'GET', path: '/file', handler: { file: Path.join(__dirname, '..', 'package.json') } });
server.inject('/file', function (res1) {
server.ext('onPreResponse', function (request, reply) {
request.response._marshall = function () { throw new Error('not called'); };
return reply.continue();
});
server.inject({ url: '/file', headers: { 'if-modified-since': res1.headers.date } }, function (res2) {
expect(res2.statusCode).to.equal(304);
done();
});
});
});
});
it('does not open file stream on 304', function (done) {
it('returns error when aborted while processing', function (done) {
var server = provisionServer();
server.route({ method: 'GET', path: '/file', handler: { fileTest: __dirname + '/../package.json' } });
var filename = Hoek.uniqueFilename(Os.tmpDir()) + '.package.json';
Fs.writeFileSync(filename, 'data');
server.inject('/file', function (res1) {
var server = provisionServer();
server.route({ method: 'GET', path: '/', handler: { file: filename } });
server.ext('onPreResponse', function (request, reply) {
reply(Boom.internal('crapping out'));
});
request.response._marshall = function () { throw new Error('not called'); };
reply();
server.inject('/', function (res) {
expect(res.statusCode).to.equal(500);
done();
});
});
server.inject({ url: '/file', headers: { 'if-modified-since': res1.headers.date } }, function (res2) {
it('returns error when stat fails unexpectedly', function (done) {
expect(res2.statusCode).to.equal(304);
var filename = Hoek.uniqueFilename(Os.tmpDir()) + '.package.json';
Fs.writeFileSync(filename, 'data');
var orig = Fs.fstat;
Fs.fstat = function (fd, callback) { // can return EIO error
Fs.fstat = orig;
callback(new Error('failed'));
};
var server = provisionServer();
server.route({ method: 'GET', path: '/', handler: { file: filename } });
server.inject('/', function (res) {
expect(res.statusCode).to.equal(500);
done();
});
});
it('has not leaked file descriptors', { skip: process.platform === 'win32' }, function (done) {
// validate that all descriptors has been closed
var cmd = ChildProcess.spawn('lsof', ['-p', process.pid]);
var lsof = '';
cmd.stdout.on('data', function (buffer) {
lsof += buffer.toString();
});
cmd.stdout.on('end', function () {
var count = 0;
var lines = lsof.split('\n');
for (var i = 0, il = lines.length; i < il; ++i) {
count += !!lines[i].match(/package.json/);
}
expect(count).to.equal(0);
done();
});
cmd.stdin.end();
});
});
});

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