Skip to content

Commit 0a0babc

Browse files
authored
feat: allow enabling and disabling compression and decompression separately (#362)
1 parent 06ff798 commit 0a0babc

File tree

4 files changed

+174
-2
lines changed

4 files changed

+174
-2
lines changed

README.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,23 @@ await fastify.register(
5757
{ global: false }
5858
)
5959
```
60+
61+
If only compression or decompression is required, set the `globalCompression` or `globalDecompression` config flags to `false` respectively (both are `true` by default).
62+
63+
```js
64+
await fastify.register(
65+
import('@fastify/compress'),
66+
// Disable compression but keep decompression enabled (default behavior for globalDecompression is true)
67+
{ globalCompression: false }
68+
)
69+
70+
// Disable decompression but keep compression enabled
71+
await fastify.register(
72+
import('@fastify/compress'),
73+
{ globalDecompression: false }
74+
)
75+
```
76+
6077
Fastify encapsulation can be used to set global compression but run it only in a subset of routes by wrapping them inside a plugin.
6178

6279
> ℹ️ Note: If using `@fastify/compress` plugin together with `@fastify/static` plugin, `@fastify/compress` must be registered (with *global hook*) **before** registering `@fastify/static`.

index.js

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,9 @@ function processCompressParams (opts) {
124124
}
125125

126126
const params = {
127-
global: (typeof opts.global === 'boolean') ? opts.global : true
127+
global: (typeof opts.globalCompression === 'boolean')
128+
? opts.globalCompression
129+
: (typeof opts.global === 'boolean') ? opts.global : true
128130
}
129131

130132
params.removeContentLengthHeader = typeof opts.removeContentLengthHeader === 'boolean' ? opts.removeContentLengthHeader : true
@@ -184,7 +186,9 @@ function processDecompressParams (opts) {
184186
const customZlib = opts.zlib || zlib
185187

186188
const params = {
187-
global: (typeof opts.global === 'boolean') ? opts.global : true,
189+
global: (typeof opts.globalDecompression === 'boolean')
190+
? opts.globalDecompression
191+
: (typeof opts.global === 'boolean') ? opts.global : true,
188192
onUnsupportedRequestEncoding: opts.onUnsupportedRequestEncoding,
189193
onInvalidRequestPayload: opts.onInvalidRequestPayload,
190194
decompressStream: {

test/global-compress.test.js

Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3399,3 +3399,152 @@ for (const contentType of notByDefaultSupportedContentTypes) {
33993399
t.assert.equal(response.rawPayload.toString('utf-8'), file)
34003400
})
34013401
}
3402+
3403+
test('It should support separate globalCompression and globalDecompression settings', async (t) => {
3404+
t.plan(3)
3405+
3406+
// Test case: disable compression but enable decompression globally
3407+
const fastify = Fastify()
3408+
await fastify.register(compressPlugin, {
3409+
globalCompression: false,
3410+
globalDecompression: true
3411+
})
3412+
3413+
const testData = { message: 'test data for decompression only' }
3414+
3415+
fastify.get('/', (request, reply) => {
3416+
reply.send(testData)
3417+
})
3418+
3419+
// Test that compression is disabled
3420+
const getResponse = await fastify.inject({
3421+
url: '/',
3422+
method: 'GET',
3423+
headers: {
3424+
'accept-encoding': 'gzip, deflate, br'
3425+
}
3426+
})
3427+
3428+
t.assert.equal(getResponse.statusCode, 200)
3429+
// No compression should happen since globalCompression is false
3430+
t.assert.equal(getResponse.headers['content-encoding'], undefined)
3431+
t.assert.deepEqual(getResponse.json(), testData)
3432+
})
3433+
3434+
test('It should enable decompression when globalDecompression is true', async (t) => {
3435+
t.plan(2)
3436+
3437+
// Test case: enable decompression globally
3438+
const fastify = Fastify()
3439+
await fastify.register(compressPlugin, {
3440+
globalCompression: false,
3441+
globalDecompression: true
3442+
})
3443+
3444+
// Create a compressed request payload to test decompression
3445+
const compressedPayload = zlib.gzipSync(JSON.stringify({ input: 'compressed data' }))
3446+
3447+
fastify.post('/', (request, reply) => {
3448+
// This should be decompressed automatically
3449+
reply.send({ received: request.body })
3450+
})
3451+
3452+
// Test that decompression is enabled
3453+
const postResponse = await fastify.inject({
3454+
url: '/',
3455+
method: 'POST',
3456+
headers: {
3457+
'content-encoding': 'gzip',
3458+
'content-type': 'application/json'
3459+
},
3460+
payload: compressedPayload
3461+
})
3462+
3463+
t.assert.equal(postResponse.statusCode, 200)
3464+
// Decompression should work since globalDecompression is true
3465+
t.assert.deepEqual(postResponse.json(), { received: { input: 'compressed data' } })
3466+
})
3467+
3468+
test('It should fall back to global option when specific options are not provided', async (t) => {
3469+
t.plan(2)
3470+
3471+
// Test that global: false affects both compression and decompression
3472+
const fastify = Fastify()
3473+
await fastify.register(compressPlugin, {
3474+
global: false
3475+
})
3476+
3477+
const testData = { message: 'test data' }
3478+
3479+
fastify.get('/', (request, reply) => {
3480+
reply.send(testData)
3481+
})
3482+
3483+
const response = await fastify.inject({
3484+
url: '/',
3485+
method: 'GET',
3486+
headers: {
3487+
'accept-encoding': 'gzip, deflate, br'
3488+
}
3489+
})
3490+
3491+
t.assert.equal(response.statusCode, 200)
3492+
// No compression should happen since global is false
3493+
t.assert.equal(response.headers['content-encoding'], undefined)
3494+
})
3495+
3496+
test('It should demonstrate globalCompression overrides global setting', async (t) => {
3497+
t.plan(2)
3498+
3499+
// Test that globalCompression: true overrides global: false
3500+
const fastify = Fastify()
3501+
await fastify.register(compressPlugin, {
3502+
global: false,
3503+
globalCompression: true,
3504+
threshold: 0
3505+
})
3506+
3507+
fastify.get('/', (request, reply) => {
3508+
reply.send({ message: 'globalCompression overrides global' })
3509+
})
3510+
3511+
const response = await fastify.inject({
3512+
url: '/',
3513+
method: 'GET',
3514+
headers: { 'accept-encoding': 'gzip' }
3515+
})
3516+
3517+
t.assert.equal(response.statusCode, 200)
3518+
t.assert.equal(response.headers['content-encoding'], 'gzip') // Compression enabled despite global: false
3519+
})
3520+
3521+
test('It should demonstrate globalDecompression controls decompression independently', async (t) => {
3522+
t.plan(2)
3523+
3524+
// Test globalDecompression: false disables decompression even with global: true
3525+
const fastify = Fastify()
3526+
await fastify.register(compressPlugin, {
3527+
global: true,
3528+
globalDecompression: false
3529+
})
3530+
3531+
const compressedPayload = zlib.gzipSync(JSON.stringify({ test: 'decompression' }))
3532+
3533+
fastify.post('/', (request, reply) => {
3534+
reply.send({ received: request.body })
3535+
})
3536+
3537+
const response = await fastify.inject({
3538+
url: '/',
3539+
method: 'POST',
3540+
headers: {
3541+
'content-encoding': 'gzip',
3542+
'content-type': 'application/json'
3543+
},
3544+
payload: compressedPayload
3545+
})
3546+
3547+
// Should get 400 error since decompression is disabled and compressed data can't be parsed
3548+
t.assert.equal(response.statusCode, 400)
3549+
t.assert.ok(response.body.includes('Content-Length') || response.body.includes('Bad Request'))
3550+
})

types/index.d.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,8 @@ declare namespace fastifyCompress {
7777
encodings?: EncodingToken[];
7878
forceRequestEncoding?: EncodingToken;
7979
global?: boolean;
80+
globalDecompression?: boolean;
81+
globalCompression?: boolean;
8082
inflateIfDeflated?: boolean;
8183
onInvalidRequestPayload?: (encoding: string, request: FastifyRequest, error: Error) => Error | undefined | null;
8284
onUnsupportedEncoding?: (encoding: string, request: FastifyRequest, reply: FastifyReply) => string | Buffer | Stream;

0 commit comments

Comments
 (0)