Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

fastify-compress

Package Overview
Dependencies
Maintainers
7
Versions
38
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

fastify-compress - npm Package Compare versions

Comparing version 0.7.1 to 0.8.0

131

index.js

@@ -7,3 +7,11 @@ 'use strict'

const mimedb = require('mime-db')
const isStream = require('is-stream')
const intoStream = require('into-stream')
const peek = require('peek-stream')
const Minipass = require('minipass')
const pumpify = require('pumpify')
const isGzip = require('is-gzip')
const isZip = require('is-zip')
const unZipper = require('unzipper')
const isDeflate = require('is-deflate')

@@ -17,8 +25,13 @@ function compressPlugin (fastify, opts, next) {

const inflateIfDeflated = opts.inflateIfDeflated === true
const threshold = typeof opts.threshold === 'number' ? opts.threshold : 1024
const compressibleTypes = opts.customTypes instanceof RegExp ? opts.customTypes : /^text\/|\+json$|\+text$|\+xml$|octet-stream$/
const compressStream = {
gzip: zlib.createGzip,
deflate: zlib.createDeflate
gzip: (opts.zlib || zlib).createGzip || zlib.createGzip,
deflate: (opts.zlib || zlib).createDeflate || zlib.createDeflate
}
const uncompressStream = {
gzip: (opts.zlib || zlib).createGunzip || zlib.createGunzip,
deflate: (opts.zlib || zlib).createInflate || zlib.createInflate
}

@@ -40,17 +53,21 @@ const supportedEncodings = ['deflate', 'gzip', 'identity']

if (this.request.headers['x-no-compression'] !== undefined) {
return this.send(payload)
}
var stream, encoding
var noCompress =
// don't compress on x-no-compression header
(this.request.headers['x-no-compression'] !== undefined) ||
// don't compress if not one of the indiated compressible types
(shouldCompress(this.getHeader('Content-Type') || 'application/json', compressibleTypes) === false) ||
// don't compress on missing or identity `accept-encoding` header
((encoding = getEncodingHeader(supportedEncodings, this.request)) === undefined || encoding === 'identity')
var type = this.getHeader('Content-Type') || 'application/json'
if (shouldCompress(type, compressibleTypes) === false) {
if (noCompress) {
if (inflateIfDeflated && isStream(stream = maybeUnzip(payload, this.serialize.bind(this)))) {
encoding === undefined
? this.removeHeader('Content-Encoding')
: this.header('Content-Encoding', 'identity')
pump(stream, payload = unzipStream(uncompressStream), onEnd.bind(this))
}
return this.send(payload)
}
var encoding = getEncodingHeader(supportedEncodings, this.request)
if (encoding === undefined || encoding === 'identity') {
return this.send(payload)
}
if (encoding === null) {

@@ -66,2 +83,5 @@ closeStream(payload)

}
}
if (typeof payload.pipe !== 'function') {
if (Buffer.byteLength(payload) < threshold) {

@@ -77,3 +97,3 @@ return this.send(payload)

var stream = compressStream[encoding]()
stream = zipStream(compressStream, encoding)
pump(payload, stream, onEnd.bind(this))

@@ -89,13 +109,21 @@ this.send(stream)

if (req.headers['x-no-compression'] !== undefined) {
return next()
}
var stream, encoding
var noCompress =
// don't compress on x-no-compression header
(req.headers['x-no-compression'] !== undefined) ||
// don't compress if not one of the indiated compressible types
(shouldCompress(reply.getHeader('Content-Type') || 'application/json', compressibleTypes) === false) ||
// don't compress on missing or identity `accept-encoding` header
((encoding = getEncodingHeader(supportedEncodings, req)) === undefined || encoding === 'identity')
var type = reply.getHeader('Content-Type') || 'application/json'
if (shouldCompress(type, compressibleTypes) === false) {
return next()
if (noCompress) {
if (inflateIfDeflated && isStream(stream = maybeUnzip(payload))) {
encoding === undefined
? reply.removeHeader('Content-Encoding')
: reply.header('Content-Encoding', 'identity')
pump(stream, payload = unzipStream(uncompressStream), onEnd.bind(reply))
}
return next(null, payload)
}
var encoding = getEncodingHeader(supportedEncodings, req)
if (encoding === null) {

@@ -108,6 +136,2 @@ closeStream(payload)

if (encoding === undefined || encoding === 'identity') {
return next()
}
if (typeof payload.pipe !== 'function') {

@@ -123,3 +147,4 @@ if (Buffer.byteLength(payload) < threshold) {

.removeHeader('content-length')
var stream = compressStream[encoding]()
stream = zipStream(compressStream, encoding)
pump(payload, stream, onEnd.bind(reply))

@@ -167,2 +192,54 @@ next(null, stream)

function isCompressed (data) {
if (isGzip(data)) return 1
if (isDeflate(data)) return 2
if (isZip(data)) return 3
return 0
}
function maybeUnzip (payload, serialize) {
if (isStream(payload)) return payload
var buf = payload; var result = payload
if (ArrayBuffer.isView(payload)) {
// Cast non-Buffer DataViews into a Buffer
buf = result = Buffer.from(
payload.buffer,
payload.byteOffset,
payload.byteLength
)
} else if (serialize && typeof payload !== 'string') {
buf = result = serialize(payload)
}
// handle case where serialize doesn't return a string or Buffer
if (!Buffer.isBuffer(buf)) return result
if (isCompressed(buf) === 0) return result
return intoStream(result)
}
function zipStream (deflate, encoding) {
return peek({ newline: false, maxBuffer: 10 }, function (data, swap) {
switch (isCompressed(data)) {
case 1: return swap(null, new Minipass())
case 2: return swap(null, new Minipass())
}
return swap(null, deflate[encoding]())
})
}
function unzipStream (inflate, maxRecursion) {
if (!(maxRecursion >= 0)) maxRecursion = 3
return peek({ newline: false, maxBuffer: 10 }, function (data, swap) {
if (maxRecursion < 0) return swap(new Error('Maximum recursion reached'))
switch (isCompressed(data)) {
case 1: return swap(null, pumpify(inflate.gzip(), unzipStream(inflate, maxRecursion - 1)))
case 2: return swap(null, pumpify(inflate.deflate(), unzipStream(inflate, maxRecursion - 1)))
case 3: return swap(null, pumpify(unZipper.ParseOne(), unzipStream(inflate, maxRecursion - 1)))
}
return swap(null, new Minipass())
})
}
module.exports = fp(compressPlugin, {

@@ -169,0 +246,0 @@ fastify: '>=1.3.0',

{
"name": "fastify-compress",
"version": "0.7.1",
"version": "0.8.0",
"description": "Fastify compression utils",

@@ -9,4 +9,13 @@ "main": "index.js",

"into-stream": "^4.0.0",
"is-deflate": "^1.0.0",
"is-gzip": "^1.0.0",
"is-stream": "^1.1.0",
"is-zip": "^1.0.0",
"mime-db": "^1.33.0",
"pump": "^3.0.0"
"minipass": "2.3.5",
"peek-stream": "^1.1.0",
"pump": "^3.0.0",
"pumpify": "^1.3.3",
"string-to-stream": "^1.1.0",
"unzipper": "^0.8.9"
},

@@ -13,0 +22,0 @@ "devDependencies": {

@@ -89,2 +89,16 @@ # fastify-compress

### Inflate pre-compressed bodies for clients that do not support compression
Optional feature to inflate pre-compressed data if the client doesn't include one of the supported compression types in its `Accept-Encoding` header.
```javascript
fastify.register(
require('fastify-compress'),
{ inflateIfDeflated: true }
)
fastify.get('/file', (req, reply) =>
// will inflate the file on the way out for clients
// that indicate they do not support compression
reply.send(fs.createReadStream('./file.gz')))
```
## Note

@@ -91,0 +105,0 @@ Please have in mind that in large scale scenarios, you should use a proxy like Nginx to handle response-compression.

@@ -39,2 +39,31 @@ 'use strict'

test('should send a deflated data with custom deflate', t => {
t.plan(5)
let usedCustom = false
const customZlib = { createDeflate: () => (usedCustom = true) && zlib.createDeflate() }
const fastify = Fastify()
fastify.register(compressPlugin, { global: false, zlib: customZlib })
fastify.get('/', (req, reply) => {
reply.type('text/plain').compress(createReadStream('./package.json'))
})
fastify.inject({
url: '/',
method: 'GET',
headers: {
'accept-encoding': 'deflate'
}
}, (err, res) => {
t.error(err)
t.strictEqual(res.headers['content-encoding'], 'deflate')
t.notOk(res.headers['content-length'], 'no content length')
const file = readFileSync('./package.json', 'utf8')
const payload = zlib.inflateSync(res.rawPayload)
t.strictEqual(payload.toString('utf-8'), file)
t.strictEqual(usedCustom, true)
})
})
test('should send a gzipped data', t => {

@@ -64,2 +93,83 @@ t.plan(3)

test('should send a gzipped data with custom zlib', t => {
t.plan(4)
let usedCustom = false
const customZlib = { createGzip: () => (usedCustom = true) && zlib.createGzip() }
const fastify = Fastify()
fastify.register(compressPlugin, { global: false, zlib: customZlib })
fastify.get('/', (req, reply) => {
reply.type('text/plain').compress(createReadStream('./package.json'))
})
fastify.inject({
url: '/',
method: 'GET',
headers: {
'accept-encoding': 'gzip'
}
}, (err, res) => {
t.error(err)
t.strictEqual(res.headers['content-encoding'], 'gzip')
const file = readFileSync('./package.json', 'utf8')
const payload = zlib.gunzipSync(res.rawPayload)
t.strictEqual(payload.toString('utf-8'), file)
t.strictEqual(usedCustom, true)
})
})
test('should not double-compress Stream if already zipped', t => {
t.plan(3)
const fastify = Fastify()
fastify.register(compressPlugin, { global: false })
fastify.get('/', (req, reply) => {
reply.type('text/plain').compress(
createReadStream('./package.json')
.pipe(zlib.createGzip())
)
})
fastify.inject({
url: '/',
method: 'GET',
headers: {
'accept-encoding': 'gzip'
}
}, (err, res) => {
t.error(err)
t.strictEqual(res.headers['content-encoding'], 'gzip')
const file = readFileSync('./package.json', 'utf8')
const payload = zlib.gunzipSync(res.rawPayload)
t.strictEqual(payload.toString('utf-8'), file)
})
})
test('onSend hook should not double-compress Stream if already zipped', t => {
t.plan(3)
const fastify = Fastify()
fastify.register(compressPlugin, { global: true })
fastify.get('/', (req, reply) => {
reply.type('text/plain').compress(
createReadStream('./package.json')
.pipe(zlib.createGzip())
)
})
fastify.inject({
url: '/',
method: 'GET',
headers: {
'accept-encoding': 'gzip'
}
}, (err, res) => {
t.error(err)
t.strictEqual(res.headers['content-encoding'], 'gzip')
const file = readFileSync('./package.json', 'utf8')
const payload = zlib.gunzipSync(res.rawPayload)
t.strictEqual(payload.toString('utf-8'), file)
})
})
test('should send a gzipped data for * header', t => {

@@ -183,2 +293,67 @@ t.plan(3)

test('should decompress compressed Buffers on missing header', t => {
t.plan(4)
const fastify = Fastify()
fastify.register(compressPlugin, { threshold: 0, inflateIfDeflated: true })
const json = { hello: 'world' }
fastify.get('/', (req, reply) => {
reply.send(zlib.gzipSync(JSON.stringify(json)))
})
fastify.inject({
url: '/',
method: 'GET'
}, (err, res) => {
t.error(err)
t.strictEqual(res.statusCode, 200)
t.notOk(res.headers['content-encoding'])
t.deepEqual(JSON.parse('' + res.payload), json)
})
})
test('should decompress data that has been compressed multiple times on missing header', t => {
t.plan(4)
const fastify = Fastify()
fastify.register(compressPlugin, { threshold: 0, inflateIfDeflated: true })
const json = { hello: 'world' }
fastify.get('/', (req, reply) => {
reply.send([0, 1, 2, 3, 4, 5, 6].reduce(
(x) => zlib.gzipSync(x), JSON.stringify(json)
))
})
fastify.inject({
url: '/',
method: 'GET'
}, (err, res) => {
t.error(err)
t.strictEqual(res.statusCode, 200)
t.notOk(res.headers['content-encoding'])
t.deepEqual(JSON.parse('' + res.payload), json)
})
})
test('should decompress compressed Streams on missing header', t => {
t.plan(4)
const fastify = Fastify()
fastify.register(compressPlugin, { threshold: 0, inflateIfDeflated: true })
fastify.get('/', (req, reply) => {
reply.send(createReadStream('./package.json').pipe(zlib.createGzip()))
})
fastify.inject({
url: '/',
method: 'GET'
}, (err, res) => {
t.error(err)
t.strictEqual(res.statusCode, 200)
t.notOk(res.headers['content-encoding'])
const file = readFileSync('./package.json', 'utf8')
t.strictEqual(res.rawPayload.toString('utf-8'), file)
})
})
test('Should close the stream', t => {

@@ -601,2 +776,26 @@ t.plan(4)

test('Should decompress compressed payloads on x-no-compression header', t => {
t.plan(4)
const fastify = Fastify()
fastify.register(compressPlugin, { threshold: 0, inflateIfDeflated: true })
const json = { hello: 'world' }
fastify.get('/', (req, reply) => {
reply.send(zlib.gzipSync(JSON.stringify(json)))
})
fastify.inject({
url: '/',
method: 'GET',
headers: {
'x-no-compression': true
}
}, (err, res) => {
t.error(err)
t.strictEqual(res.statusCode, 200)
t.notOk(res.headers['content-encoding'])
t.deepEqual(JSON.parse('' + res.payload), json)
})
})
test('Should not try compress missing payload', t => {

@@ -603,0 +802,0 @@ t.plan(4)

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc