Feature/re route specific handlers (#269)

* #264 added handlers to config

* #264 added global handlers object and defaut param for method, not sure this is correct api for users yet

* #264 Can now add all sorts of delegating handlers in all sorts of ways

* +semver: breaking #264
This commit is contained in:
Tom Pallister 2018-03-10 21:02:59 +00:00 committed by GitHub
parent a31a3ae0fc
commit 4c840d40a6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
24 changed files with 692 additions and 137 deletions

View File

@ -1,26 +1,16 @@
Delegating Handers Delegating Handers
================== ==================
Ocelot allows the user to add delegating handlers to the HttpClient transport. This feature was requested `GitHub #208 <https://github.com/TomPallister/Ocelot/issues/208>`_ and I decided that it was going to be useful in various ways. Ocelot allows the user to add delegating handlers to the HttpClient transport. This feature was requested `GitHub #208 <https://github.com/TomPallister/Ocelot/issues/208>`_
and I decided that it was going to be useful in various ways. Since then we extended it in `GitHub #208 <https://github.com/TomPallister/Ocelot/issues/264>`_.
Usage Usage
^^^^^ ^^^^^
In order to add delegating handlers to the HttpClient transport you need to do the following. In order to add delegating handlers to the HttpClient transport you need to do two main things.
This will register the Handlers as singletons. Because Ocelot caches the HttpClient for the downstream services to avoid First in order to create a class that can be used a delegating handler it must look as follows. We are going to register these handlers in the
socket exhaustion (well known http client issue) you can only register singleton handlers. asp.net core container so you can inject any other services you have registered into the constructor of your handler.
.. code-block:: csharp
services.AddOcelot()
.AddDelegatingHandler<FakeHandler>()
.AddDelegatingHandler<FakeHandlerTwo>()
You can have as many DelegatingHandlers as you want and they are run in a first in first out order. If you are using Ocelot's QoS functionality then that will always be run after your last delegating handler. If you are also registering handlers in DI these will be
run first.
In order to create a class that can be used a delegating handler it must look as follows
.. code-block:: csharp .. code-block:: csharp
@ -33,4 +23,57 @@ In order to create a class that can be used a delegating handler it must look as
} }
} }
Next you must add the handlers to Ocelot's container either as singleton like follows..
.. code-block:: csharp
services.AddOcelot()
.AddSingletonDelegatingHandler<FakeHandler>()
.AddSingletonDelegatingHandler<FakeHandlerTwo>()
Or transient as below...
.. code-block:: csharp
services.AddOcelot()
.AddTransientDelegatingHandler<FakeHandler>()
.AddTransientDelegatingHandler<FakeHandlerTwo>()
Both of these Add methods have a default parameter called global which is set to false. If it is false then the intent of
the DelegatingHandler is to be applied to specific ReRoutes via configuration.json (more on that later). If it is set to true
then it becomes a global handler and will be applied to all ReRoutes.
e.g.
.. code-block:: csharp
services.AddOcelot()
.AddSingletonDelegatingHandler<FakeHandler>(true)
Or transient as below...
.. code-block:: csharp
services.AddOcelot()
.AddTransientDelegatingHandler<FakeHandler>(true)
Finally if you want ReRoute specific DelegatingHandlers or to order your specific and / or global (more on this later) DelegatingHandlers
then you must add the following json to the specific ReRoute in configuration.json. The names in the array must match the class names of your
DelegatingHandlers for Ocelot to match them together.
.. code-block:: json
"DelegatingHandlers": [
"FakeHandlerTwo",
"FakeHandler"
]
You can have as many DelegatingHandlers as you want and they are run in the following order:
1. Any globals that are left in the order they were added to services and are not in the DelegatingHandlers array from configuration.json.
2. Any non global DelegatingHandlers plus any globals that were in the DelegatingHandlers array from configuration.json ordered as they are in the DelegatingHandlers array.
3. Tracing DelegatingHandler if enabled (see tracing docs).
4. QoS DelegatingHandler if enabled (see QoS docs).
5. The HttpClient sends the HttpRequestMessage.
Hopefully other people will find this feature useful! Hopefully other people will find this feature useful!

View File

@ -1,8 +0,0 @@
namespace Ocelot.Authentication.Handler
{
public enum SupportedAuthenticationProviders
{
IdentityServer,
Jwt
}
}

View File

@ -1,4 +1,3 @@
using Microsoft.AspNetCore.Builder;
using Ocelot.Middleware.Pipeline; using Ocelot.Middleware.Pipeline;
namespace Ocelot.Authentication.Middleware namespace Ocelot.Authentication.Middleware

View File

