Fix issues with Proxy mode and Binary Request Bodies (#334)

* Add Test for Proxy with binary request

* Fix binary parsing in BodyParser

* Fix binary Matching in RequestMessageBodyMatcher

* Improved Binary Matching in RequestMessageBodyMatcher

* BodyParser: Add test for Content Autodetection

* RequestMessageBodyMatcherTests: Make Code more pretty :)

* BodyParserChanges: Revert white space changes

* Fixed test for different behavior in request matching
This commit is contained in:
andi0b
2019-09-01 17:44:07 +02:00
committed by Stef Heyenrath
parent 8c9a51c46d
commit 2f406029c9
6 changed files with 135 additions and 7 deletions

View File

@@ -145,17 +145,17 @@ namespace WireMock.Matchers.Request
if (Func != null)
{
return MatchScores.ToScore(requestMessage?.BodyData?.DetectedBodyType == BodyType.String && Func(requestMessage.BodyData.BodyAsString));
return MatchScores.ToScore(Func(requestMessage?.BodyData?.BodyAsString));
}
if (JsonFunc != null)
{
return MatchScores.ToScore(requestMessage?.BodyData?.DetectedBodyType == BodyType.Json && JsonFunc(requestMessage.BodyData.BodyAsJson));
return MatchScores.ToScore(JsonFunc(requestMessage?.BodyData?.BodyAsJson));
}
if (DataFunc != null)
{
return MatchScores.ToScore(requestMessage?.BodyData?.DetectedBodyType == BodyType.Bytes && DataFunc(requestMessage.BodyData.BodyAsBytes));
return MatchScores.ToScore(DataFunc(requestMessage?.BodyData?.BodyAsBytes));
}
return MatchScores.Mismatch;

View File

