From 6ff7c1ae872daeafb7b9772e279a2fa91b612848 Mon Sep 17 00:00:00 2001 From: uzlopak Date: Sun, 25 Aug 2024 04:05:10 +0200 Subject: [PATCH] fix: setEncoding should not throw on body #1125 --- lib/api/readable.js | 31 +++++++++++---- test/client-request.js | 87 +++++++++++++++++++++++++++++++++++++++++- 2 files changed, 110 insertions(+), 8 deletions(-) diff --git a/lib/api/readable.js b/lib/api/readable.js index 2a302b0fa71..f4d00c34118 100644 --- a/lib/api/readable.js +++ b/lib/api/readable.js @@ -201,6 +201,17 @@ class BodyReadable extends Readable { .resume() }) } + + /** + * @param {BufferEncoding} encoding + * @returns {BodyReadable} + */ + setEncoding (encoding) { + if (Buffer.isEncoding(encoding)) { + this._readableState.encoding = encoding + } + return this + } } // https://streams.spec.whatwg.org/#readablestream-locked @@ -278,10 +289,10 @@ function consumeStart (consume) { } if (state.endEmitted) { - consumeEnd(this[kConsume]) + consumeEnd(this[kConsume], this._readableState.encoding) } else { consume.stream.on('end', function () { - consumeEnd(this[kConsume]) + consumeEnd(this[kConsume], this._readableState.encoding) }) } @@ -295,8 +306,10 @@ function consumeStart (consume) { /** * @param {Buffer[]} chunks * @param {number} length + * @param {BufferEncoding} encoding + * @returns {string} */ -function chunksDecode (chunks, length) { +function chunksDecode (chunks, length, encoding) { if (chunks.length === 0 || length === 0) { return '' } @@ -311,7 +324,11 @@ function chunksDecode (chunks, length) { buffer[2] === 0xbf ? 3 : 0 - return buffer.utf8Slice(start, bufferLength) + if (!encoding || encoding === 'utf8' || encoding === 'utf-8') { + return buffer.utf8Slice(start, bufferLength) + } else { + return buffer.subarray(start, bufferLength).toString(encoding) + } } /** @@ -339,14 +356,14 @@ function chunksConcat (chunks, length) { return buffer } -function consumeEnd (consume) { +function consumeEnd (consume, encoding) { const { type, body, resolve, stream, length } = consume try { if (type === 'text') { - resolve(chunksDecode(body, length)) + resolve(chunksDecode(body, length, encoding)) } else if (type === 'json') { - resolve(JSON.parse(chunksDecode(body, length))) + resolve(JSON.parse(chunksDecode(body, length, encoding))) } else if (type === 'arrayBuffer') { resolve(chunksConcat(body, length).buffer) } else if (type === 'blob') { diff --git a/test/client-request.js b/test/client-request.js index c67cecdb7f3..27555d5186a 100644 --- a/test/client-request.js +++ b/test/client-request.js @@ -220,7 +220,7 @@ test('request body destroyed on invalid callback', async (t) => { after(() => client.destroy()) const body = new Readable({ - read () {} + read () { } }) try { client.request({ @@ -1252,3 +1252,88 @@ test('request post body DataView', async (t) => { await t.completed }) + +test('request multibyte json with setEncoding', async (t) => { + t = tspl(t, { plan: 1 }) + + const asd = Buffer.from('あいうえお') + const data = JSON.stringify({ asd }) + const server = createServer((req, res) => { + res.write(data.slice(0, 1)) + setTimeout(() => { + res.write(data.slice(1)) + res.end() + }, 100) + }) + after(server.close.bind(server)) + + server.listen(0, async () => { + const client = new Client(`http://localhost:${server.address().port}`) + after(client.destroy.bind(client)) + + const { body } = await client.request({ + path: '/', + method: 'GET' + }) + body.setEncoding('utf8') + t.deepStrictEqual(JSON.parse(data), await body.json()) + }) + + await t.completed +}) + +test('request multibyte text with setEncoding', async (t) => { + t = tspl(t, { plan: 1 }) + + const data = Buffer.from('あいうえお') + const server = createServer((req, res) => { + res.write(data.slice(0, 1)) + setTimeout(() => { + res.write(data.slice(1)) + res.end() + }, 100) + }) + after(server.close.bind(server)) + + server.listen(0, async () => { + const client = new Client(`http://localhost:${server.address().port}`) + after(client.destroy.bind(client)) + + const { body } = await client.request({ + path: '/', + method: 'GET' + }) + body.setEncoding('utf8') + t.deepStrictEqual(data.toString('utf8'), await body.text()) + }) + + await t.completed +}) + +test('request multibyte text with setEncoding', async (t) => { + t = tspl(t, { plan: 1 }) + + const data = Buffer.from('あいうえお') + const server = createServer((req, res) => { + res.write(data.slice(0, 1)) + setTimeout(() => { + res.write(data.slice(1)) + res.end() + }, 100) + }) + after(server.close.bind(server)) + + server.listen(0, async () => { + const client = new Client(`http://localhost:${server.address().port}`) + after(client.destroy.bind(client)) + + const { body } = await client.request({ + path: '/', + method: 'GET' + }) + body.setEncoding('hex') + t.deepStrictEqual(data.toString('hex'), await body.text()) + }) + + await t.completed +})