@ -1,16 +1,12 @@
using Ocelot.Infrastructure.RequestData; namespace Ocelot.Authorisation.Middleware
using Ocelot.Logging;
using Ocelot.Responses;
using Ocelot.Configuration;
namespace Ocelot.Authorisation.Middleware
{ {
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading.Tasks; using System.Threading.Tasks;
using Errors; using Errors;
using Microsoft.AspNetCore.Http;
using Ocelot.DownstreamRouteFinder.Middleware;
using Ocelot.Middleware; using Ocelot.Middleware;
using Logging;
using Responses;
using Configuration;
public class AuthorisationMiddleware : OcelotMiddleware public class AuthorisationMiddleware : OcelotMiddleware
{ {

View File

@ -2,8 +2,6 @@ using Ocelot.Middleware.Pipeline;
namespace Ocelot.Authorisation.Middleware namespace Ocelot.Authorisation.Middleware
{ {
using Microsoft.AspNetCore.Builder;
public static class AuthorisationMiddlewareMiddlewareExtensions public static class AuthorisationMiddlewareMiddlewareExtensions
{ {
public static IOcelotPipelineBuilder UseAuthorisationMiddleware(this IOcelotPipelineBuilder builder) public static IOcelotPipelineBuilder UseAuthorisationMiddleware(this IOcelotPipelineBuilder builder)

View File

@ -34,9 +34,9 @@ namespace Ocelot.Authorisation
var userScopes = values.Data; var userScopes = values.Data;
List<string> matchesScopes = routeAllowedScopes.Intersect(userScopes).ToList(); var matchesScopes = routeAllowedScopes.Intersect(userScopes).ToList();
if (matchesScopes == null || matchesScopes.Count == 0) if (matchesScopes.Count == 0)
{ {
return new ErrorResponse<bool>(new List<Error> return new ErrorResponse<bool>(new List<Error>
{ {

View File

@ -36,9 +36,12 @@ namespace Ocelot.Configuration.Builder
private readonly List<DownstreamHostAndPort> _downstreamAddresses; private readonly List<DownstreamHostAndPort> _downstreamAddresses;
private string _upstreamHost; private string _upstreamHost;
private string _key; private string _key;
private List<string> _delegatingHandlers;
public DownstreamReRouteBuilder() public DownstreamReRouteBuilder()
{ {
_downstreamAddresses = new List<DownstreamHostAndPort>(); _downstreamAddresses = new List<DownstreamHostAndPort>();
_delegatingHandlers = new List<string>();
} }
public DownstreamReRouteBuilder WithDownstreamAddresses(List<DownstreamHostAndPort> downstreamAddresses) public DownstreamReRouteBuilder WithDownstreamAddresses(List<DownstreamHostAndPort> downstreamAddresses)
@ -215,6 +218,12 @@ namespace Ocelot.Configuration.Builder
return this; return this;
} }
public DownstreamReRouteBuilder WithDelegatingHandlers(List<string> delegatingHandlers)
{
_delegatingHandlers = delegatingHandlers;
return this;
}
public DownstreamReRoute Build() public DownstreamReRoute Build()
{ {
return new DownstreamReRoute( return new DownstreamReRoute(
@ -243,7 +252,8 @@ namespace Ocelot.Configuration.Builder
_isAuthorised, _isAuthorised,
_authenticationOptions, _authenticationOptions,
new PathTemplate(_downstreamPathTemplate), new PathTemplate(_downstreamPathTemplate),
_reRouteKey); _reRouteKey,
_delegatingHandlers);
} }
} }
} }

View File

@ -212,6 +212,7 @@ namespace Ocelot.Configuration.Creator
.WithUpstreamHeaderFindAndReplace(hAndRs.Upstream) .WithUpstreamHeaderFindAndReplace(hAndRs.Upstream)
.WithDownstreamHeaderFindAndReplace(hAndRs.Downstream) .WithDownstreamHeaderFindAndReplace(hAndRs.Downstream)
.WithUpstreamHost(fileReRoute.UpstreamHost) .WithUpstreamHost(fileReRoute.UpstreamHost)
.WithDelegatingHandlers(fileReRoute.DelegatingHandlers)
.Build(); .Build();
return reRoute; return reRoute;

View File

@ -31,8 +31,10 @@ namespace Ocelot.Configuration
bool isAuthorised, bool isAuthorised,
AuthenticationOptions authenticationOptions, AuthenticationOptions authenticationOptions,
PathTemplate downstreamPathTemplate, PathTemplate downstreamPathTemplate,
string reRouteKey) string reRouteKey,
List<string> delegatingHandlers)
{ {
DelegatingHandlers = delegatingHandlers;
Key = key; Key = key;
UpstreamPathTemplate = upstreamPathTemplate; UpstreamPathTemplate = upstreamPathTemplate;
UpstreamHeadersFindAndReplace = upstreamHeadersFindAndReplace ?? new List<HeaderFindAndReplace>(); UpstreamHeadersFindAndReplace = upstreamHeadersFindAndReplace ?? new List<HeaderFindAndReplace>();
@ -87,5 +89,6 @@ namespace Ocelot.Configuration
public AuthenticationOptions AuthenticationOptions { get; private set; } public AuthenticationOptions AuthenticationOptions { get; private set; }
public PathTemplate DownstreamPathTemplate { get; private set; } public PathTemplate DownstreamPathTemplate { get; private set; }
public string ReRouteKey { get; private set; } public string ReRouteKey { get; private set; }
public List<string> DelegatingHandlers {get;private set;}
} }
} }

View File

@ -19,6 +19,7 @@ namespace Ocelot.Configuration.File
HttpHandlerOptions = new FileHttpHandlerOptions(); HttpHandlerOptions = new FileHttpHandlerOptions();
UpstreamHeaderTransform = new Dictionary<string, string>(); UpstreamHeaderTransform = new Dictionary<string, string>();
DownstreamHostAndPorts = new List<FileHostAndPort>(); DownstreamHostAndPorts = new List<FileHostAndPort>();
DelegatingHandlers = new List<string>();
} }
public string DownstreamPathTemplate { get; set; } public string DownstreamPathTemplate { get; set; }
@ -44,5 +45,6 @@ namespace Ocelot.Configuration.File
public List<FileHostAndPort> DownstreamHostAndPorts {get;set;} public List<FileHostAndPort> DownstreamHostAndPorts {get;set;}
public string UpstreamHost { get; set; } public string UpstreamHost { get; set; }
public string Key { get;set; } public string Key { get;set; }
public List<string> DelegatingHandlers {get;set;}
} }
} }

View File

@ -3,7 +3,6 @@ using CacheManager.Core;
using System; using System;
using System.Net.Http; using System.Net.Http;
using IdentityServer4.AccessTokenValidation; using IdentityServer4.AccessTokenValidation;
using Ocelot.Requester;
namespace Ocelot.DependencyInjection namespace Ocelot.DependencyInjection
{ {
@ -19,6 +18,10 @@ namespace Ocelot.DependencyInjection
IOcelotAdministrationBuilder AddAdministration(string path, Action<IdentityServerAuthenticationOptions> configOptions); IOcelotAdministrationBuilder AddAdministration(string path, Action<IdentityServerAuthenticationOptions> configOptions);
IOcelotBuilder AddDelegatingHandler<T>() where T : DelegatingHandler; IOcelotBuilder AddSingletonDelegatingHandler<T>(bool global = false)
where T : DelegatingHandler;
IOcelotBuilder AddTransientDelegatingHandler<T>(bool global = false)
where T : DelegatingHandler;
} }
} }

View File

@ -182,10 +182,41 @@ namespace Ocelot.DependencyInjection
return new OcelotAdministrationBuilder(_services, _configurationRoot); return new OcelotAdministrationBuilder(_services, _configurationRoot);
} }
public IOcelotBuilder AddDelegatingHandler<THandler>() public IOcelotBuilder AddSingletonDelegatingHandler<THandler>(bool global = false)
where THandler : DelegatingHandler where THandler : DelegatingHandler
{
if(global)
{
_services.AddSingleton<THandler>();
_services.AddSingleton<GlobalDelegatingHandler>(s => {
var service = s.GetService<THandler>();
return new GlobalDelegatingHandler(service);
});
}
else
{ {
_services.AddSingleton<DelegatingHandler, THandler>(); _services.AddSingleton<DelegatingHandler, THandler>();
}
return this;
}
public IOcelotBuilder AddTransientDelegatingHandler<THandler>(bool global = false)
where THandler : DelegatingHandler
{
if(global)
{
_services.AddTransient<THandler>();
_services.AddTransient<GlobalDelegatingHandler>(s => {
var service = s.GetService<THandler>();
return new GlobalDelegatingHandler(service);
});
}
else
{
_services.AddTransient<DelegatingHandler, THandler>();
}
return this; return this;
} }

View File

