From fe561d17566524f0bbe46323136d3f92fe85e728 Mon Sep 17 00:00:00 2001 From: Matt Simerson Date: Thu, 23 Apr 2026 23:15:22 -0700 Subject: [PATCH 1/4] register transformer.once('end'... before piping - doc(CONTRIBUTORS): updated - doc(CHANGELOG): add commit messages for 2.0.3 --- .github/FUNDING.yml | 1 - .github/workflows/ci.yml | 3 ++- .release | 2 +- CHANGELOG.md | 4 ++++ CONTRIBUTORS.md | 4 ++-- index.js | 7 +++++-- package.json | 4 ++-- test/message-stream.js | 41 ++++++++++++++++++++++++++++++++++++++++ 8 files changed, 57 insertions(+), 9 deletions(-) delete mode 100644 .github/FUNDING.yml diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml deleted file mode 100644 index a861f70..0000000 --- a/.github/FUNDING.yml +++ /dev/null @@ -1 +0,0 @@ -github: msimerson diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 00a4ef3..e97f4bc 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -16,11 +16,12 @@ jobs: contents: read prettier: + if: ${{ github.event_name == 'push' || github.event.pull_request.head.repo.full_name == github.repository }} uses: haraka/.github/.github/workflows/prettier.yml@master permissions: contents: write with: - branch: ${{ github.head_ref }} + branch: ${{ github.event_name == 'pull_request' && github.head_ref || github.ref_name }} coverage: uses: haraka/.github/.github/workflows/coverage.yml@master diff --git a/.release b/.release index a6911a9..0512cc8 160000 --- a/.release +++ b/.release @@ -1 +1 @@ -Subproject commit a6911a90f1b15486fb319d844341421c78035b2a +Subproject commit 0512cc83f7b2b50ca01b78299b7b2a18ca4f3e66 diff --git a/CHANGELOG.md b/CHANGELOG.md index ccd8dec..95c780a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/). ### Unreleased +### [2.0.3] - 2026-04-23 + + ### [2.0.2] - 2026-04-08 - fix: limit header size to prevent memory exhaustion @@ -115,3 +118,4 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/). [2.0.0]: https://github.com/haraka/message-stream/releases/tag/v2.0.0 [2.0.1]: https://github.com/haraka/message-stream/releases/tag/v2.0.1 [2.0.2]: https://github.com/haraka/message-stream/releases/tag/v2.0.2 +[2.0.3]: https://github.com/haraka/message-stream/releases/tag/v2.0.3 diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index c066092..192f6f0 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -2,8 +2,8 @@ This handcrafted artisanal software is brought to you by: -|
msimerson (15) |
bjarn (1) | -| :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | +|
msimerson (16)|
bjarn (1)| +| :---: | :---: | this file is generated by [.release](https://github.com/msimerson/.release). Contribute to this project to get your GitHub profile included here. diff --git a/index.js b/index.js index 9e01260..5929d88 100644 --- a/index.js +++ b/index.js @@ -207,8 +207,9 @@ class MessageStream extends Stream { const source = new PassThrough() this.#currentSource = source - source.pipe(transformer).pipe(destination, { end: options.end !== false }) - + // Register before pipe() so these fire before the pipe's own 'end' handler, + // which calls destination.end() — potentially triggering a synchronous next() + // that would attempt a new pipe() while #inPipe is still true. transformer.once('end', () => { this.#inPipe = false }) @@ -218,6 +219,8 @@ class MessageStream extends Stream { }) source.once('error', (err) => this.emit('error', err)) + source.pipe(transformer).pipe(destination, { end: options.end !== false }) + // Constructor headers suppress raw-data headers; skip_headers also suppresses ctor headers const emitCtorHeaders = this.headers.length > 0 && !skipHeaders const skipRawHeaders = this.headers.length > 0 || skipHeaders diff --git a/package.json b/package.json index 78d5ab8..4006510 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "haraka-message-stream", - "version": "2.0.2", + "version": "2.0.3", "description": "Haraka email message stream", "main": "index.js", "files": [ @@ -41,4 +41,4 @@ "singleQuote": true, "semi": false } -} +} \ No newline at end of file diff --git a/test/message-stream.js b/test/message-stream.js index 0f35b3d..0529821 100644 --- a/test/message-stream.js +++ b/test/message-stream.js @@ -183,3 +183,44 @@ describe('pipe end option', () => { ) }) }) + +describe('sequential pipe', () => { + it('allows a second pipe started synchronously from the first pipe end callback', (t, done) => { + // Regression test for haraka/Haraka#3551: + // When destination.end() is called synchronously inside the 'end' listener + // registered by pipe(), and that callback triggers another pipe(), the + // #inPipe guard must already be cleared or it throws "Cannot pipe while + // currently piping". + const ms = new MessageStream({ main: {} }, 'msg', []) + ms.add_line('Subject: test\r\n') + ms.add_line('\r\n') + ms.add_line('body\r\n') + ms.add_line_end() + + const chunks1 = [] + const chunks2 = [] + + // First destination: a writable that triggers a second pipe synchronously + // inside its end() — simulating what DKIMSignStream does. + const dest1 = new stream.Writable({ + write(chunk, _enc, cb) { + chunks1.push(chunk.toString()) + cb() + }, + final(cb) { + cb() + // Synchronously start a second pipe, just like DKIMSignStream's callback + // calls next() which leads to process_delivery which pipes the stream again. + const dest2 = new stream.PassThrough() + dest2.on('data', (c) => chunks2.push(c.toString())) + dest2.on('end', () => { + assert.ok(chunks2.join('').length > 0, 'second pipe received data') + done() + }) + ms.pipe(dest2) + }, + }) + + ms.pipe(dest1) + }) +}) From e34bbb37f136dd0363b32395bfc3dd60565412d6 Mon Sep 17 00:00:00 2001 From: Matt Simerson Date: Thu, 23 Apr 2026 23:18:27 -0700 Subject: [PATCH 2/4] chore: format --- CHANGELOG.md | 1 - CONTRIBUTORS.md | 4 ++-- package.json | 2 +- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 95c780a..e1285e4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,7 +6,6 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/). ### [2.0.3] - 2026-04-23 - ### [2.0.2] - 2026-04-08 - fix: limit header size to prevent memory exhaustion diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index 192f6f0..8e8d227 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -2,8 +2,8 @@ This handcrafted artisanal software is brought to you by: -|
msimerson (16)|
bjarn (1)| -| :---: | :---: | +|
msimerson (16) |
bjarn (1) | +| :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | this file is generated by [.release](https://github.com/msimerson/.release). Contribute to this project to get your GitHub profile included here. diff --git a/package.json b/package.json index 4006510..40a14ab 100644 --- a/package.json +++ b/package.json @@ -41,4 +41,4 @@ "singleQuote": true, "semi": false } -} \ No newline at end of file +} From a30531fa5ff7a30d5da00909fd4b93fd52fe12ce Mon Sep 17 00:00:00 2001 From: Matt Simerson Date: Thu, 23 Apr 2026 23:30:29 -0700 Subject: [PATCH 3/4] update changelog --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e1285e4..5276a43 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/). ### [2.0.3] - 2026-04-23 +- register `transformer.once('end'...)` before piping + ### [2.0.2] - 2026-04-08 - fix: limit header size to prevent memory exhaustion From e933024bd5dc4dc07d4d9ac5cac8588e2b470b2c Mon Sep 17 00:00:00 2001 From: Matt Simerson Date: Thu, 23 Apr 2026 23:31:48 -0700 Subject: [PATCH 4/4] update changelog --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5276a43..97375e2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,7 +6,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/). ### [2.0.3] - 2026-04-23 -- register `transformer.once('end'...)` before piping +- register `transformer.once('end'...)` before piping #21 ### [2.0.2] - 2026-04-08