From d0ed0b852e24b01c043743c207625f1bca8fa3f2 Mon Sep 17 00:00:00 2001 From: Stef Heyenrath Date: Sat, 21 Feb 2026 19:23:57 +0100 Subject: [PATCH] ok --- ...ck.Net.RestClient.AwesomeAssertions.csproj | 1 - src/WireMock.Net.Shared/Util/BodyParser.cs | 70 +++++++++++++------ .../WireMockAdminApiAssertionsTests.cs | 9 +-- 3 files changed, 55 insertions(+), 25 deletions(-) diff --git a/src/WireMock.Net.RestClient.AwesomeAssertions/WireMock.Net.RestClient.AwesomeAssertions.csproj b/src/WireMock.Net.RestClient.AwesomeAssertions/WireMock.Net.RestClient.AwesomeAssertions.csproj index 8ce2e1b7..c69e5a50 100644 --- a/src/WireMock.Net.RestClient.AwesomeAssertions/WireMock.Net.RestClient.AwesomeAssertions.csproj +++ b/src/WireMock.Net.RestClient.AwesomeAssertions/WireMock.Net.RestClient.AwesomeAssertions.csproj @@ -1,7 +1,6 @@  - 0.0.1-preview-01 AwesomeAssertions extensions for WireMock.Net RestClient WireMock.Net.RestClient.AwesomeAssertions Stef Heyenrath diff --git a/src/WireMock.Net.Shared/Util/BodyParser.cs b/src/WireMock.Net.Shared/Util/BodyParser.cs index de940a24..826735ed 100644 --- a/src/WireMock.Net.Shared/Util/BodyParser.cs +++ b/src/WireMock.Net.Shared/Util/BodyParser.cs @@ -1,11 +1,7 @@ // Copyright © WireMock.Net -using System.Collections.Generic; -using System.IO; -using System.Linq; using System.Net.Http.Headers; using System.Text; -using System.Threading.Tasks; using Stef.Validation; using WireMock.Constants; using WireMock.Matchers; @@ -16,7 +12,7 @@ namespace WireMock.Util; internal static class BodyParser { private static readonly Encoding DefaultEncoding = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false, throwOnInvalidBytes: true); - private static readonly Encoding[] SupportedBodyAsStringEncodingForMultipart = [ DefaultEncoding, Encoding.ASCII ]; + private static readonly Encoding[] SupportedBodyAsStringEncodingForMultipart = [DefaultEncoding, Encoding.ASCII]; /* HEAD - No defined body semantics. @@ -156,7 +152,7 @@ internal static class BodyParser } // Try to get the body as String, FormUrlEncoded or Json - try + if (IsProbablyText(data.BodyAsBytes)) { data.BodyAsString = DefaultEncoding.GetString(data.BodyAsBytes); data.Encoding = DefaultEncoding; @@ -168,15 +164,8 @@ internal static class BodyParser QueryStringParser.TryParse(data.BodyAsString, false, out var nameValueCollection) ) { - try - { - data.BodyAsFormUrlEncoded = nameValueCollection; - data.DetectedBodyType = BodyType.FormUrlEncoded; - } - catch - { - // Deserialize FormUrlEncoded failed, just ignore. - } + data.BodyAsFormUrlEncoded = nameValueCollection; + data.DetectedBodyType = BodyType.FormUrlEncoded; } // If string is not null or empty, try to deserialize the string to a JObject @@ -193,14 +182,10 @@ internal static class BodyParser } } } - catch - { - // Reading as string failed, just ignore - } return data; } - + private static async Task<(string? ContentType, byte[] Bytes)> ReadBytesAsync(Stream stream, string? contentEncoding = null, bool decompressGZipAndDeflate = true) { using var memoryStream = new MemoryStream(); @@ -215,4 +200,49 @@ internal static class BodyParser return (null, data); } + + public static bool IsProbablyText(byte[] data) + { + if (data.Length == 0) + { + return true; + } + ; + + // 1) Quick binary detection + for (int i = 0; i < data.Length; i++) + { + if (data[i] == 0) + { + return false; + } + } + + // 2) Validate UTF-8 + string text; + try + { + text = DefaultEncoding.GetString(data); + } + catch + { + return false; + } + + // 3) Count printable characters + int printable = 0; + + foreach (char c in text) + { + if (!char.IsControl(c) || char.IsWhiteSpace(c)) + { + printable++; + } + } + + var ratio = (double)printable / text.Length; + + // Threshold commonly used by tools like git + return ratio > 0.85; + } } \ No newline at end of file diff --git a/test/WireMock.Net.Tests/FluentAssertions/WireMockAdminApiAssertionsTests.cs b/test/WireMock.Net.Tests/FluentAssertions/WireMockAdminApiAssertionsTests.cs index bf2578e8..abcf5080 100644 --- a/test/WireMock.Net.Tests/FluentAssertions/WireMockAdminApiAssertionsTests.cs +++ b/test/WireMock.Net.Tests/FluentAssertions/WireMockAdminApiAssertionsTests.cs @@ -1002,7 +1002,7 @@ public class WireMockAdminApiAssertionsTests : IDisposable act.Should() .Throw() - .WithMessage("Expected wiremockadminapi to have been called using body \"byte[1] {...}\", but didn't find it among the body/bodies ."); + .WithMessage("Expected wiremockadminapi to have been called using body \"byte[1] {...}\", but didn't find it among the body/bodies \"byte[1] {...}\"."); } [Fact] @@ -1016,21 +1016,22 @@ public class WireMockAdminApiAssertionsTests : IDisposable }); var adminApi = RestClient.For(server.Url); + var bytes = new byte[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 }; server - .Given(Request.Create().WithPath("/a").UsingPut().WithBody([100])) + .Given(Request.Create().WithPath("/binary").UsingPut().WithBody(bytes)) .RespondWith(Response.Create().WithBody("A response")); // Act using var httpClient = new HttpClient(); - await httpClient.PutAsync($"{server.Url}/a", new ByteArrayContent([100]), _ct); + await httpClient.PutAsync($"{server.Url}/binary", new ByteArrayContent(bytes), _ct); // Assert adminApi .Should() .HaveReceived(1) .Calls() - .WithBodyAsBytes([100]) + .WithBodyAsBytes(bytes) .And .UsingPut();