#296 merged develop into this branch

This commit is contained in:
Tom Gardham-Pallister 2018-04-17 19:45:27 +01:00
commit b7ff73729f
16 changed files with 624 additions and 428 deletions

View File

@ -1,13 +1,29 @@
Headers Transformation Headers Transformation
===================== ======================
Ocelot allows the user to transform headers pre and post downstream request. At the moment Ocelot only supports find and replace. This feature was requested `GitHub #190 <https://github.com/TomPallister/Ocelot/issues/190>`_ and I decided that it was going to be useful in various ways. Ocelot allows the user to transform headers pre and post downstream request. At the moment Ocelot only supports find and replace. This feature was requested `GitHub #190 <https://github.com/TomPallister/Ocelot/issues/190>`_ and I decided that it was going to be useful in various ways.
Add to Request
^^^^^^^^^^^^^^
This feature was requestes in `GitHub #313 <https://github.com/ThreeMammals/Ocelot/issues/313>`_.
If you want to add a header to your upstream request please add the following to a ReRoute in your ocelot.json:
.. code-block:: json
"UpstreamHeaderTransform": {
"Uncle": "Bob"
}
In the example above a header with the key Uncle and value Bob would be send to to the upstream service.
Placeholders are supported too (see below).
Add to Response Add to Response
^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^
This feature was requested in `GitHub #280 <https://github.com/TomPallister/Ocelot/issues/280>`_. I have only implemented This feature was requested in `GitHub #280 <https://github.com/TomPallister/Ocelot/issues/280>`_.
for responses but could add for requests in the future.
If you want to add a header to your downstream response please add the following to a ReRoute in ocelot.json.. If you want to add a header to your downstream response please add the following to a ReRoute in ocelot.json..
@ -50,7 +66,7 @@ Add the following to a ReRoute in ocelot.json in order to replace http://www.bbc
}, },
Post Downstream Request Post Downstream Request
^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^
Add the following to a ReRoute in ocelot.json in order to replace http://www.bbc.co.uk/ with http://ocelot.com/. This transformation will take place after Ocelot has received the response from the downstream service. Add the following to a ReRoute in ocelot.json in order to replace http://www.bbc.co.uk/ with http://ocelot.com/. This transformation will take place after Ocelot has received the response from the downstream service.

View File

@ -39,12 +39,14 @@ namespace Ocelot.Configuration.Builder
private string _key; private string _key;
private List<string> _delegatingHandlers; private List<string> _delegatingHandlers;
private List<AddHeader> _addHeadersToDownstream; private List<AddHeader> _addHeadersToDownstream;
private List<AddHeader> _addHeadersToUpstream;
public DownstreamReRouteBuilder() public DownstreamReRouteBuilder()
{ {
_downstreamAddresses = new List<DownstreamHostAndPort>(); _downstreamAddresses = new List<DownstreamHostAndPort>();
_delegatingHandlers = new List<string>(); _delegatingHandlers = new List<string>();
_addHeadersToDownstream = new List<AddHeader>(); _addHeadersToDownstream = new List<AddHeader>();
_addHeadersToUpstream = new List<AddHeader>();
} }
public DownstreamReRouteBuilder WithDownstreamAddresses(List<DownstreamHostAndPort> downstreamAddresses) public DownstreamReRouteBuilder WithDownstreamAddresses(List<DownstreamHostAndPort> downstreamAddresses)
@ -233,6 +235,12 @@ namespace Ocelot.Configuration.Builder
return this; return this;
} }
public DownstreamReRouteBuilder WithAddHeadersToUpstream(List<AddHeader> addHeadersToUpstream)
{
_addHeadersToUpstream = addHeadersToUpstream;
return this;
}
public DownstreamReRoute Build() public DownstreamReRoute Build()
{ {
return new DownstreamReRoute( return new DownstreamReRoute(
@ -263,7 +271,8 @@ namespace Ocelot.Configuration.Builder
new PathTemplate(_downstreamPathTemplate), new PathTemplate(_downstreamPathTemplate),
_reRouteKey, _reRouteKey,
_delegatingHandlers, _delegatingHandlers,
_addHeadersToDownstream); _addHeadersToDownstream,
_addHeadersToUpstream);
} }
} }
} }

View File

