Support adding custom plaintext headers to downstream requests (#314)

This commit is contained in:
Felix Boers
2018-04-14 07:41:12 +02:00
committed by Tom Pallister
parent b46ef1945d
commit fa09e4cf7a
14 changed files with 376 additions and 183 deletions

View File

@ -829,6 +829,7 @@ namespace Ocelot.UnitTests.Configuration
result.DownstreamReRoute[0].RequestIdKey.ShouldBe(expected.DownstreamReRoute[0].RequestIdKey);
result.DownstreamReRoute[0].DelegatingHandlers.ShouldBe(expected.DownstreamReRoute[0].DelegatingHandlers);
result.DownstreamReRoute[0].AddHeadersToDownstream.ShouldBe(expected.DownstreamReRoute[0].AddHeadersToDownstream);
result.DownstreamReRoute[0].AddHeadersToUpstream.ShouldBe(expected.DownstreamReRoute[0].AddHeadersToUpstream, "AddHeadersToUpstream should be set");
}
}
@ -911,7 +912,7 @@ namespace Ocelot.UnitTests.Configuration
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)

View File

@ -149,7 +149,6 @@ namespace Ocelot.UnitTests.Configuration
.Then(x => ThenTheFollowingDownstreamIsReturned(downstream))
.BDDfy();
}
[Fact]
public void should_add_trace_id_header()
{
@ -166,7 +165,45 @@ namespace Ocelot.UnitTests.Configuration
this.Given(x => GivenTheReRoute(reRoute))
.And(x => GivenTheBaseUrlIs("http://ocelot.com/"))
.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();
}
@ -180,11 +217,17 @@ namespace Ocelot.UnitTests.Configuration
_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].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)
{

View File

@ -1,151 +1,151 @@
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
using Moq;
using Ocelot.Configuration;
using Ocelot.Errors;
using Ocelot.Headers;
using Ocelot.Infrastructure.Claims.Parser;
using Ocelot.Responses;
using Shouldly;
using TestStack.BDDfy;
using Xunit;
using System.Net.Http;
using Ocelot.Request.Middleware;
namespace Ocelot.UnitTests.Headers
{
public class AddHeadersToRequestTests
{
private readonly AddHeadersToRequest _addHeadersToRequest;
private readonly Mock<IClaimsParser> _parser;
private readonly DownstreamRequest _downstreamRequest;
private List<Claim> _claims;
private List<ClaimToThing> _configuration;
private Response _result;
private Response<string> _claimValue;
public AddHeadersToRequestTests()
{
_parser = new Mock<IClaimsParser>();
_addHeadersToRequest = new AddHeadersToRequest(_parser.Object);
_downstreamRequest = new DownstreamRequest(new HttpRequestMessage(HttpMethod.Get, "http://test.com"));
}
[Fact]
public void should_add_headers_to_downstreamRequest()
{
var claims = new List<Claim>
{
new Claim("test", "data")
};
this.Given(
x => x.GivenConfigurationHeaderExtractorProperties(new List<ClaimToThing>
{
new ClaimToThing("header-key", "", "", 0)
}))
.Given(x => x.GivenClaims(claims))
.And(x => x.GivenTheClaimParserReturns(new OkResponse<string>("value")))
.When(x => x.WhenIAddHeadersToTheRequest())
.Then(x => x.ThenTheResultIsSuccess())
.And(x => x.ThenTheHeaderIsAdded())
.BDDfy();
}
[Fact]
public void should_replace_existing_headers_on_request()
{
this.Given(
x => x.GivenConfigurationHeaderExtractorProperties(new List<ClaimToThing>
{
new ClaimToThing("header-key", "", "", 0)
}))
.Given(x => x.GivenClaims(new List<Claim>
{
new Claim("test", "data")
}))
.And(x => x.GivenTheClaimParserReturns(new OkResponse<string>("value")))
.And(x => x.GivenThatTheRequestContainsHeader("header-key", "initial"))
.When(x => x.WhenIAddHeadersToTheRequest())
.Then(x => x.ThenTheResultIsSuccess())
.And(x => x.ThenTheHeaderIsAdded())
.BDDfy();
}
[Fact]
public void should_return_error()
{
this.Given(
x => x.GivenConfigurationHeaderExtractorProperties(new List<ClaimToThing>
{
new ClaimToThing("", "", "", 0)
}))
.Given(x => x.GivenClaims(new List<Claim>()))
.And(x => x.GivenTheClaimParserReturns(new ErrorResponse<string>(new List<Error>
{
new AnyError()
})))
.When(x => x.WhenIAddHeadersToTheRequest())
.Then(x => x.ThenTheResultIsError())
.BDDfy();
}
private void GivenClaims(List<Claim> claims)
{
_claims = claims;
}
private void GivenConfigurationHeaderExtractorProperties(List<ClaimToThing> configuration)
{
_configuration = configuration;
}
private void GivenThatTheRequestContainsHeader(string key, string value)
{
_downstreamRequest.Headers.Add(key, value);
}
private void GivenTheClaimParserReturns(Response<string> claimValue)
{
_claimValue = claimValue;
_parser
.Setup(
x =>
x.GetValue(It.IsAny<IEnumerable<Claim>>(),
It.IsAny<string>(),
It.IsAny<string>(),
It.IsAny<int>()))
.Returns(_claimValue);
}
private void WhenIAddHeadersToTheRequest()
{
_result = _addHeadersToRequest.SetHeadersOnDownstreamRequest(_configuration, _claims, _downstreamRequest);
}
private void ThenTheResultIsSuccess()
{
_result.IsError.ShouldBe(false);
}
private void ThenTheResultIsError()
{
_result.IsError.ShouldBe(true);
}
private void ThenTheHeaderIsAdded()
{
var header = _downstreamRequest.Headers.First(x => x.Key == "header-key");
header.Value.First().ShouldBe(_claimValue.Data);
}
class AnyError : Error
{
public AnyError()
: base("blahh", OcelotErrorCode.UnknownError)
{
}
}
}
}
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
using Moq;
using Ocelot.Configuration;
using Ocelot.Errors;
using Ocelot.Headers;
using Ocelot.Infrastructure.Claims.Parser;
using Ocelot.Responses;
using Shouldly;
using TestStack.BDDfy;
using Xunit;
using System.Net.Http;
using Ocelot.Request.Middleware;
namespace Ocelot.UnitTests.Headers
{
public class AddHeadersToRequestClaimToThingTests
{
private readonly AddHeadersToRequest _addHeadersToRequest;
private readonly Mock<IClaimsParser> _parser;
private readonly DownstreamRequest _downstreamRequest;
private List<Claim> _claims;
private List<ClaimToThing> _configuration;
private Response _result;
private Response<string> _claimValue;
public AddHeadersToRequestClaimToThingTests()
{
_parser = new Mock<IClaimsParser>();
_addHeadersToRequest = new AddHeadersToRequest(_parser.Object);
_downstreamRequest = new DownstreamRequest(new HttpRequestMessage(HttpMethod.Get, "http://test.com"));
}
[Fact]
public void should_add_headers_to_downstreamRequest()
{
var claims = new List<Claim>
{
new Claim("test", "data")
};
this.Given(
x => x.GivenConfigurationHeaderExtractorProperties(new List<ClaimToThing>
{
new ClaimToThing("header-key", "", "", 0)
}))
.Given(x => x.GivenClaims(claims))
.And(x => x.GivenTheClaimParserReturns(new OkResponse<string>("value")))
.When(x => x.WhenIAddHeadersToTheRequest())
.Then(x => x.ThenTheResultIsSuccess())
.And(x => x.ThenTheHeaderIsAdded())
.BDDfy();
}
[Fact]
public void should_replace_existing_headers_on_request()
{
this.Given(
x => x.GivenConfigurationHeaderExtractorProperties(new List<ClaimToThing>
{
new ClaimToThing("header-key", "", "", 0)
}))
.Given(x => x.GivenClaims(new List<Claim>
{
new Claim("test", "data")
}))
.And(x => x.GivenTheClaimParserReturns(new OkResponse<string>("value")))
.And(x => x.GivenThatTheRequestContainsHeader("header-key", "initial"))
.When(x => x.WhenIAddHeadersToTheRequest())
.Then(x => x.ThenTheResultIsSuccess())
.And(x => x.ThenTheHeaderIsAdded())
.BDDfy();
}
[Fact]
public void should_return_error()
{
this.Given(
x => x.GivenConfigurationHeaderExtractorProperties(new List<ClaimToThing>
{
new ClaimToThing("", "", "", 0)
}))
.Given(x => x.GivenClaims(new List<Claim>()))
.And(x => x.GivenTheClaimParserReturns(new ErrorResponse<string>(new List<Error>
{
new AnyError()
})))
.When(x => x.WhenIAddHeadersToTheRequest())
.Then(x => x.ThenTheResultIsError())
.BDDfy();
}
private void GivenClaims(List<Claim> claims)
{
_claims = claims;
}
private void GivenConfigurationHeaderExtractorProperties(List<ClaimToThing> configuration)
{
_configuration = configuration;
}
private void GivenThatTheRequestContainsHeader(string key, string value)
{
_downstreamRequest.Headers.Add(key, value);
}
private void GivenTheClaimParserReturns(Response<string> claimValue)
{
_claimValue = claimValue;
_parser
.Setup(
x =>
x.GetValue(It.IsAny<IEnumerable<Claim>>(),
It.IsAny<string>(),
It.IsAny<string>(),
It.IsAny<int>()))
.Returns(_claimValue);
}
private void WhenIAddHeadersToTheRequest()
{
_result = _addHeadersToRequest.SetHeadersOnDownstreamRequest(_configuration, _claims, _downstreamRequest);
}
private void ThenTheResultIsSuccess()
{
_result.IsError.ShouldBe(false);
}
private void ThenTheResultIsError()
{
_result.IsError.ShouldBe(true);
}
private void ThenTheHeaderIsAdded()
{
var header = _downstreamRequest.Headers.First(x => x.Key == "header-key");
header.Value.First().ShouldBe(_claimValue.Data);
}
class AnyError : Error
{
public AnyError()
: base("blahh", OcelotErrorCode.UnknownError)
{
}
}
}
}

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