Merge pull request #42 from ThreeMammals/develop

merge newest code
This commit is contained in:
geffzhang 2018-03-12 21:55:44 +08:00 committed by GitHub
commit 1a5687aca2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
61 changed files with 2113 additions and 843 deletions

View File

@ -1,74 +1,10 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<RuleSet Name="Rules for StyleCop.Analyzers" Description="Code analysis rules for StyleCop.Analyzers.csproj." ToolsVersion="14.0"> <RuleSet Name="Rules for StyleCop.Analyzers" Description="Code analysis rules for StyleCop.Analyzers.csproj." ToolsVersion="15.0">
<Rules AnalyzerId="AsyncUsageAnalyzers" RuleNamespace="AsyncUsageAnalyzers"> <Rules AnalyzerId="AsyncUsageAnalyzers" RuleNamespace="AsyncUsageAnalyzers">
<Rule Id="AvoidAsyncSuffix" Action="None" /> <Rule Id="AvoidAsyncSuffix" Action="None" />
<Rule Id="AvoidAsyncVoid" Action="None" /> <Rule Id="AvoidAsyncVoid" Action="Error" />
<Rule Id="UseAsyncSuffix" Action="None" /> <Rule Id="UseAsyncSuffix" Action="None" />
<Rule Id="UseConfigureAwait" Action="None" /> <Rule Id="UseConfigureAwait" Action="Error" />
</Rules>
<Rules AnalyzerId="Microsoft.Analyzers.ManagedCodeAnalysis" RuleNamespace="Microsoft.Rules.Managed">
<Rule Id="CA1001" Action="None" />
<Rule Id="CA1009" Action="None" />
<Rule Id="CA1016" Action="None" />
<Rule Id="CA1033" Action="None" />
<Rule Id="CA1049" Action="None" />
<Rule Id="CA1060" Action="None" />
<Rule Id="CA1061" Action="None" />
<Rule Id="CA1063" Action="None" />
<Rule Id="CA1065" Action="None" />
<Rule Id="CA1301" Action="None" />
<Rule Id="CA1400" Action="None" />
<Rule Id="CA1401" Action="None" />
<Rule Id="CA1403" Action="None" />
<Rule Id="CA1404" Action="None" />
<Rule Id="CA1405" Action="None" />
<Rule Id="CA1410" Action="None" />
<Rule Id="CA1415" Action="None" />
<Rule Id="CA1821" Action="None" />
<Rule Id="CA1900" Action="None" />
<Rule Id="CA1901" Action="None" />
<Rule Id="CA2002" Action="None" />
<Rule Id="CA2100" Action="None" />
<Rule Id="CA2101" Action="None" />
<Rule Id="CA2108" Action="None" />
<Rule Id="CA2111" Action="None" />
<Rule Id="CA2112" Action="None" />
<Rule Id="CA2114" Action="None" />
<Rule Id="CA2116" Action="None" />
<Rule Id="CA2117" Action="None" />
<Rule Id="CA2122" Action="None" />
<Rule Id="CA2123" Action="None" />
<Rule Id="CA2124" Action="None" />
<Rule Id="CA2126" Action="None" />
<Rule Id="CA2131" Action="None" />
<Rule Id="CA2132" Action="None" />
<Rule Id="CA2133" Action="None" />
<Rule Id="CA2134" Action="None" />
<Rule Id="CA2137" Action="None" />
<Rule Id="CA2138" Action="None" />
<Rule Id="CA2140" Action="None" />
<Rule Id="CA2141" Action="None" />
<Rule Id="CA2146" Action="None" />
<Rule Id="CA2147" Action="None" />
<Rule Id="CA2149" Action="None" />
<Rule Id="CA2200" Action="None" />
<Rule Id="CA2202" Action="None" />
<Rule Id="CA2207" Action="None" />
<Rule Id="CA2212" Action="None" />
<Rule Id="CA2213" Action="None" />
<Rule Id="CA2214" Action="None" />
<Rule Id="CA2216" Action="None" />
<Rule Id="CA2220" Action="None" />
<Rule Id="CA2229" Action="None" />
<Rule Id="CA2231" Action="None" />
<Rule Id="CA2232" Action="None" />
<Rule Id="CA2235" Action="None" />
<Rule Id="CA2236" Action="None" />
<Rule Id="CA2237" Action="None" />
<Rule Id="CA2238" Action="None" />
<Rule Id="CA2240" Action="None" />
<Rule Id="CA2241" Action="None" />
<Rule Id="CA2242" Action="None" />
</Rules> </Rules>
<Rules AnalyzerId="Microsoft.CodeAnalysis.CSharp.Features" RuleNamespace="Microsoft.CodeAnalysis.CSharp.Features"> <Rules AnalyzerId="Microsoft.CodeAnalysis.CSharp.Features" RuleNamespace="Microsoft.CodeAnalysis.CSharp.Features">
<Rule Id="IDE0003" Action="None" /> <Rule Id="IDE0003" Action="None" />
@ -87,12 +23,12 @@
<Rule Id="SA1013" Action="None" /> <Rule Id="SA1013" Action="None" />
<Rule Id="SA1015" Action="None" /> <Rule Id="SA1015" Action="None" />
<Rule Id="SA1016" Action="None" /> <Rule Id="SA1016" Action="None" />
<Rule Id="SA1021" Action="None" /> <Rule Id="SA1021" Action="Error" />
<Rule Id="SA1022" Action="None" /> <Rule Id="SA1022" Action="Error" />
<Rule Id="SA1024" Action="None" /> <Rule Id="SA1024" Action="None" />
<Rule Id="SA1026" Action="None" /> <Rule Id="SA1026" Action="None" />
<Rule Id="SA1028" Action="None" /> <Rule Id="SA1028" Action="None" />
<Rule Id="SA1100" Action="None" /> <Rule Id="SA1100" Action="Error" />
<Rule Id="SA1101" Action="None" /> <Rule Id="SA1101" Action="None" />
<Rule Id="SA1106" Action="None" /> <Rule Id="SA1106" Action="None" />
<Rule Id="SA1111" Action="None" /> <Rule Id="SA1111" Action="None" />
@ -100,11 +36,11 @@
<Rule Id="SA1116" Action="None" /> <Rule Id="SA1116" Action="None" />
<Rule Id="SA1117" Action="None" /> <Rule Id="SA1117" Action="None" />
<Rule Id="SA1118" Action="None" /> <Rule Id="SA1118" Action="None" />
<Rule Id="SA1119" Action="None" /> <Rule Id="SA1119" Action="Error" />
<Rule Id="SA1121" Action="None" /> <Rule Id="SA1121" Action="Error" />
<Rule Id="SA1122" Action="None" /> <Rule Id="SA1122" Action="None" />
<Rule Id="SA1128" Action="None" /> <Rule Id="SA1128" Action="None" />
<Rule Id="SA1133" Action="None" /> <Rule Id="SA1133" Action="Error" />
<Rule Id="SA1200" Action="None" /> <Rule Id="SA1200" Action="None" />
<Rule Id="SA1201" Action="None" /> <Rule Id="SA1201" Action="None" />
<Rule Id="SA1202" Action="None" /> <Rule Id="SA1202" Action="None" />
@ -116,37 +52,37 @@
<Rule Id="SA1214" Action="None" /> <Rule Id="SA1214" Action="None" />
<Rule Id="SA1216" Action="None" /> <Rule Id="SA1216" Action="None" />
<Rule Id="SA1300" Action="None" /> <Rule Id="SA1300" Action="None" />
<Rule Id="SA1302" Action="None" /> <Rule Id="SA1302" Action="Error" />
<Rule Id="SA1303" Action="None" /> <Rule Id="SA1303" Action="None" />
<Rule Id="SA1304" Action="None" /> <Rule Id="SA1304" Action="Error" />
<Rule Id="SA1305" Action="None" /> <Rule Id="SA1305" Action="None" />
<Rule Id="SA1309" Action="None" /> <Rule Id="SA1309" Action="None" />
<Rule Id="SA1310" Action="None" /> <Rule Id="SA1310" Action="None" />
<Rule Id="SA1311" Action="None" /> <Rule Id="SA1311" Action="Error" />
<Rule Id="SA1400" Action="None" /> <Rule Id="SA1400" Action="None" />
<Rule Id="SA1401" Action="None" /> <Rule Id="SA1401" Action="None" />
<Rule Id="SA1402" Action="None" /> <Rule Id="SA1402" Action="None" />
<Rule Id="SA1403" Action="None" /> <Rule Id="SA1403" Action="Error" />
<Rule Id="SA1404" Action="None" /> <Rule Id="SA1404" Action="Error" />
<Rule Id="SA1405" Action="None" /> <Rule Id="SA1405" Action="Error" />
<Rule Id="SA1406" Action="None" /> <Rule Id="SA1406" Action="Error" />
<Rule Id="SA1407" Action="None" /> <Rule Id="SA1407" Action="Error" />
<Rule Id="SA1408" Action="None" /> <Rule Id="SA1408" Action="None" />
<Rule Id="SA1410" Action="None" /> <Rule Id="SA1410" Action="Error" />
<Rule Id="SA1411" Action="None" /> <Rule Id="SA1411" Action="Error" />
<Rule Id="SA1412" Action="None" /> <Rule Id="SA1412" Action="None" />
<Rule Id="SA1500" Action="None" /> <Rule Id="SA1500" Action="None" />
<Rule Id="SA1502" Action="None" /> <Rule Id="SA1502" Action="None" />
<Rule Id="SA1516" Action="None" /> <Rule Id="SA1516" Action="None" />
<Rule Id="SA1600" Action="None" /> <Rule Id="SA1600" Action="None" />
<Rule Id="SA1603" Action="None" /> <Rule Id="SA1603" Action="Error" />
<Rule Id="SA1609" Action="None" /> <Rule Id="SA1609" Action="Error" />
<Rule Id="SA1611" Action="None" />
<Rule Id="SA1623" Action="None" /> <Rule Id="SA1623" Action="None" />
<Rule Id="SA1633" Action="None" /> <Rule Id="SA1633" Action="None" />
<Rule Id="SA1636" Action="None" /> <Rule Id="SA1636" Action="Error" />
<Rule Id="SA1642" Action="None" /> <Rule Id="SA1642" Action="Error" />
<Rule Id="SA1643" Action="None" /> <Rule Id="SA1643" Action="Error" />
<Rule Id="SA1652" Action="None" /> <Rule Id="SA1652" Action="None" />
</Rules> </Rules>
</RuleSet> </RuleSet>

View File

@ -1,33 +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 #264 <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.
.. code-block:: csharp 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
asp.net core container so you can inject any other services you have registered into the constructor of your handler.
services.AddOcelot()
.AddDelegatingHandler(() => new FakeHandler())
.AddDelegatingHandler(() => new FakeHandler());
Or for singleton like behaviour..
.. code-block:: csharp
var handlerOne = new FakeHandler();
var handlerTwo = new FakeHandler();
services.AddOcelot()
.AddDelegatingHandler(() => handlerOne)
.AddDelegatingHandler(() => handlerTwo);
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.
In order to create a class that can be used a delegating handler it must look as follows
.. code-block:: csharp .. code-block:: csharp
@ -40,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

@ -6,3 +6,27 @@ Ocelot does not support...
* Chunked Encoding - Ocelot will always get the body size and return Content-Length header. Sorry if this doesn't work for your use case! * Chunked Encoding - Ocelot will always get the body size and return Content-Length header. Sorry if this doesn't work for your use case!
* Fowarding a host header - The host header that you send to Ocelot will not be forwarded to the downstream service. Obviously this would break everything :( * Fowarding a host header - The host header that you send to Ocelot will not be forwarded to the downstream service. Obviously this would break everything :(
* Swagger - I have looked multiple times at building swagger.json out of the Ocelot configuration.json but it doesnt fit into the vision I have for Ocelot. If you would like to have Swagger in Ocelot then you must roll your own swagger.json and do the following in your Startup.cs or Program.cs. The code sample below registers a piece of middleware that loads your hand rolled swagger.json and returns it on /swagger/v1/swagger.json. It then registers the SwaggerUI middleware from Swashbuckle.AspNetCore
.. code-block:: csharp
app.Map("/swagger/v1/swagger.json", b =>
{
b.Run(async x => {
var json = File.ReadAllText("swagger.json");
await x.Response.WriteAsync(json);
});
});
app.UseSwaggerUI(c =>
{
c.SwaggerEndpoint("/swagger/v1/swagger.json", "Ocelot");
});
app.UseOcelot().Wait();
The main reasons why I don't think Swagger makes sense is we already hand roll our definition in configuration.json. If we want people developing against Ocelot to be able to see what routes are available then either share the configuration.json with them (This should be as easy as granting access to a repo etc) or use the Ocelot administration API so that they can query Ocelot for the configuration.
In addition to this many people will configure Ocelot to proxy all traffic like /products/{everything} to there product service and you would not be describing what is actually available if you parsed this and turned it into a Swagger path. Also Ocelot has no concept of the models that the downstream services can return and linking to the above problem the same endpoint can return multiple models. Ocelot does not know what models might be used in POST, PUT etc so it all gets a bit messy and finally the Swashbuckle package doesnt reload swagger.json if it changes during runtime. Ocelot's configuration can change during runtime so the Swagger and Ocelot information would not match. Unless I rolled my own Swagger implementation.
If the user wants something to easily test against the Ocelot API then I suggest using Postman as a simple way to do this. It might even be possible to write something that maps configuration.json to the postman json spec. However I don't intend to do this.

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

@ -18,7 +18,7 @@ namespace Ocelot.Configuration.Repository
_configFilePath = $"{AppContext.BaseDirectory}/configuration{(string.IsNullOrEmpty(hostingEnvironment.EnvironmentName) ? string.Empty : ".")}{hostingEnvironment.EnvironmentName}.json"; _configFilePath = $"{AppContext.BaseDirectory}/configuration{(string.IsNullOrEmpty(hostingEnvironment.EnvironmentName) ? string.Empty : ".")}{hostingEnvironment.EnvironmentName}.json";
} }
public async Task<Response<FileConfiguration>> Get() public Task<Response<FileConfiguration>> Get()
{ {
string jsonConfiguration; string jsonConfiguration;
@ -29,10 +29,10 @@ namespace Ocelot.Configuration.Repository
var fileConfiguration = JsonConvert.DeserializeObject<FileConfiguration>(jsonConfiguration); var fileConfiguration = JsonConvert.DeserializeObject<FileConfiguration>(jsonConfiguration);
return new OkResponse<FileConfiguration>(fileConfiguration); return Task.FromResult<Response<FileConfiguration>>(new OkResponse<FileConfiguration>(fileConfiguration));
} }
public async Task<Response> Set(FileConfiguration fileConfiguration) public Task<Response> Set(FileConfiguration fileConfiguration)
{ {
string jsonConfiguration = JsonConvert.SerializeObject(fileConfiguration); string jsonConfiguration = JsonConvert.SerializeObject(fileConfiguration);
@ -46,7 +46,7 @@ namespace Ocelot.Configuration.Repository
System.IO.File.WriteAllText(_configFilePath, jsonConfiguration); System.IO.File.WriteAllText(_configFilePath, jsonConfiguration);
} }
return new OkResponse(); return Task.FromResult<Response>(new OkResponse());
} }
} }
} }

