Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix issues with Proxy mode and Binary Request Bodies #334

Merged
merged 9 commits into from
Sep 1, 2019
Original file line number Diff line number Diff line change
Expand Up @@ -145,17 +145,17 @@ private double CalculateMatchScore(RequestMessage requestMessage)

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;
Expand Down
4 changes: 2 additions & 2 deletions src/WireMock.Net/Util/BodyParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
38 changes: 38 additions & 0 deletions test/WireMock.Net.Tests/FluentMockServerTests.Proxy.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using System.Net.Http.Headers;
using System.Text;
using System.Threading.Tasks;
using FluentAssertions;
using WireMock.Matchers.Request;
using WireMock.RequestBuilders;
using WireMock.ResponseBuilders;
Expand Down Expand Up @@ -308,6 +309,43 @@ public async Task FluentMockServer_Proxy_Should_preserve_cookie_header_in_proxie
Check.That(receivedRequest.Cookies).ContainsPair("name", "value");
}

/// <summary>
andi0b marked this conversation as resolved.
Show resolved Hide resolved
/// 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()
{
Expand Down
2 changes: 1 addition & 1 deletion test/WireMock.Net.Tests/FluentMockServerTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@ public async Task FluentMockServer_Should_exclude_body_for_methods_where_body_is
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
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -208,5 +213,74 @@ public void RequestMessageBodyMatcher_GetMatchingScore_BodyAsBytes_IObjectMatche
// 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 },
};
}
}
}
}
16 changes: 16 additions & 0 deletions test/WireMock.Net.Tests/Util/BodyParserTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,22 @@ public async Task BodyParser_Parse_ContentTypeString(string contentType, string
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()
{
Expand Down