Skip to content

Commit 2b6e91d

Browse files
add flag to avoid trailing slash in prefix (#123)
1 parent e1ad93b commit 2b6e91d

File tree

5 files changed

+162
-1
lines changed

5 files changed

+162
-1
lines changed

README.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,12 @@ Default: `'/'`
4444

4545
A URL path prefix used to create a virtual mount path for the static directory.
4646

47+
### `prefixAvoidTrailingSlash`
48+
49+
Default: `false`
50+
51+
If set to false prefix will get trailing "/" at the end. If set to true, prefix will not append "/" to prefix.
52+
4753
#### `schemaHide`
4854

4955
Default: `true`

index.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ declare function fastifyStatic(): fastify.Plugin<
2424
{
2525
root: string;
2626
prefix?: string;
27+
prefixAvoidTrailingSlash?: boolean;
2728
serve?: boolean;
2829
decorateReply?: boolean;
2930
schemaHide?: boolean;

index.js

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,12 @@ function fastifyStatic (fastify, opts, next) {
107107
}
108108

109109
if (opts.prefix === undefined) opts.prefix = '/'
110-
const prefix = opts.prefix[opts.prefix.length - 1] === '/' ? opts.prefix : (opts.prefix + '/')
110+
111+
let prefix = opts.prefix
112+
113+
if (!opts.prefixAvoidTrailingSlash) {
114+
prefix = opts.prefix[opts.prefix.length - 1] === '/' ? opts.prefix : (opts.prefix + '/')
115+
}
111116
// Set the schema hide property if defined in opts or true by default
112117
const schema = { schema: { hide: typeof opts.schemaHide !== 'undefined' ? opts.schemaHide : true } }
113118

test/static.test.js

Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,154 @@ function genericErrorResponseChecks (t, response) {
3333
t.ok(response.headers.date)
3434
}
3535

36+
t.test('register /static prefixAvoidTrailingSlash', t => {
37+
t.plan(11)
38+
39+
const pluginOptions = {
40+
root: path.join(__dirname, '/static'),
41+
prefix: '/static',
42+
prefixAvoidTrailingSlash: true
43+
}
44+
const fastify = Fastify()
45+
fastify.register(fastifyStatic, pluginOptions)
46+
47+
t.tearDown(fastify.close.bind(fastify))
48+
49+
fastify.listen(0, err => {
50+
t.error(err)
51+
52+
fastify.server.unref()
53+
54+
t.test('/static/index.html', t => {
55+
t.plan(3 + GENERIC_RESPONSE_CHECK_COUNT)
56+
simple.concat({
57+
method: 'GET',
58+
url: 'http://localhost:' + fastify.server.address().port + '/static/index.html'
59+
}, (err, response, body) => {
60+
t.error(err)
61+
t.strictEqual(response.statusCode, 200)
62+
t.strictEqual(body.toString(), indexContent)
63+
genericResponseChecks(t, response)
64+
})
65+
})
66+
67+
t.test('/static/index.css', t => {
68+
t.plan(2 + GENERIC_RESPONSE_CHECK_COUNT)
69+
simple.concat({
70+
method: 'GET',
71+
url: 'http://localhost:' + fastify.server.address().port + '/static/index.css'
72+
}, (err, response, body) => {
73+
t.error(err)
74+
t.strictEqual(response.statusCode, 200)
75+
genericResponseChecks(t, response)
76+
})
77+
})
78+
79+
t.test('/static/', t => {
80+
t.plan(3 + GENERIC_RESPONSE_CHECK_COUNT)
81+
simple.concat({
82+
method: 'GET',
83+
url: 'http://localhost:' + fastify.server.address().port + '/static/'
84+
}, (err, response, body) => {
85+
t.error(err)
86+
t.strictEqual(response.statusCode, 200)
87+
t.strictEqual(body.toString(), indexContent)
88+
genericResponseChecks(t, response)
89+
})
90+
})
91+
92+
t.test('/static', t => {
93+
t.plan(3 + GENERIC_RESPONSE_CHECK_COUNT)
94+
simple.concat({
95+
method: 'GET',
96+
url: 'http://localhost:' + fastify.server.address().port + '/static'
97+
}, (err, response, body) => {
98+
t.error(err)
99+
t.strictEqual(response.statusCode, 200)
100+
t.strictEqual(body.toString(), indexContent)
101+
genericResponseChecks(t, response)
102+
})
103+
})
104+
105+
t.test('/static/deep/path/for/test/purpose/foo.html', t => {
106+
t.plan(3 + GENERIC_RESPONSE_CHECK_COUNT)
107+
simple.concat({
108+
method: 'GET',
109+
url: 'http://localhost:' + fastify.server.address().port + '/static/deep/path/for/test/purpose/foo.html'
110+
}, (err, response, body) => {
111+
t.error(err)
112+
t.strictEqual(response.statusCode, 200)
113+
t.strictEqual(body.toString(), deepContent)
114+
genericResponseChecks(t, response)
115+
})
116+
})
117+
118+
t.test('/static/deep/path/for/test/', t => {
119+
t.plan(3 + GENERIC_RESPONSE_CHECK_COUNT)
120+
simple.concat({
121+
method: 'GET',
122+
url: 'http://localhost:' + fastify.server.address().port + '/static/deep/path/for/test/'
123+
}, (err, response, body) => {
124+
t.error(err)
125+
t.strictEqual(response.statusCode, 200)
126+
t.strictEqual(body.toString(), innerIndex)
127+
genericResponseChecks(t, response)
128+
})
129+
})
130+
131+
t.test('/static/this/path/for/test', t => {
132+
t.plan(2 + GENERIC_ERROR_RESPONSE_CHECK_COUNT)
133+
simple.concat({
134+
method: 'GET',
135+
url: 'http://localhost:' + fastify.server.address().port + '/static/this/path/for/test',
136+
followRedirect: false
137+
}, (err, response, body) => {
138+
t.error(err)
139+
t.strictEqual(response.statusCode, 404)
140+
genericErrorResponseChecks(t, response)
141+
})
142+
})
143+
144+
t.test('/static/this/path/doesnt/exist.html', t => {
145+
t.plan(2 + GENERIC_ERROR_RESPONSE_CHECK_COUNT)
146+
simple.concat({
147+
method: 'GET',
148+
url: 'http://localhost:' + fastify.server.address().port + '/static/this/path/doesnt/exist.html',
149+
followRedirect: false
150+
}, (err, response, body) => {
151+
t.error(err)
152+
t.strictEqual(response.statusCode, 404)
153+
genericErrorResponseChecks(t, response)
154+
})
155+
})
156+
157+
t.test('/static/../index.js', t => {
158+
t.plan(2 + GENERIC_ERROR_RESPONSE_CHECK_COUNT)
159+
simple.concat({
160+
method: 'GET',
161+
url: 'http://localhost:' + fastify.server.address().port + '/static/../index.js',
162+
followRedirect: false
163+
}, (err, response, body) => {
164+
t.error(err)
165+
t.strictEqual(response.statusCode, 403)
166+
genericErrorResponseChecks(t, response)
167+
})
168+
})
169+
170+
t.test('file not exposed outside of the plugin', t => {
171+
t.plan(2)
172+
simple.concat({
173+
method: 'GET',
174+
// foobar is in static
175+
url: 'http://localhost:' + fastify.server.address().port + '/foobar.html'
176+
}, (err, response, body) => {
177+
t.error(err)
178+
t.strictEqual(response.statusCode, 404)
179+
})
180+
})
181+
})
182+
})
183+
36184
t.test('register /static', t => {
37185
t.plan(11)
38186

test/types/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ const options = {
1717
lastModified: true,
1818
maxAge: '',
1919
prefix: '',
20+
prefixAvoidTrailingSlash: false,
2021
root: '',
2122
schemaHide: true,
2223
serve: true,

0 commit comments

Comments
 (0)