add all
This commit is contained in:
@@ -0,0 +1,98 @@
|
||||
using System;
|
||||
|
||||
using Best.HTTP.Shared.Streams;
|
||||
using Best.HTTP.Shared.Logger;
|
||||
using Best.HTTP.Shared.PlatformSupport.Memory;
|
||||
|
||||
namespace Best.HTTP.Response.Decompression
|
||||
{
|
||||
public sealed class BrotliDecompressor : IDecompressor
|
||||
{
|
||||
#if ((NET_STANDARD_2_1 || UNITY_2021_2_OR_NEWER) && (UNITY_EDITOR || (!UNITY_WEBGL && !(ENABLE_MONO && UNITY_ANDROID)))) && !BESTHTTP_DISABLE_BROTLI
|
||||
private BufferSegmentStream decompressorInputStream;
|
||||
private BufferPoolMemoryStream decompressorOutputStream;
|
||||
private System.IO.Compression.BrotliStream decompressorStream;
|
||||
|
||||
byte[] copyBuffer = null;
|
||||
#endif
|
||||
|
||||
private int _minLengthToDecompress;
|
||||
|
||||
public static bool IsSupported()
|
||||
{
|
||||
// Not enabled under android with the mono runtime
|
||||
#if ((NET_STANDARD_2_1 || UNITY_2021_2_OR_NEWER) && (UNITY_EDITOR || (!UNITY_WEBGL && !(ENABLE_MONO && UNITY_ANDROID)))) && !BESTHTTP_DISABLE_BROTLI
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
public BrotliDecompressor(int minLengthToDecompress)
|
||||
{
|
||||
this._minLengthToDecompress = minLengthToDecompress;
|
||||
}
|
||||
|
||||
public (BufferSegment decompressed, bool releaseTheOld) Decompress(BufferSegment segment, bool forceDecompress, bool dataCanBeLarger, LoggingContext context)
|
||||
{
|
||||
#if ((NET_STANDARD_2_1 || UNITY_2021_2_OR_NEWER) && (UNITY_EDITOR || (!UNITY_WEBGL && !(ENABLE_MONO && UNITY_ANDROID)))) && !BESTHTTP_DISABLE_BROTLI
|
||||
if (decompressorInputStream == null)
|
||||
decompressorInputStream = new BufferSegmentStream();
|
||||
|
||||
if (segment.Data != null)
|
||||
decompressorInputStream.Write(segment);
|
||||
|
||||
if (!forceDecompress && decompressorInputStream.Length < _minLengthToDecompress)
|
||||
return (BufferSegment.Empty, false);
|
||||
|
||||
if (decompressorStream == null)
|
||||
{
|
||||
decompressorStream = new System.IO.Compression.BrotliStream(decompressorInputStream,
|
||||
System.IO.Compression.CompressionMode.Decompress,
|
||||
true);
|
||||
}
|
||||
|
||||
if (decompressorOutputStream == null)
|
||||
decompressorOutputStream = new BufferPoolMemoryStream();
|
||||
decompressorOutputStream.SetLength(0);
|
||||
|
||||
if (copyBuffer == null)
|
||||
copyBuffer = BufferPool.Get(4 * 1024, true);
|
||||
|
||||
int readCount;
|
||||
int sumReadCount = 0;
|
||||
while ((readCount = decompressorStream.Read(copyBuffer, 0, copyBuffer.Length)) != 0)
|
||||
{
|
||||
decompressorOutputStream.Write(copyBuffer, 0, readCount);
|
||||
sumReadCount += readCount;
|
||||
}
|
||||
|
||||
byte[] result = decompressorOutputStream.ToArray(dataCanBeLarger, context);
|
||||
|
||||
return (new BufferSegment(result, 0, dataCanBeLarger ? (int)decompressorOutputStream.Length : result.Length), false);
|
||||
#else
|
||||
return (BufferSegment.Empty, false);
|
||||
#endif
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
#if ((NET_STANDARD_2_1 || UNITY_2021_2_OR_NEWER) && (UNITY_EDITOR || (!UNITY_WEBGL && !(ENABLE_MONO && UNITY_ANDROID)))) && !BESTHTTP_DISABLE_BROTLI
|
||||
if (decompressorStream != null)
|
||||
decompressorStream.Dispose();
|
||||
decompressorStream = null;
|
||||
|
||||
if (decompressorInputStream != null)
|
||||
decompressorInputStream.Dispose();
|
||||
decompressorInputStream = null;
|
||||
|
||||
if (decompressorOutputStream != null)
|
||||
decompressorOutputStream.Dispose();
|
||||
decompressorOutputStream = null;
|
||||
|
||||
BufferPool.Release(copyBuffer);
|
||||
copyBuffer = null;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c05f975899e847c4891318adccf0d7c7
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,72 @@
|
||||
using Best.HTTP.Shared;
|
||||
using Best.HTTP.Shared.Logger;
|
||||
using Best.HTTP.Shared.PlatformSupport.Text;
|
||||
|
||||
namespace Best.HTTP.Response.Decompression
|
||||
{
|
||||
public static class DecompressorFactory
|
||||
{
|
||||
public const int MinLengthToDecompress = 256;
|
||||
|
||||
// cached header value
|
||||
private static string AcceptEncoding = null;
|
||||
|
||||
public static void SetupHeaders(HTTPRequest request)
|
||||
{
|
||||
if (!request.HasHeader("Accept-Encoding"))
|
||||
{
|
||||
if (AcceptEncoding == null)
|
||||
{
|
||||
var sb = StringBuilderPool.Get(4);
|
||||
|
||||
if (BrotliDecompressor.IsSupported())
|
||||
sb.Append("br, ");
|
||||
|
||||
if (GZipDecompressor.IsSupported)
|
||||
sb.Append("gzip, ");
|
||||
|
||||
if (DeflateDecompressor.IsSupported)
|
||||
sb.Append("deflate, ");
|
||||
|
||||
sb.Append("identity");
|
||||
|
||||
AcceptEncoding = StringBuilderPool.ReleaseAndGrab(sb);
|
||||
}
|
||||
|
||||
request.AddHeader("Accept-Encoding", AcceptEncoding);
|
||||
}
|
||||
}
|
||||
|
||||
public static IDecompressor GetDecompressor(string encoding, LoggingContext context)
|
||||
{
|
||||
if (encoding == null)
|
||||
return null;
|
||||
|
||||
switch (encoding.ToLowerInvariant())
|
||||
{
|
||||
// https://github.com/Benedicht/BestHTTP-Issues/issues/183
|
||||
case "none":
|
||||
|
||||
case "identity":
|
||||
case "utf-8":
|
||||
break;
|
||||
|
||||
case "gzip": return new GZipDecompressor(MinLengthToDecompress);
|
||||
|
||||
case "deflate": return new DeflateDecompressor(MinLengthToDecompress);
|
||||
|
||||
case "br":
|
||||
if (BrotliDecompressor.IsSupported())
|
||||
return new BrotliDecompressor(MinLengthToDecompress);
|
||||
else
|
||||
goto default;
|
||||
|
||||
default:
|
||||
HTTPManager.Logger.Warning(nameof(DecompressorFactory), $"GetDecompressor - unsupported encoding '{encoding}'!", context);
|
||||
break;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ce789361386b9024ba2747db2d9072e9
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,109 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 634cdddc7ce7ec144a040e546661043f
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,104 @@
|
||||
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 GZipDecompressor : IDecompressor
|
||||
{
|
||||
private BufferPoolMemoryStream decompressorInputStream;
|
||||
private BufferPoolMemoryStream decompressorOutputStream;
|
||||
private GZipStream decompressorStream;
|
||||
|
||||
private int MinLengthToDecompress = 256;
|
||||
|
||||
public static bool IsSupported = true;
|
||||
|
||||
public GZipDecompressor(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)
|
||||
{
|
||||
decompressorStream = new GZipStream(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);
|
||||
}
|
||||
|
||||
~GZipDecompressor()
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7f98615216f088c4fbe03e782e7c2dde
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,12 @@
|
||||
using System;
|
||||
|
||||
using Best.HTTP.Shared.Logger;
|
||||
using Best.HTTP.Shared.PlatformSupport.Memory;
|
||||
|
||||
namespace Best.HTTP.Response.Decompression
|
||||
{
|
||||
public interface IDecompressor : IDisposable
|
||||
{
|
||||
(BufferSegment decompressed, bool releaseTheOld) Decompress(BufferSegment segment, bool forceDecompress, bool dataCanBeLarger, LoggingContext context);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a690aef2560186140930ad5934385d48
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user