-
Notifications
You must be signed in to change notification settings - Fork 36
/
Copy pathencodings.js
104 lines (85 loc) · 3.3 KB
/
encodings.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Accept-Encoding
const errors = require('http-errors')
const zlib = require('zlib')
class Encodings {
constructor (options = {}) {
this.wildcardAcceptEncoding = options.wildcardAcceptEncoding || Encodings.wildcardAcceptEncoding
this.preferredEncodings = options.preferredEncodings || Encodings.preferredEncodings
this.reDirective = options.reDirective || Encodings.reDirective
this.encodingWeights = new Map()
}
parseAcceptEncoding (acceptEncoding = '*') {
const { encodingWeights, reDirective } = this
acceptEncoding.split(',').forEach((directive) => {
const match = reDirective.exec(directive)
if (!match) return // not a supported encoding above
const encoding = match[1]
// weight must be in [0, 1]
let weight = match[2] && !isNaN(match[2]) ? parseFloat(match[2], 10) : 1
weight = Math.max(weight, 0)
weight = Math.min(weight, 1)
if (encoding === '*') {
// set the weights for the default encodings
this.wildcardAcceptEncoding.forEach((enc) => {
if (!encodingWeights.has(enc)) encodingWeights.set(enc, weight)
})
return
}
encodingWeights.set(encoding, weight)
})
}
getPreferredContentEncoding () {
const {
encodingWeights,
preferredEncodings
} = this
// get ordered list of accepted encodings
const acceptedEncodings = Array.from(encodingWeights.keys())
// sort by weight
.sort((a, b) => encodingWeights.get(b) - encodingWeights.get(a))
// filter by supported encodings
.filter((encoding) => encoding === 'identity' || typeof Encodings.encodingMethods[encoding] === 'function')
// group them by weights
const weightClasses = new Map()
acceptedEncodings.forEach((encoding) => {
const weight = encodingWeights.get(encoding)
if (!weightClasses.has(weight)) weightClasses.set(weight, new Set())
weightClasses.get(weight).add(encoding)
})
// search by weight, descending
const weights = Array.from(weightClasses.keys()).sort((a, b) => b - a)
for (let i = 0; i < weights.length; i++) {
// encodings at this weight
const encodings = weightClasses.get(weights[i])
// return the first encoding in the preferred list
for (let j = 0; j < preferredEncodings.length; j++) {
const preferredEncoding = preferredEncodings[j]
if (encodings.has(preferredEncoding)) return preferredEncoding
}
}
// no encoding matches, check to see if the client set identity, q=0
if (encodingWeights.get('identity') === 0) throw errors(406, 'Please accept br, gzip, deflate, or identity.')
// by default, return nothing
return 'identity'
}
}
Encodings.encodingMethods = {
gzip: zlib.createGzip,
deflate: zlib.createDeflate,
br: zlib.createBrotliCompress
}
Encodings.encodingMethodDefaultOptions = {
gzip: {},
deflate: {},
br: {
params: {
[zlib.constants.BROTLI_PARAM_QUALITY]: 4
}
}
}
// how we treat `Accept-Encoding: *`
Encodings.wildcardAcceptEncoding = ['gzip', 'deflate']
// our preferred encodings
Encodings.preferredEncodings = ['br', 'gzip', 'deflate']
Encodings.reDirective = /^\s*(gzip|compress|deflate|br|identity|\*)\s*(?:;\s*q\s*=\s*(\d(?:\.\d)?))?\s*$/
module.exports = Encodings