@fastify/compress 5.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/dependabot.yml +13 -0
- package/.github/stale.yml +21 -0
- package/.github/workflows/ci.yml +47 -0
- package/.taprc +6 -0
- package/LICENSE +21 -0
- package/README.md +310 -0
- package/index.d.ts +81 -0
- package/index.js +556 -0
- package/lib/utils.js +7 -0
- package/package.json +73 -0
- package/test/global-compress.test.js +3048 -0
- package/test/global-decompress.test.js +423 -0
- package/test/routes-compress.test.js +437 -0
- package/test/routes-decompress.test.js +305 -0
- package/test/types/index.test-d.ts +115 -0
- package/test/utils.test.js +27 -0
|
@@ -0,0 +1,305 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const { test } = require('tap')
|
|
4
|
+
const { createReadStream } = require('fs')
|
|
5
|
+
const path = require('path')
|
|
6
|
+
const zlib = require('zlib')
|
|
7
|
+
const pump = require('pump')
|
|
8
|
+
const Fastify = require('fastify')
|
|
9
|
+
const compressPlugin = require('../index')
|
|
10
|
+
|
|
11
|
+
function createPayload (compressor) {
|
|
12
|
+
let payload = createReadStream(path.resolve(__dirname, '../package.json'))
|
|
13
|
+
|
|
14
|
+
if (compressor) {
|
|
15
|
+
payload = pump(payload, compressor())
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
return payload
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
test('When using routes `decompress` settings :', async (t) => {
|
|
22
|
+
t.test('it should decompress data using the route custom provided `createInflate` method', async (t) => {
|
|
23
|
+
t.plan(8)
|
|
24
|
+
|
|
25
|
+
let usedCustomGlobal = false
|
|
26
|
+
let usedCustom = false
|
|
27
|
+
const customZlibGlobal = { createInflate: () => (usedCustomGlobal = true) && zlib.createInflate() }
|
|
28
|
+
const customZlib = { createInflate: () => (usedCustom = true) && zlib.createInflate() }
|
|
29
|
+
|
|
30
|
+
const fastify = Fastify()
|
|
31
|
+
await fastify.register(compressPlugin, { zlib: customZlibGlobal })
|
|
32
|
+
|
|
33
|
+
fastify.post('/', (request, reply) => {
|
|
34
|
+
reply.send(request.body.name)
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
fastify.post('/custom', {
|
|
38
|
+
decompress: {
|
|
39
|
+
zlib: customZlib
|
|
40
|
+
}
|
|
41
|
+
}, (request, reply) => {
|
|
42
|
+
reply.send(request.body.name)
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
await fastify.inject({
|
|
46
|
+
url: '/',
|
|
47
|
+
method: 'POST',
|
|
48
|
+
headers: {
|
|
49
|
+
'content-type': 'application/json',
|
|
50
|
+
'content-encoding': 'deflate'
|
|
51
|
+
},
|
|
52
|
+
payload: createPayload(zlib.createDeflate)
|
|
53
|
+
}).then((response) => {
|
|
54
|
+
t.equal(usedCustom, false)
|
|
55
|
+
t.equal(usedCustomGlobal, true)
|
|
56
|
+
|
|
57
|
+
t.equal(response.statusCode, 200)
|
|
58
|
+
t.equal(response.body, '@fastify/compress')
|
|
59
|
+
|
|
60
|
+
usedCustom = false
|
|
61
|
+
usedCustomGlobal = false
|
|
62
|
+
})
|
|
63
|
+
const response = await fastify.inject({
|
|
64
|
+
url: '/custom',
|
|
65
|
+
method: 'POST',
|
|
66
|
+
headers: {
|
|
67
|
+
'content-type': 'application/json',
|
|
68
|
+
'content-encoding': 'deflate'
|
|
69
|
+
},
|
|
70
|
+
payload: createPayload(zlib.createDeflate)
|
|
71
|
+
})
|
|
72
|
+
t.equal(usedCustom, true)
|
|
73
|
+
t.equal(usedCustomGlobal, false)
|
|
74
|
+
|
|
75
|
+
t.equal(response.statusCode, 200)
|
|
76
|
+
t.equal(response.body, '@fastify/compress')
|
|
77
|
+
})
|
|
78
|
+
|
|
79
|
+
t.test('it should decompress data using the route custom provided `createGunzip` method', async (t) => {
|
|
80
|
+
t.plan(8)
|
|
81
|
+
|
|
82
|
+
let usedCustomGlobal = false
|
|
83
|
+
let usedCustom = false
|
|
84
|
+
const customZlibGlobal = { createGunzip: () => (usedCustomGlobal = true) && zlib.createGunzip() }
|
|
85
|
+
const customZlib = { createGunzip: () => (usedCustom = true) && zlib.createGunzip() }
|
|
86
|
+
|
|
87
|
+
const fastify = Fastify()
|
|
88
|
+
await fastify.register(compressPlugin, { zlib: customZlibGlobal })
|
|
89
|
+
|
|
90
|
+
fastify.post('/', (request, reply) => {
|
|
91
|
+
reply.send(request.body.name)
|
|
92
|
+
})
|
|
93
|
+
|
|
94
|
+
fastify.post('/custom', {
|
|
95
|
+
decompress: {
|
|
96
|
+
zlib: customZlib
|
|
97
|
+
}
|
|
98
|
+
}, (request, reply) => {
|
|
99
|
+
reply.send(request.body.name)
|
|
100
|
+
})
|
|
101
|
+
|
|
102
|
+
await fastify.inject({
|
|
103
|
+
url: '/',
|
|
104
|
+
method: 'POST',
|
|
105
|
+
headers: {
|
|
106
|
+
'content-type': 'application/json',
|
|
107
|
+
'content-encoding': 'gzip'
|
|
108
|
+
},
|
|
109
|
+
payload: createPayload(zlib.createGzip)
|
|
110
|
+
}).then((response) => {
|
|
111
|
+
t.equal(usedCustom, false)
|
|
112
|
+
t.equal(usedCustomGlobal, true)
|
|
113
|
+
|
|
114
|
+
t.equal(response.statusCode, 200)
|
|
115
|
+
t.equal(response.body, '@fastify/compress')
|
|
116
|
+
|
|
117
|
+
usedCustom = false
|
|
118
|
+
usedCustomGlobal = false
|
|
119
|
+
})
|
|
120
|
+
|
|
121
|
+
const response = await fastify.inject({
|
|
122
|
+
url: '/custom',
|
|
123
|
+
method: 'POST',
|
|
124
|
+
headers: {
|
|
125
|
+
'content-type': 'application/json',
|
|
126
|
+
'content-encoding': 'gzip'
|
|
127
|
+
},
|
|
128
|
+
payload: createPayload(zlib.createGzip)
|
|
129
|
+
})
|
|
130
|
+
t.equal(usedCustom, true)
|
|
131
|
+
t.equal(usedCustomGlobal, false)
|
|
132
|
+
|
|
133
|
+
t.equal(response.statusCode, 200)
|
|
134
|
+
t.equal(response.body, '@fastify/compress')
|
|
135
|
+
})
|
|
136
|
+
|
|
137
|
+
t.test('it should not decompress data when route `decompress` option is set to `false`', async (t) => {
|
|
138
|
+
t.plan(6)
|
|
139
|
+
|
|
140
|
+
let usedCustomGlobal = false
|
|
141
|
+
const customZlibGlobal = { createGunzip: () => (usedCustomGlobal = true) && zlib.createGunzip() }
|
|
142
|
+
|
|
143
|
+
const fastify = Fastify()
|
|
144
|
+
await fastify.register(compressPlugin, { zlib: customZlibGlobal })
|
|
145
|
+
|
|
146
|
+
fastify.post('/', (request, reply) => {
|
|
147
|
+
reply.send(request.body.name)
|
|
148
|
+
})
|
|
149
|
+
|
|
150
|
+
fastify.post('/custom', { decompress: false }, (request, reply) => {
|
|
151
|
+
reply.send(request.body.name)
|
|
152
|
+
})
|
|
153
|
+
|
|
154
|
+
await fastify.inject({
|
|
155
|
+
url: '/',
|
|
156
|
+
method: 'POST',
|
|
157
|
+
headers: {
|
|
158
|
+
'content-type': 'application/json',
|
|
159
|
+
'content-encoding': 'gzip'
|
|
160
|
+
},
|
|
161
|
+
payload: createPayload(zlib.createGzip)
|
|
162
|
+
}).then((response) => {
|
|
163
|
+
t.equal(usedCustomGlobal, true)
|
|
164
|
+
|
|
165
|
+
t.equal(response.statusCode, 200)
|
|
166
|
+
t.equal(response.body, '@fastify/compress')
|
|
167
|
+
|
|
168
|
+
usedCustomGlobal = false
|
|
169
|
+
})
|
|
170
|
+
|
|
171
|
+
const response = await fastify.inject({
|
|
172
|
+
url: '/custom',
|
|
173
|
+
method: 'POST',
|
|
174
|
+
headers: {
|
|
175
|
+
'content-type': 'application/json',
|
|
176
|
+
'content-encoding': 'gzip'
|
|
177
|
+
},
|
|
178
|
+
payload: createPayload(zlib.createGzip)
|
|
179
|
+
})
|
|
180
|
+
t.equal(usedCustomGlobal, false)
|
|
181
|
+
|
|
182
|
+
t.equal(response.statusCode, 400)
|
|
183
|
+
t.strictSame(response.json(), {
|
|
184
|
+
statusCode: 400,
|
|
185
|
+
code: 'FST_ERR_CTP_INVALID_CONTENT_LENGTH',
|
|
186
|
+
error: 'Bad Request',
|
|
187
|
+
message: 'Request body size did not match Content-Length'
|
|
188
|
+
})
|
|
189
|
+
})
|
|
190
|
+
|
|
191
|
+
t.test('it should throw an error on invalid route `decompress` settings', async (t) => {
|
|
192
|
+
t.plan(2)
|
|
193
|
+
|
|
194
|
+
const fastify = Fastify()
|
|
195
|
+
await fastify.register(compressPlugin, { global: false })
|
|
196
|
+
|
|
197
|
+
fastify.post('/', { decompress: 'bad config' }, (request, reply) => {
|
|
198
|
+
reply.send(request.body.name)
|
|
199
|
+
})
|
|
200
|
+
|
|
201
|
+
await fastify.inject({
|
|
202
|
+
url: '/',
|
|
203
|
+
method: 'POST',
|
|
204
|
+
headers: {
|
|
205
|
+
'content-encoding': 'gzip'
|
|
206
|
+
},
|
|
207
|
+
payload: ''
|
|
208
|
+
}).catch((err) => {
|
|
209
|
+
t.type(err, Error)
|
|
210
|
+
t.equal(err.message, 'Unknown value for route decompress configuration')
|
|
211
|
+
})
|
|
212
|
+
})
|
|
213
|
+
})
|
|
214
|
+
|
|
215
|
+
test('When using the old routes `{ config: decompress }` option :', async (t) => {
|
|
216
|
+
t.test('it should decompress data using the route custom provided `createGunzip` method', async (t) => {
|
|
217
|
+
t.plan(8)
|
|
218
|
+
|
|
219
|
+
let usedCustomGlobal = false
|
|
220
|
+
let usedCustom = false
|
|
221
|
+
const customZlibGlobal = { createGunzip: () => (usedCustomGlobal = true) && zlib.createGunzip() }
|
|
222
|
+
const customZlib = { createGunzip: () => (usedCustom = true) && zlib.createGunzip() }
|
|
223
|
+
|
|
224
|
+
const fastify = Fastify()
|
|
225
|
+
await fastify.register(compressPlugin, { zlib: customZlibGlobal })
|
|
226
|
+
|
|
227
|
+
fastify.post('/', (request, reply) => {
|
|
228
|
+
reply.send(request.body.name)
|
|
229
|
+
})
|
|
230
|
+
|
|
231
|
+
fastify.post('/custom', {
|
|
232
|
+
config: {
|
|
233
|
+
decompress: {
|
|
234
|
+
zlib: customZlib
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
}, (request, reply) => {
|
|
238
|
+
reply.send(request.body.name)
|
|
239
|
+
})
|
|
240
|
+
|
|
241
|
+
await fastify.inject({
|
|
242
|
+
url: '/',
|
|
243
|
+
method: 'POST',
|
|
244
|
+
headers: {
|
|
245
|
+
'content-type': 'application/json',
|
|
246
|
+
'content-encoding': 'gzip'
|
|
247
|
+
},
|
|
248
|
+
payload: createPayload(zlib.createGzip)
|
|
249
|
+
}).then((response) => {
|
|
250
|
+
t.equal(usedCustom, false)
|
|
251
|
+
t.equal(usedCustomGlobal, true)
|
|
252
|
+
|
|
253
|
+
t.equal(response.statusCode, 200)
|
|
254
|
+
t.equal(response.body, '@fastify/compress')
|
|
255
|
+
|
|
256
|
+
usedCustom = false
|
|
257
|
+
usedCustomGlobal = false
|
|
258
|
+
})
|
|
259
|
+
|
|
260
|
+
const response = await fastify.inject({
|
|
261
|
+
url: '/custom',
|
|
262
|
+
method: 'POST',
|
|
263
|
+
headers: {
|
|
264
|
+
'content-type': 'application/json',
|
|
265
|
+
'content-encoding': 'gzip'
|
|
266
|
+
},
|
|
267
|
+
payload: createPayload(zlib.createGzip)
|
|
268
|
+
})
|
|
269
|
+
t.equal(usedCustom, true)
|
|
270
|
+
t.equal(usedCustomGlobal, false)
|
|
271
|
+
|
|
272
|
+
t.equal(response.statusCode, 200)
|
|
273
|
+
t.equal(response.body, '@fastify/compress')
|
|
274
|
+
})
|
|
275
|
+
|
|
276
|
+
t.test('it should use the old routes `{ config: decompress }` options over routes `decompress` options', async (t) => {
|
|
277
|
+
t.plan(2)
|
|
278
|
+
|
|
279
|
+
const fastify = Fastify()
|
|
280
|
+
await fastify.register(compressPlugin, { global: false })
|
|
281
|
+
|
|
282
|
+
fastify.post('/', {
|
|
283
|
+
decompress: {
|
|
284
|
+
zlib: { createGunzip: () => zlib.createGunzip() }
|
|
285
|
+
},
|
|
286
|
+
config: {
|
|
287
|
+
decompress: 'bad config'
|
|
288
|
+
}
|
|
289
|
+
}, (request, reply) => {
|
|
290
|
+
reply.send(request.body.name)
|
|
291
|
+
})
|
|
292
|
+
|
|
293
|
+
await fastify.inject({
|
|
294
|
+
url: '/',
|
|
295
|
+
method: 'POST',
|
|
296
|
+
headers: {
|
|
297
|
+
'content-encoding': 'gzip'
|
|
298
|
+
},
|
|
299
|
+
payload: ''
|
|
300
|
+
}).catch((err) => {
|
|
301
|
+
t.type(err, Error)
|
|
302
|
+
t.equal(err.message, 'Unknown value for route decompress configuration')
|
|
303
|
+
})
|
|
304
|
+
})
|
|
305
|
+
})
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import fastify, { FastifyInstance } from 'fastify'
|
|
2
|
+
import { createReadStream } from 'fs'
|
|
3
|
+
import { expectError, expectType } from 'tsd'
|
|
4
|
+
import * as zlib from 'zlib'
|
|
5
|
+
import fastifyCompress, { FastifyCompressOptions } from '../..'
|
|
6
|
+
|
|
7
|
+
const stream = createReadStream('./package.json')
|
|
8
|
+
|
|
9
|
+
const withGlobalOptions: FastifyCompressOptions = {
|
|
10
|
+
global: true,
|
|
11
|
+
threshold: 10,
|
|
12
|
+
zlib: zlib,
|
|
13
|
+
brotliOptions: {
|
|
14
|
+
params: {
|
|
15
|
+
[zlib.constants.BROTLI_PARAM_MODE]: zlib.constants.BROTLI_MODE_TEXT,
|
|
16
|
+
[zlib.constants.BROTLI_PARAM_QUALITY]: 4
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
zlibOptions: { level: 1 },
|
|
20
|
+
inflateIfDeflated: true,
|
|
21
|
+
customTypes: /x-protobuf$/,
|
|
22
|
+
encodings: ['gzip', 'br', 'identity', 'deflate'],
|
|
23
|
+
requestEncodings: ['gzip', 'br', 'identity', 'deflate'],
|
|
24
|
+
forceRequestEncoding: 'gzip',
|
|
25
|
+
removeContentLengthHeader: true
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const app: FastifyInstance = fastify()
|
|
29
|
+
app.register(fastifyCompress, withGlobalOptions)
|
|
30
|
+
|
|
31
|
+
app.get('/test-one', async (request, reply) => {
|
|
32
|
+
expectType<void>(reply.compress(stream))
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
app.get('/test-two', async (request, reply) => {
|
|
36
|
+
expectError(reply.compress())
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
// Instanciation of an app without global
|
|
40
|
+
const appWithoutGlobal: FastifyInstance = fastify()
|
|
41
|
+
appWithoutGlobal.register(fastifyCompress, { global: false })
|
|
42
|
+
|
|
43
|
+
appWithoutGlobal.get('/one', {
|
|
44
|
+
compress: {
|
|
45
|
+
zlib: {
|
|
46
|
+
createGzip: () => zlib.createGzip()
|
|
47
|
+
},
|
|
48
|
+
removeContentLengthHeader: false
|
|
49
|
+
},
|
|
50
|
+
decompress: {
|
|
51
|
+
forceRequestEncoding: 'gzip',
|
|
52
|
+
zlib: {
|
|
53
|
+
createGunzip: () => zlib.createGunzip()
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}, (request, reply) => {
|
|
57
|
+
expectType<void>(reply.type('text/plain').compress(stream))
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
appWithoutGlobal.get('/two', {
|
|
61
|
+
config: {
|
|
62
|
+
compress: {
|
|
63
|
+
zlib: {
|
|
64
|
+
createGzip: () => zlib.createGzip()
|
|
65
|
+
}
|
|
66
|
+
},
|
|
67
|
+
decompress: {
|
|
68
|
+
forceRequestEncoding: 'gzip',
|
|
69
|
+
zlib: {
|
|
70
|
+
createGunzip: () => zlib.createGunzip()
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}, (request, reply) => {
|
|
75
|
+
expectType<void>(reply.type('text/plain').compress(stream))
|
|
76
|
+
})
|
|
77
|
+
|
|
78
|
+
expectError(
|
|
79
|
+
appWithoutGlobal.get('/throw-a-ts-arg-error-on-shorthand-route', {
|
|
80
|
+
compress: 'bad compress route option value',
|
|
81
|
+
decompress: 'bad decompress route option value'
|
|
82
|
+
}, (request, reply) => {
|
|
83
|
+
expectType<void>(reply.type('text/plain').compress(stream))
|
|
84
|
+
})
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
expectError(
|
|
88
|
+
appWithoutGlobal.route({
|
|
89
|
+
method: 'GET',
|
|
90
|
+
path: '/throw-a-ts-arg-error',
|
|
91
|
+
compress: 'bad compress route option value',
|
|
92
|
+
decompress: 'bad decompress route option value',
|
|
93
|
+
handler: (request, reply) => { expectType<void>(reply.type('text/plain').compress(stream)) }
|
|
94
|
+
})
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
appWithoutGlobal.inject(
|
|
98
|
+
{
|
|
99
|
+
method: 'GET',
|
|
100
|
+
path: '/throw-a-ts-arg-error',
|
|
101
|
+
headers: {
|
|
102
|
+
'accept-encoding': 'gzip'
|
|
103
|
+
}
|
|
104
|
+
},
|
|
105
|
+
(err) => {
|
|
106
|
+
expectType<Error>(err)
|
|
107
|
+
}
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
// Instanciation of an app that should trigger a typescript error
|
|
111
|
+
const appThatTriggerAnError = fastify()
|
|
112
|
+
expectError(appThatTriggerAnError.register(fastifyCompress, {
|
|
113
|
+
global: true,
|
|
114
|
+
thisOptionDoesNotExist: 'trigger a typescript error'
|
|
115
|
+
}))
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const { createReadStream } = require('fs')
|
|
4
|
+
const { Socket } = require('net')
|
|
5
|
+
const { Duplex, PassThrough, Readable, Stream, Transform, Writable } = require('stream')
|
|
6
|
+
const { test } = require('tap')
|
|
7
|
+
const { isStream } = require('../lib/utils')
|
|
8
|
+
|
|
9
|
+
test('isStream() utility should be able to detect Streams', async (t) => {
|
|
10
|
+
t.plan(12)
|
|
11
|
+
|
|
12
|
+
t.equal(isStream(new Stream()), true)
|
|
13
|
+
t.equal(isStream(new Readable()), true)
|
|
14
|
+
t.equal(isStream(new Writable()), true)
|
|
15
|
+
t.equal(isStream(new Duplex()), true)
|
|
16
|
+
t.equal(isStream(new Transform()), true)
|
|
17
|
+
t.equal(isStream(new PassThrough()), true)
|
|
18
|
+
|
|
19
|
+
t.equal(isStream(createReadStream('package.json')), true)
|
|
20
|
+
|
|
21
|
+
t.equal(isStream(new Socket()), true)
|
|
22
|
+
|
|
23
|
+
t.equal(isStream({}), false)
|
|
24
|
+
t.equal(isStream(null), false)
|
|
25
|
+
t.equal(isStream(undefined), false)
|
|
26
|
+
t.equal(isStream(''), false)
|
|
27
|
+
})
|