@ -1,8 +1,9 @@
using Newtonsoft.Json; using Newtonsoft.Json;
namespace Ocelot.Authentication namespace Ocelot.Raft
{ {
class BearerToken [ExcludeFromCoverage]
internal class BearerToken
{ {
[JsonProperty("access_token")] [JsonProperty("access_token")]
public string AccessToken { get; set; } public string AccessToken { get; set; }

View File

@ -1,5 +1,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using System.Net.Http; using System.Net.Http;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Ocelot.Configuration; using Ocelot.Configuration;
@ -29,14 +30,37 @@ namespace Ocelot.Requester
public Response<List<Func<DelegatingHandler>>> Get(DownstreamReRoute request) public Response<List<Func<DelegatingHandler>>> Get(DownstreamReRoute request)
{ {
var handlersAppliedToAll = _serviceProvider.GetServices<DelegatingHandler>(); var globalDelegatingHandlers = _serviceProvider
.GetServices<GlobalDelegatingHandler>()
.ToList();
var reRouteSpecificHandlers = _serviceProvider
.GetServices<DelegatingHandler>()
.ToList();
var handlers = new List<Func<DelegatingHandler>>(); var handlers = new List<Func<DelegatingHandler>>();
foreach (var handler in handlersAppliedToAll) foreach (var handler in globalDelegatingHandlers)
{
if (GlobalIsInHandlersConfig(request, handler))
{
reRouteSpecificHandlers.Add(handler.DelegatingHandler);
}
else
{
handlers.Add(() => handler.DelegatingHandler);
}
}
if (request.DelegatingHandlers.Any())
{
var sorted = SortByConfigOrder(request, reRouteSpecificHandlers);
foreach (var handler in sorted)
{ {
handlers.Add(() => handler); handlers.Add(() => handler);
} }
}
if (request.HttpHandlerOptions.UseTracing) if (request.HttpHandlerOptions.UseTracing)
{ {
@ -57,5 +81,22 @@ namespace Ocelot.Requester
return new OkResponse<List<Func<DelegatingHandler>>>(handlers); return new OkResponse<List<Func<DelegatingHandler>>>(handlers);
} }
private List<DelegatingHandler> SortByConfigOrder(DownstreamReRoute request, List<DelegatingHandler> reRouteSpecificHandlers)
{
return reRouteSpecificHandlers
.Where(x => request.DelegatingHandlers.Contains(x.GetType().Name))
.OrderBy(d =>
{
var type = d.GetType().Name;
var pos = request.DelegatingHandlers.IndexOf(type);
return pos;
}).ToList();
}
private bool GlobalIsInHandlersConfig(DownstreamReRoute request, GlobalDelegatingHandler handler)
{
return request.DelegatingHandlers.Contains(handler.DelegatingHandler.GetType().Name);
}
} }
} }

View File

@ -76,4 +76,19 @@ namespace Ocelot.Requester
return baseUrl; return baseUrl;
} }
} }
public class ReRouteDelegatingHandler<T> where T : DelegatingHandler
{
public T DelegatingHandler { get; private set; }
}
public class GlobalDelegatingHandler
{
public GlobalDelegatingHandler(DelegatingHandler delegatingHandler)
{
DelegatingHandler = delegatingHandler;
}
public DelegatingHandler DelegatingHandler { get; private set; }
}
} }

View File

@ -107,6 +107,7 @@ namespace Ocelot.AcceptanceTests
_builder.Start(); _builder.Start();
} }
public void Dispose() public void Dispose()
{ {
_builder?.Dispose(); _builder?.Dispose();

View File

@ -8,7 +8,6 @@ using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Ocelot.Configuration.File; using Ocelot.Configuration.File;
using Shouldly;
using TestStack.BDDfy; using TestStack.BDDfy;
using Xunit; using Xunit;

View File

@ -26,9 +26,48 @@ namespace Ocelot.AcceptanceTests
_steps = new Steps(); _steps = new Steps();
} }
[Fact]
public void should_call_re_route_ordered_specific_handlers()
{
var configuration = new FileConfiguration
{
ReRoutes = new List<FileReRoute>
{
new FileReRoute
{
DownstreamPathTemplate = "/",
DownstreamScheme = "http",
DownstreamHostAndPorts = new List<FileHostAndPort>
{
new FileHostAndPort
{
Host = "localhost",
Port = 7197,
}
},
UpstreamPathTemplate = "/",
UpstreamHttpMethod = new List<string> { "Get" },
DelegatingHandlers = new List<string>
{
"FakeHandlerTwo",
"FakeHandler"
}
}
}
};
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:7197", "/", 200, "Hello from Laura"))
.And(x => _steps.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunningWithSpecficHandlersRegisteredInDi<FakeHandler, FakeHandlerTwo>())
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
.And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura"))
.And(x => ThenTheOrderedHandlersAreCalledCorrectly())
.BDDfy();
}
[Fact] [Fact]
public void should_call_di_handlers() public void should_call_global_di_handlers()
{ {
var configuration = new FileConfiguration var configuration = new FileConfiguration
{ {
@ -54,7 +93,7 @@ namespace Ocelot.AcceptanceTests
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:7187", "/", 200, "Hello from Laura")) this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:7187", "/", 200, "Hello from Laura"))
.And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunningWithHandlersRegisteredInDi<FakeHandler, FakeHandlerTwo>()) .And(x => _steps.GivenOcelotIsRunningWithGlobalHandlersRegisteredInDi<FakeHandler, FakeHandlerTwo>())
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) .When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
.And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura")) .And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura"))
@ -62,9 +101,8 @@ namespace Ocelot.AcceptanceTests
.BDDfy(); .BDDfy();
} }
[Fact] [Fact]
public void should_call_di_handlers_with_dependency() public void should_call_global_di_handlers_with_dependency()
{ {
var configuration = new FileConfiguration var configuration = new FileConfiguration
{ {
@ -92,7 +130,7 @@ namespace Ocelot.AcceptanceTests
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:7188", "/", 200, "Hello from Laura")) this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:7188", "/", 200, "Hello from Laura"))
.And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunningWithHandlersRegisteredInDi<FakeHandlerWithDependency>(dependency)) .And(x => _steps.GivenOcelotIsRunningWithGlobalHandlersRegisteredInDi<FakeHandlerWithDependency>(dependency))
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/")) .When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
.And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura")) .And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura"))
@ -110,45 +148,54 @@ namespace Ocelot.AcceptanceTests
FakeHandler.TimeCalled.ShouldBeLessThan(FakeHandlerTwo.TimeCalled); FakeHandler.TimeCalled.ShouldBeLessThan(FakeHandlerTwo.TimeCalled);
} }
private void ThenTheOrderedHandlersAreCalledCorrectly()
{
FakeHandlerTwo.TimeCalled.ShouldBeLessThan(FakeHandler.TimeCalled);
}
public class FakeDependency public class FakeDependency
{ {
public bool Called; public bool Called;
} }
class FakeHandlerWithDependency : DelegatingHandler // ReSharper disable once ClassNeverInstantiated.Local
private class FakeHandlerWithDependency : DelegatingHandler
{ {
private FakeDependency _dependency; private readonly FakeDependency _dependency;
public FakeHandlerWithDependency(FakeDependency dependency) public FakeHandlerWithDependency(FakeDependency dependency)
{ {
_dependency = dependency; _dependency = dependency;
} }
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{ {
_dependency.Called = true; _dependency.Called = true;
return await base.SendAsync(request, cancellationToken); return base.SendAsync(request, cancellationToken);
} }
} }
class FakeHandler : DelegatingHandler // ReSharper disable once ClassNeverInstantiated.Local
private class FakeHandler : DelegatingHandler
{ {
public static DateTime TimeCalled { get; private set; } public static DateTime TimeCalled { get; private set; }
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{ {
TimeCalled = DateTime.Now; TimeCalled = DateTime.Now;
return await base.SendAsync(request, cancellationToken); return base.SendAsync(request, cancellationToken);
} }
} }
class FakeHandlerTwo : DelegatingHandler
// ReSharper disable once ClassNeverInstantiated.Local
private class FakeHandlerTwo : DelegatingHandler
{ {
public static DateTime TimeCalled { get; private set; } public static DateTime TimeCalled { get; private set; }
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{ {
TimeCalled = DateTime.Now; TimeCalled = DateTime.Now;
return await base.SendAsync(request, cancellationToken); return base.SendAsync(request, cancellationToken);
} }
} }

View File

