Comparing version 1.1.0 to 2.0.0
@@ -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 @@ |
103
lib/file.js
@@ -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(); | ||
}); | ||
}); | ||
}); | ||
}); |
1133
test/file.js
@@ -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
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
346998
5
1460
3
14759
+ Addedjoi@5.1.0(transitive)
- Removedmimos@1.x.x
- Removedjoi@4.9.0(transitive)
- Removedmime-db@1.53.0(transitive)
- Removedmimos@1.0.1(transitive)
Updatedjoi@5.x.x