diff --git a/src/node_zlib.cc b/src/node_zlib.cc index da60d4430f3042..f7e808cf1b1d82 100644 --- a/src/node_zlib.cc +++ b/src/node_zlib.cc @@ -271,8 +271,12 @@ class ZCtx : public AsyncWrap { // Acceptable error states depend on the type of zlib stream. switch (ctx->err_) { case Z_OK: - case Z_STREAM_END: case Z_BUF_ERROR: + if (ctx->strm_.avail_out != 0 && ctx->flush_ == Z_FINISH) { + ZCtx::Error(ctx, "unexpected end of file"); + return false; + } + case Z_STREAM_END: // normal statuses, not fatal break; case Z_NEED_DICT: diff --git a/test/parallel/test-zlib-dictionary.js b/test/parallel/test-zlib-dictionary.js index 109f4273f767d0..7e11b1966c69bf 100644 --- a/test/parallel/test-zlib-dictionary.js +++ b/test/parallel/test-zlib-dictionary.js @@ -1,12 +1,12 @@ 'use strict'; // test compression/decompression with dictionary -var common = require('../common'); -var assert = require('assert'); -var zlib = require('zlib'); -var path = require('path'); +const common = require('../common'); +const assert = require('assert'); +const zlib = require('zlib'); +const path = require('path'); -var spdyDict = new Buffer([ +const spdyDict = new Buffer([ 'optionsgetheadpostputdeletetraceacceptaccept-charsetaccept-encodingaccept-', 'languageauthorizationexpectfromhostif-modified-sinceif-matchif-none-matchi', 'f-rangeif-unmodifiedsincemax-forwardsproxy-authorizationrangerefererteuser', @@ -22,54 +22,69 @@ var spdyDict = new Buffer([ '.1statusversionurl\0' ].join('')); -var deflate = zlib.createDeflate({ dictionary: spdyDict }); - -var input = [ +const input = [ 'HTTP/1.1 200 Ok', 'Server: node.js', 'Content-Length: 0', '' ].join('\r\n'); -var called = 0; - -// -// We'll use clean-new inflate stream each time -// and .reset() old dirty deflate one -// -function run(num) { - var inflate = zlib.createInflate({ dictionary: spdyDict }); - - if (num === 2) { - deflate.reset(); - deflate.removeAllListeners('data'); - } +function basicDictionaryTest() { + let output = ''; + const deflate = zlib.createDeflate({ dictionary: spdyDict }); + const inflate = zlib.createInflate({ dictionary: spdyDict }); - // Put data into deflate stream deflate.on('data', function(chunk) { inflate.write(chunk); }); - // Get data from inflate stream - var output = []; inflate.on('data', function(chunk) { - output.push(chunk); + output += chunk; + }); + + deflate.on('end', function() { + inflate.end(); }); + inflate.on('end', function() { - called++; + assert.equal(input, output); + }); + + deflate.write(input); + deflate.end(); +} - assert.equal(output.join(''), input); +function deflateResetDictionaryTest() { + let doneReset = false; + let output = ''; + const deflate = zlib.createDeflate({ dictionary: spdyDict }); + const inflate = zlib.createInflate({ dictionary: spdyDict }); - if (num < 2) run(num + 1); + deflate.on('data', function(chunk) { + if (doneReset) + inflate.write(chunk); + }); + + inflate.on('data', function(chunk) { + output += chunk; + }); + + deflate.on('end', function() { + inflate.end(); + }); + + inflate.on('end', function() { + assert.equal(input, output); }); deflate.write(input); deflate.flush(function() { - inflate.end(); + deflate.reset(); + doneReset = true; + deflate.write(input); + deflate.end(); }); } -run(1); -process.on('exit', function() { - assert.equal(called, 2); -}); +basicDictionaryTest(); +deflateResetDictionaryTest(); diff --git a/test/parallel/test-zlib-truncated.js b/test/parallel/test-zlib-truncated.js new file mode 100644 index 00000000000000..9a716f8d0b2110 --- /dev/null +++ b/test/parallel/test-zlib-truncated.js @@ -0,0 +1,49 @@ +'use strict'; +// tests zlib streams with truncated compressed input + +const common = require('../common'); +const assert = require('assert'); +const zlib = require ('zlib'); + +const inputString = 'ΩΩLorem ipsum dolor sit amet, consectetur adipiscing el' + + 'it. Morbi faucibus, purus at gravida dictum, libero arcu convallis la' + + 'cus, in commodo libero metus eu nisi. Nullam commodo, neque nec porta' + + ' placerat, nisi est fermentum augue, vitae gravida tellus sapien sit ' + + 'amet tellus. Aenean non diam orci. Proin quis elit turpis. Suspendiss' + + 'e non diam ipsum. Suspendisse nec ullamcorper odio. Vestibulum arcu m' + + 'i, sodales non suscipit id, ultrices ut massa. Sed ac sem sit amet ar' + + 'cu malesuada fermentum. Nunc sed. '; + +[ + { comp: 'gzip', decomp: 'gunzip', decompSync: 'gunzipSync' }, + { comp: 'gzip', decomp: 'unzip', decompSync: 'unzipSync' }, + { comp: 'deflate', decomp: 'inflate', decompSync: 'inflateSync' }, + { comp: 'deflateRaw', decomp: 'inflateRaw', decompSync: 'inflateRawSync' } +].forEach(function(methods) { + zlib[methods.comp](inputString, function(err, compressed) { + assert(!err); + let truncated = compressed.slice(0, compressed.length / 2); + + // sync sanity + assert.doesNotThrow(function() { + let decompressed = zlib[methods.decompSync](compressed); + assert.equal(decompressed, inputString); + }); + + // async sanity + zlib[methods.decomp](compressed, function(err, result) { + assert.ifError(err); + assert.equal(result, inputString); + }); + + // sync truncated input test + assert.throws(function() { + zlib[methods.decompSync](truncated); + }, /unexpected end of file/); + + // async truncated input test + zlib[methods.decomp](truncated, function(err, result) { + assert(/unexpected end of file/.test(err.message)); + }); + }); +});