@@ -13,8 +13,8 @@ namespace WireMock.Util
{
internal static class BodyParser
{
private static readonly Encoding DefaultEncoding = Encoding.UTF8;
private static readonly Encoding[] SupportedBodyAsStringEncodingForMultipart = { Encoding.UTF8, Encoding.ASCII };
private static readonly Encoding DefaultEncoding = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false, throwOnInvalidBytes: true);
private static readonly Encoding[] SupportedBodyAsStringEncodingForMultipart = { DefaultEncoding, Encoding.ASCII };
/*
HEAD - No defined body semantics.

View File

@@ -6,6 +6,7 @@ using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Threading.Tasks;
using FluentAssertions;
using WireMock.Matchers.Request;
using WireMock.RequestBuilders;
using WireMock.ResponseBuilders;
@@ -308,6 +309,43 @@ namespace WireMock.Net.Tests
Check.That(receivedRequest.Cookies).ContainsPair("name", "value");
}
/// <summary>
/// Send some binary content in a request through the proxy and check that the same content
/// arrived at the target. As example a JPEG/JIFF header is used, which is not representable
/// in UTF8 and breaks if it is not treated as binary content.
/// </summary>
[Fact]
public async Task FluentMockServer_Proxy_Should_preserve_binary_request_content()
{
// arrange
var jpegHeader = new byte[] {0xFF, 0xD8, 0xFF, 0xE0, 0x00, 0x10, 0x4A, 0x46, 0x49, 0x46, 0x00};
var brokenJpegHeader = new byte[]
{0xEF, 0xBF, 0xBD, 0xEF, 0xBF, 0xBD, 0xEF, 0xBF, 0xBD, 0xEF, 0xBF, 0xBD, 0x00, 0x10, 0x4A, 0x46, 0x49, 0x46, 0x00};
bool HasCorrectHeader(byte[] bytes) => bytes.SequenceEqual(jpegHeader);
bool HasBrokenHeader(byte[] bytes) => bytes.SequenceEqual(brokenJpegHeader);
var serverForProxyForwarding = FluentMockServer.Start();
serverForProxyForwarding
.Given(Request.Create().WithBody(HasCorrectHeader))
.RespondWith(Response.Create().WithSuccess());
serverForProxyForwarding
.Given(Request.Create().WithBody(HasBrokenHeader))
.RespondWith(Response.Create().WithStatusCode(HttpStatusCode.InternalServerError));
var server = FluentMockServer.Start();
server
.Given(Request.Create())
.RespondWith(Response.Create().WithProxy(serverForProxyForwarding.Urls[0]));
// act
var response = await new HttpClient().PostAsync(server.Urls[0], new ByteArrayContent(jpegHeader));
// assert
Check.That(response.StatusCode).IsEqualTo(HttpStatusCode.OK);
}
[Fact]
public async Task FluentMockServer_Proxy_Should_set_BodyAsJson_in_proxied_response()
{

View File

@@ -187,7 +187,7 @@ namespace WireMock.Net.Tests
var server = FluentMockServer.Start();
server
.Given(Request.Create().WithBody(b => true))
.Given(Request.Create().WithBody((byte[] bodyBytes) => bodyBytes != null))
.AtPriority(0)
.RespondWith(Response.Create().WithStatusCode(400));
server

View File

@@ -1,5 +1,10 @@
using System.Linq;
using System;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Moq;
using Newtonsoft.Json;
using NFluent;
using WireMock.Matchers;
using WireMock.Matchers.Request;
@@ -208,5 +213,74 @@ namespace WireMock.Net.Tests.RequestMatchers
// Verify
objectMatcherMock.Verify(m => m.IsMatch(It.IsAny<byte[]>()), Times.Once);
}
[Theory]
[MemberData(nameof(MatchingScoreData))]
public async Task RequestMessageBodyMatcher_GetMatchingScore_Funcs_Matching(object body, RequestMessageBodyMatcher matcher, bool shouldMatch)
{
// assign
BodyData bodyData;
if (body is byte[] b)
bodyData = await BodyParser.Parse(new MemoryStream(b), null);
else if (body is string s)
bodyData = await BodyParser.Parse(new MemoryStream(Encoding.UTF8.GetBytes(s)), null);
else
throw new Exception();
var requestMessage = new RequestMessage(new UrlDetails("http://localhost"), "GET", "127.0.0.1", bodyData);
// act
var result = new RequestMatchResult();
var score = matcher.GetMatchingScore(requestMessage, result);
// assert
Check.That(score).IsEqualTo(shouldMatch ? 1d : 0d);
}
public static TheoryData<object, RequestMessageBodyMatcher, bool> MatchingScoreData
{
get
{
var json = "{'a':'b'}";
var str = "HelloWorld";
var bytes = new byte[] {0xFF, 0xD8, 0xFF, 0xE0, 0x00, 0x10, 0x4A, 0x46, 0x49, 0x46, 0x00};
return new TheoryData<object, RequestMessageBodyMatcher, bool>
{
// JSON match +++
{json, new RequestMessageBodyMatcher((object o) => ((dynamic) o).a == "b"), true},
{json, new RequestMessageBodyMatcher((string s) => s == json), true},
{json, new RequestMessageBodyMatcher((byte[] b) => b.SequenceEqual(Encoding.UTF8.GetBytes(json))), true},
// JSON no match ---
{json, new RequestMessageBodyMatcher((object o) => false), false},
{json, new RequestMessageBodyMatcher((string s) => false), false},
{json, new RequestMessageBodyMatcher((byte[] b) => false), false},
{json, new RequestMessageBodyMatcher(), false },
// string match +++
{str, new RequestMessageBodyMatcher((object o) => o == null), true},
{str, new RequestMessageBodyMatcher((string s) => s == str), true},
{str, new RequestMessageBodyMatcher((byte[] b) => b.SequenceEqual(Encoding.UTF8.GetBytes(str))), true},
// string no match ---
{str, new RequestMessageBodyMatcher((object o) => false), false},
{str, new RequestMessageBodyMatcher((string s) => false), false},
{str, new RequestMessageBodyMatcher((byte[] b) => false), false},
{str, new RequestMessageBodyMatcher(), false },
// binary match +++
{bytes, new RequestMessageBodyMatcher((object o) => o == null), true},
{bytes, new RequestMessageBodyMatcher((string s) => s == null), true},
{bytes, new RequestMessageBodyMatcher((byte[] b) => b.SequenceEqual(bytes)), true},
// binary no match ---
{bytes, new RequestMessageBodyMatcher((object o) => false), false},
{bytes, new RequestMessageBodyMatcher((string s) => false), false},
{bytes, new RequestMessageBodyMatcher((byte[] b) => false), false},
{bytes, new RequestMessageBodyMatcher(), false },
};
}
}
}
}

View File

@@ -50,6 +50,22 @@ namespace WireMock.Net.Tests.Util
Check.That(body.DetectedBodyTypeFromContentType).IsEqualTo(detectedBodyTypeFromContentType);
}
[Theory]
[InlineData(new byte[] {34, 97, 34}, BodyType.Json)]
[InlineData(new byte[] {97}, BodyType.String)]
[InlineData(new byte[] {0xFF, 0xD8, 0xFF, 0xE0}, BodyType.Bytes)]
public async Task BodyParser_Parse_DetectedBodyType(byte[] content, BodyType detectedBodyType)
{
// arrange
var memoryStream = new MemoryStream(content);
// act
var body = await BodyParser.Parse(memoryStream, null);
// assert
Check.That(body.DetectedBodyType).IsEqualTo(detectedBodyType);
}
[Fact]
public async Task BodyParser_Parse_WithUTF8EncodingAndContentTypeMultipart_DetectedBodyTypeEqualsString()
{