@ -212,6 +212,7 @@ namespace Ocelot.Configuration.Creator
.WithUpstreamHost(fileReRoute.UpstreamHost) .WithUpstreamHost(fileReRoute.UpstreamHost)
.WithDelegatingHandlers(fileReRoute.DelegatingHandlers) .WithDelegatingHandlers(fileReRoute.DelegatingHandlers)
.WithAddHeadersToDownstream(hAndRs.AddHeadersToDownstream) .WithAddHeadersToDownstream(hAndRs.AddHeadersToDownstream)
.WithAddHeadersToUpstream(hAndRs.AddHeadersToUpstream)
.Build(); .Build();
return reRoute; return reRoute;

View File

@ -22,17 +22,25 @@ namespace Ocelot.Configuration.Creator
public HeaderTransformations Create(FileReRoute fileReRoute) public HeaderTransformations Create(FileReRoute fileReRoute)
{ {
var upstream = new List<HeaderFindAndReplace>(); var upstream = new List<HeaderFindAndReplace>();
var addHeadersToUpstream = new List<AddHeader>();
foreach(var input in fileReRoute.UpstreamHeaderTransform) foreach(var input in fileReRoute.UpstreamHeaderTransform)
{ {
var hAndr = Map(input); if (input.Value.Contains(","))
if(!hAndr.IsError)
{ {
upstream.Add(hAndr.Data); var hAndr = Map(input);
if (!hAndr.IsError)
{
upstream.Add(hAndr.Data);
}
else
{
_logger.LogWarning($"Unable to add UpstreamHeaderTransform {input.Key}: {input.Value}");
}
} }
else else
{ {
_logger.LogWarning($"Unable to add UpstreamHeaderTransform {input.Key}: {input.Value}"); addHeadersToUpstream.Add(new AddHeader(input.Key, input.Value));
} }
} }
@ -59,7 +67,7 @@ namespace Ocelot.Configuration.Creator
} }
} }
return new HeaderTransformations(upstream, downstream, addHeadersToDownstream); return new HeaderTransformations(upstream, downstream, addHeadersToDownstream, addHeadersToUpstream);
} }
private Response<HeaderFindAndReplace> Map(KeyValuePair<string,string> input) private Response<HeaderFindAndReplace> Map(KeyValuePair<string,string> input)

View File

@ -7,9 +7,11 @@ namespace Ocelot.Configuration.Creator
public HeaderTransformations( public HeaderTransformations(
List<HeaderFindAndReplace> upstream, List<HeaderFindAndReplace> upstream,
List<HeaderFindAndReplace> downstream, List<HeaderFindAndReplace> downstream,
List<AddHeader> addHeader) List<AddHeader> addHeaderToDownstream,
List<AddHeader> addHeaderToUpstream)
{ {
AddHeadersToDownstream = addHeader; AddHeadersToDownstream = addHeaderToDownstream;
AddHeadersToUpstream = addHeaderToUpstream;
Upstream = upstream; Upstream = upstream;
Downstream = downstream; Downstream = downstream;
} }
@ -19,5 +21,6 @@ namespace Ocelot.Configuration.Creator
public List<HeaderFindAndReplace> Downstream { get; } public List<HeaderFindAndReplace> Downstream { get; }
public List<AddHeader> AddHeadersToDownstream { get; } public List<AddHeader> AddHeadersToDownstream { get; }
public List<AddHeader> AddHeadersToUpstream { get; }
} }
} }

View File

@ -34,7 +34,8 @@ namespace Ocelot.Configuration
PathTemplate downstreamPathTemplate, PathTemplate downstreamPathTemplate,
string reRouteKey, string reRouteKey,
List<string> delegatingHandlers, List<string> delegatingHandlers,
List<AddHeader> addHeadersToDownstream) List<AddHeader> addHeadersToDownstream,
List<AddHeader> addHeadersToUpstream)
{ {
AddHeadersToDownstream = addHeadersToDownstream; AddHeadersToDownstream = addHeadersToDownstream;
DelegatingHandlers = delegatingHandlers; DelegatingHandlers = delegatingHandlers;
@ -64,6 +65,7 @@ namespace Ocelot.Configuration
AuthenticationOptions = authenticationOptions; AuthenticationOptions = authenticationOptions;
DownstreamPathTemplate = downstreamPathTemplate; DownstreamPathTemplate = downstreamPathTemplate;
ReRouteKey = reRouteKey; ReRouteKey = reRouteKey;
AddHeadersToUpstream = addHeadersToUpstream;
} }
public string Key { get; private set; } public string Key { get; private set; }
@ -94,5 +96,6 @@ namespace Ocelot.Configuration
public string ReRouteKey { get; private set; } public string ReRouteKey { get; private set; }
public List<string> DelegatingHandlers {get;private set;} public List<string> DelegatingHandlers {get;private set;}
public List<AddHeader> AddHeadersToDownstream {get;private set;} public List<AddHeader> AddHeadersToDownstream {get;private set;}
public List<AddHeader> AddHeadersToUpstream { get; private set; }
} }
} }

