UNPKG

stick

Version:

JSGI based webapp framework

81 lines (76 loc) 2.64 kB
/** * @fileOverview Middleware for on-the-fly GZip compression of response bodies. * * By default only text content types are compressed. This can be controlled * using the `gzip.contentTypes` property: * @example * app.configure("gzip"); * app.gzip.contentTypes = /^text|xml|json|javascript/; * */ var zlib = require('zlib'); var Binary = require('binary').Binary; var Stream = require('io').Stream; var Headers = require('../utils/http').Headers; var CHARSET_PATTERN = /charset=([^;]+)/; function getCharset(contentType) { var value = CHARSET_PATTERN.exec(contentType); return value && value[1]; } /** * JSGI middleware for GZIP compression. * @param {Function} next the wrapped middleware chain * @param {Object} app the Stick Application object * @returns {Function} a JSGI middleware function */ exports.middleware = function(next, app) { // app.gzip.contentTypes is a regular expression that use used to determine // whether or not gzip encoding should be applied to a response app.gzip = { contentTypes:/^text|xml|json|javascript/, options:{ chunkSize:256 * 1024, windowBits:15, level:zlib.Z_BEST_COMPRESSION, memLevel:9 } }; // Return true if content-type matches regex and other conditions are met function canCompress(status, acceptEncoding, contentType, contentEncoding) { if (status === 200 && acceptEncoding && contentType && !contentEncoding) { if (typeof acceptEncoding !== 'string') { acceptEncoding = acceptEncoding.join(', '); } if (typeof contentType !== 'string') { contentType = contentType.join(', '); } return ~acceptEncoding.indexOf('gzip') && app.gzip.contentTypes.test(contentType); } return false; } return function(request) { var res = next(request); var headers = Headers(res.headers); var contentType = headers.get('Content-Type'); if (canCompress(res.status, request.headers['accept-encoding'], contentType, headers.get('Content-Encoding'))) { var gzip = zlib.createGzip(app.gzip.options); var charset = getCharset(contentType); var length = 0; res.body.forEach(function(part) { if (!(part instanceof Binary)) { part = part.toByteString(charset); } length += part.length; gzip.write(part.buffer); }); gzip.end(); gzip.writeable = false; var compressed = new Stream(gzip).read(); if (compressed.length + 'Content-Encoding: gzip\n'.length < length) { headers.set('Content-Encoding', 'gzip'); res.body = [compressed]; } } return res; }; };