mirror of
				https://github.com/nsnail/Ocelot.git
				synced 2025-11-04 21:10:49 +08:00 
			
		
		
		
	Feature/transform headers (#204)
* New feature that lets a user do find and replace on an upstream header * can transform downstream and upstream headers, not sure if interface is good * can replace location header with placeholder * added some syntax
This commit is contained in:
		@@ -39,6 +39,7 @@ namespace Ocelot.UnitTests.Configuration
 | 
			
		||||
        private Mock<IRegionCreator> _regionCreator;
 | 
			
		||||
        private Mock<IHttpHandlerOptionsCreator> _httpHandlerOptionsCreator;
 | 
			
		||||
        private Mock<IAdministrationPath> _adminPath;
 | 
			
		||||
        private readonly Mock<IHeaderFindAndReplaceCreator> _headerFindAndReplaceCreator;
 | 
			
		||||
 | 
			
		||||
        public FileConfigurationCreatorTests()
 | 
			
		||||
        {
 | 
			
		||||
@@ -56,6 +57,7 @@ namespace Ocelot.UnitTests.Configuration
 | 
			
		||||
            _regionCreator = new Mock<IRegionCreator>();
 | 
			
		||||
            _httpHandlerOptionsCreator = new Mock<IHttpHandlerOptionsCreator>();
 | 
			
		||||
            _adminPath = new Mock<IAdministrationPath>();
 | 
			
		||||
            _headerFindAndReplaceCreator = new Mock<IHeaderFindAndReplaceCreator>();
 | 
			
		||||
 | 
			
		||||
            _ocelotConfigurationCreator = new FileOcelotConfigurationCreator( 
 | 
			
		||||
                _fileConfig.Object,
 | 
			
		||||
@@ -71,7 +73,8 @@ namespace Ocelot.UnitTests.Configuration
 | 
			
		||||
                _rateLimitOptions.Object,
 | 
			
		||||
                _regionCreator.Object,
 | 
			
		||||
                _httpHandlerOptionsCreator.Object,
 | 
			
		||||
                _adminPath.Object);
 | 
			
		||||
                _adminPath.Object,
 | 
			
		||||
                _headerFindAndReplaceCreator.Object);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        [Fact]
 | 
			
		||||
@@ -91,6 +94,7 @@ namespace Ocelot.UnitTests.Configuration
 | 
			
		||||
                }
 | 
			
		||||
            }))
 | 
			
		||||
                .And(x => x.GivenTheFollowingIsReturned(serviceProviderConfig))
 | 
			
		||||
                .And(x => GivenTheHeaderFindAndReplaceCreatorReturns())
 | 
			
		||||
                .And(x => x.GivenTheConfigIsValid())
 | 
			
		||||
                .When(x => x.WhenICreateTheConfig())
 | 
			
		||||
                .Then(x => x.ThenTheServiceProviderCreatorIsCalledCorrectly())
 | 
			
		||||
@@ -121,10 +125,12 @@ namespace Ocelot.UnitTests.Configuration
 | 
			
		||||
                            },
 | 
			
		||||
            }))
 | 
			
		||||
                .And(x => x.GivenTheFollowingOptionsAreReturned(reRouteOptions))
 | 
			
		||||
                .And(x => GivenTheHeaderFindAndReplaceCreatorReturns())
 | 
			
		||||
                .And(x => x.GivenTheConfigIsValid())
 | 
			
		||||
                .And(x => x.GivenTheFollowingRegionIsReturned("region"))
 | 
			
		||||
                .When(x => x.WhenICreateTheConfig())
 | 
			
		||||
                .Then(x => x.ThenTheRegionCreatorIsCalledCorrectly("region"))
 | 
			
		||||
                .And(x => x.ThenTheHeaderFindAndReplaceCreatorIsCalledCorrectly())
 | 
			
		||||
                .BDDfy();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@@ -148,6 +154,7 @@ namespace Ocelot.UnitTests.Configuration
 | 
			
		||||
                            },
 | 
			
		||||
            }))
 | 
			
		||||
                .And(x => x.GivenTheConfigIsValid())
 | 
			
		||||
                .And(x => GivenTheHeaderFindAndReplaceCreatorReturns())
 | 
			
		||||
                .And(x => x.GivenTheFollowingOptionsAreReturned(reRouteOptions))
 | 
			
		||||
                .When(x => x.WhenICreateTheConfig())
 | 
			
		||||
                .Then(x => x.ThenTheRateLimitOptionsCreatorIsCalledCorrectly())
 | 
			
		||||
