diff --git a/@vates/read-chunk/index.js b/@vates/read-chunk/index.js index ba87261adc1..421c53a281e 100644 --- a/@vates/read-chunk/index.js +++ b/@vates/read-chunk/index.js @@ -1,6 +1,7 @@ 'use strict' const assert = require('assert') +const isUtf8 = require('isutf8') /** * Read a chunk of data from a stream. @@ -81,6 +82,13 @@ exports.readChunkStrict = async function readChunkStrict(stream, size) { if (size !== undefined && chunk.length !== size) { const error = new Error(`stream has ended with not enough data (actual: ${chunk.length}, expected: ${size})`) + + // Buffer.isUtf8 is too recent for now + // @todo : replace external package by Buffer.isUtf8 when the supported version of node reach 18 + + if (chunk.length < 1024 && isUtf8(chunk)) { + error.text = chunk.toString('utf8') + } Object.defineProperties(error, { chunk: { value: chunk, diff --git a/@vates/read-chunk/index.test.js b/@vates/read-chunk/index.test.js index 4cfdaa94a58..a73609ac0cf 100644 --- a/@vates/read-chunk/index.test.js +++ b/@vates/read-chunk/index.test.js @@ -102,12 +102,37 @@ describe('readChunkStrict', function () { assert.strictEqual(error.chunk, undefined) }) - it('throws if stream ends with not enough data', async () => { + it('throws if stream ends with not enough data, utf8', async () => { const error = await rejectionOf(readChunkStrict(makeStream(['foo', 'bar']), 10)) assert(error instanceof Error) assert.strictEqual(error.message, 'stream has ended with not enough data (actual: 6, expected: 10)') + assert.strictEqual(error.text, 'foobar') assert.deepEqual(error.chunk, Buffer.from('foobar')) }) + + it('throws if stream ends with not enough data, non utf8 ', async () => { + const source = [Buffer.alloc(10, 128), Buffer.alloc(10, 128)] + const error = await rejectionOf(readChunkStrict(makeStream(source), 30)) + assert(error instanceof Error) + assert.strictEqual(error.message, 'stream has ended with not enough data (actual: 20, expected: 30)') + assert.strictEqual(error.text, undefined) + assert.deepEqual(error.chunk, Buffer.concat(source)) + }) + + it('throws if stream ends with not enough data, utf8 , long data', async () => { + const source = Buffer.from('a'.repeat(1500)) + const error = await rejectionOf(readChunkStrict(makeStream([source]), 2000)) + assert(error instanceof Error) + assert.strictEqual(error.message, `stream has ended with not enough data (actual: 1500, expected: 2000)`) + assert.strictEqual(error.text, undefined) + assert.deepEqual(error.chunk, source) + }) + + it('succeed', async () => { + const source = Buffer.from('a'.repeat(20)) + const chunk = await readChunkStrict(makeStream([source]), 10) + assert.deepEqual(source.subarray(10), chunk) + }) }) describe('skip', function () { @@ -134,6 +159,16 @@ describe('skip', function () { it('returns less size if stream ends', async () => { assert.deepEqual(await skip(makeStream('foo bar'), 10), 7) }) + + it('put back if it read too much', async () => { + let source = makeStream(['foo', 'bar']) + await skip(source, 1) // read part of data chunk + const chunk = (await readChunkStrict(source, 2)).toString('utf-8') + assert.strictEqual(chunk, 'oo') + + source = makeStream(['foo', 'bar']) + assert.strictEqual(await skip(source, 3), 3) // read aligned with data chunk + }) }) describe('skipStrict', function () { @@ -144,4 +179,9 @@ describe('skipStrict', function () { assert.strictEqual(error.message, 'stream has ended with not enough data (actual: 7, expected: 10)') assert.deepEqual(error.bytesSkipped, 7) }) + it('succeed', async () => { + const source = makeStream(['foo', 'bar', 'baz']) + const res = await skipStrict(source, 4) + assert.strictEqual(res, undefined) + }) }) diff --git a/@vates/read-chunk/package.json b/@vates/read-chunk/package.json index 5e246b6ac1c..86e27ad70a4 100644 --- a/@vates/read-chunk/package.json +++ b/@vates/read-chunk/package.json @@ -33,5 +33,8 @@ }, "devDependencies": { "test": "^3.2.1" + }, + "dependencies": { + "isutf8": "^4.0.0" } } diff --git a/CHANGELOG.unreleased.md b/CHANGELOG.unreleased.md index 24c3ec0e57a..22bd06c2aeb 100644 --- a/CHANGELOG.unreleased.md +++ b/CHANGELOG.unreleased.md @@ -18,6 +18,7 @@ - [XO Config Cloud Backup] Improve wording about passphrase (PR [#6938](https://github.com/vatesfr/xen-orchestra/pull/6938)) - [Pool] Fix IPv6 handling when adding hosts - [New SR] Send provided NFS version to XAPI when probing a share +- [Backup/exports] Show more information on error ` stream has ended with not enough data (actual: xxx, expected: 512)` (PR [#6940](https://github.com/vatesfr/xen-orchestra/pull/6940)) ### Packages to release @@ -41,6 +42,7 @@ - @xen-orchestra/backups minor - @xen-orchestra/vmware-explorer minor - @xen-orchestra/xapi major +- @vates/read-chunk minor - complex-matcher patch - xen-api patch - xo-server patch