Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion lib/internal/streams/readable.js
Original file line number Diff line number Diff line change
Expand Up @@ -1349,7 +1349,7 @@ Readable.prototype.wrap = function(stream) {

// Proxy all the other methods. Important when wrapping filters and duplexes.
const streamKeys = ObjectKeys(stream);
for (let j = 1; j < streamKeys.length; j++) {
for (let j = 0; j < streamKeys.length; j++) {
const i = streamKeys[j];
if (this[i] === undefined && typeof stream[i] === 'function') {
this[i] = stream[i].bind(stream);
Expand Down
40 changes: 40 additions & 0 deletions test/parallel/test-stream2-readable-wrap-proxy-methods.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
'use strict';
const common = require('../common');
const assert = require('assert');
const { Readable } = require('stream');

// Readable.prototype.wrap() proxies the methods of the wrapped old-style
// stream onto the new Readable. Regression test for an off-by-one that made
// the proxy loop start at index 1, silently skipping the first own-enumerable
// key returned by Object.keys() — so a method happening to sit at that first
// position was never proxied.

// A minimal old-style stream whose *first* own-enumerable key is a method.
// `Object.keys()` preserves insertion order for string keys, so `firstMethod`
// is `streamKeys[0]` — exactly the slot the bug skipped.
const source = {
firstMethod() { return `first:${this === source}`; },
secondMethod() { return `second:${this === source}`; },
on() { return this; },
pause() {},
resume() {},
};

assert.strictEqual(Object.keys(source)[0], 'firstMethod');

const wrapped = new Readable().wrap(source);

// The method at the first key must be proxied (was `undefined` before the fix).
assert.strictEqual(typeof wrapped.firstMethod, 'function');
assert.strictEqual(typeof wrapped.secondMethod, 'function');

// Proxied methods stay bound to the original stream.
assert.strictEqual(wrapped.firstMethod(), 'first:true');
assert.strictEqual(wrapped.secondMethod(), 'second:true');

// Existing Readable methods must not be clobbered by the proxying.
assert.strictEqual(wrapped.pause, Readable.prototype.pause);
assert.strictEqual(wrapped.resume, Readable.prototype.resume);

wrapped.on('end', common.mustNotCall());
wrapped.destroy();
Loading