vapr-compress
Version:
A compression plugin for Vapr
60 lines (52 loc) • 2.56 kB
JavaScript
;
const negotiated = require('negotiated');
/*
This handler is used when content negotiation is turned on. It supports both
the gzip and deflate encodings, but will choose gzip when no preference is
given by the client. It prefers to use the Transfer-Encoding header if
possible, but will use the Content-Encoding header as a fallback.
*/
module.exports = (lateHandler, disableTransfer, disableContent, encodeByDefault) => (req) => {
let transferEncoding = '';
let contentEncoding = '';
if (!disableTransfer) {
// Identify the preferred Transfer-Encoding, if provided by the client.
const teHeader = req.headers.get('te');
if (teHeader) {
try { transferEncoding = getTopEncoding(negotiated.transferEncodings(teHeader), transferEncodings); }
catch (_) { return [400, 'Malformed TE Header']; }
if (!transferEncoding) return [501, 'No Acceptable Transfer-Encoding'];
if (transferEncoding === 'identity') transferEncoding = '';
}
}
if (!disableContent) {
// Identify the preferred Content-Encoding, if provided by the client.
const acHeader = req.headers.get('accept-encoding');
if (acHeader !== undefined) {
try { contentEncoding = getTopEncoding(negotiated.encodings(acHeader), contentEncodings); }
catch (_) { return [400, 'Malformed Accept-Encoding Header']; }
if (!contentEncoding) return [406, 'No Acceptable Content-Encoding'];
if (contentEncoding === 'identity') contentEncoding = '';
} else {
// If no Accept-Encoding header was provided, any encoding is valid.
if (encodeByDefault) contentEncoding = 'gzip';
}
}
if (transferEncoding) return lateHandler(req, 'transfer-encoding', transferEncoding);
if (contentEncoding) return lateHandler(req, 'content-encoding', contentEncoding);
return lateHandler(req, '', '');
};
const getTopEncoding = (encodings, understood) => {
const present = new Map;
for (const x of encodings) if (understood.includes(x.encoding)) present.set(x.encoding, x);
if (!present.size) return 'identity';
const [best, second] = [...present.values()].sort(sortByWeight);
if (!best.weight) return present.has('identity') || present.has('*') ? '' : 'identity';
if (best.encoding !== '*') return best.encoding;
const missing = understood.find(findMissing, present);
return missing || (second.weight ? second.encoding : '');
};
function findMissing(x) { return !this.has(x); }
const sortByWeight = (a, b) => b.weight - a.weight;
const transferEncodings = ['gzip', 'deflate', 'identity'];
const contentEncodings = ['gzip', 'deflate', 'identity', '*'];