diff --git a/docs/features/headerstransformation.rst b/docs/features/headerstransformation.rst index 744227e2..8c1f1459 100644 --- a/docs/features/headerstransformation.rst +++ b/docs/features/headerstransformation.rst @@ -81,6 +81,7 @@ Placeholders Ocelot allows placeholders that can be used in header transformation. +{RemoteIpAddress} - This will find the clients IP address using _httpContextAccessor.HttpContext.Connection.RemoteIpAddress.ToString() so you will get back some IP. {BaseUrl} - This will use Ocelot's base url e.g. http://localhost:5000 as its value. {DownstreamBaseUrl} - This will use the downstream services base url e.g. http://localhost:5000 as its value. This only works for DownstreamHeaderTransform at the moment. {TraceId} - This will use the Butterfly APM Trace Id. This only works for DownstreamHeaderTransform at the moment. @@ -120,6 +121,17 @@ finally if you are using a load balancer with Ocelot you will get multiple downs "AllowAutoRedirect": false, }, +X-Forwarded-For +^^^^^^^^^^^^^^^ + +An example of using {RemoteIpAddress} placeholder... + +.. code-block:: json + + "UpstreamHeaderTransform": { + "X-Forwarded-For": "{RemoteIpAddress}" + } + Future ^^^^^^ diff --git a/src/Ocelot/Headers/AddHeadersToRequest.cs b/src/Ocelot/Headers/AddHeadersToRequest.cs index a7b5b689..6027e9de 100644 --- a/src/Ocelot/Headers/AddHeadersToRequest.cs +++ b/src/Ocelot/Headers/AddHeadersToRequest.cs @@ -1,22 +1,29 @@ -using System.Collections.Generic; -using System.Linq; -using Ocelot.Configuration; -using Ocelot.Infrastructure.Claims.Parser; -using Ocelot.Responses; -using System.Net.Http; -using Microsoft.AspNetCore.Http; -using Ocelot.Configuration.Creator; -using Ocelot.Request.Middleware; - -namespace Ocelot.Headers +namespace Ocelot.Headers { + using System.Collections.Generic; + using System.Linq; + using Infrastructure; + using Logging; + using Ocelot.Configuration; + using Ocelot.Infrastructure.Claims.Parser; + using Ocelot.Responses; + using Microsoft.AspNetCore.Http; + using Microsoft.Extensions.Primitives; + using Ocelot.Configuration.Creator; + using Ocelot.Middleware; + using Ocelot.Request.Middleware; + public class AddHeadersToRequest : IAddHeadersToRequest { private readonly IClaimsParser _claimsParser; + private readonly IPlaceholders _placeholders; + private readonly IOcelotLogger _logger; - public AddHeadersToRequest(IClaimsParser claimsParser) + public AddHeadersToRequest(IClaimsParser claimsParser, IPlaceholders placeholders, IOcelotLoggerFactory factory) { + _logger = factory.CreateLogger(); _claimsParser = claimsParser; + _placeholders = placeholders; } public Response SetHeadersOnDownstreamRequest(List claimsToThings, IEnumerable claims, DownstreamRequest downstreamRequest) @@ -46,6 +53,7 @@ namespace Ocelot.Headers public void SetHeadersOnDownstreamRequest(IEnumerable headers, HttpContext context) { var requestHeader = context.Request.Headers; + foreach (var header in headers) { if (requestHeader.ContainsKey(header.Key)) @@ -53,7 +61,22 @@ namespace Ocelot.Headers requestHeader.Remove(header.Key); } - requestHeader.Add(header.Key, header.Value); + if (header.Value.StartsWith("{") && header.Value.EndsWith("}")) + { + var value = _placeholders.Get(header.Value); + + if (value.IsError) + { + _logger.LogWarning($"Unable to add header to response {header.Key}: {header.Value}"); + continue; + } + + requestHeader.Add(header.Key, new StringValues(value.Data)); + } + else + { + requestHeader.Add(header.Key, header.Value); + } } } } diff --git a/src/Ocelot/Headers/HttpResponseHeaderReplacer.cs b/src/Ocelot/Headers/HttpResponseHeaderReplacer.cs index d1fb295f..eb88af7a 100644 --- a/src/Ocelot/Headers/HttpResponseHeaderReplacer.cs +++ b/src/Ocelot/Headers/HttpResponseHeaderReplacer.cs @@ -1,15 +1,13 @@ -using System.Collections.Generic; -using System.Linq; -using Ocelot.Configuration; -using Ocelot.Infrastructure; -using Ocelot.Infrastructure.Extensions; -using Ocelot.Middleware; -using Ocelot.Middleware.Multiplexer; -using Ocelot.Request.Middleware; -using Ocelot.Responses; - namespace Ocelot.Headers { + using System.Collections.Generic; + using System.Linq; + using Ocelot.Configuration; + using Ocelot.Infrastructure; + using Ocelot.Infrastructure.Extensions; + using Ocelot.Middleware; + using Ocelot.Responses; + public class HttpResponseHeaderReplacer : IHttpResponseHeaderReplacer { private readonly IPlaceholders _placeholders; @@ -19,8 +17,11 @@ namespace Ocelot.Headers _placeholders = placeholders; } - public Response Replace(DownstreamResponse response, List fAndRs, DownstreamRequest request) + public Response Replace(DownstreamContext context, List fAndRs) { + var response = context.DownstreamResponse; + var request = context.DownstreamRequest; + foreach (var f in fAndRs) { var dict = response.Headers.ToDictionary(x => x.Key); diff --git a/src/Ocelot/Headers/IHttpResponseHeaderReplacer.cs b/src/Ocelot/Headers/IHttpResponseHeaderReplacer.cs index b871ac19..7a180196 100644 --- a/src/Ocelot/Headers/IHttpResponseHeaderReplacer.cs +++ b/src/Ocelot/Headers/IHttpResponseHeaderReplacer.cs @@ -1,14 +1,12 @@ -using System.Collections.Generic; -using Ocelot.Configuration; -using Ocelot.Middleware; -using Ocelot.Middleware.Multiplexer; -using Ocelot.Request.Middleware; -using Ocelot.Responses; - namespace Ocelot.Headers { + using System.Collections.Generic; + using Ocelot.Configuration; + using Ocelot.Middleware; + using Ocelot.Responses; + public interface IHttpResponseHeaderReplacer { - Response Replace(DownstreamResponse response, List fAndRs, DownstreamRequest httpRequestMessage); + Response Replace(DownstreamContext context, List fAndRs); } } diff --git a/src/Ocelot/Headers/Middleware/HttpHeadersTransformationMiddleware.cs b/src/Ocelot/Headers/Middleware/HttpHeadersTransformationMiddleware.cs index bead85c5..4ac0fc8c 100644 --- a/src/Ocelot/Headers/Middleware/HttpHeadersTransformationMiddleware.cs +++ b/src/Ocelot/Headers/Middleware/HttpHeadersTransformationMiddleware.cs @@ -45,7 +45,7 @@ namespace Ocelot.Headers.Middleware var postFAndRs = context.DownstreamReRoute.DownstreamHeadersFindAndReplace; - _postReplacer.Replace(context.DownstreamResponse, postFAndRs, context.DownstreamRequest); + _postReplacer.Replace(context, postFAndRs); _addHeadersToResponse.Add(context.DownstreamReRoute.AddHeadersToDownstream, context.DownstreamResponse); } diff --git a/src/Ocelot/Infrastructure/Placeholders.cs b/src/Ocelot/Infrastructure/Placeholders.cs index c43dea14..3aecec31 100644 --- a/src/Ocelot/Infrastructure/Placeholders.cs +++ b/src/Ocelot/Infrastructure/Placeholders.cs @@ -1,47 +1,38 @@ -using System; -using System.Collections.Generic; -using System.Net.Http; -using Ocelot.Infrastructure.RequestData; -using Ocelot.Middleware; -using Ocelot.Request.Middleware; -using Ocelot.Responses; - namespace Ocelot.Infrastructure { + using System; + using System.Collections.Generic; + using Microsoft.AspNetCore.Http; + using Ocelot.Infrastructure.RequestData; + using Ocelot.Middleware; + using Ocelot.Request.Middleware; + using Ocelot.Responses; + public class Placeholders : IPlaceholders { private readonly Dictionary>> _placeholders; private readonly Dictionary> _requestPlaceholders; private readonly IBaseUrlFinder _finder; private readonly IRequestScopedDataRepository _repo; + private readonly IHttpContextAccessor _httpContextAccessor; - public Placeholders(IBaseUrlFinder finder, IRequestScopedDataRepository repo) + public Placeholders(IBaseUrlFinder finder, IRequestScopedDataRepository repo, IHttpContextAccessor httpContextAccessor) { _repo = repo; + _httpContextAccessor = httpContextAccessor; _finder = finder; - _placeholders = new Dictionary>>(); - _placeholders.Add("{BaseUrl}", () => new OkResponse(_finder.Find())); - _placeholders.Add("{TraceId}", () => { - var traceId = _repo.Get("TraceId"); - if(traceId.IsError) - { - return new ErrorResponse(traceId.Errors); - } + _placeholders = new Dictionary>> + { + { "{BaseUrl}", GetBaseUrl() }, + { "{TraceId}", GetTraceId() }, + { "{RemoteIpAddress}", GetRemoteIpAddress() } - return new OkResponse(traceId.Data); - }); + }; - _requestPlaceholders = new Dictionary>(); - _requestPlaceholders.Add("{DownstreamBaseUrl}", x => { - var downstreamUrl = $"{x.Scheme}://{x.Host}"; - - if(x.Port != 80 && x.Port != 443) - { - downstreamUrl = $"{downstreamUrl}:{x.Port}"; - } - - return $"{downstreamUrl}/"; - }); + _requestPlaceholders = new Dictionary> + { + { "{DownstreamBaseUrl}", GetDownstreamBaseUrl() } + }; } public Response Get(string key) @@ -67,5 +58,56 @@ namespace Ocelot.Infrastructure return new ErrorResponse(new CouldNotFindPlaceholderError(key)); } + + private Func> GetRemoteIpAddress() + { + return () => + { + // this can blow up so adding try catch and return error + try + { + var remoteIdAddress = _httpContextAccessor.HttpContext.Connection.RemoteIpAddress.ToString(); + return new OkResponse(remoteIdAddress); + } + catch (Exception e) + { + return new ErrorResponse(new CouldNotFindPlaceholderError("{RemoteIpAddress}")); + } + }; + } + + private Func GetDownstreamBaseUrl() + { + return x => + { + var downstreamUrl = $"{x.Scheme}://{x.Host}"; + + if (x.Port != 80 && x.Port != 443) + { + downstreamUrl = $"{downstreamUrl}:{x.Port}"; + } + + return $"{downstreamUrl}/"; + }; + } + + private Func> GetTraceId() + { + return () => + { + var traceId = _repo.Get("TraceId"); + if (traceId.IsError) + { + return new ErrorResponse(traceId.Errors); + } + + return new OkResponse(traceId.Data); + }; + } + + private Func> GetBaseUrl() + { + return () => new OkResponse(_finder.Find()); + } } } diff --git a/test/Ocelot.AcceptanceTests/HeaderTests.cs b/test/Ocelot.AcceptanceTests/HeaderTests.cs index c31c8868..839157e5 100644 --- a/test/Ocelot.AcceptanceTests/HeaderTests.cs +++ b/test/Ocelot.AcceptanceTests/HeaderTests.cs @@ -4,6 +4,7 @@ namespace Ocelot.AcceptanceTests using System.Collections.Generic; using System.Linq; using System.Net; + using System.Net.Sockets; using System.Threading.Tasks; using Microsoft.AspNetCore.Http; using Ocelot.Configuration.File; diff --git a/test/Ocelot.IntegrationTests/HeaderTests.cs b/test/Ocelot.IntegrationTests/HeaderTests.cs new file mode 100644 index 00000000..c5120813 --- /dev/null +++ b/test/Ocelot.IntegrationTests/HeaderTests.cs @@ -0,0 +1,199 @@ +using Xunit; + +[assembly: CollectionBehavior(DisableTestParallelization = true)] + +namespace Ocelot.IntegrationTests +{ + using System; + using System.Collections.Generic; + using System.IO; + using System.Net.Http; + using Microsoft.AspNetCore.Builder; + using Microsoft.AspNetCore.Hosting; + using Newtonsoft.Json; + using Ocelot.Configuration.File; + using Shouldly; + using TestStack.BDDfy; + using Microsoft.AspNetCore.Http; + using System.Threading.Tasks; + using Microsoft.Extensions.Configuration; + using Ocelot.DependencyInjection; + using Ocelot.Middleware; + using System.Linq; + using System.Net; + + public class HeaderTests : IDisposable + { + private readonly HttpClient _httpClient; + private IWebHost _builder; + private IWebHostBuilder _webHostBuilder; + private readonly string _ocelotBaseUrl; + private IWebHost _downstreamBuilder; + private HttpResponseMessage _response; + + public HeaderTests() + { + _httpClient = new HttpClient(); + _ocelotBaseUrl = "http://localhost:5005"; + _httpClient.BaseAddress = new Uri(_ocelotBaseUrl); + } + + [Fact] + public void should_pass_remote_ip_address_if_as_x_forwarded_for_header() + { + var configuration = new FileConfiguration + { + ReRoutes = new List + { + new FileReRoute + { + DownstreamPathTemplate = "/", + DownstreamScheme = "http", + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = 6773, + } + }, + UpstreamPathTemplate = "/", + UpstreamHttpMethod = new List { "Get" }, + UpstreamHeaderTransform = new Dictionary + { + {"X-Forwarded-For", "{RemoteIpAddress}"} + }, + HttpHandlerOptions = new FileHttpHandlerOptions + { + AllowAutoRedirect = false + } + } + } + }; + + this.Given(x => GivenThereIsAServiceRunningOn("http://localhost:6773", 200, "X-Forwarded-For")) + .And(x => GivenThereIsAConfiguration(configuration)) + .And(x => GivenOcelotIsRunning()) + .When(x => WhenIGetUrlOnTheApiGateway("/")) + .Then(x => ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) + .And(x => ThenXForwardedForIsSet()) + .BDDfy(); + } + + private void GivenThereIsAServiceRunningOn(string url, int statusCode, string headerKey) + { + _downstreamBuilder = new WebHostBuilder() + .UseUrls(url) + .UseKestrel() + .UseContentRoot(Directory.GetCurrentDirectory()) + .UseIISIntegration() + .UseUrls(url) + .Configure(app => + { + app.Run(async context => + { + if (context.Request.Headers.TryGetValue(headerKey, out var values)) + { + var result = values.First(); + context.Response.StatusCode = statusCode; + await context.Response.WriteAsync(result); + } + }); + }) + .Build(); + + _downstreamBuilder.Start(); + } + + private void GivenOcelotIsRunning() + { + _webHostBuilder = new WebHostBuilder() + .UseUrls(_ocelotBaseUrl) + .UseKestrel() + .UseContentRoot(Directory.GetCurrentDirectory()) + .ConfigureAppConfiguration((hostingContext, config) => + { + config.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath); + var env = hostingContext.HostingEnvironment; + config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: false) + .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: false); + config.AddJsonFile("ocelot.json", false, false); + config.AddEnvironmentVariables(); + }) + .ConfigureServices(x => + { + x.AddOcelot(); + }) + .Configure(app => + { + app.UseOcelot().Wait(); + }); + + _builder = _webHostBuilder.Build(); + + _builder.Start(); + } + + private void GivenThereIsAConfiguration(FileConfiguration fileConfiguration) + { + var configurationPath = $"{Directory.GetCurrentDirectory()}/ocelot.json"; + + var jsonConfiguration = JsonConvert.SerializeObject(fileConfiguration); + + if (File.Exists(configurationPath)) + { + File.Delete(configurationPath); + } + + File.WriteAllText(configurationPath, jsonConfiguration); + + var text = File.ReadAllText(configurationPath); + + configurationPath = $"{AppContext.BaseDirectory}/ocelot.json"; + + if (File.Exists(configurationPath)) + { + File.Delete(configurationPath); + } + + File.WriteAllText(configurationPath, jsonConfiguration); + + text = File.ReadAllText(configurationPath); + } + + public async Task WhenIGetUrlOnTheApiGateway(string url) + { + var request = new HttpRequestMessage(HttpMethod.Get, url); + _response = await _httpClient.SendAsync(request); + } + + private void ThenTheStatusCodeShouldBe(HttpStatusCode code) + { + _response.StatusCode.ShouldBe(code); + } + + private void ThenXForwardedForIsSet() + { + var windowsOrMac = "::1"; + var linux = "127.0.0.1"; + + var header = _response.Content.ReadAsStringAsync().Result; + + bool passed = false; + + if(header == windowsOrMac || header == linux) + { + passed = true; + } + + passed.ShouldBeTrue(); + } + + public void Dispose() + { + _builder?.Dispose(); + _httpClient?.Dispose(); + _downstreamBuilder?.Dispose(); + } + } +} diff --git a/test/Ocelot.UnitTests/Configuration/HeaderFindAndReplaceCreatorTests.cs b/test/Ocelot.UnitTests/Configuration/HeaderFindAndReplaceCreatorTests.cs index a8f8644a..6dc292f3 100644 --- a/test/Ocelot.UnitTests/Configuration/HeaderFindAndReplaceCreatorTests.cs +++ b/test/Ocelot.UnitTests/Configuration/HeaderFindAndReplaceCreatorTests.cs @@ -70,6 +70,28 @@ namespace Ocelot.UnitTests.Configuration .BDDfy(); } + [Fact] + public void should_create_with_add_headers_to_request() + { + const string key = "X-Forwarded-For"; + const string value = "{RemoteIpAddress}"; + + var reRoute = new FileReRoute + { + UpstreamHeaderTransform = new Dictionary + { + {key, value}, + } + }; + + var expected = new AddHeader(key, value); + + this.Given(x => GivenTheReRoute(reRoute)) + .When(x => WhenICreate()) + .Then(x => ThenTheFollowingAddHeaderToUpstreamIsReturned(expected)) + .BDDfy(); + } + [Fact] public void should_use_base_url_placeholder() { diff --git a/test/Ocelot.UnitTests/Headers/AddHeadersToRequestClaimToThingTests.cs b/test/Ocelot.UnitTests/Headers/AddHeadersToRequestClaimToThingTests.cs index 2ad59ca5..05e6ab2c 100644 --- a/test/Ocelot.UnitTests/Headers/AddHeadersToRequestClaimToThingTests.cs +++ b/test/Ocelot.UnitTests/Headers/AddHeadersToRequestClaimToThingTests.cs @@ -15,6 +15,9 @@ using Ocelot.Request.Middleware; namespace Ocelot.UnitTests.Headers { + using Ocelot.Infrastructure; + using Ocelot.Logging; + public class AddHeadersToRequestClaimToThingTests { private readonly AddHeadersToRequest _addHeadersToRequest; @@ -24,11 +27,15 @@ namespace Ocelot.UnitTests.Headers private List _configuration; private Response _result; private Response _claimValue; + private Mock _placeholders; + private Mock _factory; public AddHeadersToRequestClaimToThingTests() { _parser = new Mock(); - _addHeadersToRequest = new AddHeadersToRequest(_parser.Object); + _placeholders = new Mock(); + _factory = new Mock(); + _addHeadersToRequest = new AddHeadersToRequest(_parser.Object, _placeholders.Object, _factory.Object); _downstreamRequest = new DownstreamRequest(new HttpRequestMessage(HttpMethod.Get, "http://test.com")); } diff --git a/test/Ocelot.UnitTests/Headers/AddHeadersToRequestPlainTests.cs b/test/Ocelot.UnitTests/Headers/AddHeadersToRequestPlainTests.cs index 69fd4f69..c2b4189d 100644 --- a/test/Ocelot.UnitTests/Headers/AddHeadersToRequestPlainTests.cs +++ b/test/Ocelot.UnitTests/Headers/AddHeadersToRequestPlainTests.cs @@ -1,23 +1,56 @@ -using Microsoft.AspNetCore.Http; -using Moq; -using Ocelot.Configuration.Creator; -using Ocelot.Headers; -using Ocelot.Infrastructure.Claims.Parser; -using Shouldly; -using TestStack.BDDfy; -using Xunit; - -namespace Ocelot.UnitTests.Headers +namespace Ocelot.UnitTests.Headers { + using Ocelot.Infrastructure; + using Ocelot.Logging; + using Microsoft.AspNetCore.Http; + using Moq; + using Ocelot.Configuration.Creator; + using Ocelot.Headers; + using Ocelot.Infrastructure.Claims.Parser; + using Responder; + using Responses; + using Shouldly; + using TestStack.BDDfy; + using Xunit; + public class AddHeadersToRequestPlainTests { private readonly AddHeadersToRequest _addHeadersToRequest; private HttpContext _context; private AddHeader _addedHeader; + private readonly Mock _placeholders; + private Mock _factory; + private readonly Mock _logger; public AddHeadersToRequestPlainTests() { - _addHeadersToRequest = new AddHeadersToRequest(Mock.Of()); + _placeholders = new Mock(); + _factory = new Mock(); + _logger = new Mock(); + _factory.Setup(x => x.CreateLogger()).Returns(_logger.Object); + _addHeadersToRequest = new AddHeadersToRequest(Mock.Of(), _placeholders.Object, _factory.Object); + } + + [Fact] + public void should_log_error_if_cannot_find_placeholder() + { + _placeholders.Setup(x => x.Get(It.IsAny())).Returns(new ErrorResponse(new AnyError())); + + this.Given(_ => GivenHttpRequestWithoutHeaders()) + .When(_ => WhenAddingHeader("X-Forwarded-For", "{RemoteIdAddress}")) + .Then(_ => ThenAnErrorIsLogged("X-Forwarded-For", "{RemoteIdAddress}")) + .BDDfy(); + } + + [Fact] + public void should_add_placeholder_to_downstream_request() + { + _placeholders.Setup(x => x.Get(It.IsAny())).Returns(new OkResponse("replaced")); + + this.Given(_ => GivenHttpRequestWithoutHeaders()) + .When(_ => WhenAddingHeader("X-Forwarded-For", "{RemoteIdAddress}")) + .Then(_ => ThenTheHeaderGetsTakenOverToTheRequestHeaders("replaced")) + .BDDfy(); } [Fact] @@ -38,9 +71,23 @@ namespace Ocelot.UnitTests.Headers .BDDfy(); } + private void ThenAnErrorIsLogged(string key, string value) + { + _logger.Verify(x => x.LogWarning($"Unable to add header to response {key}: {value}"), Times.Once); + } + + private void GivenHttpRequestWithoutHeaders() { - _context = new DefaultHttpContext(); + _context = new DefaultHttpContext + { + Request = + { + Headers = + { + } + } + }; } private void GivenHttpRequestWithHeader(string headerKey, string headerValue) @@ -71,5 +118,12 @@ namespace Ocelot.UnitTests.Headers value.ShouldNotBeNull($"Value of header {_addedHeader.Key} was expected to not be null."); value.ToString().ShouldBe(_addedHeader.Value); } + + private void ThenTheHeaderGetsTakenOverToTheRequestHeaders(string expected) + { + var requestHeaders = _context.Request.Headers; + var value = requestHeaders[_addedHeader.Key]; + value.ToString().ShouldBe(expected); + } } } diff --git a/test/Ocelot.UnitTests/Headers/HttpHeadersTransformationMiddlewareTests.cs b/test/Ocelot.UnitTests/Headers/HttpHeadersTransformationMiddlewareTests.cs index 7b13cc6d..8cba2297 100644 --- a/test/Ocelot.UnitTests/Headers/HttpHeadersTransformationMiddlewareTests.cs +++ b/test/Ocelot.UnitTests/Headers/HttpHeadersTransformationMiddlewareTests.cs @@ -109,7 +109,7 @@ namespace Ocelot.UnitTests.Headers private void ThenTheIHttpResponseHeaderReplacerIsCalledCorrectly() { - _postReplacer.Verify(x => x.Replace(It.IsAny(), It.IsAny>(), It.IsAny()), Times.Once); + _postReplacer.Verify(x => x.Replace(It.IsAny(), It.IsAny>()), Times.Once); } private void GivenTheFollowingRequest() diff --git a/test/Ocelot.UnitTests/Headers/HttpResponseHeaderReplacerTests.cs b/test/Ocelot.UnitTests/Headers/HttpResponseHeaderReplacerTests.cs index a630c2d8..4726c7ae 100644 --- a/test/Ocelot.UnitTests/Headers/HttpResponseHeaderReplacerTests.cs +++ b/test/Ocelot.UnitTests/Headers/HttpResponseHeaderReplacerTests.cs @@ -1,22 +1,22 @@ -using Xunit; -using Shouldly; -using TestStack.BDDfy; -using System.Net.Http; -using Ocelot.Headers; -using Ocelot.Configuration; -using System.Collections.Generic; -using Ocelot.Responses; -using System.Linq; -using System.Net; -using Moq; -using Ocelot.Infrastructure; -using Ocelot.Middleware; -using Ocelot.Infrastructure.RequestData; -using Ocelot.Middleware.Multiplexer; -using Ocelot.Request.Middleware; - namespace Ocelot.UnitTests.Headers { + using Microsoft.AspNetCore.Http; + using Ocelot.Infrastructure; + using Ocelot.Middleware; + using Ocelot.Infrastructure.RequestData; + using Ocelot.Request.Middleware; + using Xunit; + using Shouldly; + using TestStack.BDDfy; + using System.Net.Http; + using Ocelot.Headers; + using Ocelot.Configuration; + using System.Collections.Generic; + using Ocelot.Responses; + using System.Linq; + using System.Net; + using Moq; + public class HttpResponseHeaderReplacerTests { private DownstreamResponse _response; @@ -27,12 +27,14 @@ namespace Ocelot.UnitTests.Headers private DownstreamRequest _request; private Mock _finder; private Mock _repo; + private Mock _accessor; public HttpResponseHeaderReplacerTests() { + _accessor = new Mock(); _repo = new Mock(); _finder = new Mock(); - _placeholders = new Placeholders(_finder.Object, _repo.Object); + _placeholders = new Placeholders(_finder.Object, _repo.Object, _accessor.Object); _replacer = new HttpResponseHeaderReplacer(_placeholders); } @@ -261,7 +263,8 @@ namespace Ocelot.UnitTests.Headers private void WhenICallTheReplacer() { - _result = _replacer.Replace(_response, _headerFindAndReplaces, _request); + var context = new DownstreamContext(new DefaultHttpContext()) {DownstreamResponse = _response, DownstreamRequest = _request}; + _result = _replacer.Replace(context, _headerFindAndReplaces); } private void ThenTheHeaderShouldBe(string key, string value) diff --git a/test/Ocelot.UnitTests/Infrastructure/PlaceholdersTests.cs b/test/Ocelot.UnitTests/Infrastructure/PlaceholdersTests.cs index 729c4329..191d15fc 100644 --- a/test/Ocelot.UnitTests/Infrastructure/PlaceholdersTests.cs +++ b/test/Ocelot.UnitTests/Infrastructure/PlaceholdersTests.cs @@ -1,27 +1,31 @@ -using System; -using System.Net.Http; -using Moq; -using Ocelot.Infrastructure; -using Ocelot.Infrastructure.RequestData; -using Ocelot.Middleware; -using Ocelot.Request.Middleware; -using Ocelot.Responses; -using Shouldly; -using Xunit; - namespace Ocelot.UnitTests.Infrastructure { + using Microsoft.AspNetCore.Http; + using System; + using System.Net; + using System.Net.Http; + using Moq; + using Ocelot.Infrastructure; + using Ocelot.Infrastructure.RequestData; + using Ocelot.Middleware; + using Ocelot.Request.Middleware; + using Ocelot.Responses; + using Shouldly; + using Xunit; + public class PlaceholdersTests { - private IPlaceholders _placeholders; - private Mock _finder; - private Mock _repo; - + private readonly IPlaceholders _placeholders; + private readonly Mock _finder; + private readonly Mock _repo; + private readonly Mock _accessor; + public PlaceholdersTests() { + _accessor = new Mock(); _repo = new Mock(); _finder = new Mock(); - _placeholders = new Placeholders(_finder.Object, _repo.Object); + _placeholders = new Placeholders(_finder.Object, _repo.Object, _accessor.Object); } [Fact] @@ -33,6 +37,15 @@ namespace Ocelot.UnitTests.Infrastructure result.Data.ShouldBe(baseUrl); } + [Fact] + public void should_return_remote_ip_address() + { + var httpContext = new DefaultHttpContext(){Connection = { RemoteIpAddress = IPAddress.Any}}; + _accessor.Setup(x => x.HttpContext).Returns(httpContext); + var result = _placeholders.Get("{RemoteIpAddress}"); + result.Data.ShouldBe(httpContext.Connection.RemoteIpAddress.ToString()); + } + [Fact] public void should_return_key_does_not_exist() {