From bdae04d05012d5e984a541a257f016b8d3ec8bc0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dawid=20Gawe=C5=82?= Date: Wed, 1 Oct 2025 09:40:54 +0200 Subject: [PATCH 1/2] check extensions before directory redirects --- index.js | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/index.js b/index.js index 1655053..9d48442 100644 --- a/index.js +++ b/index.js @@ -606,17 +606,27 @@ SendStream.prototype.sendFile = function sendFile (path) { var pathEndsWithSep = path[path.length - 1] === sep if (err && err.code === 'ENOENT' && !extname(path) && !pathEndsWithSep) { // not found, check extensions - return next(err) + return next(err, false) } if (err) return self.onStatError(err) - if (stat.isDirectory()) return self.redirect(path) + if (stat.isDirectory()) { + // if extensions are configured and path has no extension, try extensions first + if (self._extensions.length > 0 && !extname(path) && !pathEndsWithSep) { + return next(null, true) + } + return self.redirect(path) + } if (pathEndsWithSep) return self.error(404) self.emit('file', path, stat) self.send(path, stat) }) - function next (err) { + function next (err, isDir) { if (self._extensions.length <= i) { + // if original path was a directory, redirect to it + if (isDir) { + return self.redirect(path) + } return err ? self.onStatError(err) : self.error(404) @@ -626,8 +636,8 @@ SendStream.prototype.sendFile = function sendFile (path) { debug('stat "%s"', p) fs.stat(p, function (err, stat) { - if (err) return next(err) - if (stat.isDirectory()) return next() + if (err) return next(err, isDir) + if (stat.isDirectory()) return next(null, isDir) self.emit('file', p, stat) self.send(p, stat) }) From ed4673e946068da4169afb69b8097d78c37e1f39 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dawid=20Gawe=C5=82?= Date: Sat, 6 Dec 2025 00:19:12 +0100 Subject: [PATCH 2/2] add tests --- test/fixtures/about.html | 1 + test/fixtures/about/team.html | 1 + test/send.js | 30 ++++++++++++++++++++++++++++++ 3 files changed, 32 insertions(+) create mode 100644 test/fixtures/about.html create mode 100644 test/fixtures/about/team.html diff --git a/test/fixtures/about.html b/test/fixtures/about.html new file mode 100644 index 0000000..a5d3a7f --- /dev/null +++ b/test/fixtures/about.html @@ -0,0 +1 @@ +About Page \ No newline at end of file diff --git a/test/fixtures/about/team.html b/test/fixtures/about/team.html new file mode 100644 index 0000000..0081272 --- /dev/null +++ b/test/fixtures/about/team.html @@ -0,0 +1 @@ +Team Page \ No newline at end of file diff --git a/test/send.js b/test/send.js index b824282..02ac1f3 100644 --- a/test/send.js +++ b/test/send.js @@ -917,6 +917,36 @@ describe('send(file, options)', function () { .get('/thing.html') .expect(404, done) }) + + it('should serve file with extension when no directory exists', function (done) { + // /contact (no directory exists) → serves contact.html + request(createServer({ extensions: ['html'], root: fixtures })) + .get('/tobi') + .expect(200, '

tobi

', done) + }) + + it('should serve file with extension when directory also exists', function (done) { + // /about (directory exists) → serves about.html (not redirect to /about/) + request(createServer({ extensions: ['html'], root: fixtures })) + .get('/about') + .expect(200, 'About Page', done) + }) + + it('should redirect when directory exists and extensions not configured', function (done) { + // When extensions are not configured, should still redirect when directory exists + request(createServer({ root: fixtures })) + .get('/about') + .expect('Location', '/about/') + .expect(301, done) + }) + + it('should redirect to directory when file with extension does not exist', function (done) { + // /about with only about/ directory → redirects to /about/ + request(createServer({ extensions: ['html'], root: fixtures })) + .get('/pets') + .expect('Location', '/pets/') + .expect(301, done) + }) }) describe('lastModified', function () {