110 lines
4.1 KiB
C#
110 lines
4.1 KiB
C#
using System;
|
|
|
|
using Best.HTTP.Shared.Compression.Zlib;
|
|
using Best.HTTP.Shared.Logger;
|
|
using Best.HTTP.Shared.PlatformSupport.Memory;
|
|
using Best.HTTP.Shared.Streams;
|
|
|
|
namespace Best.HTTP.Response.Decompression
|
|
{
|
|
public sealed class DeflateDecompressor : IDecompressor
|
|
{
|
|
private BufferPoolMemoryStream decompressorInputStream;
|
|
private BufferPoolMemoryStream decompressorOutputStream;
|
|
private DeflateStream decompressorStream;
|
|
|
|
private int MinLengthToDecompress = 256;
|
|
|
|
public static bool IsSupported = true;
|
|
|
|
public DeflateDecompressor(int minLengthToDecompress)
|
|
{
|
|
this.MinLengthToDecompress = minLengthToDecompress;
|
|
}
|
|
|
|
public (BufferSegment decompressed, bool releaseTheOld) Decompress(BufferSegment segment, bool forceDecompress, bool dataCanBeLarger, LoggingContext context)
|
|
{
|
|
if (decompressorInputStream == null)
|
|
decompressorInputStream = new BufferPoolMemoryStream(segment.Count);
|
|
|
|
if (segment.Data != null)
|
|
decompressorInputStream.Write(segment.Data, segment.Offset, segment.Count);
|
|
|
|
if (!forceDecompress && decompressorInputStream.Length < MinLengthToDecompress)
|
|
return (BufferSegment.Empty, true);
|
|
|
|
decompressorInputStream.Position = 0;
|
|
|
|
if (decompressorStream == null)
|
|
{
|
|
// Had to change from this
|
|
// _baseStream = new ZlibBaseStream(stream, mode, level, ZlibStreamFlavor.DEFLATE, leaveOpen);
|
|
// this this:
|
|
// _baseStream = new ZlibBaseStream(stream, mode, level, ZlibStreamFlavor.ZLIB, leaveOpen);
|
|
// Because there are two bytes in the header additionally to what the deflate flavor expects that the zlib one handles fine.
|
|
decompressorStream = new DeflateStream(decompressorInputStream,
|
|
CompressionMode.Decompress,
|
|
CompressionLevel.Default,
|
|
true);
|
|
decompressorStream.FlushMode = FlushType.Sync;
|
|
}
|
|
|
|
if (decompressorOutputStream == null)
|
|
decompressorOutputStream = new BufferPoolMemoryStream();
|
|
decompressorOutputStream.SetLength(0);
|
|
|
|
byte[] copyBuffer = BufferPool.Get(1024, true);
|
|
|
|
int readCount;
|
|
int sumReadCount = 0;
|
|
while ((readCount = decompressorStream.Read(copyBuffer, 0, copyBuffer.Length)) != 0)
|
|
{
|
|
decompressorOutputStream.Write(copyBuffer, 0, readCount);
|
|
sumReadCount += readCount;
|
|
}
|
|
|
|
BufferPool.Release(copyBuffer);
|
|
|
|
// If no read is done (returned with any data) don't zero out the input stream, as it would delete any not yet used data.
|
|
if (sumReadCount > 0)
|
|
decompressorStream.SetLength(0);
|
|
|
|
byte[] result = decompressorOutputStream.ToArray(dataCanBeLarger, context);
|
|
|
|
return (new BufferSegment(result, 0, dataCanBeLarger ? (int)decompressorOutputStream.Length : result.Length), true);
|
|
}
|
|
|
|
~DeflateDecompressor()
|
|
{
|
|
Dispose();
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
if (decompressorInputStream != null)
|
|
decompressorInputStream.Dispose();
|
|
decompressorInputStream = null;
|
|
|
|
if (decompressorOutputStream != null)
|
|
decompressorOutputStream.Dispose();
|
|
decompressorOutputStream = null;
|
|
|
|
if (decompressorStream != null)
|
|
{
|
|
// If the decompressor closed before receiving data, or it's incomplete, disposing (eg. closing) it
|
|
// throws an execption like this:
|
|
// "Missing or incomplete GZIP trailer. Expected 8 bytes, got 0."
|
|
try
|
|
{
|
|
decompressorStream.Dispose();
|
|
}
|
|
catch
|
|
{ }
|
|
}
|
|
decompressorStream = null;
|
|
|
|
GC.SuppressFinalize(this);
|
|
}
|
|
}
|
|
}
|