|
|
/*! * 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) { options = options || {}; var names = Object.keys(exports.methods) , filter = options.filter || exports.filter;
return function compress(req, res, next){ var accept = req.headers['accept-encoding'] , vary = res.getHeader('Vary') , write = res.write , end = res.end , stream , method;
// vary
if (!vary) { res.setHeader('Vary', 'Accept-Encoding'); } else if (!~vary.indexOf('Accept-Encoding')) { res.setHeader('Vary', vary + ', Accept-Encoding'); }
// see #724
req.on('close', function(){ res.write = res.end = function(){}; });
// 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(); }; };
|