@@ -57,9 +57,7 @@ function fastifyCompress (fastify, opts, next) {
5757 // Manage compression options
5858 if ( routeOptions . compress !== undefined ) {
5959 if ( typeof routeOptions . compress === 'object' ) {
60- const mergedCompressParams = Object . assign (
61- { } , globalCompressParams , processCompressParams ( routeOptions . compress )
62- )
60+ const mergedCompressParams = processCompressParams ( routeOptions . compress , globalCompressParams )
6361
6462 // if the current endpoint has a custom compress configuration ...
6563 buildRouteCompress ( fastify , mergedCompressParams , routeOptions )
@@ -87,9 +85,7 @@ function fastifyCompress (fastify, opts, next) {
8785 if ( routeOptions . decompress !== undefined ) {
8886 if ( typeof routeOptions . decompress === 'object' ) {
8987 // if the current endpoint has a custom compress configuration ...
90- const mergedDecompressParams = Object . assign (
91- { } , globalDecompressParams , processDecompressParams ( routeOptions . decompress )
92- )
88+ const mergedDecompressParams = processDecompressParams ( routeOptions . decompress , globalDecompressParams )
9389
9490 buildRouteDecompress ( fastify , mergedDecompressParams , routeOptions )
9591 } else if ( routeOptions . decompress === false ) {
@@ -116,109 +112,156 @@ const recommendedDefaultBrotliOptions = {
116112 }
117113}
118114
119- function processCompressParams ( opts ) {
120- /* c8 ignore next 3 */
121- if ( ! opts ) {
122- return
115+ const hasOwn = ( obj , key ) => Object . prototype . hasOwnProperty . call ( obj , key )
116+ const baseOrDefault = ( baseParams , key , defaultValue ) => baseParams !== undefined ? baseParams [ key ] : defaultValue
117+ const optionOrBase = ( opts , key , baseParams , defaultValue ) => hasOwn ( opts , key ) ? opts [ key ] : baseOrDefault ( baseParams , key , defaultValue )
118+ const booleanOptionOrBase = ( opts , key , baseParams , defaultValue ) => typeof opts [ key ] === 'boolean' ? opts [ key ] : baseOrDefault ( baseParams , key , defaultValue )
119+ const numberOptionOrBase = ( opts , key , baseParams , defaultValue ) => typeof opts [ key ] === 'number' ? opts [ key ] : baseOrDefault ( baseParams , key , defaultValue )
120+ const zlibOrBase = ( opts , baseParams ) => opts . zlib || baseOrDefault ( baseParams , 'zlib' , zlib )
121+
122+ function globalOptionOrBase ( opts , key , baseParams ) {
123+ if ( typeof opts [ key ] === 'boolean' ) return opts [ key ]
124+
125+ if ( typeof opts . global === 'boolean' ) return opts . global
126+
127+ return baseOrDefault ( baseParams , 'global' , true )
128+ }
129+
130+ function getBrotliOptions ( opts , isGlobal , baseParams ) {
131+ // Route-level compress objects are active compression configs, even when global compression is disabled.
132+ const useDefaults = isGlobal || baseParams !== undefined
133+
134+ if ( hasOwn ( opts , 'brotliOptions' ) ) {
135+ return useDefaults
136+ ? { ...recommendedDefaultBrotliOptions , ...opts . brotliOptions }
137+ : opts . brotliOptions
123138 }
124139
125- const params = {
126- global : ( typeof opts . globalCompression === 'boolean' )
127- ? opts . globalCompression
128- : ( typeof opts . global === 'boolean' ) ? opts . global : true
129- }
130-
131- params . removeContentLengthHeader = typeof opts . removeContentLengthHeader === 'boolean' ? opts . removeContentLengthHeader : true
132- params . brotliOptions = params . global
133- ? { ...recommendedDefaultBrotliOptions , ...opts . brotliOptions }
134- : opts . brotliOptions
135- params . zlibOptions = opts . zlibOptions
136- params . onUnsupportedEncoding = opts . onUnsupportedEncoding
137- params . inflateIfDeflated = opts . inflateIfDeflated === true
138- params . threshold = typeof opts . threshold === 'number' ? opts . threshold : 1024
139- params . compressibleTypes = opts . customTypes instanceof RegExp
140- ? opts . customTypes . test . bind ( opts . customTypes )
141- : typeof opts . customTypes === 'function'
142- ? opts . customTypes
143- : defaultCompressibleTypes . test . bind ( defaultCompressibleTypes )
144- params . compressStream = {
145- br : ( ) => ( ( opts . zlib || zlib ) . createBrotliCompress || zlib . createBrotliCompress ) ( params . brotliOptions ) ,
146- gzip : ( ) => ( ( opts . zlib || zlib ) . createGzip || zlib . createGzip ) ( params . zlibOptions ) ,
147- deflate : ( ) => ( ( opts . zlib || zlib ) . createDeflate || zlib . createDeflate ) ( params . zlibOptions )
148- }
149- if ( typeof ( ( opts . zlib || zlib ) . createZstdCompress || zlib . createZstdCompress ) === 'function' ) {
150- params . compressStream . zstd = ( ) => ( ( opts . zlib || zlib ) . createZstdCompress || zlib . createZstdCompress ) ( params . zlibOptions )
151- }
152- params . uncompressStream = {
153- // Currently params.uncompressStream.br() is never called as we do not have any way to autodetect brotli compression in `fastify-compress`
140+ if ( baseParams !== undefined && baseParams . brotliOptions !== undefined ) return baseParams . brotliOptions
141+
142+ return useDefaults ? { ...recommendedDefaultBrotliOptions } : undefined
143+ }
144+
145+ function getCompressibleTypes ( opts , baseParams ) {
146+ if ( opts . customTypes instanceof RegExp ) return opts . customTypes . test . bind ( opts . customTypes )
147+
148+ if ( typeof opts . customTypes === 'function' ) return opts . customTypes
149+
150+ if ( ! hasOwn ( opts , 'customTypes' ) && baseParams !== undefined ) return baseParams . compressibleTypes
151+
152+ return defaultCompressibleTypes . test . bind ( defaultCompressibleTypes )
153+ }
154+
155+ function getSupportedEncodings ( ) {
156+ const supportedEncodings = [ 'br' , 'gzip' , 'deflate' , 'identity' ]
157+ if ( typeof zlib . createZstdCompress === 'function' ) supportedEncodings . unshift ( 'zstd' )
158+ return supportedEncodings
159+ }
160+
161+ function encodingsOrBase ( opts , key , baseParams ) {
162+ const supportedEncodings = getSupportedEncodings ( )
163+
164+ if ( Array . isArray ( opts [ key ] ) ) {
165+ return supportedEncodings
166+ . filter ( encoding => opts [ key ] . includes ( encoding ) )
167+ . sort ( ( a , b ) => opts [ key ] . indexOf ( a ) - opts [ key ] . indexOf ( b ) )
168+ }
169+
170+ return hasOwn ( opts , key ) || baseParams === undefined ? supportedEncodings : baseParams . encodings
171+ }
172+
173+ function getCompressStream ( customZlib , params ) {
174+ const compressStream = {
175+ br : ( ) => ( customZlib . createBrotliCompress || zlib . createBrotliCompress ) ( params . brotliOptions ) ,
176+ gzip : ( ) => ( customZlib . createGzip || zlib . createGzip ) ( params . zlibOptions ) ,
177+ deflate : ( ) => ( customZlib . createDeflate || zlib . createDeflate ) ( params . zlibOptions )
178+ }
179+
180+ if ( typeof ( customZlib . createZstdCompress || zlib . createZstdCompress ) === 'function' ) compressStream . zstd = ( ) => ( customZlib . createZstdCompress || zlib . createZstdCompress ) ( params . zlibOptions )
181+
182+ return compressStream
183+ }
184+
185+ function getUncompressStream ( customZlib , params ) {
186+ const uncompressStream = {
187+ // Currently uncompressStream.br() is never called as we do not have any way to autodetect brotli compression in `fastify-compress`
154188 // Brotli documentation reference: [RFC 7932](https://www.rfc-editor.org/rfc/rfc7932)
155- br : /* c8 ignore next */ ( ) => ( ( opts . zlib || zlib ) . createBrotliDecompress || zlib . createBrotliDecompress ) ( params . brotliOptions ) ,
156- gzip : ( ) => ( ( opts . zlib || zlib ) . createGunzip || zlib . createGunzip ) ( params . zlibOptions ) ,
157- deflate : ( ) => ( ( opts . zlib || zlib ) . createInflate || zlib . createInflate ) ( params . zlibOptions )
189+ br : /* c8 ignore next */ ( ) => ( customZlib . createBrotliDecompress || zlib . createBrotliDecompress ) ( params . brotliOptions ) ,
190+ gzip : ( ) => ( customZlib . createGunzip || zlib . createGunzip ) ( params . zlibOptions ) ,
191+ deflate : ( ) => ( customZlib . createInflate || zlib . createInflate ) ( params . zlibOptions )
158192 }
159- if ( typeof ( ( opts . zlib || zlib ) . createZstdDecompress || zlib . createZstdDecompress ) === 'function' ) {
160- // Currently params.uncompressStream.zstd() is never called as we do not have any way to autodetect zstd compression in `fastify-compress`
193+
194+ if ( typeof ( customZlib . createZstdDecompress || zlib . createZstdDecompress ) === 'function' ) {
195+ // Currently uncompressStream.zstd() is never called as we do not have any way to autodetect zstd compression in `fastify-compress`
161196 // Zstd documentation reference: [RFC 8878](https://www.rfc-editor.org/rfc/rfc8878)
162- params . uncompressStream . zstd = /* c8 ignore next */ ( ) => ( ( opts . zlib || zlib ) . createZstdDecompress || zlib . createZstdDecompress ) ( params . zlibOptions )
197+ uncompressStream . zstd = /* c8 ignore next */ ( ) => ( customZlib . createZstdDecompress || zlib . createZstdDecompress ) ( params . zlibOptions )
163198 }
164199
165- const supportedEncodings = [ 'br' , 'gzip' , 'deflate' , 'identity' ]
166- if ( typeof zlib . createZstdCompress === 'function' ) {
167- supportedEncodings . unshift ( 'zstd' )
200+ return uncompressStream
201+ }
202+
203+ function getDecompressStream ( customZlib ) {
204+ const decompressStream = {
205+ br : customZlib . createBrotliDecompress || zlib . createBrotliDecompress ,
206+ gzip : customZlib . createGunzip || zlib . createGunzip ,
207+ deflate : customZlib . createInflate || zlib . createInflate
168208 }
169209
170- params . encodings = Array . isArray ( opts . encodings )
171- ? supportedEncodings
172- . filter ( encoding => opts . encodings . includes ( encoding ) )
173- . sort ( ( a , b ) => opts . encodings . indexOf ( a ) - opts . encodings . indexOf ( b ) )
174- : supportedEncodings
210+ if ( typeof ( customZlib . createZstdDecompress || zlib . createZstdDecompress ) === 'function' ) decompressStream . zstd = customZlib . createZstdDecompress || zlib . createZstdDecompress
175211
176- return params
212+ return decompressStream
177213}
178214
179- function processDecompressParams ( opts ) {
215+ const forceEncodingOrBase = ( opts , baseParams ) => hasOwn ( opts , 'forceRequestEncoding' )
216+ ? opts . forceRequestEncoding || null
217+ : baseOrDefault ( baseParams , 'forceEncoding' , null )
218+
219+ function processCompressParams ( opts , baseParams ) {
180220 /* c8 ignore next 3 */
181221 if ( ! opts ) {
182- return
222+ return baseParams
183223 }
184224
185- const customZlib = opts . zlib || zlib
186-
225+ const customZlib = zlibOrBase ( opts , baseParams )
187226 const params = {
188- global : ( typeof opts . globalDecompression === 'boolean' )
189- ? opts . globalDecompression
190- : ( typeof opts . global === 'boolean' ) ? opts . global : true ,
191- onUnsupportedRequestEncoding : opts . onUnsupportedRequestEncoding ,
192- onInvalidRequestPayload : opts . onInvalidRequestPayload ,
193- decompressStream : {
194- br : customZlib . createBrotliDecompress || zlib . createBrotliDecompress ,
195- gzip : customZlib . createGunzip || zlib . createGunzip ,
196- deflate : customZlib . createInflate || zlib . createInflate
197- } ,
198- encodings : [ ] ,
199- forceEncoding : null
200- }
201- if ( typeof ( customZlib . createZstdDecompress || zlib . createZstdDecompress ) === 'function' ) {
202- params . decompressStream . zstd = customZlib . createZstdDecompress || zlib . createZstdDecompress
227+ zlib : customZlib ,
228+ global : globalOptionOrBase ( opts , 'globalCompression' , baseParams ) ,
229+ removeContentLengthHeader : booleanOptionOrBase ( opts , 'removeContentLengthHeader' , baseParams , true ) ,
230+ zlibOptions : optionOrBase ( opts , 'zlibOptions' , baseParams ) ,
231+ onUnsupportedEncoding : optionOrBase ( opts , 'onUnsupportedEncoding' , baseParams ) ,
232+ inflateIfDeflated : booleanOptionOrBase ( opts , 'inflateIfDeflated' , baseParams , false ) ,
233+ threshold : numberOptionOrBase ( opts , 'threshold' , baseParams , 1024 ) ,
234+ compressibleTypes : getCompressibleTypes ( opts , baseParams )
203235 }
204236
205- const supportedEncodings = [ 'br' , 'gzip' , 'deflate' , 'identity' ]
206- if ( typeof zlib . createZstdCompress === 'function' ) {
207- supportedEncodings . unshift ( 'zstd' )
237+ params . brotliOptions = getBrotliOptions ( opts , params . global , baseParams )
238+ params . compressStream = getCompressStream ( customZlib , params )
239+ params . uncompressStream = getUncompressStream ( customZlib , params )
240+ params . encodings = encodingsOrBase ( opts , 'encodings' , baseParams )
241+
242+ return params
243+ }
244+
245+ function processDecompressParams ( opts , baseParams ) {
246+ /* c8 ignore next 3 */
247+ if ( ! opts ) {
248+ return baseParams
208249 }
209250
210- params . encodings = Array . isArray ( opts . requestEncodings )
211- ? supportedEncodings
212- . filter ( encoding => opts . requestEncodings . includes ( encoding ) )
213- . sort ( ( a , b ) => opts . requestEncodings . indexOf ( a ) - opts . requestEncodings . indexOf ( b ) )
214- : supportedEncodings
251+ const customZlib = zlibOrBase ( opts , baseParams )
215252
216- if ( opts . forceRequestEncoding ) {
217- params . forceEncoding = opts . forceRequestEncoding
253+ const params = {
254+ zlib : customZlib ,
255+ global : globalOptionOrBase ( opts , 'globalDecompression' , baseParams ) ,
256+ onUnsupportedRequestEncoding : optionOrBase ( opts , 'onUnsupportedRequestEncoding' , baseParams ) ,
257+ onInvalidRequestPayload : optionOrBase ( opts , 'onInvalidRequestPayload' , baseParams ) ,
258+ decompressStream : getDecompressStream ( customZlib ) ,
259+ encodings : encodingsOrBase ( opts , 'requestEncodings' , baseParams ) ,
260+ forceEncoding : forceEncodingOrBase ( opts , baseParams )
261+ }
218262
219- if ( params . encodings . includes ( opts . forceRequestEncoding ) ) {
220- params . encodings = [ opts . forceRequestEncoding ]
221- }
263+ if ( params . forceEncoding && params . encodings . includes ( params . forceEncoding ) ) {
264+ params . encodings = [ params . forceEncoding ]
222265 }
223266
224267 return params
0 commit comments