Skip to content
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,12 @@ Default: `{}`
Constraints that will be added to registered routes. See Fastify's documentation for
[route constraints](https://fastify.dev/docs/latest/Reference/Routes/#constraints).

#### `logLevel`

Default: `info`

Set log level to registered routes.

#### `prefixAvoidTrailingSlash`

Default: `false`
Expand Down
1 change: 1 addition & 0 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ async function fastifyStatic (fastify, opts) {
schema: {
hide: opts.schemaHide !== undefined ? opts.schemaHide : true
},
logLevel: opts.logLevel,
errorHandler (error, request, reply) {
if (error?.code === 'ERR_STREAM_PREMATURE_CLOSE') {
reply.request.raw.destroy()
Expand Down
173 changes: 173 additions & 0 deletions test/static.test.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
'use strict'

Check failure on line 1 in test/static.test.js

View workflow job for this annotation

GitHub Actions / test / Test (20, ubuntu-latest)

/home/runner/work/fastify-static/fastify-static/test/static.test.js

[Error: test failed] { code: 'ERR_TEST_FAILURE', failureType: 'testCodeFailure', cause: 'test failed', exitCode: 7, signal: null }

Check failure on line 1 in test/static.test.js

View workflow job for this annotation

GitHub Actions / test / Test (22, macos-latest)

/Users/runner/work/fastify-static/fastify-static/test/static.test.js

[Error: test failed] { code: 'ERR_TEST_FAILURE', failureType: 'testCodeFailure', cause: 'test failed', exitCode: 7, signal: null }

Check failure on line 1 in test/static.test.js

View workflow job for this annotation

GitHub Actions / test / Test (22, ubuntu-latest)

/home/runner/work/fastify-static/fastify-static/test/static.test.js

[Error: test failed] { code: 'ERR_TEST_FAILURE', failureType: 'testCodeFailure', cause: 'test failed', exitCode: 7, signal: null }

/* eslint n/no-deprecated-api: "off" */

Expand Down Expand Up @@ -3493,7 +3493,7 @@
}
})

genericResponseChecks(t, response)

Check failure on line 3496 in test/static.test.js

View workflow job for this annotation

GitHub Actions / test / Lint Code

't' is not defined
t.equal(response.headers['content-encoding'], 'br')
t.equal(response.statusCode, 200)
t.same(response.rawPayload, dirIndexBr)
Expand Down Expand Up @@ -3525,7 +3525,7 @@
genericResponseChecks(t, response)
t.equal(response.headers['content-encoding'], 'gzip')
t.equal(response.statusCode, 200)
t.same(response.rawPayload, dirIndexGz)

Check failure on line 3528 in test/static.test.js

View workflow job for this annotation

GitHub Actions / test / Lint Code

'simple' is not defined
t.end()
}
)
Expand All @@ -3538,7 +3538,7 @@
prefix: '/static-pre-compressed/',
preCompressed: true,
index: ['all-three.html']
}

Check failure on line 3541 in test/static.test.js

View workflow job for this annotation

GitHub Actions / test / Lint Code

'simple' is not defined

const fastify = Fastify()

Expand All @@ -3551,7 +3551,7 @@
headers: {
'accept-encoding': 'gzip, deflate, br'
}
})

Check failure on line 3554 in test/static.test.js

View workflow job for this annotation

GitHub Actions / test / Lint Code

'simple' is not defined

genericResponseChecks(t, response)
t.equal(response.headers['content-encoding'], 'br')
Expand All @@ -3563,7 +3563,7 @@

