compress.js
3.14 KB
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
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
/*!
* Connect - compress
* Copyright(c) 2010 Sencha Inc.
* Copyright(c) 2011 TJ Holowaychuk
* MIT Licensed
*/
/**
* Module dependencies.
*/
var zlib = require('zlib');
/**
* Supported content-encoding methods.
*/
exports.methods = {
gzip: zlib.createGzip
, deflate: zlib.createDeflate
};
/**
* Default filter function.
*/
exports.filter = function(req, res){
return /json|text|javascript/.test(res.getHeader('Content-Type'));
};
/**
* Compress:
*
* Compress response data with gzip/deflate.
*
* Filter:
*
* A `filter` callback function may be passed to
* replace the default logic of:
*
* exports.filter = function(req, res){
* return /json|text|javascript/.test(res.getHeader('Content-Type'));
* };
*
* Options:
*
* All remaining options are passed to the gzip/deflate
* creation functions. Consult node's docs for additional details.
*
* - `chunkSize` (default: 16*1024)
* - `windowBits`
* - `level`: 0-9 where 0 is no compression, and 9 is slow but best compression
* - `memLevel`: 1-9 low is slower but uses less memory, high is fast but uses more
* - `strategy`: compression strategy
*
* @param {Object} options
* @return {Function}
* @api public
*/
module.exports = function compress(options) {
var options = options || {}
, names = Object.keys(exports.methods)
, filter = options.filter || exports.filter;
return function(req, res, next){
var accept = req.headers['accept-encoding']
, write = res.write
, end = res.end
, stream
, method;
// vary
res.setHeader('Vary', 'Accept-Encoding');
// proxy
res.write = function(chunk, encoding){
if (!this.headerSent) this._implicitHeader();
return stream
? stream.write(new Buffer(chunk, encoding))
: write.call(res, chunk, encoding);
};
res.end = function(chunk, encoding){
if (chunk) this.write(chunk, encoding);
return stream
? stream.end()
: end.call(res);
};
res.on('header', function(){
var encoding = res.getHeader('Content-Encoding') || 'identity';
// already encoded
if ('identity' != encoding) return;
// default request filter
if (!filter(req, res)) return;
// SHOULD use identity
if (!accept) return;
// head
if ('HEAD' == req.method) return;
// default to gzip
if ('*' == accept.trim()) method = 'gzip';
// compression method
if (!method) {
for (var i = 0, len = names.length; i < len; ++i) {
if (~accept.indexOf(names[i])) {
method = names[i];
break;
}
}
}
// compression method
if (!method) return;
// compression stream
stream = exports.methods[method](options);
// header fields
res.setHeader('Content-Encoding', method);
res.removeHeader('Content-Length');
// compression
stream.on('data', function(chunk){
write.call(res, chunk);
});
stream.on('end', function(){
end.call(res);
});
stream.on('drain', function() {
res.emit('drain');
});
});
next();
};
}