Skip to content

Commit 0d4ae13

Browse files
committed
zlib: restore unzip auto-detection on reset
Restore the initial UNZIP mode and clear partial gzip header state when resetting an Unzip stream. Preserve GUNZIP mode for internal resets between concatenated gzip members. Signed-off-by: Archkon <180910180+Archkon@users.noreply.github.com>
1 parent fe3b3b8 commit 0d4ae13

2 files changed

Lines changed: 54 additions & 2 deletions

File tree

src/node_zlib.cc

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -188,7 +188,10 @@ class ZlibContext final : public MemoryRetainer {
188188
void SetFlush(int flush);
189189
void GetAfterWriteOffsets(uint32_t* avail_in, uint32_t* avail_out) const;
190190
CompressionError GetErrorInfo() const;
191-
inline void SetMode(node_zlib_mode mode) { mode_ = mode; }
191+
inline void SetMode(node_zlib_mode mode) {
192+
mode_ = mode;
193+
initial_mode_ = mode;
194+
}
192195
CompressionError ResetStream();
193196

194197
// Zlib-specific:
@@ -212,6 +215,7 @@ class ZlibContext final : public MemoryRetainer {
212215

213216
private:
214217
CompressionError ErrorForMessage(const char* message) const;
218+
CompressionError ResetStream(node_zlib_mode target_mode);
215219
CompressionError SetDictionary();
216220
bool InitZlib();
217221

@@ -222,6 +226,7 @@ class ZlibContext final : public MemoryRetainer {
222226
int level_ = 0;
223227
int mem_level_ = 0;
224228
node_zlib_mode mode_ = NONE;
229+
node_zlib_mode initial_mode_ = NONE;
225230
int strategy_ = 0;
226231
int window_bits_ = 0;
227232
bool reject_garbage_after_end_ = false;
@@ -1144,7 +1149,7 @@ void ZlibContext::DoThreadPoolWork() {
11441149
// Trailing zero bytes are okay, though, since they are frequently
11451150
// used for padding.
11461151

1147-
ResetStream();
1152+
ResetStream(mode_);
11481153
err_ = inflate(&strm_, flush_);
11491154
}
11501155
break;
@@ -1209,6 +1214,10 @@ CompressionError ZlibContext::GetErrorInfo() const {
12091214

12101215

12111216
CompressionError ZlibContext::ResetStream() {
1217+
return ResetStream(initial_mode_);
1218+
}
1219+
1220+
CompressionError ZlibContext::ResetStream(node_zlib_mode target_mode) {
12121221
bool first_init_call = InitZlib();
12131222
if (first_init_call && err_ != Z_OK) {
12141223
return ErrorForMessage("Failed to init stream before reset");
@@ -1225,6 +1234,7 @@ CompressionError ZlibContext::ResetStream() {
12251234
case INFLATE:
12261235
case INFLATERAW:
12271236
case GUNZIP:
1237+
case UNZIP:
12281238
err_ = inflateReset(&strm_);
12291239
break;
12301240
default:
@@ -1234,6 +1244,9 @@ CompressionError ZlibContext::ResetStream() {
12341244
if (err_ != Z_OK)
12351245
return ErrorForMessage("Failed to reset stream");
12361246

1247+
mode_ = target_mode;
1248+
if (mode_ == UNZIP) gzip_id_bytes_read_ = 0;
1249+
12371250
return SetDictionary();
12381251
}
12391252

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
'use strict';
2+
3+
const common = require('../common');
4+
const assert = require('node:assert');
5+
const zlib = require('node:zlib');
6+
7+
function testReset(prefix, input, expected) {
8+
const output = [];
9+
const unzip = zlib.createUnzip()
10+
.on('error', common.mustNotCall())
11+
.on('data', (chunk) => output.push(chunk))
12+
.on('end', common.mustCall(() => {
13+
assert.strictEqual(Buffer.concat(output).toString(), expected);
14+
}));
15+
16+
unzip.write(prefix, common.mustCall(() => {
17+
unzip.reset();
18+
unzip.end(input);
19+
}));
20+
}
21+
22+
// Reset while gzip auto-detection is halfway through.
23+
testReset(
24+
Buffer.from([0x1f]),
25+
zlib.gzipSync('hello'),
26+
'hello',
27+
);
28+
29+
// Reset after auto-detection selected INFLATE. The new input must be detected
30+
// as GUNZIP so that every concatenated gzip member is processed.
31+
testReset(
32+
zlib.deflateSync('discarded').subarray(0, 2),
33+
Buffer.concat([
34+
zlib.gzipSync('abc'),
35+
zlib.gzipSync('def'),
36+
zlib.gzipSync('ghi'),
37+
]),
38+
'abcdefghi',
39+
);

0 commit comments

Comments
 (0)