View File

@ -12,19 +12,19 @@ namespace Ocelot.Configuration.Repository
private IOcelotConfiguration _ocelotConfiguration; private IOcelotConfiguration _ocelotConfiguration;
public async Task<Response<IOcelotConfiguration>> Get() public Task<Response<IOcelotConfiguration>> Get()
{ {
return new OkResponse<IOcelotConfiguration>(_ocelotConfiguration); return Task.FromResult<Response<IOcelotConfiguration>>(new OkResponse<IOcelotConfiguration>(_ocelotConfiguration));
} }
public async Task<Response> AddOrReplace(IOcelotConfiguration ocelotConfiguration) public Task<Response> AddOrReplace(IOcelotConfiguration ocelotConfiguration)
{ {
lock (LockObject) lock (LockObject)
{ {
_ocelotConfiguration = ocelotConfiguration; _ocelotConfiguration = ocelotConfiguration;
} }
return new OkResponse(); return Task.FromResult<Response>(new OkResponse());
} }
} }
} }

View File

@ -9,10 +9,19 @@ namespace Ocelot.DependencyInjection
public interface IOcelotBuilder public interface IOcelotBuilder
{ {
IOcelotBuilder AddStoreOcelotConfigurationInConsul(); IOcelotBuilder AddStoreOcelotConfigurationInConsul();
IOcelotBuilder AddCacheManager(Action<ConfigurationBuilderCachePart> settings); IOcelotBuilder AddCacheManager(Action<ConfigurationBuilderCachePart> settings);
IOcelotBuilder AddOpenTracing(Action<ButterflyOptions> settings); IOcelotBuilder AddOpenTracing(Action<ButterflyOptions> settings);
IOcelotAdministrationBuilder AddAdministration(string path, string secret); IOcelotAdministrationBuilder AddAdministration(string path, string secret);
IOcelotAdministrationBuilder AddAdministration(string path, Action<IdentityServerAuthenticationOptions> configOptions); IOcelotAdministrationBuilder AddAdministration(string path, Action<IdentityServerAuthenticationOptions> configOptions);
IOcelotBuilder AddDelegatingHandler(Func<DelegatingHandler> delegatingHandler);
IOcelotBuilder AddSingletonDelegatingHandler<T>(bool global = false)
where T : DelegatingHandler;
IOcelotBuilder AddTransientDelegatingHandler<T>(bool global = false)
where T : DelegatingHandler;
} }
} }

View File