@ -50,4 +50,9 @@
<PackageReference Include="xunit" Version="2.3.1" /> <PackageReference Include="xunit" Version="2.3.1" />
<PackageReference Include="Butterfly.Client.AspNetCore" Version="0.0.8" /> <PackageReference Include="Butterfly.Client.AspNetCore" Version="0.0.8" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<Reference Include="Castle.Core">
<HintPath>..\..\..\..\Users\TGP\.nuget\packages\castle.core\4.2.1\lib\netstandard1.3\Castle.Core.dll</HintPath>
</Reference>
</ItemGroup>
</Project> </Project>

View File

@ -25,6 +25,7 @@ using Ocelot.AcceptanceTests.Caching;
using System.IO.Compression; using System.IO.Compression;
using System.Text; using System.Text;
using static Ocelot.AcceptanceTests.HttpDelegatingHandlersTests; using static Ocelot.AcceptanceTests.HttpDelegatingHandlersTests;
using Ocelot.Requester;
namespace Ocelot.AcceptanceTests namespace Ocelot.AcceptanceTests
{ {
@ -175,7 +176,7 @@ namespace Ocelot.AcceptanceTests
_ocelotClient = _ocelotServer.CreateClient(); _ocelotClient = _ocelotServer.CreateClient();
} }
public void GivenOcelotIsRunningWithHandlersRegisteredInDi<TOne, TWo>() public void GivenOcelotIsRunningWithSpecficHandlersRegisteredInDi<TOne, TWo>()
where TOne : DelegatingHandler where TOne : DelegatingHandler
where TWo : DelegatingHandler where TWo : DelegatingHandler
{ {
@ -195,8 +196,8 @@ namespace Ocelot.AcceptanceTests
{ {
s.AddSingleton(_webHostBuilder); s.AddSingleton(_webHostBuilder);
s.AddOcelot() s.AddOcelot()
.AddDelegatingHandler<TOne>() .AddSingletonDelegatingHandler<TOne>()
.AddDelegatingHandler<TWo>(); .AddSingletonDelegatingHandler<TWo>();
}) })
.Configure(a => .Configure(a =>
{ {
@ -208,7 +209,40 @@ namespace Ocelot.AcceptanceTests
_ocelotClient = _ocelotServer.CreateClient(); _ocelotClient = _ocelotServer.CreateClient();
} }
public void GivenOcelotIsRunningWithHandlersRegisteredInDi<TOne>(FakeDependency dependency) public void GivenOcelotIsRunningWithGlobalHandlersRegisteredInDi<TOne, TWo>()
where TOne : DelegatingHandler
where TWo : DelegatingHandler
{
_webHostBuilder = new WebHostBuilder();
_webHostBuilder
.ConfigureAppConfiguration((hostingContext, config) =>
{
config.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath);
var env = hostingContext.HostingEnvironment;
config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: true);
config.AddJsonFile("configuration.json");
config.AddEnvironmentVariables();
})
.ConfigureServices(s =>
{
s.AddSingleton(_webHostBuilder);
s.AddOcelot()
.AddSingletonDelegatingHandler<TOne>(true)
.AddSingletonDelegatingHandler<TWo>(true);
})
.Configure(a =>
{
a.UseOcelot().Wait();
});
_ocelotServer = new TestServer(_webHostBuilder);
_ocelotClient = _ocelotServer.CreateClient();
}
public void GivenOcelotIsRunningWithGlobalHandlersRegisteredInDi<TOne>(FakeDependency dependency)
where TOne : DelegatingHandler where TOne : DelegatingHandler
{ {
_webHostBuilder = new WebHostBuilder(); _webHostBuilder = new WebHostBuilder();
@ -228,7 +262,7 @@ namespace Ocelot.AcceptanceTests
s.AddSingleton(_webHostBuilder); s.AddSingleton(_webHostBuilder);
s.AddSingleton<FakeDependency>(dependency); s.AddSingleton<FakeDependency>(dependency);
s.AddOcelot() s.AddOcelot()
.AddDelegatingHandler<TOne>(); .AddSingletonDelegatingHandler<TOne>(true);
}) })
.Configure(a => .Configure(a =>
{ {
@ -618,15 +652,16 @@ namespace Ocelot.AcceptanceTests
public void GivenThePostHasGzipContent(object input) public void GivenThePostHasGzipContent(object input)
{ {
string json = JsonConvert.SerializeObject(input); var json = JsonConvert.SerializeObject(input);
byte[] jsonBytes = Encoding.UTF8.GetBytes(json); var jsonBytes = Encoding.UTF8.GetBytes(json);
MemoryStream ms = new MemoryStream(); var ms = new MemoryStream();
using (GZipStream gzip = new GZipStream(ms, CompressionMode.Compress, true)) using (var gzip = new GZipStream(ms, CompressionMode.Compress, true))
{ {
gzip.Write(jsonBytes, 0, jsonBytes.Length); gzip.Write(jsonBytes, 0, jsonBytes.Length);
} }
ms.Position = 0; ms.Position = 0;
StreamContent content = new StreamContent(ms); var content = new StreamContent(ms);
content.Headers.ContentType = new MediaTypeHeaderValue("application/json"); content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
content.Headers.ContentEncoding.Add("gzip"); content.Headers.ContentEncoding.Add("gzip");
_postContent = content; _postContent = content;

View File

@ -402,11 +402,14 @@ namespace Ocelot.UnitTests.Configuration
var reRouteOptions = new ReRouteOptionsBuilder() var reRouteOptions = new ReRouteOptionsBuilder()
.Build(); .Build();
var handlers = new List<string> {"Polly", "Tracer"};
var downstreamReRoute = new DownstreamReRouteBuilder() var downstreamReRoute = new DownstreamReRouteBuilder()
.WithDownstreamScheme("https") .WithDownstreamScheme("https")
.WithDownstreamPathTemplate("/products/{productId}") .WithDownstreamPathTemplate("/products/{productId}")
.WithUpstreamPathTemplate("/api/products/{productId}") .WithUpstreamPathTemplate("/api/products/{productId}")
.WithUpstreamHttpMethod(new List<string> {"Get"}) .WithUpstreamHttpMethod(new List<string> {"Get"})
.WithDelegatingHandlers(handlers)
.Build(); .Build();
this.Given(x => x.GivenTheConfigIs(new FileConfiguration this.Given(x => x.GivenTheConfigIs(new FileConfiguration
@ -419,6 +422,7 @@ namespace Ocelot.UnitTests.Configuration
UpstreamPathTemplate = "/api/products/{productId}", UpstreamPathTemplate = "/api/products/{productId}",
DownstreamPathTemplate = "/products/{productId}", DownstreamPathTemplate = "/products/{productId}",
UpstreamHttpMethod = new List<string> { "Get" }, UpstreamHttpMethod = new List<string> { "Get" },
DelegatingHandlers = handlers
} }
}, },
})) }))
@ -822,6 +826,7 @@ namespace Ocelot.UnitTests.Configuration
result.DownstreamReRoute[0].ClaimsToHeaders.Count.ShouldBe(expected.DownstreamReRoute[0].ClaimsToHeaders.Count); result.DownstreamReRoute[0].ClaimsToHeaders.Count.ShouldBe(expected.DownstreamReRoute[0].ClaimsToHeaders.Count);
result.DownstreamReRoute[0].ClaimsToQueries.Count.ShouldBe(expected.DownstreamReRoute[0].ClaimsToQueries.Count); result.DownstreamReRoute[0].ClaimsToQueries.Count.ShouldBe(expected.DownstreamReRoute[0].ClaimsToQueries.Count);
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);
} }
} }

