koa-compress 5.1.0 → 5.1.1
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/HISTORY.md +1 -0
- package/lib/encodings.js +76 -74
- package/lib/index.js +24 -27
- package/package.json +19 -16
package/HISTORY.md
CHANGED
package/lib/encodings.js
CHANGED
|
@@ -3,16 +3,80 @@
|
|
|
3
3
|
const errors = require('http-errors')
|
|
4
4
|
const zlib = require('zlib')
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
class Encodings {
|
|
7
|
+
constructor (options = {}) {
|
|
8
|
+
this.wildcardAcceptEncoding = options.wildcardAcceptEncoding || Encodings.wildcardAcceptEncoding
|
|
9
|
+
this.preferredEncodings = options.preferredEncodings || Encodings.preferredEncodings
|
|
10
|
+
this.reDirective = options.reDirective || Encodings.reDirective
|
|
11
|
+
|
|
12
|
+
this.encodingWeights = new Map()
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
parseAcceptEncoding (acceptEncoding = '*') {
|
|
16
|
+
const { encodingWeights, reDirective } = this
|
|
17
|
+
|
|
18
|
+
acceptEncoding.split(',').forEach((directive) => {
|
|
19
|
+
const match = reDirective.exec(directive)
|
|
20
|
+
if (!match) return // not a supported encoding above
|
|
21
|
+
|
|
22
|
+
const encoding = match[1]
|
|
23
|
+
|
|
24
|
+
// weight must be in [0, 1]
|
|
25
|
+
let weight = match[2] && !isNaN(match[2]) ? parseFloat(match[2], 10) : 1
|
|
26
|
+
weight = Math.max(weight, 0)
|
|
27
|
+
weight = Math.min(weight, 1)
|
|
7
28
|
|
|
8
|
-
|
|
9
|
-
//
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
29
|
+
if (encoding === '*') {
|
|
30
|
+
// set the weights for the default encodings
|
|
31
|
+
this.wildcardAcceptEncoding.forEach((enc) => {
|
|
32
|
+
if (!encodingWeights.has(enc)) encodingWeights.set(enc, weight)
|
|
33
|
+
})
|
|
34
|
+
return
|
|
35
|
+
}
|
|
14
36
|
|
|
15
|
-
|
|
37
|
+
encodingWeights.set(encoding, weight)
|
|
38
|
+
})
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
getPreferredContentEncoding () {
|
|
42
|
+
const {
|
|
43
|
+
encodingWeights,
|
|
44
|
+
preferredEncodings
|
|
45
|
+
} = this
|
|
46
|
+
|
|
47
|
+
// get ordered list of accepted encodings
|
|
48
|
+
const acceptedEncodings = Array.from(encodingWeights.keys())
|
|
49
|
+
// sort by weight
|
|
50
|
+
.sort((a, b) => encodingWeights.get(b) - encodingWeights.get(a))
|
|
51
|
+
// filter by supported encodings
|
|
52
|
+
.filter((encoding) => encoding === 'identity' || typeof Encodings.encodingMethods[encoding] === 'function')
|
|
53
|
+
|
|
54
|
+
// group them by weights
|
|
55
|
+
const weightClasses = new Map()
|
|
56
|
+
acceptedEncodings.forEach((encoding) => {
|
|
57
|
+
const weight = encodingWeights.get(encoding)
|
|
58
|
+
if (!weightClasses.has(weight)) weightClasses.set(weight, new Set())
|
|
59
|
+
weightClasses.get(weight).add(encoding)
|
|
60
|
+
})
|
|
61
|
+
|
|
62
|
+
// search by weight, descending
|
|
63
|
+
const weights = Array.from(weightClasses.keys()).sort((a, b) => b - a)
|
|
64
|
+
for (let i = 0; i < weights.length; i++) {
|
|
65
|
+
// encodings at this weight
|
|
66
|
+
const encodings = weightClasses.get(weights[i])
|
|
67
|
+
// return the first encoding in the preferred list
|
|
68
|
+
for (let j = 0; j < preferredEncodings.length; j++) {
|
|
69
|
+
const preferredEncoding = preferredEncodings[j]
|
|
70
|
+
if (encodings.has(preferredEncoding)) return preferredEncoding
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// no encoding matches, check to see if the client set identity, q=0
|
|
75
|
+
if (encodingWeights.get('identity') === 0) throw errors(406, 'Please accept br, gzip, deflate, or identity.')
|
|
76
|
+
|
|
77
|
+
// by default, return nothing
|
|
78
|
+
return 'identity'
|
|
79
|
+
}
|
|
16
80
|
}
|
|
17
81
|
|
|
18
82
|
Encodings.encodingMethods = {
|
|
@@ -25,7 +89,9 @@ Encodings.encodingMethodDefaultOptions = {
|
|
|
25
89
|
gzip: {},
|
|
26
90
|
deflate: {},
|
|
27
91
|
br: {
|
|
28
|
-
|
|
92
|
+
params: {
|
|
93
|
+
[zlib.constants.BROTLI_PARAM_QUALITY]: 4
|
|
94
|
+
}
|
|
29
95
|
}
|
|
30
96
|
}
|
|
31
97
|
|
|
@@ -35,68 +101,4 @@ Encodings.wildcardAcceptEncoding = ['gzip', 'deflate']
|
|
|
35
101
|
Encodings.preferredEncodings = ['br', 'gzip', 'deflate']
|
|
36
102
|
Encodings.reDirective = /^\s*(gzip|compress|deflate|br|identity|\*)\s*(?:;\s*q\s*=\s*(\d(?:\.\d)?))?\s*$/
|
|
37
103
|
|
|
38
|
-
|
|
39
|
-
const { encodingWeights, reDirective } = this
|
|
40
|
-
|
|
41
|
-
acceptEncoding.split(',').forEach((directive) => {
|
|
42
|
-
const match = reDirective.exec(directive)
|
|
43
|
-
if (!match) return // not a supported encoding above
|
|
44
|
-
|
|
45
|
-
const encoding = match[1]
|
|
46
|
-
|
|
47
|
-
// weight must be in [0, 1]
|
|
48
|
-
let weight = match[2] && !isNaN(match[2]) ? parseFloat(match[2], 10) : 1
|
|
49
|
-
weight = Math.max(weight, 0)
|
|
50
|
-
weight = Math.min(weight, 1)
|
|
51
|
-
|
|
52
|
-
if (encoding === '*') {
|
|
53
|
-
// set the weights for the default encodings
|
|
54
|
-
this.wildcardAcceptEncoding.forEach((enc) => {
|
|
55
|
-
if (!encodingWeights.has(enc)) encodingWeights.set(enc, weight)
|
|
56
|
-
})
|
|
57
|
-
return
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
encodingWeights.set(encoding, weight)
|
|
61
|
-
})
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
Encodings.prototype.getPreferredContentEncoding = function () {
|
|
65
|
-
const {
|
|
66
|
-
encodingWeights,
|
|
67
|
-
preferredEncodings
|
|
68
|
-
} = this
|
|
69
|
-
|
|
70
|
-
// get ordered list of accepted encodings
|
|
71
|
-
const acceptedEncodings = Array.from(encodingWeights.keys())
|
|
72
|
-
// sort by weight
|
|
73
|
-
.sort((a, b) => encodingWeights.get(b) - encodingWeights.get(a))
|
|
74
|
-
// filter by supported encodings
|
|
75
|
-
.filter((encoding) => encoding === 'identity' || typeof Encodings.encodingMethods[encoding] === 'function')
|
|
76
|
-
|
|
77
|
-
// group them by weights
|
|
78
|
-
const weightClasses = new Map()
|
|
79
|
-
acceptedEncodings.forEach((encoding) => {
|
|
80
|
-
const weight = encodingWeights.get(encoding)
|
|
81
|
-
if (!weightClasses.has(weight)) weightClasses.set(weight, new Set())
|
|
82
|
-
weightClasses.get(weight).add(encoding)
|
|
83
|
-
})
|
|
84
|
-
|
|
85
|
-
// search by weight, descending
|
|
86
|
-
const weights = Array.from(weightClasses.keys()).sort((a, b) => b - a)
|
|
87
|
-
for (let i = 0; i < weights.length; i++) {
|
|
88
|
-
// encodings at this weight
|
|
89
|
-
const encodings = weightClasses.get(weights[i])
|
|
90
|
-
// return the first encoding in the preferred list
|
|
91
|
-
for (let j = 0; j < preferredEncodings.length; j++) {
|
|
92
|
-
const preferredEncoding = preferredEncodings[j]
|
|
93
|
-
if (encodings.has(preferredEncoding)) return preferredEncoding
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
// no encoding matches, check to see if the client set identity, q=0
|
|
98
|
-
if (encodingWeights.get('identity') === 0) throw errors(406, 'Please accept br, gzip, deflate, or identity.')
|
|
99
|
-
|
|
100
|
-
// by default, return nothing
|
|
101
|
-
return 'identity'
|
|
102
|
-
}
|
|
104
|
+
module.exports = Encodings
|
package/lib/index.js
CHANGED
|
@@ -6,7 +6,6 @@
|
|
|
6
6
|
|
|
7
7
|
const compressible = require('compressible')
|
|
8
8
|
const isJSON = require('koa-is-json')
|
|
9
|
-
const status = require('statuses')
|
|
10
9
|
const Stream = require('stream')
|
|
11
10
|
const bytes = require('bytes')
|
|
12
11
|
|
|
@@ -17,6 +16,11 @@ const Encodings = require('./encodings')
|
|
|
17
16
|
*/
|
|
18
17
|
const NO_TRANSFORM_REGEX = /(?:^|,)\s*?no-transform\s*?(?:,|$)/
|
|
19
18
|
|
|
19
|
+
/**
|
|
20
|
+
* empty body statues.
|
|
21
|
+
*/
|
|
22
|
+
const emptyBodyStatues = new Set([204, 205, 304])
|
|
23
|
+
|
|
20
24
|
/**
|
|
21
25
|
* Compress middleware.
|
|
22
26
|
*
|
|
@@ -51,30 +55,26 @@ module.exports = (options = {}) => {
|
|
|
51
55
|
|
|
52
56
|
await next()
|
|
53
57
|
|
|
54
|
-
// early exit if there's no content body or the body is already encoded
|
|
55
58
|
let { body } = ctx
|
|
56
|
-
if (
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
if (threshold && ctx.response.length < threshold) return
|
|
59
|
+
if (
|
|
60
|
+
// early exit if there's no content body or the body is already encoded
|
|
61
|
+
!body ||
|
|
62
|
+
ctx.res.headersSent || !ctx.writable ||
|
|
63
|
+
ctx.compress === false ||
|
|
64
|
+
ctx.request.method === 'HEAD' ||
|
|
65
|
+
emptyBodyStatues.has(+ctx.response.status) ||
|
|
66
|
+
ctx.response.get('Content-Encoding') ||
|
|
67
|
+
// forced compression or implied
|
|
68
|
+
!(ctx.compress === true || filter(ctx.response.type)) ||
|
|
69
|
+
// don't compress for Cache-Control: no-transform
|
|
70
|
+
// https://tools.ietf.org/html/rfc7234#section-5.2.1.6
|
|
71
|
+
NO_TRANSFORM_REGEX.test(ctx.response.get('Cache-Control')) ||
|
|
72
|
+
// don't compress if the current response is below the threshold
|
|
73
|
+
(threshold && ctx.response.length < threshold)
|
|
74
|
+
) return
|
|
73
75
|
|
|
74
76
|
// get the preferred content encoding
|
|
75
|
-
const encodings = new Encodings({
|
|
76
|
-
preferredEncodings
|
|
77
|
-
})
|
|
77
|
+
const encodings = new Encodings({ preferredEncodings })
|
|
78
78
|
encodings.parseAcceptEncoding(ctx.request.headers['accept-encoding'] || defaultEncoding)
|
|
79
79
|
const encoding = encodings.getPreferredContentEncoding()
|
|
80
80
|
|
|
@@ -92,10 +92,7 @@ module.exports = (options = {}) => {
|
|
|
92
92
|
const compress = Encodings.encodingMethods[encoding]
|
|
93
93
|
const stream = ctx.body = compress(encodingOptions[encoding])
|
|
94
94
|
|
|
95
|
-
if (body instanceof Stream)
|
|
96
|
-
|
|
97
|
-
} else {
|
|
98
|
-
stream.end(body)
|
|
99
|
-
}
|
|
95
|
+
if (body instanceof Stream) return body.pipe(stream)
|
|
96
|
+
stream.end(body)
|
|
100
97
|
}
|
|
101
98
|
}
|
package/package.json
CHANGED
|
@@ -1,32 +1,38 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "koa-compress",
|
|
3
3
|
"description": "Compress middleware for koa",
|
|
4
|
-
"version": "5.1.
|
|
4
|
+
"version": "5.1.1",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Jonathan Ong",
|
|
7
7
|
"email": "me@jongleberry.com",
|
|
8
8
|
"url": "http://jongleberry.com",
|
|
9
9
|
"twitter": "https://twitter.com/jongleberry"
|
|
10
10
|
},
|
|
11
|
+
"contributors": [
|
|
12
|
+
{
|
|
13
|
+
"name": "Imed Jaberi",
|
|
14
|
+
"email": "imed-jaberi@outlook.com",
|
|
15
|
+
"url": "https://www.3imed-jaberi.com/"
|
|
16
|
+
}
|
|
17
|
+
],
|
|
11
18
|
"license": "MIT",
|
|
12
19
|
"repository": "koajs/compress",
|
|
13
20
|
"dependencies": {
|
|
14
|
-
"bytes": "^3.
|
|
15
|
-
"compressible": "^2.0.
|
|
16
|
-
"http-errors": "^1.8.
|
|
17
|
-
"koa-is-json": "^1.0.0"
|
|
18
|
-
"statuses": "^2.0.1"
|
|
21
|
+
"bytes": "^3.1.2",
|
|
22
|
+
"compressible": "^2.0.18",
|
|
23
|
+
"http-errors": "^1.8.1",
|
|
24
|
+
"koa-is-json": "^1.0.0"
|
|
19
25
|
},
|
|
20
26
|
"devDependencies": {
|
|
21
27
|
"eslint": "^7.32.0",
|
|
22
28
|
"eslint-config-standard": "^16.0.3",
|
|
23
|
-
"eslint-plugin-import": "^2.
|
|
24
|
-
"eslint-plugin-node": "^11.
|
|
25
|
-
"eslint-plugin-promise": "^5.
|
|
29
|
+
"eslint-plugin-import": "^2.26.0",
|
|
30
|
+
"eslint-plugin-node": "^11.1.0",
|
|
31
|
+
"eslint-plugin-promise": "^5.2.0",
|
|
26
32
|
"eslint-plugin-standard": "^5.0.0",
|
|
27
|
-
"jest": "^27.
|
|
28
|
-
"koa": "^2.13.
|
|
29
|
-
"supertest": "^6.
|
|
33
|
+
"jest": "^27.5.1",
|
|
34
|
+
"koa": "^2.13.4",
|
|
35
|
+
"supertest": "^6.2.4"
|
|
30
36
|
},
|
|
31
37
|
"scripts": {
|
|
32
38
|
"eslint": "eslint --ignore-path .gitignore .",
|
|
@@ -38,10 +44,7 @@
|
|
|
38
44
|
"HISTORY.md"
|
|
39
45
|
],
|
|
40
46
|
"main": "lib",
|
|
41
|
-
"jest": {
|
|
42
|
-
"testEnvironment": "node"
|
|
43
|
-
},
|
|
44
47
|
"engines": {
|
|
45
|
-
"node": ">=
|
|
48
|
+
"node": ">= 12"
|
|
46
49
|
}
|
|
47
50
|
}
|