@ -57,7 +57,6 @@ namespace Ocelot.DependencyInjection
{ {
private readonly IServiceCollection _services; private readonly IServiceCollection _services;
private readonly IConfiguration _configurationRoot; private readonly IConfiguration _configurationRoot;
private readonly IDelegatingHandlerHandlerProvider _provider;
public OcelotBuilder(IServiceCollection services, IConfiguration configurationRoot) public OcelotBuilder(IServiceCollection services, IConfiguration configurationRoot)
{ {
@ -120,8 +119,7 @@ namespace Ocelot.DependencyInjection
_services.TryAddSingleton<IRequestMapper, RequestMapper>(); _services.TryAddSingleton<IRequestMapper, RequestMapper>();
_services.TryAddSingleton<IHttpHandlerOptionsCreator, HttpHandlerOptionsCreator>(); _services.TryAddSingleton<IHttpHandlerOptionsCreator, HttpHandlerOptionsCreator>();
_services.TryAddSingleton<IDownstreamAddressesCreator, DownstreamAddressesCreator>(); _services.TryAddSingleton<IDownstreamAddressesCreator, DownstreamAddressesCreator>();
_services.TryAddSingleton<IDelegatingHandlerHandlerProviderFactory, DelegatingHandlerHandlerProviderFactory>(); _services.TryAddSingleton<IDelegatingHandlerHandlerFactory, DelegatingHandlerHandlerFactory>();
_services.TryAddSingleton<IDelegatingHandlerHandlerHouse, DelegatingHandlerHandlerHouse>();
// see this for why we register this as singleton http://stackoverflow.com/questions/37371264/invalidoperationexception-unable-to-resolve-service-for-type-microsoft-aspnetc // see this for why we register this as singleton http://stackoverflow.com/questions/37371264/invalidoperationexception-unable-to-resolve-service-for-type-microsoft-aspnetc
// could maybe use a scoped data repository // could maybe use a scoped data repository
@ -144,9 +142,6 @@ namespace Ocelot.DependencyInjection
_services.AddWebEncoders(); _services.AddWebEncoders();
_services.AddSingleton<IAdministrationPath>(new NullAdministrationPath()); _services.AddSingleton<IAdministrationPath>(new NullAdministrationPath());
//these get picked out later and added to http request
_provider = new DelegatingHandlerHandlerProvider();
_services.TryAddSingleton<IDelegatingHandlerHandlerProvider>(_provider);
_services.TryAddSingleton<IMultiplexer, Multiplexer>(); _services.TryAddSingleton<IMultiplexer, Multiplexer>();
_services.TryAddSingleton<IResponseAggregator, SimpleJsonResponseAggregator>(); _services.TryAddSingleton<IResponseAggregator, SimpleJsonResponseAggregator>();
_services.AddSingleton<ITracingHandlerFactory, TracingHandlerFactory>(); _services.AddSingleton<ITracingHandlerFactory, TracingHandlerFactory>();
@ -187,9 +182,41 @@ namespace Ocelot.DependencyInjection
return new OcelotAdministrationBuilder(_services, _configurationRoot); return new OcelotAdministrationBuilder(_services, _configurationRoot);
} }
public IOcelotBuilder AddDelegatingHandler(Func<DelegatingHandler> delegatingHandler) public IOcelotBuilder AddSingletonDelegatingHandler<THandler>(bool global = false)
where THandler : DelegatingHandler
{ {
_provider.Add(delegatingHandler); if(global)
{
_services.AddSingleton<THandler>();
_services.AddSingleton<GlobalDelegatingHandler>(s => {
var service = s.GetService<THandler>();
return new GlobalDelegatingHandler(service);
});
}
else
{
_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

@ -47,11 +47,28 @@
var content = new ByteArrayContent(await ToByteArray(request.Body)); var content = new ByteArrayContent(await ToByteArray(request.Body));
content.Headers.TryAddWithoutValidation("Content-Type", new[] {request.ContentType}); content.Headers
.TryAddWithoutValidation("Content-Type", new[] {request.ContentType});
AddHeaderIfExistsOnRequest("Content-Language", content, request);
AddHeaderIfExistsOnRequest("Content-Location", content, request);
AddHeaderIfExistsOnRequest("Content-Range", content, request);
AddHeaderIfExistsOnRequest("Content-MD5", content, request);
AddHeaderIfExistsOnRequest("Content-Disposition", content, request);
AddHeaderIfExistsOnRequest("Content-Encoding", content, request);
return content; return content;
} }
private void AddHeaderIfExistsOnRequest(string key, HttpContent content, HttpRequest request)
{
if(request.Headers.ContainsKey(key))
{
content.Headers
.TryAddWithoutValidation(key, request.Headers[key].ToList());
}
}
private HttpMethod MapMethod(HttpRequest request) private HttpMethod MapMethod(HttpRequest request)
{ {
return new HttpMethod(request.Method); return new HttpMethod(request.Method);

View File

@ -0,0 +1,102 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using Microsoft.Extensions.DependencyInjection;
using Ocelot.Configuration;
using Ocelot.Logging;
using Ocelot.Requester.QoS;
using Ocelot.Responses;
namespace Ocelot.Requester
{
public class DelegatingHandlerHandlerFactory : IDelegatingHandlerHandlerFactory
{
private readonly ITracingHandlerFactory _factory;
private readonly IOcelotLoggerFactory _loggerFactory;
private readonly IQosProviderHouse _qosProviderHouse;
private readonly IServiceProvider _serviceProvider;
public DelegatingHandlerHandlerFactory(IOcelotLoggerFactory loggerFactory,
ITracingHandlerFactory factory,
IQosProviderHouse qosProviderHouse,
IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
_factory = factory;
_loggerFactory = loggerFactory;
_qosProviderHouse = qosProviderHouse;
}
public Response<List<Func<DelegatingHandler>>> Get(DownstreamReRoute request)
{
var globalDelegatingHandlers = _serviceProvider
.GetServices<GlobalDelegatingHandler>()
.ToList();
var reRouteSpecificHandlers = _serviceProvider
.GetServices<DelegatingHandler>()
.ToList();
var handlers = new List<Func<DelegatingHandler>>();
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);
}
}
if (request.HttpHandlerOptions.UseTracing)
{
handlers.Add(() => (DelegatingHandler)_factory.Get());
}
if (request.IsQos)
{
var qosProvider = _qosProviderHouse.Get(request);
if (qosProvider.IsError)
{
return new ErrorResponse<List<Func<DelegatingHandler>>>(qosProvider.Errors);
}
handlers.Add(() => new PollyCircuitBreakingDelegatingHandler(qosProvider.Data, _loggerFactory));
}
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

@ -1,58 +0,0 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using Ocelot.Configuration;
using Ocelot.Errors;
using Ocelot.Responses;
namespace Ocelot.Requester
{
public class DelegatingHandlerHandlerHouse : IDelegatingHandlerHandlerHouse
{
private readonly IDelegatingHandlerHandlerProviderFactory _factory;
private readonly ConcurrentDictionary<string, IDelegatingHandlerHandlerProvider> _housed;
public DelegatingHandlerHandlerHouse(IDelegatingHandlerHandlerProviderFactory factory)
{
_factory = factory;
_housed = new ConcurrentDictionary<string, IDelegatingHandlerHandlerProvider>();
}
public Response<IDelegatingHandlerHandlerProvider> Get(DownstreamReRoute request)
{
try
{
if (_housed.TryGetValue(request.ReRouteKey, out var provider))
{
//todo once day we might need a check here to see if we need to create a new provider
provider = _housed[request.ReRouteKey];
return new OkResponse<IDelegatingHandlerHandlerProvider>(provider);
}
//todo - unit test for this
var providerResponse = _factory.Get(request);
if (providerResponse.IsError)
{
return new ErrorResponse<IDelegatingHandlerHandlerProvider>(providerResponse.Errors);
}
provider = providerResponse.Data;
AddHoused(request.ReRouteKey, provider);
return new OkResponse<IDelegatingHandlerHandlerProvider>(provider);
}
catch (Exception ex)
{
return new ErrorResponse<IDelegatingHandlerHandlerProvider>(new List<Error>()
{
new UnableToFindDelegatingHandlerProviderError($"unabe to find delegating handler provider for {request.ReRouteKey} exception is {ex}")
});
}
}
private void AddHoused(string key, IDelegatingHandlerHandlerProvider provider)
{
_housed.AddOrUpdate(key, provider, (k, v) => provider);
}
}
}

View File

@ -1,28 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
namespace Ocelot.Requester
{
public class DelegatingHandlerHandlerProvider : IDelegatingHandlerHandlerProvider
{
private readonly Dictionary<int, Func<DelegatingHandler>> _handlers;
public DelegatingHandlerHandlerProvider()
{
_handlers = new Dictionary<int, Func<DelegatingHandler>>();
}
public void Add(Func<DelegatingHandler> handler)
{
var key = _handlers.Count == 0 ? 0 : _handlers.Count + 1;
_handlers[key] = handler;
}
public List<Func<DelegatingHandler>> Get()
{
return _handlers.Count > 0 ? _handlers.OrderBy(x => x.Key).Select(x => x.Value).ToList() : new List<Func<DelegatingHandler>>();
}
}
}

View File

@ -1,59 +0,0 @@
using System;
using System.Net.Http;
using Ocelot.Configuration;
using Ocelot.Logging;
using Ocelot.Requester.QoS;
using Ocelot.Responses;
namespace Ocelot.Requester
{
public class DelegatingHandlerHandlerProviderFactory : IDelegatingHandlerHandlerProviderFactory
{
private readonly ITracingHandlerFactory _factory;
private readonly IOcelotLoggerFactory _loggerFactory;
private readonly IDelegatingHandlerHandlerProvider _allRoutesProvider;
private readonly IQosProviderHouse _qosProviderHouse;
public DelegatingHandlerHandlerProviderFactory(IOcelotLoggerFactory loggerFactory,
IDelegatingHandlerHandlerProvider allRoutesProvider,
ITracingHandlerFactory factory,
IQosProviderHouse qosProviderHouse)
{
_factory = factory;
_loggerFactory = loggerFactory;
_allRoutesProvider = allRoutesProvider;
_qosProviderHouse = qosProviderHouse;
}
public Response<IDelegatingHandlerHandlerProvider> Get(DownstreamReRoute request)
{
var handlersAppliedToAll = _allRoutesProvider.Get();
var provider = new DelegatingHandlerHandlerProvider();
foreach (var handler in handlersAppliedToAll)
{
provider.Add(handler);
}
if (request.HttpHandlerOptions.UseTracing)
{
provider.Add(() => (DelegatingHandler)_factory.Get());
}
if (request.IsQos)
{
var qosProvider = _qosProviderHouse.Get(request);
if (qosProvider.IsError)
{
return new ErrorResponse<IDelegatingHandlerHandlerProvider>(qosProvider.Errors);
}
provider.Add(() => new PollyCircuitBreakingDelegatingHandler(qosProvider.Data, _loggerFactory));
}
return new OkResponse<IDelegatingHandlerHandlerProvider>(provider);
}
}
}

View File

@ -6,11 +6,11 @@ namespace Ocelot.Requester
{ {
public class HttpClientBuilder : IHttpClientBuilder public class HttpClientBuilder : IHttpClientBuilder
{ {
private readonly IDelegatingHandlerHandlerHouse _house; private readonly IDelegatingHandlerHandlerFactory _factory;
public HttpClientBuilder(IDelegatingHandlerHandlerHouse house) public HttpClientBuilder(IDelegatingHandlerHandlerFactory house)
{ {
_house = house; _factory = house;
} }
public IHttpClient Create(DownstreamReRoute request) public IHttpClient Create(DownstreamReRoute request)
@ -24,11 +24,9 @@ namespace Ocelot.Requester
private HttpMessageHandler CreateHttpMessageHandler(HttpMessageHandler httpMessageHandler, DownstreamReRoute request) private HttpMessageHandler CreateHttpMessageHandler(HttpMessageHandler httpMessageHandler, DownstreamReRoute request)
{ {
var provider = _house.Get(request);
var handlers = provider.Data.Get();
//todo handle error //todo handle error
var handlers = _factory.Get(request).Data;
handlers handlers
.Select(handler => handler) .Select(handler => handler)
.Reverse() .Reverse()

View File

@ -13,20 +13,20 @@ namespace Ocelot.Requester
{ {
private readonly IHttpClientCache _cacheHandlers; private readonly IHttpClientCache _cacheHandlers;
private readonly IOcelotLogger _logger; private readonly IOcelotLogger _logger;
private readonly IDelegatingHandlerHandlerHouse _house; private readonly IDelegatingHandlerHandlerFactory _factory;
public HttpClientHttpRequester(IOcelotLoggerFactory loggerFactory, public HttpClientHttpRequester(IOcelotLoggerFactory loggerFactory,
IHttpClientCache cacheHandlers, IHttpClientCache cacheHandlers,
IDelegatingHandlerHandlerHouse house) IDelegatingHandlerHandlerFactory house)
{ {
_logger = loggerFactory.CreateLogger<HttpClientHttpRequester>(); _logger = loggerFactory.CreateLogger<HttpClientHttpRequester>();
_cacheHandlers = cacheHandlers; _cacheHandlers = cacheHandlers;
_house = house; _factory = house;
} }
public async Task<Response<HttpResponseMessage>> GetResponse(DownstreamContext request) public async Task<Response<HttpResponseMessage>> GetResponse(DownstreamContext request)
{ {
var builder = new HttpClientBuilder(_house); var builder = new HttpClientBuilder(_factory);
var cacheKey = GetCacheKey(request); var cacheKey = GetCacheKey(request);
@ -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

@ -1,10 +0,0 @@
using Ocelot.Configuration;
using Ocelot.Responses;
namespace Ocelot.Requester
{
public interface IDelegatingHandlerHandlerHouse
{
Response<IDelegatingHandlerHandlerProvider> Get(DownstreamReRoute request);
}
}

View File

@ -1,12 +0,0 @@
using System;
using System.Collections.Generic;
using System.Net.Http;
namespace Ocelot.Requester
{
public interface IDelegatingHandlerHandlerProvider
{
void Add(Func<DelegatingHandler> handler);
List<Func<DelegatingHandler>> Get();
}
}

View File

@ -1,10 +1,13 @@
using System;
using System.Collections.Generic;
using System.Net.Http;
using Ocelot.Configuration; using Ocelot.Configuration;
using Ocelot.Responses; using Ocelot.Responses;
namespace Ocelot.Requester namespace Ocelot.Requester
{ {
public interface IDelegatingHandlerHandlerProviderFactory public interface IDelegatingHandlerHandlerFactory
{ {
Response<IDelegatingHandlerHandlerProvider> Get(DownstreamReRoute request); Response<List<Func<DelegatingHandler>>> Get(DownstreamReRoute request);
} }
} }

View File

@ -1,5 +1,4 @@
using System; using System.Collections.Generic;
using System.Collections.Generic;
using System.Threading.Tasks; using System.Threading.Tasks;
using Ocelot.Values; using Ocelot.Values;
@ -14,16 +13,16 @@ namespace Ocelot.ServiceDiscovery
_configuration = configuration; _configuration = configuration;
} }
public async Task<List<Service>> Get() public Task<List<Service>> Get()
{ {
return new List<Service> return Task.FromResult(new List<Service>
{ {
new Service(_configuration.ServiceName, new Service(_configuration.ServiceName,
new ServiceHostAndPort(_configuration.HostName, _configuration.Port), new ServiceHostAndPort(_configuration.HostName, _configuration.Port),
"doesnt matter with service fabric", "doesnt matter with service fabric",
"doesnt matter with service fabric", "doesnt matter with service fabric",
new List<string>()) new List<string>())
}; });
} }
} }
} }

View File

@ -0,0 +1,117 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Ocelot.Configuration.File;
using Shouldly;
using TestStack.BDDfy;
using Xunit;
namespace Ocelot.AcceptanceTests
{
public class GzipTests : IDisposable
{
private IWebHost _builder;
private readonly Steps _steps;
public GzipTests()
{
_steps = new Steps();
}
[Fact]
public void should_return_response_200_with_simple_url()
{
var configuration = new FileConfiguration
{
ReRoutes = new List<FileReRoute>
{
new FileReRoute
{
DownstreamPathTemplate = "/",
DownstreamScheme = "http",
DownstreamHostAndPorts = new List<FileHostAndPort>
{
new FileHostAndPort
{
Host = "localhost",
Port = 51879,
}
},
UpstreamPathTemplate = "/",
UpstreamHttpMethod = new List<string> { "Post" },
}
}
};
var input = "people";
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51879", "/", 200, "Hello from Laura", "\"people\""))
.And(x => _steps.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunning())
.And(x => _steps.GivenThePostHasGzipContent(input))
.When(x => _steps.WhenIPostUrlOnTheApiGateway("/"))
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
.And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura"))
.BDDfy();
}
private void GivenThereIsAServiceRunningOn(string baseUrl, string basePath, int statusCode, string responseBody, string expected)
{
_builder = new WebHostBuilder()
.UseUrls(baseUrl)
.UseKestrel()
.UseContentRoot(Directory.GetCurrentDirectory())
.UseIISIntegration()
.Configure(app =>
{
app.UsePathBase(basePath);
app.Run(async context =>
{
if(context.Request.Headers.TryGetValue("Content-Encoding", out var contentEncoding))
{
contentEncoding.First().ShouldBe("gzip");
string text = null;
using (var decompress = new GZipStream(context.Request.Body, CompressionMode.Decompress))
{
using (var sr = new StreamReader(decompress)) {
text = sr.ReadToEnd();
}
}
if(text != expected)
{
throw new Exception("not gzipped");
}
context.Response.StatusCode = statusCode;
await context.Response.WriteAsync(responseBody);
}
else
{
context.Response.StatusCode = statusCode;
await context.Response.WriteAsync("downstream path didnt match base path");
}
});
})
.Build();
_builder.Start();
}
public void Dispose()
{
_builder?.Dispose();
_steps.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;
@ -18,7 +17,6 @@ namespace Ocelot.AcceptanceTests
{ {
private IWebHost _builder; private IWebHost _builder;
private readonly Steps _steps; private readonly Steps _steps;
private string _downstreamPath;
public HeaderTests() public HeaderTests()
{ {
@ -221,13 +219,15 @@ namespace Ocelot.AcceptanceTests
.Configure(app => .Configure(app =>
{ {
app.UsePathBase(basePath); app.UsePathBase(basePath);
app.Run(async context => app.Run(context =>
{ {
context.Response.OnStarting(() => { context.Response.OnStarting(() => {
context.Response.Headers.Add(headerKey, headerValue); context.Response.Headers.Add(headerKey, headerValue);
context.Response.StatusCode = statusCode; context.Response.StatusCode = statusCode;
return Task.CompletedTask; return Task.CompletedTask;
}); });
return Task.CompletedTask;
}); });
}) })
.Build(); .Build();
@ -235,11 +235,6 @@ namespace Ocelot.AcceptanceTests
_builder.Start(); _builder.Start();
} }
internal void ThenTheDownstreamUrlPathShouldBe(string expectedDownstreamPath)
{
_downstreamPath.ShouldBe(expectedDownstreamPath);
}
public void Dispose() public void Dispose()
{ {
_builder?.Dispose(); _builder?.Dispose();

View File

@ -27,7 +27,7 @@ namespace Ocelot.AcceptanceTests
} }
[Fact] [Fact]
public void should_call_handlers() public void should_call_re_route_ordered_specific_handlers()
{ {
var configuration = new FileConfiguration var configuration = new FileConfiguration
{ {
@ -42,7 +42,47 @@ namespace Ocelot.AcceptanceTests
new FileHostAndPort new FileHostAndPort
{ {
Host = "localhost", Host = "localhost",
Port = 61879, 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]
public void should_call_global_di_handlers()
{
var configuration = new FileConfiguration
{
ReRoutes = new List<FileReRoute>
{
new FileReRoute
{
DownstreamPathTemplate = "/",
DownstreamScheme = "http",
DownstreamHostAndPorts = new List<FileHostAndPort>
{
new FileHostAndPort
{
Host = "localhost",
Port = 7187,
} }
}, },
UpstreamPathTemplate = "/", UpstreamPathTemplate = "/",
@ -51,32 +91,111 @@ namespace Ocelot.AcceptanceTests
} }
}; };
var handlerOne = new FakeHandler(); this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:7187", "/", 200, "Hello from Laura"))
var handlerTwo = new FakeHandler();
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:61879", "/", 200, "Hello from Laura"))
.And(x => _steps.GivenThereIsAConfiguration(configuration)) .And(x => _steps.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunningWithHandlers(handlerOne, handlerTwo)) .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"))
.And(x => ThenTheHandlersAreCalledCorrectly(handlerOne, handlerTwo)) .And(x => ThenTheHandlersAreCalledCorrectly())
.BDDfy(); .BDDfy();
} }
private void ThenTheHandlersAreCalledCorrectly(FakeHandler one, FakeHandler two) [Fact]
public void should_call_global_di_handlers_with_dependency()
{ {
one.TimeCalled.ShouldBeLessThan(two.TimeCalled); var configuration = new FileConfiguration
{
ReRoutes = new List<FileReRoute>
{
new FileReRoute
{
DownstreamPathTemplate = "/",
DownstreamScheme = "http",
DownstreamHostAndPorts = new List<FileHostAndPort>
{
new FileHostAndPort
{
Host = "localhost",
Port = 7188,
}
},
UpstreamPathTemplate = "/",
UpstreamHttpMethod = new List<string> { "Get" },
}
}
};
var dependency = new FakeDependency();
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:7188", "/", 200, "Hello from Laura"))
.And(x => _steps.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunningWithGlobalHandlersRegisteredInDi<FakeHandlerWithDependency>(dependency))
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
.And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura"))
.And(x => ThenTheDependencyIsCalled(dependency))
.BDDfy();
} }
class FakeHandler : DelegatingHandler private void ThenTheDependencyIsCalled(FakeDependency dependency)
{ {
public DateTime TimeCalled { get; private set; } dependency.Called.ShouldBeTrue();
}
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) private void ThenTheHandlersAreCalledCorrectly()
{
FakeHandler.TimeCalled.ShouldBeLessThan(FakeHandlerTwo.TimeCalled);
}
private void ThenTheOrderedHandlersAreCalledCorrectly()
{
FakeHandlerTwo.TimeCalled.ShouldBeLessThan(FakeHandler.TimeCalled);
}
public class FakeDependency
{
public bool Called;
}
// ReSharper disable once ClassNeverInstantiated.Local
private class FakeHandlerWithDependency : DelegatingHandler
{
private readonly FakeDependency _dependency;
public FakeHandlerWithDependency(FakeDependency dependency)
{
_dependency = dependency;
}
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
_dependency.Called = true;
return base.SendAsync(request, cancellationToken);
}
}
// ReSharper disable once ClassNeverInstantiated.Local
private class FakeHandler : DelegatingHandler
{
public static DateTime TimeCalled { get; private set; }
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);
}
}
// ReSharper disable once ClassNeverInstantiated.Local
private class FakeHandlerTwo : DelegatingHandler
{
public static DateTime TimeCalled { get; private set; }
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
TimeCalled = DateTime.Now;
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

@ -22,6 +22,10 @@ using Ocelot.Middleware;
using Shouldly; using Shouldly;
using ConfigurationBuilder = Microsoft.Extensions.Configuration.ConfigurationBuilder; using ConfigurationBuilder = Microsoft.Extensions.Configuration.ConfigurationBuilder;
using Ocelot.AcceptanceTests.Caching; using Ocelot.AcceptanceTests.Caching;
using System.IO.Compression;
using System.Text;
using static Ocelot.AcceptanceTests.HttpDelegatingHandlersTests;
using Ocelot.Requester;
namespace Ocelot.AcceptanceTests namespace Ocelot.AcceptanceTests
{ {
@ -172,10 +176,9 @@ namespace Ocelot.AcceptanceTests
_ocelotClient = _ocelotServer.CreateClient(); _ocelotClient = _ocelotServer.CreateClient();
} }
/// <summary> public void GivenOcelotIsRunningWithSpecficHandlersRegisteredInDi<TOne, TWo>()
/// This is annoying cos it should be in the constructor but we need to set up the file before calling startup so its a step. where TOne : DelegatingHandler
/// </summary> where TWo : DelegatingHandler
public void GivenOcelotIsRunningWithHandlers(DelegatingHandler handlerOne, DelegatingHandler handlerTwo)
{ {
_webHostBuilder = new WebHostBuilder(); _webHostBuilder = new WebHostBuilder();
@ -193,8 +196,73 @@ namespace Ocelot.AcceptanceTests
{ {
s.AddSingleton(_webHostBuilder); s.AddSingleton(_webHostBuilder);
s.AddOcelot() s.AddOcelot()
.AddDelegatingHandler(() => handlerOne) .AddSingletonDelegatingHandler<TOne>()
.AddDelegatingHandler(() => handlerTwo); .AddSingletonDelegatingHandler<TWo>();
})
.Configure(a =>
{
a.UseOcelot().Wait();
});
_ocelotServer = new TestServer(_webHostBuilder);
_ocelotClient = _ocelotServer.CreateClient();
}
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
{
_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.AddSingleton<FakeDependency>(dependency);
s.AddOcelot()
.AddSingletonDelegatingHandler<TOne>(true);
}) })
.Configure(a => .Configure(a =>
{ {
@ -582,6 +650,23 @@ namespace Ocelot.AcceptanceTests
_postContent = new StringContent(postcontent); _postContent = new StringContent(postcontent);
} }
public void GivenThePostHasGzipContent(object input)
{
var json = JsonConvert.SerializeObject(input);
var jsonBytes = Encoding.UTF8.GetBytes(json);
var ms = new MemoryStream();
using (var gzip = new GZipStream(ms, CompressionMode.Compress, true))
{
gzip.Write(jsonBytes, 0, jsonBytes.Length);
}
ms.Position = 0;
var content = new StreamContent(ms);
content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
content.Headers.ContentEncoding.Add("gzip");
_postContent = content;
}
public void ThenTheResponseBodyShouldBe(string expectedBody) public void ThenTheResponseBodyShouldBe(string expectedBody)
{ {
_response.Content.ReadAsStringAsync().Result.ShouldBe(expectedBody); _response.Content.ReadAsStringAsync().Result.ShouldBe(expectedBody);

View File

@ -0,0 +1,722 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading;
using System.Threading.Tasks;
using CacheManager.Core;
using IdentityServer4.AccessTokenValidation;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.TestHost;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using Ocelot.Configuration.File;
using Ocelot.DependencyInjection;
using Ocelot.Middleware;
using Shouldly;
using ConfigurationBuilder = Microsoft.Extensions.Configuration.ConfigurationBuilder;
using Ocelot.AcceptanceTests.Caching;
<<<<<<< HEAD
using static Ocelot.AcceptanceTests.HttpDelegatingHandlersTests;
||||||| merged common ancestors
=======
using System.IO.Compression;
using System.Text;
>>>>>>> develop
namespace Ocelot.AcceptanceTests
{
public class Steps : IDisposable
{
private TestServer _ocelotServer;
private HttpClient _ocelotClient;
private HttpResponseMessage _response;
private HttpContent _postContent;
private BearerToken _token;
public HttpClient OcelotClient => _ocelotClient;
public string RequestIdKey = "OcRequestId";
private readonly Random _random;
private IWebHostBuilder _webHostBuilder;
public Steps()
{
_random = new Random();
}
public void GivenThereIsAConfiguration(FileConfiguration fileConfiguration)
{
var configurationPath = TestConfiguration.ConfigurationPath;
var jsonConfiguration = JsonConvert.SerializeObject(fileConfiguration);
if (File.Exists(configurationPath))
{
File.Delete(configurationPath);
}
File.WriteAllText(configurationPath, jsonConfiguration);
}
public void GivenThereIsAConfiguration(FileConfiguration fileConfiguration, string configurationPath)
{
var jsonConfiguration = JsonConvert.SerializeObject(fileConfiguration);
if (File.Exists(configurationPath))
{
File.Delete(configurationPath);
}
File.WriteAllText(configurationPath, jsonConfiguration);
}
/// <summary>
/// This is annoying cos it should be in the constructor but we need to set up the file before calling startup so its a step.
/// </summary>
public void GivenOcelotIsRunning()
{
_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.AddOcelot();
})
.Configure(app =>
{
app.UseOcelot().Wait();
});
_ocelotServer = new TestServer(_webHostBuilder);
_ocelotClient = _ocelotServer.CreateClient();
}
internal void GivenOcelotIsRunningUsingButterfly(string butterflyUrl)
{
_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.AddOcelot()
.AddOpenTracing(option =>
{
//this is the url that the butterfly collector server is running on...
option.CollectorUrl = butterflyUrl;
option.Service = "Ocelot";
});
})
.Configure(app =>
{
app.Use(async (context, next) =>
{
await next.Invoke();
});
app.UseOcelot().Wait();
});
_ocelotServer = new TestServer(_webHostBuilder);
_ocelotClient = _ocelotServer.CreateClient();
}
/*
public void GivenIHaveAddedXForwardedForHeader(string value)
{
_ocelotClient.DefaultRequestHeaders.TryAddWithoutValidation("X-Forwarded-For", value);
}*/
public void GivenOcelotIsRunningWithMiddleareBeforePipeline<T>(Func<object, Task> callback)
{
_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.AddOcelot();
})
.Configure(app =>
{
app.UseMiddleware<T>(callback);
app.UseOcelot().Wait();
});
_ocelotServer = new TestServer(_webHostBuilder);
_ocelotClient = _ocelotServer.CreateClient();
}
public void GivenOcelotIsRunningWithHandlersRegisteredInDi<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()
.AddDelegatingHandler<TOne>()
.AddDelegatingHandler<TWo>();
})
.Configure(a =>
{
a.UseOcelot().Wait();
});
_ocelotServer = new TestServer(_webHostBuilder);
_ocelotClient = _ocelotServer.CreateClient();
}
public void GivenOcelotIsRunningWithHandlersRegisteredInDi<TOne>(FakeDependency dependency)
where TOne : 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.AddSingleton<FakeDependency>(dependency);
s.AddOcelot()
.AddDelegatingHandler<TOne>();
})
.Configure(a =>
{
a.UseOcelot().Wait();
});
_ocelotServer = new TestServer(_webHostBuilder);
_ocelotClient = _ocelotServer.CreateClient();
}
/// <summary>
/// This is annoying cos it should be in the constructor but we need to set up the file before calling startup so its a step.
/// </summary>
public void GivenOcelotIsRunning(Action<IdentityServerAuthenticationOptions> options, string authenticationProviderKey)
{
_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.AddOcelot();
s.AddAuthentication()
.AddIdentityServerAuthentication(authenticationProviderKey, options);
})
.Configure(app =>
{
app.UseOcelot().Wait();
});
_ocelotServer = new TestServer(_webHostBuilder);
_ocelotClient = _ocelotServer.CreateClient();
}
public void ThenTheResponseHeaderIs(string key, string value)
{
var header = _response.Headers.GetValues(key);
header.First().ShouldBe(value);
}
public void GivenOcelotIsRunningUsingJsonSerializedCache()
{
_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.AddOcelot()
.AddCacheManager((x) =>
{
x.WithMicrosoftLogging(log =>
{
log.AddConsole(LogLevel.Debug);
})
.WithJsonSerializer()
.WithHandle(typeof(InMemoryJsonHandle<>));
});
})
.Configure(app =>
{
app.UseOcelot().Wait();
});
_ocelotServer = new TestServer(_webHostBuilder);
_ocelotClient = _ocelotServer.CreateClient();
}
public void GivenOcelotIsRunningUsingConsulToStoreConfig()
{
_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.AddOcelot().AddStoreOcelotConfigurationInConsul();
})
.Configure(app =>
{
app.UseOcelot().Wait();
});
_ocelotServer = new TestServer(_webHostBuilder);
_ocelotClient = _ocelotServer.CreateClient();
}
public void GivenOcelotIsRunningUsingConsulToStoreConfigAndJsonSerializedCache()
{
_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.AddOcelot()
.AddCacheManager((x) =>
{
x.WithMicrosoftLogging(log =>
{
log.AddConsole(LogLevel.Debug);
})
.WithJsonSerializer()
.WithHandle(typeof(InMemoryJsonHandle<>));
})
.AddStoreOcelotConfigurationInConsul();
})
.Configure(app =>
{
app.UseOcelot().Wait();
});
_ocelotServer = new TestServer(_webHostBuilder);
_ocelotClient = _ocelotServer.CreateClient();
}
internal void ThenTheResponseShouldBe(FileConfiguration expecteds)
{
var response = JsonConvert.DeserializeObject<FileConfiguration>(_response.Content.ReadAsStringAsync().Result);
response.GlobalConfiguration.RequestIdKey.ShouldBe(expecteds.GlobalConfiguration.RequestIdKey);
response.GlobalConfiguration.ServiceDiscoveryProvider.Host.ShouldBe(expecteds.GlobalConfiguration.ServiceDiscoveryProvider.Host);
response.GlobalConfiguration.ServiceDiscoveryProvider.Port.ShouldBe(expecteds.GlobalConfiguration.ServiceDiscoveryProvider.Port);
for (var i = 0; i < response.ReRoutes.Count; i++)
{
for (var j = 0; j < response.ReRoutes[i].DownstreamHostAndPorts.Count; j++)
{
var result = response.ReRoutes[i].DownstreamHostAndPorts[j];
var expected = expecteds.ReRoutes[i].DownstreamHostAndPorts[j];
result.Host.ShouldBe(expected.Host);
result.Port.ShouldBe(expected.Port);
}
response.ReRoutes[i].DownstreamPathTemplate.ShouldBe(expecteds.ReRoutes[i].DownstreamPathTemplate);
response.ReRoutes[i].DownstreamScheme.ShouldBe(expecteds.ReRoutes[i].DownstreamScheme);
response.ReRoutes[i].UpstreamPathTemplate.ShouldBe(expecteds.ReRoutes[i].UpstreamPathTemplate);
response.ReRoutes[i].UpstreamHttpMethod.ShouldBe(expecteds.ReRoutes[i].UpstreamHttpMethod);
}
}
/// <summary>
/// This is annoying cos it should be in the constructor but we need to set up the file before calling startup so its a step.
/// </summary>
public void GivenOcelotIsRunning(OcelotPipelineConfiguration ocelotPipelineConfig)
{
var builder = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.AddJsonFile("configuration.json")
.AddEnvironmentVariables();
var configuration = builder.Build();
_webHostBuilder = new WebHostBuilder();
_webHostBuilder.ConfigureServices(s =>
{
s.AddSingleton(_webHostBuilder);
});
_ocelotServer = new TestServer(_webHostBuilder
.UseConfiguration(configuration)
.ConfigureServices(s =>
{
Action<ConfigurationBuilderCachePart> settings = (x) =>
{
x.WithMicrosoftLogging(log =>
{
log.AddConsole(LogLevel.Debug);
})
.WithDictionaryHandle();
};
s.AddOcelot(configuration);
})
.ConfigureLogging(l =>
{
l.AddConsole();
l.AddDebug();
})
.Configure(a =>
{
a.UseOcelot(ocelotPipelineConfig).Wait();
}));
_ocelotClient = _ocelotServer.CreateClient();
}
public void GivenIHaveAddedATokenToMyRequest()
{
_ocelotClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", _token.AccessToken);
}
public void GivenIHaveAToken(string url)
{
var tokenUrl = $"{url}/connect/token";
var formData = new List<KeyValuePair<string, string>>
{
new KeyValuePair<string, string>("client_id", "client"),
new KeyValuePair<string, string>("client_secret", "secret"),
new KeyValuePair<string, string>("scope", "api"),
new KeyValuePair<string, string>("username", "test"),
new KeyValuePair<string, string>("password", "test"),
new KeyValuePair<string, string>("grant_type", "password")
};
var content = new FormUrlEncodedContent(formData);
using (var httpClient = new HttpClient())
{
var response = httpClient.PostAsync(tokenUrl, content).Result;
var responseContent = response.Content.ReadAsStringAsync().Result;
response.EnsureSuccessStatusCode();
_token = JsonConvert.DeserializeObject<BearerToken>(responseContent);
}
}
public void GivenIHaveATokenForApiReadOnlyScope(string url)
{
var tokenUrl = $"{url}/connect/token";
var formData = new List<KeyValuePair<string, string>>
{
new KeyValuePair<string, string>("client_id", "client"),
new KeyValuePair<string, string>("client_secret", "secret"),
new KeyValuePair<string, string>("scope", "api.readOnly"),
new KeyValuePair<string, string>("username", "test"),
new KeyValuePair<string, string>("password", "test"),
new KeyValuePair<string, string>("grant_type", "password")
};
var content = new FormUrlEncodedContent(formData);
using (var httpClient = new HttpClient())
{
var response = httpClient.PostAsync(tokenUrl, content).Result;
var responseContent = response.Content.ReadAsStringAsync().Result;
response.EnsureSuccessStatusCode();
_token = JsonConvert.DeserializeObject<BearerToken>(responseContent);
}
}
public void GivenIHaveATokenForApi2(string url)
{
var tokenUrl = $"{url}/connect/token";
var formData = new List<KeyValuePair<string, string>>
{
new KeyValuePair<string, string>("client_id", "client"),
new KeyValuePair<string, string>("client_secret", "secret"),
new KeyValuePair<string, string>("scope", "api2"),
new KeyValuePair<string, string>("username", "test"),
new KeyValuePair<string, string>("password", "test"),
new KeyValuePair<string, string>("grant_type", "password")
};
var content = new FormUrlEncodedContent(formData);
using (var httpClient = new HttpClient())
{
var response = httpClient.PostAsync(tokenUrl, content).Result;
var responseContent = response.Content.ReadAsStringAsync().Result;
response.EnsureSuccessStatusCode();
_token = JsonConvert.DeserializeObject<BearerToken>(responseContent);
}
}
public void GivenIHaveAnOcelotToken(string adminPath)
{
var tokenUrl = $"{adminPath}/connect/token";
var formData = new List<KeyValuePair<string, string>>
{
new KeyValuePair<string, string>("client_id", "admin"),
new KeyValuePair<string, string>("client_secret", "secret"),
new KeyValuePair<string, string>("scope", "admin"),
new KeyValuePair<string, string>("username", "admin"),
new KeyValuePair<string, string>("password", "admin"),
new KeyValuePair<string, string>("grant_type", "password")
};
var content = new FormUrlEncodedContent(formData);
var response = _ocelotClient.PostAsync(tokenUrl, content).Result;
var responseContent = response.Content.ReadAsStringAsync().Result;
response.EnsureSuccessStatusCode();
_token = JsonConvert.DeserializeObject<BearerToken>(responseContent);
}
public void VerifyIdentiryServerStarted(string url)
{
using (var httpClient = new HttpClient())
{
var response = httpClient.GetAsync($"{url}/.well-known/openid-configuration").Result;
response.EnsureSuccessStatusCode();
}
}
public void WhenIGetUrlOnTheApiGateway(string url)
{
_response = _ocelotClient.GetAsync(url).Result;
}
public void GivenIAddAHeader(string key, string value)
{
_ocelotClient.DefaultRequestHeaders.Add(key, value);
}
public void WhenIGetUrlOnTheApiGatewayMultipleTimes(string url, int times)
{
var tasks = new Task[times];
for (int i = 0; i < times; i++)
{
var urlCopy = url;
tasks[i] = GetForServiceDiscoveryTest(urlCopy);
Thread.Sleep(_random.Next(40, 60));
}
Task.WaitAll(tasks);
}
private async Task GetForServiceDiscoveryTest(string url)
{
var response = await _ocelotClient.GetAsync(url);
var content = await response.Content.ReadAsStringAsync();
int count = int.Parse(content);
count.ShouldBeGreaterThan(0);
}
public void WhenIGetUrlOnTheApiGatewayMultipleTimesForRateLimit(string url, int times)
{
for (int i = 0; i < times; i++)
{
var clientId = "ocelotclient1";
var request = new HttpRequestMessage(new HttpMethod("GET"), url);
request.Headers.Add("ClientId", clientId);
_response = _ocelotClient.SendAsync(request).Result;
}
}
public void WhenIGetUrlOnTheApiGateway(string url, string requestId)
{
_ocelotClient.DefaultRequestHeaders.TryAddWithoutValidation(RequestIdKey, requestId);
_response = _ocelotClient.GetAsync(url).Result;
}
public void WhenIPostUrlOnTheApiGateway(string url)
{
_response = _ocelotClient.PostAsync(url, _postContent).Result;
}
public void GivenThePostHasContent(string postcontent)
{
_postContent = new StringContent(postcontent);
}
public void GivenThePostHasGzipContent(object input)
{
string json = JsonConvert.SerializeObject(input);
byte[] jsonBytes = Encoding.UTF8.GetBytes(json);
MemoryStream ms = new MemoryStream();
using (GZipStream gzip = new GZipStream(ms, CompressionMode.Compress, true))
{
gzip.Write(jsonBytes, 0, jsonBytes.Length);
}
ms.Position = 0;
StreamContent content = new StreamContent(ms);
content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
content.Headers.ContentEncoding.Add("gzip");
_postContent = content;
}
public void ThenTheResponseBodyShouldBe(string expectedBody)
{
_response.Content.ReadAsStringAsync().Result.ShouldBe(expectedBody);
}
public void ThenTheStatusCodeShouldBe(HttpStatusCode expectedHttpStatusCode)
{
_response.StatusCode.ShouldBe(expectedHttpStatusCode);
}
public void ThenTheStatusCodeShouldBe(int expectedHttpStatusCode)
{
var responseStatusCode = (int)_response.StatusCode;
responseStatusCode.ShouldBe(expectedHttpStatusCode);
}
public void Dispose()
{
_ocelotClient?.Dispose();
_ocelotServer?.Dispose();
}
public void ThenTheRequestIdIsReturned()
{
_response.Headers.GetValues(RequestIdKey).First().ShouldNotBeNullOrEmpty();
}
public void ThenTheRequestIdIsReturned(string expected)
{
_response.Headers.GetValues(RequestIdKey).First().ShouldBe(expected);
}
public void ThenTheContentLengthIs(int expected)
{
_response.Content.Headers.ContentLength.ShouldBe(expected);
}
public void WhenIMakeLotsOfDifferentRequestsToTheApiGateway()
{
int numberOfRequests = 100;
var aggregateUrl = "/";
var aggregateExpected = "{\"Laura\":{Hello from Laura},\"Tom\":{Hello from Tom}}";
var tomUrl = "/tom";
var tomExpected = "{Hello from Tom}";
var lauraUrl = "/laura";
var lauraExpected = "{Hello from Laura}";
var random = new Random();
var aggregateTasks = new Task[numberOfRequests];
for (int i = 0; i < numberOfRequests; i++)
{
aggregateTasks[i] = Fire(aggregateUrl, aggregateExpected, random);
}
var tomTasks = new Task[numberOfRequests];
for (int i = 0; i < numberOfRequests; i++)
{
tomTasks[i] = Fire(tomUrl, tomExpected, random);
}
var lauraTasks = new Task[numberOfRequests];
for (int i = 0; i < numberOfRequests; i++)
{
lauraTasks[i] = Fire(lauraUrl, lauraExpected, random);
}
Task.WaitAll(lauraTasks);
Task.WaitAll(tomTasks);
Task.WaitAll(aggregateTasks);
}
private async Task Fire(string url, string expectedBody, Random random)
{
var request = new HttpRequestMessage(new HttpMethod("GET"), url);
await Task.Delay(random.Next(0, 2));
var response = await _ocelotClient.SendAsync(request);
var content = await response.Content.ReadAsStringAsync();
content.ShouldBe(expectedBody);
}
}
}