View File

@ -4,6 +4,7 @@ using Ocelot.Configuration;
using Ocelot.Infrastructure.Claims.Parser; using Ocelot.Infrastructure.Claims.Parser;
using Ocelot.Responses; using Ocelot.Responses;
using System.Net.Http; using System.Net.Http;
using Microsoft.AspNetCore.Http;
using Ocelot.Configuration.Creator; using Ocelot.Configuration.Creator;
using Ocelot.Request.Middleware; using Ocelot.Request.Middleware;
@ -41,5 +42,19 @@ namespace Ocelot.Headers
return new OkResponse(); return new OkResponse();
} }
public void SetHeadersOnDownstreamRequest(IEnumerable<AddHeader> headers, HttpContext context)
{
var requestHeader = context.Request.Headers;
foreach (var header in headers)
{
if (requestHeader.ContainsKey(header.Key))
{
requestHeader.Remove(header.Key);
}
requestHeader.Add(header.Key, header.Value);
}
}
} }
} }

View File

@ -1,4 +1,6 @@
namespace Ocelot.Headers using Microsoft.AspNetCore.Http;
namespace Ocelot.Headers
{ {
using System.Collections.Generic; using System.Collections.Generic;
using System.Net.Http; using System.Net.Http;
@ -12,5 +14,6 @@
public interface IAddHeadersToRequest public interface IAddHeadersToRequest
{ {
Response SetHeadersOnDownstreamRequest(List<ClaimToThing> claimsToThings, IEnumerable<System.Security.Claims.Claim> claims, DownstreamRequest downstreamRequest); Response SetHeadersOnDownstreamRequest(List<ClaimToThing> claimsToThings, IEnumerable<System.Security.Claims.Claim> claims, DownstreamRequest downstreamRequest);
void SetHeadersOnDownstreamRequest(IEnumerable<AddHeader> headers, HttpContext context);
} }
} }

View File

@ -9,16 +9,19 @@ namespace Ocelot.Headers.Middleware
private readonly OcelotRequestDelegate _next; private readonly OcelotRequestDelegate _next;
private readonly IHttpContextRequestHeaderReplacer _preReplacer; private readonly IHttpContextRequestHeaderReplacer _preReplacer;
private readonly IHttpResponseHeaderReplacer _postReplacer; private readonly IHttpResponseHeaderReplacer _postReplacer;
private readonly IAddHeadersToResponse _addHeaders; private readonly IAddHeadersToResponse _addHeadersToResponse;
private readonly IAddHeadersToRequest _addHeadersToRequest;
public HttpHeadersTransformationMiddleware(OcelotRequestDelegate next, public HttpHeadersTransformationMiddleware(OcelotRequestDelegate next,
IOcelotLoggerFactory loggerFactory, IOcelotLoggerFactory loggerFactory,
IHttpContextRequestHeaderReplacer preReplacer, IHttpContextRequestHeaderReplacer preReplacer,
IHttpResponseHeaderReplacer postReplacer, IHttpResponseHeaderReplacer postReplacer,
IAddHeadersToResponse addHeaders) IAddHeadersToResponse addHeadersToResponse,
IAddHeadersToRequest addHeadersToRequest)
:base(loggerFactory.CreateLogger<HttpHeadersTransformationMiddleware>()) :base(loggerFactory.CreateLogger<HttpHeadersTransformationMiddleware>())
{ {
_addHeaders = addHeaders; _addHeadersToResponse = addHeadersToResponse;
_addHeadersToRequest = addHeadersToRequest;
_next = next; _next = next;
_postReplacer = postReplacer; _postReplacer = postReplacer;
_preReplacer = preReplacer; _preReplacer = preReplacer;
@ -31,13 +34,15 @@ namespace Ocelot.Headers.Middleware
//todo - this should be on httprequestmessage not httpcontext? //todo - this should be on httprequestmessage not httpcontext?
_preReplacer.Replace(context.HttpContext, preFAndRs); _preReplacer.Replace(context.HttpContext, preFAndRs);
_addHeadersToRequest.SetHeadersOnDownstreamRequest(context.DownstreamReRoute.AddHeadersToUpstream, context.HttpContext);
await _next.Invoke(context); await _next.Invoke(context);
var postFAndRs = context.DownstreamReRoute.DownstreamHeadersFindAndReplace; var postFAndRs = context.DownstreamReRoute.DownstreamHeadersFindAndReplace;
_postReplacer.Replace(context.DownstreamResponse, postFAndRs, context.DownstreamRequest); _postReplacer.Replace(context.DownstreamResponse, postFAndRs, context.DownstreamRequest);
_addHeaders.Add(context.DownstreamReRoute.AddHeadersToDownstream, context.DownstreamResponse); _addHeadersToResponse.Add(context.DownstreamReRoute.AddHeadersToDownstream, context.DownstreamResponse);
} }
} }
} }

