Comparing version
@@ -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. | ||
[](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
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
Found 1 instance in 1 package
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
Found 1 instance in 1 package
346998
2.52%5
-16.67%1460
9.77%3
50%+ Added
- Removed
- Removed
- Removed
- Removed
Updated