View File

@ -1,5 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk.Web"> <Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup> <PropertyGroup>
<VersionPrefix>0.0.0-dev</VersionPrefix> <VersionPrefix>0.0.0-dev</VersionPrefix>
<TargetFramework>netcoreapp2.0</TargetFramework> <TargetFramework>netcoreapp2.0</TargetFramework>
@ -11,23 +10,19 @@
<RuntimeIdentifiers>osx.10.11-x64;osx.10.12-x64;win7-x64;win10-x64</RuntimeIdentifiers> <RuntimeIdentifiers>osx.10.11-x64;osx.10.12-x64;win7-x64;win10-x64</RuntimeIdentifiers>
<CodeAnalysisRuleSet>..\..\codeanalysis.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRuleSet>..\..\codeanalysis.ruleset</CodeAnalysisRuleSet>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<None Update="Views;Areas\**\Views"> <None Update="Views;Areas\**\Views">
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory> <CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</None> </None>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<None Update="configuration.json;appsettings.json;idsrv3test.pfx"> <None Update="configuration.json;appsettings.json;idsrv3test.pfx">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None> </None>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\..\src\Ocelot\Ocelot.csproj"/> <ProjectReference Include="..\..\src\Ocelot\Ocelot.csproj"/>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.All" Version="2.0.0"/> <PackageReference Include="Microsoft.AspNetCore.All" Version="2.0.0"/>
<PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="2.0.0"/> <PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="2.0.0"/>
@ -43,5 +38,4 @@
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
</PackageReference> </PackageReference>
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@ -6,6 +6,7 @@ namespace Ocelot.UnitTests.Authentication
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Text; using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Moq; using Moq;
using Ocelot.Authentication.Middleware; using Ocelot.Authentication.Middleware;
@ -44,10 +45,11 @@ namespace Ocelot.UnitTests.Authentication
private void WhenICallTheMiddleware() private void WhenICallTheMiddleware()
{ {
_next = async (context) => { _next = (context) => {
byte[] byteArray = Encoding.ASCII.GetBytes("The user is authenticated"); byte[] byteArray = Encoding.ASCII.GetBytes("The user is authenticated");
MemoryStream stream = new MemoryStream(byteArray); var stream = new MemoryStream(byteArray);
context.HttpContext.Response.Body = stream; context.HttpContext.Response.Body = stream;
return Task.CompletedTask;
}; };
_middleware = new AuthenticationMiddleware(_next, _factory.Object); _middleware = new AuthenticationMiddleware(_next, _factory.Object);
_middleware.Invoke(_downstreamContext).GetAwaiter().GetResult(); _middleware.Invoke(_downstreamContext).GetAwaiter().GetResult();
@ -55,10 +57,11 @@ namespace Ocelot.UnitTests.Authentication
private void GivenTheTestServerPipelineIsConfigured() private void GivenTheTestServerPipelineIsConfigured()
{ {
_next = async (context) => { _next = (context) => {
byte[] byteArray = Encoding.ASCII.GetBytes("The user is authenticated"); byte[] byteArray = Encoding.ASCII.GetBytes("The user is authenticated");
MemoryStream stream = new MemoryStream(byteArray); var stream = new MemoryStream(byteArray);
context.HttpContext.Response.Body = stream; context.HttpContext.Response.Body = stream;
return Task.CompletedTask;
}; };
} }

View File

@ -4,18 +4,17 @@ namespace Ocelot.UnitTests.Authorization
{ {
using System.Collections.Generic; using System.Collections.Generic;
using System.Security.Claims; using System.Security.Claims;
using System.Threading.Tasks;
using Moq; using Moq;
using Ocelot.Authorisation; using Ocelot.Authorisation;
using Ocelot.Authorisation.Middleware; using Ocelot.Authorisation.Middleware;
using Ocelot.Configuration.Builder; using Ocelot.Configuration.Builder;
using Ocelot.DownstreamRouteFinder;
using Ocelot.DownstreamRouteFinder.UrlMatcher; using Ocelot.DownstreamRouteFinder.UrlMatcher;
using Ocelot.Logging; using Ocelot.Logging;
using Ocelot.Responses; using Ocelot.Responses;
using TestStack.BDDfy; using TestStack.BDDfy;
using Xunit; using Xunit;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Ocelot.DownstreamRouteFinder.Middleware;
using Ocelot.Configuration; using Ocelot.Configuration;
public class AuthorisationMiddlewareTests public class AuthorisationMiddlewareTests
@ -36,9 +35,7 @@ namespace Ocelot.UnitTests.Authorization
_loggerFactory = new Mock<IOcelotLoggerFactory>(); _loggerFactory = new Mock<IOcelotLoggerFactory>();
_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 = async context => { _next = context => Task.CompletedTask;
//do nothing
};
_middleware = new AuthorisationMiddleware(_next, _authService.Object, _authScopesService.Object, _loggerFactory.Object); _middleware = new AuthorisationMiddleware(_next, _authService.Object, _authScopesService.Object, _loggerFactory.Object);
} }

View File

@ -10,6 +10,7 @@ namespace Ocelot.UnitTests.Cache
using Shouldly; using Shouldly;
using System.Collections.Generic; using System.Collections.Generic;
using System.Net.Http; using System.Net.Http;
using System.Threading.Tasks;
using Moq; using Moq;
using Ocelot.Cache; using Ocelot.Cache;
using Ocelot.Cache.Middleware; using Ocelot.Cache.Middleware;
@ -43,9 +44,7 @@ namespace Ocelot.UnitTests.Cache
_cacheManager = new OcelotCacheManagerCache<CachedResponse>(cacheManagerOutputCache); _cacheManager = new OcelotCacheManagerCache<CachedResponse>(cacheManagerOutputCache);
_downstreamContext = new DownstreamContext(new DefaultHttpContext()); _downstreamContext = new DownstreamContext(new DefaultHttpContext());
_downstreamContext.DownstreamRequest = new HttpRequestMessage(HttpMethod.Get, "https://some.url/blah?abcd=123"); _downstreamContext.DownstreamRequest = new HttpRequestMessage(HttpMethod.Get, "https://some.url/blah?abcd=123");
_next = async context => { _next = context => Task.CompletedTask;
//do nothing..
};
_middleware = new OutputCacheMiddleware(_next, _loggerFactory.Object, _cacheManager, _regionCreator); _middleware = new OutputCacheMiddleware(_next, _loggerFactory.Object, _cacheManager, _regionCreator);
} }

