mirror of
https://github.com/wiremock/WireMock.Net.git
synced 2026-04-20 23:41:19 +02:00
Proxy : also save multipart as string in mapping file (#264)
* ExactObjectMatcher * BytesEncodingUtils * BodyParser * Encoding.ASCII
This commit is contained in:
@@ -4,7 +4,7 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<VersionPrefix>1.0.11</VersionPrefix>
|
<VersionPrefix>1.0.12</VersionPrefix>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<Choose>
|
<Choose>
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
using Newtonsoft.Json;
|
using System;
|
||||||
|
using System.Net.Http;
|
||||||
|
using Newtonsoft.Json;
|
||||||
using WireMock.Server;
|
using WireMock.Server;
|
||||||
using WireMock.Settings;
|
using WireMock.Settings;
|
||||||
|
|
||||||
@@ -8,14 +10,15 @@ namespace WireMock.Net.Console.Proxy.Net452
|
|||||||
{
|
{
|
||||||
static void Main(string[] args)
|
static void Main(string[] args)
|
||||||
{
|
{
|
||||||
|
string[] urls = { "http://localhost:9091/", "https://localhost:9443/" };
|
||||||
var server = FluentMockServer.Start(new FluentMockServerSettings
|
var server = FluentMockServer.Start(new FluentMockServerSettings
|
||||||
{
|
{
|
||||||
Urls = new[] { "http://localhost:9091/", "https://localhost:9443/" },
|
Urls = urls,
|
||||||
StartAdminInterface = true,
|
StartAdminInterface = true,
|
||||||
ReadStaticMappings = false,
|
ReadStaticMappings = false,
|
||||||
ProxyAndRecordSettings = new ProxyAndRecordSettings
|
ProxyAndRecordSettings = new ProxyAndRecordSettings
|
||||||
{
|
{
|
||||||
Url = "https://www.google.com",
|
Url = "http://postman-echo.com/post",
|
||||||
//ClientX509Certificate2ThumbprintOrSubjectName = "www.yourclientcertname.com OR yourcertificatethumbprint (only if the service you're proxying to requires it)",
|
//ClientX509Certificate2ThumbprintOrSubjectName = "www.yourclientcertname.com OR yourcertificatethumbprint (only if the service you're proxying to requires it)",
|
||||||
SaveMapping = true,
|
SaveMapping = true,
|
||||||
SaveMappingToFile = false,
|
SaveMappingToFile = false,
|
||||||
@@ -28,6 +31,13 @@ namespace WireMock.Net.Console.Proxy.Net452
|
|||||||
System.Console.WriteLine(JsonConvert.SerializeObject(eventRecordArgs.NewItems, Formatting.Indented));
|
System.Console.WriteLine(JsonConvert.SerializeObject(eventRecordArgs.NewItems, Formatting.Indented));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
var uri = new Uri(urls[0]);
|
||||||
|
var form = new MultipartFormDataContent
|
||||||
|
{
|
||||||
|
{ new StringContent("data"), "test", "test.txt" }
|
||||||
|
};
|
||||||
|
new HttpClient().PostAsync(uri, form).GetAwaiter().GetResult();
|
||||||
|
|
||||||
System.Console.WriteLine("Press any key to stop the server");
|
System.Console.WriteLine("Press any key to stop the server");
|
||||||
System.Console.ReadKey();
|
System.Console.ReadKey();
|
||||||
server.Stop();
|
server.Stop();
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
using System.Linq;
|
using JetBrains.Annotations;
|
||||||
using JetBrains.Annotations;
|
using System.Linq;
|
||||||
using WireMock.Validation;
|
using WireMock.Validation;
|
||||||
|
|
||||||
namespace WireMock.Matchers
|
namespace WireMock.Matchers
|
||||||
@@ -10,8 +10,9 @@ namespace WireMock.Matchers
|
|||||||
/// <seealso cref="IObjectMatcher" />
|
/// <seealso cref="IObjectMatcher" />
|
||||||
public class ExactObjectMatcher : IObjectMatcher
|
public class ExactObjectMatcher : IObjectMatcher
|
||||||
{
|
{
|
||||||
private readonly object _object;
|
public object ValueAsObject { get; }
|
||||||
private readonly byte[] _bytes;
|
|
||||||
|
public byte[] ValueAsBytes { get; }
|
||||||
|
|
||||||
/// <inheritdoc cref="IMatcher.MatchBehaviour"/>
|
/// <inheritdoc cref="IMatcher.MatchBehaviour"/>
|
||||||
public MatchBehaviour MatchBehaviour { get; }
|
public MatchBehaviour MatchBehaviour { get; }
|
||||||
@@ -33,7 +34,7 @@ namespace WireMock.Matchers
|
|||||||
{
|
{
|
||||||
Check.NotNull(value, nameof(value));
|
Check.NotNull(value, nameof(value));
|
||||||
|
|
||||||
_object = value;
|
ValueAsObject = value;
|
||||||
MatchBehaviour = matchBehaviour;
|
MatchBehaviour = matchBehaviour;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -54,14 +55,14 @@ namespace WireMock.Matchers
|
|||||||
{
|
{
|
||||||
Check.NotNull(value, nameof(value));
|
Check.NotNull(value, nameof(value));
|
||||||
|
|
||||||
_bytes = value;
|
ValueAsBytes = value;
|
||||||
MatchBehaviour = matchBehaviour;
|
MatchBehaviour = matchBehaviour;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc cref="IObjectMatcher.IsMatch"/>
|
/// <inheritdoc cref="IObjectMatcher.IsMatch"/>
|
||||||
public double IsMatch(object input)
|
public double IsMatch(object input)
|
||||||
{
|
{
|
||||||
bool equals = _object != null ? Equals(_object, input) : _bytes.SequenceEqual((byte[])input);
|
bool equals = ValueAsObject != null ? Equals(ValueAsObject, input) : ValueAsBytes.SequenceEqual((byte[])input);
|
||||||
return MatchBehaviourHelper.Convert(MatchBehaviour, MatchScores.ToScore(equals));
|
return MatchBehaviourHelper.Convert(MatchBehaviour, MatchScores.ToScore(equals));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,10 @@ namespace WireMock
|
|||||||
internal static class ResponseMessageBuilder
|
internal static class ResponseMessageBuilder
|
||||||
{
|
{
|
||||||
private static string ContentTypeJson = "application/json";
|
private static string ContentTypeJson = "application/json";
|
||||||
private static readonly IDictionary<string, WireMockList<string>> ContentTypeJsonHeaders = new Dictionary<string, WireMockList<string>> { { HttpKnownHeaderNames.ContentType, new WireMockList<string> { ContentTypeJson } } };
|
private static readonly IDictionary<string, WireMockList<string>> ContentTypeJsonHeaders = new Dictionary<string, WireMockList<string>>
|
||||||
|
{
|
||||||
|
{ HttpKnownHeaderNames.ContentType, new WireMockList<string> { ContentTypeJson } }
|
||||||
|
};
|
||||||
|
|
||||||
internal static ResponseMessage Create(string message, int statusCode = 200, Guid? guid = null)
|
internal static ResponseMessage Create(string message, int statusCode = 200, Guid? guid = null)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
using System;
|
using JetBrains.Annotations;
|
||||||
|
using SimMetrics.Net;
|
||||||
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using JetBrains.Annotations;
|
|
||||||
using SimMetrics.Net;
|
|
||||||
using WireMock.Admin.Mappings;
|
using WireMock.Admin.Mappings;
|
||||||
using WireMock.Matchers;
|
using WireMock.Matchers;
|
||||||
|
|
||||||
@@ -32,6 +32,10 @@ namespace WireMock.Serialization
|
|||||||
case "ExactMatcher":
|
case "ExactMatcher":
|
||||||
return new ExactMatcher(matchBehaviour, stringPatterns);
|
return new ExactMatcher(matchBehaviour, stringPatterns);
|
||||||
|
|
||||||
|
case "ExactObjectMatcher":
|
||||||
|
var bytePattern = Convert.FromBase64String(stringPatterns[0]);
|
||||||
|
return new ExactObjectMatcher(matchBehaviour, bytePattern);
|
||||||
|
|
||||||
case "RegexMatcher":
|
case "RegexMatcher":
|
||||||
return new RegexMatcher(matchBehaviour, stringPatterns, matcher.IgnoreCase == true);
|
return new RegexMatcher(matchBehaviour, stringPatterns, matcher.IgnoreCase == true);
|
||||||
|
|
||||||
@@ -73,20 +77,33 @@ namespace WireMock.Serialization
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the matcher is a IStringMatcher, get the patterns.
|
object[] patterns = new object[0]; // Default empty array
|
||||||
// If the matcher is a IValueMatcher, get the value (can be string or object).
|
switch (matcher)
|
||||||
// Else empty array
|
{
|
||||||
object[] patterns = matcher is IStringMatcher stringMatcher ?
|
// If the matcher is a IStringMatcher, get the patterns.
|
||||||
stringMatcher.GetPatterns().Cast<object>().ToArray() :
|
case IStringMatcher stringMatcher:
|
||||||
matcher is IValueMatcher valueMatcher ? new[] { valueMatcher.Value } :
|
patterns = stringMatcher.GetPatterns().Cast<object>().ToArray();
|
||||||
new object[0];
|
break;
|
||||||
bool? ignorecase = matcher is IIgnoreCaseMatcher ignoreCaseMatcher ? ignoreCaseMatcher.IgnoreCase : (bool?)null;
|
|
||||||
|
// If the matcher is a IValueMatcher, get the value (can be string or object).
|
||||||
|
case IValueMatcher valueMatcher:
|
||||||
|
patterns = new[] { valueMatcher.Value };
|
||||||
|
break;
|
||||||
|
|
||||||
|
// If the matcher is a ExactObjectMatcher, get the ValueAsObject or ValueAsBytes.
|
||||||
|
case ExactObjectMatcher exactObjectMatcher:
|
||||||
|
patterns = new[] { exactObjectMatcher.ValueAsObject ?? exactObjectMatcher.ValueAsBytes };
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool? ignoreCase = matcher is IIgnoreCaseMatcher ignoreCaseMatcher ? ignoreCaseMatcher.IgnoreCase : (bool?)null;
|
||||||
|
|
||||||
bool? rejectOnMatch = matcher.MatchBehaviour == MatchBehaviour.RejectOnMatch ? true : (bool?)null;
|
bool? rejectOnMatch = matcher.MatchBehaviour == MatchBehaviour.RejectOnMatch ? true : (bool?)null;
|
||||||
|
|
||||||
return new MatcherModel
|
return new MatcherModel
|
||||||
{
|
{
|
||||||
RejectOnMatch = rejectOnMatch,
|
RejectOnMatch = rejectOnMatch,
|
||||||
IgnoreCase = ignorecase,
|
IgnoreCase = ignoreCase,
|
||||||
Name = matcher.Name,
|
Name = matcher.Name,
|
||||||
Pattern = patterns.Length == 1 ? patterns.First() : null,
|
Pattern = patterns.Length == 1 ? patterns.First() : null,
|
||||||
Patterns = patterns.Length > 1 ? patterns : null
|
Patterns = patterns.Length > 1 ? patterns : null
|
||||||
|
|||||||
@@ -277,13 +277,19 @@ namespace WireMock.Server
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if (requestMessage.BodyData?.DetectedBodyType == BodyType.Json)
|
switch (requestMessage.BodyData?.DetectedBodyType)
|
||||||
{
|
{
|
||||||
request.WithBody(new JsonMatcher(MatchBehaviour.AcceptOnMatch, requestMessage.BodyData.BodyAsJson));
|
case BodyType.Json:
|
||||||
}
|
request.WithBody(new JsonMatcher(MatchBehaviour.AcceptOnMatch, requestMessage.BodyData.BodyAsJson));
|
||||||
else if (requestMessage.BodyData?.DetectedBodyType == BodyType.String)
|
break;
|
||||||
{
|
|
||||||
request.WithBody(new ExactMatcher(MatchBehaviour.AcceptOnMatch, requestMessage.BodyData.BodyAsString));
|
case BodyType.String:
|
||||||
|
request.WithBody(new ExactMatcher(MatchBehaviour.AcceptOnMatch, requestMessage.BodyData.BodyAsString));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case BodyType.Bytes:
|
||||||
|
request.WithBody(new ExactObjectMatcher(MatchBehaviour.AcceptOnMatch, requestMessage.BodyData.BodyAsBytes));
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
var response = Response.Create(responseMessage);
|
var response = Response.Create(responseMessage);
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ namespace WireMock.Util
|
|||||||
internal static class BodyParser
|
internal static class BodyParser
|
||||||
{
|
{
|
||||||
private static readonly Encoding DefaultEncoding = Encoding.UTF8;
|
private static readonly Encoding DefaultEncoding = Encoding.UTF8;
|
||||||
|
private static readonly Encoding[] SupportedBodyAsStringEncodingForMultipart = { Encoding.UTF8, Encoding.ASCII };
|
||||||
|
|
||||||
/*
|
/*
|
||||||
HEAD - No defined body semantics.
|
HEAD - No defined body semantics.
|
||||||
@@ -91,9 +92,19 @@ namespace WireMock.Util
|
|||||||
DetectedBodyTypeFromContentType = DetectBodyTypeFromContentType(contentType)
|
DetectedBodyTypeFromContentType = DetectBodyTypeFromContentType(contentType)
|
||||||
};
|
};
|
||||||
|
|
||||||
// In case of MultiPart: never try to read as String but keep as-is
|
// 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
|
||||||
if (data.DetectedBodyTypeFromContentType == BodyType.MultiPart)
|
if (data.DetectedBodyTypeFromContentType == BodyType.MultiPart)
|
||||||
{
|
{
|
||||||
|
if (BytesEncodingUtils.TryGetEncoding(data.BodyAsBytes, out Encoding encoding) &&
|
||||||
|
SupportedBodyAsStringEncodingForMultipart.Select(x => x.Equals(encoding)).Any())
|
||||||
|
{
|
||||||
|
data.BodyAsString = encoding.GetString(data.BodyAsBytes);
|
||||||
|
data.Encoding = encoding;
|
||||||
|
data.DetectedBodyType = BodyType.String;
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
230
src/WireMock.Net/Util/BytesEncodingUtils.cs
Normal file
230
src/WireMock.Net/Util/BytesEncodingUtils.cs
Normal file
@@ -0,0 +1,230 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace WireMock.Util
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Based on:
|
||||||
|
/// http://utf8checker.codeplex.com
|
||||||
|
/// https://github.com/0x53A/Mvvm/blob/master/src/Mvvm/src/Utf8Checker.cs
|
||||||
|
///
|
||||||
|
/// References:
|
||||||
|
/// http://anubis.dkuug.dk/JTC1/SC2/WG2/docs/n1335
|
||||||
|
/// http://www.cl.cam.ac.uk/~mgk25/ucs/ISO-10646-UTF-8.html
|
||||||
|
/// http://www.unicode.org/versions/corrigendum1.html
|
||||||
|
/// http://www.ietf.org/rfc/rfc2279.txt
|
||||||
|
/// </summary>
|
||||||
|
public static class BytesEncodingUtils
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Tries the get the Encoding from an array of bytes.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="bytes">The bytes.</param>
|
||||||
|
/// <param name="encoding">The output encoding.</param>
|
||||||
|
public static bool TryGetEncoding(byte[] bytes, out Encoding encoding)
|
||||||
|
{
|
||||||
|
encoding = null;
|
||||||
|
if (bytes.All(b => b < 80))
|
||||||
|
{
|
||||||
|
encoding = Encoding.ASCII;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (StartsWith(bytes, new byte[] { 0xff, 0xfe, 0x00, 0x00 }))
|
||||||
|
{
|
||||||
|
encoding = Encoding.UTF32;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (StartsWith(bytes, new byte[] { 0xfe, 0xff }))
|
||||||
|
{
|
||||||
|
encoding = Encoding.BigEndianUnicode;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (StartsWith(bytes, new byte[] { 0xff, 0xfe }))
|
||||||
|
{
|
||||||
|
encoding = Encoding.Unicode;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (StartsWith(bytes, new byte[] { 0xef, 0xbb, 0xbf }))
|
||||||
|
{
|
||||||
|
encoding = Encoding.UTF8;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (IsUtf8(bytes, bytes.Length))
|
||||||
|
{
|
||||||
|
encoding = new UTF8Encoding(false);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool StartsWith(IEnumerable<byte> data, IReadOnlyCollection<byte> other)
|
||||||
|
{
|
||||||
|
byte[] arraySelf = data.Take(other.Count).ToArray();
|
||||||
|
return other.SequenceEqual(arraySelf);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool IsUtf8(IReadOnlyList<byte> buffer, int length)
|
||||||
|
{
|
||||||
|
int position = 0;
|
||||||
|
int bytes = 0;
|
||||||
|
while (position < length)
|
||||||
|
{
|
||||||
|
if (!IsValid(buffer, position, length, ref bytes))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
position += bytes;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma warning disable S3776 // Cognitive Complexity of methods should not be too high
|
||||||
|
private static bool IsValid(IReadOnlyList<byte> buffer, int position, int length, ref int bytes)
|
||||||
|
{
|
||||||
|
if (length > buffer.Count)
|
||||||
|
{
|
||||||
|
throw new ArgumentException("Invalid length");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (position > length - 1)
|
||||||
|
{
|
||||||
|
bytes = 0;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
byte ch = buffer[position];
|
||||||
|
if (ch <= 0x7F)
|
||||||
|
{
|
||||||
|
bytes = 1;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ch >= 0xc2 && ch <= 0xdf)
|
||||||
|
{
|
||||||
|
if (position >= length - 2)
|
||||||
|
{
|
||||||
|
bytes = 0;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (buffer[position + 1] < 0x80 || buffer[position + 1] > 0xbf)
|
||||||
|
{
|
||||||
|
bytes = 0;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bytes = 2;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ch == 0xe0)
|
||||||
|
{
|
||||||
|
if (position >= length - 3)
|
||||||
|
{
|
||||||
|
bytes = 0;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (buffer[position + 1] < 0xa0 || buffer[position + 1] > 0xbf ||
|
||||||
|
buffer[position + 2] < 0x80 || buffer[position + 2] > 0xbf)
|
||||||
|
{
|
||||||
|
bytes = 0;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bytes = 3;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ch >= 0xe1 && ch <= 0xef)
|
||||||
|
{
|
||||||
|
if (position >= length - 3)
|
||||||
|
{
|
||||||
|
bytes = 0;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (buffer[position + 1] < 0x80 || buffer[position + 1] > 0xbf ||
|
||||||
|
buffer[position + 2] < 0x80 || buffer[position + 2] > 0xbf)
|
||||||
|
{
|
||||||
|
bytes = 0;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bytes = 3;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ch == 0xf0)
|
||||||
|
{
|
||||||
|
if (position >= length - 4)
|
||||||
|
{
|
||||||
|
bytes = 0;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (buffer[position + 1] < 0x90 || buffer[position + 1] > 0xbf ||
|
||||||
|
buffer[position + 2] < 0x80 || buffer[position + 2] > 0xbf ||
|
||||||
|
buffer[position + 3] < 0x80 || buffer[position + 3] > 0xbf)
|
||||||
|
{
|
||||||
|
bytes = 0;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bytes = 4;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ch == 0xf4)
|
||||||
|
{
|
||||||
|
if (position >= length - 4)
|
||||||
|
{
|
||||||
|
bytes = 0;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (buffer[position + 1] < 0x80 || buffer[position + 1] > 0x8f ||
|
||||||
|
buffer[position + 2] < 0x80 || buffer[position + 2] > 0xbf ||
|
||||||
|
buffer[position + 3] < 0x80 || buffer[position + 3] > 0xbf)
|
||||||
|
{
|
||||||
|
bytes = 0;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bytes = 4;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ch >= 0xf1 && ch <= 0xf3)
|
||||||
|
{
|
||||||
|
if (position >= length - 4)
|
||||||
|
{
|
||||||
|
bytes = 0;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (buffer[position + 1] < 0x80 || buffer[position + 1] > 0xbf ||
|
||||||
|
buffer[position + 2] < 0x80 || buffer[position + 2] > 0xbf ||
|
||||||
|
buffer[position + 3] < 0x80 || buffer[position + 3] > 0xbf)
|
||||||
|
{
|
||||||
|
bytes = 0;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bytes = 4;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#pragma warning restore S3776 // Cognitive Complexity of methods should not be too high
|
||||||
|
}
|
||||||
@@ -290,6 +290,36 @@ namespace WireMock.Net.Tests
|
|||||||
Check.That(response.Content.Headers.GetValues("Content-Type")).ContainsExactly("application/json; charset=utf-8");
|
Check.That(response.Content.Headers.GetValues("Content-Type")).ContainsExactly("application/json; charset=utf-8");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task FluentMockServer_Proxy_Should_set_Body_in_multipart_proxied_response()
|
||||||
|
{
|
||||||
|
// Assign
|
||||||
|
string path = $"/prx_{Guid.NewGuid().ToString()}";
|
||||||
|
var serverForProxyForwarding = FluentMockServer.Start();
|
||||||
|
serverForProxyForwarding
|
||||||
|
.Given(Request.Create().WithPath(path))
|
||||||
|
.RespondWith(Response.Create()
|
||||||
|
.WithBodyAsJson(new { i = 42 })
|
||||||
|
);
|
||||||
|
|
||||||
|
var server = FluentMockServer.Start();
|
||||||
|
server
|
||||||
|
.Given(Request.Create().WithPath(path))
|
||||||
|
.RespondWith(Response.Create().WithProxy(serverForProxyForwarding.Urls[0]));
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var uri = new Uri($"{server.Urls[0]}{path}");
|
||||||
|
var form = new MultipartFormDataContent
|
||||||
|
{
|
||||||
|
{ new StringContent("data"), "test", "test.txt" }
|
||||||
|
};
|
||||||
|
var response = await new HttpClient().PostAsync(uri, form);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
string content = await response.Content.ReadAsStringAsync();
|
||||||
|
Check.That(content).IsEqualTo("{\"i\":42}");
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task FluentMockServer_Proxy_Should_Not_overrule_AdminMappings()
|
public async Task FluentMockServer_Proxy_Should_Not_overrule_AdminMappings()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
using System;
|
using NFluent;
|
||||||
using NFluent;
|
using System;
|
||||||
using WireMock.Admin.Mappings;
|
using WireMock.Admin.Mappings;
|
||||||
using WireMock.Matchers;
|
using WireMock.Matchers;
|
||||||
using WireMock.Serialization;
|
using WireMock.Serialization;
|
||||||
@@ -53,6 +53,23 @@ namespace WireMock.Net.Tests.Serialization
|
|||||||
Check.That(matcher.GetPatterns()).ContainsExactly("x", "y");
|
Check.That(matcher.GetPatterns()).ContainsExactly("x", "y");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void MatcherModelMapper_Map_ExactObjectMatcher_Pattern()
|
||||||
|
{
|
||||||
|
// Assign
|
||||||
|
var model = new MatcherModel
|
||||||
|
{
|
||||||
|
Name = "ExactObjectMatcher",
|
||||||
|
Patterns = new object[] { "c3RlZg==" }
|
||||||
|
};
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var matcher = (ExactObjectMatcher)MatcherMapper.Map(model);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Check.That(matcher.ValueAsBytes).ContainsExactly(new byte[] { 115, 116, 101, 102 });
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void MatcherModelMapper_Map_RegexMatcher()
|
public void MatcherModelMapper_Map_RegexMatcher()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ namespace WireMock.Net.Tests.Util
|
|||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task BodyParser_Parse_ContentTypeMultipart()
|
public async Task BodyParser_Parse_WithUTF8EncodingAndContentTypeMultipart_DetectedBodyTypeEqualsString()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
string contentType = "multipart/form-data";
|
string contentType = "multipart/form-data";
|
||||||
@@ -80,6 +80,26 @@ Content-Type: text/html
|
|||||||
// Act
|
// Act
|
||||||
var result = await BodyParser.Parse(memoryStream, contentType);
|
var result = await BodyParser.Parse(memoryStream, contentType);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Check.That(result.DetectedBodyType).IsEqualTo(BodyType.String);
|
||||||
|
Check.That(result.DetectedBodyTypeFromContentType).IsEqualTo(BodyType.MultiPart);
|
||||||
|
Check.That(result.BodyAsBytes).IsNotNull();
|
||||||
|
Check.That(result.BodyAsJson).IsNull();
|
||||||
|
Check.That(result.BodyAsString).IsNotNull();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task BodyParser_Parse_WithUTF16EncodingAndContentTypeMultipart_DetectedBodyTypeEqualsString()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
string contentType = "multipart/form-data";
|
||||||
|
string body = char.ConvertFromUtf32(0x1D161); //U+1D161 = MUSICAL SYMBOL SIXTEENTH NOTE
|
||||||
|
|
||||||
|
var memoryStream = new MemoryStream(Encoding.UTF32.GetBytes(body));
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = await BodyParser.Parse(memoryStream, contentType);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Check.That(result.DetectedBodyType).IsEqualTo(BodyType.Bytes);
|
Check.That(result.DetectedBodyType).IsEqualTo(BodyType.Bytes);
|
||||||
Check.That(result.DetectedBodyTypeFromContentType).IsEqualTo(BodyType.MultiPart);
|
Check.That(result.DetectedBodyTypeFromContentType).IsEqualTo(BodyType.MultiPart);
|
||||||
|
|||||||
Reference in New Issue
Block a user