diff --git a/WireMock.Net Solution.sln.DotSettings b/WireMock.Net Solution.sln.DotSettings index 84b8ed70..f55a13e7 100644 --- a/WireMock.Net Solution.sln.DotSettings +++ b/WireMock.Net Solution.sln.DotSettings @@ -9,6 +9,7 @@ WWW XMS XUA + True True True True diff --git a/examples/WireMock.Net.Console.Net452.Classic/MainApp.cs b/examples/WireMock.Net.Console.Net452.Classic/MainApp.cs index 1c590629..a18cd321 100644 --- a/examples/WireMock.Net.Console.Net452.Classic/MainApp.cs +++ b/examples/WireMock.Net.Console.Net452.Classic/MainApp.cs @@ -97,7 +97,7 @@ namespace WireMock.Net.ConsoleApplication .WithHeader("postmanecho", "post") ) .RespondWith(Response.Create() - .WithProxy(new ProxyAndRecordSettings { Url = "http://postman-echo.com/post" }) + .WithProxy(new ProxyAndRecordSettings { Url = "http://postman-echo.com" }) ); server diff --git a/src/WireMock.Net.Abstractions/Admin/Requests/LogRequestModel.cs b/src/WireMock.Net.Abstractions/Admin/Requests/LogRequestModel.cs index e63eb76d..dc94df25 100644 --- a/src/WireMock.Net.Abstractions/Admin/Requests/LogRequestModel.cs +++ b/src/WireMock.Net.Abstractions/Admin/Requests/LogRequestModel.cs @@ -40,6 +40,11 @@ namespace WireMock.Admin.Requests /// public string AbsoluteUrl { get; set; } + /// + /// The ProxyUrl (if a proxy is used). + /// + public string ProxyUrl { get; set; } + /// /// The query. /// diff --git a/src/WireMock.Net/RequestMessage.cs b/src/WireMock.Net/RequestMessage.cs index 10882ba6..361db0ea 100644 --- a/src/WireMock.Net/RequestMessage.cs +++ b/src/WireMock.Net/RequestMessage.cs @@ -32,6 +32,11 @@ namespace WireMock /// public string AbsoluteUrl { get; } + /// + /// The ProxyUrl (if a proxy is used). + /// + public string ProxyUrl { get; set; } + /// /// Gets the DateTime. /// diff --git a/src/WireMock.Net/ResponseBuilders/Response.cs b/src/WireMock.Net/ResponseBuilders/Response.cs index e3d67985..c791969a 100644 --- a/src/WireMock.Net/ResponseBuilders/Response.cs +++ b/src/WireMock.Net/ResponseBuilders/Response.cs @@ -343,14 +343,22 @@ namespace WireMock.ResponseBuilders if (ProxyUrl != null && _httpClientForProxy != null) { + string RemoveFirstOccurrence(string source, string find) + { + int place = source.IndexOf(find, StringComparison.OrdinalIgnoreCase); + return place >= 0 ? source.Remove(place, find.Length) : source; + } + var requestUri = new Uri(requestMessage.Url); - var proxyUri = new Uri(ProxyUrl); - var proxyUriWithRequestPathAndQuery = new Uri(proxyUri, requestUri.PathAndQuery); + + // Build the proxy url and skip duplicates + string extra = RemoveFirstOccurrence(requestUri.LocalPath.TrimEnd('/'), new Uri(ProxyUrl).LocalPath.TrimEnd('/')); + requestMessage.ProxyUrl = ProxyUrl + extra + requestUri.Query; return await HttpClientHelper.SendAsync( - _httpClientForProxy, + _httpClientForProxy, requestMessage, - proxyUriWithRequestPathAndQuery.AbsoluteUri, + requestMessage.ProxyUrl, !settings.DisableJsonBodyParsing.GetValueOrDefault(false), !settings.DisableRequestBodyDecompressing.GetValueOrDefault(false) ); diff --git a/src/WireMock.Net/Serialization/LogEntryMapper.cs b/src/WireMock.Net/Serialization/LogEntryMapper.cs index 9ea871f6..46ef1085 100644 --- a/src/WireMock.Net/Serialization/LogEntryMapper.cs +++ b/src/WireMock.Net/Serialization/LogEntryMapper.cs @@ -20,6 +20,7 @@ namespace WireMock.Serialization AbsolutePath = logEntry.RequestMessage.AbsolutePath, Url = logEntry.RequestMessage.Url, AbsoluteUrl = logEntry.RequestMessage.AbsoluteUrl, + ProxyUrl = logEntry.RequestMessage.ProxyUrl, Query = logEntry.RequestMessage.Query, Method = logEntry.RequestMessage.Method, Headers = logEntry.RequestMessage.Headers, diff --git a/test/WireMock.Net.Tests/ResponseBuilders/ResponseWithProxyTests.cs b/test/WireMock.Net.Tests/ResponseBuilders/ResponseWithProxyTests.cs index 9219e2cd..c5eae9f6 100644 --- a/test/WireMock.Net.Tests/ResponseBuilders/ResponseWithProxyTests.cs +++ b/test/WireMock.Net.Tests/ResponseBuilders/ResponseWithProxyTests.cs @@ -16,6 +16,7 @@ namespace WireMock.Net.Tests.ResponseBuilders { public class ResponseWithProxyTests : IDisposable { + private const string ClientIp = "::1"; private readonly WireMockServerSettings _settings = new WireMockServerSettings(); private readonly WireMockServer _server; private readonly Guid _guid; @@ -27,21 +28,32 @@ namespace WireMock.Net.Tests.ResponseBuilders _server = WireMockServer.Start(); _server.Given(Request.Create().UsingPost().WithPath($"/{_guid}")) .RespondWith(Response.Create().WithStatusCode(201).WithBodyAsJson(new { p = 42 }).WithHeader("Content-Type", "application/json")); + _server.Given(Request.Create().UsingPost().WithPath($"/{_guid}/append")) + .RespondWith(Response.Create().WithStatusCode(201).WithBodyAsJson(new { p = 10 }).WithHeader("Content-Type", "application/json")); + _server.Given(Request.Create().UsingPost().WithPath($"/prepend/{_guid}")) + .RespondWith(Response.Create().WithStatusCode(201).WithBodyAsJson(new { p = 11 }).WithHeader("Content-Type", "application/json")); + _server.Given(Request.Create().UsingPost().WithPath($"/prepend/{_guid}/append")) + .RespondWith(Response.Create().WithStatusCode(201).WithBodyAsJson(new { p = 12 }).WithHeader("Content-Type", "application/json")); } - [Fact] - public async Task Response_WithProxy() + [Theory] + [InlineData("", "", "{\"p\":42}")] + [InlineData("", "/append", "{\"p\":10}")] + [InlineData("/prepend", "", "{\"p\":11}")] + [InlineData("/prepend", "/append", "{\"p\":12}")] + public async Task Response_WithProxy(string prepend, string append, string expectedBody) { // Assign var headers = new Dictionary { { "Content-Type", new[] { "application/xml" } } }; - var request = new RequestMessage(new UrlDetails($"{_server.Urls[0]}/{_guid}"), "POST", "::1", new BodyData { DetectedBodyType = BodyType.Json, BodyAsJson = new { a = 1 } }, headers); + var request = new RequestMessage(new UrlDetails($"{_server.Urls[0]}{prepend}/{_guid}{append}"), "POST", ClientIp, new BodyData { DetectedBodyType = BodyType.Json, BodyAsJson = new { a = 1 } }, headers); var response = Response.Create().WithProxy(_server.Urls[0]); // Act var responseMessage = await response.ProvideResponseAsync(request, _settings); // Assert - Check.That(responseMessage.BodyData.BodyAsString).IsEqualTo("{\"p\":42}"); + Check.That(request.ProxyUrl).IsNotNull(); + Check.That(responseMessage.BodyData.BodyAsString).IsEqualTo(expectedBody); Check.That(responseMessage.StatusCode).IsEqualTo(201); Check.That(responseMessage.Headers["Content-Type"].ToString()).IsEqualTo("application/json"); } @@ -63,7 +75,7 @@ namespace WireMock.Net.Tests.ResponseBuilders var response = Response.Create().WithProxy(settings); // Act - var request = new RequestMessage(new UrlDetails($"{_server.Urls[0]}/{_guid}"), "GET", "::1"); + var request = new RequestMessage(new UrlDetails($"{_server.Urls[0]}/{_guid}"), "GET", ClientIp); Check.ThatAsyncCode(() => response.ProvideResponseAsync(request, _settings)).Throws(); }