View File

@ -28,26 +28,58 @@ namespace Ocelot.UnitTests.DependencyInjection
private readonly IConfiguration _configRoot; private readonly IConfiguration _configRoot;
private IOcelotBuilder _ocelotBuilder; private IOcelotBuilder _ocelotBuilder;
private readonly int _maxRetries; private readonly int _maxRetries;
private Exception _ex;
public OcelotBuilderTests() public OcelotBuilderTests()
{ {
_configRoot = new ConfigurationRoot(new List<IConfigurationProvider>()); _configRoot = new ConfigurationRoot(new List<IConfigurationProvider>());
_services = new ServiceCollection(); _services = new ServiceCollection();
_services.AddSingleton<IHostingEnvironment, HostingEnvironment>(); _services.AddSingleton<IHostingEnvironment, HostingEnvironment>();
_services.AddSingleton<IConfiguration>(_configRoot); _services.AddSingleton(_configRoot);
_maxRetries = 100; _maxRetries = 100;
} }
private Exception _ex; [Fact]
public void should_add_specific_delegating_handlers_transient()
{
this.Given(x => WhenISetUpOcelotServices())
.When(x => AddSpecificTransientDelegatingHandler<FakeDelegatingHandler>())
.And(x => AddSpecificTransientDelegatingHandler<FakeDelegatingHandlerTwo>())
.Then(x => ThenTheProviderIsRegisteredAndReturnsSpecificHandlers<FakeDelegatingHandler, FakeDelegatingHandlerTwo>())
.And(x => ThenTheSpecificHandlersAreTransient())
.BDDfy();
}
[Fact] [Fact]
public void should_add_delegating_handlers_with_di() public void should_add_specific_delegating_handler_singleton()
{ {
this.Given(x => WhenISetUpOcelotServices()) this.Given(x => WhenISetUpOcelotServices())
.When(x => AddDelegate<FakeDelegatingHandler>()) .When(x => AddSpecificDelegatingHandler<FakeDelegatingHandler>())
.And(x => AddDelegate<FakeDelegatingHandlerTwo>()) .And(x => AddSpecificDelegatingHandler<FakeDelegatingHandlerTwo>())
.Then(x => ThenTheProviderIsRegisteredAndReturnsSpecificHandlers<FakeDelegatingHandler, FakeDelegatingHandlerTwo>())
.And(x => ThenTheSpecificHandlersAreSingleton())
.BDDfy();
}
[Fact]
public void should_add_global_delegating_handlers_transient()
{
this.Given(x => WhenISetUpOcelotServices())
.When(x => AddTransientGlobalDelegatingHandler<FakeDelegatingHandler>())
.And(x => AddTransientGlobalDelegatingHandler<FakeDelegatingHandlerTwo>())
.Then(x => ThenTheProviderIsRegisteredAndReturnsHandlers<FakeDelegatingHandler, FakeDelegatingHandlerTwo>()) .Then(x => ThenTheProviderIsRegisteredAndReturnsHandlers<FakeDelegatingHandler, FakeDelegatingHandlerTwo>())
.And(x => ThenTheGlobalHandlersAreTransient())
.BDDfy();
}
[Fact]
public void should_add_global_delegating_handlers_singleton()
{
this.Given(x => WhenISetUpOcelotServices())
.When(x => AddGlobalDelegatingHandler<FakeDelegatingHandler>())
.And(x => AddGlobalDelegatingHandler<FakeDelegatingHandlerTwo>())
.Then(x => ThenTheProviderIsRegisteredAndReturnsHandlers<FakeDelegatingHandler, FakeDelegatingHandlerTwo>())
.And(x => ThenTheGlobalHandlersAreSingleton())
.BDDfy(); .BDDfy();
} }
@ -118,16 +150,6 @@ namespace Ocelot.UnitTests.DependencyInjection
.BDDfy(); .BDDfy();
} }
private void WhenISetUpAdministration()
{
_ocelotBuilder.AddAdministration("/administration", "secret");
}
private void WhenISetUpAdministration(Action<IdentityServerAuthenticationOptions> options)
{
_ocelotBuilder.AddAdministration("/administration", options);
}
[Fact] [Fact]
public void should_use_logger_factory() public void should_use_logger_factory()
{ {
@ -155,6 +177,62 @@ namespace Ocelot.UnitTests.DependencyInjection
.BDDfy(); .BDDfy();
} }
private void ThenTheSpecificHandlersAreSingleton()
{
var handlers = _serviceProvider.GetServices<DelegatingHandler>().ToList();
var first = handlers[0];
handlers = _serviceProvider.GetServices<DelegatingHandler>().ToList();
var second = handlers[0];
first.ShouldBe(second);
}
private void ThenTheSpecificHandlersAreTransient()
{
var handlers = _serviceProvider.GetServices<DelegatingHandler>().ToList();
var first = handlers[0];
handlers = _serviceProvider.GetServices<DelegatingHandler>().ToList();
var second = handlers[0];
first.ShouldNotBe(second);
}
private void ThenTheGlobalHandlersAreSingleton()
{
var handlers = _serviceProvider.GetServices<GlobalDelegatingHandler>().ToList();
var first = handlers[0].DelegatingHandler;
handlers = _serviceProvider.GetServices<GlobalDelegatingHandler>().ToList();
var second = handlers[0].DelegatingHandler;
first.ShouldBe(second);
}
private void ThenTheGlobalHandlersAreTransient()
{
var handlers = _serviceProvider.GetServices<GlobalDelegatingHandler>().ToList();
var first = handlers[0].DelegatingHandler;
handlers = _serviceProvider.GetServices<GlobalDelegatingHandler>().ToList();
var second = handlers[0].DelegatingHandler;
first.ShouldNotBe(second);
}
private void WhenISetUpAdministration()
{
_ocelotBuilder.AddAdministration("/administration", "secret");
}
private void WhenISetUpAdministration(Action<IdentityServerAuthenticationOptions> options)
{
_ocelotBuilder.AddAdministration("/administration", options);
}
private void AddTransientGlobalDelegatingHandler<T>() where T : DelegatingHandler
{
_ocelotBuilder.AddTransientDelegatingHandler<T>(true);
}
private void AddSpecificTransientDelegatingHandler<T>() where T : DelegatingHandler
{
_ocelotBuilder.AddTransientDelegatingHandler<T>();
}
private void ThenTheCorrectAdminPathIsRegitered() private void ThenTheCorrectAdminPathIsRegitered()
{ {
_serviceProvider = _services.BuildServiceProvider(); _serviceProvider = _services.BuildServiceProvider();
@ -163,6 +241,14 @@ namespace Ocelot.UnitTests.DependencyInjection
} }
private void ThenTheProviderIsRegisteredAndReturnsHandlers<TOne, TWo>() private void ThenTheProviderIsRegisteredAndReturnsHandlers<TOne, TWo>()
{
_serviceProvider = _services.BuildServiceProvider();
var handlers = _serviceProvider.GetServices<GlobalDelegatingHandler>().ToList();
handlers[0].DelegatingHandler.ShouldBeOfType<TOne>();
handlers[1].DelegatingHandler.ShouldBeOfType<TWo>();
}
private void ThenTheProviderIsRegisteredAndReturnsSpecificHandlers<TOne, TWo>()
{ {
_serviceProvider = _services.BuildServiceProvider(); _serviceProvider = _services.BuildServiceProvider();
var handlers = _serviceProvider.GetServices<DelegatingHandler>().ToList(); var handlers = _serviceProvider.GetServices<DelegatingHandler>().ToList();
@ -174,14 +260,18 @@ namespace Ocelot.UnitTests.DependencyInjection
{ {
var outputCache = _services.Single(x => x.ServiceType == typeof(IOcelotCache<CachedResponse>)); var outputCache = _services.Single(x => x.ServiceType == typeof(IOcelotCache<CachedResponse>));
var outputCacheManager = _services.Single(x => x.ServiceType == typeof(ICacheManager<CachedResponse>)); var outputCacheManager = _services.Single(x => x.ServiceType == typeof(ICacheManager<CachedResponse>));
var thing = (CacheManager.Core.ICacheManager<CachedResponse>)outputCacheManager.ImplementationInstance; var instance = (ICacheManager<CachedResponse>)outputCacheManager.ImplementationInstance;
thing.Configuration.MaxRetries.ShouldBe(_maxRetries);
var ocelotConfigCache = _services.Single(x => x.ServiceType == typeof(IOcelotCache<IOcelotConfiguration>)); var ocelotConfigCache = _services.Single(x => x.ServiceType == typeof(IOcelotCache<IOcelotConfiguration>));
var ocelotConfigCacheManager = _services.Single(x => x.ServiceType == typeof(ICacheManager<IOcelotConfiguration>)); var ocelotConfigCacheManager = _services.Single(x => x.ServiceType == typeof(ICacheManager<IOcelotConfiguration>));
var fileConfigCache = _services.Single(x => x.ServiceType == typeof(IOcelotCache<FileConfiguration>)); var fileConfigCache = _services.Single(x => x.ServiceType == typeof(IOcelotCache<FileConfiguration>));
var fileConfigCacheManager = _services.Single(x => x.ServiceType == typeof(ICacheManager<FileConfiguration>)); var fileConfigCacheManager = _services.Single(x => x.ServiceType == typeof(ICacheManager<FileConfiguration>));
instance.Configuration.MaxRetries.ShouldBe(_maxRetries);
outputCache.ShouldNotBeNull();
ocelotConfigCache.ShouldNotBeNull();
ocelotConfigCacheManager.ShouldNotBeNull();
fileConfigCache.ShouldNotBeNull();
fileConfigCacheManager.ShouldNotBeNull();
} }
private void WhenISetUpConsul() private void WhenISetUpConsul()
@ -208,9 +298,14 @@ namespace Ocelot.UnitTests.DependencyInjection
} }
} }
private void AddDelegate<T>() where T : DelegatingHandler private void AddGlobalDelegatingHandler<T>() where T : DelegatingHandler
{ {
_ocelotBuilder.AddDelegatingHandler<T>(); _ocelotBuilder.AddSingletonDelegatingHandler<T>(true);
}
private void AddSpecificDelegatingHandler<T>() where T : DelegatingHandler
{
_ocelotBuilder.AddSingletonDelegatingHandler<T>();
} }
private void ThenAnOcelotBuilderIsReturned() private void ThenAnOcelotBuilderIsReturned()
@ -281,6 +376,7 @@ namespace Ocelot.UnitTests.DependencyInjection
{ {
_serviceProvider = _services.BuildServiceProvider(); _serviceProvider = _services.BuildServiceProvider();
var logger = _serviceProvider.GetService<IFileConfigurationSetter>(); var logger = _serviceProvider.GetService<IFileConfigurationSetter>();
logger.ShouldNotBeNull();
} }
catch (Exception e) catch (Exception e)
{ {
@ -293,6 +389,7 @@ namespace Ocelot.UnitTests.DependencyInjection
try try
{ {
var tracingHandler = _serviceProvider.GetService<OcelotHttpTracingHandler>(); var tracingHandler = _serviceProvider.GetService<OcelotHttpTracingHandler>();
tracingHandler.ShouldNotBeNull();
} }
catch (Exception e) catch (Exception e)
{ {

View File

@ -19,18 +19,179 @@ namespace Ocelot.UnitTests.Requester
public class DelegatingHandlerHandlerProviderFactoryTests public class DelegatingHandlerHandlerProviderFactoryTests
{ {
private DelegatingHandlerHandlerFactory _factory; private DelegatingHandlerHandlerFactory _factory;
private Mock<IOcelotLoggerFactory> _loggerFactory; private readonly Mock<IOcelotLoggerFactory> _loggerFactory;
private DownstreamReRoute _request; private DownstreamReRoute _request;
private Response<List<Func<DelegatingHandler>>> _provider; private Response<List<Func<DelegatingHandler>>> _result;
private readonly Mock<IQosProviderHouse> _qosProviderHouse; private readonly Mock<IQosProviderHouse> _qosProviderHouse;
private readonly Mock<ITracingHandlerFactory> _tracingFactory; private readonly Mock<ITracingHandlerFactory> _tracingFactory;
private IServiceProvider _serviceProvider; private IServiceProvider _serviceProvider;
private readonly IServiceCollection _services;
public DelegatingHandlerHandlerProviderFactoryTests() public DelegatingHandlerHandlerProviderFactoryTests()
{ {
_tracingFactory = new Mock<ITracingHandlerFactory>(); _tracingFactory = new Mock<ITracingHandlerFactory>();
_qosProviderHouse = new Mock<IQosProviderHouse>(); _qosProviderHouse = new Mock<IQosProviderHouse>();
_loggerFactory = new Mock<IOcelotLoggerFactory>(); _loggerFactory = new Mock<IOcelotLoggerFactory>();
_services = new ServiceCollection();
}
[Fact]
public void should_follow_ordering_add_specifics()
{
var reRoute = new DownstreamReRouteBuilder().WithIsQos(true)
.WithHttpHandlerOptions(new HttpHandlerOptions(true, true, true))
.WithDelegatingHandlers(new List<string>
{
"FakeDelegatingHandler",
"FakeDelegatingHandlerTwo"
})
.WithReRouteKey("")
.Build();
this.Given(x => GivenTheFollowingRequest(reRoute))
.And(x => GivenTheTracingFactoryReturns())
.And(x => GivenTheQosProviderHouseReturns(new OkResponse<IQoSProvider>(It.IsAny<PollyQoSProvider>())))
.And(x => GivenTheServiceProviderReturnsGlobalDelegatingHandlers<FakeDelegatingHandlerThree, FakeDelegatingHandlerFour>())
.And(x => GivenTheServiceProviderReturnsSpecificDelegatingHandlers<FakeDelegatingHandler, FakeDelegatingHandlerTwo>())
.When(x => WhenIGet())
.Then(x => ThenThereIsDelegatesInProvider(6))
.And(x => ThenHandlerAtPositionIs<FakeDelegatingHandlerThree>(0))
.And(x => ThenHandlerAtPositionIs<FakeDelegatingHandlerFour>(1))
.And(x => ThenHandlerAtPositionIs<FakeDelegatingHandler>(2))
.And(x => ThenHandlerAtPositionIs<FakeDelegatingHandlerTwo>(3))
.And(x => ThenHandlerAtPositionIs<FakeTracingHandler>(4))
.And(x => ThenHandlerAtPositionIs<PollyCircuitBreakingDelegatingHandler>(5))
.BDDfy();
}
[Fact]
public void should_follow_ordering_order_specifics_and_globals()
{
var reRoute = new DownstreamReRouteBuilder().WithIsQos(true)
.WithHttpHandlerOptions(new HttpHandlerOptions(true, true, true))
.WithDelegatingHandlers(new List<string>
{
"FakeDelegatingHandlerTwo",
"FakeDelegatingHandler",
"FakeDelegatingHandlerFour"
})
.WithReRouteKey("")
.Build();
this.Given(x => GivenTheFollowingRequest(reRoute))
.And(x => GivenTheTracingFactoryReturns())
.And(x => GivenTheQosProviderHouseReturns(new OkResponse<IQoSProvider>(It.IsAny<PollyQoSProvider>())))
.And(x => GivenTheServiceProviderReturnsGlobalDelegatingHandlers<FakeDelegatingHandlerFour, FakeDelegatingHandlerThree>())
.And(x => GivenTheServiceProviderReturnsSpecificDelegatingHandlers<FakeDelegatingHandler, FakeDelegatingHandlerTwo>())
.When(x => WhenIGet())
.Then(x => ThenThereIsDelegatesInProvider(6))
.And(x => ThenHandlerAtPositionIs<FakeDelegatingHandlerThree>(0)) //first because global not in config
.And(x => ThenHandlerAtPositionIs<FakeDelegatingHandlerTwo>(1)) //first from config
.And(x => ThenHandlerAtPositionIs<FakeDelegatingHandler>(2)) //second from config
.And(x => ThenHandlerAtPositionIs<FakeDelegatingHandlerFour>(3)) //third from config (global)
.And(x => ThenHandlerAtPositionIs<FakeTracingHandler>(4))
.And(x => ThenHandlerAtPositionIs<PollyCircuitBreakingDelegatingHandler>(5))
.BDDfy();
}
[Fact]
public void should_follow_ordering_order_specifics()
{
var reRoute = new DownstreamReRouteBuilder().WithIsQos(true)
.WithHttpHandlerOptions(new HttpHandlerOptions(true, true, true))
.WithDelegatingHandlers(new List<string>
{
"FakeDelegatingHandlerTwo",
"FakeDelegatingHandler"
})
.WithReRouteKey("")
.Build();
this.Given(x => GivenTheFollowingRequest(reRoute))
.And(x => GivenTheTracingFactoryReturns())
.And(x => GivenTheQosProviderHouseReturns(new OkResponse<IQoSProvider>(It.IsAny<PollyQoSProvider>())))
.And(x => GivenTheServiceProviderReturnsGlobalDelegatingHandlers<FakeDelegatingHandlerThree, FakeDelegatingHandlerFour>())
.And(x => GivenTheServiceProviderReturnsSpecificDelegatingHandlers<FakeDelegatingHandler, FakeDelegatingHandlerTwo>())
.When(x => WhenIGet())
.Then(x => ThenThereIsDelegatesInProvider(6))
.And(x => ThenHandlerAtPositionIs<FakeDelegatingHandlerThree>(0))
.And(x => ThenHandlerAtPositionIs<FakeDelegatingHandlerFour>(1))
.And(x => ThenHandlerAtPositionIs<FakeDelegatingHandlerTwo>(2))
.And(x => ThenHandlerAtPositionIs<FakeDelegatingHandler>(3))
.And(x => ThenHandlerAtPositionIs<FakeTracingHandler>(4))
.And(x => ThenHandlerAtPositionIs<PollyCircuitBreakingDelegatingHandler>(5))
.BDDfy();
}
[Fact]
public void should_follow_ordering_order_and_only_add_specifics_in_config()
{
var reRoute = new DownstreamReRouteBuilder().WithIsQos(true)
.WithHttpHandlerOptions(new HttpHandlerOptions(true, true, true))
.WithDelegatingHandlers(new List<string>
{
"FakeDelegatingHandler",
})
.WithReRouteKey("")
.Build();
this.Given(x => GivenTheFollowingRequest(reRoute))
.And(x => GivenTheTracingFactoryReturns())
.And(x => GivenTheQosProviderHouseReturns(new OkResponse<IQoSProvider>(It.IsAny<PollyQoSProvider>())))
.And(x => GivenTheServiceProviderReturnsGlobalDelegatingHandlers<FakeDelegatingHandlerThree, FakeDelegatingHandlerFour>())
.And(x => GivenTheServiceProviderReturnsSpecificDelegatingHandlers<FakeDelegatingHandler, FakeDelegatingHandlerTwo>())
.When(x => WhenIGet())
.Then(x => ThenThereIsDelegatesInProvider(5))
.And(x => ThenHandlerAtPositionIs<FakeDelegatingHandlerThree>(0))
.And(x => ThenHandlerAtPositionIs<FakeDelegatingHandlerFour>(1))
.And(x => ThenHandlerAtPositionIs<FakeDelegatingHandler>(2))
.And(x => ThenHandlerAtPositionIs<FakeTracingHandler>(3))
.And(x => ThenHandlerAtPositionIs<PollyCircuitBreakingDelegatingHandler>(4))
.BDDfy();
}
[Fact]
public void should_follow_ordering_dont_add_specifics()
{
var reRoute = new DownstreamReRouteBuilder().WithIsQos(true)
.WithHttpHandlerOptions(new HttpHandlerOptions(true, true, true))
.WithReRouteKey("")
.Build();
this.Given(x => GivenTheFollowingRequest(reRoute))
.And(x => GivenTheTracingFactoryReturns())
.And(x => GivenTheQosProviderHouseReturns(new OkResponse<IQoSProvider>(It.IsAny<PollyQoSProvider>())))
.And(x => GivenTheServiceProviderReturnsGlobalDelegatingHandlers<FakeDelegatingHandler, FakeDelegatingHandlerTwo>())
.And(x => GivenTheServiceProviderReturnsSpecificDelegatingHandlers<FakeDelegatingHandler, FakeDelegatingHandlerTwo>())
.When(x => WhenIGet())
.Then(x => ThenThereIsDelegatesInProvider(4))
.And(x => ThenHandlerAtPositionIs<FakeDelegatingHandler>(0))
.And(x => ThenHandlerAtPositionIs<FakeDelegatingHandlerTwo>(1))
.And(x => ThenHandlerAtPositionIs<FakeTracingHandler>(2))
.And(x => ThenHandlerAtPositionIs<PollyCircuitBreakingDelegatingHandler>(3))
.BDDfy();
}
[Fact]
public void should_apply_re_route_specific()
{
var reRoute = new DownstreamReRouteBuilder()
.WithHttpHandlerOptions(new HttpHandlerOptions(true, true, false))
.WithDelegatingHandlers(new List<string>
{
"FakeDelegatingHandler",
"FakeDelegatingHandlerTwo"
})
.WithReRouteKey("")
.Build();
this.Given(x => GivenTheFollowingRequest(reRoute))
.And(x => GivenTheQosProviderHouseReturns(new OkResponse<IQoSProvider>(It.IsAny<PollyQoSProvider>())))
.And(x => GivenTheServiceProviderReturnsSpecificDelegatingHandlers<FakeDelegatingHandler, FakeDelegatingHandlerTwo>())
.When(x => WhenIGet())
.Then(x => ThenThereIsDelegatesInProvider(2))
.And(x => ThenTheDelegatesAreAddedCorrectly())
.BDDfy();
} }
[Fact] [Fact]
@ -41,7 +202,7 @@ namespace Ocelot.UnitTests.Requester
this.Given(x => GivenTheFollowingRequest(reRoute)) this.Given(x => GivenTheFollowingRequest(reRoute))
.And(x => GivenTheQosProviderHouseReturns(new OkResponse<IQoSProvider>(It.IsAny<PollyQoSProvider>()))) .And(x => GivenTheQosProviderHouseReturns(new OkResponse<IQoSProvider>(It.IsAny<PollyQoSProvider>())))
.And(x => GivenTheServiceProviderReturns<FakeDelegatingHandler, FakeDelegatingHandlerTwo>()) .And(x => GivenTheServiceProviderReturnsGlobalDelegatingHandlers<FakeDelegatingHandler, FakeDelegatingHandlerTwo>())
.When(x => WhenIGet()) .When(x => WhenIGet())
.Then(x => ThenThereIsDelegatesInProvider(3)) .Then(x => ThenThereIsDelegatesInProvider(3))
.And(x => ThenTheDelegatesAreAddedCorrectly()) .And(x => ThenTheDelegatesAreAddedCorrectly())
@ -91,30 +252,58 @@ namespace Ocelot.UnitTests.Requester
.BDDfy(); .BDDfy();
} }
private void GivenTheServiceProviderReturns<TOne, TTwo>() private void ThenHandlerAtPositionIs<T>(int pos)
where T : DelegatingHandler
{
var delegates = _result.Data;
var del = delegates[pos].Invoke();
del.ShouldBeOfType<T>();
}
private void GivenTheTracingFactoryReturns()
{
_tracingFactory
.Setup(x => x.Get())
.Returns(new FakeTracingHandler());
}
private void GivenTheServiceProviderReturnsGlobalDelegatingHandlers<TOne, TTwo>()
where TOne : DelegatingHandler where TOne : DelegatingHandler
where TTwo : DelegatingHandler where TTwo : DelegatingHandler
{ {
IServiceCollection services = new ServiceCollection(); _services.AddTransient<TOne>();
services.AddSingleton<DelegatingHandler, TOne>(); _services.AddTransient<GlobalDelegatingHandler>(s => {
services.AddSingleton<DelegatingHandler, TTwo>(); var service = s.GetService<TOne>();
_serviceProvider = services.BuildServiceProvider(); return new GlobalDelegatingHandler(service);
});
_services.AddTransient<TTwo>();
_services.AddTransient<GlobalDelegatingHandler>(s => {
var service = s.GetService<TTwo>();
return new GlobalDelegatingHandler(service);
});
}
private void GivenTheServiceProviderReturnsSpecificDelegatingHandlers<TOne, TTwo>()
where TOne : DelegatingHandler
where TTwo : DelegatingHandler
{
_services.AddTransient<DelegatingHandler, TOne>();
_services.AddTransient<DelegatingHandler, TTwo>();
} }
private void GivenTheServiceProviderReturnsNothing() private void GivenTheServiceProviderReturnsNothing()
{ {
IServiceCollection services = new ServiceCollection(); _serviceProvider = _services.BuildServiceProvider();
_serviceProvider = services.BuildServiceProvider();
} }
private void ThenAnErrorIsReturned() private void ThenAnErrorIsReturned()
{ {
_provider.IsError.ShouldBeTrue(); _result.IsError.ShouldBeTrue();
} }
private void ThenTheDelegatesAreAddedCorrectly() private void ThenTheDelegatesAreAddedCorrectly()
{ {
var delegates = _provider.Data; var delegates = _result.Data;
var del = delegates[0].Invoke(); var del = delegates[0].Invoke();
var handler = (FakeDelegatingHandler) del; var handler = (FakeDelegatingHandler) del;
@ -134,15 +323,15 @@ namespace Ocelot.UnitTests.Requester
private void ThenItIsPolly(int i) private void ThenItIsPolly(int i)
{ {
var delegates = _provider.Data; var delegates = _result.Data;
var del = delegates[i].Invoke(); var del = delegates[i].Invoke();
del.ShouldBeOfType<PollyCircuitBreakingDelegatingHandler>(); del.ShouldBeOfType<PollyCircuitBreakingDelegatingHandler>();
} }
private void ThenThereIsDelegatesInProvider(int count) private void ThenThereIsDelegatesInProvider(int count)
{ {
_provider.ShouldNotBeNull(); _result.ShouldNotBeNull();
_provider.Data.Count.ShouldBe(count); _result.Data.Count.ShouldBe(count);
} }
private void GivenTheFollowingRequest(DownstreamReRoute request) private void GivenTheFollowingRequest(DownstreamReRoute request)
@ -152,14 +341,19 @@ namespace Ocelot.UnitTests.Requester
private void WhenIGet() private void WhenIGet()
{ {
_serviceProvider = _services.BuildServiceProvider();
_factory = new DelegatingHandlerHandlerFactory(_loggerFactory.Object, _tracingFactory.Object, _qosProviderHouse.Object, _serviceProvider); _factory = new DelegatingHandlerHandlerFactory(_loggerFactory.Object, _tracingFactory.Object, _qosProviderHouse.Object, _serviceProvider);
_provider = _factory.Get(_request); _result = _factory.Get(_request);
} }
private void ThenNoDelegatesAreInTheProvider() private void ThenNoDelegatesAreInTheProvider()
{ {
_provider.ShouldNotBeNull(); _result.ShouldNotBeNull();
_provider.Data.Count.ShouldBe(0); _result.Data.Count.ShouldBe(0);
} }
} }
internal class FakeTracingHandler : DelegatingHandler, ITracingHandler
{
}
} }

View File

@ -28,6 +28,42 @@ namespace Ocelot.UnitTests.Requester
} }
} }
public class FakeDelegatingHandlerThree : DelegatingHandler
{
public FakeDelegatingHandlerThree()
{
Order = 3;
}
public int Order { get; private set; }
public DateTime TimeCalled { get; private set; }
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
TimeCalled = DateTime.Now;
return Task.FromResult(new HttpResponseMessage());
}
}
public class FakeDelegatingHandlerFour : DelegatingHandler
{
public FakeDelegatingHandlerFour()
{
Order = 4;
}
public int Order { get; private set; }
public DateTime TimeCalled { get; private set; }
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
TimeCalled = DateTime.Now;
return Task.FromResult(new HttpResponseMessage());
}
}
public class FakeDelegatingHandlerTwo : DelegatingHandler public class FakeDelegatingHandlerTwo : DelegatingHandler
{ {
public FakeDelegatingHandlerTwo() public FakeDelegatingHandlerTwo()
@ -36,12 +72,13 @@ namespace Ocelot.UnitTests.Requester
} }
public int Order {get;private set;} public int Order {get;private set;}
public DateTime TimeCalled {get;private set;} public DateTime TimeCalled {get;private set;}
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{ {
TimeCalled = DateTime.Now; TimeCalled = DateTime.Now;
return new HttpResponseMessage(); return Task.FromResult(new HttpResponseMessage());
} }
} }
} }