@@ -187,6 +194,7 @@ namespace Ocelot.UnitTests.Configuration
 | 
			
		||||
                },
 | 
			
		||||
            }))
 | 
			
		||||
                .And(x => x.GivenTheConfigIsValid())
 | 
			
		||||
                .And(x => GivenTheHeaderFindAndReplaceCreatorReturns())
 | 
			
		||||
                .And(x => x.GivenTheFollowingOptionsAreReturned(serviceOptions))
 | 
			
		||||
                .And(x => x.GivenTheQosOptionsCreatorReturns(expected))
 | 
			
		||||
                .When(x => x.WhenICreateTheConfig())
 | 
			
		||||
@@ -214,6 +222,7 @@ namespace Ocelot.UnitTests.Configuration
 | 
			
		||||
                            },
 | 
			
		||||
                        }))
 | 
			
		||||
                            .And(x => x.GivenTheConfigIsValid())
 | 
			
		||||
                            .And(x => GivenTheHeaderFindAndReplaceCreatorReturns())
 | 
			
		||||
                            .And(x => x.GivenTheFollowingOptionsAreReturned(reRouteOptions))
 | 
			
		||||
                            .When(x => x.WhenICreateTheConfig())
 | 
			
		||||
                            .Then(x => x.ThenTheReRoutesAre(new List<ReRoute>
 | 
			
		||||
@@ -248,6 +257,7 @@ namespace Ocelot.UnitTests.Configuration
 | 
			
		||||
                                            },
 | 
			
		||||
                                        }))
 | 
			
		||||
                                            .And(x => x.GivenTheConfigIsValid())
 | 
			
		||||
                                            .And(x => GivenTheHeaderFindAndReplaceCreatorReturns())
 | 
			
		||||
                                            .And(x => x.GivenTheFollowingOptionsAreReturned(reRouteOptions))
 | 
			
		||||
                                            .When(x => x.WhenICreateTheConfig())
 | 
			
		||||
                                            .Then(x => x.ThenTheReRoutesAre(new List<ReRoute>
 | 
			
		||||
@@ -290,6 +300,7 @@ namespace Ocelot.UnitTests.Configuration
 | 
			
		||||
                            }
 | 
			
		||||
                        }))
 | 
			
		||||
                            .And(x => x.GivenTheConfigIsValid())
 | 
			
		||||
                            .And(x => GivenTheHeaderFindAndReplaceCreatorReturns())
 | 
			
		||||
                            .And(x => x.GivenTheFollowingOptionsAreReturned(reRouteOptions))
 | 
			
		||||
                            .When(x => x.WhenICreateTheConfig())
 | 
			
		||||
                            .Then(x => x.ThenTheReRoutesAre(new List<ReRoute>
 | 
			
		||||
@@ -325,6 +336,7 @@ namespace Ocelot.UnitTests.Configuration
 | 
			
		||||
                            }
 | 
			
		||||
                        }))
 | 
			
		||||
                            .And(x => x.GivenTheConfigIsValid())
 | 
			
		||||
                            .And(x => GivenTheHeaderFindAndReplaceCreatorReturns())
 | 
			
		||||
                            .And(x => x.GivenTheFollowingOptionsAreReturned(reRouteOptions))
 | 
			
		||||
                            .When(x => x.WhenICreateTheConfig())
 | 
			
		||||
                            .Then(x => x.ThenTheReRoutesAre(new List<ReRoute>
 | 
			
		||||
@@ -359,6 +371,7 @@ namespace Ocelot.UnitTests.Configuration
 | 
			
		||||
                }
 | 
			
		||||
            }))
 | 
			
		||||
                .And(x => x.GivenTheConfigIsValid())
 | 
			
		||||
                .And(x => GivenTheHeaderFindAndReplaceCreatorReturns())
 | 
			
		||||
                .And(x => x.GivenTheFollowingOptionsAreReturned(reRouteOptions))
 | 
			
		||||
                .And(x => x.GivenTheUpstreamTemplatePatternCreatorReturns("(?i)/api/products/.*/$"))
 | 
			
		||||
                .When(x => x.WhenICreateTheConfig())
 | 
			
		||||
