From c2c76f147f590db46ba061783f36e5dc158ff49e Mon Sep 17 00:00:00 2001 From: Jacek Kopecky Date: Tue, 29 Jul 2014 12:10:39 +0100 Subject: [PATCH] Add extensions option closes #51 --- History.md | 5 +++ Readme.md | 6 ++++ lib/send.js | 60 +++++++++++++++++++++++++++------ test/fixtures/name.dir/name.txt | 1 + test/fixtures/name.html | 1 + test/send.js | 50 +++++++++++++++++++++++++++ 6 files changed, 112 insertions(+), 11 deletions(-) create mode 100644 test/fixtures/name.dir/name.txt create mode 100644 test/fixtures/name.html diff --git a/History.md b/History.md index bd3145c..e43e8a8 100644 --- a/History.md +++ b/History.md @@ -1,3 +1,8 @@ +unreleased +========== + + * Add `extensions` option + 0.7.4 / 2014-08-04 ================== diff --git a/Readme.md b/Readme.md index 5eceeef..3ada3b1 100644 --- a/Readme.md +++ b/Readme.md @@ -84,6 +84,12 @@ var app = http.createServer(function(req, res){ - `'deny'` Send a 403 for any request for a dotfile. - `'ignore'` Pretend like the dotfile does not exist and 404. +#### extensions + + If a given file doesn't exist, try appending one of the given extensions, + in the given order. By default, this is disabled (set to `false`). An + example value that will serve extension-less HTML files: `['html', 'htm']`. + #### index By default send supports "index.html" files, to disable this diff --git a/lib/send.js b/lib/send.js index 806df2e..cc09021 100644 --- a/lib/send.js +++ b/lib/send.js @@ -101,8 +101,12 @@ function SendStream(req, path, options) { this._dotfiles = undefined } + this._extensions = options.extensions !== undefined + ? normalizeList(options.extensions) + : [] + this._index = options.index !== undefined - ? normalizeIndex(options.index) + ? normalizeList(options.index) : ['index.html'] this._maxage = options.maxAge || options.maxage @@ -169,7 +173,7 @@ SendStream.prototype.hidden = deprecate.function(function hidden(val) { */ SendStream.prototype.index = deprecate.function(function index(paths) { - var index = !paths ? [] : normalizeIndex(paths); + var index = !paths ? [] : normalizeList(paths); debug('index %o', paths); this._index = index; return this; @@ -459,14 +463,7 @@ SendStream.prototype.pipe = function(res){ return res; } - debug('stat "%s"', path); - fs.stat(path, function(err, stat){ - if (err) return self.onStatError(err); - if (stat.isDirectory()) return self.redirect(self.path); - self.emit('file', path, stat); - self.send(path, stat); - }); - + this.sendFile(path); return res; }; @@ -557,6 +554,47 @@ SendStream.prototype.send = function(path, stat){ this.stream(path, options); }; +/** + * Transfer file for `path`. + * + * @param {String} path + * @api private + */ +SendStream.prototype.sendFile = function sendFile(path) { + var i = 0 + var self = this + + debug('stat "%s"', path); + fs.stat(path, function onstat(err, stat) { + if (err && err.code === 'ENOENT' && path[path.length - 1] !== sep) { + // not found, check extensions + return next(err) + } + if (err) return self.onStatError(err) + if (stat.isDirectory()) return self.redirect(self.path) + self.emit('file', path, stat) + self.send(path, stat) + }) + + function next(err) { + if (self._extensions.length <= i) { + return err + ? self.onStatError(err) + : self.error(404) + } + + var p = path + '.' + self._extensions[i++] + + debug('stat "%s"', p) + fs.stat(p, function (err, stat) { + if (err) return next(err) + if (stat.isDirectory()) return next() + self.emit('file', p, stat) + self.send(p, stat) + }) + } +} + /** * Transfer index for `path`. * @@ -705,6 +743,6 @@ function containsDotFile(parts) { * @api private */ -function normalizeIndex(val){ +function normalizeList(val){ return [].concat(val || []) } diff --git a/test/fixtures/name.dir/name.txt b/test/fixtures/name.dir/name.txt new file mode 100644 index 0000000..fa66f37 --- /dev/null +++ b/test/fixtures/name.dir/name.txt @@ -0,0 +1 @@ +tobi \ No newline at end of file diff --git a/test/fixtures/name.html b/test/fixtures/name.html new file mode 100644 index 0000000..52b1923 --- /dev/null +++ b/test/fixtures/name.html @@ -0,0 +1 @@ +

tobi

\ No newline at end of file diff --git a/test/send.js b/test/send.js index 97cfe13..478dbe4 100644 --- a/test/send.js +++ b/test/send.js @@ -719,6 +719,56 @@ describe('send(file, options)', function(){ }) }) + describe('extensions', function () { + it('should be not be enabled by default', function (done) { + var server = createServer({root: fixtures}); + + request(server) + .get('/tobi') + .expect(404, done) + }) + + it('should be configurable', function (done) { + var server = createServer({extensions: 'txt', root: fixtures}) + + request(server) + .get('/name') + .expect(200, 'tobi', done) + }) + + it('should support disabling extensions', function (done) { + var server = createServer({extensions: false, root: fixtures}) + + request(server) + .get('/name') + .expect(404, done) + }) + + it('should support fallbacks', function (done) { + var server = createServer({extensions: ['htm', 'html', 'txt'], root: fixtures}) + + request(server) + .get('/name') + .expect(200, '

tobi

', done) + }) + + it('should 404 if nothing found', function (done) { + var server = createServer({extensions: ['htm', 'html', 'txt'], root: fixtures}) + + request(server) + .get('/bob') + .expect(404, done) + }) + + it('should skip directories', function (done) { + var server = createServer({extensions: ['file', 'dir'], root: fixtures}) + + request(server) + .get('/name') + .expect(404, done) + }) + }) + describe('from', function(){ it('should set with deprecated from', function(done){ var app = http.createServer(function(req, res){