mirror of
https://github.com/nsnail/Ocelot.git
synced 2025-04-22 06:22:50 +08:00
Feature/#623 (#632)
* #630 only set status code if response hasnt started, otherwise exception * #623 made {RemoteIpAddress} available as placeholder so you can do x-forwarded-for * #623 local address different on mac, windows and linux for integration test
This commit is contained in:
parent
54cdc74293
commit
aa14b2f877
@ -81,6 +81,7 @@ Placeholders
|
|||||||
|
|
||||||
Ocelot allows placeholders that can be used in header transformation.
|
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.
|
{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.
|
{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.
|
{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,
|
"AllowAutoRedirect": false,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
X-Forwarded-For
|
||||||
|
^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
An example of using {RemoteIpAddress} placeholder...
|
||||||
|
|
||||||
|
.. code-block:: json
|
||||||
|
|
||||||
|
"UpstreamHeaderTransform": {
|
||||||
|
"X-Forwarded-For": "{RemoteIpAddress}"
|
||||||
|
}
|
||||||
|
|
||||||
Future
|
Future
|
||||||
^^^^^^
|
^^^^^^
|
||||||
|
|
||||||
|
@ -1,22 +1,29 @@
|
|||||||
using System.Collections.Generic;
|
namespace Ocelot.Headers
|
||||||
|
{
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using Infrastructure;
|
||||||
|
using Logging;
|
||||||
using Ocelot.Configuration;
|
using Ocelot.Configuration;
|
||||||
using Ocelot.Infrastructure.Claims.Parser;
|
using Ocelot.Infrastructure.Claims.Parser;
|
||||||
using Ocelot.Responses;
|
using Ocelot.Responses;
|
||||||
using System.Net.Http;
|
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using Microsoft.Extensions.Primitives;
|
||||||
using Ocelot.Configuration.Creator;
|
using Ocelot.Configuration.Creator;
|
||||||
|
using Ocelot.Middleware;
|
||||||
using Ocelot.Request.Middleware;
|
using Ocelot.Request.Middleware;
|
||||||
|
|
||||||
namespace Ocelot.Headers
|
|
||||||
{
|
|
||||||
public class AddHeadersToRequest : IAddHeadersToRequest
|
public class AddHeadersToRequest : IAddHeadersToRequest
|
||||||
{
|
{
|
||||||
private readonly IClaimsParser _claimsParser;
|
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<AddHeadersToRequest>();
|
||||||
_claimsParser = claimsParser;
|
_claimsParser = claimsParser;
|
||||||
|
_placeholders = placeholders;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Response SetHeadersOnDownstreamRequest(List<ClaimToThing> claimsToThings, IEnumerable<System.Security.Claims.Claim> claims, DownstreamRequest downstreamRequest)
|
public Response SetHeadersOnDownstreamRequest(List<ClaimToThing> claimsToThings, IEnumerable<System.Security.Claims.Claim> claims, DownstreamRequest downstreamRequest)
|
||||||
@ -46,6 +53,7 @@ namespace Ocelot.Headers
|
|||||||
public void SetHeadersOnDownstreamRequest(IEnumerable<AddHeader> headers, HttpContext context)
|
public void SetHeadersOnDownstreamRequest(IEnumerable<AddHeader> headers, HttpContext context)
|
||||||
{
|
{
|
||||||
var requestHeader = context.Request.Headers;
|
var requestHeader = context.Request.Headers;
|
||||||
|
|
||||||
foreach (var header in headers)
|
foreach (var header in headers)
|
||||||
{
|
{
|
||||||
if (requestHeader.ContainsKey(header.Key))
|
if (requestHeader.ContainsKey(header.Key))
|
||||||
@ -53,8 +61,23 @@ namespace Ocelot.Headers
|
|||||||
requestHeader.Remove(header.Key);
|
requestHeader.Remove(header.Key);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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);
|
requestHeader.Add(header.Key, header.Value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
@ -1,15 +1,13 @@
|
|||||||
|
namespace Ocelot.Headers
|
||||||
|
{
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Ocelot.Configuration;
|
using Ocelot.Configuration;
|
||||||
using Ocelot.Infrastructure;
|
using Ocelot.Infrastructure;
|
||||||
using Ocelot.Infrastructure.Extensions;
|
using Ocelot.Infrastructure.Extensions;
|
||||||
using Ocelot.Middleware;
|
using Ocelot.Middleware;
|
||||||
using Ocelot.Middleware.Multiplexer;
|
|
||||||
using Ocelot.Request.Middleware;
|
|
||||||
using Ocelot.Responses;
|
using Ocelot.Responses;
|
||||||
|
|
||||||
namespace Ocelot.Headers
|
|
||||||
{
|
|
||||||
public class HttpResponseHeaderReplacer : IHttpResponseHeaderReplacer
|
public class HttpResponseHeaderReplacer : IHttpResponseHeaderReplacer
|
||||||
{
|
{
|
||||||
private readonly IPlaceholders _placeholders;
|
private readonly IPlaceholders _placeholders;
|
||||||
@ -19,8 +17,11 @@ namespace Ocelot.Headers
|
|||||||
_placeholders = placeholders;
|
_placeholders = placeholders;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Response Replace(DownstreamResponse response, List<HeaderFindAndReplace> fAndRs, DownstreamRequest request)
|
public Response Replace(DownstreamContext context, List<HeaderFindAndReplace> fAndRs)
|
||||||
{
|
{
|
||||||
|
var response = context.DownstreamResponse;
|
||||||
|
var request = context.DownstreamRequest;
|
||||||
|
|
||||||
foreach (var f in fAndRs)
|
foreach (var f in fAndRs)
|
||||||
{
|
{
|
||||||
var dict = response.Headers.ToDictionary(x => x.Key);
|
var dict = response.Headers.ToDictionary(x => x.Key);
|
||||||
|
@ -1,14 +1,12 @@
|
|||||||
|
namespace Ocelot.Headers
|
||||||
|
{
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using Ocelot.Configuration;
|
using Ocelot.Configuration;
|
||||||
using Ocelot.Middleware;
|
using Ocelot.Middleware;
|
||||||
using Ocelot.Middleware.Multiplexer;
|
|
||||||
using Ocelot.Request.Middleware;
|
|
||||||
using Ocelot.Responses;
|
using Ocelot.Responses;
|
||||||
|
|
||||||
namespace Ocelot.Headers
|
|
||||||
{
|
|
||||||
public interface IHttpResponseHeaderReplacer
|
public interface IHttpResponseHeaderReplacer
|
||||||
{
|
{
|
||||||
Response Replace(DownstreamResponse response, List<HeaderFindAndReplace> fAndRs, DownstreamRequest httpRequestMessage);
|
Response Replace(DownstreamContext context, List<HeaderFindAndReplace> fAndRs);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -45,7 +45,7 @@ namespace Ocelot.Headers.Middleware
|
|||||||
|
|
||||||
var postFAndRs = context.DownstreamReRoute.DownstreamHeadersFindAndReplace;
|
var postFAndRs = context.DownstreamReRoute.DownstreamHeadersFindAndReplace;
|
||||||
|
|
||||||
_postReplacer.Replace(context.DownstreamResponse, postFAndRs, context.DownstreamRequest);
|
_postReplacer.Replace(context, postFAndRs);
|
||||||
|
|
||||||
_addHeadersToResponse.Add(context.DownstreamReRoute.AddHeadersToDownstream, context.DownstreamResponse);
|
_addHeadersToResponse.Add(context.DownstreamReRoute.AddHeadersToDownstream, context.DownstreamResponse);
|
||||||
}
|
}
|
||||||
|
@ -1,47 +1,38 @@
|
|||||||
|
namespace Ocelot.Infrastructure
|
||||||
|
{
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Net.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
using Ocelot.Infrastructure.RequestData;
|
using Ocelot.Infrastructure.RequestData;
|
||||||
using Ocelot.Middleware;
|
using Ocelot.Middleware;
|
||||||
using Ocelot.Request.Middleware;
|
using Ocelot.Request.Middleware;
|
||||||
using Ocelot.Responses;
|
using Ocelot.Responses;
|
||||||
|
|
||||||
namespace Ocelot.Infrastructure
|
|
||||||
{
|
|
||||||
public class Placeholders : IPlaceholders
|
public class Placeholders : IPlaceholders
|
||||||
{
|
{
|
||||||
private readonly Dictionary<string, Func<Response<string>>> _placeholders;
|
private readonly Dictionary<string, Func<Response<string>>> _placeholders;
|
||||||
private readonly Dictionary<string, Func<DownstreamRequest, string>> _requestPlaceholders;
|
private readonly Dictionary<string, Func<DownstreamRequest, string>> _requestPlaceholders;
|
||||||
private readonly IBaseUrlFinder _finder;
|
private readonly IBaseUrlFinder _finder;
|
||||||
private readonly IRequestScopedDataRepository _repo;
|
private readonly IRequestScopedDataRepository _repo;
|
||||||
|
private readonly IHttpContextAccessor _httpContextAccessor;
|
||||||
|
|
||||||
public Placeholders(IBaseUrlFinder finder, IRequestScopedDataRepository repo)
|
public Placeholders(IBaseUrlFinder finder, IRequestScopedDataRepository repo, IHttpContextAccessor httpContextAccessor)
|
||||||
{
|
{
|
||||||
_repo = repo;
|
_repo = repo;
|
||||||
|
_httpContextAccessor = httpContextAccessor;
|
||||||
_finder = finder;
|
_finder = finder;
|
||||||
_placeholders = new Dictionary<string, Func<Response<string>>>();
|
_placeholders = new Dictionary<string, Func<Response<string>>>
|
||||||
_placeholders.Add("{BaseUrl}", () => new OkResponse<string>(_finder.Find()));
|
|
||||||
_placeholders.Add("{TraceId}", () => {
|
|
||||||
var traceId = _repo.Get<string>("TraceId");
|
|
||||||
if(traceId.IsError)
|
|
||||||
{
|
{
|
||||||
return new ErrorResponse<string>(traceId.Errors);
|
{ "{BaseUrl}", GetBaseUrl() },
|
||||||
}
|
{ "{TraceId}", GetTraceId() },
|
||||||
|
{ "{RemoteIpAddress}", GetRemoteIpAddress() }
|
||||||
|
|
||||||
return new OkResponse<string>(traceId.Data);
|
};
|
||||||
});
|
|
||||||
|
|
||||||
_requestPlaceholders = new Dictionary<string, Func<DownstreamRequest, string>>();
|
_requestPlaceholders = new Dictionary<string, Func<DownstreamRequest, string>>
|
||||||
_requestPlaceholders.Add("{DownstreamBaseUrl}", x => {
|
|
||||||
var downstreamUrl = $"{x.Scheme}://{x.Host}";
|
|
||||||
|
|
||||||
if(x.Port != 80 && x.Port != 443)
|
|
||||||
{
|
{
|
||||||
downstreamUrl = $"{downstreamUrl}:{x.Port}";
|
{ "{DownstreamBaseUrl}", GetDownstreamBaseUrl() }
|
||||||
}
|
};
|
||||||
|
|
||||||
return $"{downstreamUrl}/";
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Response<string> Get(string key)
|
public Response<string> Get(string key)
|
||||||
@ -67,5 +58,56 @@ namespace Ocelot.Infrastructure
|
|||||||
|
|
||||||
return new ErrorResponse<string>(new CouldNotFindPlaceholderError(key));
|
return new ErrorResponse<string>(new CouldNotFindPlaceholderError(key));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Func<Response<string>> GetRemoteIpAddress()
|
||||||
|
{
|
||||||
|
return () =>
|
||||||
|
{
|
||||||
|
// this can blow up so adding try catch and return error
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var remoteIdAddress = _httpContextAccessor.HttpContext.Connection.RemoteIpAddress.ToString();
|
||||||
|
return new OkResponse<string>(remoteIdAddress);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
return new ErrorResponse<string>(new CouldNotFindPlaceholderError("{RemoteIpAddress}"));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private Func<DownstreamRequest, string> GetDownstreamBaseUrl()
|
||||||
|
{
|
||||||
|
return x =>
|
||||||
|
{
|
||||||
|
var downstreamUrl = $"{x.Scheme}://{x.Host}";
|
||||||
|
|
||||||
|
if (x.Port != 80 && x.Port != 443)
|
||||||
|
{
|
||||||
|
downstreamUrl = $"{downstreamUrl}:{x.Port}";
|
||||||
|
}
|
||||||
|
|
||||||
|
return $"{downstreamUrl}/";
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private Func<Response<string>> GetTraceId()
|
||||||
|
{
|
||||||
|
return () =>
|
||||||
|
{
|
||||||
|
var traceId = _repo.Get<string>("TraceId");
|
||||||
|
if (traceId.IsError)
|
||||||
|
{
|
||||||
|
return new ErrorResponse<string>(traceId.Errors);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new OkResponse<string>(traceId.Data);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private Func<Response<string>> GetBaseUrl()
|
||||||
|
{
|
||||||
|
return () => new OkResponse<string>(_finder.Find());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,7 @@ namespace Ocelot.AcceptanceTests
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
|
using System.Net.Sockets;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
using Ocelot.Configuration.File;
|
using Ocelot.Configuration.File;
|
||||||
|
199
test/Ocelot.IntegrationTests/HeaderTests.cs
Normal file
199
test/Ocelot.IntegrationTests/HeaderTests.cs
Normal file
@ -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<FileReRoute>
|
||||||
|
{
|
||||||
|
new FileReRoute
|
||||||
|
{
|
||||||
|
DownstreamPathTemplate = "/",
|
||||||
|
DownstreamScheme = "http",
|
||||||
|
DownstreamHostAndPorts = new List<FileHostAndPort>
|
||||||
|
{
|
||||||
|
new FileHostAndPort
|
||||||
|
{
|
||||||
|
Host = "localhost",
|
||||||
|
Port = 6773,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
UpstreamPathTemplate = "/",
|
||||||
|
UpstreamHttpMethod = new List<string> { "Get" },
|
||||||
|
UpstreamHeaderTransform = new Dictionary<string,string>
|
||||||
|
{
|
||||||
|
{"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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -70,6 +70,28 @@ namespace Ocelot.UnitTests.Configuration
|
|||||||
.BDDfy();
|
.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<string, string>
|
||||||
|
{
|
||||||
|
{key, value},
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var expected = new AddHeader(key, value);
|
||||||
|
|
||||||
|
this.Given(x => GivenTheReRoute(reRoute))
|
||||||
|
.When(x => WhenICreate())
|
||||||
|
.Then(x => ThenTheFollowingAddHeaderToUpstreamIsReturned(expected))
|
||||||
|
.BDDfy();
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void should_use_base_url_placeholder()
|
public void should_use_base_url_placeholder()
|
||||||
{
|
{
|
||||||
|
@ -15,6 +15,9 @@ using Ocelot.Request.Middleware;
|
|||||||
|
|
||||||
namespace Ocelot.UnitTests.Headers
|
namespace Ocelot.UnitTests.Headers
|
||||||
{
|
{
|
||||||
|
using Ocelot.Infrastructure;
|
||||||
|
using Ocelot.Logging;
|
||||||
|
|
||||||
public class AddHeadersToRequestClaimToThingTests
|
public class AddHeadersToRequestClaimToThingTests
|
||||||
{
|
{
|
||||||
private readonly AddHeadersToRequest _addHeadersToRequest;
|
private readonly AddHeadersToRequest _addHeadersToRequest;
|
||||||
@ -24,11 +27,15 @@ namespace Ocelot.UnitTests.Headers
|
|||||||
private List<ClaimToThing> _configuration;
|
private List<ClaimToThing> _configuration;
|
||||||
private Response _result;
|
private Response _result;
|
||||||
private Response<string> _claimValue;
|
private Response<string> _claimValue;
|
||||||
|
private Mock<IPlaceholders> _placeholders;
|
||||||
|
private Mock<IOcelotLoggerFactory> _factory;
|
||||||
|
|
||||||
public AddHeadersToRequestClaimToThingTests()
|
public AddHeadersToRequestClaimToThingTests()
|
||||||
{
|
{
|
||||||
_parser = new Mock<IClaimsParser>();
|
_parser = new Mock<IClaimsParser>();
|
||||||
_addHeadersToRequest = new AddHeadersToRequest(_parser.Object);
|
_placeholders = new Mock<IPlaceholders>();
|
||||||
|
_factory = new Mock<IOcelotLoggerFactory>();
|
||||||
|
_addHeadersToRequest = new AddHeadersToRequest(_parser.Object, _placeholders.Object, _factory.Object);
|
||||||
_downstreamRequest = new DownstreamRequest(new HttpRequestMessage(HttpMethod.Get, "http://test.com"));
|
_downstreamRequest = new DownstreamRequest(new HttpRequestMessage(HttpMethod.Get, "http://test.com"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,23 +1,56 @@
|
|||||||
using Microsoft.AspNetCore.Http;
|
namespace Ocelot.UnitTests.Headers
|
||||||
|
{
|
||||||
|
using Ocelot.Infrastructure;
|
||||||
|
using Ocelot.Logging;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
using Moq;
|
using Moq;
|
||||||
using Ocelot.Configuration.Creator;
|
using Ocelot.Configuration.Creator;
|
||||||
using Ocelot.Headers;
|
using Ocelot.Headers;
|
||||||
using Ocelot.Infrastructure.Claims.Parser;
|
using Ocelot.Infrastructure.Claims.Parser;
|
||||||
|
using Responder;
|
||||||
|
using Responses;
|
||||||
using Shouldly;
|
using Shouldly;
|
||||||
using TestStack.BDDfy;
|
using TestStack.BDDfy;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace Ocelot.UnitTests.Headers
|
|
||||||
{
|
|
||||||
public class AddHeadersToRequestPlainTests
|
public class AddHeadersToRequestPlainTests
|
||||||
{
|
{
|
||||||
private readonly AddHeadersToRequest _addHeadersToRequest;
|
private readonly AddHeadersToRequest _addHeadersToRequest;
|
||||||
private HttpContext _context;
|
private HttpContext _context;
|
||||||
private AddHeader _addedHeader;
|
private AddHeader _addedHeader;
|
||||||
|
private readonly Mock<IPlaceholders> _placeholders;
|
||||||
|
private Mock<IOcelotLoggerFactory> _factory;
|
||||||
|
private readonly Mock<IOcelotLogger> _logger;
|
||||||
|
|
||||||
public AddHeadersToRequestPlainTests()
|
public AddHeadersToRequestPlainTests()
|
||||||
{
|
{
|
||||||
_addHeadersToRequest = new AddHeadersToRequest(Mock.Of<IClaimsParser>());
|
_placeholders = new Mock<IPlaceholders>();
|
||||||
|
_factory = new Mock<IOcelotLoggerFactory>();
|
||||||
|
_logger = new Mock<IOcelotLogger>();
|
||||||
|
_factory.Setup(x => x.CreateLogger<AddHeadersToRequest>()).Returns(_logger.Object);
|
||||||
|
_addHeadersToRequest = new AddHeadersToRequest(Mock.Of<IClaimsParser>(), _placeholders.Object, _factory.Object);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void should_log_error_if_cannot_find_placeholder()
|
||||||
|
{
|
||||||
|
_placeholders.Setup(x => x.Get(It.IsAny<string>())).Returns(new ErrorResponse<string>(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<string>())).Returns(new OkResponse<string>("replaced"));
|
||||||
|
|
||||||
|
this.Given(_ => GivenHttpRequestWithoutHeaders())
|
||||||
|
.When(_ => WhenAddingHeader("X-Forwarded-For", "{RemoteIdAddress}"))
|
||||||
|
.Then(_ => ThenTheHeaderGetsTakenOverToTheRequestHeaders("replaced"))
|
||||||
|
.BDDfy();
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
@ -38,9 +71,23 @@ namespace Ocelot.UnitTests.Headers
|
|||||||
.BDDfy();
|
.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()
|
private void GivenHttpRequestWithoutHeaders()
|
||||||
{
|
{
|
||||||
_context = new DefaultHttpContext();
|
_context = new DefaultHttpContext
|
||||||
|
{
|
||||||
|
Request =
|
||||||
|
{
|
||||||
|
Headers =
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private void GivenHttpRequestWithHeader(string headerKey, string headerValue)
|
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.ShouldNotBeNull($"Value of header {_addedHeader.Key} was expected to not be null.");
|
||||||
value.ToString().ShouldBe(_addedHeader.Value);
|
value.ToString().ShouldBe(_addedHeader.Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void ThenTheHeaderGetsTakenOverToTheRequestHeaders(string expected)
|
||||||
|
{
|
||||||
|
var requestHeaders = _context.Request.Headers;
|
||||||
|
var value = requestHeaders[_addedHeader.Key];
|
||||||
|
value.ToString().ShouldBe(expected);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -109,7 +109,7 @@ namespace Ocelot.UnitTests.Headers
|
|||||||
|
|
||||||
private void ThenTheIHttpResponseHeaderReplacerIsCalledCorrectly()
|
private void ThenTheIHttpResponseHeaderReplacerIsCalledCorrectly()
|
||||||
{
|
{
|
||||||
_postReplacer.Verify(x => x.Replace(It.IsAny<DownstreamResponse>(), It.IsAny<List<HeaderFindAndReplace>>(), It.IsAny<DownstreamRequest>()), Times.Once);
|
_postReplacer.Verify(x => x.Replace(It.IsAny<DownstreamContext>(), It.IsAny<List<HeaderFindAndReplace>>()), Times.Once);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void GivenTheFollowingRequest()
|
private void GivenTheFollowingRequest()
|
||||||
|
@ -1,3 +1,10 @@
|
|||||||
|
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 Xunit;
|
||||||
using Shouldly;
|
using Shouldly;
|
||||||
using TestStack.BDDfy;
|
using TestStack.BDDfy;
|
||||||
@ -9,14 +16,7 @@ using Ocelot.Responses;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using Moq;
|
using Moq;
|
||||||
using Ocelot.Infrastructure;
|
|
||||||
using Ocelot.Middleware;
|
|
||||||
using Ocelot.Infrastructure.RequestData;
|
|
||||||
using Ocelot.Middleware.Multiplexer;
|
|
||||||
using Ocelot.Request.Middleware;
|
|
||||||
|
|
||||||
namespace Ocelot.UnitTests.Headers
|
|
||||||
{
|
|
||||||
public class HttpResponseHeaderReplacerTests
|
public class HttpResponseHeaderReplacerTests
|
||||||
{
|
{
|
||||||
private DownstreamResponse _response;
|
private DownstreamResponse _response;
|
||||||
@ -27,12 +27,14 @@ namespace Ocelot.UnitTests.Headers
|
|||||||
private DownstreamRequest _request;
|
private DownstreamRequest _request;
|
||||||
private Mock<IBaseUrlFinder> _finder;
|
private Mock<IBaseUrlFinder> _finder;
|
||||||
private Mock<IRequestScopedDataRepository> _repo;
|
private Mock<IRequestScopedDataRepository> _repo;
|
||||||
|
private Mock<IHttpContextAccessor> _accessor;
|
||||||
|
|
||||||
public HttpResponseHeaderReplacerTests()
|
public HttpResponseHeaderReplacerTests()
|
||||||
{
|
{
|
||||||
|
_accessor = new Mock<IHttpContextAccessor>();
|
||||||
_repo = new Mock<IRequestScopedDataRepository>();
|
_repo = new Mock<IRequestScopedDataRepository>();
|
||||||
_finder = new Mock<IBaseUrlFinder>();
|
_finder = new Mock<IBaseUrlFinder>();
|
||||||
_placeholders = new Placeholders(_finder.Object, _repo.Object);
|
_placeholders = new Placeholders(_finder.Object, _repo.Object, _accessor.Object);
|
||||||
_replacer = new HttpResponseHeaderReplacer(_placeholders);
|
_replacer = new HttpResponseHeaderReplacer(_placeholders);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -261,7 +263,8 @@ namespace Ocelot.UnitTests.Headers
|
|||||||
|
|
||||||
private void WhenICallTheReplacer()
|
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)
|
private void ThenTheHeaderShouldBe(string key, string value)
|
||||||
|
@ -1,4 +1,8 @@
|
|||||||
|
namespace Ocelot.UnitTests.Infrastructure
|
||||||
|
{
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
using System;
|
using System;
|
||||||
|
using System.Net;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
using Moq;
|
using Moq;
|
||||||
using Ocelot.Infrastructure;
|
using Ocelot.Infrastructure;
|
||||||
@ -9,19 +13,19 @@ using Ocelot.Responses;
|
|||||||
using Shouldly;
|
using Shouldly;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace Ocelot.UnitTests.Infrastructure
|
|
||||||
{
|
|
||||||
public class PlaceholdersTests
|
public class PlaceholdersTests
|
||||||
{
|
{
|
||||||
private IPlaceholders _placeholders;
|
private readonly IPlaceholders _placeholders;
|
||||||
private Mock<IBaseUrlFinder> _finder;
|
private readonly Mock<IBaseUrlFinder> _finder;
|
||||||
private Mock<IRequestScopedDataRepository> _repo;
|
private readonly Mock<IRequestScopedDataRepository> _repo;
|
||||||
|
private readonly Mock<IHttpContextAccessor> _accessor;
|
||||||
|
|
||||||
public PlaceholdersTests()
|
public PlaceholdersTests()
|
||||||
{
|
{
|
||||||
|
_accessor = new Mock<IHttpContextAccessor>();
|
||||||
_repo = new Mock<IRequestScopedDataRepository>();
|
_repo = new Mock<IRequestScopedDataRepository>();
|
||||||
_finder = new Mock<IBaseUrlFinder>();
|
_finder = new Mock<IBaseUrlFinder>();
|
||||||
_placeholders = new Placeholders(_finder.Object, _repo.Object);
|
_placeholders = new Placeholders(_finder.Object, _repo.Object, _accessor.Object);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
@ -33,6 +37,15 @@ namespace Ocelot.UnitTests.Infrastructure
|
|||||||
result.Data.ShouldBe(baseUrl);
|
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]
|
[Fact]
|
||||||
public void should_return_key_does_not_exist()
|
public void should_return_key_does_not_exist()
|
||||||
{
|
{
|
||||||
|
Loading…
x
Reference in New Issue
Block a user