mirror of
https://github.com/wiremock/WireMock.Net.git
synced 2026-04-27 02:38:30 +02:00
Update BodyParser logic (#212)
* Update BodyParser logic * update logic for byte[] * small update * MyGetKey * myget * dotnet nuget push * dotnet build * Release * . * StringContent * 1.0.4.18-preview-02 * Debug * 1.0.4.18-preview-02 * disable some proxy tests * myget * packagesToPack * fix * <AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects> <GenerateBindingRedirectsOutputType>true</GenerateBindingRedirectsOutputType> * Release * <VersionPrefix>1.0.4.18</VersionPrefix> * fix * BodyParserTests * ResponseBodyData (#216) * ResponseBodyData * refactor tests * LogEntryMapperTests
This commit is contained in:
@@ -26,5 +26,30 @@ namespace WireMock.Util
|
||||
/// The body (as bytearray).
|
||||
/// </summary>
|
||||
public byte[] BodyAsBytes { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether child objects to be indented according to the Newtonsoft.Json.JsonTextWriter.Indentation and Newtonsoft.Json.JsonTextWriter.IndentChar settings.
|
||||
/// </summary>
|
||||
public bool? BodyAsJsonIndented { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the body as a file.
|
||||
/// </summary>
|
||||
public string BodyAsFile { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Is the body as file cached?
|
||||
/// </summary>
|
||||
public bool? BodyAsFileIsCached { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The detected body type (detection based on body content).
|
||||
/// </summary>
|
||||
public BodyType DetectedBodyType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The detected body type (detection based on Content-Type).
|
||||
/// </summary>
|
||||
public BodyType DetectedBodyTypeFromContentType { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -4,36 +4,112 @@ using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using JetBrains.Annotations;
|
||||
using MimeKit;
|
||||
using Newtonsoft.Json;
|
||||
using WireMock.Matchers;
|
||||
using WireMock.Validation;
|
||||
|
||||
namespace WireMock.Util
|
||||
{
|
||||
internal static class BodyParser
|
||||
{
|
||||
private static readonly string[] JsonContentTypes =
|
||||
{
|
||||
"application/json",
|
||||
"application/vnd.api+json"
|
||||
private static readonly Encoding DefaultEncoding = Encoding.UTF8;
|
||||
|
||||
/*
|
||||
HEAD - No defined body semantics.
|
||||
GET - No defined body semantics.
|
||||
PUT - Body supported.
|
||||
POST - Body supported.
|
||||
DELETE - No defined body semantics.
|
||||
TRACE - Body not supported.
|
||||
OPTIONS - Body supported but no semantics on usage (maybe in the future).
|
||||
CONNECT - No defined body semantics
|
||||
PATCH - Body supported.
|
||||
*/
|
||||
private static readonly string[] AllowedBodyParseMethods = { "PUT", "POST", "OPTIONS", "PATCH" };
|
||||
|
||||
private static readonly IStringMatcher[] JsonContentTypesMatchers = {
|
||||
new WildcardMatcher("application/json", true),
|
||||
new WildcardMatcher("application/vnd.*+json", true)
|
||||
};
|
||||
|
||||
private static readonly string[] TextContentTypes =
|
||||
private static readonly IStringMatcher[] TextContentTypeMatchers =
|
||||
{
|
||||
"text/",
|
||||
"application/javascript", "application/typescript",
|
||||
"application/xml", "application/xhtml+xml",
|
||||
"application/x-www-form-urlencoded"
|
||||
new WildcardMatcher("text/*", true),
|
||||
new RegexMatcher("^application\\/(java|type)script$", true),
|
||||
new WildcardMatcher("application/*xml", true),
|
||||
new WildcardMatcher("application/x-www-form-urlencoded", true)
|
||||
};
|
||||
|
||||
private static async Task<Tuple<string, Encoding>> ReadStringAsync(Stream stream)
|
||||
public static bool ParseBodyAsIsValid([CanBeNull] string parseBodyAs)
|
||||
{
|
||||
using (var streamReader = new StreamReader(stream))
|
||||
{
|
||||
string content = await streamReader.ReadToEndAsync();
|
||||
|
||||
return new Tuple<string, Encoding>(content, streamReader.CurrentEncoding);
|
||||
}
|
||||
return Enum.TryParse(parseBodyAs, out BodyType _);
|
||||
}
|
||||
|
||||
public static bool ShouldParseBody([CanBeNull] string method)
|
||||
{
|
||||
return AllowedBodyParseMethods.Contains(method, StringComparer.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
public static BodyType DetectBodyTypeFromContentType([CanBeNull] string contentTypeValue)
|
||||
{
|
||||
if (string.IsNullOrEmpty(contentTypeValue) || !ContentType.TryParse(contentTypeValue, out ContentType contentType))
|
||||
{
|
||||
return BodyType.Bytes;
|
||||
}
|
||||
|
||||
if (TextContentTypeMatchers.Any(matcher => MatchScores.IsPerfect(matcher.IsMatch(contentType.MimeType))))
|
||||
{
|
||||
return BodyType.String;
|
||||
}
|
||||
|
||||
if (JsonContentTypesMatchers.Any(matcher => MatchScores.IsPerfect(matcher.IsMatch(contentType.MimeType))))
|
||||
{
|
||||
return BodyType.Json;
|
||||
}
|
||||
|
||||
return BodyType.Bytes;
|
||||
}
|
||||
|
||||
public static async Task<BodyData> Parse([NotNull] Stream stream, [CanBeNull] string contentType)
|
||||
{
|
||||
Check.NotNull(stream, nameof(stream));
|
||||
|
||||
var data = new BodyData
|
||||
{
|
||||
BodyAsBytes = await ReadBytesAsync(stream),
|
||||
DetectedBodyType = BodyType.Bytes,
|
||||
DetectedBodyTypeFromContentType = DetectBodyTypeFromContentType(contentType)
|
||||
};
|
||||
|
||||
// Try to get the body as String
|
||||
try
|
||||
{
|
||||
data.BodyAsString = DefaultEncoding.GetString(data.BodyAsBytes);
|
||||
data.Encoding = DefaultEncoding;
|
||||
data.DetectedBodyType = BodyType.String;
|
||||
|
||||
// If string is not null or empty, try to get as Json
|
||||
if (!string.IsNullOrEmpty(data.BodyAsString))
|
||||
{
|
||||
try
|
||||
{
|
||||
data.BodyAsJson = JsonConvert.DeserializeObject(data.BodyAsString, new JsonSerializerSettings { Formatting = Formatting.Indented });
|
||||
data.DetectedBodyType = BodyType.Json;
|
||||
}
|
||||
catch
|
||||
{
|
||||
// JsonConvert failed, just ignore.
|
||||
}
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Reading as string failed, just ignore
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
private static async Task<byte[]> ReadBytesAsync(Stream stream)
|
||||
{
|
||||
using (var memoryStream = new MemoryStream())
|
||||
@@ -42,47 +118,5 @@ namespace WireMock.Util
|
||||
return memoryStream.ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
public static async Task<BodyData> Parse([NotNull] Stream stream, [CanBeNull] string contentTypeHeaderValue)
|
||||
{
|
||||
var data = new BodyData();
|
||||
|
||||
if (contentTypeHeaderValue != null && TextContentTypes.Any(text => contentTypeHeaderValue.StartsWith(text, StringComparison.OrdinalIgnoreCase)))
|
||||
{
|
||||
try
|
||||
{
|
||||
var stringData = await ReadStringAsync(stream);
|
||||
data.BodyAsString = stringData.Item1;
|
||||
data.Encoding = stringData.Item2;
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Reading as string failed, just get the ByteArray.
|
||||
data.BodyAsBytes = await ReadBytesAsync(stream);
|
||||
}
|
||||
}
|
||||
else if (contentTypeHeaderValue != null && JsonContentTypes.Any(json => contentTypeHeaderValue.StartsWith(json, StringComparison.OrdinalIgnoreCase)))
|
||||
{
|
||||
var stringData = await ReadStringAsync(stream);
|
||||
data.BodyAsString = stringData.Item1;
|
||||
data.Encoding = stringData.Item2;
|
||||
|
||||
try
|
||||
{
|
||||
data.BodyAsJson = JsonConvert.DeserializeObject(stringData.Item1, new JsonSerializerSettings { Formatting = Formatting.Indented });
|
||||
}
|
||||
catch
|
||||
{
|
||||
// JsonConvert failed, just set the Body as string.
|
||||
data.BodyAsString = stringData.Item1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
data.BodyAsBytes = await ReadBytesAsync(stream);
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
}
|
||||
}
|
||||
33
src/WireMock.Net/Util/BodyType.cs
Normal file
33
src/WireMock.Net/Util/BodyType.cs
Normal file
@@ -0,0 +1,33 @@
|
||||
namespace WireMock.Util
|
||||
{
|
||||
/// <summary>
|
||||
/// The BodyType
|
||||
/// </summary>
|
||||
public enum BodyType
|
||||
{
|
||||
/// <summary>
|
||||
/// No body present
|
||||
/// </summary>
|
||||
None,
|
||||
|
||||
/// <summary>
|
||||
/// Body is a String
|
||||
/// </summary>
|
||||
String,
|
||||
|
||||
/// <summary>
|
||||
/// Body is a Json object
|
||||
/// </summary>
|
||||
Json,
|
||||
|
||||
/// <summary>
|
||||
/// Body is a Byte array
|
||||
/// </summary>
|
||||
Bytes,
|
||||
|
||||
/// <summary>
|
||||
/// Body is a File
|
||||
/// </summary>
|
||||
File
|
||||
}
|
||||
}
|
||||
@@ -8,26 +8,26 @@ namespace WireMock.Util
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of elements in the collection.</typeparam>
|
||||
/// <inheritdoc cref="ObservableCollection{T}" />
|
||||
public class ConcurentObservableCollection<T> : ObservableCollection<T>
|
||||
public class ConcurrentObservableCollection<T> : ObservableCollection<T>
|
||||
{
|
||||
private readonly object _lockObject = new object();
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="T:WireMock.Util.ConcurentObservableCollection`1" /> class.
|
||||
/// Initializes a new instance of the <see cref="T:WireMock.Util.ConcurrentObservableCollection`1" /> class.
|
||||
/// </summary>
|
||||
public ConcurentObservableCollection() { }
|
||||
public ConcurrentObservableCollection() { }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ConcurentObservableCollection{T}"/> class that contains elements copied from the specified list.
|
||||
/// Initializes a new instance of the <see cref="ConcurrentObservableCollection{T}"/> class that contains elements copied from the specified list.
|
||||
/// </summary>
|
||||
/// <param name="list">The list from which the elements are copied.</param>
|
||||
public ConcurentObservableCollection(List<T> list) : base(list) { }
|
||||
public ConcurrentObservableCollection(List<T> list) : base(list) { }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ConcurentObservableCollection{T}"/> class that contains elements copied from the specified collection.
|
||||
/// Initializes a new instance of the <see cref="ConcurrentObservableCollection{T}"/> class that contains elements copied from the specified collection.
|
||||
/// </summary>
|
||||
/// <param name="collection">The collection from which the elements are copied.</param>
|
||||
public ConcurentObservableCollection(IEnumerable<T> collection) : base(collection) { }
|
||||
public ConcurrentObservableCollection(IEnumerable<T> collection) : base(collection) { }
|
||||
|
||||
/// <inheritdoc cref="ObservableCollection{T}.ClearItems"/>
|
||||
protected override void ClearItems()
|
||||
@@ -11,16 +11,16 @@ namespace WireMock.Util
|
||||
private const int NumberOfRetries = 3;
|
||||
private const int DelayOnRetry = 500;
|
||||
|
||||
public static string ReadAllTextWithRetryAndDelay([NotNull] IFileSystemHandler filehandler, [NotNull] string path)
|
||||
public static string ReadAllTextWithRetryAndDelay([NotNull] IFileSystemHandler handler, [NotNull] string path)
|
||||
{
|
||||
Check.NotNull(filehandler, nameof(filehandler));
|
||||
Check.NotNull(handler, nameof(handler));
|
||||
Check.NotNullOrEmpty(path, nameof(path));
|
||||
|
||||
for (int i = 1; i <= NumberOfRetries; ++i)
|
||||
{
|
||||
try
|
||||
{
|
||||
return filehandler.ReadMappingFile(path);
|
||||
return handler.ReadMappingFile(path);
|
||||
}
|
||||
catch
|
||||
{
|
||||
|
||||
@@ -92,7 +92,7 @@ namespace WireMock.Util
|
||||
|
||||
private static void ProcessItem(JToken node, string path, string propertyName, List<string> lines)
|
||||
{
|
||||
string castText = string.Empty;
|
||||
string castText;
|
||||
switch (node.Type)
|
||||
{
|
||||
case JTokenType.Boolean:
|
||||
@@ -132,8 +132,7 @@ namespace WireMock.Util
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new NotSupportedException(
|
||||
$"JTokenType '{node.Type}' cannot be converted to a Dynamic Linq cast operator.");
|
||||
throw new NotSupportedException($"JTokenType '{node.Type}' cannot be converted to a Dynamic Linq cast operator.");
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(propertyName))
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using JetBrains.Annotations;
|
||||
using WireMock.Models;
|
||||
using WireMock.Validation;
|
||||
#if !USE_ASPNETCORE
|
||||
using Microsoft.Owin;
|
||||
#else
|
||||
@@ -13,6 +14,8 @@ namespace WireMock.Util
|
||||
{
|
||||
public static UrlDetails Parse([NotNull] Uri uri, PathString pathBase)
|
||||
{
|
||||
Check.NotNull(uri, nameof(uri));
|
||||
|
||||
if (!pathBase.HasValue)
|
||||
{
|
||||
return new UrlDetails(uri, uri);
|
||||
@@ -26,7 +29,7 @@ namespace WireMock.Util
|
||||
|
||||
private static string RemoveFirst(string text, string search)
|
||||
{
|
||||
int pos = text.IndexOf(search);
|
||||
int pos = text.IndexOf(search, StringComparison.Ordinal);
|
||||
if (pos < 0)
|
||||
{
|
||||
return text;
|
||||
|
||||
Reference in New Issue
Block a user