Skip to content

Commit 88cfaa6

Browse files
adamreevepiksel
authored andcommitted
Merge PR #225: Fix flushing of GZipOutputStream
* Fix flushing a GZipOutputStream When flushing a GZipOutputStream, ensure we deflate all data in the input buffer and write it to the underlying stream before we flush the underlying stream. * Fix infinite loop when flushing DeflaterOutputStream with no compression DeflaterEngine.DeflateStored would always write more output even if there was no more input data to write, resulting in an infinite loop.
1 parent 1a12d9c commit 88cfaa6

File tree

4 files changed

+66
-3
lines changed

4 files changed

+66
-3
lines changed

src/ICSharpCode.SharpZipLib/GZip/GzipInputStream.cs

+3
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,9 @@ public override int Read(byte[] buffer, int offset, int count)
139139
if (inf.IsFinished)
140140
{
141141
ReadFooter();
142+
} else if (inf.RemainingInput == 0) {
143+
// If the stream is not finished but we have no more data to read, don't keep looping forever
144+
throw new GZipException("Unexpected EOF");
142145
}
143146

144147
if (bytesRead > 0)

src/ICSharpCode.SharpZipLib/Zip/Compression/DeflaterEngine.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -643,7 +643,7 @@ private bool DeflateStored(bool flush, bool finish)
643643

644644
huffman.FlushStoredBlock(window, blockStart, storedLength, lastBlock);
645645
blockStart += storedLength;
646-
return !lastBlock;
646+
return !(lastBlock || storedLength == 0);
647647
}
648648
return true;
649649
}

src/ICSharpCode.SharpZipLib/Zip/Compression/Streams/DeflaterOutputStream.cs

+7-2
Original file line numberDiff line numberDiff line change
@@ -241,7 +241,12 @@ protected void InitializeAESPassword(ZipEntry entry, string rawPassword,
241241
/// </summary>
242242
protected void Deflate()
243243
{
244-
while (!deflater_.IsNeedingInput)
244+
Deflate(false);
245+
}
246+
247+
private void Deflate(bool flushing)
248+
{
249+
while (flushing || !deflater_.IsNeedingInput)
245250
{
246251
int deflateCount = deflater_.Deflate(buffer_, 0, buffer_.Length);
247252

@@ -380,7 +385,7 @@ public override int Read(byte[] buffer, int offset, int count)
380385
public override void Flush()
381386
{
382387
deflater_.Flush();
383-
Deflate();
388+
Deflate(true);
384389
baseOutputStream_.Flush();
385390
}
386391

test/ICSharpCode.SharpZipLib.Tests/GZip/GZipTests.cs

+55
Original file line numberDiff line numberDiff line change
@@ -289,6 +289,61 @@ public void TrailingGarbage()
289289
}
290290
}
291291

292+
/// <summary>
293+
/// Test that if we flush a GZip output stream then all data that has been written
294+
/// is flushed through to the underlying stream and can be successfully read back
295+
/// even if the stream is not yet finished.
296+
/// </summary>
297+
[Test]
298+
[Category("GZip")]
299+
public void FlushToUnderlyingStream()
300+
{
301+
var ms = new MemoryStream();
302+
var outStream = new GZipOutputStream(ms);
303+
304+
byte[] buf = new byte[100000];
305+
var rnd = new Random();
306+
rnd.NextBytes(buf);
307+
308+
outStream.Write(buf, 0, buf.Length);
309+
// Flush output stream but don't finish it yet
310+
outStream.Flush();
311+
312+
ms.Seek(0, SeekOrigin.Begin);
313+
314+
var inStream = new GZipInputStream(ms);
315+
byte[] buf2 = new byte[buf.Length];
316+
int currentIndex = 0;
317+
int count = buf2.Length;
318+
319+
while (true)
320+
{
321+
try
322+
{
323+
int numRead = inStream.Read(buf2, currentIndex, count);
324+
if (numRead <= 0)
325+
{
326+
break;
327+
}
328+
currentIndex += numRead;
329+
count -= numRead;
330+
}
331+
catch (GZipException)
332+
{
333+
// We should get an unexpected EOF exception once we've read all
334+
// data as the stream isn't yet finished.
335+
break;
336+
}
337+
}
338+
339+
Assert.AreEqual(0, count);
340+
341+
for (int i = 0; i < buf.Length; ++i)
342+
{
343+
Assert.AreEqual(buf2[i], buf[i]);
344+
}
345+
}
346+
292347
[Test]
293348
[Category("GZip")]
294349
[Category("Performance")]

0 commit comments

Comments
 (0)