From 7cfdcffdf6d8a249cf13c72b86771a7017f7c3f9 Mon Sep 17 00:00:00 2001 From: Zhenwei Jin <109658203+kylo5aby@users.noreply.github.com> Date: Sat, 11 May 2024 23:39:21 +0800 Subject: [PATCH] fs: keep fs.promises.readFile read until EOF is reached PR-URL: https://github.com/nodejs/node/pull/52178 Fixes: https://github.com/nodejs/node/issues/52155 Reviewed-By: Chengzhong Wu --- lib/internal/fs/promises.js | 13 +++++--- test/parallel/test-fs-readfile-eof.js | 46 +++++++++++++++++++++++++++ 2 files changed, 54 insertions(+), 5 deletions(-) create mode 100644 test/parallel/test-fs-readfile-eof.js diff --git a/lib/internal/fs/promises.js b/lib/internal/fs/promises.js index 1544c34e6f469a..c9e9c840de3b70 100644 --- a/lib/internal/fs/promises.js +++ b/lib/internal/fs/promises.js @@ -536,6 +536,7 @@ async function readFileHandle(filehandle, options) { throw new ERR_FS_FILE_TOO_LARGE(size); let totalRead = 0; + const noSize = size === 0; let buffer = Buffer.allocUnsafeSlow(length); let result = ''; let offset = 0; @@ -558,7 +559,7 @@ async function readFileHandle(filehandle, options) { if (bytesRead === 0 || totalRead === size || - (bytesRead !== buffer.length && !chunkedRead)) { + (bytesRead !== buffer.length && !chunkedRead && !noSize)) { const singleRead = bytesRead === totalRead; const bytesToCheck = chunkedRead ? totalRead : bytesRead; @@ -568,7 +569,7 @@ async function readFileHandle(filehandle, options) { } if (!encoding) { - if (size === 0 && !singleRead) { + if (noSize && !singleRead) { ArrayPrototypePush(buffers, buffer); return Buffer.concat(buffers, totalRead); } @@ -581,15 +582,17 @@ async function readFileHandle(filehandle, options) { result += decoder.end(buffer); return result; } - + const readBuffer = bytesRead !== buffer.length ? + buffer.subarray(0, bytesRead) : + buffer; if (encoding) { - result += decoder.write(buffer); + result += decoder.write(readBuffer); } else if (size !== 0) { offset = totalRead; } else { buffers ??= []; // Unknown file size requires chunks. - ArrayPrototypePush(buffers, buffer); + ArrayPrototypePush(buffers, readBuffer); buffer = Buffer.allocUnsafeSlow(kReadFileUnknownBufferLength); } } diff --git a/test/parallel/test-fs-readfile-eof.js b/test/parallel/test-fs-readfile-eof.js new file mode 100644 index 00000000000000..94354b915b8dd5 --- /dev/null +++ b/test/parallel/test-fs-readfile-eof.js @@ -0,0 +1,46 @@ +'use strict'; +const common = require('../common'); + +if (common.isWindows || common.isAIX || common.isIBMi) + common.skip(`No /dev/stdin on ${process.platform}.`); + +const assert = require('assert'); +const fs = require('fs/promises'); +const childType = ['child-encoding', 'child-non-encoding']; + +if (process.argv[2] === childType[0]) { + fs.readFile('/dev/stdin', 'utf8').then((data) => { + process.stdout.write(data); + }); + return; +} else if (process.argv[2] === childType[1]) { + fs.readFile('/dev/stdin').then((data) => { + process.stdout.write(data); + }); + return; +} + +const data1 = 'Hello'; +const data2 = 'World'; +const expected = `${data1}\n${data2}\n`; + +const exec = require('child_process').exec; +const f = JSON.stringify(__filename); +const node = JSON.stringify(process.execPath); + +function test(child) { + const cmd = `(echo ${data1}; sleep 0.5; echo ${data2}) | ${node} ${f} ${child}`; + exec(cmd, common.mustSucceed((stdout, stderr) => { + assert.strictEqual( + stdout, + expected, + `expected to read(${child === childType[0] ? 'with' : 'without'} encoding): '${expected}' but got: '${stdout}'`); + assert.strictEqual( + stderr, + '', + `expected not to read anything from stderr but got: '${stderr}'`); + })); +} + +test(childType[0]); +test(childType[1]);