GZipDecompressor.cs 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104
  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 GZipDecompressor : IDecompressor
  9. {
  10. private BufferPoolMemoryStream decompressorInputStream;
  11. private BufferPoolMemoryStream decompressorOutputStream;
  12. private GZipStream decompressorStream;
  13. private int MinLengthToDecompress = 256;
  14. public static bool IsSupported = true;
  15. public GZipDecompressor(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. decompressorStream = new GZipStream(decompressorInputStream,
  31. CompressionMode.Decompress,
  32. CompressionLevel.Default,
  33. true);
  34. decompressorStream.FlushMode = FlushType.Sync;
  35. }
  36. if (decompressorOutputStream == null)
  37. decompressorOutputStream = new BufferPoolMemoryStream();
  38. decompressorOutputStream.SetLength(0);
  39. byte[] copyBuffer = BufferPool.Get(1024, true);
  40. int readCount;
  41. int sumReadCount = 0;
  42. while ((readCount = decompressorStream.Read(copyBuffer, 0, copyBuffer.Length)) != 0)
  43. {
  44. decompressorOutputStream.Write(copyBuffer, 0, readCount);
  45. sumReadCount += readCount;
  46. }
  47. BufferPool.Release(copyBuffer);
  48. // 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.
  49. if (sumReadCount > 0)
  50. decompressorStream.SetLength(0);
  51. byte[] result = decompressorOutputStream.ToArray(dataCanBeLarger, context);
  52. return (new BufferSegment(result, 0, dataCanBeLarger ? (int)decompressorOutputStream.Length : result.Length), true);
  53. }
  54. ~GZipDecompressor()
  55. {
  56. Dispose();
  57. }
  58. public void Dispose()
  59. {
  60. if (decompressorInputStream != null)
  61. decompressorInputStream.Dispose();
  62. decompressorInputStream = null;
  63. if (decompressorOutputStream != null)
  64. decompressorOutputStream.Dispose();
  65. decompressorOutputStream = null;
  66. if (decompressorStream != null)
  67. {
  68. // If the decompressor closed before receiving data, or it's incomplete, disposing (eg. closing) it
  69. // throws an execption like this:
  70. // "Missing or incomplete GZIP trailer. Expected 8 bytes, got 0."
  71. try
  72. {
  73. decompressorStream.Dispose();
  74. }
  75. catch
  76. { }
  77. }
  78. decompressorStream = null;
  79. GC.SuppressFinalize(this);
  80. }
  81. }
  82. }