View File

@ -819,6 +819,7 @@
result.DownstreamReRoute[0].RequestIdKey.ShouldBe(expected.DownstreamReRoute[0].RequestIdKey); result.DownstreamReRoute[0].RequestIdKey.ShouldBe(expected.DownstreamReRoute[0].RequestIdKey);
result.DownstreamReRoute[0].DelegatingHandlers.ShouldBe(expected.DownstreamReRoute[0].DelegatingHandlers); result.DownstreamReRoute[0].DelegatingHandlers.ShouldBe(expected.DownstreamReRoute[0].DelegatingHandlers);
result.DownstreamReRoute[0].AddHeadersToDownstream.ShouldBe(expected.DownstreamReRoute[0].AddHeadersToDownstream); result.DownstreamReRoute[0].AddHeadersToDownstream.ShouldBe(expected.DownstreamReRoute[0].AddHeadersToDownstream);
result.DownstreamReRoute[0].AddHeadersToUpstream.ShouldBe(expected.DownstreamReRoute[0].AddHeadersToUpstream, "AddHeadersToUpstream should be set");
} }
} }
@ -901,7 +902,7 @@
private void GivenTheHeaderFindAndReplaceCreatorReturns() private void GivenTheHeaderFindAndReplaceCreatorReturns()
{ {
_headerFindAndReplaceCreator.Setup(x => x.Create(It.IsAny<FileReRoute>())).Returns(new HeaderTransformations(new List<HeaderFindAndReplace>(), new List<HeaderFindAndReplace>(), new List<AddHeader>())); _headerFindAndReplaceCreator.Setup(x => x.Create(It.IsAny<FileReRoute>())).Returns(new HeaderTransformations(new List<HeaderFindAndReplace>(), new List<HeaderFindAndReplace>(), new List<AddHeader>(), new List<AddHeader>()));
} }
private void GivenTheFollowingIsReturned(ServiceProviderConfiguration serviceProviderConfiguration) private void GivenTheFollowingIsReturned(ServiceProviderConfiguration serviceProviderConfiguration)

View File

@ -12,13 +12,17 @@ using Ocelot.Configuration.Repository;
namespace Ocelot.UnitTests.Configuration namespace Ocelot.UnitTests.Configuration
{ {
public class FileConfigurationRepositoryTests public class FileConfigurationRepositoryTests : IDisposable
{ {
private readonly Mock<IHostingEnvironment> _hostingEnvironment = new Mock<IHostingEnvironment>(); private readonly Mock<IHostingEnvironment> _hostingEnvironment = new Mock<IHostingEnvironment>();
private IFileConfigurationRepository _repo; private IFileConfigurationRepository _repo;
private FileConfiguration _result; private FileConfiguration _result;
private FileConfiguration _fileConfiguration; private FileConfiguration _fileConfiguration;
private string _environmentName = "DEV";
// This is a bit dirty and it is dev.dev so that the configuration tests
// cant pick it up if they run in parralel..sigh these are not really unit
// tests but whatever...
private string _environmentName = "DEV.DEV";
public FileConfigurationRepositoryTests() public FileConfigurationRepositoryTests()
{ {
@ -221,5 +225,10 @@ namespace Ocelot.UnitTests.Configuration
ReRoutes = reRoutes ReRoutes = reRoutes
}; };
} }
public void Dispose()
{
File.Delete($"./ocelot.{_environmentName}.json");
}
} }
} }

