source: frontend/node_modules/compression/index.js

Last change on this file was 9af201e, checked in by MBK <marija.karapandzova@…>, 11 days ago

Fix frontend appearance

  • Property mode set to 100644
File size: 7.0 KB
Line 
1/*!
2 * compression
3 * Copyright(c) 2010 Sencha Inc.
4 * Copyright(c) 2011 TJ Holowaychuk
5 * Copyright(c) 2014 Jonathan Ong
6 * Copyright(c) 2014-2015 Douglas Christopher Wilson
7 * MIT Licensed
8 */
9
10'use strict'
11
12/**
13 * Module dependencies.
14 * @private
15 */
16
17var Negotiator = require('negotiator')
18var Buffer = require('safe-buffer').Buffer
19var bytes = require('bytes')
20var compressible = require('compressible')
21var debug = require('debug')('compression')
22var onHeaders = require('on-headers')
23var vary = require('vary')
24var zlib = require('zlib')
25
26/**
27 * Module exports.
28 */
29
30module.exports = compression
31module.exports.filter = shouldCompress
32
33/**
34 * @const
35 * whether current node version has brotli support
36 */
37var hasBrotliSupport = 'createBrotliCompress' in zlib
38
39/**
40 * Module variables.
41 * @private
42 */
43var cacheControlNoTransformRegExp = /(?:^|,)\s*?no-transform\s*?(?:,|$)/
44var SUPPORTED_ENCODING = hasBrotliSupport ? ['br', 'gzip', 'deflate', 'identity'] : ['gzip', 'deflate', 'identity']
45var PREFERRED_ENCODING = hasBrotliSupport ? ['br', 'gzip'] : ['gzip']
46
47var encodingSupported = ['gzip', 'deflate', 'identity', 'br']
48
49/**
50 * Compress response data with gzip / deflate.
51 *
52 * @param {Object} [options]
53 * @return {Function} middleware
54 * @public
55 */
56
57function compression (options) {
58 var opts = options || {}
59 var optsBrotli = {}
60
61 if (hasBrotliSupport) {
62 Object.assign(optsBrotli, opts.brotli)
63
64 var brotliParams = {}
65 brotliParams[zlib.constants.BROTLI_PARAM_QUALITY] = 4
66
67 // set the default level to a reasonable value with balanced speed/ratio
68 optsBrotli.params = Object.assign(brotliParams, optsBrotli.params)
69 }
70
71 // options
72 var filter = opts.filter || shouldCompress
73 var threshold = bytes.parse(opts.threshold)
74 var enforceEncoding = opts.enforceEncoding || 'identity'
75
76 if (threshold == null) {
77 threshold = 1024
78 }
79
80 return function compression (req, res, next) {
81 var ended = false
82 var length
83 var listeners = []
84 var stream
85
86 var _end = res.end
87 var _on = res.on
88 var _write = res.write
89
90 // flush
91 res.flush = function flush () {
92 if (stream) {
93 stream.flush()
94 }
95 }
96
97 // proxy
98
99 res.write = function write (chunk, encoding) {
100 if (ended) {
101 return false
102 }
103
104 if (!headersSent(res)) {
105 this.writeHead(this.statusCode)
106 }
107
108 return stream
109 ? stream.write(toBuffer(chunk, encoding))
110 : _write.call(this, chunk, encoding)
111 }
112
113 res.end = function end (chunk, encoding) {
114 if (ended) {
115 return false
116 }
117
118 if (!headersSent(res)) {
119 // estimate the length
120 if (!this.getHeader('Content-Length')) {
121 length = chunkLength(chunk, encoding)
122 }
123
124 this.writeHead(this.statusCode)
125 }
126
127 if (!stream) {
128 return _end.call(this, chunk, encoding)
129 }
130
131 // mark ended
132 ended = true
133
134 // write Buffer for Node.js 0.8
135 return chunk
136 ? stream.end(toBuffer(chunk, encoding))
137 : stream.end()
138 }
139
140 res.on = function on (type, listener) {
141 if (!listeners || type !== 'drain') {
142 return _on.call(this, type, listener)
143 }
144
145 if (stream) {
146 return stream.on(type, listener)
147 }
148
149 // buffer listeners for future stream
150 listeners.push([type, listener])
151
152 return this
153 }
154
155 function nocompress (msg) {
156 debug('no compression: %s', msg)
157 addListeners(res, _on, listeners)
158 listeners = null
159 }
160
161 onHeaders(res, function onResponseHeaders () {
162 // determine if request is filtered
163 if (!filter(req, res)) {
164 nocompress('filtered')
165 return
166 }
167
168 // determine if the entity should be transformed
169 if (!shouldTransform(req, res)) {
170 nocompress('no transform')
171 return
172 }
173
174 // vary
175 vary(res, 'Accept-Encoding')
176
177 // content-length below threshold
178 if (Number(res.getHeader('Content-Length')) < threshold || length < threshold) {
179 nocompress('size below threshold')
180 return
181 }
182
183 var encoding = res.getHeader('Content-Encoding') || 'identity'
184
185 // already encoded
186 if (encoding !== 'identity') {
187 nocompress('already encoded')
188 return
189 }
190
191 // head
192 if (req.method === 'HEAD') {
193 nocompress('HEAD request')
194 return
195 }
196
197 // compression method
198 var negotiator = new Negotiator(req)
199 var method = negotiator.encoding(SUPPORTED_ENCODING, PREFERRED_ENCODING)
200
201 // if no method is found, use the default encoding
202 if (!req.headers['accept-encoding'] && encodingSupported.indexOf(enforceEncoding) !== -1) {
203 method = enforceEncoding
204 }
205
206 // negotiation failed
207 if (!method || method === 'identity') {
208 nocompress('not acceptable')
209 return
210 }
211
212 // compression stream
213 debug('%s compression', method)
214 stream = method === 'gzip'
215 ? zlib.createGzip(opts)
216 : method === 'br'
217 ? zlib.createBrotliCompress(optsBrotli)
218 : zlib.createDeflate(opts)
219
220 // add buffered listeners to stream
221 addListeners(stream, stream.on, listeners)
222
223 // header fields
224 res.setHeader('Content-Encoding', method)
225 res.removeHeader('Content-Length')
226
227 // compression
228 stream.on('data', function onStreamData (chunk) {
229 if (_write.call(res, chunk) === false) {
230 stream.pause()
231 }
232 })
233
234 stream.on('end', function onStreamEnd () {
235 _end.call(res)
236 })
237
238 _on.call(res, 'drain', function onResponseDrain () {
239 stream.resume()
240 })
241 })
242
243 next()
244 }
245}
246
247/**
248 * Add bufferred listeners to stream
249 * @private
250 */
251
252function addListeners (stream, on, listeners) {
253 for (var i = 0; i < listeners.length; i++) {
254 on.apply(stream, listeners[i])
255 }
256}
257
258/**
259 * Get the length of a given chunk
260 */
261
262function chunkLength (chunk, encoding) {
263 if (!chunk) {
264 return 0
265 }
266
267 return Buffer.isBuffer(chunk)
268 ? chunk.length
269 : Buffer.byteLength(chunk, encoding)
270}
271
272/**
273 * Default filter function.
274 * @private
275 */
276
277function shouldCompress (req, res) {
278 var type = res.getHeader('Content-Type')
279
280 if (type === undefined || !compressible(type)) {
281 debug('%s not compressible', type)
282 return false
283 }
284
285 return true
286}
287
288/**
289 * Determine if the entity should be transformed.
290 * @private
291 */
292
293function shouldTransform (req, res) {
294 var cacheControl = res.getHeader('Cache-Control')
295
296 // Don't compress for Cache-Control: no-transform
297 // https://tools.ietf.org/html/rfc7234#section-5.2.2.4
298 return !cacheControl ||
299 !cacheControlNoTransformRegExp.test(cacheControl)
300}
301
302/**
303 * Coerce arguments to Buffer
304 * @private
305 */
306
307function toBuffer (chunk, encoding) {
308 return Buffer.isBuffer(chunk)
309 ? chunk
310 : Buffer.from(chunk, encoding)
311}
312
313/**
314 * Determine if the response headers have been sent.
315 *
316 * @param {object} res
317 * @returns {boolean}
318 * @private
319 */
320
321function headersSent (res) {
322 return typeof res.headersSent !== 'boolean'
323 ? Boolean(res._header)
324 : res.headersSent
325}
Note: See TracBrowser for help on using the repository browser.