@@ -398,6 +411,7 @@ namespace Ocelot.UnitTests.Configuration
 | 
			
		||||
                }
 | 
			
		||||
            }))
 | 
			
		||||
                .And(x => x.GivenTheConfigIsValid())    
 | 
			
		||||
                .And(x => GivenTheHeaderFindAndReplaceCreatorReturns())
 | 
			
		||||
                .And(x => x.GivenTheFollowingOptionsAreReturned(reRouteOptions))
 | 
			
		||||
                .And(x => x.GivenTheRequestIdCreatorReturns("blahhhh"))
 | 
			
		||||
                .When(x => x.WhenICreateTheConfig())
 | 
			
		||||
@@ -435,6 +449,7 @@ namespace Ocelot.UnitTests.Configuration
 | 
			
		||||
                            },
 | 
			
		||||
            }))
 | 
			
		||||
                .And(x => x.GivenTheFollowingOptionsAreReturned(reRouteOptions))
 | 
			
		||||
                .And(x => GivenTheHeaderFindAndReplaceCreatorReturns())
 | 
			
		||||
                .And(x => x.GivenTheConfigIsValid())
 | 
			
		||||
                .And(x => x.GivenTheFollowingHttpHandlerOptionsAreReturned(httpHandlerOptions))
 | 
			
		||||
                .When(x => x.WhenICreateTheConfig())
 | 
			
		||||
@@ -470,6 +485,7 @@ namespace Ocelot.UnitTests.Configuration
 | 
			
		||||
 | 
			
		||||
            this.Given(x => x.GivenTheConfigIs(fileConfig))
 | 
			
		||||
                .And(x => x.GivenTheConfigIsValid())
 | 
			
		||||
                .And(x => GivenTheHeaderFindAndReplaceCreatorReturns())
 | 
			
		||||
                .And(x => x.GivenTheAuthOptionsCreatorReturns(authenticationOptions))
 | 
			
		||||
                .And(x => x.GivenTheFollowingOptionsAreReturned(reRouteOptions))
 | 
			
		||||
                .And(x => x.GivenTheClaimsToThingCreatorReturns(new List<ClaimToThing> { new ClaimToThing("CustomerId", "CustomerId", "", 0) }))
 | 
			
		||||
@@ -504,6 +520,7 @@ namespace Ocelot.UnitTests.Configuration
 | 
			
		||||
 | 
			
		||||
            this.Given(x => x.GivenTheConfigIs(fileConfig))
 | 
			
		||||
                .And(x => x.GivenTheConfigIsValid())
 | 
			
		||||
                .And(x => GivenTheHeaderFindAndReplaceCreatorReturns())
 | 
			
		||||
                .And(x => x.GivenTheFollowingOptionsAreReturned(reRouteOptions))
 | 
			
		||||
                .And(x => x.GivenTheAuthOptionsCreatorReturns(authenticationOptions))
 | 
			
		||||
                .When(x => x.WhenICreateTheConfig())
 | 
			
		||||