View File

@ -149,7 +149,6 @@ namespace Ocelot.UnitTests.Configuration
.Then(x => ThenTheFollowingDownstreamIsReturned(downstream)) .Then(x => ThenTheFollowingDownstreamIsReturned(downstream))
.BDDfy(); .BDDfy();
} }
[Fact] [Fact]
public void should_add_trace_id_header() public void should_add_trace_id_header()
{ {
@ -166,7 +165,45 @@ namespace Ocelot.UnitTests.Configuration
this.Given(x => GivenTheReRoute(reRoute)) this.Given(x => GivenTheReRoute(reRoute))
.And(x => GivenTheBaseUrlIs("http://ocelot.com/")) .And(x => GivenTheBaseUrlIs("http://ocelot.com/"))
.When(x => WhenICreate()) .When(x => WhenICreate())
.Then(x => ThenTheFollowingAddHeaderIsReturned(expected)) .Then(x => ThenTheFollowingAddHeaderToDownstreamIsReturned(expected))
.BDDfy();
}
[Fact]
public void should_add_downstream_header_as_is_when_no_replacement_is_given()
{
var reRoute = new FileReRoute
{
DownstreamHeaderTransform = new Dictionary<string, string>
{
{"X-Custom-Header", "Value"},
}
};
var expected = new AddHeader("X-Custom-Header", "Value");
this.Given(x => GivenTheReRoute(reRoute))
.And(x => WhenICreate())
.Then(x => x.ThenTheFollowingAddHeaderToDownstreamIsReturned(expected))
.BDDfy();
}
[Fact]
public void should_add_upstream_header_as_is_when_no_replacement_is_given()
{
var reRoute = new FileReRoute
{
UpstreamHeaderTransform = new Dictionary<string, string>
{
{"X-Custom-Header", "Value"},
}
};
var expected = new AddHeader("X-Custom-Header", "Value");
this.Given(x => GivenTheReRoute(reRoute))
.And(x => WhenICreate())
.Then(x => x.ThenTheFollowingAddHeaderToUpstreamIsReturned(expected))
.BDDfy(); .BDDfy();
} }
@ -180,12 +217,18 @@ namespace Ocelot.UnitTests.Configuration
_placeholders.Setup(x => x.Get(It.IsAny<string>())).Returns(new ErrorResponse<string>(new AnyError())); _placeholders.Setup(x => x.Get(It.IsAny<string>())).Returns(new ErrorResponse<string>(new AnyError()));
} }
private void ThenTheFollowingAddHeaderIsReturned(AddHeader addHeader) private void ThenTheFollowingAddHeaderToDownstreamIsReturned(AddHeader addHeader)
{ {
_result.AddHeadersToDownstream[0].Key.ShouldBe(addHeader.Key); _result.AddHeadersToDownstream[0].Key.ShouldBe(addHeader.Key);
_result.AddHeadersToDownstream[0].Value.ShouldBe(addHeader.Value); _result.AddHeadersToDownstream[0].Value.ShouldBe(addHeader.Value);
} }
private void ThenTheFollowingAddHeaderToUpstreamIsReturned(AddHeader addHeader)
{
_result.AddHeadersToUpstream[0].Key.ShouldBe(addHeader.Key);
_result.AddHeadersToUpstream[0].Value.ShouldBe(addHeader.Value);
}
private void ThenTheFollowingDownstreamIsReturned(List<HeaderFindAndReplace> downstream) private void ThenTheFollowingDownstreamIsReturned(List<HeaderFindAndReplace> downstream)
{ {
_result.Downstream.Count.ShouldBe(downstream.Count); _result.Downstream.Count.ShouldBe(downstream.Count);

View File

@ -6,6 +6,7 @@ using Xunit;
namespace Ocelot.UnitTests.DependencyInjection namespace Ocelot.UnitTests.DependencyInjection
{ {
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using Newtonsoft.Json; using Newtonsoft.Json;
@ -17,7 +18,7 @@ namespace Ocelot.UnitTests.DependencyInjection
private string _result; private string _result;
private IConfigurationRoot _configRoot; private IConfigurationRoot _configRoot;
private FileConfiguration _globalConfig; private FileConfiguration _globalConfig;
private FileConfiguration _reRoute; private FileConfiguration _reRouteA;
private FileConfiguration _reRouteB; private FileConfiguration _reRouteB;
private FileConfiguration _aggregate; private FileConfiguration _aggregate;
@ -64,7 +65,7 @@ namespace Ocelot.UnitTests.DependencyInjection
} }
}; };
_reRoute = new FileConfiguration _reRouteA = new FileConfiguration
{ {
ReRoutes = new List<FileReRoute> ReRoutes = new List<FileReRoute>
{ {
@ -160,17 +161,10 @@ namespace Ocelot.UnitTests.DependencyInjection
} }
}; };
var globalJson = JsonConvert.SerializeObject(_globalConfig); File.WriteAllText("ocelot.global.json", JsonConvert.SerializeObject(_globalConfig));
File.WriteAllText("ocelot.global.json", globalJson); File.WriteAllText("ocelot.reRoutesA.json", JsonConvert.SerializeObject(_reRouteA));
File.WriteAllText("ocelot.reRoutesB.json", JsonConvert.SerializeObject(_reRouteB));
var reRouteJson = JsonConvert.SerializeObject(_reRoute); File.WriteAllText("ocelot.aggregates.json", JsonConvert.SerializeObject(_aggregate));
File.WriteAllText("ocelot.reRoutes.json", reRouteJson);
var reRouteJsonB = JsonConvert.SerializeObject(_reRouteB);
File.WriteAllText("ocelot.reRoutesB.json", reRouteJsonB);
var aggregates = JsonConvert.SerializeObject(_aggregate);
File.WriteAllText("ocelot.aggregates.json", aggregates);
} }
private void WhenIAddOcelotConfiguration() private void WhenIAddOcelotConfiguration()
@ -195,21 +189,21 @@ namespace Ocelot.UnitTests.DependencyInjection
fc.GlobalConfiguration.ServiceDiscoveryProvider.Port.ShouldBe(_globalConfig.GlobalConfiguration.ServiceDiscoveryProvider.Port); fc.GlobalConfiguration.ServiceDiscoveryProvider.Port.ShouldBe(_globalConfig.GlobalConfiguration.ServiceDiscoveryProvider.Port);
fc.GlobalConfiguration.ServiceDiscoveryProvider.Type.ShouldBe(_globalConfig.GlobalConfiguration.ServiceDiscoveryProvider.Type); fc.GlobalConfiguration.ServiceDiscoveryProvider.Type.ShouldBe(_globalConfig.GlobalConfiguration.ServiceDiscoveryProvider.Type);
fc.ReRoutes.Count.ShouldBe(_reRoute.ReRoutes.Count + _reRouteB.ReRoutes.Count); fc.ReRoutes.Count.ShouldBe(_reRouteA.ReRoutes.Count + _reRouteB.ReRoutes.Count);
fc.ReRoutes.ShouldContain(x => x.DownstreamPathTemplate == _reRoute.ReRoutes[0].DownstreamPathTemplate); fc.ReRoutes.ShouldContain(x => x.DownstreamPathTemplate == _reRouteA.ReRoutes[0].DownstreamPathTemplate);
fc.ReRoutes.ShouldContain(x => x.DownstreamPathTemplate == _reRouteB.ReRoutes[0].DownstreamPathTemplate); fc.ReRoutes.ShouldContain(x => x.DownstreamPathTemplate == _reRouteB.ReRoutes[0].DownstreamPathTemplate);
fc.ReRoutes.ShouldContain(x => x.DownstreamPathTemplate == _reRouteB.ReRoutes[1].DownstreamPathTemplate); fc.ReRoutes.ShouldContain(x => x.DownstreamPathTemplate == _reRouteB.ReRoutes[1].DownstreamPathTemplate);
fc.ReRoutes.ShouldContain(x => x.DownstreamScheme == _reRoute.ReRoutes[0].DownstreamScheme); fc.ReRoutes.ShouldContain(x => x.DownstreamScheme == _reRouteA.ReRoutes[0].DownstreamScheme);
fc.ReRoutes.ShouldContain(x => x.DownstreamScheme == _reRouteB.ReRoutes[0].DownstreamScheme); fc.ReRoutes.ShouldContain(x => x.DownstreamScheme == _reRouteB.ReRoutes[0].DownstreamScheme);
fc.ReRoutes.ShouldContain(x => x.DownstreamScheme == _reRouteB.ReRoutes[1].DownstreamScheme); fc.ReRoutes.ShouldContain(x => x.DownstreamScheme == _reRouteB.ReRoutes[1].DownstreamScheme);
fc.ReRoutes.ShouldContain(x => x.Key == _reRoute.ReRoutes[0].Key); fc.ReRoutes.ShouldContain(x => x.Key == _reRouteA.ReRoutes[0].Key);
fc.ReRoutes.ShouldContain(x => x.Key == _reRouteB.ReRoutes[0].Key); fc.ReRoutes.ShouldContain(x => x.Key == _reRouteB.ReRoutes[0].Key);
fc.ReRoutes.ShouldContain(x => x.Key == _reRouteB.ReRoutes[1].Key); fc.ReRoutes.ShouldContain(x => x.Key == _reRouteB.ReRoutes[1].Key);
fc.ReRoutes.ShouldContain(x => x.UpstreamHost == _reRoute.ReRoutes[0].UpstreamHost); fc.ReRoutes.ShouldContain(x => x.UpstreamHost == _reRouteA.ReRoutes[0].UpstreamHost);
fc.ReRoutes.ShouldContain(x => x.UpstreamHost == _reRouteB.ReRoutes[0].UpstreamHost); fc.ReRoutes.ShouldContain(x => x.UpstreamHost == _reRouteB.ReRoutes[0].UpstreamHost);
fc.ReRoutes.ShouldContain(x => x.UpstreamHost == _reRouteB.ReRoutes[1].UpstreamHost); fc.ReRoutes.ShouldContain(x => x.UpstreamHost == _reRouteB.ReRoutes[1].UpstreamHost);

