Repository navigation
stream.unshift - TypeError: Argument must be a buffer #27192
Description
Activity
@marcosc90 Would you be interested in opening a PR, since it seems that you already have a solution available?
- addedstreamIssues and PRs related to Node.js streams.Issues and PRs related to Node.js streams.
on Apr 11, 2019 Yes, I have the solution ready, I can submit the PR in a couple of hours once I finish working.
Just to confirm, a new argument must be added to
unshift, the documentation change should be added in the same commit?@marcosc90 I’m not sure about the new argument – if the expectation is that the data was previously read from the stream, the encoding should match the encoding used for reading data from the stream (i.e.
state.encoding, rather thanstate.defaultEncoding– the naming clash is unfortunate here).state.encodingwill benull, in all cases where this bug occurs. I can doBuffer.from(chunk, state.encoding)but that will always default toutf8. So unless you set the encoding beforehand, where the error does not occur, if you.unshifta string, that string will always be treated as anutf8one.Is this the behaviour that you want? I believe if
.unshiftneeds to support strings, anencodingargument is recommended.In any case, I can submit the PR which whatever functionality you prefer.
I'm working on a few snippets to see if not adding
encodingis going to be problematic. (I know that in most cases when working with strings, utf8 is going to be the encoding)Take the following example, using
hexas encoding instead ofutf8function parseHeader(stream, callback) { stream.on('error', callback); stream.on('readable', onReadable); const decoder = new StringDecoder('hex'); let header = ''; function onReadable() { let chunk; while (null !== (chunk = stream.read())) { const str = decoder.write(chunk); if (str.match(/\n\n/)) { const split = str.split(/0a0a/); header += split.shift(); const remaining = split.join('0a0a'); stream.removeListener('error', callback); stream.removeListener('readable', onReadable); if (remaining.length) stream.unshift(remaining); // Now the body of the message can be read from the stream. callback(null, header, stream); } else { // still reading the header. header += str; } } } } parseHeader(new ArrayReader(), (err, header, stream) => { stream.once('data', chunk => { console.log(chunk.toString('utf8')) }); });That will output:
3030303030303030303030303030303030303030Instead of:
00000000000000000000, since the pushed chunk has the wrong encoding, and there isn't a way to set it.With the fix I'm proposing, using:
stream.unshift(remaining, 'hex')I get the correct output:00000000000000000000So right now every string unshifted, is coerced to
utf8.- added a commit that references this issue
on Jun 3, 2019
The
stream.unshiftdocumentation states:The issue occurs when the
streamencoding is not passed, or is not set usingsetEncoding. When that happensstate.decoderis not set andfromListfunction may usestate.buffer.concat(whenstate.buffer.length > 1) , which internally usescopyBuffer, that requires the source & target to be aBufferorUint8Array.When
stream.unshiftis called with astring(which the documentation states that is a valid argument), in some cases where the buffer is filled, andstate.buffer.concatis triggered, an error will be thrown:Here's the script to reproduce the error:
It's the
parseHeaderexample from the documentation, but instead of convertingremainingto aBuffer, I pass it directly to.unshiftThis can be fixed, either by converting the
chunktoBuffersimilar to how it's done in.push, and maybe adding anencodingargument too. Or by changing the documentation to state that astringcan only be passed if thestreamencoding is set.