View File

@ -7,19 +7,16 @@ namespace Ocelot.UnitTests.Cache
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Net.Http; using System.Net.Http;
using Microsoft.AspNetCore.Builder; using System.Threading.Tasks;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using Moq; using Moq;
using Ocelot.Cache; using Ocelot.Cache;
using Ocelot.Cache.Middleware; using Ocelot.Cache.Middleware;
using Ocelot.Configuration; using Ocelot.Configuration;
using Ocelot.Configuration.Builder; using Ocelot.Configuration.Builder;
using Ocelot.DownstreamRouteFinder; using Ocelot.DownstreamRouteFinder;
using Ocelot.DownstreamRouteFinder.Middleware;
using Ocelot.DownstreamRouteFinder.UrlMatcher; using Ocelot.DownstreamRouteFinder.UrlMatcher;
using Ocelot.Logging; using Ocelot.Logging;
using Ocelot.Responses;
using TestStack.BDDfy; using TestStack.BDDfy;
using Xunit; using Xunit;
@ -42,10 +39,7 @@ namespace Ocelot.UnitTests.Cache
_loggerFactory = new Mock<IOcelotLoggerFactory>(); _loggerFactory = new Mock<IOcelotLoggerFactory>();
_logger = new Mock<IOcelotLogger>(); _logger = new Mock<IOcelotLogger>();
_loggerFactory.Setup(x => x.CreateLogger<OutputCacheMiddleware>()).Returns(_logger.Object); _loggerFactory.Setup(x => x.CreateLogger<OutputCacheMiddleware>()).Returns(_logger.Object);
_next = async context => { _next = context => Task.CompletedTask;
//do nothing
};
_downstreamContext.DownstreamRequest = new HttpRequestMessage(HttpMethod.Get, "https://some.url/blah?abcd=123"); _downstreamContext.DownstreamRequest = new HttpRequestMessage(HttpMethod.Get, "https://some.url/blah?abcd=123");
} }

View File

@ -3,6 +3,7 @@
namespace Ocelot.UnitTests.Claims namespace Ocelot.UnitTests.Claims
{ {
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Moq; using Moq;
using Ocelot.Claims; using Ocelot.Claims;
@ -32,9 +33,7 @@ namespace Ocelot.UnitTests.Claims
_loggerFactory = new Mock<IOcelotLoggerFactory>(); _loggerFactory = new Mock<IOcelotLoggerFactory>();
_logger = new Mock<IOcelotLogger>(); _logger = new Mock<IOcelotLogger>();
_loggerFactory.Setup(x => x.CreateLogger<ClaimsBuilderMiddleware>()).Returns(_logger.Object); _loggerFactory.Setup(x => x.CreateLogger<ClaimsBuilderMiddleware>()).Returns(_logger.Object);
_next = async context => { _next = context => Task.CompletedTask;
//do nothing
};
_middleware = new ClaimsBuilderMiddleware(_next, _loggerFactory.Object, _addHeaders.Object); _middleware = new ClaimsBuilderMiddleware(_next, _loggerFactory.Object, _addHeaders.Object);
} }

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,28 +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() public void should_add_specific_delegating_handler_singleton()
{ {
var fakeOne = new FakeDelegatingHandler(0);
var fakeTwo = new FakeDelegatingHandler(1);
this.Given(x => WhenISetUpOcelotServices()) this.Given(x => WhenISetUpOcelotServices())
.When(x => AddDelegate(fakeOne)) .When(x => AddSpecificDelegatingHandler<FakeDelegatingHandler>())
.And(x => AddDelegate(fakeTwo)) .And(x => AddSpecificDelegatingHandler<FakeDelegatingHandlerTwo>())
.Then(x => ThenTheProviderIsRegisteredAndReturnsHandlers()) .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>())
.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();
} }
@ -120,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()
{ {
@ -157,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();
@ -164,29 +240,38 @@ namespace Ocelot.UnitTests.DependencyInjection
path.Path.ShouldBe("/administration"); path.Path.ShouldBe("/administration");
} }
private void ThenTheProviderIsRegisteredAndReturnsHandlers() private void ThenTheProviderIsRegisteredAndReturnsHandlers<TOne, TWo>()
{ {
_serviceProvider = _services.BuildServiceProvider(); _serviceProvider = _services.BuildServiceProvider();
var provider = _serviceProvider.GetService<IDelegatingHandlerHandlerProvider>(); var handlers = _serviceProvider.GetServices<GlobalDelegatingHandler>().ToList();
var handlers = provider.Get(); handlers[0].DelegatingHandler.ShouldBeOfType<TOne>();
var handler = (FakeDelegatingHandler)handlers[0].Invoke(); handlers[1].DelegatingHandler.ShouldBeOfType<TWo>();
handler.Order.ShouldBe(0); }
handler = (FakeDelegatingHandler)handlers[1].Invoke();
handler.Order.ShouldBe(1); private void ThenTheProviderIsRegisteredAndReturnsSpecificHandlers<TOne, TWo>()
{
_serviceProvider = _services.BuildServiceProvider();
var handlers = _serviceProvider.GetServices<DelegatingHandler>().ToList();
handlers[0].ShouldBeOfType<TOne>();
handlers[1].ShouldBeOfType<TWo>();
} }
private void OnlyOneVersionOfEachCacheIsRegistered() private void OnlyOneVersionOfEachCacheIsRegistered()
{ {
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()
@ -213,9 +298,14 @@ namespace Ocelot.UnitTests.DependencyInjection
} }
} }
private void AddDelegate(DelegatingHandler handler) private void AddGlobalDelegatingHandler<T>() where T : DelegatingHandler
{ {
_ocelotBuilder.AddDelegatingHandler(() => handler); _ocelotBuilder.AddSingletonDelegatingHandler<T>(true);
}
private void AddSpecificDelegatingHandler<T>() where T : DelegatingHandler
{
_ocelotBuilder.AddSingletonDelegatingHandler<T>();
} }
private void ThenAnOcelotBuilderIsReturned() private void ThenAnOcelotBuilderIsReturned()
@ -286,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)
{ {
@ -298,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

@ -4,9 +4,8 @@ using Ocelot.Middleware.Multiplexer;
namespace Ocelot.UnitTests.DownstreamRouteFinder namespace Ocelot.UnitTests.DownstreamRouteFinder
{ {
using System.Collections.Generic; using System.Collections.Generic;
using Microsoft.AspNetCore.Builder; using System.Threading.Tasks;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using Moq; using Moq;
using Ocelot.Configuration; using Ocelot.Configuration;
using Ocelot.Configuration.Builder; using Ocelot.Configuration.Builder;
@ -42,9 +41,7 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder
_loggerFactory = new Mock<IOcelotLoggerFactory>(); _loggerFactory = new Mock<IOcelotLoggerFactory>();
_logger = new Mock<IOcelotLogger>(); _logger = new Mock<IOcelotLogger>();
_loggerFactory.Setup(x => x.CreateLogger<DownstreamRouteFinderMiddleware>()).Returns(_logger.Object); _loggerFactory.Setup(x => x.CreateLogger<DownstreamRouteFinderMiddleware>()).Returns(_logger.Object);
_next = async context => { _next = context => Task.CompletedTask;
//do nothing
};
_multiplexer = new Mock<IMultiplexer>(); _multiplexer = new Mock<IMultiplexer>();
_middleware = new DownstreamRouteFinderMiddleware(_next, _loggerFactory.Object, _finder.Object, _provider.Object, _multiplexer.Object); _middleware = new DownstreamRouteFinderMiddleware(_next, _loggerFactory.Object, _finder.Object, _provider.Object, _multiplexer.Object);
} }

View File

@ -6,23 +6,19 @@ namespace Ocelot.UnitTests.DownstreamUrlCreator
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Net.Http; using System.Net.Http;
using Microsoft.AspNetCore.Builder; using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Moq; using Moq;
using Ocelot.Configuration.Builder; using Ocelot.Configuration.Builder;
using Ocelot.DownstreamRouteFinder; using Ocelot.DownstreamRouteFinder;
using Ocelot.DownstreamRouteFinder.UrlMatcher; using Ocelot.DownstreamRouteFinder.UrlMatcher;
using Ocelot.DownstreamUrlCreator;
using Ocelot.DownstreamUrlCreator.Middleware; using Ocelot.DownstreamUrlCreator.Middleware;
using Ocelot.DownstreamUrlCreator.UrlTemplateReplacer; using Ocelot.DownstreamUrlCreator.UrlTemplateReplacer;
using Ocelot.Infrastructure.RequestData;
using Ocelot.Logging; using Ocelot.Logging;
using Ocelot.Responses; using Ocelot.Responses;
using Ocelot.Values; using Ocelot.Values;
using TestStack.BDDfy; using TestStack.BDDfy;
using Xunit; using Xunit;
using Shouldly; using Shouldly;
using Ocelot.DownstreamRouteFinder.Middleware;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
public class DownstreamUrlCreatorMiddlewareTests public class DownstreamUrlCreatorMiddlewareTests
@ -43,9 +39,7 @@ namespace Ocelot.UnitTests.DownstreamUrlCreator
_loggerFactory.Setup(x => x.CreateLogger<DownstreamUrlCreatorMiddleware>()).Returns(_logger.Object); _loggerFactory.Setup(x => x.CreateLogger<DownstreamUrlCreatorMiddleware>()).Returns(_logger.Object);
_downstreamUrlTemplateVariableReplacer = new Mock<IDownstreamPathPlaceholderReplacer>(); _downstreamUrlTemplateVariableReplacer = new Mock<IDownstreamPathPlaceholderReplacer>();
_downstreamContext.DownstreamRequest = new HttpRequestMessage(HttpMethod.Get, "https://my.url/abc/?q=123"); _downstreamContext.DownstreamRequest = new HttpRequestMessage(HttpMethod.Get, "https://my.url/abc/?q=123");
_next = async context => { _next = context => Task.CompletedTask;
//do nothing
};
} }
[Fact] [Fact]