t.test(
'will serve precompressed file without content-type charset',
async (t) => {

Check failure on line 3566 in test/static.test.js

View workflow job for this annotation

GitHub Actions / test / Lint Code

'simple' is not defined
const pluginOptions = {
root: path.join(__dirname, '/static-pre-compressed'),
prefix: '/static-pre-compressed/',
Expand All @@ -3576,7 +3576,7 @@
t.teardown(fastify.close.bind(fastify))

const response = await fastify.inject({
method: 'GET',

Check failure on line 3579 in test/static.test.js

View workflow job for this annotation

GitHub Actions / test / Lint Code

'simple' is not defined
url: '/static-pre-compressed/sample.jpg',
headers: {
'accept-encoding': 'gzip, deflate, br'
Expand All @@ -3589,7 +3589,7 @@
t.end()
}
)

Check failure on line 3592 in test/static.test.js

View workflow job for this annotation

GitHub Actions / test / Lint Code

'simple' is not defined
t.test(
'nonexistent index with precompressed option',
async (t) => {
Expand All @@ -3602,7 +3602,7 @@
const fastify = Fastify()

fastify.register(fastifyStatic, pluginOptions)
t.teardown(fastify.close.bind(fastify))

Check failure on line 3605 in test/static.test.js

View workflow job for this annotation

GitHub Actions / test / Lint Code

'simple' is not defined

const response = await fastify.inject({
method: 'GET',
Expand All @@ -3615,7 +3615,7 @@
t.equal(response.statusCode, 404)
genericErrorResponseChecks(t, response)
t.end()
}

Check failure on line 3618 in test/static.test.js

View workflow job for this annotation

GitHub Actions / test / Lint Code

'simple' is not defined
)

t.test('should not redirect to protocol-relative locations', (t) => {
Expand All @@ -3628,7 +3628,7 @@
['/^', null, 404], // it is NOT recognized as a directory by pillarjs/send
['//google.com/%2e%2e', '/', 301],
['//users/%2e%2e', '/', 301],
['//users', null, 404],

Check failure on line 3631 in test/static.test.js

View workflow job for this annotation

GitHub Actions / test / Lint Code

'simple' is not defined
['///deep/path//for//test//index.html', null, 200]
]

Expand Down Expand Up @@ -4102,3 +4102,176 @@
})
})
})

t.test('register /static/ with custom log level', t => {
t.plan(11)

const pluginOptions = {
root: path.join(__dirname, '/static'),
prefix: '/static/',
logLevel: 'warn'
}
const fastify = Fastify({
logger: {
stream: {
write: (logLine) => {
const log = JSON.parse(logLine)

if (log.reqId) {
t.fail('Not expecting any log since plugin\'s log level is set at WARN')
}
},
},
},
})
fastify.register(fastifyStatic, pluginOptions)

t.teardown(fastify.close.bind(fastify))

fastify.listen({ port: 0 }, (err) => {
t.error(err)

fastify.server.unref()

t.test('/static/index.html', (t) => {
t.plan(3 + GENERIC_RESPONSE_CHECK_COUNT)
simple.concat({
method: 'GET',
url: 'http://localhost:' + fastify.server.address().port + '/static/index.html'
}, (err, response, body) => {
t.error(err)
t.equal(response.statusCode, 200)
t.equal(body.toString(), indexContent)
genericResponseChecks(t, response)
})
})

t.test('/static/index.html', t => {
t.plan(3 + GENERIC_RESPONSE_CHECK_COUNT)
simple.concat({
method: 'HEAD',
url: 'http://localhost:' + fastify.server.address().port + '/static/index.html'
}, (err, response, body) => {
t.error(err)
t.equal(response.statusCode, 200)
t.equal(body.toString(), '')
genericResponseChecks(t, response)
})
})

t.test('/static/index.css', (t) => {
t.plan(2 + GENERIC_RESPONSE_CHECK_COUNT)
simple.concat({
method: 'GET',
url: 'http://localhost:' + fastify.server.address().port + '/static/index.css'
}, (err, response, body) => {
t.error(err)
t.equal(response.statusCode, 200)
genericResponseChecks(t, response)
})
})

t.test('/static/', (t) => {
t.plan(3 + GENERIC_RESPONSE_CHECK_COUNT)
simple.concat({
method: 'GET',
url: 'http://localhost:' + fastify.server.address().port + '/static/'
}, (err, response, body) => {
t.error(err)
t.equal(response.statusCode, 200)
t.equal(body.toString(), indexContent)
genericResponseChecks(t, response)
})
})

t.test('/static/deep/path/for/test/purpose/foo.html', (t) => {
t.plan(3 + GENERIC_RESPONSE_CHECK_COUNT)
simple.concat({
method: 'GET',
url: 'http://localhost:' + fastify.server.address().port + '/static/deep/path/for/test/purpose/foo.html'
}, (err, response, body) => {
t.error(err)
t.equal(response.statusCode, 200)
t.equal(body.toString(), deepContent)
genericResponseChecks(t, response)
})
})

t.test('/static/deep/path/for/test/', (t) => {
t.plan(3 + GENERIC_RESPONSE_CHECK_COUNT)
simple.concat({
method: 'GET',
url: 'http://localhost:' + fastify.server.address().port + '/static/deep/path/for/test/'
}, (err, response, body) => {
t.error(err)
t.equal(response.statusCode, 200)
t.equal(body.toString(), innerIndex)
genericResponseChecks(t, response)
})
})

t.test('/static/this/path/for/test', (t) => {
t.plan(2 + GENERIC_ERROR_RESPONSE_CHECK_COUNT)
simple.concat({
method: 'GET',
url: 'http://localhost:' + fastify.server.address().port + '/static/this/path/for/test',
followRedirect: false
}, (err, response, body) => {
t.error(err)
t.equal(response.statusCode, 404)
genericErrorResponseChecks(t, response)
})
})

t.test('/static/this/path/doesnt/exist.html', (t) => {
t.plan(2 + GENERIC_ERROR_RESPONSE_CHECK_COUNT)
simple.concat({
method: 'GET',
url: 'http://localhost:' + fastify.server.address().port + '/static/this/path/doesnt/exist.html',
followRedirect: false
}, (err, response, body) => {
t.error(err)
t.equal(response.statusCode, 404)
genericErrorResponseChecks(t, response)
})
})

t.test('/static/../index.js', (t) => {
t.plan(2 + GENERIC_ERROR_RESPONSE_CHECK_COUNT)
simple.concat({
method: 'GET',
url: 'http://localhost:' + fastify.server.address().port + '/static/../index.js',
followRedirect: false
}, (err, response, body) => {
t.error(err)
t.equal(response.statusCode, 403)
genericErrorResponseChecks(t, response)
})
})

t.test('304', t => {
t.plan(5 + GENERIC_RESPONSE_CHECK_COUNT)
simple.concat({
method: 'GET',
url: 'http://localhost:' + fastify.server.address().port + '/static/index.html'
}, (err, response, body) => {
t.error(err)
const etag = response.headers.etag
t.equal(response.statusCode, 200)
t.equal(body.toString(), indexContent)
genericResponseChecks(t, response)

simple.concat({
method: 'GET',
url: 'http://localhost:' + fastify.server.address().port + '/static/index.html',
headers: {
'if-none-match': etag
}
}, (err, response, body) => {
t.error(err)
t.equal(response.statusCode, 304)
})
})
})
})
})
1 change: 1 addition & 0 deletions types/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ declare namespace fastifyStatic {
lastModified?: boolean;
maxAge?: string | number;
constraints?: RouteOptions['constraints'];
logLevel?: RouteOptions['logLevel'];
}

export const fastifyStatic: FastifyStaticPlugin
Expand Down
3 changes: 2 additions & 1 deletion types/index.test-d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,8 @@ const options: FastifyStaticOptions = {
constraints: {
host: /.*\.example\.com/,
version: '1.0.2'
}
},
logLevel: 'warn'
}

expectError<FastifyStaticOptions>({
Expand Down
Loading