View File

@ -15,7 +15,7 @@ using Ocelot.Request.Middleware;
namespace Ocelot.UnitTests.Headers namespace Ocelot.UnitTests.Headers
{ {
public class AddHeadersToRequestTests public class AddHeadersToRequestClaimToThingTests
{ {
private readonly AddHeadersToRequest _addHeadersToRequest; private readonly AddHeadersToRequest _addHeadersToRequest;
private readonly Mock<IClaimsParser> _parser; private readonly Mock<IClaimsParser> _parser;
@ -25,7 +25,7 @@ namespace Ocelot.UnitTests.Headers
private Response _result; private Response _result;
private Response<string> _claimValue; private Response<string> _claimValue;
public AddHeadersToRequestTests() public AddHeadersToRequestClaimToThingTests()
{ {
_parser = new Mock<IClaimsParser>(); _parser = new Mock<IClaimsParser>();
_addHeadersToRequest = new AddHeadersToRequest(_parser.Object); _addHeadersToRequest = new AddHeadersToRequest(_parser.Object);

View File

@ -0,0 +1,75 @@
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
{
public class AddHeadersToRequestPlainTests
{
private readonly AddHeadersToRequest _addHeadersToRequest;
private HttpContext _context;
private AddHeader _addedHeader;
public AddHeadersToRequestPlainTests()
{
_addHeadersToRequest = new AddHeadersToRequest(Mock.Of<IClaimsParser>());
}
[Fact]
public void should_add_plain_text_header_to_downstream_request()
{
this.Given(_ => GivenHttpRequestWithoutHeaders())
.When(_ => WhenAddingHeader("X-Custom-Header", "PlainValue"))
.Then(_ => ThenTheHeaderGetsTakenOverToTheRequestHeaders())
.BDDfy();
}
[Fact]
public void should_overwrite_existing_header_with_added_header()
{
this.Given(_ => GivenHttpRequestWithHeader("X-Custom-Header", "This should get overwritten"))
.When(_ => WhenAddingHeader("X-Custom-Header", "PlainValue"))
.Then(_ => ThenTheHeaderGetsTakenOverToTheRequestHeaders())
.BDDfy();
}
private void GivenHttpRequestWithoutHeaders()
{
_context = new DefaultHttpContext();
}
private void GivenHttpRequestWithHeader(string headerKey, string headerValue)
{
_context = new DefaultHttpContext
{
Request =
{
Headers =
{
{ headerKey, headerValue }
}
}
};
}
private void WhenAddingHeader(string headerKey, string headerValue)
{
_addedHeader = new AddHeader(headerKey, headerValue);
_addHeadersToRequest.SetHeadersOnDownstreamRequest(new[] { _addedHeader }, _context);
}
private void ThenTheHeaderGetsTakenOverToTheRequestHeaders()
{
var requestHeaders = _context.Request.Headers;
requestHeaders.ContainsKey(_addedHeader.Key).ShouldBeTrue($"Header {_addedHeader.Key} was expected but not there.");
var value = requestHeaders[_addedHeader.Key];
value.ShouldNotBeNull($"Value of header {_addedHeader.Key} was expected to not be null.");
value.ToString().ShouldBe(_addedHeader.Value);
}
}
}

View File

@ -27,7 +27,8 @@ namespace Ocelot.UnitTests.Headers
private readonly HttpHeadersTransformationMiddleware _middleware; private readonly HttpHeadersTransformationMiddleware _middleware;
private readonly DownstreamContext _downstreamContext; private readonly DownstreamContext _downstreamContext;
private OcelotRequestDelegate _next; private OcelotRequestDelegate _next;
private readonly Mock<IAddHeadersToResponse> _addHeaders; private readonly Mock<IAddHeadersToResponse> _addHeadersToResponse;
private readonly Mock<IAddHeadersToRequest> _addHeadersToRequest;
public HttpHeadersTransformationMiddlewareTests() public HttpHeadersTransformationMiddlewareTests()
{ {
@ -38,8 +39,11 @@ namespace Ocelot.UnitTests.Headers
_logger = new Mock<IOcelotLogger>(); _logger = new Mock<IOcelotLogger>();
_loggerFactory.Setup(x => x.CreateLogger<AuthorisationMiddleware>()).Returns(_logger.Object); _loggerFactory.Setup(x => x.CreateLogger<AuthorisationMiddleware>()).Returns(_logger.Object);
_next = context => Task.CompletedTask; _next = context => Task.CompletedTask;
_addHeaders = new Mock<IAddHeadersToResponse>(); _addHeadersToResponse = new Mock<IAddHeadersToResponse>();
_middleware = new HttpHeadersTransformationMiddleware(_next, _loggerFactory.Object, _preReplacer.Object, _postReplacer.Object, _addHeaders.Object); _addHeadersToRequest = new Mock<IAddHeadersToRequest>();
_middleware = new HttpHeadersTransformationMiddleware(
_next, _loggerFactory.Object, _preReplacer.Object,
_postReplacer.Object, _addHeadersToResponse.Object, _addHeadersToRequest.Object);
} }
[Fact] [Fact]
@ -51,17 +55,24 @@ namespace Ocelot.UnitTests.Headers
.And(x => GivenTheHttpResponseMessageIs()) .And(x => GivenTheHttpResponseMessageIs())
.When(x => WhenICallTheMiddleware()) .When(x => WhenICallTheMiddleware())
.Then(x => ThenTheIHttpContextRequestHeaderReplacerIsCalledCorrectly()) .Then(x => ThenTheIHttpContextRequestHeaderReplacerIsCalledCorrectly())
.Then(x => ThenAddHeadersToRequestIsCalledCorrectly())
.And(x => ThenTheIHttpResponseHeaderReplacerIsCalledCorrectly()) .And(x => ThenTheIHttpResponseHeaderReplacerIsCalledCorrectly())
.And(x => ThenAddHeadersIsCalledCorrectly()) .And(x => ThenAddHeadersToResponseIsCalledCorrectly())
.BDDfy(); .BDDfy();
} }
private void ThenAddHeadersIsCalledCorrectly() private void ThenAddHeadersToResponseIsCalledCorrectly()
{ {
_addHeaders _addHeadersToResponse
.Verify(x => x.Add(_downstreamContext.DownstreamReRoute.AddHeadersToDownstream, _downstreamContext.DownstreamResponse), Times.Once); .Verify(x => x.Add(_downstreamContext.DownstreamReRoute.AddHeadersToDownstream, _downstreamContext.DownstreamResponse), Times.Once);
} }
private void ThenAddHeadersToRequestIsCalledCorrectly()
{
_addHeadersToRequest
.Verify(x => x.SetHeadersOnDownstreamRequest(_downstreamContext.DownstreamReRoute.AddHeadersToUpstream, _downstreamContext.HttpContext), Times.Once);
}
private void WhenICallTheMiddleware() private void WhenICallTheMiddleware()
{ {
_middleware.Invoke(_downstreamContext).GetAwaiter().GetResult(); _middleware.Invoke(_downstreamContext).GetAwaiter().GetResult();