View File

@ -11,11 +11,12 @@ using Ocelot.Configuration.Builder;
using Ocelot.Headers; using Ocelot.Headers;
using System.Net.Http; using System.Net.Http;
using Ocelot.Authorisation.Middleware; using Ocelot.Authorisation.Middleware;
using Ocelot.DownstreamRouteFinder.Middleware;
using Ocelot.Middleware; using Ocelot.Middleware;
namespace Ocelot.UnitTests.Headers namespace Ocelot.UnitTests.Headers
{ {
using System.Threading.Tasks;
public class HttpHeadersTransformationMiddlewareTests public class HttpHeadersTransformationMiddlewareTests
{ {
private Mock<IHttpContextRequestHeaderReplacer> _preReplacer; private Mock<IHttpContextRequestHeaderReplacer> _preReplacer;
@ -34,9 +35,7 @@ namespace Ocelot.UnitTests.Headers
_loggerFactory = new Mock<IOcelotLoggerFactory>(); _loggerFactory = new Mock<IOcelotLoggerFactory>();
_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 = async context => { _next = context => Task.CompletedTask;
//do nothing
};
_middleware = new HttpHeadersTransformationMiddleware(_next, _loggerFactory.Object, _preReplacer.Object, _postReplacer.Object); _middleware = new HttpHeadersTransformationMiddleware(_next, _loggerFactory.Object, _preReplacer.Object, _postReplacer.Object);
} }

View File

@ -4,14 +4,12 @@ namespace Ocelot.UnitTests.Headers
{ {
using System.Collections.Generic; using System.Collections.Generic;
using System.Net.Http; using System.Net.Http;
using Microsoft.AspNetCore.Builder; using System.Threading.Tasks;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using Moq; using Moq;
using Ocelot.Configuration; using Ocelot.Configuration;
using Ocelot.Configuration.Builder; using Ocelot.Configuration.Builder;
using Ocelot.DownstreamRouteFinder; using Ocelot.DownstreamRouteFinder;
using Ocelot.DownstreamRouteFinder.Middleware;
using Ocelot.DownstreamRouteFinder.UrlMatcher; using Ocelot.DownstreamRouteFinder.UrlMatcher;
using Ocelot.Headers; using Ocelot.Headers;
using Ocelot.Headers.Middleware; using Ocelot.Headers.Middleware;
@ -37,9 +35,7 @@ namespace Ocelot.UnitTests.Headers
_loggerFactory = new Mock<IOcelotLoggerFactory>(); _loggerFactory = new Mock<IOcelotLoggerFactory>();
_logger = new Mock<IOcelotLogger>(); _logger = new Mock<IOcelotLogger>();
_loggerFactory.Setup(x => x.CreateLogger<HttpRequestHeadersBuilderMiddleware>()).Returns(_logger.Object); _loggerFactory.Setup(x => x.CreateLogger<HttpRequestHeadersBuilderMiddleware>()).Returns(_logger.Object);
_next = async context => { _next = context => Task.CompletedTask;
//do nothing
};
_middleware = new HttpRequestHeadersBuilderMiddleware(_next, _loggerFactory.Object, _addHeaders.Object); _middleware = new HttpRequestHeadersBuilderMiddleware(_next, _loggerFactory.Object, _addHeaders.Object);
_downstreamContext.DownstreamRequest = new HttpRequestMessage(); _downstreamContext.DownstreamRequest = new HttpRequestMessage();
} }

View File

@ -19,12 +19,13 @@ namespace Ocelot.UnitTests.LoadBalancer
private readonly LoadBalancerHouse _loadBalancerHouse; private readonly LoadBalancerHouse _loadBalancerHouse;
private Response<ILoadBalancer> _getResult; private Response<ILoadBalancer> _getResult;
private readonly Mock<ILoadBalancerFactory> _factory; private readonly Mock<ILoadBalancerFactory> _factory;
private ServiceProviderConfiguration _serviceProviderConfig; private readonly ServiceProviderConfiguration _serviceProviderConfig;
public LoadBalancerHouseTests() public LoadBalancerHouseTests()
{ {
_factory = new Mock<ILoadBalancerFactory>(); _factory = new Mock<ILoadBalancerFactory>();
_loadBalancerHouse = new LoadBalancerHouse(_factory.Object); _loadBalancerHouse = new LoadBalancerHouse(_factory.Object);
_serviceProviderConfig = new ServiceProviderConfiguration("myType","myHost",123);
} }
[Fact] [Fact]

View File

@ -4,6 +4,7 @@ namespace Ocelot.UnitTests.LoadBalancer
{ {
using System.Collections.Generic; using System.Collections.Generic;
using System.Net.Http; using System.Net.Http;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Moq; using Moq;
using Ocelot.Configuration; using Ocelot.Configuration;
@ -43,9 +44,7 @@ namespace Ocelot.UnitTests.LoadBalancer
_loggerFactory = new Mock<IOcelotLoggerFactory>(); _loggerFactory = new Mock<IOcelotLoggerFactory>();
_logger = new Mock<IOcelotLogger>(); _logger = new Mock<IOcelotLogger>();
_loggerFactory.Setup(x => x.CreateLogger<LoadBalancingMiddleware>()).Returns(_logger.Object); _loggerFactory.Setup(x => x.CreateLogger<LoadBalancingMiddleware>()).Returns(_logger.Object);
_next = async context => { _next = context => Task.CompletedTask;
//do nothing
};
_downstreamContext.DownstreamRequest = _downstreamRequest; _downstreamContext.DownstreamRequest = _downstreamRequest;
} }

View File

@ -24,7 +24,7 @@ namespace Ocelot.UnitTests.Middleware
{ {
_aggregator = new Mock<IResponseAggregator>(); _aggregator = new Mock<IResponseAggregator>();
_context = new DownstreamContext(new DefaultHttpContext()); _context = new DownstreamContext(new DefaultHttpContext());
_pipeline = async context => { _count++; }; _pipeline = context => Task.FromResult(_count++);
_multiplexer = new Multiplexer(_aggregator.Object); _multiplexer = new Multiplexer(_aggregator.Object);
} }

View File

@ -4,7 +4,6 @@ namespace Ocelot.UnitTests.QueryStrings
{ {
using System.Collections.Generic; using System.Collections.Generic;
using System.Net.Http; using System.Net.Http;
using Microsoft.Extensions.DependencyInjection;
using Moq; using Moq;
using Ocelot.Configuration; using Ocelot.Configuration;
using Ocelot.Configuration.Builder; using Ocelot.Configuration.Builder;
@ -17,9 +16,8 @@ namespace Ocelot.UnitTests.QueryStrings
using TestStack.BDDfy; using TestStack.BDDfy;
using Xunit; using Xunit;
using System.Security.Claims; using System.Security.Claims;
using Microsoft.AspNetCore.Builder;
using Ocelot.DownstreamRouteFinder.Middleware;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using System.Threading.Tasks;
public class QueryStringBuilderMiddlewareTests public class QueryStringBuilderMiddlewareTests
{ {
@ -36,9 +34,7 @@ namespace Ocelot.UnitTests.QueryStrings
_loggerFactory = new Mock<IOcelotLoggerFactory>(); _loggerFactory = new Mock<IOcelotLoggerFactory>();
_logger = new Mock<IOcelotLogger>(); _logger = new Mock<IOcelotLogger>();
_loggerFactory.Setup(x => x.CreateLogger<QueryStringBuilderMiddleware>()).Returns(_logger.Object); _loggerFactory.Setup(x => x.CreateLogger<QueryStringBuilderMiddleware>()).Returns(_logger.Object);
_next = async context => { _next = context => Task.CompletedTask;
//do nothing
};
_addQueries = new Mock<IAddQueriesToRequest>(); _addQueries = new Mock<IAddQueriesToRequest>();
_downstreamContext.DownstreamRequest = new HttpRequestMessage(); _downstreamContext.DownstreamRequest = new HttpRequestMessage();
_middleware = new QueryStringBuilderMiddleware(_next, _loggerFactory.Object, _addQueries.Object); _middleware = new QueryStringBuilderMiddleware(_next, _loggerFactory.Object, _addQueries.Object);

View File

@ -15,9 +15,9 @@ namespace Ocelot.UnitTests.RateLimit
using Shouldly; using Shouldly;
using TestStack.BDDfy; using TestStack.BDDfy;
using Xunit; using Xunit;
using Ocelot.DownstreamRouteFinder.Middleware;
using Microsoft.Extensions.Caching.Memory; using Microsoft.Extensions.Caching.Memory;
using System.IO; using System.IO;
using System.Threading.Tasks;
public class ClientRateLimitMiddlewareTests public class ClientRateLimitMiddlewareTests
{ {
@ -42,8 +42,7 @@ namespace Ocelot.UnitTests.RateLimit
_loggerFactory = new Mock<IOcelotLoggerFactory>(); _loggerFactory = new Mock<IOcelotLoggerFactory>();
_logger = new Mock<IOcelotLogger>(); _logger = new Mock<IOcelotLogger>();
_loggerFactory.Setup(x => x.CreateLogger<ClientRateLimitMiddleware>()).Returns(_logger.Object); _loggerFactory.Setup(x => x.CreateLogger<ClientRateLimitMiddleware>()).Returns(_logger.Object);
_next = async (context) => { _next = context => Task.CompletedTask;
};
_middleware = new ClientRateLimitMiddleware(_next, _loggerFactory.Object, _rateLimitCounterHandler); _middleware = new ClientRateLimitMiddleware(_next, _loggerFactory.Object, _rateLimitCounterHandler);
} }

View File

@ -15,6 +15,7 @@
using System; using System;
using System.IO; using System.IO;
using System.Text; using System.Text;
using System.Security.Cryptography;
public class RequestMapperTests public class RequestMapperTests
{ {
@ -118,19 +119,151 @@
} }
[Fact] [Fact]
public void Should_map_content_type_header() public void Should_handle_no_content()
{ {
this.Given(_ => GivenTheInputRequestHasNoContent())
.And(_ => GivenTheInputRequestHasMethod("GET"))
.And(_ => GivenTheInputRequestHasAValidUri())
.When(_ => WhenMapped())
.Then(_ => ThenNoErrorIsReturned())
.And(_ => ThenTheMappedRequestHasNoContent())
.BDDfy();
}
[Fact]
public void Should_map_content_headers()
{
byte[] md5bytes = new byte[0];
using (var md5 = MD5.Create())
{
md5bytes = md5.ComputeHash(Encoding.UTF8.GetBytes("some md5"));
}
this.Given(_ => GivenTheInputRequestHasContent("This is my content")) this.Given(_ => GivenTheInputRequestHasContent("This is my content"))
.And(_ => GivenTheContentTypeIs("application/json")) .And(_ => GivenTheContentTypeIs("application/json"))
.And(_ => GivenTheContentEncodingIs("gzip, compress"))
.And(_ => GivenTheContentLanguageIs("english"))
.And(_ => GivenTheContentLocationIs("/my-receipts/38"))
.And(_ => GivenTheContentRangeIs("bytes 1-2/*"))
.And(_ => GivenTheContentDispositionIs("inline"))
.And(_ => GivenTheContentMD5Is(md5bytes))
.And(_ => GivenTheInputRequestHasMethod("GET")) .And(_ => GivenTheInputRequestHasMethod("GET"))
.And(_ => GivenTheInputRequestHasAValidUri()) .And(_ => GivenTheInputRequestHasAValidUri())
.When(_ => WhenMapped()) .When(_ => WhenMapped())
.Then(_ => ThenNoErrorIsReturned()) .Then(_ => ThenNoErrorIsReturned())
.And(_ => ThenTheMappedRequestHasContentTypeHeader("application/json")) .And(_ => ThenTheMappedRequestHasContentTypeHeader("application/json"))
.And(_ => ThenTheMappedRequestHasContentEncodingHeader("gzip", "compress"))
.And(_ => ThenTheMappedRequestHasContentLanguageHeader("english"))
.And(_ => ThenTheMappedRequestHasContentLocationHeader("/my-receipts/38"))
.And(_ => ThenTheMappedRequestHasContentMD5Header(md5bytes))
.And(_ => ThenTheMappedRequestHasContentRangeHeader())
.And(_ => ThenTheMappedRequestHasContentDispositionHeader("inline"))
.And(_ => ThenTheMappedRequestHasContentSize("This is my content".Length)) .And(_ => ThenTheMappedRequestHasContentSize("This is my content".Length))
.And(_ => ThenTheContentHeadersAreNotAddedToNonContentHeaders())
.BDDfy(); .BDDfy();
} }
[Fact]
public void should_not_add_content_headers()
{
this.Given(_ => GivenTheInputRequestHasContent("This is my content"))
.And(_ => GivenTheContentTypeIs("application/json"))
.And(_ => GivenTheInputRequestHasMethod("POST"))
.And(_ => GivenTheInputRequestHasAValidUri())
.When(_ => WhenMapped())
.Then(_ => ThenNoErrorIsReturned())
.And(_ => ThenTheMappedRequestHasContentTypeHeader("application/json"))
.And(_ => ThenTheMappedRequestHasContentSize("This is my content".Length))
.And(_ => ThenTheOtherContentTypeHeadersAreNotMapped())
.BDDfy();
}
private void ThenTheContentHeadersAreNotAddedToNonContentHeaders()
{
_mappedRequest.Data.Headers.ShouldNotContain(x => x.Key == "Content-Disposition");
_mappedRequest.Data.Headers.ShouldNotContain(x => x.Key == "Content-ContentMD5");
_mappedRequest.Data.Headers.ShouldNotContain(x => x.Key == "Content-ContentRange");
_mappedRequest.Data.Headers.ShouldNotContain(x => x.Key == "Content-ContentLanguage");
_mappedRequest.Data.Headers.ShouldNotContain(x => x.Key == "Content-ContentEncoding");
_mappedRequest.Data.Headers.ShouldNotContain(x => x.Key == "Content-ContentLocation");
_mappedRequest.Data.Headers.ShouldNotContain(x => x.Key == "Content-Length");
_mappedRequest.Data.Headers.ShouldNotContain(x => x.Key == "Content-Type");
}
private void ThenTheOtherContentTypeHeadersAreNotMapped()
{
_mappedRequest.Data.Content.Headers.ContentDisposition.ShouldBeNull();
_mappedRequest.Data.Content.Headers.ContentMD5.ShouldBeNull();
_mappedRequest.Data.Content.Headers.ContentRange.ShouldBeNull();
_mappedRequest.Data.Content.Headers.ContentLanguage.ShouldBeEmpty();
_mappedRequest.Data.Content.Headers.ContentEncoding.ShouldBeEmpty();
_mappedRequest.Data.Content.Headers.ContentLocation.ShouldBeNull();
}
private void ThenTheMappedRequestHasContentDispositionHeader(string expected)
{
_mappedRequest.Data.Content.Headers.ContentDisposition.DispositionType.ShouldBe(expected);
}
private void GivenTheContentDispositionIs(string input)
{
_inputRequest.Headers.Add("Content-Disposition", input);
}
private void ThenTheMappedRequestHasContentMD5Header(byte[] expected)
{
_mappedRequest.Data.Content.Headers.ContentMD5.ShouldBe(expected);
}
private void GivenTheContentMD5Is(byte[] input)
{
var base64 = Convert.ToBase64String(input);
_inputRequest.Headers.Add("Content-MD5", base64);
}
private void ThenTheMappedRequestHasContentRangeHeader()
{
_mappedRequest.Data.Content.Headers.ContentRange.From.ShouldBe(1);
_mappedRequest.Data.Content.Headers.ContentRange.To.ShouldBe(2);
}
private void GivenTheContentRangeIs(string input)
{
_inputRequest.Headers.Add("Content-Range", input);
}
private void ThenTheMappedRequestHasContentLocationHeader(string expected)
{
_mappedRequest.Data.Content.Headers.ContentLocation.OriginalString.ShouldBe(expected);
}
private void GivenTheContentLocationIs(string input)
{
_inputRequest.Headers.Add("Content-Location", input);
}
private void ThenTheMappedRequestHasContentLanguageHeader(string expected)
{
_mappedRequest.Data.Content.Headers.ContentLanguage.First().ShouldBe(expected);
}
private void GivenTheContentLanguageIs(string input)
{
_inputRequest.Headers.Add("Content-Language", input);
}
private void ThenTheMappedRequestHasContentEncodingHeader(string expected, string expectedTwo)
{
_mappedRequest.Data.Content.Headers.ContentEncoding.ToArray()[0].ShouldBe(expected);
_mappedRequest.Data.Content.Headers.ContentEncoding.ToArray()[1].ShouldBe(expectedTwo);
}
private void GivenTheContentEncodingIs(string input)
{
_inputRequest.Headers.Add("Content-Encoding", input);
}
private void GivenTheContentTypeIs(string contentType) private void GivenTheContentTypeIs(string contentType)
{ {
_inputRequest.ContentType = contentType; _inputRequest.ContentType = contentType;
@ -146,18 +279,6 @@
_mappedRequest.Data.Content.Headers.ContentLength.ShouldBe(expected); _mappedRequest.Data.Content.Headers.ContentLength.ShouldBe(expected);
} }
[Fact]
public void Should_handle_no_content()
{
this.Given(_ => GivenTheInputRequestHasNoContent())
.And(_ => GivenTheInputRequestHasMethod("GET"))
.And(_ => GivenTheInputRequestHasAValidUri())
.When(_ => WhenMapped())
.Then(_ => ThenNoErrorIsReturned())
.And(_ => ThenTheMappedRequestHasNoContent())
.BDDfy();
}
private void GivenTheInputRequestHasMethod(string method) private void GivenTheInputRequestHasMethod(string method)
{ {
_inputRequest.Method = method; _inputRequest.Method = method;

View File

@ -9,6 +9,7 @@ namespace Ocelot.UnitTests.RequestId
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Net.Http; using System.Net.Http;
using System.Threading.Tasks;
using Moq; using Moq;
using Ocelot.Configuration.Builder; using Ocelot.Configuration.Builder;
using Ocelot.DownstreamRouteFinder; using Ocelot.DownstreamRouteFinder;
@ -40,8 +41,10 @@ namespace Ocelot.UnitTests.RequestId
_loggerFactory = new Mock<IOcelotLoggerFactory>(); _loggerFactory = new Mock<IOcelotLoggerFactory>();
_logger = new Mock<IOcelotLogger>(); _logger = new Mock<IOcelotLogger>();
_loggerFactory.Setup(x => x.CreateLogger<ReRouteRequestIdMiddleware>()).Returns(_logger.Object); _loggerFactory.Setup(x => x.CreateLogger<ReRouteRequestIdMiddleware>()).Returns(_logger.Object);
_next = async context => { _next = context =>
{
context.HttpContext.Response.Headers.Add("LSRequestId", context.HttpContext.TraceIdentifier); context.HttpContext.Response.Headers.Add("LSRequestId", context.HttpContext.TraceIdentifier);
return Task.CompletedTask;
}; };
_middleware = new ReRouteRequestIdMiddleware(_next, _loggerFactory.Object, _repo.Object); _middleware = new ReRouteRequestIdMiddleware(_next, _loggerFactory.Object, _repo.Object);
_downstreamContext.DownstreamRequest = _downstreamRequest; _downstreamContext.DownstreamRequest = _downstreamRequest;

View File

@ -1,137 +0,0 @@
using System;
using System.Net.Http;
using Moq;
using Ocelot.Configuration;
using Ocelot.Configuration.Builder;
using Ocelot.Errors;
using Ocelot.Requester;
using Ocelot.Responses;
using Shouldly;
using TestStack.BDDfy;
using Xunit;
namespace Ocelot.UnitTests.Requester
{
public class DelegatingHandlerHandlerHouseTests
{
private readonly DelegatingHandlerHandlerHouse _house;
private Mock<IDelegatingHandlerHandlerProviderFactory> _factory;
private readonly Mock<IDelegatingHandlerHandlerProvider> _provider;
private DownstreamReRoute _request;
private Response<IDelegatingHandlerHandlerProvider> _result;
public DelegatingHandlerHandlerHouseTests()
{
_provider = new Mock<IDelegatingHandlerHandlerProvider>();
_factory = new Mock<IDelegatingHandlerHandlerProviderFactory>();
_house = new DelegatingHandlerHandlerHouse(_factory.Object);
}
[Fact]
public void should_create_and_store_provider()
{
var reRoute = new DownstreamReRouteBuilder().WithIsQos(true)
.WithHttpHandlerOptions(new HttpHandlerOptions(true, true, false)).WithReRouteKey("key").Build();
this.Given(x => GivenTheRequest(reRoute))
.And(x => GivenTheProviderReturns())
.When(x => WhenIGet())
.Then(x => ThenTheFactoryIsCalled(1))
.And(x => ThenTheProviderIsNotNull())
.BDDfy();
}
[Fact]
public void should_get_provider()
{
var reRoute = new DownstreamReRouteBuilder().WithIsQos(true)
.WithHttpHandlerOptions(new HttpHandlerOptions(true, true, false)).WithReRouteKey("key").Build();
this.Given(x => GivenTheRequest(reRoute))
.And(x => GivenTheProviderReturns())
.And(x => WhenIGet())
.And(x => GivenTheFactoryIsCleared())
.When(x => WhenIGet())
.Then(x => ThenTheFactoryIsCalled(0))
.And(x => ThenTheProviderIsNotNull())
.BDDfy();
}
[Fact]
public void should_return_error()
{
var reRoute = new DownstreamReRouteBuilder().WithIsQos(true)
.WithHttpHandlerOptions(new HttpHandlerOptions(true, true, false)).WithReRouteKey("key").Build();
this.Given(x => GivenTheRequest(reRoute))
.And(x => GivenTheProviderThrows())
.When(x => WhenIGet())
.And(x => ThenAnErrorIsReturned())
.BDDfy();
}
[Fact]
public void should_return_error_if_factory_errors()
{
var reRoute = new DownstreamReRouteBuilder().WithIsQos(true)
.WithHttpHandlerOptions(new HttpHandlerOptions(true, true, false)).WithReRouteKey("key").Build();
this.Given(x => GivenTheRequest(reRoute))
.And(x => GivenTheProviderReturnsError())
.When(x => WhenIGet())
.Then(x => ThenAnUnknownErrorIsReturned())
.BDDfy();
}
private void ThenAnUnknownErrorIsReturned()
{
_result.IsError.ShouldBeTrue();
}
private void ThenAnErrorIsReturned()
{
_result.IsError.ShouldBeTrue();
_result.Errors[0].ShouldBeOfType<UnableToFindDelegatingHandlerProviderError>();
}
private void GivenTheProviderThrows()
{
_factory.Setup(x => x.Get(It.IsAny<DownstreamReRoute>())).Throws<Exception>();
}
private void GivenTheFactoryIsCleared()
{
_factory = new Mock<IDelegatingHandlerHandlerProviderFactory>();
}
private void ThenTheProviderIsNotNull()
{
_result.Data.ShouldBe(_provider.Object);
}
private void WhenIGet()
{
_result = _house.Get(_request);
}
private void GivenTheRequest(DownstreamReRoute request)
{
_request = request;
}
private void GivenTheProviderReturns()
{
_factory.Setup(x => x.Get(It.IsAny<DownstreamReRoute>())).Returns(new OkResponse<IDelegatingHandlerHandlerProvider>(_provider.Object));
}
private void GivenTheProviderReturnsError()
{
_factory.Setup(x => x.Get(It.IsAny<DownstreamReRoute>())).Returns(new ErrorResponse<IDelegatingHandlerHandlerProvider>(It.IsAny<Error>()));
}
private void ThenTheFactoryIsCalled(int times)
{
_factory.Verify(x => x.Get(_request), Times.Exactly(times));
}
}
}

View File

@ -1,6 +1,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Net.Http; using System.Net.Http;
using Microsoft.Extensions.DependencyInjection;
using Moq; using Moq;
using Ocelot.Configuration; using Ocelot.Configuration;
using Ocelot.Configuration.Builder; using Ocelot.Configuration.Builder;
@ -17,45 +18,191 @@ namespace Ocelot.UnitTests.Requester
{ {
public class DelegatingHandlerHandlerProviderFactoryTests public class DelegatingHandlerHandlerProviderFactoryTests
{ {
private readonly DelegatingHandlerHandlerProviderFactory _factory; private DelegatingHandlerHandlerFactory _factory;
private Mock<IOcelotLoggerFactory> _loggerFactory; private readonly Mock<IOcelotLoggerFactory> _loggerFactory;
private DownstreamReRoute _request; private DownstreamReRoute _request;
private Response<IDelegatingHandlerHandlerProvider> _provider; private Response<List<Func<DelegatingHandler>>> _result;
private readonly Mock<IDelegatingHandlerHandlerProvider> _allRoutesProvider;
private readonly Mock<IQosProviderHouse> _qosProviderHouse; private readonly Mock<IQosProviderHouse> _qosProviderHouse;
private readonly Mock<ITracingHandlerFactory> _tracingFactory; private readonly Mock<ITracingHandlerFactory> _tracingFactory;
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>();
_allRoutesProvider = new Mock<IDelegatingHandlerHandlerProvider>();
_loggerFactory = new Mock<IOcelotLoggerFactory>(); _loggerFactory = new Mock<IOcelotLoggerFactory>();
_factory = new DelegatingHandlerHandlerProviderFactory(_loggerFactory.Object, _allRoutesProvider.Object, _tracingFactory.Object, _qosProviderHouse.Object); _services = new ServiceCollection();
} }
private void GivenTheQosProviderHouseReturns(Response<IQoSProvider> qosProvider) [Fact]
public void should_follow_ordering_add_specifics()
{ {
_qosProviderHouse var reRoute = new DownstreamReRouteBuilder().WithIsQos(true)
.Setup(x => x.Get(It.IsAny<DownstreamReRoute>())) .WithHttpHandlerOptions(new HttpHandlerOptions(true, true, true))
.Returns(qosProvider); .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]
public void should_all_from_all_routes_provider_and_qos() public void should_all_from_all_routes_provider_and_qos()
{ {
var handlers = new List<Func<DelegatingHandler>>
{
() => new FakeDelegatingHandler(0),
() => new FakeDelegatingHandler(1)
};
var reRoute = new DownstreamReRouteBuilder().WithIsQos(true) var reRoute = new DownstreamReRouteBuilder().WithIsQos(true)
.WithHttpHandlerOptions(new HttpHandlerOptions(true, true, false)).WithReRouteKey("").Build(); .WithHttpHandlerOptions(new HttpHandlerOptions(true, true, false)).WithReRouteKey("").Build();
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 => GivenTheAllRoutesProviderReturns(handlers)) .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())
@ -70,7 +217,7 @@ namespace Ocelot.UnitTests.Requester
.WithHttpHandlerOptions(new HttpHandlerOptions(true, true, false)).WithReRouteKey("").Build(); .WithHttpHandlerOptions(new HttpHandlerOptions(true, true, false)).WithReRouteKey("").Build();
this.Given(x => GivenTheFollowingRequest(reRoute)) this.Given(x => GivenTheFollowingRequest(reRoute))
.And(x => GivenTheAllRoutesProviderReturns()) .And(x => GivenTheServiceProviderReturnsNothing())
.When(x => WhenIGet()) .When(x => WhenIGet())
.Then(x => ThenNoDelegatesAreInTheProvider()) .Then(x => ThenNoDelegatesAreInTheProvider())
.BDDfy(); .BDDfy();
@ -84,7 +231,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 => GivenTheAllRoutesProviderReturns()) .And(x => GivenTheServiceProviderReturnsNothing())
.When(x => WhenIGet()) .When(x => WhenIGet())
.Then(x => ThenThereIsDelegatesInProvider(1)) .Then(x => ThenThereIsDelegatesInProvider(1))
.And(x => ThenItIsPolly(0)) .And(x => ThenItIsPolly(0))
@ -99,50 +246,92 @@ namespace Ocelot.UnitTests.Requester
this.Given(x => GivenTheFollowingRequest(reRoute)) this.Given(x => GivenTheFollowingRequest(reRoute))
.And(x => GivenTheQosProviderHouseReturns(new ErrorResponse<IQoSProvider>(It.IsAny<Error>()))) .And(x => GivenTheQosProviderHouseReturns(new ErrorResponse<IQoSProvider>(It.IsAny<Error>())))
.And(x => GivenTheAllRoutesProviderReturns()) .And(x => GivenTheServiceProviderReturnsNothing())
.When(x => WhenIGet()) .When(x => WhenIGet())
.Then(x => ThenAnErrorIsReturned()) .Then(x => ThenAnErrorIsReturned())
.BDDfy(); .BDDfy();
} }
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 TTwo : DelegatingHandler
{
_services.AddTransient<TOne>();
_services.AddTransient<GlobalDelegatingHandler>(s => {
var service = s.GetService<TOne>();
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()
{
_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.Get(); var delegates = _result.Data;
var del = delegates[0].Invoke(); var del = delegates[0].Invoke();
var handler = (FakeDelegatingHandler) del; var handler = (FakeDelegatingHandler) del;
handler.Order.ShouldBe(0); handler.Order.ShouldBe(1);
del = delegates[1].Invoke(); del = delegates[1].Invoke();
handler = (FakeDelegatingHandler)del; var handlerTwo = (FakeDelegatingHandlerTwo) del;
handler.Order.ShouldBe(1); handlerTwo.Order.ShouldBe(2);
} }
private void GivenTheAllRoutesProviderReturns() private void GivenTheQosProviderHouseReturns(Response<IQoSProvider> qosProvider)
{ {
_allRoutesProvider.Setup(x => x.Get()).Returns(new List<Func<DelegatingHandler>>()); _qosProviderHouse
} .Setup(x => x.Get(It.IsAny<DownstreamReRoute>()))
.Returns(qosProvider);
private void GivenTheAllRoutesProviderReturns(List<Func<DelegatingHandler>> handlers)
{
_allRoutesProvider.Setup(x => x.Get()).Returns(handlers);
} }
private void ThenItIsPolly(int i) private void ThenItIsPolly(int i)
{ {
var delegates = _provider.Data.Get(); 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.Get().Count.ShouldBe(count); _result.Data.Count.ShouldBe(count);
} }
private void GivenTheFollowingRequest(DownstreamReRoute request) private void GivenTheFollowingRequest(DownstreamReRoute request)
@ -152,13 +341,19 @@ namespace Ocelot.UnitTests.Requester
private void WhenIGet() private void WhenIGet()
{ {
_provider = _factory.Get(_request); _serviceProvider = _services.BuildServiceProvider();
_factory = new DelegatingHandlerHandlerFactory(_loggerFactory.Object, _tracingFactory.Object, _qosProviderHouse.Object, _serviceProvider);
_result = _factory.Get(_request);
} }
private void ThenNoDelegatesAreInTheProvider() private void ThenNoDelegatesAreInTheProvider()
{ {
_provider.ShouldNotBeNull(); _result.ShouldNotBeNull();
_provider.Data.Get().Count.ShouldBe(0); _result.Data.Count.ShouldBe(0);
} }
} }
internal class FakeTracingHandler : DelegatingHandler, ITracingHandler
{
}
} }

View File

@ -1,62 +0,0 @@
using System;
using System.Collections.Generic;
using System.Net.Http;
using Ocelot.Requester;
using Shouldly;
using TestStack.BDDfy;
using Xunit;
namespace Ocelot.UnitTests.Requester
{
public class DelegatingHandlerHandlerProviderTests
{
private readonly DelegatingHandlerHandlerProvider _provider;
private List<Func<DelegatingHandler>> _handlers;
public DelegatingHandlerHandlerProviderTests()
{
_provider = new DelegatingHandlerHandlerProvider();
}
[Fact]
public void should_return_empty_list()
{
this.When(x => WhenIGet())
.Then(x => ThenAnEmptyListIsReturned())
.BDDfy();
}
[Fact]
public void should_get_delegating_handlers_in_order_first_in_first_out()
{
this.Given(x => GivenTheHandlers())
.When(x => WhenIGet())
.Then(x => ThenTheHandlersAreReturnedInOrder())
.BDDfy();
}
private void ThenAnEmptyListIsReturned()
{
_handlers.Count.ShouldBe(0);
}
private void ThenTheHandlersAreReturnedInOrder()
{
var handler = (FakeDelegatingHandler)_handlers[0].Invoke();
handler.Order.ShouldBe(0);
handler = (FakeDelegatingHandler)_handlers[1].Invoke();
handler.Order.ShouldBe(1);
}
private void WhenIGet()
{
_handlers = _provider.Get();
}
private void GivenTheHandlers()
{
_provider.Add(() => new FakeDelegatingHandler(0));
_provider.Add(() => new FakeDelegatingHandler(1));
}
}
}

View File

@ -9,6 +9,7 @@ namespace Ocelot.UnitTests.Requester
{ {
public FakeDelegatingHandler() public FakeDelegatingHandler()
{ {
Order = 1;
} }
public FakeDelegatingHandler(int order) public FakeDelegatingHandler(int order)
@ -17,12 +18,67 @@ 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());
}
}
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 FakeDelegatingHandlerTwo()
{
Order = 2;
}
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());
} }
} }
} }

