DeflateDecompressor.cs 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109
  1. using System;
  2. using Best.HTTP.Shared.Compression.Zlib;
  3. using Best.HTTP.Shared.Logger;
  4. using Best.HTTP.Shared.PlatformSupport.Memory;
  5. using Best.HTTP.Shared.Streams;
  6. namespace Best.HTTP.Response.Decompression
  7. {
  8. public sealed class DeflateDecompressor : IDecompressor
  9. {
  10. private BufferPoolMemoryStream decompressorInputStream;
  11. private BufferPoolMemoryStream decompressorOutputStream;
  12. private DeflateStream decompressorStream;
  13. private int MinLengthToDecompress = 256;
  14. public static bool IsSupported = true;
  15. public DeflateDecompressor(int minLengthToDecompress)
  16. {
  17. this.MinLengthToDecompress = minLengthToDecompress;
  18. }
  19. public (BufferSegment decompressed, bool releaseTheOld) Decompress(BufferSegment segment, bool forceDecompress, bool dataCanBeLarger, LoggingContext context)
  20. {
  21. if (decompressorInputStream == null)
  22. decompressorInputStream = new BufferPoolMemoryStream(segment.Count);
  23. if (segment.Data != null)
  24. decompressorInputStream.Write(segment.Data, segment.Offset, segment.Count);
  25. if (!forceDecompress && decompressorInputStream.Length < MinLengthToDecompress)
  26. return (BufferSegment.Empty, true);
  27. decompressorInputStream.Position = 0;
  28. if (decompressorStream == null)
  29. {
  30. // Had to change from this
  31. // _baseStream = new ZlibBaseStream(stream, mode, level, ZlibStreamFlavor.DEFLATE, leaveOpen);
  32. // this this:
  33. // _baseStream = new ZlibBaseStream(stream, mode, level, ZlibStreamFlavor.ZLIB, leaveOpen);
  34. // Because there are two bytes in the header additionally to what the deflate flavor expects that the zlib one handles fine.
  35. decompressorStream = new DeflateStream(decompressorInputStream,
  36. CompressionMode.Decompress,
  37. CompressionLevel.Default,
  38. true);
  39. decompressorStream.FlushMode = FlushType.Sync;
  40. }
  41. if (decompressorOutputStream == null)
  42. decompressorOutputStream = new BufferPoolMemoryStream();
  43. decompressorOutputStream.SetLength(0);
  44. byte[] copyBuffer = BufferPool.Get(1024, true);
  45. int readCount;
  46. int sumReadCount = 0;
  47. while ((readCount = decompressorStream.Read(copyBuffer, 0, copyBuffer.Length)) != 0)
  48. {
  49. decompressorOutputStream.Write(copyBuffer, 0, readCount);
  50. sumReadCount += readCount;
  51. }
  52. BufferPool.Release(copyBuffer);
  53. // 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.
  54. if (sumReadCount > 0)
  55. decompressorStream.SetLength(0);
  56. byte[] result = decompressorOutputStream.ToArray(dataCanBeLarger, context);
  57. return (new BufferSegment(result, 0, dataCanBeLarger ? (int)decompressorOutputStream.Length : result.Length), true);
  58. }
  59. ~DeflateDecompressor()
  60. {
  61. Dispose();
  62. }
  63. public void Dispose()
  64. {
  65. if (decompressorInputStream != null)
  66. decompressorInputStream.Dispose();
  67. decompressorInputStream = null;
  68. if (decompressorOutputStream != null)
  69. decompressorOutputStream.Dispose();
  70. decompressorOutputStream = null;
  71. if (decompressorStream != null)
  72. {
  73. // If the decompressor closed before receiving data, or it's incomplete, disposing (eg. closing) it
  74. // throws an execption like this:
  75. // "Missing or incomplete GZIP trailer. Expected 8 bytes, got 0."
  76. try
  77. {
  78. decompressorStream.Dispose();
  79. }
  80. catch
  81. { }
  82. }
  83. decompressorStream = null;
  84. GC.SuppressFinalize(this);
  85. }
  86. }
  87. }