From 7521feb506ef27c4708fda22ed56aa4fe78d84d9 Mon Sep 17 00:00:00 2001 From: Yarchik Date: Tue, 23 Jun 2026 18:13:21 +0100 Subject: [PATCH] fix: drop split CRLFCRLF terminator bytes from the header buffer When a part's `\r\n\r\n` header terminator is split across two pushes as `...\r` followed by `\n\r\n`, HeaderParser appended the leading `\r` to the header buffer on the first push, then matched the terminator on the second push without removing it. The stray `\r` stayed attached to the last header value, so e.g. `Content-Type: image/png` was reported as `image/png\r`. This made parsing depend on transport chunking: the same bytes produced a different result depending on where the stream was split, corrupting the last header (mimetype, content-transfer-encoding, `filename*`) of any part whenever the terminator happened to break right after the first CR. Track how many terminator-prefix bytes were buffered on the previous push and trim them before finishing. The count is 0 in the common case, so the slice is a no-op there. --- deps/dicer/lib/HeaderParser.js | 7 +++++++ test/dicer-headerparser.test.js | 5 +++++ 2 files changed, 12 insertions(+) diff --git a/deps/dicer/lib/HeaderParser.js b/deps/dicer/lib/HeaderParser.js index 92c4935..31ddfc3 100644 --- a/deps/dicer/lib/HeaderParser.js +++ b/deps/dicer/lib/HeaderParser.js @@ -29,6 +29,7 @@ HeaderParser.prototype.push = function (data) { let end = data.length let appendEnd = data.length let found = false + let splitTail = 0 const tail = this.tail for (let i = tail.length; i > 0; --i) { @@ -40,6 +41,7 @@ HeaderParser.prototype.push = function (data) { if (matched) { end = S_DCRLF.length - i appendEnd = 0 + splitTail = i found = true break } @@ -74,6 +76,11 @@ HeaderParser.prototype.push = function (data) { } if (found) { + // A CRLFCRLF header terminator split across pushes left its leading bytes + // in this.buffer on the previous push; drop them so they do not trail the + // last header value. splitTail is 0 in the common case, making this a no-op. + this.buffer = this.buffer.slice(0, this.buffer.length - splitTail) + this.nread -= splitTail this._finish() return end } diff --git a/test/dicer-headerparser.test.js b/test/dicer-headerparser.test.js index dd37ef0..f0a8bd8 100644 --- a/test/dicer-headerparser.test.js +++ b/test/dicer-headerparser.test.js @@ -19,6 +19,11 @@ test('dicer-headerparser', async t => { expected: { foo: ['bar'] }, what: 'Header terminator across chunks' }, + { + source: ['Foo: bar\r', '\n\r\n'], + expected: { foo: ['bar'] }, + what: 'Header terminator split between CR and LFCRLF leaves no trailing CR on the value' + }, { source: ['Foo: bar\r', '\n\r', '\nextra'], cfg: {