View File

@ -15,25 +15,22 @@ namespace Ocelot.UnitTests.Requester
public class HttpClientBuilderTests public class HttpClientBuilderTests
{ {
private readonly HttpClientBuilder _builder; private readonly HttpClientBuilder _builder;
private readonly Mock<IDelegatingHandlerHandlerHouse> _house; private readonly Mock<IDelegatingHandlerHandlerFactory> _factory;
private readonly Mock<IDelegatingHandlerHandlerProvider> _provider;
private IHttpClient _httpClient; private IHttpClient _httpClient;
private HttpResponseMessage _response; private HttpResponseMessage _response;
private DownstreamReRoute _request; private DownstreamReRoute _request;
public HttpClientBuilderTests() public HttpClientBuilderTests()
{ {
_provider = new Mock<IDelegatingHandlerHandlerProvider>(); _factory = new Mock<IDelegatingHandlerHandlerFactory>();
_house = new Mock<IDelegatingHandlerHandlerHouse>(); _builder = new HttpClientBuilder(_factory.Object);
_builder = new HttpClientBuilder(_house.Object);
} }
[Fact] [Fact]
public void should_build_http_client() public void should_build_http_client()
{ {
this.Given(x => GivenTheProviderReturns()) this.Given(x => GivenTheFactoryReturns())
.And(x => GivenARequest()) .And(x => GivenARequest())
.And(x => GivenTheHouseReturns())
.When(x => WhenIBuild()) .When(x => WhenIBuild())
.Then(x => ThenTheHttpClientShouldNotBeNull()) .Then(x => ThenTheHttpClientShouldNotBeNull())
.BDDfy(); .BDDfy();
@ -51,9 +48,8 @@ namespace Ocelot.UnitTests.Requester
() => fakeTwo () => fakeTwo
}; };
this.Given(x => GivenTheProviderReturns(handlers)) this.Given(x => GivenTheFactoryReturns(handlers))
.And(x => GivenARequest()) .And(x => GivenARequest())
.And(x => GivenTheHouseReturns())
.And(x => WhenIBuild()) .And(x => WhenIBuild())
.When(x => WhenICallTheClient()) .When(x => WhenICallTheClient())
.Then(x => ThenTheFakeAreHandledInOrder(fakeOne, fakeTwo)) .Then(x => ThenTheFakeAreHandledInOrder(fakeOne, fakeTwo))
@ -69,13 +65,6 @@ namespace Ocelot.UnitTests.Requester
_request = reRoute; _request = reRoute;
} }
private void GivenTheHouseReturns()
{
_house
.Setup(x => x.Get(It.IsAny<DownstreamReRoute>()))
.Returns(new OkResponse<IDelegatingHandlerHandlerProvider>(_provider.Object));
}
private void ThenSomethingIsReturned() private void ThenSomethingIsReturned()
{ {
_response.ShouldNotBeNull(); _response.ShouldNotBeNull();
@ -91,18 +80,20 @@ namespace Ocelot.UnitTests.Requester
fakeOne.TimeCalled.ShouldBeGreaterThan(fakeTwo.TimeCalled); fakeOne.TimeCalled.ShouldBeGreaterThan(fakeTwo.TimeCalled);
} }
private void GivenTheProviderReturns() private void GivenTheFactoryReturns()
{ {
_provider var handlers = new List<Func<DelegatingHandler>>(){ () => new FakeDelegatingHandler()};
.Setup(x => x.Get())
.Returns(new List<Func<DelegatingHandler>>(){ () => new FakeDelegatingHandler()}); _factory
.Setup(x => x.Get(It.IsAny<DownstreamReRoute>()))
.Returns(new OkResponse<List<Func<DelegatingHandler>>>(handlers));
} }
private void GivenTheProviderReturns(List<Func<DelegatingHandler>> handlers) private void GivenTheFactoryReturns(List<Func<DelegatingHandler>> handlers)
{ {
_provider _factory
.Setup(x => x.Get()) .Setup(x => x.Get(It.IsAny<DownstreamReRoute>()))
.Returns(handlers); .Returns(new OkResponse<List<Func<DelegatingHandler>>>(handlers));
} }
private void WhenIBuild() private void WhenIBuild()

