mirror of
https://github.com/wiremock/WireMock.Net.git
synced 2026-04-26 18:28:27 +02:00
Add support for GZip and Deflate (#439)
* gzip - wip * wip * tests * fix gzip and deflate * CheckIfShouldKillVBCSCompiler * DisableRequestBodyDecompressing
This commit is contained in:
@@ -52,5 +52,10 @@ namespace WireMock.Util
|
||||
/// The detected body type (detection based on Content-Type).
|
||||
/// </summary>
|
||||
public BodyType DetectedBodyTypeFromContentType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The detected compression.
|
||||
/// </summary>
|
||||
public string DetectedCompression { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,12 @@
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.IO.Compression;
|
||||
using System.Linq;
|
||||
using System.Net.Http.Headers;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using JetBrains.Annotations;
|
||||
using Newtonsoft.Json;
|
||||
using WireMock.Http;
|
||||
using WireMock.Matchers;
|
||||
using WireMock.Types;
|
||||
@@ -108,15 +109,17 @@ namespace WireMock.Util
|
||||
return BodyType.Bytes;
|
||||
}
|
||||
|
||||
public static async Task<BodyData> Parse([NotNull] Stream stream, [CanBeNull] string contentType = null, bool deserializeJson = true)
|
||||
public static async Task<BodyData> Parse([NotNull] BodyParserSettings settings)
|
||||
{
|
||||
Check.NotNull(stream, nameof(stream));
|
||||
Check.NotNull(settings, nameof(settings));
|
||||
|
||||
var bodyWithContentEncoding = await ReadBytesAsync(settings.Stream, settings.ContentEncoding, settings.DecompressGZipAndDeflate);
|
||||
var data = new BodyData
|
||||
{
|
||||
BodyAsBytes = await ReadBytesAsync(stream),
|
||||
BodyAsBytes = bodyWithContentEncoding.Value,
|
||||
DetectedCompression = bodyWithContentEncoding.Key,
|
||||
DetectedBodyType = BodyType.Bytes,
|
||||
DetectedBodyTypeFromContentType = DetectBodyTypeFromContentType(contentType)
|
||||
DetectedBodyTypeFromContentType = DetectBodyTypeFromContentType(settings.ContentType)
|
||||
};
|
||||
|
||||
// In case of MultiPart: check if the BodyAsBytes is a valid UTF8 or ASCII string, in that case read as String else keep as-is
|
||||
@@ -141,7 +144,7 @@ namespace WireMock.Util
|
||||
data.DetectedBodyType = BodyType.String;
|
||||
|
||||
// If string is not null or empty, try to deserialize the string to a JObject
|
||||
if (deserializeJson && !string.IsNullOrEmpty(data.BodyAsString))
|
||||
if (settings.DeserializeJson && !string.IsNullOrEmpty(data.BodyAsString))
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -161,12 +164,21 @@ namespace WireMock.Util
|
||||
|
||||
return data;
|
||||
}
|
||||
private static async Task<byte[]> ReadBytesAsync(Stream stream)
|
||||
|
||||
private static async Task<KeyValuePair<string, byte[]>> ReadBytesAsync(Stream stream, string contentEncoding = null, bool decompressGZipAndDeflate = true)
|
||||
{
|
||||
using (var memoryStream = new MemoryStream())
|
||||
{
|
||||
await stream.CopyToAsync(memoryStream);
|
||||
return memoryStream.ToArray();
|
||||
byte[] data = memoryStream.ToArray();
|
||||
|
||||
string type = contentEncoding?.ToLowerInvariant();
|
||||
if (decompressGZipAndDeflate && (type == "gzip" || type == "deflate"))
|
||||
{
|
||||
return new KeyValuePair<string, byte[]>(type, CompressionUtils.Decompress(type, data));
|
||||
}
|
||||
|
||||
return new KeyValuePair<string, byte[]>(null, data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
17
src/WireMock.Net/Util/BodyParserSettings.cs
Normal file
17
src/WireMock.Net/Util/BodyParserSettings.cs
Normal file
@@ -0,0 +1,17 @@
|
||||
using System.IO;
|
||||
|
||||
namespace WireMock.Util
|
||||
{
|
||||
internal class BodyParserSettings
|
||||
{
|
||||
public Stream Stream { get; set; }
|
||||
|
||||
public string ContentType { get; set; }
|
||||
|
||||
public string ContentEncoding { get; set; }
|
||||
|
||||
public bool DecompressGZipAndDeflate { get; set; } = true;
|
||||
|
||||
public bool DeserializeJson { get; set; } = true;
|
||||
}
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
using FastDeepCloner;
|
||||
using System;
|
||||
using System.Runtime.Serialization;
|
||||
|
||||
namespace WireMock.Util
|
||||
{
|
||||
internal static class CloneUtils
|
||||
{
|
||||
private static FastDeepClonerSettings settings = new FastDeepCloner.FastDeepClonerSettings()
|
||||
{
|
||||
FieldType = FastDeepCloner.FieldType.FieldInfo,
|
||||
OnCreateInstance = new FastDeepCloner.Extensions.CreateInstance((Type type) =>
|
||||
{
|
||||
#if !NETSTANDARD1_3
|
||||
return FormatterServices.GetUninitializedObject(type);
|
||||
#else
|
||||
|
||||
#endif
|
||||
})
|
||||
};
|
||||
|
||||
public static T DeepClone<T>(T objectToBeCloned) where T : class
|
||||
{
|
||||
//return CloneExtensionsEx.CloneFactory.GetClone(objectToBeCloned);
|
||||
// Expression.Lambda<Func<object>>(Expression.New(type)).Compile()
|
||||
return FastDeepCloner.DeepCloner.Clone(objectToBeCloned, settings);
|
||||
}
|
||||
}
|
||||
}
|
||||
49
src/WireMock.Net/Util/CompressionUtils.cs
Normal file
49
src/WireMock.Net/Util/CompressionUtils.cs
Normal file
@@ -0,0 +1,49 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.IO.Compression;
|
||||
|
||||
namespace WireMock.Util
|
||||
{
|
||||
internal static class CompressionUtils
|
||||
{
|
||||
public static byte[] Compress(string contentEncoding, byte[] data)
|
||||
{
|
||||
using (var compressedStream = new MemoryStream())
|
||||
using (var zipStream = Create(contentEncoding, compressedStream, CompressionMode.Compress))
|
||||
{
|
||||
zipStream.Write(data, 0, data.Length);
|
||||
|
||||
#if !NETSTANDARD1_3
|
||||
zipStream.Close();
|
||||
#endif
|
||||
return compressedStream.ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
public static byte[] Decompress(string contentEncoding, byte[] data)
|
||||
{
|
||||
using (var compressedStream = new MemoryStream(data))
|
||||
using (var zipStream = Create(contentEncoding, compressedStream, CompressionMode.Decompress))
|
||||
using (var resultStream = new MemoryStream())
|
||||
{
|
||||
zipStream.CopyTo(resultStream);
|
||||
return resultStream.ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
private static Stream Create(string contentEncoding, Stream stream, CompressionMode mode)
|
||||
{
|
||||
switch (contentEncoding)
|
||||
{
|
||||
case "gzip":
|
||||
return new GZipStream(stream, mode);
|
||||
|
||||
case "deflate":
|
||||
return new DeflateStream(stream, mode);
|
||||
|
||||
default:
|
||||
throw new NotSupportedException($"ContentEncoding '{contentEncoding}' is not supported.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace WireMock.Util
|
||||
{
|
||||
public class IndexableDictionary<TKey, TValue> : Dictionary<TKey, TValue>
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the value associated with the specified index.
|
||||
/// </summary>
|
||||
/// <param name="index"> The index of the value to get.</param>
|
||||
/// <returns>The value associated with the specified index.</returns>
|
||||
public TValue this[int index]
|
||||
{
|
||||
get
|
||||
{
|
||||
// get the item for that index.
|
||||
if (index < 0 || index > Count)
|
||||
{
|
||||
throw new KeyNotFoundException();
|
||||
}
|
||||
return Values.Cast<TValue>().ToArray()[index];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,80 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Threading;
|
||||
|
||||
namespace WireMock.Util
|
||||
{
|
||||
/// <summary>
|
||||
/// http://johnculviner.com/achieving-named-lock-locker-functionality-in-c-4-0/
|
||||
/// </summary>
|
||||
internal class NamedReaderWriterLocker
|
||||
{
|
||||
private readonly ConcurrentDictionary<string, ReaderWriterLockSlim> _lockDict = new ConcurrentDictionary<string, ReaderWriterLockSlim>();
|
||||
|
||||
public ReaderWriterLockSlim GetLock(string name)
|
||||
{
|
||||
return _lockDict.GetOrAdd(name, s => new ReaderWriterLockSlim());
|
||||
}
|
||||
|
||||
public TResult RunWithReadLock<TResult>(string name, Func<TResult> body)
|
||||
{
|
||||
var rwLock = GetLock(name);
|
||||
try
|
||||
{
|
||||
rwLock.EnterReadLock();
|
||||
return body();
|
||||
}
|
||||
finally
|
||||
{
|
||||
rwLock.ExitReadLock();
|
||||
}
|
||||
}
|
||||
|
||||
public void RunWithReadLock(string name, Action body)
|
||||
{
|
||||
var rwLock = GetLock(name);
|
||||
try
|
||||
{
|
||||
rwLock.EnterReadLock();
|
||||
body();
|
||||
}
|
||||
finally
|
||||
{
|
||||
rwLock.ExitReadLock();
|
||||
}
|
||||
}
|
||||
|
||||
public TResult RunWithWriteLock<TResult>(string name, Func<TResult> body)
|
||||
{
|
||||
var rwLock = GetLock(name);
|
||||
try
|
||||
{
|
||||
rwLock.EnterWriteLock();
|
||||
return body();
|
||||
}
|
||||
finally
|
||||
{
|
||||
rwLock.ExitWriteLock();
|
||||
}
|
||||
}
|
||||
|
||||
public void RunWithWriteLock(string name, Action body)
|
||||
{
|
||||
var rwLock = GetLock(name);
|
||||
try
|
||||
{
|
||||
rwLock.EnterWriteLock();
|
||||
body();
|
||||
}
|
||||
finally
|
||||
{
|
||||
rwLock.ExitWriteLock();
|
||||
}
|
||||
}
|
||||
|
||||
public void RemoveLock(string name)
|
||||
{
|
||||
_lockDict.TryRemove(name, out _);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user