Skip to content

Commit

Permalink
Fix for files with % in filename (#452)
Browse files Browse the repository at this point in the history
* Add a test file with a percent in the filename

* Fix `href` encoding in directory listing

* Fix for filename with `%` in their filename

* Reinstate `path.join` workaround for Windows
  • Loading branch information
chetbox committed May 7, 2024
1 parent de16de9 commit 0f6af4e
Show file tree
Hide file tree
Showing 5 changed files with 39 additions and 8 deletions.
3 changes: 2 additions & 1 deletion index.js
Expand Up @@ -221,7 +221,8 @@ async function fastifyStatic (fastify, opts) {
}
}

const stream = send(request.raw, pathnameForSend, options)
// `send(..., path, ...)` will URI-decode path so we pass an encoded path here
const stream = send(request.raw, encodeURI(pathnameForSend), options)
let resolvedFilename
stream.on('file', function (file) {
resolvedFilename = file
Expand Down
2 changes: 1 addition & 1 deletion lib/dirList.js
Expand Up @@ -149,7 +149,7 @@ const dirList = {
route = path.normalize(path.join(route, '..'))
}
return {
href: path.join(prefix, route, entry.name).replace(/\\/gu, '/'),
href: encodeURI(path.join(prefix, route, entry.name).replace(/\\/gu, '/')),
name: entry.name,
stats: entry.stats,
extendedInfo: entry.extendedInfo
Expand Down
14 changes: 8 additions & 6 deletions test/dir-list.test.js
Expand Up @@ -154,7 +154,7 @@ t.test('dir list, custom options', t => {
}

const route = '/public/'
const content = { dirs: ['deep', 'shallow'], files: ['.example', 'a .md', 'foo.html', 'foobar.html', 'index.css', 'index.html'] }
const content = { dirs: ['deep', 'shallow'], files: ['.example', '100%.txt', 'a .md', 'foo.html', 'foobar.html', 'index.css', 'index.html'] }

helper.arrange(t, options, (url) => {
t.test(route, t => {
Expand Down Expand Up @@ -205,7 +205,8 @@ t.test('dir list html format', t => {
</ul>
<ul>
<li><a href="/public/.example" target="_blank">.example</a></li>
<li><a href="/public/a .md" target="_blank">a .md</a></li>
<li><a href="/public/100%25.txt" target="_blank">100%.txt</a></li>
<li><a href="/public/a%20.md" target="_blank">a .md</a></li>
<li><a href="/public/foo.html" target="_blank">foo.html</a></li>
<li><a href="/public/foobar.html" target="_blank">foobar.html</a></li>
<li><a href="/public/index.css" target="_blank">index.css</a></li>
Expand Down Expand Up @@ -236,7 +237,8 @@ t.test('dir list html format', t => {
</ul>
<ul>
<li><a href="/public/.example" target="_blank">.example</a></li>
<li><a href="/public/a .md" target="_blank">a .md</a></li>
<li><a href="/public/100%25.txt" target="_blank">100%.txt</a></li>
<li><a href="/public/a%20.md" target="_blank">a .md</a></li>
<li><a href="/public/foo.html" target="_blank">foo.html</a></li>
<li><a href="/public/foobar.html" target="_blank">foobar.html</a></li>
<li><a href="/public/index.css" target="_blank">index.css</a></li>
Expand Down Expand Up @@ -492,7 +494,7 @@ t.test('json format with url parameter format', t => {
}
}
const route = '/public/'
const jsonContent = { dirs: ['deep', 'shallow'], files: ['.example', 'a .md', 'foo.html', 'foobar.html', 'index.css', 'index.html'] }
const jsonContent = { dirs: ['deep', 'shallow'], files: ['.example', '100%.txt', 'a .md', 'foo.html', 'foobar.html', 'index.css', 'index.html'] }

helper.arrange(t, options, (url) => {
simple.concat({
Expand Down Expand Up @@ -539,7 +541,7 @@ t.test('json format with url parameter format and without render option', t => {
}
}
const route = '/public/'
const jsonContent = { dirs: ['deep', 'shallow'], files: ['.example', 'a .md', 'foo.html', 'foobar.html', 'index.css', 'index.html'] }
const jsonContent = { dirs: ['deep', 'shallow'], files: ['.example', '100%.txt', 'a .md', 'foo.html', 'foobar.html', 'index.css', 'index.html'] }

helper.arrange(t, options, (url) => {
simple.concat({
Expand Down Expand Up @@ -588,7 +590,7 @@ t.test('html format with url parameter format', t => {
}
}
const route = '/public/'
const jsonContent = { dirs: ['deep', 'shallow'], files: ['.example', 'a .md', 'foo.html', 'foobar.html', 'index.css', 'index.html'] }
const jsonContent = { dirs: ['deep', 'shallow'], files: ['.example', '100%.txt', 'a .md', 'foo.html', 'foobar.html', 'index.css', 'index.html'] }

helper.arrange(t, options, (url) => {
simple.concat({
Expand Down
27 changes: 27 additions & 0 deletions test/static.test.js
Expand Up @@ -3963,6 +3963,33 @@ t.test(
}
)

t.test(
'serves files with % in the filename',
async (t) => {
t.plan(2)

const txtContent = fs.readFileSync(path.join(__dirname, 'static', '100%.txt'), 'utf-8')

const pluginOptions = {
root: url.pathToFileURL(path.join(__dirname, '/static')),
wildcard: false
}

const fastify = Fastify()

fastify.register(fastifyStatic, pluginOptions)
const response = await fastify.inject({
method: 'GET',
url: '100%25.txt',
headers: {
'accept-encoding': '*, *'
}
})
t.equal(response.statusCode, 200)
t.same(response.body, txtContent)
}
)

t.test('content-length in head route should not return zero when using wildcard', t => {
t.plan(6)

Expand Down
1 change: 1 addition & 0 deletions test/static/100%.txt
@@ -0,0 +1 @@
100%

0 comments on commit 0f6af4e

Please sign in to comment.