View File

@ -18,8 +18,7 @@ namespace Ocelot.UnitTests.Requester
public class HttpClientHttpRequesterTest public class HttpClientHttpRequesterTest
{ {
private readonly Mock<IHttpClientCache> _cacheHandlers; private readonly Mock<IHttpClientCache> _cacheHandlers;
private Mock<IDelegatingHandlerHandlerHouse> _house; private Mock<IDelegatingHandlerHandlerFactory> _house;
private Mock<IDelegatingHandlerHandlerProvider> _provider;
private Response<HttpResponseMessage> _response; private Response<HttpResponseMessage> _response;
private readonly HttpClientHttpRequester _httpClientRequester; private readonly HttpClientHttpRequester _httpClientRequester;
private DownstreamContext _request; private DownstreamContext _request;
@ -28,10 +27,8 @@ namespace Ocelot.UnitTests.Requester
public HttpClientHttpRequesterTest() public HttpClientHttpRequesterTest()
{ {
_provider = new Mock<IDelegatingHandlerHandlerProvider>(); _house = new Mock<IDelegatingHandlerHandlerFactory>();
_provider.Setup(x => x.Get()).Returns(new List<Func<DelegatingHandler>>()); _house.Setup(x => x.Get(It.IsAny<DownstreamReRoute>())).Returns(new OkResponse<List<Func<DelegatingHandler>>>(new List<Func<DelegatingHandler>>()));
_house = new Mock<IDelegatingHandlerHandlerHouse>();
_house.Setup(x => x.Get(It.IsAny<DownstreamReRoute>())).Returns(new OkResponse<IDelegatingHandlerHandlerProvider>(_provider.Object));
_logger = new Mock<IOcelotLogger>(); _logger = new Mock<IOcelotLogger>();
_loggerFactory = new Mock<IOcelotLoggerFactory>(); _loggerFactory = new Mock<IOcelotLoggerFactory>();
_loggerFactory _loggerFactory

View File

@ -13,6 +13,7 @@ namespace Ocelot.UnitTests.Requester
using TestStack.BDDfy; using TestStack.BDDfy;
using Xunit; using Xunit;
using Shouldly; using Shouldly;
using System.Threading.Tasks;
public class HttpRequesterMiddlewareTests public class HttpRequesterMiddlewareTests
{ {
@ -30,9 +31,7 @@ namespace Ocelot.UnitTests.Requester
_loggerFactory = new Mock<IOcelotLoggerFactory>(); _loggerFactory = new Mock<IOcelotLoggerFactory>();
_logger = new Mock<IOcelotLogger>(); _logger = new Mock<IOcelotLogger>();
_loggerFactory.Setup(x => x.CreateLogger<HttpRequesterMiddleware>()).Returns(_logger.Object); _loggerFactory.Setup(x => x.CreateLogger<HttpRequesterMiddleware>()).Returns(_logger.Object);
_next = async context => { _next = context => Task.CompletedTask;
//do nothing
};
_middleware = new HttpRequesterMiddleware(_next, _loggerFactory.Object, _requester.Object); _middleware = new HttpRequesterMiddleware(_next, _loggerFactory.Object, _requester.Object);
} }

View File

@ -5,6 +5,7 @@ namespace Ocelot.UnitTests.Responder
{ {
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using System.Net.Http; using System.Net.Http;
using System.Threading.Tasks;
using Moq; using Moq;
using Ocelot.DownstreamRouteFinder.Finder; using Ocelot.DownstreamRouteFinder.Finder;
using Ocelot.Errors; using Ocelot.Errors;
@ -32,9 +33,7 @@ namespace Ocelot.UnitTests.Responder
_loggerFactory = new Mock<IOcelotLoggerFactory>(); _loggerFactory = new Mock<IOcelotLoggerFactory>();
_logger = new Mock<IOcelotLogger>(); _logger = new Mock<IOcelotLogger>();
_loggerFactory.Setup(x => x.CreateLogger<ResponderMiddleware>()).Returns(_logger.Object); _loggerFactory.Setup(x => x.CreateLogger<ResponderMiddleware>()).Returns(_logger.Object);
_next = async context => { _next = context => Task.CompletedTask;
//do nothing
};
_middleware = new ResponderMiddleware(_next, _responder.Object, _loggerFactory.Object, _codeMapper.Object); _middleware = new ResponderMiddleware(_next, _responder.Object, _loggerFactory.Object, _codeMapper.Object);
} }