@@ -661,6 +678,17 @@ namespace Ocelot.UnitTests.Configuration
 | 
			
		||||
                .Verify(x => x.Create(_fileConfiguration.GlobalConfiguration), Times.Once);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private void ThenTheHeaderFindAndReplaceCreatorIsCalledCorrectly()
 | 
			
		||||
        {
 | 
			
		||||
            _headerFindAndReplaceCreator
 | 
			
		||||
                .Verify(x => x.Create(It.IsAny<FileReRoute>()), Times.Once);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private void GivenTheHeaderFindAndReplaceCreatorReturns()
 | 
			
		||||
        {
 | 
			
		||||
            _headerFindAndReplaceCreator.Setup(x => x.Create(It.IsAny<FileReRoute>())).Returns(new HeaderTransformations(new List<HeaderFindAndReplace>(), new List<HeaderFindAndReplace>()));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private void GivenTheFollowingIsReturned(ServiceProviderConfiguration serviceProviderConfiguration)
 | 
			
		||||
        {
 | 
			
		||||
            _serviceProviderConfigCreator
 | 
			
		||||
 
 | 
			
		||||
@@ -0,0 +1,158 @@
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using Moq;
 | 
			
		||||
using Ocelot.Configuration;
 | 
			
		||||
using Ocelot.Configuration.Builder;
 | 
			
		||||
using Ocelot.Configuration.Creator;
 | 
			
		||||
using Ocelot.Configuration.File;
 | 
			
		||||
using Ocelot.Middleware;
 | 
			
		||||
using Shouldly;
 | 
			
		||||
using TestStack.BDDfy;
 | 
			
		||||
using Xunit;
 | 
			
		||||
 | 
			
		||||
namespace Ocelot.UnitTests.Configuration
 | 
			
		||||
{
 | 
			
		||||
    public class HeaderFindAndReplaceCreatorTests
 | 
			
		||||
    {
 | 
			
		||||
        private HeaderFindAndReplaceCreator _creator;
 | 
			
		||||
        private FileReRoute _reRoute;
 | 
			
		||||
        private HeaderTransformations _result;
 | 
			
		||||
        private Mock<IBaseUrlFinder> _finder;
 | 
			
		||||
 | 
			
		||||
        public HeaderFindAndReplaceCreatorTests()
 | 
			
		||||
        {
 | 
			
		||||
            _finder = new Mock<IBaseUrlFinder>();
 | 
			
		||||
            _creator = new HeaderFindAndReplaceCreator(_finder.Object);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        [Fact]
 | 
			
		||||
        public void should_create()
 | 
			
		||||
        {
 | 
			
		||||
            var reRoute = new FileReRoute
 | 
			
		||||
            {
 | 
			
		||||
                UpstreamHeaderTransform = new Dictionary<string, string>
 | 
			
		||||
                {
 | 
			
		||||
                    {"Test", "Test, Chicken"},
 | 
			
		||||
 | 
			
		||||
                    {"Moop", "o, a"}
 | 
			
		||||
                },
 | 
			
		||||
                 DownstreamHeaderTransform = new Dictionary<string, string>
 | 
			
		||||
                {
 | 
			
		||||
                    {"Pop", "West, East"},
 | 
			
		||||
 | 
			
		||||
                    {"Bop", "e, r"}
 | 
			
		||||
                }
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
            var upstream = new List<HeaderFindAndReplace>
 | 
			
		||||
            {
 | 
			
		||||
                new HeaderFindAndReplace("Test", "Test", "Chicken", 0),
 | 
			
		||||
                new HeaderFindAndReplace("Moop", "o", "a", 0)
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
            var downstream = new List<HeaderFindAndReplace>
 | 
			
		||||
            {
 | 
			
		||||
                new HeaderFindAndReplace("Pop", "West", "East", 0),
 | 
			
		||||
                new HeaderFindAndReplace("Bop", "e", "r", 0)
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
            this.Given(x => GivenTheReRoute(reRoute))
 | 
			
		||||
                .When(x => WhenICreate())
 | 
			
		||||
                .Then(x => ThenTheFollowingUpstreamIsReturned(upstream))
 | 
			
		||||
                .Then(x => ThenTheFollowingDownstreamIsReturned(downstream))
 | 
			
		||||
                .BDDfy();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        [Fact]
 | 
			
		||||
        public void should_use_base_url_placeholder()
 | 
			
		||||
        {
 | 
			
		||||
            var reRoute = new FileReRoute
 | 
			
		||||
            {
 | 
			
		||||
                 DownstreamHeaderTransform = new Dictionary<string, string>
 | 
			
		||||
                {
 | 
			
		||||
                    {"Location", "http://www.bbc.co.uk/, {BaseUrl}"},
 | 
			
		||||
                }
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
            var downstream = new List<HeaderFindAndReplace>
 | 
			
		||||
            {
 | 
			
		||||
                new HeaderFindAndReplace("Location", "http://www.bbc.co.uk/", "http://ocelot.com/", 0),
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
            this.Given(x => GivenTheReRoute(reRoute))
 | 
			
		||||
                .And(x => GivenTheBaseUrlIs("http://ocelot.com/"))
 | 
			
		||||
                .When(x => WhenICreate())
 | 
			
		||||
                .Then(x => ThenTheFollowingDownstreamIsReturned(downstream))
 | 
			
		||||
                .BDDfy();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        [Fact]
 | 
			
		||||
        public void should_use_base_url_partial_placeholder()
 | 
			
		||||
        {
 | 
			
		||||
            var reRoute = new FileReRoute
 | 
			
		||||
            {
 | 
			
		||||
                 DownstreamHeaderTransform = new Dictionary<string, string>
 | 
			
		||||
                {
 | 
			
		||||
                    {"Location", "http://www.bbc.co.uk/pay, {BaseUrl}pay"},
 | 
			
		||||
                }
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
            var downstream = new List<HeaderFindAndReplace>
 | 
			
		||||
            {
 | 
			
		||||
                new HeaderFindAndReplace("Location", "http://www.bbc.co.uk/pay", "http://ocelot.com/pay", 0),
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
            this.Given(x => GivenTheReRoute(reRoute))
 | 
			
		||||
                .And(x => GivenTheBaseUrlIs("http://ocelot.com/"))
 | 
			
		||||
                .When(x => WhenICreate())
 | 
			
		||||
                .Then(x => ThenTheFollowingDownstreamIsReturned(downstream))
 | 
			
		||||
                .BDDfy();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private void GivenTheBaseUrlIs(string baseUrl)
 | 
			
		||||
        {
 | 
			
		||||
            _finder.Setup(x => x.Find()).Returns(baseUrl);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private void ThenTheFollowingDownstreamIsReturned(List<HeaderFindAndReplace> downstream)
 | 
			
		||||
        {
 | 
			
		||||
            _result.Downstream.Count.ShouldBe(downstream.Count);
 | 
			
		||||
            
 | 
			
		||||
            for (int i = 0; i < _result.Downstream.Count; i++)
 | 
			
		||||
            {
 | 
			
		||||
                var result = _result.Downstream[i];
 | 
			
		||||
                var expected = downstream[i];
 | 
			
		||||
                result.Find.ShouldBe(expected.Find);
 | 
			
		||||
                result.Index.ShouldBe(expected.Index);
 | 
			
		||||
                result.Key.ShouldBe(expected.Key);
 | 
			
		||||
                result.Replace.ShouldBe(expected.Replace);
 | 
			
		||||
            }        
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private void GivenTheReRoute(FileReRoute reRoute)
 | 
			
		||||
        {
 | 
			
		||||
            _reRoute = reRoute;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private void WhenICreate()
 | 
			
		||||
        {
 | 
			
		||||
            _result = _creator.Create(_reRoute);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private void ThenTheFollowingUpstreamIsReturned(List<HeaderFindAndReplace> expecteds)
 | 
			
		||||
        {
 | 
			
		||||
            _result.Upstream.Count.ShouldBe(expecteds.Count);
 | 
			
		||||
            
 | 
			
		||||
            for (int i = 0; i < _result.Upstream.Count; i++)
 | 
			
		||||
            {
 | 
			
		||||
                var result = _result.Upstream[i];
 | 
			
		||||
                var expected = expecteds[i];
 | 
			
		||||
                result.Find.ShouldBe(expected.Find);
 | 
			
		||||
                result.Index.ShouldBe(expected.Index);
 | 
			
		||||
                result.Key.ShouldBe(expected.Key);
 | 
			
		||||
                result.Replace.ShouldBe(expected.Replace);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user