From 79ee2525958ebf30e33f08d4ff97e9bd9bc4c086 Mon Sep 17 00:00:00 2001 From: Sindre Sorhus Date: Sun, 7 May 2017 18:08:35 +0800 Subject: [PATCH] Preserve custom properties on the original stream (#8) Fixes #6 --- index.js | 44 +++++++++++++++++++++++++++++++++----------- test/test.js | 22 +++++++++++++++++++++- 2 files changed, 54 insertions(+), 12 deletions(-) diff --git a/index.js b/index.js index 88f3f60..d060db3 100644 --- a/index.js +++ b/index.js @@ -2,6 +2,38 @@ const PassThrough = require('stream').PassThrough; const zlib = require('zlib'); +// We define these manually to ensure they're always copied +// even if they would move up the prototype chain +// https://nodejs.org/api/http.html#http_class_http_incomingmessage +const knownProps = [ + 'destroy', + 'setTimeout', + 'socket', + 'headers', + 'trailers', + 'rawHeaders', + 'statusCode', + 'httpVersion', + 'httpVersionMinor', + 'httpVersionMajor', + 'rawTrailers', + 'statusMessage' +]; + +const copyProps = (fromStream, toStream) => { + const toProps = Object.keys(toStream); + const fromProps = new Set(Object.keys(fromStream).concat(knownProps)); + + for (const prop of fromProps) { + // Don't overwrite existing properties + if (toProps.indexOf(prop) !== -1) { + continue; + } + + toStream[prop] = typeof prop === 'function' ? fromStream[prop].bind(fromStream) : fromStream[prop]; + } +}; + module.exports = res => { // TODO: Use Array#includes when targeting Node.js 6 if (['gzip', 'deflate'].indexOf(res.headers['content-encoding']) === -1) { @@ -11,17 +43,7 @@ module.exports = res => { const unzip = zlib.createUnzip(); const stream = new PassThrough(); - // https://nodejs.org/api/http.html#http_class_http_incomingmessage - stream.destroy = res.destroy.bind(res); - stream.setTimeout = res.setTimeout.bind(res); - stream.socket = res.socket; - stream.headers = res.headers; - stream.trailers = res.trailers; - stream.rawHeaders = res.rawHeaders; - stream.statusCode = res.statusCode; - stream.httpVersion = res.httpVersion; - stream.rawTrailers = res.rawTrailers; - stream.statusMessage = res.statusMessage; + copyProps(res, stream); unzip.on('error', err => { if (err.code === 'Z_BUF_ERROR') { diff --git a/test/test.js b/test/test.js index 60eb1a4..3c6c60b 100644 --- a/test/test.js +++ b/test/test.js @@ -47,8 +47,18 @@ test.after('cleanup', async () => { test('decompress gzipped content', async t => { const res = m(await httpGetP(s.url)); - t.is(typeof res.httpVersion, 'string'); + t.truthy(res.destroy); + t.truthy(res.setTimeout); + t.truthy(res.socket); t.truthy(res.headers); + t.truthy(res.trailers); + t.truthy(res.rawHeaders); + t.truthy(res.statusCode); + t.truthy(res.httpVersion); + t.truthy(res.httpVersionMinor); + t.truthy(res.httpVersionMajor); + t.truthy(res.rawTrailers); + t.truthy(res.statusMessage); res.setEncoding('utf8'); @@ -76,3 +86,13 @@ test('ignore missing data', async t => { t.is(await getStream(res), fixture); }); + +test('preserves custom properties on the stream', async t => { + let res = await httpGetP(s.url); + res.customProp = '🦄'; + res = m(res); + + t.is(res.customProp, '🦄'); + + res.destroy(); +});