-
-
Notifications
You must be signed in to change notification settings - Fork 46
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #1 from fastify/init
Initial release
- Loading branch information
Showing
6 changed files
with
767 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
language: node_js | ||
|
||
node_js: | ||
- "9" | ||
- "8" | ||
- "6" | ||
- "4" | ||
|
||
notifications: | ||
email: | ||
on_success: never | ||
on_failure: always |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,79 @@ | ||
# fastify-compress | ||
# fastify-compress | ||
|
||
[![Build Status](https://travis-ci.org/fastify/fastify-compress.svg?branch=master)](https://travis-ci.org/fastify/fastify-compress) [![js-standard-style](https://img.shields.io/badge/code%20style-standard-brightgreen.svg?style=flat)](http://standardjs.com/) | ||
|
||
Adds compression utils to the Fastify `reply` object. | ||
Support `gzip`, `deflate` and `brotli`. | ||
|
||
## Install | ||
``` | ||
npm i fastify-compress --save | ||
``` | ||
|
||
## Usage | ||
This plugins adds two functionalities to Fastify, a compress utility and a global compression hook. | ||
|
||
Currently the following headers are supported: | ||
- `'deflate'` | ||
- `'gzip'` | ||
- `'br'` | ||
|
||
If an unsupported encoding is received, it will automatically return a `406` error, if the `'accept-encoding'` header is missing, it will return a `400` error. | ||
|
||
It automatically defines if a payload should be compressed or not based on its `Content-Type`, if no content type is present, it will assume is `application/json`. | ||
|
||
### Global hook | ||
The global compression hook is enabled by default if you want to disable it, pass the option `{ global: false }`. | ||
```javascript | ||
fastify.register( | ||
require('fastify-compress'), | ||
{ global: false } | ||
) | ||
``` | ||
Remember that thanks to the Fastify encapsulation model, you can set a global compression, but running it only in a subset of routes is you wrap them inside a plugin. | ||
|
||
### `reply.compress` | ||
This plugin add a `compress` function to `reply` that accepts a stream or a string and compress it based on the `'accept-encoding'` header. If a js object is passed in, will be stringified as json. | ||
|
||
```javascript | ||
const fs = require('fs') | ||
const fastify = require('fastify') | ||
|
||
fastify.register(require('fastify-compress'), { global: false }) | ||
|
||
fastify.get('/', (req, reply) => { | ||
reply | ||
.type('text/plain') | ||
.compress(fs.createReadStream('./package.json')) | ||
}) | ||
|
||
fastify.listen(3000, function (err) { | ||
if (err) throw err | ||
console.log(`server listening on ${fastify.server.address().port}`) | ||
}) | ||
``` | ||
## Options | ||
### Threshold | ||
You can set a custom threshold below which it will not be made compression, default to `1024`. | ||
```javascript | ||
fastify.register( | ||
require('fastify-compress'), | ||
{ threshold: 2048 } | ||
) | ||
``` | ||
### Brotli | ||
Brotli compression is not enabled by default, if you need it we recommend to install [`iltorb`](https://www.npmjs.com/package/iltorb) and pass it as option. | ||
```javascript | ||
fastify.register( | ||
require('fastify-compress'), | ||
{ brotli: require('iltorb') } | ||
) | ||
``` | ||
|
||
## Acknowledgements | ||
This project is kindly sponsored by: | ||
- [LetzDoIt](http://www.letzdoitapp.com/) | ||
|
||
## License | ||
|
||
Licensed under [MIT](./LICENSE). |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,169 @@ | ||
'use strict' | ||
|
||
const fp = require('fastify-plugin') | ||
const zlib = require('zlib') | ||
const pump = require('pump') | ||
const sts = require('string-to-stream') | ||
const mimedb = require('mime-db') | ||
const supportedEncodings = ['deflate', 'gzip', 'br', 'identity'] | ||
const compressibleTypes = /^text\/|\+json$|\+text$|\+xml$/ | ||
|
||
function compressPlugin (fastify, opts, next) { | ||
fastify.decorateReply('compress', compress) | ||
|
||
if (opts.global !== false) { | ||
fastify.addHook('onSend', onSend) | ||
} | ||
|
||
const threshold = typeof opts.threshold === 'number' ? opts.threshold : 1024 | ||
const compressStream = { | ||
gzip: zlib.createGzip, | ||
deflate: zlib.createDeflate | ||
} | ||
|
||
if (opts.brotli) { | ||
compressStream.br = opts.brotli.compressStream | ||
} | ||
|
||
next() | ||
|
||
function compress (payload) { | ||
if (!payload) { | ||
this.res.log.warn('compress: missing payload') | ||
this.send(new Error('Internal server error')) | ||
return | ||
} | ||
|
||
if (this.request.headers['x-no-compression'] !== undefined) { | ||
return this.send(payload) | ||
} | ||
|
||
var type = this.res.getHeader('Content-Type') || 'application/json' | ||
if (shouldCompress(type) === false) { | ||
return this.send(payload) | ||
} | ||
|
||
var encoding = getEncodingHeader(this.request) | ||
|
||
if (encoding === undefined) { | ||
closeStream(payload) | ||
this.code(400).send(new Error('Missing `accept encoding` header')) | ||
return | ||
} | ||
|
||
if (encoding === 'identity') { | ||
return this.send(payload) | ||
} | ||
|
||
if (encoding === null) { | ||
closeStream(payload) | ||
this.code(406).send(new Error('Unsupported encoding')) | ||
return | ||
} | ||
|
||
if (payload._readableState === undefined) { | ||
if (typeof payload !== 'string') { | ||
payload = this.serialize(payload) | ||
} | ||
if (Buffer.byteLength(payload) < threshold) { | ||
return this.send(payload) | ||
} | ||
payload = sts(payload) | ||
} | ||
|
||
this.header('Content-Encoding', encoding) | ||
this.send(pump( | ||
payload, | ||
compressStream[encoding](), | ||
onEnd.bind(this)) | ||
) | ||
} | ||
|
||
function onSend (req, reply, payload, next) { | ||
if (!payload) { | ||
reply.res.log.warn('compress: missing payload') | ||
return next() | ||
} | ||
|
||
if (req.headers['x-no-compression'] !== undefined) { | ||
return next() | ||
} | ||
|
||
var type = reply.res.getHeader('Content-Type') || 'application/json' | ||
if (shouldCompress(type) === false) { | ||
return next() | ||
} | ||
|
||
var encoding = getEncodingHeader(req) | ||
|
||
if (encoding === undefined) { | ||
closeStream(payload) | ||
reply.code(400) | ||
next(new Error('Missing `accept encoding` header')) | ||
return | ||
} | ||
|
||
if (encoding === null) { | ||
closeStream(payload) | ||
reply.code(406) | ||
next(new Error('Unsupported encoding')) | ||
return | ||
} | ||
|
||
if (encoding === 'identity') { | ||
return next() | ||
} | ||
|
||
if (payload._readableState === undefined) { | ||
if (typeof payload !== 'string') { | ||
payload = reply.serialize(payload) | ||
} | ||
if (Buffer.byteLength(payload) < threshold) { | ||
return next() | ||
} | ||
payload = sts(payload) | ||
} | ||
|
||
reply.header('Content-Encoding', encoding) | ||
next(null, pump( | ||
payload, | ||
compressStream[encoding](), | ||
onEnd.bind(reply)) | ||
) | ||
} | ||
} | ||
|
||
function onEnd (err) { | ||
if (err) this.res.log.error(err) | ||
} | ||
|
||
function closeStream (payload) { | ||
if (typeof payload.close === 'function') { | ||
payload.close() | ||
} else if (typeof payload.destroy === 'function') { | ||
payload.destroy() | ||
} else if (typeof payload.abort === 'function') { | ||
payload.abort() | ||
} | ||
} | ||
|
||
function getEncodingHeader (request) { | ||
var header = request.headers['accept-encoding'] | ||
if (!header) return undefined | ||
var acceptEncodings = header.split(',') | ||
for (var i = 0; i < acceptEncodings.length; i++) { | ||
if (supportedEncodings.indexOf(acceptEncodings[i]) > -1) { | ||
return acceptEncodings[i] | ||
} | ||
} | ||
return null | ||
} | ||
|
||
function shouldCompress (type) { | ||
if (compressibleTypes.test(type)) return true | ||
var data = mimedb[type.split(';', 1)[0].trim().toLowerCase()] | ||
if (data === undefined) return false | ||
return data.compressible | ||
} | ||
|
||
module.exports = fp(compressPlugin, '>=0.20.0') |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
{ | ||
"name": "fastify-compress", | ||
"version": "0.1.0", | ||
"description": "Fastify compression utils", | ||
"main": "index.js", | ||
"dependencies": { | ||
"fastify-plugin": "^0.1.1", | ||
"mime-db": "^1.31.0", | ||
"pump": "^1.0.3", | ||
"string-to-stream": "^1.1.0" | ||
}, | ||
"devDependencies": { | ||
"fastify": "^0.35.0", | ||
"iltorb": "^2.0.2", | ||
"standard": "^10.0.3", | ||
"tap": "^10.7.3" | ||
}, | ||
"scripts": { | ||
"test": "standard && tap test.js" | ||
}, | ||
"keywords": [ | ||
"fastify", | ||
"compression", | ||
"deflate", | ||
"gzip", | ||
"brodli" | ||
], | ||
"author": "Tomas Della Vedova - @delvedor (http://delved.org)", | ||
"license": "MIT", | ||
"bugs": { | ||
"url": "https://github.com/fastify/fastify-compress/issues" | ||
}, | ||
"homepage": "https://github.com/fastify/fastify-compress#readme", | ||
"repository": { | ||
"type": "git", | ||
"url": "git+https://github.com/fastify/fastify-compress.git" | ||
} | ||
} |
Oops, something went wrong.