Merge pull request #27 from TomPallister/develop

merge newest code
This commit is contained in:
geffzhang 2017-08-26 09:45:51 +08:00 committed by GitHub
commit 2c4aaaadaa
92 changed files with 2479 additions and 1980 deletions

1
.gitignore vendored
View File

@ -183,6 +183,7 @@ ClientBin/
*.dbmdl *.dbmdl
*.dbproj.schemaview *.dbproj.schemaview
*.pfx *.pfx
!idsrv3test.pfx
*.publishsettings *.publishsettings
node_modules/ node_modules/
orleans.codegen.cs orleans.codegen.cs

View File

@ -16,7 +16,7 @@ var artifactsDir = Directory("artifacts");
// unit testing // unit testing
var artifactsForUnitTestsDir = artifactsDir + Directory("UnitTests"); var artifactsForUnitTestsDir = artifactsDir + Directory("UnitTests");
var unitTestAssemblies = @"./test/Ocelot.UnitTests/Ocelot.UnitTests.csproj"; var unitTestAssemblies = @"./test/Ocelot.UnitTests/Ocelot.UnitTests.csproj";
var minCodeCoverage = 75d; var minCodeCoverage = 76.4d;
var coverallsRepoToken = "coveralls-repo-token-ocelot"; var coverallsRepoToken = "coveralls-repo-token-ocelot";
var coverallsRepo = "https://coveralls.io/github/TomPallister/Ocelot"; var coverallsRepo = "https://coveralls.io/github/TomPallister/Ocelot";
@ -229,11 +229,11 @@ Task("CreatePackages")
EnsureDirectoryExists(packagesDir); EnsureDirectoryExists(packagesDir);
CopyFiles("./src/**/Ocelot.*.nupkg", packagesDir); CopyFiles("./src/**/Ocelot.*.nupkg", packagesDir);
GenerateReleaseNotes(releaseNotesFile); //GenerateReleaseNotes(releaseNotesFile);
System.IO.File.WriteAllLines(artifactsFile, new[]{ System.IO.File.WriteAllLines(artifactsFile, new[]{
"nuget:Ocelot." + buildVersion + ".nupkg", "nuget:Ocelot." + buildVersion + ".nupkg",
"releaseNotes:releasenotes.md" //"releaseNotes:releasenotes.md"
}); });
if (AppVeyor.IsRunningOnAppVeyor) if (AppVeyor.IsRunningOnAppVeyor)

View File

@ -1,103 +0,0 @@
{
"ReRoutes": [
{
# The downstream path we are forwarding the request to, ocelot will not add a trailing slash.
# Ocelot replaces any placeholders {etc} with matched values from the incoming request.
"DownstreamPathTemplate": "/identityserverexample/{someid}/something",
# The scheme you want Ocelot to use when making the downstream request
"DownstreamScheme": "https",
# The port you want Ocelot to use when making the downstream request, will default to
# scheme if nothing set
"DownstreamPort": 80,
# The host address of the downstream service, should not have a trailing slash or scheme
# if there is a trailing slash Ocelot will remove it.
"DownstreamHost" "localhost"
# The path template we are listening on for this re route, Ocelot will add a trailing
# slash to this property. Then when a request is made Ocelot makes sure a trailing
# slash is added, so everything matches
"UpstreamPathTemplate": "/identityserverexample",
# The method we are listening for on this re route
"UpstreamHttpMethod": "Get",
# Only support identity server at the moment
"AuthenticationOptions": {
"Provider": "IdentityServer",
"ProviderRootUrl": "http://localhost:52888",
"ApiName": "api",
"AllowedScopes": [
"openid",
"offline_access"
],
# Required if using reference tokens
"ApiSecret": "secret"
},
# WARNING - will overwrite any headers already in the request with these values.
# Ocelot will look in the user claims for the key in [] then return the value and save
# it as a header with the given key before the colon (:). The index selection on value
# means that Ocelot will use the delimiter specified after the next > to split the
# claim value and return the index specified.
"AddHeadersToRequest": {
"CustomerId": "Claims[CustomerId] > value",
"LocationId": "Claims[LocationId] > value",
"UserType": "Claims[sub] > value[0] > |",
"UserId": "Claims[sub] > value[1] > |"
},
# WARNING - will overwrite any claims already in the request with these values.
# Ocelot will look in the user claims for the key in [] then return the value and save
# it as a claim with the given key before the colon (:). The index selection on value
# means that Ocelot will use the delimiter specified after the next > to split the
# claim value and return the index specified.
"AddClaimsToRequest": {
"CustomerId": "Claims[CustomerId] > value",
"LocationId": "Claims[LocationId] > value",
"UserType": "Claims[sub] > value[0] > |",
"UserId": "Claims[sub] > value[1] > |"
},
# WARNING - will overwrite any query string entries already in the request with these values.
# Ocelot will look in the user claims for the key in [] then return the value and save
# it as a query string with the given key before the colon (:). The index selection on value
# means that Ocelot will use the delimiter specified after the next > to split the
# claim value and return the index specified.
"AddQueriesToRequest": {
"CustomerId": "Claims[CustomerId] > value",
"LocationId": "Claims[LocationId] > value",
"UserType": "Claims[sub] > value[0] > |",
"UserId": "Claims[sub] > value[1] > |"
},
# This specifies any claims that are required for the user to access this re route.
# In this example the user must have the claim type UserType and
# the value must be registered
"RouteClaimsRequirement": {
"UserType": "registered"
},
# This tells Ocelot to look for a header and use its value as a request/correlation id.
# If it is set here then the id will be forwarded to the downstream service. If it
# does not then it will not be forwarded
"RequestIdKey": "OcRequestId",
# If this is set the response from the downstream service will be cached using the key that called it.
# This gives the user a chance to influence the key by adding some random query string paramter for
# a user id or something that would get ignored by the downstream service. This is a hack and I
# intend to provide a mechanism the user can specify for the ttl caching. Also want to expand
# the caching a lot.
"FileCacheOptions": { "TtlSeconds": 15 },
# The value of this is used when matching the upstream template to an upstream url.
"ReRouteIsCaseSensitive": false,
# Tells Ocelot the name of the service it is looking when making requests to service discovery
# for hosts and ports
"ServiceName": "product"
# Tells Ocelot which load balancer to use when making downstream requests.
"LoadBalancer": "RoundRobin"
},
# This section is meant to be for global configuration settings
"GlobalConfiguration": {
# If this is set it will override any route specific request id keys, behaves the same
# otherwise
"RequestIdKey": "OcRequestId",
# If set Ocelot will try and use service discovery to locate downstream hosts and ports
"ServiceDiscoveryProvider":
{
"Provider":"Consul",
"Host":"localhost",
"Port":8500
}
}
}

View File

@ -1 +0,0 @@
{"ReRoutes":[{"DownstreamPathTemplate":"/","UpstreamPathTemplate":"/","UpstreamHttpMethod":"get","AuthenticationOptions":{"Provider":null,"ProviderRootUrl":null,"ScopeName":null,"RequireHttps":false,"AdditionalScopes":[],"ScopeSecret":null},"AddHeadersToRequest":{},"AddClaimsToRequest":{},"RouteClaimsRequirement":{},"AddQueriesToRequest":{},"RequestIdKey":null,"FileCacheOptions":{"TtlSeconds":0},"ReRouteIsCaseSensitive":false,"ServiceName":null,"DownstreamScheme":"https","DownstreamHost":"localhost","DownstreamPort":80,"QoSOptions":{"ExceptionsAllowedBeforeBreaking":0,"DurationOfBreak":0,"TimeoutValue":0},"LoadBalancer":null,"RateLimitOptions":{"ClientWhitelist":[],"EnableRateLimiting":false,"Period":null,"PeriodTimespan":0.0,"Limit":0}},{"DownstreamPathTemplate":"/","UpstreamPathTemplate":"/test","UpstreamHttpMethod":"get","AuthenticationOptions":{"Provider":null,"ProviderRootUrl":null,"ScopeName":null,"RequireHttps":false,"AdditionalScopes":[],"ScopeSecret":null},"AddHeadersToRequest":{},"AddClaimsToRequest":{},"RouteClaimsRequirement":{},"AddQueriesToRequest":{},"RequestIdKey":null,"FileCacheOptions":{"TtlSeconds":0},"ReRouteIsCaseSensitive":false,"ServiceName":null,"DownstreamScheme":"https","DownstreamHost":"localhost","DownstreamPort":80,"QoSOptions":{"ExceptionsAllowedBeforeBreaking":0,"DurationOfBreak":0,"TimeoutValue":0},"LoadBalancer":null,"RateLimitOptions":{"ClientWhitelist":[],"EnableRateLimiting":false,"Period":null,"PeriodTimespan":0.0,"Limit":0}}],"GlobalConfiguration":{"RequestIdKey":"RequestId","ServiceDiscoveryProvider":{"Provider":"test","Host":"127.0.0.1","Port":0},"AdministrationPath":"/administration","RateLimitOptions":{"ClientIdHeader":"ClientId","QuotaExceededMessage":null,"RateLimitCounterPrefix":"ocelot","DisableRateLimitHeaders":false,"HttpStatusCode":429}}}

View File

@ -1,3 +0,0 @@
Routes:
- Downstream: http://localhost:51879/
Upstream: /heee

View File

@ -1,3 +1,4 @@
using System;
using IdentityServer4.AccessTokenValidation; using IdentityServer4.AccessTokenValidation;
using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
@ -5,6 +6,8 @@ using Ocelot.Responses;
namespace Ocelot.Authentication.Handler.Creator namespace Ocelot.Authentication.Handler.Creator
{ {
using Ocelot.Configuration;
using AuthenticationOptions = Configuration.AuthenticationOptions; using AuthenticationOptions = Configuration.AuthenticationOptions;
/// <summary> /// <summary>
@ -16,15 +19,31 @@ namespace Ocelot.Authentication.Handler.Creator
{ {
var builder = app.New(); var builder = app.New();
if (authOptions.Provider.ToLower() == "jwt")
{
var authenticationConfig = authOptions.Config as JwtConfig;
builder.UseJwtBearerAuthentication(
new JwtBearerOptions()
{
Authority = authenticationConfig.Authority,
Audience = authenticationConfig.Audience
});
}
else
{
var authenticationConfig = authOptions.Config as IdentityServerConfig;
builder.UseIdentityServerAuthentication(new IdentityServerAuthenticationOptions builder.UseIdentityServerAuthentication(new IdentityServerAuthenticationOptions
{ {
Authority = authOptions.ProviderRootUrl, Authority = authenticationConfig.ProviderRootUrl,
ApiName = authOptions.ApiName, ApiName = authenticationConfig.ApiName,
RequireHttpsMetadata = authOptions.RequireHttps, RequireHttpsMetadata = authenticationConfig.RequireHttps,
AllowedScopes = authOptions.AllowedScopes, AllowedScopes = authOptions.AllowedScopes,
SupportedTokens = SupportedTokens.Both, SupportedTokens = SupportedTokens.Both,
ApiSecret = authOptions.ApiSecret ApiSecret = authenticationConfig.ApiSecret
}); });
}
var authenticationNext = builder.Build(); var authenticationNext = builder.Build();

View File

@ -2,6 +2,7 @@
{ {
public enum SupportedAuthenticationProviders public enum SupportedAuthenticationProviders
{ {
IdentityServer IdentityServer,
Jwt
} }
} }

View File

@ -0,0 +1,59 @@
using System;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Ocelot.Configuration;
namespace Ocelot.Authentication.JsonConverters
{
public class AuthenticationConfigConverter : JsonConverter
{
public override bool CanWrite => false;
public override bool CanRead => true;
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new InvalidOperationException("Use default serialization.");
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var jsonObject = JObject.Load(reader);
var setting = default(IAuthenticationConfig);
if (jsonObject["Provider"] != null)
{
switch (jsonObject["Provider"].Value<string>())
{
case "Jwt":
setting = new JwtConfig(
jsonObject["Authority"].Value<string>(),
jsonObject["Audience"].Value<string>());
break;
default:
setting = new IdentityServerConfig(
jsonObject["ProviderRootUrl"].Value<string>(),
jsonObject["ApiName"].Value<string>(),
jsonObject["RequireHttps"].Value<bool>(),
jsonObject["ApiSecret"].Value<string>());
break;
}
}
else
{
setting = new IdentityServerConfig(string.Empty, string.Empty, false, string.Empty);
}
serializer.Populate(jsonObject.CreateReader(), setting);
return setting;
}
public override bool CanConvert(Type objectType)
{
return objectType == typeof(IAuthenticationConfig);
}
}
}

View File

@ -34,11 +34,9 @@ namespace Ocelot.Authentication.Middleware
public async Task Invoke(HttpContext context) public async Task Invoke(HttpContext context)
{ {
_logger.TraceMiddlewareEntry();
if (IsAuthenticatedRoute(DownstreamRoute.ReRoute)) if (IsAuthenticatedRoute(DownstreamRoute.ReRoute))
{ {
_logger.LogDebug($"{context.Request.Path} is an authenticated route. {MiddlwareName} checking if client is authenticated"); _logger.LogDebug($"{context.Request.Path} is an authenticated route. {MiddlewareName} checking if client is authenticated");
var authenticationHandler = _authHandlerFactory.Get(_app, DownstreamRoute.ReRoute.AuthenticationOptions); var authenticationHandler = _authHandlerFactory.Get(_app, DownstreamRoute.ReRoute.AuthenticationOptions);
@ -46,7 +44,6 @@ namespace Ocelot.Authentication.Middleware
{ {
_logger.LogError($"Error getting authentication handler for {context.Request.Path}. {authenticationHandler.Errors.ToErrorString()}"); _logger.LogError($"Error getting authentication handler for {context.Request.Path}. {authenticationHandler.Errors.ToErrorString()}");
SetPipelineError(authenticationHandler.Errors); SetPipelineError(authenticationHandler.Errors);
_logger.TraceMiddlewareCompleted();
return; return;
} }
@ -56,11 +53,7 @@ namespace Ocelot.Authentication.Middleware
if (context.User.Identity.IsAuthenticated) if (context.User.Identity.IsAuthenticated)
{ {
_logger.LogDebug($"Client has been authenticated for {context.Request.Path}"); _logger.LogDebug($"Client has been authenticated for {context.Request.Path}");
_logger.TraceInvokeNext();
await _next.Invoke(context); await _next.Invoke(context);
_logger.TraceInvokeNextCompleted();
_logger.TraceMiddlewareCompleted();
} }
else else
{ {
@ -72,8 +65,6 @@ namespace Ocelot.Authentication.Middleware
_logger.LogError($"Client has NOT been authenticated for {context.Request.Path} and pipeline error set. {error.ToErrorString()}"); _logger.LogError($"Client has NOT been authenticated for {context.Request.Path} and pipeline error set. {error.ToErrorString()}");
SetPipelineError(error); SetPipelineError(error);
_logger.TraceMiddlewareCompleted();
return; return;
} }
} }
@ -81,10 +72,7 @@ namespace Ocelot.Authentication.Middleware
{ {
_logger.LogTrace($"No authentication needed for {context.Request.Path}"); _logger.LogTrace($"No authentication needed for {context.Request.Path}");
_logger.TraceInvokeNext();
await _next.Invoke(context); await _next.Invoke(context);
_logger.TraceInvokeNextCompleted();
_logger.TraceMiddlewareCompleted();
} }
} }

View File

@ -33,8 +33,6 @@ namespace Ocelot.Authorisation.Middleware
public async Task Invoke(HttpContext context) public async Task Invoke(HttpContext context)
{ {
_logger.LogDebug("started authorisation");
if (IsAuthenticatedRoute(DownstreamRoute.ReRoute)) if (IsAuthenticatedRoute(DownstreamRoute.ReRoute))
{ {
_logger.LogDebug("route is authenticated scopes must be checked"); _logger.LogDebug("route is authenticated scopes must be checked");
@ -73,7 +71,7 @@ namespace Ocelot.Authorisation.Middleware
if (authorised.IsError) if (authorised.IsError)
{ {
_logger.LogDebug("error authorising user"); _logger.LogDebug($"Error whilst authorising {context.User.Identity.Name} for {context.User.Identity.Name}. Setting pipeline error");
SetPipelineError(authorised.Errors); SetPipelineError(authorised.Errors);
return; return;
@ -81,30 +79,23 @@ namespace Ocelot.Authorisation.Middleware
if (IsAuthorised(authorised)) if (IsAuthorised(authorised))
{ {
_logger.LogDebug("user is authorised calling next middleware"); _logger.LogDebug($"{context.User.Identity.Name} has succesfully been authorised for {DownstreamRoute.ReRoute.UpstreamPathTemplate.Value}. Calling next middleware");
await _next.Invoke(context); await _next.Invoke(context);
_logger.LogDebug("succesfully called next middleware");
} }
else else
{ {
_logger.LogDebug("user is not authorised setting pipeline error"); _logger.LogDebug($"{context.User.Identity.Name} is not authorised to access {DownstreamRoute.ReRoute.UpstreamPathTemplate.Value}. Setting pipeline error");
SetPipelineError(new List<Error> SetPipelineError(new List<Error>
{ {
new UnauthorisedError( new UnauthorisedError($"{context.User.Identity.Name} is not authorised to access {DownstreamRoute.ReRoute.UpstreamPathTemplate.Value}")
$"{context.User.Identity.Name} unable to access {DownstreamRoute.ReRoute.UpstreamPathTemplate.Value}")
}); });
} }
} }
else else
{ {
_logger.LogDebug("AuthorisationMiddleware.Invoke route is not authorised calling next middleware"); _logger.LogDebug($"{DownstreamRoute.ReRoute.DownstreamPathTemplate.Value} route does not require user to be authorised");
await _next.Invoke(context); await _next.Invoke(context);
_logger.LogDebug("succesfully called next middleware");
} }
} }

View File

@ -1,11 +1,13 @@
using System; using System;
using System.Collections.Generic;
namespace Ocelot.Cache namespace Ocelot.Cache
{ {
public interface IOcelotCache<T> public interface IOcelotCache<T>
{ {
void Add(string key, T value, TimeSpan ttl); void Add(string key, T value, TimeSpan ttl, string region);
void AddAndDelete(string key, T value, TimeSpan ttl); void AddAndDelete(string key, T value, TimeSpan ttl, string region);
T Get(string key); T Get(string key, string region);
void ClearRegion(string region);
} }
} }

View File

@ -0,0 +1,9 @@
using Ocelot.Configuration.File;
namespace Ocelot.Cache
{
public interface IRegionCreator
{
string Create(FileReRoute reRoute);
}
}

View File

@ -1,4 +1,5 @@
using System; using System;
using System.Linq;
using System.Net.Http; using System.Net.Http;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
@ -13,16 +14,19 @@ namespace Ocelot.Cache.Middleware
private readonly RequestDelegate _next; private readonly RequestDelegate _next;
private readonly IOcelotLogger _logger; private readonly IOcelotLogger _logger;
private readonly IOcelotCache<HttpResponseMessage> _outputCache; private readonly IOcelotCache<HttpResponseMessage> _outputCache;
private readonly IRegionCreator _regionCreator;
public OutputCacheMiddleware(RequestDelegate next, public OutputCacheMiddleware(RequestDelegate next,
IOcelotLoggerFactory loggerFactory, IOcelotLoggerFactory loggerFactory,
IRequestScopedDataRepository scopedDataRepository, IRequestScopedDataRepository scopedDataRepository,
IOcelotCache<HttpResponseMessage> outputCache) IOcelotCache<HttpResponseMessage> outputCache,
IRegionCreator regionCreator)
:base(scopedDataRepository) :base(scopedDataRepository)
{ {
_next = next; _next = next;
_outputCache = outputCache; _outputCache = outputCache;
_logger = loggerFactory.CreateLogger<OutputCacheMiddleware>(); _logger = loggerFactory.CreateLogger<OutputCacheMiddleware>();
_regionCreator = regionCreator;
} }
public async Task Invoke(HttpContext context) public async Task Invoke(HttpContext context)
@ -33,11 +37,11 @@ namespace Ocelot.Cache.Middleware
return; return;
} }
var downstreamUrlKey = DownstreamRequest.RequestUri.OriginalString; var downstreamUrlKey = $"{DownstreamRequest.Method.Method}-{DownstreamRequest.RequestUri.OriginalString}";
_logger.LogDebug("started checking cache for {downstreamUrlKey}", downstreamUrlKey); _logger.LogDebug("started checking cache for {downstreamUrlKey}", downstreamUrlKey);
var cached = _outputCache.Get(downstreamUrlKey); var cached = _outputCache.Get(downstreamUrlKey, DownstreamRoute.ReRoute.CacheOptions.Region);
if (cached != null) if (cached != null)
{ {
@ -54,8 +58,6 @@ namespace Ocelot.Cache.Middleware
await _next.Invoke(context); await _next.Invoke(context);
_logger.LogDebug("succesfully called next middleware");
if (PipelineError) if (PipelineError)
{ {
_logger.LogDebug("there was a pipeline error for {downstreamUrlKey}", downstreamUrlKey); _logger.LogDebug("there was a pipeline error for {downstreamUrlKey}", downstreamUrlKey);
@ -65,7 +67,7 @@ namespace Ocelot.Cache.Middleware
var response = HttpResponseMessage; var response = HttpResponseMessage;
_outputCache.Add(downstreamUrlKey, response, TimeSpan.FromSeconds(DownstreamRoute.ReRoute.FileCacheOptions.TtlSeconds)); _outputCache.Add(downstreamUrlKey, response, TimeSpan.FromSeconds(DownstreamRoute.ReRoute.CacheOptions.TtlSeconds), DownstreamRoute.ReRoute.CacheOptions.Region);
_logger.LogDebug("finished response added to cache for {downstreamUrlKey}", downstreamUrlKey); _logger.LogDebug("finished response added to cache for {downstreamUrlKey}", downstreamUrlKey);
} }

View File

@ -1,4 +1,6 @@
using System; using System;
using System.Collections.Generic;
using System.Linq;
using CacheManager.Core; using CacheManager.Core;
namespace Ocelot.Cache namespace Ocelot.Cache
@ -12,12 +14,12 @@ namespace Ocelot.Cache
_cacheManager = cacheManager; _cacheManager = cacheManager;
} }
public void Add(string key, T value, TimeSpan ttl) public void Add(string key, T value, TimeSpan ttl, string region)
{ {
_cacheManager.Add(new CacheItem<T>(key, value, ExpirationMode.Absolute, ttl)); _cacheManager.Add(new CacheItem<T>(key, region, value, ExpirationMode.Absolute, ttl));
} }
public void AddAndDelete(string key, T value, TimeSpan ttl) public void AddAndDelete(string key, T value, TimeSpan ttl, string region)
{ {
var exists = _cacheManager.Get(key); var exists = _cacheManager.Get(key);
@ -26,12 +28,17 @@ namespace Ocelot.Cache
_cacheManager.Remove(key); _cacheManager.Remove(key);
} }
_cacheManager.Add(new CacheItem<T>(key, value, ExpirationMode.Absolute, ttl)); Add(key, value, ttl, region);
} }
public T Get(string key) public T Get(string key, string region)
{ {
return _cacheManager.Get<T>(key); return _cacheManager.Get<T>(key, region);
}
public void ClearRegion(string region)
{
_cacheManager.ClearRegion(region);
} }
} }
} }

View File

@ -0,0 +1,24 @@
using System.Linq;
using Ocelot.Configuration;
using Ocelot.Configuration.File;
namespace Ocelot.Cache
{
public class RegionCreator : IRegionCreator
{
public string Create(FileReRoute reRoute)
{
if(!string.IsNullOrEmpty(reRoute?.FileCacheOptions?.Region))
{
return reRoute?.FileCacheOptions?.Region;
}
var methods = string.Join("", reRoute.UpstreamHttpMethod.Select(m => m));
var region = $"{methods}{reRoute.UpstreamPathTemplate.Replace("/", "")}";
return region;
}
}
}

View File

@ -0,0 +1,13 @@
using System.Collections.Generic;
namespace Ocelot.Cache
{
public class Regions
{
public Regions(List<string> value)
{
Value = value;
}
public List<string> Value {get;private set;}
}
}

View File

@ -26,8 +26,6 @@ namespace Ocelot.Claims.Middleware
public async Task Invoke(HttpContext context) public async Task Invoke(HttpContext context)
{ {
_logger.LogDebug("started claims middleware");
if (DownstreamRoute.ReRoute.ClaimsToClaims.Any()) if (DownstreamRoute.ReRoute.ClaimsToClaims.Any())
{ {
_logger.LogDebug("this route has instructions to convert claims to other claims"); _logger.LogDebug("this route has instructions to convert claims to other claims");
@ -42,12 +40,7 @@ namespace Ocelot.Claims.Middleware
return; return;
} }
} }
_logger.LogDebug("calling next middleware");
await _next.Invoke(context); await _next.Invoke(context);
_logger.LogDebug("succesfully called next middleware");
} }
} }
} }

View File

@ -2,24 +2,52 @@
namespace Ocelot.Configuration namespace Ocelot.Configuration
{ {
using Newtonsoft.Json;
public class AuthenticationOptions public class AuthenticationOptions
{ {
public AuthenticationOptions(string provider, string providerRootUrl, string apiName, bool requireHttps, List<string> allowedScopes, string apiSecret) public AuthenticationOptions(string provider, List<string> allowedScopes, IAuthenticationConfig config)
{ {
Provider = provider; Provider = provider;
ProviderRootUrl = providerRootUrl;
ApiName = apiName;
RequireHttps = requireHttps;
AllowedScopes = allowedScopes; AllowedScopes = allowedScopes;
ApiSecret = apiSecret; Config = config;
} }
public string Provider { get; private set; } public string Provider { get; private set; }
public List<string> AllowedScopes { get; private set; }
public IAuthenticationConfig Config { get; private set; }
}
public class IdentityServerConfig : IAuthenticationConfig
{
public IdentityServerConfig(string providerRootUrl, string apiName, bool requireHttps, string apiSecret)
{
ProviderRootUrl = providerRootUrl;
ApiName = apiName;
RequireHttps = requireHttps;
ApiSecret = apiSecret;
}
public string ProviderRootUrl { get; private set; } public string ProviderRootUrl { get; private set; }
public string ApiName { get; private set; } public string ApiName { get; private set; }
public string ApiSecret { get; private set; } public string ApiSecret { get; private set; }
public bool RequireHttps { get; private set; } public bool RequireHttps { get; private set; }
public List<string> AllowedScopes { get; private set; } }
public class JwtConfig : IAuthenticationConfig
{
public JwtConfig(string authority, string audience)
{
Audience = audience;
Authority = authority;
} }
public string Audience { get; }
public string Authority { get; }
}
public interface IAuthenticationConfig {}
} }

View File

@ -6,51 +6,93 @@ namespace Ocelot.Configuration.Builder
{ {
private string _provider; private string _provider;
private string _providerRootUrl;
private string _apiName;
private string _apiSecret;
private bool _requireHttps;
private List<string> _allowedScopes; private List<string> _allowedScopes;
private IAuthenticationConfig _identityServerConfig;
public AuthenticationOptionsBuilder WithProvider(string provider) public AuthenticationOptionsBuilder WithProvider(string provider)
{ {
_provider = provider; _provider = provider;
return this; return this;
} }
public AuthenticationOptionsBuilder WithProviderRootUrl(string providerRootUrl)
{
_providerRootUrl = providerRootUrl;
return this;
}
public AuthenticationOptionsBuilder WithApiName(string apiName)
{
_apiName = apiName;
return this;
}
public AuthenticationOptionsBuilder WithApiSecret(string apiSecret)
{
_apiSecret = apiSecret;
return this;
}
public AuthenticationOptionsBuilder WithRequireHttps(bool requireHttps)
{
_requireHttps = requireHttps;
return this;
}
public AuthenticationOptionsBuilder WithAllowedScopes(List<string> allowedScopes) public AuthenticationOptionsBuilder WithAllowedScopes(List<string> allowedScopes)
{ {
_allowedScopes = allowedScopes; _allowedScopes = allowedScopes;
return this; return this;
} }
public AuthenticationOptionsBuilder WithConfig(IAuthenticationConfig config)
{
_identityServerConfig = config;
return this;
}
public AuthenticationOptions Build() public AuthenticationOptions Build()
{ {
return new AuthenticationOptions(_provider, _providerRootUrl, _apiName, _requireHttps, _allowedScopes, _apiSecret); return new AuthenticationOptions(_provider, _allowedScopes, _identityServerConfig);
}
}
public class IdentityServerConfigBuilder
{
private string _providerRootUrl;
private string _apiName;
private string _apiSecret;
private bool _requireHttps;
public IdentityServerConfigBuilder WithProviderRootUrl(string providerRootUrl)
{
_providerRootUrl = providerRootUrl;
return this;
}
public IdentityServerConfigBuilder WithApiName(string apiName)
{
_apiName = apiName;
return this;
}
public IdentityServerConfigBuilder WithApiSecret(string apiSecret)
{
_apiSecret = apiSecret;
return this;
}
public IdentityServerConfigBuilder WithRequireHttps(bool requireHttps)
{
_requireHttps = requireHttps;
return this;
}
public IdentityServerConfig Build()
{
return new IdentityServerConfig(_providerRootUrl, _apiName, _requireHttps, _apiSecret);
}
}
public class JwtConfigBuilder
{
public string _authority;
public string _audience;
public JwtConfigBuilder WithAuthority(string authority)
{
_authority = authority;
return this;
}
public JwtConfigBuilder WithAudience(string audience)
{
_audience = audience;
return this;
}
public JwtConfig Build()
{
return new JwtConfig(_authority, _audience);
} }
} }
} }

View File

@ -2,11 +2,13 @@
{ {
public class CacheOptions public class CacheOptions
{ {
public CacheOptions(int ttlSeconds) public CacheOptions(int ttlSeconds, string region)
{ {
TtlSeconds = ttlSeconds; TtlSeconds = ttlSeconds;
Region = region;
} }
public int TtlSeconds { get; private set; } public int TtlSeconds { get; private set; }
public string Region {get;private set;}
} }
} }

View File

@ -1,19 +1,26 @@
using Ocelot.Configuration.Builder; using Ocelot.Configuration.Builder;
using Ocelot.Configuration.File; using Ocelot.Configuration.File;
using Ocelot.Creator.Configuration;
namespace Ocelot.Configuration.Creator namespace Ocelot.Configuration.Creator
{ {
public class AuthenticationOptionsCreator : IAuthenticationOptionsCreator public class AuthenticationOptionsCreator : IAuthenticationOptionsCreator
{ {
private readonly IAuthenticationProviderConfigCreator _creator;
public AuthenticationOptionsCreator(IAuthenticationProviderConfigCreator creator)
{
_creator = creator;
}
public AuthenticationOptions Create(FileReRoute fileReRoute) public AuthenticationOptions Create(FileReRoute fileReRoute)
{ {
var authenticationConfig = _creator.Create(fileReRoute.AuthenticationOptions);
return new AuthenticationOptionsBuilder() return new AuthenticationOptionsBuilder()
.WithProvider(fileReRoute.AuthenticationOptions?.Provider) .WithProvider(fileReRoute.AuthenticationOptions?.Provider)
.WithProviderRootUrl(fileReRoute.AuthenticationOptions?.ProviderRootUrl)
.WithApiName(fileReRoute.AuthenticationOptions?.ApiName)
.WithRequireHttps(fileReRoute.AuthenticationOptions.RequireHttps)
.WithAllowedScopes(fileReRoute.AuthenticationOptions?.AllowedScopes) .WithAllowedScopes(fileReRoute.AuthenticationOptions?.AllowedScopes)
.WithApiSecret(fileReRoute.AuthenticationOptions?.ApiSecret) .WithConfig(authenticationConfig)
.Build(); .Build();
} }
} }

View File

@ -0,0 +1,37 @@
using Ocelot.Creator.Configuration;
namespace Ocelot.Configuration.Creator
{
using Ocelot.Configuration.Builder;
using Ocelot.Configuration.File;
public class AuthenticationProviderConfigCreator : IAuthenticationProviderConfigCreator
{
public IAuthenticationConfig Create(FileAuthenticationOptions authenticationOptions)
{
if (authenticationOptions.Provider?.ToLower() == "jwt")
{
return CreateJwt(authenticationOptions);
}
return CreateIdentityServer(authenticationOptions);
}
private JwtConfig CreateJwt(FileAuthenticationOptions authenticationOptions)
{
return new JwtConfigBuilder()
.WithAudience(authenticationOptions.JwtConfig?.Audience)
.WithAuthority(authenticationOptions.JwtConfig?.Authority)
.Build();
}
private IdentityServerConfig CreateIdentityServer(FileAuthenticationOptions authenticationOptions)
{
return new IdentityServerConfigBuilder()
.WithApiName(authenticationOptions.IdentityServerConfig?.ApiName)
.WithApiSecret(authenticationOptions.IdentityServerConfig?.ApiSecret)
.WithProviderRootUrl(authenticationOptions.IdentityServerConfig?.ProviderRootUrl)
.WithRequireHttps(authenticationOptions.IdentityServerConfig.RequireHttps).Build();
}
}
}

View File

@ -4,6 +4,7 @@ using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using Ocelot.Cache;
using Ocelot.Configuration.Builder; using Ocelot.Configuration.Builder;
using Ocelot.Configuration.File; using Ocelot.Configuration.File;
using Ocelot.Configuration.Parser; using Ocelot.Configuration.Parser;
@ -30,12 +31,13 @@ namespace Ocelot.Configuration.Creator
private readonly IQosProviderHouse _qosProviderHouse; private readonly IQosProviderHouse _qosProviderHouse;
private readonly IClaimsToThingCreator _claimsToThingCreator; private readonly IClaimsToThingCreator _claimsToThingCreator;
private readonly IAuthenticationOptionsCreator _authOptionsCreator; private readonly IAuthenticationOptionsCreator _authOptionsCreator;
private IUpstreamTemplatePatternCreator _upstreamTemplatePatternCreator; private readonly IUpstreamTemplatePatternCreator _upstreamTemplatePatternCreator;
private IRequestIdKeyCreator _requestIdKeyCreator; private readonly IRequestIdKeyCreator _requestIdKeyCreator;
private IServiceProviderConfigurationCreator _serviceProviderConfigCreator; private readonly IServiceProviderConfigurationCreator _serviceProviderConfigCreator;
private IQoSOptionsCreator _qosOptionsCreator; private readonly IQoSOptionsCreator _qosOptionsCreator;
private IReRouteOptionsCreator _fileReRouteOptionsCreator; private readonly IReRouteOptionsCreator _fileReRouteOptionsCreator;
private IRateLimitOptionsCreator _rateLimitOptionsCreator; private readonly IRateLimitOptionsCreator _rateLimitOptionsCreator;
private readonly IRegionCreator _regionCreator;
public FileOcelotConfigurationCreator( public FileOcelotConfigurationCreator(
IOptions<FileConfiguration> options, IOptions<FileConfiguration> options,
@ -52,9 +54,11 @@ namespace Ocelot.Configuration.Creator
IServiceProviderConfigurationCreator serviceProviderConfigCreator, IServiceProviderConfigurationCreator serviceProviderConfigCreator,
IQoSOptionsCreator qosOptionsCreator, IQoSOptionsCreator qosOptionsCreator,
IReRouteOptionsCreator fileReRouteOptionsCreator, IReRouteOptionsCreator fileReRouteOptionsCreator,
IRateLimitOptionsCreator rateLimitOptionsCreator IRateLimitOptionsCreator rateLimitOptionsCreator,
IRegionCreator regionCreator
) )
{ {
_regionCreator = regionCreator;
_rateLimitOptionsCreator = rateLimitOptionsCreator; _rateLimitOptionsCreator = rateLimitOptionsCreator;
_requestIdKeyCreator = requestIdKeyCreator; _requestIdKeyCreator = requestIdKeyCreator;
_upstreamTemplatePatternCreator = upstreamTemplatePatternCreator; _upstreamTemplatePatternCreator = upstreamTemplatePatternCreator;
@ -137,6 +141,8 @@ namespace Ocelot.Configuration.Creator
var rateLimitOption = _rateLimitOptionsCreator.Create(fileReRoute, globalConfiguration, fileReRouteOptions.EnableRateLimiting); var rateLimitOption = _rateLimitOptionsCreator.Create(fileReRoute, globalConfiguration, fileReRouteOptions.EnableRateLimiting);
var region = _regionCreator.Create(fileReRoute);
var reRoute = new ReRouteBuilder() var reRoute = new ReRouteBuilder()
.WithDownstreamPathTemplate(fileReRoute.DownstreamPathTemplate) .WithDownstreamPathTemplate(fileReRoute.DownstreamPathTemplate)
.WithUpstreamPathTemplate(fileReRoute.UpstreamPathTemplate) .WithUpstreamPathTemplate(fileReRoute.UpstreamPathTemplate)
@ -151,7 +157,7 @@ namespace Ocelot.Configuration.Creator
.WithClaimsToQueries(claimsToQueries) .WithClaimsToQueries(claimsToQueries)
.WithRequestIdKey(requestIdKey) .WithRequestIdKey(requestIdKey)
.WithIsCached(fileReRouteOptions.IsCached) .WithIsCached(fileReRouteOptions.IsCached)
.WithCacheOptions(new CacheOptions(fileReRoute.FileCacheOptions.TtlSeconds)) .WithCacheOptions(new CacheOptions(fileReRoute.FileCacheOptions.TtlSeconds, region))
.WithDownstreamScheme(fileReRoute.DownstreamScheme) .WithDownstreamScheme(fileReRoute.DownstreamScheme)
.WithLoadBalancer(fileReRoute.LoadBalancer) .WithLoadBalancer(fileReRoute.LoadBalancer)
.WithDownstreamHost(fileReRoute.DownstreamHost) .WithDownstreamHost(fileReRoute.DownstreamHost)

View File

@ -13,6 +13,8 @@ namespace Ocelot.Configuration.Creator
var username = Environment.GetEnvironmentVariable("OCELOT_USERNAME"); var username = Environment.GetEnvironmentVariable("OCELOT_USERNAME");
var hash = Environment.GetEnvironmentVariable("OCELOT_HASH"); var hash = Environment.GetEnvironmentVariable("OCELOT_HASH");
var salt = Environment.GetEnvironmentVariable("OCELOT_SALT"); var salt = Environment.GetEnvironmentVariable("OCELOT_SALT");
var credentialsSigningCertificateLocation = Environment.GetEnvironmentVariable("OCELOT_CERTIFICATE");
var credentialsSigningCertificatePassword = Environment.GetEnvironmentVariable("OCELOT_CERTIFICATE_PASSWORD");
return new IdentityServerConfiguration( return new IdentityServerConfiguration(
"admin", "admin",
@ -28,7 +30,9 @@ namespace Ocelot.Configuration.Creator
new List<User> new List<User>
{ {
new User("admin", username, hash, salt) new User("admin", username, hash, salt)
} },
credentialsSigningCertificateLocation,
credentialsSigningCertificatePassword
); );
} }
} }

View File

@ -7,13 +7,13 @@ namespace Ocelot.Configuration.File
public FileAuthenticationOptions() public FileAuthenticationOptions()
{ {
AllowedScopes = new List<string>(); AllowedScopes = new List<string>();
IdentityServerConfig = new FileIdentityServerConfig();
JwtConfig = new FileJwtConfig();
} }
public string Provider { get; set; } public string Provider { get; set; }
public string ProviderRootUrl { get; set; }
public string ApiName { get; set; }
public bool RequireHttps { get; set; }
public List<string> AllowedScopes { get; set; } public List<string> AllowedScopes { get; set; }
public string ApiSecret { get; set; } public FileIdentityServerConfig IdentityServerConfig { get; set; }
public FileJwtConfig JwtConfig { get; set; }
} }
} }

View File

@ -3,5 +3,6 @@
public class FileCacheOptions public class FileCacheOptions
{ {
public int TtlSeconds { get; set; } public int TtlSeconds { get; set; }
public string Region {get; set;}
} }
} }

View File

@ -0,0 +1,10 @@
namespace Ocelot.Configuration.File
{
public class FileIdentityServerConfig
{
public string ProviderRootUrl { get; set; }
public string ApiName { get; set; }
public bool RequireHttps { get; set; }
public string ApiSecret { get; set; }
}
}

View File

@ -0,0 +1,9 @@
namespace Ocelot.Configuration.File
{
public class FileJwtConfig
{
public string Authority { get; set; }
public string Audience { get; set; }
}
}

View File

@ -17,5 +17,7 @@ namespace Ocelot.Configuration.Provider
AccessTokenType AccessTokenType {get;} AccessTokenType AccessTokenType {get;}
bool RequireClientSecret {get;} bool RequireClientSecret {get;}
List<User> Users {get;} List<User> Users {get;}
string CredentialsSigningCertificateLocation { get; }
string CredentialsSigningCertificatePassword { get; }
} }
} }

View File

@ -17,7 +17,7 @@ namespace Ocelot.Configuration.Provider
IEnumerable<string> grantType, IEnumerable<string> grantType,
AccessTokenType accessTokenType, AccessTokenType accessTokenType,
bool requireClientSecret, bool requireClientSecret,
List<User> users) List<User> users, string credentialsSigningCertificateLocation, string credentialsSigningCertificatePassword)
{ {
ApiName = apiName; ApiName = apiName;
RequireHttps = requireHttps; RequireHttps = requireHttps;
@ -30,6 +30,8 @@ namespace Ocelot.Configuration.Provider
AccessTokenType = accessTokenType; AccessTokenType = accessTokenType;
RequireClientSecret = requireClientSecret; RequireClientSecret = requireClientSecret;
Users = users; Users = users;
CredentialsSigningCertificateLocation = credentialsSigningCertificateLocation;
CredentialsSigningCertificatePassword = credentialsSigningCertificatePassword;
} }
public string ApiName { get; private set; } public string ApiName { get; private set; }
@ -43,5 +45,7 @@ namespace Ocelot.Configuration.Provider
public AccessTokenType AccessTokenType {get;private set;} public AccessTokenType AccessTokenType {get;private set;}
public bool RequireClientSecret {get;private set;} public bool RequireClientSecret {get;private set;}
public List<User> Users {get;private set;} public List<User> Users {get;private set;}
public string CredentialsSigningCertificateLocation { get; private set; }
public string CredentialsSigningCertificatePassword { get; private set; }
} }
} }

View File

@ -46,7 +46,7 @@ namespace Ocelot.Configuration
IsAuthorised = isAuthorised; IsAuthorised = isAuthorised;
RequestIdKey = requestIdKey; RequestIdKey = requestIdKey;
IsCached = isCached; IsCached = isCached;
FileCacheOptions = fileCacheOptions; CacheOptions = fileCacheOptions;
ClaimsToQueries = claimsToQueries ClaimsToQueries = claimsToQueries
?? new List<ClaimToThing>(); ?? new List<ClaimToThing>();
ClaimsToClaims = claimsToClaims ClaimsToClaims = claimsToClaims
@ -74,7 +74,7 @@ namespace Ocelot.Configuration
public Dictionary<string, string> RouteClaimsRequirement { get; private set; } public Dictionary<string, string> RouteClaimsRequirement { get; private set; }
public string RequestIdKey { get; private set; } public string RequestIdKey { get; private set; }
public bool IsCached { get; private set; } public bool IsCached { get; private set; }
public CacheOptions FileCacheOptions { get; private set; } public CacheOptions CacheOptions { get; private set; }
public string DownstreamScheme {get;private set;} public string DownstreamScheme {get;private set;}
public bool IsQos { get; private set; } public bool IsQos { get; private set; }
public QoSOptions QosOptionsOptions { get; private set; } public QoSOptions QosOptionsOptions { get; private set; }

View File

@ -4,6 +4,7 @@ using System.Threading.Tasks;
using Consul; using Consul;
using Newtonsoft.Json; using Newtonsoft.Json;
using Newtonsoft.Json.Linq; using Newtonsoft.Json.Linq;
using Ocelot.Authentication.JsonConverters;
using Ocelot.Responses; using Ocelot.Responses;
using Ocelot.ServiceDiscovery; using Ocelot.ServiceDiscovery;
@ -30,7 +31,7 @@ namespace Ocelot.Configuration.Repository
public async Task<Response<IOcelotConfiguration>> Get() public async Task<Response<IOcelotConfiguration>> Get()
{ {
var config = _cache.Get(_ocelotConfiguration); var config = _cache.Get(_ocelotConfiguration, _ocelotConfiguration);
if (config != null) if (config != null)
{ {
@ -48,7 +49,9 @@ namespace Ocelot.Configuration.Repository
var json = Encoding.UTF8.GetString(bytes); var json = Encoding.UTF8.GetString(bytes);
var consulConfig = JsonConvert.DeserializeObject<OcelotConfiguration>(json); var settings = new JsonSerializerSettings();
settings.Converters.Add(new AuthenticationConfigConverter());
var consulConfig = JsonConvert.DeserializeObject<OcelotConfiguration>(json, settings);
return new OkResponse<IOcelotConfiguration>(consulConfig); return new OkResponse<IOcelotConfiguration>(consulConfig);
} }
@ -68,7 +71,7 @@ namespace Ocelot.Configuration.Repository
if (result.Response) if (result.Response)
{ {
_cache.AddAndDelete(_ocelotConfiguration, ocelotConfiguration, TimeSpan.FromSeconds(3)); _cache.AddAndDelete(_ocelotConfiguration, ocelotConfiguration, TimeSpan.FromSeconds(3), _ocelotConfiguration);
return new OkResponse(); return new OkResponse();
} }

View File

@ -0,0 +1,29 @@
using System.Net.Http;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Ocelot.Cache;
using Ocelot.Configuration.Provider;
namespace Ocelot.Controllers
{
[Authorize]
[Route("outputcache")]
public class OutputCacheController : Controller
{
private IOcelotCache<HttpResponseMessage> _cache;
public OutputCacheController(IOcelotCache<HttpResponseMessage> cache)
{
_cache = cache;
}
[HttpDelete]
[Route("{region}")]
public IActionResult Delete(string region)
{
_cache.ClearRegion(region);
return new NoContentResult();
}
}
}

View File

@ -0,0 +1,10 @@
using Ocelot.Configuration;
using Ocelot.Configuration.File;
namespace Ocelot.Creator.Configuration
{
public interface IAuthenticationProviderConfigCreator
{
IAuthenticationConfig Create(FileAuthenticationOptions authenticationOptions);
}
}

View File

@ -41,7 +41,10 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Net.Http; using System.Net.Http;
using System.Reflection; using System.Reflection;
using System.Security.Cryptography.X509Certificates;
using Microsoft.IdentityModel.Tokens;
using Ocelot.Configuration; using Ocelot.Configuration;
using Ocelot.Creator.Configuration;
using FileConfigurationProvider = Ocelot.Configuration.Provider.FileConfigurationProvider; using FileConfigurationProvider = Ocelot.Configuration.Provider.FileConfigurationProvider;
namespace Ocelot.DependencyInjection namespace Ocelot.DependencyInjection
@ -69,6 +72,7 @@ namespace Ocelot.DependencyInjection
services.Configure<FileConfiguration>(configurationRoot); services.Configure<FileConfiguration>(configurationRoot);
services.TryAddSingleton<IOcelotConfigurationCreator, FileOcelotConfigurationCreator>(); services.TryAddSingleton<IOcelotConfigurationCreator, FileOcelotConfigurationCreator>();
services.TryAddSingleton<IAuthenticationProviderConfigCreator, AuthenticationProviderConfigCreator>();
services.TryAddSingleton<IOcelotConfigurationRepository, InMemoryOcelotConfigurationRepository>(); services.TryAddSingleton<IOcelotConfigurationRepository, InMemoryOcelotConfigurationRepository>();
services.TryAddSingleton<IConfigurationValidator, FileConfigurationValidator>(); services.TryAddSingleton<IConfigurationValidator, FileConfigurationValidator>();
services.TryAddSingleton<IBaseUrlFinder, BaseUrlFinder>(); services.TryAddSingleton<IBaseUrlFinder, BaseUrlFinder>();
@ -87,8 +91,10 @@ namespace Ocelot.DependencyInjection
{ {
services.TryAddSingleton<IIdentityServerConfiguration>(identityServerConfiguration); services.TryAddSingleton<IIdentityServerConfiguration>(identityServerConfiguration);
services.TryAddSingleton<IHashMatcher, HashMatcher>(); services.TryAddSingleton<IHashMatcher, HashMatcher>();
services.AddIdentityServer() var identityServerBuilder = services
.AddTemporarySigningCredential() .AddIdentityServer(options => {
options.IssuerUri = "Ocelot";
})
.AddInMemoryApiResources(new List<ApiResource> .AddInMemoryApiResources(new List<ApiResource>
{ {
new ApiResource new ApiResource
@ -120,6 +126,16 @@ namespace Ocelot.DependencyInjection
RequireClientSecret = identityServerConfiguration.RequireClientSecret RequireClientSecret = identityServerConfiguration.RequireClientSecret
} }
}).AddResourceOwnerValidator<OcelotResourceOwnerPasswordValidator>(); }).AddResourceOwnerValidator<OcelotResourceOwnerPasswordValidator>();
if (string.IsNullOrEmpty(identityServerConfiguration.CredentialsSigningCertificateLocation) || string.IsNullOrEmpty(identityServerConfiguration.CredentialsSigningCertificatePassword))
{
identityServerBuilder.AddTemporarySigningCredential();
}
else
{
var cert = new X509Certificate2(identityServerConfiguration.CredentialsSigningCertificateLocation, identityServerConfiguration.CredentialsSigningCertificatePassword);
identityServerBuilder.AddSigningCredential(cert);
}
} }
var assembly = typeof(FileConfigurationController).GetTypeInfo().Assembly; var assembly = typeof(FileConfigurationController).GetTypeInfo().Assembly;
@ -131,6 +147,7 @@ namespace Ocelot.DependencyInjection
.AddJsonFormatters(); .AddJsonFormatters();
services.AddLogging(); services.AddLogging();
services.TryAddSingleton<IRegionCreator, RegionCreator>();
services.TryAddSingleton<IFileConfigurationRepository, FileConfigurationRepository>(); services.TryAddSingleton<IFileConfigurationRepository, FileConfigurationRepository>();
services.TryAddSingleton<IFileConfigurationSetter, FileConfigurationSetter>(); services.TryAddSingleton<IFileConfigurationSetter, FileConfigurationSetter>();
services.TryAddSingleton<IFileConfigurationProvider, FileConfigurationProvider>(); services.TryAddSingleton<IFileConfigurationProvider, FileConfigurationProvider>();
@ -169,6 +186,11 @@ namespace Ocelot.DependencyInjection
services.TryAddSingleton<IHttpContextAccessor, HttpContextAccessor>(); services.TryAddSingleton<IHttpContextAccessor, HttpContextAccessor>();
services.TryAddScoped<IRequestScopedDataRepository, HttpDataRepository>(); services.TryAddScoped<IRequestScopedDataRepository, HttpDataRepository>();
services.AddMemoryCache(); services.AddMemoryCache();
//Used to log the the start and ending of middleware
services.TryAddSingleton<OcelotDiagnosticListener>();
services.AddMiddlewareAnalysis();
return services; return services;
} }
} }

View File

@ -30,8 +30,6 @@ namespace Ocelot.DownstreamRouteFinder.Middleware
public async Task Invoke(HttpContext context) public async Task Invoke(HttpContext context)
{ {
_logger.TraceMiddlewareEntry();
var upstreamUrlPath = context.Request.Path.ToString().SetLastCharacterAs('/'); var upstreamUrlPath = context.Request.Path.ToString().SetLastCharacterAs('/');
_logger.LogDebug("upstream url path is {upstreamUrlPath}", upstreamUrlPath); _logger.LogDebug("upstream url path is {upstreamUrlPath}", upstreamUrlPath);
@ -40,11 +38,9 @@ namespace Ocelot.DownstreamRouteFinder.Middleware
if (downstreamRoute.IsError) if (downstreamRoute.IsError)
{ {
_logger.LogError($"{MiddlwareName} setting pipeline errors. IDownstreamRouteFinder returned {downstreamRoute.Errors.ToErrorString()}"); _logger.LogError($"{MiddlewareName} setting pipeline errors. IDownstreamRouteFinder returned {downstreamRoute.Errors.ToErrorString()}");
SetPipelineError(downstreamRoute.Errors); SetPipelineError(downstreamRoute.Errors);
_logger.TraceMiddlewareCompleted();
return; return;
} }
@ -52,12 +48,7 @@ namespace Ocelot.DownstreamRouteFinder.Middleware
SetDownstreamRouteForThisRequest(downstreamRoute.Data); SetDownstreamRouteForThisRequest(downstreamRoute.Data);
_logger.TraceInvokeNext();
await _next.Invoke(context); await _next.Invoke(context);
_logger.TraceInvokeNextCompleted();
_logger.TraceMiddlewareCompleted();
} }
} }
} }

View File

@ -30,8 +30,6 @@ namespace Ocelot.DownstreamUrlCreator.Middleware
public async Task Invoke(HttpContext context) public async Task Invoke(HttpContext context)
{ {
_logger.LogDebug("started calling downstream url creator middleware");
var dsPath = _replacer var dsPath = _replacer
.Replace(DownstreamRoute.ReRoute.DownstreamPathTemplate, DownstreamRoute.TemplatePlaceholderNameAndValues); .Replace(DownstreamRoute.ReRoute.DownstreamPathTemplate, DownstreamRoute.TemplatePlaceholderNameAndValues);
@ -53,11 +51,7 @@ namespace Ocelot.DownstreamUrlCreator.Middleware
_logger.LogDebug("downstream url is {downstreamUrl.Data.Value}", DownstreamRequest.RequestUri); _logger.LogDebug("downstream url is {downstreamUrl.Data.Value}", DownstreamRequest.RequestUri);
_logger.LogDebug("calling next middleware");
await _next.Invoke(context); await _next.Invoke(context);
_logger.LogDebug("succesfully called next middleware");
} }
} }
} }

View File

@ -26,17 +26,15 @@ namespace Ocelot.Headers.Middleware
public async Task Invoke(HttpContext context) public async Task Invoke(HttpContext context)
{ {
_logger.LogDebug("started calling headers builder middleware");
if (DownstreamRoute.ReRoute.ClaimsToHeaders.Any()) if (DownstreamRoute.ReRoute.ClaimsToHeaders.Any())
{ {
_logger.LogDebug("this route has instructions to convert claims to headers"); _logger.LogDebug($"{ DownstreamRoute.ReRoute.DownstreamPathTemplate.Value} has instructions to convert claims to headers");
var response = _addHeadersToRequest.SetHeadersOnDownstreamRequest(DownstreamRoute.ReRoute.ClaimsToHeaders, context.User.Claims, DownstreamRequest); var response = _addHeadersToRequest.SetHeadersOnDownstreamRequest(DownstreamRoute.ReRoute.ClaimsToHeaders, context.User.Claims, DownstreamRequest);
if (response.IsError) if (response.IsError)
{ {
_logger.LogDebug("there was an error setting headers on context, setting pipeline error"); _logger.LogDebug("Error setting headers on context, setting pipeline error");
SetPipelineError(response.Errors); SetPipelineError(response.Errors);
return; return;
@ -45,11 +43,7 @@ namespace Ocelot.Headers.Middleware
_logger.LogDebug("headers have been set on context"); _logger.LogDebug("headers have been set on context");
} }
_logger.LogDebug("calling next middleware");
await _next.Invoke(context); await _next.Invoke(context);
_logger.LogDebug("succesfully called next middleware");
} }
} }
} }

View File

@ -28,11 +28,10 @@ namespace Ocelot.LoadBalancer.Middleware
public async Task Invoke(HttpContext context) public async Task Invoke(HttpContext context)
{ {
_logger.LogDebug("started calling load balancing middleware");
var loadBalancer = _loadBalancerHouse.Get(DownstreamRoute.ReRoute.ReRouteKey); var loadBalancer = _loadBalancerHouse.Get(DownstreamRoute.ReRoute.ReRouteKey);
if(loadBalancer.IsError) if(loadBalancer.IsError)
{ {
_logger.LogDebug("there was an error retriving the loadbalancer, setting pipeline error");
SetPipelineError(loadBalancer.Errors); SetPipelineError(loadBalancer.Errors);
return; return;
} }
@ -40,6 +39,7 @@ namespace Ocelot.LoadBalancer.Middleware
var hostAndPort = await loadBalancer.Data.Lease(); var hostAndPort = await loadBalancer.Data.Lease();
if(hostAndPort.IsError) if(hostAndPort.IsError)
{ {
_logger.LogDebug("there was an error leasing the loadbalancer, setting pipeline error");
SetPipelineError(hostAndPort.Errors); SetPipelineError(hostAndPort.Errors);
return; return;
} }
@ -52,23 +52,19 @@ namespace Ocelot.LoadBalancer.Middleware
} }
DownstreamRequest.RequestUri = uriBuilder.Uri; DownstreamRequest.RequestUri = uriBuilder.Uri;
_logger.LogDebug("calling next middleware");
try try
{ {
await _next.Invoke(context); await _next.Invoke(context);
loadBalancer.Data.Release(hostAndPort.Data);
} }
catch (Exception) catch (Exception)
{ {
loadBalancer.Data.Release(hostAndPort.Data); _logger.LogDebug("Exception calling next middleware, exception will be thrown to global handler");
_logger.LogDebug("error calling next middleware, exception will be thrown to global handler");
throw; throw;
} }
finally
_logger.LogDebug("succesfully called next middleware"); {
loadBalancer.Data.Release(hostAndPort.Data);
}
} }
} }
} }

View File

@ -0,0 +1,34 @@
using System;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DiagnosticAdapter;
namespace Ocelot.Logging
{
public class OcelotDiagnosticListener
{
private IOcelotLogger _logger;
public OcelotDiagnosticListener(IOcelotLoggerFactory factory)
{
_logger = factory.CreateLogger<OcelotDiagnosticListener>();
}
[DiagnosticName("Microsoft.AspNetCore.MiddlewareAnalysis.MiddlewareStarting")]
public virtual void OnMiddlewareStarting(HttpContext httpContext, string name)
{
_logger.LogTrace($"MiddlewareStarting: {name}; {httpContext.Request.Path}");
}
[DiagnosticName("Microsoft.AspNetCore.MiddlewareAnalysis.MiddlewareException")]
public virtual void OnMiddlewareException(Exception exception, string name)
{
_logger.LogTrace($"MiddlewareException: {name}; {exception.Message}");
}
[DiagnosticName("Microsoft.AspNetCore.MiddlewareAnalysis.MiddlewareFinished")]
public virtual void OnMiddlewareFinished(HttpContext httpContext, string name)
{
_logger.LogTrace($"MiddlewareFinished: {name}; {httpContext.Response.StatusCode}");
}
}
}

View File

@ -1,29 +0,0 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Ocelot.Logging
{
public static class OcelotLoggerExtensions
{
public static void TraceMiddlewareEntry(this IOcelotLogger logger)
{
logger.LogTrace($"entered {logger.Name}");
}
public static void TraceInvokeNext(this IOcelotLogger logger)
{
logger.LogTrace($"invoking next middleware from {logger.Name}");
}
public static void TraceInvokeNextCompleted(this IOcelotLogger logger)
{
logger.LogTrace($"returned to {logger.Name} after next middleware completed");
}
public static void TraceMiddlewareCompleted(this IOcelotLogger logger)
{
logger.LogTrace($"completed {logger.Name}");
}
}
}

View File

@ -13,10 +13,10 @@ namespace Ocelot.Middleware
protected OcelotMiddleware(IRequestScopedDataRepository requestScopedDataRepository) protected OcelotMiddleware(IRequestScopedDataRepository requestScopedDataRepository)
{ {
_requestScopedDataRepository = requestScopedDataRepository; _requestScopedDataRepository = requestScopedDataRepository;
MiddlwareName = this.GetType().Name; MiddlewareName = this.GetType().Name;
} }
public string MiddlwareName { get; } public string MiddlewareName { get; }
public bool PipelineError => _requestScopedDataRepository.Get<bool>("OcelotMiddlewareError").Data; public bool PipelineError => _requestScopedDataRepository.Get<bool>("OcelotMiddlewareError").Data;

View File

@ -1,4 +1,5 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics;
using IdentityServer4.AccessTokenValidation; using IdentityServer4.AccessTokenValidation;
using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Builder;
using Ocelot.Authentication.Middleware; using Ocelot.Authentication.Middleware;
@ -8,6 +9,7 @@ using Ocelot.DownstreamRouteFinder.Middleware;
using Ocelot.DownstreamUrlCreator.Middleware; using Ocelot.DownstreamUrlCreator.Middleware;
using Ocelot.Errors.Middleware; using Ocelot.Errors.Middleware;
using Ocelot.Headers.Middleware; using Ocelot.Headers.Middleware;
using Ocelot.Logging;
using Ocelot.QueryStrings.Middleware; using Ocelot.QueryStrings.Middleware;
using Ocelot.Request.Middleware; using Ocelot.Request.Middleware;
using Ocelot.Requester.Middleware; using Ocelot.Requester.Middleware;
@ -53,6 +55,8 @@ namespace Ocelot.Middleware
{ {
await CreateAdministrationArea(builder); await CreateAdministrationArea(builder);
ConfigureDiagnosticListener(builder);
// This is registered to catch any global exceptions that are not handled // This is registered to catch any global exceptions that are not handled
builder.UseExceptionHandlerMiddleware(); builder.UseExceptionHandlerMiddleware();
@ -181,7 +185,6 @@ namespace Ocelot.Middleware
builder.Map(configuration.AdministrationPath, app => builder.Map(configuration.AdministrationPath, app =>
{ {
var identityServerUrl = $"{baseSchemeUrlAndPort}/{configuration.AdministrationPath.Remove(0,1)}"; var identityServerUrl = $"{baseSchemeUrlAndPort}/{configuration.AdministrationPath.Remove(0,1)}";
app.UseIdentityServerAuthentication(new IdentityServerAuthenticationOptions app.UseIdentityServerAuthentication(new IdentityServerAuthenticationOptions
{ {
Authority = identityServerUrl, Authority = identityServerUrl,
@ -206,5 +209,22 @@ namespace Ocelot.Middleware
builder.Use(middleware); builder.Use(middleware);
} }
} }
/// <summary>
/// Configure a DiagnosticListener to listen for diagnostic events when the middleware starts and ends
/// </summary>
/// <param name="builder"></param>
private static void ConfigureDiagnosticListener(IApplicationBuilder builder)
{
var env = (IHostingEnvironment)builder.ApplicationServices.GetService(typeof(IHostingEnvironment));
//https://github.com/TomPallister/Ocelot/pull/87 not sure why only for dev envs and marc disapeered so just merging and maybe change one day?
if (!env.IsProduction())
{
var listener = (OcelotDiagnosticListener)builder.ApplicationServices.GetService(typeof(OcelotDiagnosticListener));
var diagnosticListener = (DiagnosticListener)builder.ApplicationServices.GetService(typeof(DiagnosticListener));
diagnosticListener.SubscribeWithAdapter(listener);
}
}
} }
} }

View File

@ -26,10 +26,12 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.MiddlewareAnalysis" Version="1.1.1" />
<PackageReference Include="Microsoft.AspNetCore.Server.IISIntegration" Version="1.1.1" /> <PackageReference Include="Microsoft.AspNetCore.Server.IISIntegration" Version="1.1.1" />
<PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="1.1.1" /> <PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="1.1.1" />
<PackageReference Include="Microsoft.Extensions.Configuration.FileExtensions" Version="1.1.1" /> <PackageReference Include="Microsoft.Extensions.Configuration.FileExtensions" Version="1.1.1" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="1.1.1" /> <PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="1.1.1" />
<PackageReference Include="Microsoft.Extensions.DiagnosticAdapter" Version="1.1.0" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="1.1.1" /> <PackageReference Include="Microsoft.Extensions.Logging" Version="1.1.1" />
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="1.1.1" /> <PackageReference Include="Microsoft.Extensions.Logging.Console" Version="1.1.1" />
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="1.1.1" /> <PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="1.1.1" />

View File

@ -26,11 +26,9 @@ namespace Ocelot.QueryStrings.Middleware
public async Task Invoke(HttpContext context) public async Task Invoke(HttpContext context)
{ {
_logger.LogDebug("started calling query string builder middleware");
if (DownstreamRoute.ReRoute.ClaimsToQueries.Any()) if (DownstreamRoute.ReRoute.ClaimsToQueries.Any())
{ {
_logger.LogDebug("this route has instructions to convert claims to queries"); _logger.LogDebug($"{DownstreamRoute.ReRoute.DownstreamPathTemplate.Value} has instructions to convert claims to queries");
var response = _addQueriesToRequest.SetQueriesOnDownstreamRequest(DownstreamRoute.ReRoute.ClaimsToQueries, context.User.Claims, DownstreamRequest); var response = _addQueriesToRequest.SetQueriesOnDownstreamRequest(DownstreamRoute.ReRoute.ClaimsToQueries, context.User.Claims, DownstreamRequest);
@ -43,11 +41,7 @@ namespace Ocelot.QueryStrings.Middleware
} }
} }
_logger.LogDebug("calling next middleware");
await _next.Invoke(context); await _next.Invoke(context);
_logger.LogDebug("succesfully called next middleware");
} }
} }
} }

View File

@ -31,18 +31,12 @@ namespace Ocelot.RateLimit.Middleware
public async Task Invoke(HttpContext context) public async Task Invoke(HttpContext context)
{ {
_logger.TraceMiddlewareEntry();
var options = DownstreamRoute.ReRoute.RateLimitOptions; var options = DownstreamRoute.ReRoute.RateLimitOptions;
// check if rate limiting is enabled // check if rate limiting is enabled
if (!DownstreamRoute.ReRoute.EnableEndpointEndpointRateLimiting) if (!DownstreamRoute.ReRoute.EnableEndpointEndpointRateLimiting)
{ {
_logger.LogDebug($"EndpointRateLimiting is not enabled for {DownstreamRoute.ReRoute.DownstreamPathTemplate}"); _logger.LogDebug($"EndpointRateLimiting is not enabled for {DownstreamRoute.ReRoute.DownstreamPathTemplate}");
_logger.TraceInvokeNext();
await _next.Invoke(context); await _next.Invoke(context);
_logger.TraceInvokeNextCompleted();
_logger.TraceMiddlewareCompleted();
return; return;
} }
// compute identity from request // compute identity from request
@ -52,11 +46,7 @@ namespace Ocelot.RateLimit.Middleware
if (IsWhitelisted(identity, options)) if (IsWhitelisted(identity, options))
{ {
_logger.LogDebug($"{DownstreamRoute.ReRoute.DownstreamPathTemplate} is white listed from rate limiting"); _logger.LogDebug($"{DownstreamRoute.ReRoute.DownstreamPathTemplate} is white listed from rate limiting");
_logger.TraceInvokeNext();
await _next.Invoke(context); await _next.Invoke(context);
_logger.TraceInvokeNextCompleted();
_logger.TraceMiddlewareCompleted();
return; return;
} }
@ -78,7 +68,6 @@ namespace Ocelot.RateLimit.Middleware
var retrystring = retryAfter.ToString(System.Globalization.CultureInfo.InvariantCulture); var retrystring = retryAfter.ToString(System.Globalization.CultureInfo.InvariantCulture);
// break execution // break execution
await ReturnQuotaExceededResponse(context, options, retrystring); await ReturnQuotaExceededResponse(context, options, retrystring);
_logger.TraceMiddlewareCompleted();
return; return;
} }
@ -91,10 +80,7 @@ namespace Ocelot.RateLimit.Middleware
context.Response.OnStarting(SetRateLimitHeaders, state: headers); context.Response.OnStarting(SetRateLimitHeaders, state: headers);
} }
_logger.TraceInvokeNext();
await _next.Invoke(context); await _next.Invoke(context);
_logger.TraceInvokeNextCompleted();
_logger.TraceMiddlewareCompleted();
} }
public virtual ClientRequestIdentity SetIdentity(HttpContext httpContext, RateLimitOptions option) public virtual ClientRequestIdentity SetIdentity(HttpContext httpContext, RateLimitOptions option)

View File

@ -29,14 +29,8 @@ namespace Ocelot.RequestId.Middleware
public async Task Invoke(HttpContext context) public async Task Invoke(HttpContext context)
{ {
_logger.TraceMiddlewareEntry();
SetOcelotRequestId(context); SetOcelotRequestId(context);
_logger.TraceInvokeNext();
await _next.Invoke(context); await _next.Invoke(context);
_logger.TraceInvokeNextCompleted();
_logger.TraceMiddlewareCompleted();
} }
private void SetOcelotRequestId(HttpContext context) private void SetOcelotRequestId(HttpContext context)

View File

@ -62,14 +62,6 @@ namespace Ocelot.Responder
} }
} }
private static void AddHeaderIfDoesntExist(HttpContext context, KeyValuePair<string, IEnumerable<string>> httpResponseHeader)
{
if (!context.Response.Headers.ContainsKey(httpResponseHeader.Key))
{
context.Response.Headers.Add(httpResponseHeader.Key, new StringValues(httpResponseHeader.Value.ToArray()));
}
}
public void SetErrorResponseOnContext(HttpContext context, int statusCode) public void SetErrorResponseOnContext(HttpContext context, int statusCode)
{ {
context.Response.OnStarting(x => context.Response.OnStarting(x =>
@ -78,5 +70,13 @@ namespace Ocelot.Responder
return Task.CompletedTask; return Task.CompletedTask;
}, context); }, context);
} }
private static void AddHeaderIfDoesntExist(HttpContext context, KeyValuePair<string, IEnumerable<string>> httpResponseHeader)
{
if (!context.Response.Headers.ContainsKey(httpResponseHeader.Key))
{
context.Response.Headers.Add(httpResponseHeader.Key, new StringValues(httpResponseHeader.Value.ToArray()));
}
}
} }
} }

View File

@ -34,15 +34,12 @@ namespace Ocelot.Responder.Middleware
public async Task Invoke(HttpContext context) public async Task Invoke(HttpContext context)
{ {
_logger.TraceMiddlewareEntry();
_logger.TraceInvokeNext();
await _next.Invoke(context); await _next.Invoke(context);
_logger.TraceInvokeNextCompleted();
if (PipelineError) if (PipelineError)
{ {
var errors = PipelineErrors; var errors = PipelineErrors;
_logger.LogError($"{errors.Count} pipeline errors found in {MiddlwareName}. Setting error response status code"); _logger.LogError($"{PipelineErrors.Count} pipeline errors found in {MiddlewareName}. Setting error response status code");
SetErrorResponse(context, errors); SetErrorResponse(context, errors);
} }
@ -51,13 +48,11 @@ namespace Ocelot.Responder.Middleware
_logger.LogDebug("no pipeline errors, setting and returning completed response"); _logger.LogDebug("no pipeline errors, setting and returning completed response");
await _responder.SetResponseOnHttpContext(context, HttpResponseMessage); await _responder.SetResponseOnHttpContext(context, HttpResponseMessage);
} }
_logger.TraceMiddlewareCompleted();
} }
private void SetErrorResponse(HttpContext context, List<Error> errors) private void SetErrorResponse(HttpContext context, List<Error> errors)
{ {
var statusCode = _codeMapper.Map(errors); var statusCode = _codeMapper.Map(errors);
_responder.SetErrorResponseOnContext(context, statusCode); _responder.SetErrorResponseOnContext(context, statusCode);
} }
} }

View File

@ -53,6 +53,7 @@ namespace Ocelot.AcceptanceTests
{ {
AllowedScopes = new List<string>(), AllowedScopes = new List<string>(),
Provider = "IdentityServer", Provider = "IdentityServer",
IdentityServerConfig = new FileIdentityServerConfig{
ProviderRootUrl = _identityServerRootUrl, ProviderRootUrl = _identityServerRootUrl,
RequireHttps = false, RequireHttps = false,
ApiName = "api", ApiName = "api",
@ -60,6 +61,7 @@ namespace Ocelot.AcceptanceTests
} }
} }
} }
}
}; };
this.Given(x => x.GivenThereIsAnIdentityServerOn(_identityServerRootUrl, "api", "api2", AccessTokenType.Jwt)) this.Given(x => x.GivenThereIsAnIdentityServerOn(_identityServerRootUrl, "api", "api2", AccessTokenType.Jwt))
@ -72,44 +74,6 @@ namespace Ocelot.AcceptanceTests
.BDDfy(); .BDDfy();
} }
[Fact]
public void should_return_401_using_identity_server_reference_token()
{
var configuration = new FileConfiguration
{
ReRoutes = new List<FileReRoute>
{
new FileReRoute
{
DownstreamPathTemplate = _downstreamServicePath,
DownstreamPort = _downstreamServicePort,
DownstreamHost = _downstreamServiceHost,
DownstreamScheme = _downstreamServiceScheme,
UpstreamPathTemplate = "/",
UpstreamHttpMethod = new List<string> { "Post" },
AuthenticationOptions = new FileAuthenticationOptions
{
AllowedScopes = new List<string>(),
Provider = "IdentityServer",
ProviderRootUrl = _identityServerRootUrl,
RequireHttps = false,
ApiName = "api",
ApiSecret = "secret"
}
}
}
};
this.Given(x => x.GivenThereIsAnIdentityServerOn(_identityServerRootUrl, "api", "api2", AccessTokenType.Reference))
.And(x => x.GivenThereIsAServiceRunningOn(_downstreamServiceUrl, 201, string.Empty))
.And(x => _steps.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunning())
.And(x => _steps.GivenThePostHasContent("postContent"))
.When(x => _steps.WhenIPostUrlOnTheApiGateway("/"))
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.Unauthorized))
.BDDfy();
}
[Fact] [Fact]
public void should_return_response_200_using_identity_server() public void should_return_response_200_using_identity_server()
{ {
@ -129,6 +93,7 @@ namespace Ocelot.AcceptanceTests
{ {
AllowedScopes = new List<string>(), AllowedScopes = new List<string>(),
Provider = "IdentityServer", Provider = "IdentityServer",
IdentityServerConfig = new FileIdentityServerConfig{
ProviderRootUrl = _identityServerRootUrl, ProviderRootUrl = _identityServerRootUrl,
RequireHttps = false, RequireHttps = false,
ApiName = "api", ApiName = "api",
@ -136,6 +101,7 @@ namespace Ocelot.AcceptanceTests
} }
} }
} }
}
}; };
this.Given(x => x.GivenThereIsAnIdentityServerOn(_identityServerRootUrl, "api", "api2", AccessTokenType.Jwt)) this.Given(x => x.GivenThereIsAnIdentityServerOn(_identityServerRootUrl, "api", "api2", AccessTokenType.Jwt))
@ -169,6 +135,7 @@ namespace Ocelot.AcceptanceTests
{ {
AllowedScopes = new List<string>(), AllowedScopes = new List<string>(),
Provider = "IdentityServer", Provider = "IdentityServer",
IdentityServerConfig = new FileIdentityServerConfig{
ProviderRootUrl = _identityServerRootUrl, ProviderRootUrl = _identityServerRootUrl,
RequireHttps = false, RequireHttps = false,
ApiName = "api", ApiName = "api",
@ -176,6 +143,7 @@ namespace Ocelot.AcceptanceTests
} }
} }
} }
}
}; };
this.Given(x => x.GivenThereIsAnIdentityServerOn(_identityServerRootUrl, "api", "api2", AccessTokenType.Jwt)) this.Given(x => x.GivenThereIsAnIdentityServerOn(_identityServerRootUrl, "api", "api2", AccessTokenType.Jwt))
@ -209,6 +177,7 @@ namespace Ocelot.AcceptanceTests
{ {
AllowedScopes = new List<string>(), AllowedScopes = new List<string>(),
Provider = "IdentityServer", Provider = "IdentityServer",
IdentityServerConfig = new FileIdentityServerConfig{
ProviderRootUrl = _identityServerRootUrl, ProviderRootUrl = _identityServerRootUrl,
RequireHttps = false, RequireHttps = false,
ApiName = "api", ApiName = "api",
@ -216,6 +185,7 @@ namespace Ocelot.AcceptanceTests
} }
} }
} }
}
}; };
this.Given(x => x.GivenThereIsAnIdentityServerOn(_identityServerRootUrl, "api", "api2", AccessTokenType.Jwt)) this.Given(x => x.GivenThereIsAnIdentityServerOn(_identityServerRootUrl, "api", "api2", AccessTokenType.Jwt))
@ -249,6 +219,7 @@ namespace Ocelot.AcceptanceTests
{ {
AllowedScopes = new List<string>(), AllowedScopes = new List<string>(),
Provider = "IdentityServer", Provider = "IdentityServer",
IdentityServerConfig = new FileIdentityServerConfig{
ProviderRootUrl = _identityServerRootUrl, ProviderRootUrl = _identityServerRootUrl,
RequireHttps = false, RequireHttps = false,
ApiName = "api", ApiName = "api",
@ -256,6 +227,7 @@ namespace Ocelot.AcceptanceTests
} }
} }
} }
}
}; };
this.Given(x => x.GivenThereIsAnIdentityServerOn(_identityServerRootUrl, "api", "api2", AccessTokenType.Reference)) this.Given(x => x.GivenThereIsAnIdentityServerOn(_identityServerRootUrl, "api", "api2", AccessTokenType.Reference))

View File

@ -47,10 +47,12 @@ namespace Ocelot.AcceptanceTests
{ {
AllowedScopes = new List<string>(), AllowedScopes = new List<string>(),
Provider = "IdentityServer", Provider = "IdentityServer",
IdentityServerConfig = new FileIdentityServerConfig{
ProviderRootUrl = "http://localhost:51888", ProviderRootUrl = "http://localhost:51888",
RequireHttps = false, RequireHttps = false,
ApiName = "api", ApiName = "api",
ApiSecret = "secret" ApiSecret = "secret"
}
}, },
AddHeadersToRequest = AddHeadersToRequest =
{ {
@ -104,10 +106,12 @@ namespace Ocelot.AcceptanceTests
{ {
AllowedScopes = new List<string>(), AllowedScopes = new List<string>(),
Provider = "IdentityServer", Provider = "IdentityServer",
IdentityServerConfig = new FileIdentityServerConfig{
ProviderRootUrl = "http://localhost:51888", ProviderRootUrl = "http://localhost:51888",
RequireHttps = false, RequireHttps = false,
ApiName = "api", ApiName = "api",
ApiSecret = "secret" ApiSecret = "secret"
}
}, },
AddHeadersToRequest = AddHeadersToRequest =
{ {
@ -159,6 +163,7 @@ namespace Ocelot.AcceptanceTests
{ {
AllowedScopes = new List<string>{ "api", "api.readOnly", "openid", "offline_access" }, AllowedScopes = new List<string>{ "api", "api.readOnly", "openid", "offline_access" },
Provider = "IdentityServer", Provider = "IdentityServer",
IdentityServerConfig = new FileIdentityServerConfig{
ProviderRootUrl = "http://localhost:51888", ProviderRootUrl = "http://localhost:51888",
RequireHttps = false, RequireHttps = false,
ApiName = "api", ApiName = "api",
@ -166,6 +171,7 @@ namespace Ocelot.AcceptanceTests
} }
} }
} }
}
}; };
this.Given(x => x.GivenThereIsAnIdentityServerOn("http://localhost:51888", "api", AccessTokenType.Jwt)) this.Given(x => x.GivenThereIsAnIdentityServerOn("http://localhost:51888", "api", AccessTokenType.Jwt))
@ -198,6 +204,7 @@ namespace Ocelot.AcceptanceTests
{ {
AllowedScopes = new List<string>{ "api", "openid", "offline_access" }, AllowedScopes = new List<string>{ "api", "openid", "offline_access" },
Provider = "IdentityServer", Provider = "IdentityServer",
IdentityServerConfig = new FileIdentityServerConfig{
ProviderRootUrl = "http://localhost:51888", ProviderRootUrl = "http://localhost:51888",
RequireHttps = false, RequireHttps = false,
ApiName = "api", ApiName = "api",
@ -205,6 +212,7 @@ namespace Ocelot.AcceptanceTests
} }
} }
} }
}
}; };
this.Given(x => x.GivenThereIsAnIdentityServerOn("http://localhost:51888", "api", AccessTokenType.Jwt)) this.Given(x => x.GivenThereIsAnIdentityServerOn("http://localhost:51888", "api", AccessTokenType.Jwt))

View File

@ -64,10 +64,12 @@ namespace Ocelot.AcceptanceTests
"openid", "offline_access", "api" "openid", "offline_access", "api"
}, },
Provider = "IdentityServer", Provider = "IdentityServer",
IdentityServerConfig = new FileIdentityServerConfig{
ProviderRootUrl = "http://localhost:52888", ProviderRootUrl = "http://localhost:52888",
RequireHttps = false, RequireHttps = false,
ApiName = "api", ApiName = "api",
ApiSecret = "secret", ApiSecret = "secret"
}
}, },
AddHeadersToRequest = AddHeadersToRequest =
{ {

View File

@ -64,10 +64,12 @@ namespace Ocelot.AcceptanceTests
"openid", "offline_access", "api" "openid", "offline_access", "api"
}, },
Provider = "IdentityServer", Provider = "IdentityServer",
IdentityServerConfig = new FileIdentityServerConfig{
ProviderRootUrl = "http://localhost:57888", ProviderRootUrl = "http://localhost:57888",
RequireHttps = false, RequireHttps = false,
ApiName = "api", ApiName = "api",
ApiSecret = "secret", ApiSecret = "secret"
}
}, },
AddQueriesToRequest = AddQueriesToRequest =
{ {

View File

@ -8,6 +8,7 @@ using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Newtonsoft.Json; using Newtonsoft.Json;
using Ocelot.Authentication.JsonConverters;
using Ocelot.Configuration; using Ocelot.Configuration;
using Ocelot.Configuration.File; using Ocelot.Configuration.File;
using Ocelot.Configuration.Repository; using Ocelot.Configuration.Repository;
@ -29,7 +30,6 @@ namespace Ocelot.AcceptanceTests
_steps = new Steps(); _steps = new Steps();
} }
[Fact] [Fact]
public void should_return_response_200_with_simple_url() public void should_return_response_200_with_simple_url()
{ {
@ -105,7 +105,9 @@ namespace Ocelot.AcceptanceTests
var json = reader.ReadToEnd(); var json = reader.ReadToEnd();
_config = JsonConvert.DeserializeObject<OcelotConfiguration>(json); var settings = new JsonSerializerSettings();
settings.Converters.Add(new AuthenticationConfigConverter());
_config = JsonConvert.DeserializeObject<OcelotConfiguration>(json, settings);
var response = JsonConvert.SerializeObject(true); var response = JsonConvert.SerializeObject(true);

View File

@ -15,7 +15,7 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<None Update="configuration.json"> <None Update="configuration.json;appsettings.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None> </None>
</ItemGroup> </ItemGroup>
@ -30,6 +30,7 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.MiddlewareAnalysis" Version="1.1.1" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.0.0-preview-20170106-08" /> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.0.0-preview-20170106-08" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.3.0-beta2-build1317" /> <PackageReference Include="xunit.runner.visualstudio" Version="2.3.0-beta2-build1317" />
<PackageReference Include="Microsoft.AspNetCore.Server.IISIntegration" Version="1.1.1" /> <PackageReference Include="Microsoft.AspNetCore.Server.IISIntegration" Version="1.1.1" />

View File

@ -0,0 +1,10 @@
{
"Logging": {
"IncludeScopes": true,
"LogLevel": {
"Default": "Error",
"System": "Error",
"Microsoft": "Error"
}
}
}

View File

@ -7,6 +7,7 @@ using System.Net.Http.Headers;
using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Newtonsoft.Json; using Newtonsoft.Json;
using Ocelot.Cache;
using Ocelot.Configuration.File; using Ocelot.Configuration.File;
using Ocelot.ManualTest; using Ocelot.ManualTest;
using Shouldly; using Shouldly;
@ -19,15 +20,19 @@ namespace Ocelot.IntegrationTests
public class AdministrationTests : IDisposable public class AdministrationTests : IDisposable
{ {
private readonly HttpClient _httpClient; private readonly HttpClient _httpClient;
private readonly HttpClient _httpClientTwo;
private HttpResponseMessage _response; private HttpResponseMessage _response;
private IWebHost _builder; private IWebHost _builder;
private IWebHostBuilder _webHostBuilder; private IWebHostBuilder _webHostBuilder;
private readonly string _ocelotBaseUrl; private readonly string _ocelotBaseUrl;
private BearerToken _token; private BearerToken _token;
private IWebHostBuilder _webHostBuilderTwo;
private IWebHost _builderTwo;
public AdministrationTests() public AdministrationTests()
{ {
_httpClient = new HttpClient(); _httpClient = new HttpClient();
_httpClientTwo = new HttpClient();
_ocelotBaseUrl = "http://localhost:5000"; _ocelotBaseUrl = "http://localhost:5000";
_httpClient.BaseAddress = new Uri(_ocelotBaseUrl); _httpClient.BaseAddress = new Uri(_ocelotBaseUrl);
} }
@ -70,6 +75,27 @@ namespace Ocelot.IntegrationTests
.BDDfy(); .BDDfy();
} }
[Fact]
public void should_be_able_to_use_token_from_ocelot_a_on_ocelot_b()
{
var configuration = new FileConfiguration
{
GlobalConfiguration = new FileGlobalConfiguration
{
AdministrationPath = "/administration"
}
};
this.Given(x => GivenThereIsAConfiguration(configuration))
.And(x => GivenIdentityServerSigningEnvironmentalVariablesAreSet())
.And(x => GivenOcelotIsRunning())
.And(x => GivenIHaveAnOcelotToken("/administration"))
.And(x => GivenAnotherOcelotIsRunning("http://localhost:5007"))
.When(x => WhenIGetUrlOnTheSecondOcelot("/administration/configuration"))
.Then(x => ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
.BDDfy();
}
[Fact] [Fact]
public void should_return_file_configuration() public void should_return_file_configuration()
{ {
@ -95,7 +121,12 @@ namespace Ocelot.IntegrationTests
DownstreamScheme = "https", DownstreamScheme = "https",
DownstreamPathTemplate = "/", DownstreamPathTemplate = "/",
UpstreamHttpMethod = new List<string> { "get" }, UpstreamHttpMethod = new List<string> { "get" },
UpstreamPathTemplate = "/" UpstreamPathTemplate = "/",
FileCacheOptions = new FileCacheOptions
{
TtlSeconds = 10,
Region = "Geoff"
}
}, },
new FileReRoute() new FileReRoute()
{ {
@ -104,7 +135,12 @@ namespace Ocelot.IntegrationTests
DownstreamScheme = "https", DownstreamScheme = "https",
DownstreamPathTemplate = "/", DownstreamPathTemplate = "/",
UpstreamHttpMethod = new List<string> { "get" }, UpstreamHttpMethod = new List<string> { "get" },
UpstreamPathTemplate = "/test" UpstreamPathTemplate = "/test",
FileCacheOptions = new FileCacheOptions
{
TtlSeconds = 10,
Region = "Dave"
}
} }
} }
}; };
@ -193,6 +229,87 @@ namespace Ocelot.IntegrationTests
.BDDfy(); .BDDfy();
} }
[Fact]
public void should_clear_region()
{
var initialConfiguration = new FileConfiguration
{
GlobalConfiguration = new FileGlobalConfiguration
{
AdministrationPath = "/administration"
},
ReRoutes = new List<FileReRoute>()
{
new FileReRoute()
{
DownstreamHost = "localhost",
DownstreamPort = 80,
DownstreamScheme = "https",
DownstreamPathTemplate = "/",
UpstreamHttpMethod = new List<string> { "get" },
UpstreamPathTemplate = "/",
FileCacheOptions = new FileCacheOptions
{
TtlSeconds = 10
}
},
new FileReRoute()
{
DownstreamHost = "localhost",
DownstreamPort = 80,
DownstreamScheme = "https",
DownstreamPathTemplate = "/",
UpstreamHttpMethod = new List<string> { "get" },
UpstreamPathTemplate = "/test",
FileCacheOptions = new FileCacheOptions
{
TtlSeconds = 10
}
}
}
};
var regionToClear = "gettest";
this.Given(x => GivenThereIsAConfiguration(initialConfiguration))
.And(x => GivenOcelotIsRunning())
.And(x => GivenIHaveAnOcelotToken("/administration"))
.And(x => GivenIHaveAddedATokenToMyRequest())
.When(x => WhenIDeleteOnTheApiGateway($"/administration/outputcache/{regionToClear}"))
.Then(x => ThenTheStatusCodeShouldBe(HttpStatusCode.NoContent))
.BDDfy();
}
private void GivenAnotherOcelotIsRunning(string baseUrl)
{
_httpClientTwo.BaseAddress = new Uri(baseUrl);
_webHostBuilderTwo = new WebHostBuilder()
.UseUrls(baseUrl)
.UseKestrel()
.UseContentRoot(Directory.GetCurrentDirectory())
.ConfigureServices(x => {
x.AddSingleton(_webHostBuilderTwo);
})
.UseStartup<Startup>();
_builderTwo = _webHostBuilderTwo.Build();
_builderTwo.Start();
}
private void GivenIdentityServerSigningEnvironmentalVariablesAreSet()
{
Environment.SetEnvironmentVariable("OCELOT_CERTIFICATE", "idsrv3test.pfx");
Environment.SetEnvironmentVariable("OCELOT_CERTIFICATE_PASSWORD", "idsrv3test");
}
private void WhenIGetUrlOnTheSecondOcelot(string url)
{
_httpClientTwo.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", _token.AccessToken);
_response = _httpClientTwo.GetAsync(url).Result;
}
private void WhenIPostOnTheApiGateway(string url, FileConfiguration updatedConfiguration) private void WhenIPostOnTheApiGateway(string url, FileConfiguration updatedConfiguration)
{ {
var json = JsonConvert.SerializeObject(updatedConfiguration); var json = JsonConvert.SerializeObject(updatedConfiguration);
@ -201,6 +318,13 @@ namespace Ocelot.IntegrationTests
_response = _httpClient.PostAsync(url, content).Result; _response = _httpClient.PostAsync(url, content).Result;
} }
private void ThenTheResponseShouldBe(List<string> expected)
{
var content = _response.Content.ReadAsStringAsync().Result;
var result = JsonConvert.DeserializeObject<Regions>(content);
result.Value.ShouldBe(expected);
}
private void ThenTheResponseShouldBe(FileConfiguration expected) private void ThenTheResponseShouldBe(FileConfiguration expected)
{ {
var response = JsonConvert.DeserializeObject<FileConfiguration>(_response.Content.ReadAsStringAsync().Result); var response = JsonConvert.DeserializeObject<FileConfiguration>(_response.Content.ReadAsStringAsync().Result);
@ -298,6 +422,11 @@ namespace Ocelot.IntegrationTests
_response = _httpClient.GetAsync(url).Result; _response = _httpClient.GetAsync(url).Result;
} }
private void WhenIDeleteOnTheApiGateway(string url)
{
_response = _httpClient.DeleteAsync(url).Result;
}
private void ThenTheStatusCodeShouldBe(HttpStatusCode expectedHttpStatusCode) private void ThenTheStatusCodeShouldBe(HttpStatusCode expectedHttpStatusCode)
{ {
_response.StatusCode.ShouldBe(expectedHttpStatusCode); _response.StatusCode.ShouldBe(expectedHttpStatusCode);
@ -305,6 +434,8 @@ namespace Ocelot.IntegrationTests
public void Dispose() public void Dispose()
{ {
Environment.SetEnvironmentVariable("OCELOT_CERTIFICATE", "");
Environment.SetEnvironmentVariable("OCELOT_CERTIFICATE_PASSWORD", "");
_builder?.Dispose(); _builder?.Dispose();
_httpClient?.Dispose(); _httpClient?.Dispose();
} }

View File

@ -15,7 +15,7 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<None Update="configuration.json;appsettings.json"> <None Update="configuration.json;appsettings.json;idsrv3test.pfx">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None> </None>
</ItemGroup> </ItemGroup>
@ -30,6 +30,7 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.MiddlewareAnalysis" Version="1.1.1" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.0.0-preview-20170106-08" /> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.0.0-preview-20170106-08" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.3.0-beta2-build1317" /> <PackageReference Include="xunit.runner.visualstudio" Version="2.3.0-beta2-build1317" />
<PackageReference Include="Microsoft.AspNetCore.Server.IISIntegration" Version="1.1.1" /> <PackageReference Include="Microsoft.AspNetCore.Server.IISIntegration" Version="1.1.1" />

View File

@ -15,7 +15,6 @@ using TestStack.BDDfy;
using Xunit; using Xunit;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Threading;
using System.Collections.Concurrent; using System.Collections.Concurrent;
namespace Ocelot.IntegrationTests namespace Ocelot.IntegrationTests
@ -23,11 +22,9 @@ namespace Ocelot.IntegrationTests
public class ThreadSafeHeadersTests : IDisposable public class ThreadSafeHeadersTests : IDisposable
{ {
private readonly HttpClient _httpClient; private readonly HttpClient _httpClient;
private HttpResponseMessage _response;
private IWebHost _builder; private IWebHost _builder;
private IWebHostBuilder _webHostBuilder; private IWebHostBuilder _webHostBuilder;
private readonly string _ocelotBaseUrl; private readonly string _ocelotBaseUrl;
private BearerToken _token;
private IWebHost _downstreamBuilder; private IWebHost _downstreamBuilder;
private readonly Random _random; private readonly Random _random;
private readonly ConcurrentBag<ThreadSafeHeadersTestResult> _results; private readonly ConcurrentBag<ThreadSafeHeadersTestResult> _results;
@ -135,7 +132,7 @@ namespace Ocelot.IntegrationTests
text = File.ReadAllText(configurationPath); text = File.ReadAllText(configurationPath);
} }
public void WhenIGetUrlOnTheApiGatewayMultipleTimesWithDifferentHeaderValues(string url, int times) private void WhenIGetUrlOnTheApiGatewayMultipleTimesWithDifferentHeaderValues(string url, int times)
{ {
var tasks = new Task[times]; var tasks = new Task[times];

View File

@ -3,8 +3,8 @@
"IncludeScopes": true, "IncludeScopes": true,
"LogLevel": { "LogLevel": {
"Default": "Error", "Default": "Error",
"System": "Information", "System": "Error",
"Microsoft": "Information" "Microsoft": "Error"
} }
} }
} }

Binary file not shown.

View File

@ -22,6 +22,7 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.MiddlewareAnalysis" Version="1.1.1" />
<PackageReference Include="Microsoft.AspNetCore.Http" Version="1.1.1" /> <PackageReference Include="Microsoft.AspNetCore.Http" Version="1.1.1" />
<PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="1.1.1" /> <PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="1.1.1" />
<PackageReference Include="Microsoft.Extensions.Configuration.FileExtensions" Version="1.1.1" /> <PackageReference Include="Microsoft.Extensions.Configuration.FileExtensions" Version="1.1.1" />

View File

@ -14,13 +14,16 @@
}, },
"AuthenticationOptions": { "AuthenticationOptions": {
"Provider": "IdentityServer", "Provider": "IdentityServer",
"ProviderRootUrl": "http://localhost:52888",
"ApiName": "api",
"AllowedScopes": [ "AllowedScopes": [
"openid", "openid",
"offline_access" "offline_access"
], ],
"ApiSecret": "secret" "IdentityServerConfig": {
"ProviderRootUrl": "http://localhost:52888",
"ApiName": "api",
"ApiSecret": "secret",
"RequireHttps": false
}
}, },
"AddHeadersToRequest": { "AddHeadersToRequest": {
"CustomerId": "Claims[CustomerId] > value", "CustomerId": "Claims[CustomerId] > value",

View File

@ -31,17 +31,19 @@ namespace Ocelot.UnitTests.Authentication
_authenticationHandlerFactory = new AuthenticationHandlerFactory(_creator.Object); _authenticationHandlerFactory = new AuthenticationHandlerFactory(_creator.Object);
} }
[Fact] [Theory]
public void should_return_identity_server_access_token_handler() [InlineData("IdentityServer")]
[InlineData("Jwt")]
public void should_return_access_token_handler(string provider)
{ {
var authenticationOptions = new AuthenticationOptionsBuilder() var authenticationOptions = new AuthenticationOptionsBuilder()
.WithProvider("IdentityServer") .WithProvider(provider)
.Build(); .Build();
this.Given(x => x.GivenTheAuthenticationOptionsAre(authenticationOptions)) this.Given(x => x.GivenTheAuthenticationOptionsAre(authenticationOptions))
.And(x => x.GivenTheCreatorReturns()) .And(x => x.GivenTheCreatorReturns())
.When(x => x.WhenIGetFromTheFactory()) .When(x => x.WhenIGetFromTheFactory())
.Then(x => x.ThenTheHandlerIsReturned("IdentityServer")) .Then(x => x.ThenTheHandlerIsReturned(provider))
.BDDfy(); .BDDfy();
} }

View File

@ -1,60 +1,55 @@
using System; namespace Ocelot.UnitTests.Authentication
{
using System.Collections.Generic; using System.Collections.Generic;
using System.IO;
using System.Net.Http;
using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.TestHost;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Moq; using Moq;
using Ocelot.Authentication.Handler;
using Ocelot.Authentication.Handler.Factory; using Ocelot.Authentication.Handler.Factory;
using Ocelot.Authentication.Middleware; using Ocelot.Authentication.Middleware;
using Ocelot.Cache.Middleware;
using Ocelot.Configuration;
using Ocelot.Configuration.Builder; using Ocelot.Configuration.Builder;
using Ocelot.DownstreamRouteFinder; using Ocelot.DownstreamRouteFinder;
using Ocelot.DownstreamRouteFinder.UrlMatcher; using Ocelot.DownstreamRouteFinder.UrlMatcher;
using Ocelot.Infrastructure.RequestData;
using Ocelot.Logging; using Ocelot.Logging;
using Ocelot.Responses; using Ocelot.Responses;
using Shouldly; using Shouldly;
using TestStack.BDDfy; using TestStack.BDDfy;
using Xunit; using Xunit;
namespace Ocelot.UnitTests.Authentication public class AuthenticationMiddlewareTests : ServerHostedMiddlewareTest
{ {
public class AuthenticationMiddlewareTests : IDisposable
{
private readonly Mock<IRequestScopedDataRepository> _scopedRepository;
private readonly Mock<IAuthenticationHandlerFactory> _authFactory; private readonly Mock<IAuthenticationHandlerFactory> _authFactory;
private readonly string _url;
private readonly TestServer _server;
private readonly HttpClient _client;
private HttpResponseMessage _result;
private OkResponse<DownstreamRoute> _downstreamRoute; private OkResponse<DownstreamRoute> _downstreamRoute;
public AuthenticationMiddlewareTests() public AuthenticationMiddlewareTests()
{ {
_url = "http://localhost:51879";
_scopedRepository = new Mock<IRequestScopedDataRepository>();
_authFactory = new Mock<IAuthenticationHandlerFactory>(); _authFactory = new Mock<IAuthenticationHandlerFactory>();
var builder = new WebHostBuilder()
.ConfigureServices(x => GivenTheTestServerIsConfigured();
}
[Fact]
public void should_call_next_middleware_if_route_is_not_authenticated()
{ {
x.AddSingleton<IOcelotLoggerFactory, AspDotNetLoggerFactory>(); this.Given(x => x.GivenTheDownStreamRouteIs(
x.AddLogging(); new DownstreamRoute(
x.AddSingleton(_authFactory.Object); new List<UrlPathPlaceholderNameAndValue>(),
x.AddSingleton(_scopedRepository.Object); new ReRouteBuilder().WithUpstreamHttpMethod(new List<string> { "Get" }).Build())))
}) .When(x => x.WhenICallTheMiddleware())
.UseUrls(_url) .Then(x => x.ThenTheUserIsAuthenticated())
.UseKestrel() .BDDfy();
.UseContentRoot(Directory.GetCurrentDirectory()) }
.UseIISIntegration()
.UseUrls(_url) protected override void GivenTheTestServerServicesAreConfigured(IServiceCollection services)
.Configure(app => {
services.AddSingleton<IOcelotLoggerFactory, AspDotNetLoggerFactory>();
services.AddLogging();
services.AddSingleton(_authFactory.Object);
services.AddSingleton(ScopedRepository.Object);
}
protected override void GivenTheTestServerPipelineIsConfigured(IApplicationBuilder app)
{ {
app.UseAuthenticationMiddleware(); app.UseAuthenticationMiddleware();
@ -62,47 +57,20 @@ namespace Ocelot.UnitTests.Authentication
{ {
await x.Response.WriteAsync("The user is authenticated"); await x.Response.WriteAsync("The user is authenticated");
}); });
});
_server = new TestServer(builder);
_client = _server.CreateClient();
}
[Fact]
public void should_call_next_middleware_if_route_is_not_authenticated()
{
this.Given(x => x.GivenTheDownStreamRouteIs(new DownstreamRoute(new List<UrlPathPlaceholderNameAndValue>(), new ReRouteBuilder()
.WithUpstreamHttpMethod(new List<string> { "Get" })
.Build())))
.When(x => x.WhenICallTheMiddleware())
.Then(x => x.ThenTheUserIsAuthenticated())
.BDDfy();
} }
private void ThenTheUserIsAuthenticated() private void ThenTheUserIsAuthenticated()
{ {
var content = _result.Content.ReadAsStringAsync().Result; var content = ResponseMessage.Content.ReadAsStringAsync().Result;
content.ShouldBe("The user is authenticated"); content.ShouldBe("The user is authenticated");
} }
private void GivenTheDownStreamRouteIs(DownstreamRoute downstreamRoute) private void GivenTheDownStreamRouteIs(DownstreamRoute downstreamRoute)
{ {
_downstreamRoute = new OkResponse<DownstreamRoute>(downstreamRoute); _downstreamRoute = new OkResponse<DownstreamRoute>(downstreamRoute);
_scopedRepository ScopedRepository
.Setup(x => x.Get<DownstreamRoute>(It.IsAny<string>())) .Setup(x => x.Get<DownstreamRoute>(It.IsAny<string>()))
.Returns(_downstreamRoute); .Returns(_downstreamRoute);
} }
private void WhenICallTheMiddleware()
{
_result = _client.GetAsync(_url).Result;
}
public void Dispose()
{
_client.Dispose();
_server.Dispose();
}
} }
} }

View File

@ -1,66 +1,32 @@
using System; namespace Ocelot.UnitTests.Authorization
{
using System.Collections.Generic; using System.Collections.Generic;
using System.IO;
using System.Net.Http;
using System.Security.Claims; using System.Security.Claims;
using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.TestHost;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Moq; using Moq;
using Ocelot.Authorisation; using Ocelot.Authorisation;
using Ocelot.Authorisation.Middleware;
using Ocelot.Configuration.Builder; using Ocelot.Configuration.Builder;
using Ocelot.DownstreamRouteFinder; using Ocelot.DownstreamRouteFinder;
using Ocelot.DownstreamRouteFinder.UrlMatcher; using Ocelot.DownstreamRouteFinder.UrlMatcher;
using Ocelot.Infrastructure.RequestData;
using Ocelot.Logging; using Ocelot.Logging;
using Ocelot.Responses; using Ocelot.Responses;
using TestStack.BDDfy; using TestStack.BDDfy;
using Xunit; using Xunit;
namespace Ocelot.UnitTests.Authorization public class AuthorisationMiddlewareTests : ServerHostedMiddlewareTest
{ {
using Authorisation.Middleware;
public class AuthorisationMiddlewareTests : IDisposable
{
private readonly Mock<IRequestScopedDataRepository> _scopedRepository;
private readonly Mock<IClaimsAuthoriser> _authService; private readonly Mock<IClaimsAuthoriser> _authService;
private readonly Mock<IScopesAuthoriser> _authScopesService; private readonly Mock<IScopesAuthoriser> _authScopesService;
private readonly string _url;
private readonly TestServer _server;
private readonly HttpClient _client;
private HttpResponseMessage _result;
private OkResponse<DownstreamRoute> _downstreamRoute; private OkResponse<DownstreamRoute> _downstreamRoute;
public AuthorisationMiddlewareTests() public AuthorisationMiddlewareTests()
{ {
_url = "http://localhost:51879";
_scopedRepository = new Mock<IRequestScopedDataRepository>();
_authService = new Mock<IClaimsAuthoriser>(); _authService = new Mock<IClaimsAuthoriser>();
_authScopesService = new Mock<IScopesAuthoriser>(); _authScopesService = new Mock<IScopesAuthoriser>();
var builder = new WebHostBuilder() GivenTheTestServerIsConfigured();
.ConfigureServices(x =>
{
x.AddSingleton<IOcelotLoggerFactory, AspDotNetLoggerFactory>();
x.AddLogging();
x.AddSingleton(_authService.Object);
x.AddSingleton(_authScopesService.Object);
x.AddSingleton(_scopedRepository.Object);
})
.UseUrls(_url)
.UseKestrel()
.UseContentRoot(Directory.GetCurrentDirectory())
.UseIISIntegration()
.UseUrls(_url)
.Configure(app =>
{
app.UseAuthorisationMiddleware();
});
_server = new TestServer(builder);
_client = _server.CreateClient();
} }
[Fact] [Fact]
@ -77,6 +43,28 @@ namespace Ocelot.UnitTests.Authorization
.BDDfy(); .BDDfy();
} }
protected override void GivenTheTestServerServicesAreConfigured(IServiceCollection services)
{
services.AddSingleton<IOcelotLoggerFactory, AspDotNetLoggerFactory>();
services.AddLogging();
services.AddSingleton(_authService.Object);
services.AddSingleton(_authScopesService.Object);
services.AddSingleton(ScopedRepository.Object);
}
protected override void GivenTheTestServerPipelineIsConfigured(IApplicationBuilder app)
{
app.UseAuthorisationMiddleware();
}
private void GivenTheDownStreamRouteIs(DownstreamRoute downstreamRoute)
{
_downstreamRoute = new OkResponse<DownstreamRoute>(downstreamRoute);
ScopedRepository
.Setup(x => x.Get<DownstreamRoute>(It.IsAny<string>()))
.Returns(_downstreamRoute);
}
private void GivenTheAuthServiceReturns(Response<bool> expected) private void GivenTheAuthServiceReturns(Response<bool> expected)
{ {
_authService _authService
@ -90,25 +78,5 @@ namespace Ocelot.UnitTests.Authorization
.Verify(x => x.Authorise(It.IsAny<ClaimsPrincipal>(), .Verify(x => x.Authorise(It.IsAny<ClaimsPrincipal>(),
It.IsAny<Dictionary<string, string>>()), Times.Once); It.IsAny<Dictionary<string, string>>()), Times.Once);
} }
private void GivenTheDownStreamRouteIs(DownstreamRoute downstreamRoute)
{
_downstreamRoute = new OkResponse<DownstreamRoute>(downstreamRoute);
_scopedRepository
.Setup(x => x.Get<DownstreamRoute>(It.IsAny<string>()))
.Returns(_downstreamRoute);
}
private void WhenICallTheMiddleware()
{
_result = _client.GetAsync(_url).Result;
}
public void Dispose()
{
_client.Dispose();
_server.Dispose();
}
} }
} }

View File

@ -16,20 +16,21 @@ namespace Ocelot.UnitTests.Cache
private string _value; private string _value;
private string _resultGet; private string _resultGet;
private TimeSpan _ttlSeconds; private TimeSpan _ttlSeconds;
private string _region;
public CacheManagerCacheTests() public CacheManagerCacheTests()
{ {
_mockCacheManager = new Mock<ICacheManager<string>>(); _mockCacheManager = new Mock<ICacheManager<string>>();
_ocelotOcelotCacheManager = new OcelotCacheManagerCache<string>(_mockCacheManager.Object); _ocelotOcelotCacheManager = new OcelotCacheManagerCache<string>(_mockCacheManager.Object);
} }
[Fact] [Fact]
public void should_get_from_cache() public void should_get_from_cache()
{ {
this.Given(x => x.GivenTheFollowingIsCached("someKey", "someValue")) this.Given(x => x.GivenTheFollowingIsCached("someKey", "someRegion", "someValue"))
.When(x => x.WhenIGetFromTheCache()) .When(x => x.WhenIGetFromTheCache())
.Then(x => x.ThenTheResultIs("someValue")) .Then(x => x.ThenTheResultIs("someValue"))
.BDDfy(); .BDDfy();
} }
[Fact] [Fact]
@ -40,13 +41,37 @@ namespace Ocelot.UnitTests.Cache
.BDDfy(); .BDDfy();
} }
[Fact]
public void should_delete_key_from_cache()
{
this.Given(_ => GivenTheFollowingRegion("fookey"))
.When(_ => WhenIDeleteTheRegion("fookey"))
.Then(_ => ThenTheRegionIsDeleted("fookey"))
.BDDfy();
}
private void WhenIDeleteTheRegion(string region)
{
_ocelotOcelotCacheManager.ClearRegion(region);
}
private void ThenTheRegionIsDeleted(string region)
{
_mockCacheManager
.Verify(x => x.ClearRegion(region), Times.Once);
}
private void GivenTheFollowingRegion(string key)
{
_ocelotOcelotCacheManager.Add(key, "doesnt matter", TimeSpan.FromSeconds(10), "region");
}
private void WhenIAddToTheCache(string key, string value, TimeSpan ttlSeconds) private void WhenIAddToTheCache(string key, string value, TimeSpan ttlSeconds)
{ {
_key = key; _key = key;
_value = value; _value = value;
_ttlSeconds = ttlSeconds; _ttlSeconds = ttlSeconds;
_ocelotOcelotCacheManager.Add(_key, _value, _ttlSeconds, "region");
_ocelotOcelotCacheManager.Add(_key, _value, _ttlSeconds);
} }
private void ThenTheCacheIsCalledCorrectly() private void ThenTheCacheIsCalledCorrectly()
@ -62,15 +87,16 @@ namespace Ocelot.UnitTests.Cache
private void WhenIGetFromTheCache() private void WhenIGetFromTheCache()
{ {
_resultGet = _ocelotOcelotCacheManager.Get(_key); _resultGet = _ocelotOcelotCacheManager.Get(_key, _region);
} }
private void GivenTheFollowingIsCached(string key, string value) private void GivenTheFollowingIsCached(string key, string region, string value)
{ {
_key = key; _key = key;
_value = value; _value = value;
_region = region;
_mockCacheManager _mockCacheManager
.Setup(x => x.Get<string>(It.IsAny<string>())) .Setup(x => x.Get<string>(It.IsAny<string>(), It.IsAny<string>()))
.Returns(value); .Returns(value);
} }
} }

View File

@ -1,9 +1,9 @@
using System; namespace Ocelot.UnitTests.Cache
{
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO;
using System.Net.Http; using System.Net.Http;
using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.TestHost;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Moq; using Moq;
using Ocelot.Cache; using Ocelot.Cache;
@ -12,54 +12,25 @@ using Ocelot.Configuration;
using Ocelot.Configuration.Builder; using Ocelot.Configuration.Builder;
using Ocelot.DownstreamRouteFinder; using Ocelot.DownstreamRouteFinder;
using Ocelot.DownstreamRouteFinder.UrlMatcher; using Ocelot.DownstreamRouteFinder.UrlMatcher;
using Ocelot.Infrastructure.RequestData;
using Ocelot.Logging; using Ocelot.Logging;
using Ocelot.Responses; using Ocelot.Responses;
using TestStack.BDDfy; using TestStack.BDDfy;
using Xunit; using Xunit;
namespace Ocelot.UnitTests.Cache public class OutputCacheMiddlewareTests : ServerHostedMiddlewareTest
{
public class OutputCacheMiddlewareTests
{ {
private readonly Mock<IOcelotCache<HttpResponseMessage>> _cacheManager; private readonly Mock<IOcelotCache<HttpResponseMessage>> _cacheManager;
private readonly Mock<IRequestScopedDataRepository> _scopedRepo;
private readonly string _url;
private readonly TestServer _server;
private readonly HttpClient _client;
private HttpResponseMessage _result;
private HttpResponseMessage _response; private HttpResponseMessage _response;
public OutputCacheMiddlewareTests() public OutputCacheMiddlewareTests()
{ {
_cacheManager = new Mock<IOcelotCache<HttpResponseMessage>>(); _cacheManager = new Mock<IOcelotCache<HttpResponseMessage>>();
_scopedRepo = new Mock<IRequestScopedDataRepository>();
_url = "http://localhost:51879"; ScopedRepository
var builder = new WebHostBuilder()
.ConfigureServices(x =>
{
x.AddSingleton<IOcelotLoggerFactory, AspDotNetLoggerFactory>();
x.AddLogging();
x.AddSingleton(_cacheManager.Object);
x.AddSingleton(_scopedRepo.Object);
})
.UseUrls(_url)
.UseKestrel()
.UseContentRoot(Directory.GetCurrentDirectory())
.UseIISIntegration()
.UseUrls(_url)
.Configure(app =>
{
app.UseOutputCacheMiddleware();
});
_scopedRepo
.Setup(sr => sr.Get<HttpRequestMessage>("DownstreamRequest")) .Setup(sr => sr.Get<HttpRequestMessage>("DownstreamRequest"))
.Returns(new OkResponse<HttpRequestMessage>(new HttpRequestMessage(HttpMethod.Get, "https://some.url/blah?abcd=123"))); .Returns(new OkResponse<HttpRequestMessage>(new HttpRequestMessage(HttpMethod.Get, "https://some.url/blah?abcd=123")));
_server = new TestServer(builder); GivenTheTestServerIsConfigured();
_client = _server.CreateClient();
} }
[Fact] [Fact]
@ -85,32 +56,60 @@ namespace Ocelot.UnitTests.Cache
.BDDfy(); .BDDfy();
} }
protected override void GivenTheTestServerServicesAreConfigured(IServiceCollection services)
{
services.AddSingleton<IOcelotLoggerFactory, AspDotNetLoggerFactory>();
services.AddLogging();
services.AddSingleton(_cacheManager.Object);
services.AddSingleton(ScopedRepository.Object);
services.AddSingleton<IRegionCreator, RegionCreator>();
}
protected override void GivenTheTestServerPipelineIsConfigured(IApplicationBuilder app)
{
app.UseOutputCacheMiddleware();
}
private void GivenThereIsACachedResponse(HttpResponseMessage response)
{
_response = response;
_cacheManager
.Setup(x => x.Get(It.IsAny<string>(), It.IsAny<string>()))
.Returns(_response);
}
private void GivenResponseIsNotCached()
{
ScopedRepository
.Setup(x => x.Get<HttpResponseMessage>("HttpResponseMessage"))
.Returns(new OkResponse<HttpResponseMessage>(new HttpResponseMessage()));
}
private void GivenTheDownstreamRouteIs() private void GivenTheDownstreamRouteIs()
{ {
var reRoute = new ReRouteBuilder() var reRoute = new ReRouteBuilder()
.WithIsCached(true) .WithIsCached(true)
.WithCacheOptions(new CacheOptions(100)) .WithCacheOptions(new CacheOptions(100, "kanken"))
.WithUpstreamHttpMethod(new List<string> { "Get" }) .WithUpstreamHttpMethod(new List<string> { "Get" })
.Build(); .Build();
var downstreamRoute = new DownstreamRoute(new List<UrlPathPlaceholderNameAndValue>(), reRoute); var downstreamRoute = new DownstreamRoute(new List<UrlPathPlaceholderNameAndValue>(), reRoute);
_scopedRepo ScopedRepository
.Setup(x => x.Get<DownstreamRoute>(It.IsAny<string>())) .Setup(x => x.Get<DownstreamRoute>(It.IsAny<string>()))
.Returns(new OkResponse<DownstreamRoute>(downstreamRoute)); .Returns(new OkResponse<DownstreamRoute>(downstreamRoute));
} }
private void GivenThereAreNoErrors() private void GivenThereAreNoErrors()
{ {
_scopedRepo ScopedRepository
.Setup(x => x.Get<bool>("OcelotMiddlewareError")) .Setup(x => x.Get<bool>("OcelotMiddlewareError"))
.Returns(new OkResponse<bool>(false)); .Returns(new OkResponse<bool>(false));
} }
private void GivenThereIsADownstreamUrl() private void GivenThereIsADownstreamUrl()
{ {
_scopedRepo ScopedRepository
.Setup(x => x.Get<string>("DownstreamUrl")) .Setup(x => x.Get<string>("DownstreamUrl"))
.Returns(new OkResponse<string>("anything")); .Returns(new OkResponse<string>("anything"));
} }
@ -118,33 +117,13 @@ namespace Ocelot.UnitTests.Cache
private void ThenTheCacheGetIsCalledCorrectly() private void ThenTheCacheGetIsCalledCorrectly()
{ {
_cacheManager _cacheManager
.Verify(x => x.Get(It.IsAny<string>()), Times.Once); .Verify(x => x.Get(It.IsAny<string>(), It.IsAny<string>()), Times.Once);
} }
private void ThenTheCacheAddIsCalledCorrectly() private void ThenTheCacheAddIsCalledCorrectly()
{ {
_cacheManager _cacheManager
.Verify(x => x.Add(It.IsAny<string>(), It.IsAny<HttpResponseMessage>(), It.IsAny<TimeSpan>()), Times.Once); .Verify(x => x.Add(It.IsAny<string>(), It.IsAny<HttpResponseMessage>(), It.IsAny<TimeSpan>(), It.IsAny<string>()), Times.Once);
}
private void GivenResponseIsNotCached()
{
_scopedRepo
.Setup(x => x.Get<HttpResponseMessage>("HttpResponseMessage"))
.Returns(new OkResponse<HttpResponseMessage>(new HttpResponseMessage()));
}
private void GivenThereIsACachedResponse(HttpResponseMessage response)
{
_response = response;
_cacheManager
.Setup(x => x.Get(It.IsAny<string>()))
.Returns(_response);
}
private void WhenICallTheMiddleware()
{
_result = _client.GetAsync(_url).Result;
} }
} }
} }

View File

@ -0,0 +1,65 @@
using System.Collections.Generic;
using Ocelot.Cache;
using Ocelot.Configuration;
using Ocelot.Configuration.Builder;
using Ocelot.Configuration.File;
using Shouldly;
using TestStack.BDDfy;
using Xunit;
namespace Ocelot.UnitTests.Cache
{
public class RegionCreatorTests
{
private string _result;
private FileReRoute _reRoute;
[Fact]
public void should_create_region()
{
var reRoute = new FileReRoute
{
UpstreamHttpMethod = new List<string> { "Get" },
UpstreamPathTemplate = "/testdummy"
};
this.Given(_ => GivenTheReRoute(reRoute))
.When(_ => WhenICreateTheRegion())
.Then(_ => ThenTheRegionIs("Gettestdummy"))
.BDDfy();
}
[Fact]
public void should_use_region()
{
var reRoute = new FileReRoute
{
FileCacheOptions = new FileCacheOptions
{
Region = "region"
}
};
this.Given(_ => GivenTheReRoute(reRoute))
.When(_ => WhenICreateTheRegion())
.Then(_ => ThenTheRegionIs("region"))
.BDDfy();
}
private void GivenTheReRoute(FileReRoute reRoute)
{
_reRoute = reRoute;
}
private void WhenICreateTheRegion()
{
RegionCreator regionCreator = new RegionCreator();
_result = regionCreator.Create(_reRoute);
}
private void ThenTheRegionIs(string expected)
{
_result.ShouldBe(expected);
}
}
}

View File

@ -1,65 +1,31 @@
using System; namespace Ocelot.UnitTests.Claims
{
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using Microsoft.AspNetCore.Builder;
using System.Net.Http;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.TestHost;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Moq; using Moq;
using Ocelot.Cache.Middleware;
using Ocelot.Claims; using Ocelot.Claims;
using Ocelot.Claims.Middleware; using Ocelot.Claims.Middleware;
using Ocelot.Configuration; using Ocelot.Configuration;
using Ocelot.Configuration.Builder; using Ocelot.Configuration.Builder;
using Ocelot.DownstreamRouteFinder; using Ocelot.DownstreamRouteFinder;
using Ocelot.DownstreamRouteFinder.UrlMatcher; using Ocelot.DownstreamRouteFinder.UrlMatcher;
using Ocelot.Infrastructure.RequestData;
using Ocelot.Logging; using Ocelot.Logging;
using Ocelot.Responses; using Ocelot.Responses;
using TestStack.BDDfy; using TestStack.BDDfy;
using Xunit; using Xunit;
namespace Ocelot.UnitTests.Claims public class ClaimsBuilderMiddlewareTests : ServerHostedMiddlewareTest
{ {
public class ClaimsBuilderMiddlewareTests : IDisposable
{
private readonly Mock<IRequestScopedDataRepository> _scopedRepository;
private readonly Mock<IAddClaimsToRequest> _addHeaders; private readonly Mock<IAddClaimsToRequest> _addHeaders;
private readonly string _url;
private readonly TestServer _server;
private readonly HttpClient _client;
private Response<DownstreamRoute> _downstreamRoute; private Response<DownstreamRoute> _downstreamRoute;
private HttpResponseMessage _result;
public ClaimsBuilderMiddlewareTests() public ClaimsBuilderMiddlewareTests()
{ {
_url = "http://localhost:51879";
_scopedRepository = new Mock<IRequestScopedDataRepository>();
_addHeaders = new Mock<IAddClaimsToRequest>(); _addHeaders = new Mock<IAddClaimsToRequest>();
var builder = new WebHostBuilder() GivenTheTestServerIsConfigured();
.ConfigureServices(x =>
{
x.AddSingleton<IOcelotLoggerFactory, AspDotNetLoggerFactory>();
x.AddLogging();
x.AddSingleton(_addHeaders.Object);
x.AddSingleton(_scopedRepository.Object);
})
.UseUrls(_url)
.UseKestrel()
.UseContentRoot(Directory.GetCurrentDirectory())
.UseIISIntegration()
.UseUrls(_url)
.Configure(app =>
{
app.UseClaimsBuilderMiddleware();
});
_server = new TestServer(builder);
_client = _server.CreateClient();
} }
[Fact] [Fact]
@ -82,6 +48,27 @@ namespace Ocelot.UnitTests.Claims
.BDDfy(); .BDDfy();
} }
protected override void GivenTheTestServerServicesAreConfigured(IServiceCollection services)
{
services.AddSingleton<IOcelotLoggerFactory, AspDotNetLoggerFactory>();
services.AddLogging();
services.AddSingleton(_addHeaders.Object);
services.AddSingleton(ScopedRepository.Object);
}
protected override void GivenTheTestServerPipelineIsConfigured(IApplicationBuilder app)
{
app.UseClaimsBuilderMiddleware();
}
private void GivenTheDownStreamRouteIs(DownstreamRoute downstreamRoute)
{
_downstreamRoute = new OkResponse<DownstreamRoute>(downstreamRoute);
ScopedRepository
.Setup(x => x.Get<DownstreamRoute>(It.IsAny<string>()))
.Returns(_downstreamRoute);
}
private void GivenTheAddClaimsToRequestReturns() private void GivenTheAddClaimsToRequestReturns()
{ {
_addHeaders _addHeaders
@ -96,24 +83,5 @@ namespace Ocelot.UnitTests.Claims
.Verify(x => x.SetClaimsOnContext(It.IsAny<List<ClaimToThing>>(), .Verify(x => x.SetClaimsOnContext(It.IsAny<List<ClaimToThing>>(),
It.IsAny<HttpContext>()), Times.Once); It.IsAny<HttpContext>()), Times.Once);
} }
private void WhenICallTheMiddleware()
{
_result = _client.GetAsync(_url).Result;
}
private void GivenTheDownStreamRouteIs(DownstreamRoute downstreamRoute)
{
_downstreamRoute = new OkResponse<DownstreamRoute>(downstreamRoute);
_scopedRepository
.Setup(x => x.Get<DownstreamRoute>(It.IsAny<string>()))
.Returns(_downstreamRoute);
}
public void Dispose()
{
_client.Dispose();
_server.Dispose();
}
} }
} }

View File

@ -11,13 +11,13 @@ namespace Ocelot.UnitTests.Configuration
{ {
public class AuthenticationOptionsCreatorTests public class AuthenticationOptionsCreatorTests
{ {
private AuthenticationOptionsCreator _authOptionsCreator; private readonly AuthenticationOptionsCreator _authOptionsCreator;
private FileReRoute _fileReRoute; private FileReRoute _fileReRoute;
private AuthenticationOptions _result; private AuthenticationOptions _result;
public AuthenticationOptionsCreatorTests() public AuthenticationOptionsCreatorTests()
{ {
_authOptionsCreator = new AuthenticationOptionsCreator(); _authOptionsCreator = new AuthenticationOptionsCreator(new AuthenticationProviderConfigCreator());
} }
[Fact] [Fact]
@ -28,26 +28,68 @@ namespace Ocelot.UnitTests.Configuration
AuthenticationOptions = new FileAuthenticationOptions AuthenticationOptions = new FileAuthenticationOptions
{ {
Provider = "Geoff", Provider = "Geoff",
IdentityServerConfig = new FileIdentityServerConfig()
{
ProviderRootUrl = "http://www.bbc.co.uk/", ProviderRootUrl = "http://www.bbc.co.uk/",
ApiName = "Laura", ApiName = "Laura",
RequireHttps = true, RequireHttps = true,
AllowedScopes = new List<string> {"cheese"},
ApiSecret = "secret" ApiSecret = "secret"
},
AllowedScopes = new List<string> { "cheese" },
} }
}; };
var authenticationConfig = new IdentityServerConfigBuilder()
.WithProviderRootUrl(fileReRoute.AuthenticationOptions?.IdentityServerConfig?.ProviderRootUrl)
.WithApiName(fileReRoute.AuthenticationOptions?.IdentityServerConfig?.ApiName)
.WithRequireHttps(fileReRoute.AuthenticationOptions.IdentityServerConfig.RequireHttps)
.WithApiSecret(fileReRoute.AuthenticationOptions?.IdentityServerConfig?.ApiSecret)
.Build();
var expected = new AuthenticationOptionsBuilder() var expected = new AuthenticationOptionsBuilder()
.WithProvider(fileReRoute.AuthenticationOptions?.Provider) .WithProvider(fileReRoute.AuthenticationOptions?.Provider)
.WithProviderRootUrl(fileReRoute.AuthenticationOptions?.ProviderRootUrl)
.WithApiName(fileReRoute.AuthenticationOptions?.ApiName)
.WithRequireHttps(fileReRoute.AuthenticationOptions.RequireHttps)
.WithAllowedScopes(fileReRoute.AuthenticationOptions?.AllowedScopes) .WithAllowedScopes(fileReRoute.AuthenticationOptions?.AllowedScopes)
.WithApiSecret(fileReRoute.AuthenticationOptions?.ApiSecret) .WithConfig(authenticationConfig)
.Build(); .Build();
this.Given(x => x.GivenTheFollowing(fileReRoute)) this.Given(x => x.GivenTheFollowing(fileReRoute))
.When(x => x.WhenICreateTheAuthenticationOptions()) .When(x => x.WhenICreateTheAuthenticationOptions())
.Then(x => x.ThenTheFollowingIsReturned(expected)) .Then(x => x.ThenTheFollowingIdentityServerConfigIsReturned(expected))
.BDDfy();
}
[Fact]
public void should_return_Jwt_auth_options()
{
var fileReRoute = new FileReRoute()
{
AuthenticationOptions = new FileAuthenticationOptions
{
Provider = "Jwt",
JwtConfig = new FileJwtConfig()
{
Audience = "Audience",
Authority = "Authority"
},
AllowedScopes = new List<string> { "cheese" }
}
};
var authenticationConfig = new JwtConfigBuilder()
.WithAudience(fileReRoute.AuthenticationOptions?.JwtConfig?.Audience)
.WithAuthority(fileReRoute.AuthenticationOptions?.JwtConfig?.Authority)
.Build();
var expected = new AuthenticationOptionsBuilder()
.WithProvider(fileReRoute.AuthenticationOptions?.Provider)
.WithAllowedScopes(fileReRoute.AuthenticationOptions?.AllowedScopes)
.WithConfig(authenticationConfig)
.Build();
this.Given(x => x.GivenTheFollowing(fileReRoute))
.When(x => x.WhenICreateTheAuthenticationOptions())
.Then(x => x.ThenTheFollowingJwtConfigIsReturned(expected))
.BDDfy(); .BDDfy();
} }
@ -61,14 +103,31 @@ namespace Ocelot.UnitTests.Configuration
_result = _authOptionsCreator.Create(_fileReRoute); _result = _authOptionsCreator.Create(_fileReRoute);
} }
private void ThenTheFollowingIsReturned(AuthenticationOptions expected) private void ThenTheFollowingJwtConfigIsReturned(AuthenticationOptions expected)
{ {
_result.AllowedScopes.ShouldBe(expected.AllowedScopes); _result.AllowedScopes.ShouldBe(expected.AllowedScopes);
_result.Provider.ShouldBe(expected.Provider); _result.Provider.ShouldBe(expected.Provider);
_result.ProviderRootUrl.ShouldBe(expected.ProviderRootUrl);
_result.RequireHttps.ShouldBe(expected.RequireHttps); var _resultSettings = _result.Config as JwtConfig;
_result.ApiName.ShouldBe(expected.ApiName); var expectedSettngs = expected.Config as JwtConfig;
_result.ApiSecret.ShouldBe(expected.ApiSecret);
_resultSettings.Audience.ShouldBe(expectedSettngs.Audience);
_resultSettings.Authority.ShouldBe(expectedSettngs.Authority);
}
private void ThenTheFollowingIdentityServerConfigIsReturned(AuthenticationOptions expected)
{
_result.AllowedScopes.ShouldBe(expected.AllowedScopes);
_result.Provider.ShouldBe(expected.Provider);
var _resultSettings = _result.Config as IdentityServerConfig;
var expectedSettngs = expected.Config as IdentityServerConfig;
_resultSettings.ProviderRootUrl.ShouldBe(expectedSettngs.ProviderRootUrl);
_resultSettings.RequireHttps.ShouldBe(expectedSettngs.RequireHttps);
_resultSettings.ApiName.ShouldBe(expectedSettngs.ApiName);
_resultSettings.ApiSecret.ShouldBe(expectedSettngs.ApiSecret);
} }
} }
} }

View File

@ -2,6 +2,7 @@
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using Moq; using Moq;
using Ocelot.Cache;
using Ocelot.Configuration; using Ocelot.Configuration;
using Ocelot.Configuration.Builder; using Ocelot.Configuration.Builder;
using Ocelot.Configuration.Creator; using Ocelot.Configuration.Creator;
@ -17,6 +18,10 @@ using Xunit;
namespace Ocelot.UnitTests.Configuration namespace Ocelot.UnitTests.Configuration
{ {
using System.Collections;
using Ocelot.UnitTests.TestData;
public class FileConfigurationCreatorTests public class FileConfigurationCreatorTests
{ {
private readonly Mock<IOptions<FileConfiguration>> _fileConfig; private readonly Mock<IOptions<FileConfiguration>> _fileConfig;
@ -39,6 +44,7 @@ namespace Ocelot.UnitTests.Configuration
private Mock<IQoSOptionsCreator> _qosOptionsCreator; private Mock<IQoSOptionsCreator> _qosOptionsCreator;
private Mock<IReRouteOptionsCreator> _fileReRouteOptionsCreator; private Mock<IReRouteOptionsCreator> _fileReRouteOptionsCreator;
private Mock<IRateLimitOptionsCreator> _rateLimitOptions; private Mock<IRateLimitOptionsCreator> _rateLimitOptions;
private Mock<IRegionCreator> _regionCreator;
public FileConfigurationCreatorTests() public FileConfigurationCreatorTests()
{ {
@ -59,6 +65,7 @@ namespace Ocelot.UnitTests.Configuration
_qosOptionsCreator = new Mock<IQoSOptionsCreator>(); _qosOptionsCreator = new Mock<IQoSOptionsCreator>();
_fileReRouteOptionsCreator = new Mock<IReRouteOptionsCreator>(); _fileReRouteOptionsCreator = new Mock<IReRouteOptionsCreator>();
_rateLimitOptions = new Mock<IRateLimitOptionsCreator>(); _rateLimitOptions = new Mock<IRateLimitOptionsCreator>();
_regionCreator = new Mock<IRegionCreator>();
_ocelotConfigurationCreator = new FileOcelotConfigurationCreator( _ocelotConfigurationCreator = new FileOcelotConfigurationCreator(
_fileConfig.Object, _validator.Object, _logger.Object, _fileConfig.Object, _validator.Object, _logger.Object,
@ -66,7 +73,51 @@ namespace Ocelot.UnitTests.Configuration
_qosProviderFactory.Object, _qosProviderHouse.Object, _claimsToThingCreator.Object, _qosProviderFactory.Object, _qosProviderHouse.Object, _claimsToThingCreator.Object,
_authOptionsCreator.Object, _upstreamTemplatePatternCreator.Object, _requestIdKeyCreator.Object, _authOptionsCreator.Object, _upstreamTemplatePatternCreator.Object, _requestIdKeyCreator.Object,
_serviceProviderConfigCreator.Object, _qosOptionsCreator.Object, _fileReRouteOptionsCreator.Object, _serviceProviderConfigCreator.Object, _qosOptionsCreator.Object, _fileReRouteOptionsCreator.Object,
_rateLimitOptions.Object); _rateLimitOptions.Object, _regionCreator.Object);
}
[Fact]
public void should_call_region_creator()
{
var reRouteOptions = new ReRouteOptionsBuilder()
.Build();
this.Given(x => x.GivenTheConfigIs(new FileConfiguration
{
ReRoutes = new List<FileReRoute>
{
new FileReRoute
{
DownstreamHost = "127.0.0.1",
UpstreamPathTemplate = "/api/products/{productId}",
DownstreamPathTemplate = "/products/{productId}",
UpstreamHttpMethod = new List<string> { "Get" },
FileCacheOptions = new FileCacheOptions
{
Region = "region"
}
}
},
}))
.And(x => x.GivenTheFollowingOptionsAreReturned(reRouteOptions))
.And(x => x.GivenTheConfigIsValid())
.And(x => x.GivenTheFollowingRegionIsReturned("region"))
.When(x => x.WhenICreateTheConfig())
.Then(x => x.ThenTheRegionCreatorIsCalledCorrectly("region"))
.BDDfy();
}
private void GivenTheFollowingRegionIsReturned(string region)
{
_regionCreator
.Setup(x => x.Create(It.IsAny<FileReRoute>()))
.Returns(region);
}
private void ThenTheRegionCreatorIsCalledCorrectly(string expected)
{
_regionCreator
.Verify(x => x.Create(_fileConfiguration.ReRoutes[0]), Times.Once);
} }
[Fact] [Fact]
@ -393,20 +444,18 @@ namespace Ocelot.UnitTests.Configuration
.BDDfy(); .BDDfy();
} }
[Fact] [Theory]
public void should_create_with_headers_to_extract() [MemberData(nameof(AuthenticationConfigTestData.GetAuthenticationData), MemberType = typeof(AuthenticationConfigTestData))]
public void should_create_with_headers_to_extract(string provider, IAuthenticationConfig config, FileConfiguration fileConfig)
{ {
var reRouteOptions = new ReRouteOptionsBuilder() var reRouteOptions = new ReRouteOptionsBuilder()
.WithIsAuthenticated(true) .WithIsAuthenticated(true)
.Build(); .Build();
var authenticationOptions = new AuthenticationOptionsBuilder() var authenticationOptions = new AuthenticationOptionsBuilder()
.WithProvider("IdentityServer") .WithProvider(provider)
.WithProviderRootUrl("http://localhost:51888")
.WithRequireHttps(false)
.WithApiSecret("secret")
.WithApiName("api")
.WithAllowedScopes(new List<string>()) .WithAllowedScopes(new List<string>())
.WithConfig(config)
.Build(); .Build();
var expected = new List<ReRoute> var expected = new List<ReRoute>
@ -423,32 +472,7 @@ namespace Ocelot.UnitTests.Configuration
.Build() .Build()
}; };
this.Given(x => x.GivenTheConfigIs(new FileConfiguration this.Given(x => x.GivenTheConfigIs(fileConfig))
{
ReRoutes = new List<FileReRoute>
{
new FileReRoute
{
UpstreamPathTemplate = "/api/products/{productId}",
DownstreamPathTemplate = "/products/{productId}",
UpstreamHttpMethod = new List<string> { "Get" },
ReRouteIsCaseSensitive = true,
AuthenticationOptions = new FileAuthenticationOptions
{
AllowedScopes= new List<string>(),
Provider = "IdentityServer",
ProviderRootUrl = "http://localhost:51888",
RequireHttps = false,
ApiName= "api",
ApiSecret = "secret"
},
AddHeadersToRequest =
{
{"CustomerId", "Claims[CustomerId] > value"},
}
}
}
}))
.And(x => x.GivenTheConfigIsValid()) .And(x => x.GivenTheConfigIsValid())
.And(x => x.GivenTheAuthOptionsCreatorReturns(authenticationOptions)) .And(x => x.GivenTheAuthOptionsCreatorReturns(authenticationOptions))
.And(x => x.GivenTheFollowingOptionsAreReturned(reRouteOptions)) .And(x => x.GivenTheFollowingOptionsAreReturned(reRouteOptions))
@ -456,25 +480,23 @@ namespace Ocelot.UnitTests.Configuration
.And(x => x.GivenTheLoadBalancerFactoryReturns()) .And(x => x.GivenTheLoadBalancerFactoryReturns())
.When(x => x.WhenICreateTheConfig()) .When(x => x.WhenICreateTheConfig())
.Then(x => x.ThenTheReRoutesAre(expected)) .Then(x => x.ThenTheReRoutesAre(expected))
.And(x => x.ThenTheAuthenticationOptionsAre(expected)) .And(x => x.ThenTheAuthenticationOptionsAre(provider, expected))
.And(x => x.ThenTheAuthOptionsCreatorIsCalledCorrectly()) .And(x => x.ThenTheAuthOptionsCreatorIsCalledCorrectly())
.BDDfy(); .BDDfy();
} }
[Fact] [Theory]
public void should_create_with_authentication_properties() [MemberData(nameof(AuthenticationConfigTestData.GetAuthenticationData), MemberType = typeof(AuthenticationConfigTestData))]
public void should_create_with_authentication_properties(string provider, IAuthenticationConfig config, FileConfiguration fileConfig)
{ {
var reRouteOptions = new ReRouteOptionsBuilder() var reRouteOptions = new ReRouteOptionsBuilder()
.WithIsAuthenticated(true) .WithIsAuthenticated(true)
.Build(); .Build();
var authenticationOptions = new AuthenticationOptionsBuilder() var authenticationOptions = new AuthenticationOptionsBuilder()
.WithProvider("IdentityServer") .WithProvider(provider)
.WithProviderRootUrl("http://localhost:51888")
.WithRequireHttps(false)
.WithApiSecret("secret")
.WithApiName("api")
.WithAllowedScopes(new List<string>()) .WithAllowedScopes(new List<string>())
.WithConfig(config)
.Build(); .Build();
var expected = new List<ReRoute> var expected = new List<ReRoute>
@ -487,35 +509,14 @@ namespace Ocelot.UnitTests.Configuration
.Build() .Build()
}; };
this.Given(x => x.GivenTheConfigIs(new FileConfiguration this.Given(x => x.GivenTheConfigIs(fileConfig))
{
ReRoutes = new List<FileReRoute>
{
new FileReRoute
{
UpstreamPathTemplate = "/api/products/{productId}",
DownstreamPathTemplate = "/products/{productId}",
UpstreamHttpMethod = new List<string> { "Get" },
ReRouteIsCaseSensitive = true,
AuthenticationOptions = new FileAuthenticationOptions
{
AllowedScopes = new List<string>(),
Provider = "IdentityServer",
ProviderRootUrl = "http://localhost:51888",
RequireHttps = false,
ApiName= "api",
ApiSecret = "secret"
}
}
}
}))
.And(x => x.GivenTheConfigIsValid()) .And(x => x.GivenTheConfigIsValid())
.And(x => x.GivenTheFollowingOptionsAreReturned(reRouteOptions)) .And(x => x.GivenTheFollowingOptionsAreReturned(reRouteOptions))
.And(x => x.GivenTheAuthOptionsCreatorReturns(authenticationOptions)) .And(x => x.GivenTheAuthOptionsCreatorReturns(authenticationOptions))
.And(x => x.GivenTheLoadBalancerFactoryReturns()) .And(x => x.GivenTheLoadBalancerFactoryReturns())
.When(x => x.WhenICreateTheConfig()) .When(x => x.WhenICreateTheConfig())
.Then(x => x.ThenTheReRoutesAre(expected)) .Then(x => x.ThenTheReRoutesAre(expected))
.And(x => x.ThenTheAuthenticationOptionsAre(expected)) .And(x => x.ThenTheAuthenticationOptionsAre(provider, expected))
.And(x => x.ThenTheAuthOptionsCreatorIsCalledCorrectly()) .And(x => x.ThenTheAuthOptionsCreatorIsCalledCorrectly())
.BDDfy(); .BDDfy();
} }
@ -586,7 +587,7 @@ namespace Ocelot.UnitTests.Configuration
} }
} }
private void ThenTheAuthenticationOptionsAre(List<ReRoute> expectedReRoutes) private void ThenTheAuthenticationOptionsAre(string provider, List<ReRoute> expectedReRoutes)
{ {
for (int i = 0; i < _config.Data.ReRoutes.Count; i++) for (int i = 0; i < _config.Data.ReRoutes.Count; i++)
{ {
@ -595,11 +596,25 @@ namespace Ocelot.UnitTests.Configuration
result.AllowedScopes.ShouldBe(expected.AllowedScopes); result.AllowedScopes.ShouldBe(expected.AllowedScopes);
result.Provider.ShouldBe(expected.Provider); result.Provider.ShouldBe(expected.Provider);
result.ProviderRootUrl.ShouldBe(expected.ProviderRootUrl);
result.RequireHttps.ShouldBe(expected.RequireHttps);
result.ApiName.ShouldBe(expected.ApiName);
result.ApiSecret.ShouldBe(expected.ApiSecret);
if (provider.ToLower() == "identityserver")
{
var config = result.Config as IdentityServerConfig;
var expectedConfig = expected.Config as IdentityServerConfig;
config.ProviderRootUrl.ShouldBe(expectedConfig.ProviderRootUrl);
config.RequireHttps.ShouldBe(expectedConfig.RequireHttps);
config.ApiName.ShouldBe(expectedConfig.ApiName);
config.ApiSecret.ShouldBe(expectedConfig.ApiSecret);
}
else
{
var config = result.Config as JwtConfig;
var expectedConfig = expected.Config as JwtConfig;
config.Audience.ShouldBe(expectedConfig.Audience);
config.Authority.ShouldBe(expectedConfig.Authority);
}
} }
} }

View File

@ -0,0 +1,16 @@
using Ocelot.Configuration.Creator;
using Shouldly;
using Xunit;
namespace Ocelot.UnitTests.Configuration
{
public class IdentityServerConfigurationCreatorTests
{
[Fact]
public void happy_path_only_exists_for_test_coverage_even_uncle_bob_probably_wouldnt_test_this()
{
var result = IdentityServerConfigurationCreator.GetIdentityServerConfiguration();
result.ApiName.ShouldBe("admin");
}
}
}

View File

@ -0,0 +1,46 @@
using Xunit;
using Shouldly;
using TestStack.BDDfy;
using Ocelot.Controllers;
using System;
using Moq;
using Ocelot.Cache;
using System.Net.Http;
using System.Collections.Generic;
using Microsoft.AspNetCore.Mvc;
namespace Ocelot.UnitTests.Controllers
{
public class OutputCacheControllerTests
{
private OutputCacheController _controller;
private Mock<IOcelotCache<HttpResponseMessage>> _cache;
private IActionResult _result;
public OutputCacheControllerTests()
{
_cache = new Mock<IOcelotCache<HttpResponseMessage>>();
_controller = new OutputCacheController(_cache.Object);
}
[Fact]
public void should_delete_key()
{
this.When(_ => WhenIDeleteTheKey("a"))
.Then(_ => ThenTheKeyIsDeleted("a"))
.BDDfy();
}
private void ThenTheKeyIsDeleted(string key)
{
_result.ShouldBeOfType<NoContentResult>();
_cache
.Verify(x => x.ClearRegion(key), Times.Once);
}
private void WhenIDeleteTheKey(string key)
{
_result = _controller.Delete(key);
}
}
}

View File

@ -1,61 +1,29 @@
using System; namespace Ocelot.UnitTests.DownstreamRouteFinder
{
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using Microsoft.AspNetCore.Builder;
using System.Net.Http;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.TestHost;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Moq; using Moq;
using Ocelot.Configuration.Builder; using Ocelot.Configuration.Builder;
using Ocelot.DownstreamRouteFinder; using Ocelot.DownstreamRouteFinder;
using Ocelot.DownstreamRouteFinder.Finder; using Ocelot.DownstreamRouteFinder.Finder;
using Ocelot.DownstreamRouteFinder.Middleware; using Ocelot.DownstreamRouteFinder.Middleware;
using Ocelot.DownstreamRouteFinder.UrlMatcher; using Ocelot.DownstreamRouteFinder.UrlMatcher;
using Ocelot.Infrastructure.RequestData;
using Ocelot.Logging; using Ocelot.Logging;
using Ocelot.Responses; using Ocelot.Responses;
using TestStack.BDDfy; using TestStack.BDDfy;
using Xunit; using Xunit;
namespace Ocelot.UnitTests.DownstreamRouteFinder public class DownstreamRouteFinderMiddlewareTests : ServerHostedMiddlewareTest
{
public class DownstreamRouteFinderMiddlewareTests : IDisposable
{ {
private readonly Mock<IDownstreamRouteFinder> _downstreamRouteFinder; private readonly Mock<IDownstreamRouteFinder> _downstreamRouteFinder;
private readonly Mock<IRequestScopedDataRepository> _scopedRepository;
private readonly string _url;
private readonly TestServer _server;
private readonly HttpClient _client;
private Response<DownstreamRoute> _downstreamRoute; private Response<DownstreamRoute> _downstreamRoute;
private HttpResponseMessage _result;
public DownstreamRouteFinderMiddlewareTests() public DownstreamRouteFinderMiddlewareTests()
{ {
_url = "http://localhost:51879";
_downstreamRouteFinder = new Mock<IDownstreamRouteFinder>(); _downstreamRouteFinder = new Mock<IDownstreamRouteFinder>();
_scopedRepository = new Mock<IRequestScopedDataRepository>();
var builder = new WebHostBuilder() GivenTheTestServerIsConfigured();
.ConfigureServices(x =>
{
x.AddSingleton<IOcelotLoggerFactory, AspDotNetLoggerFactory>();
x.AddLogging();
x.AddSingleton(_downstreamRouteFinder.Object);
x.AddSingleton(_scopedRepository.Object);
})
.UseUrls(_url)
.UseKestrel()
.UseContentRoot(Directory.GetCurrentDirectory())
.UseIISIntegration()
.UseUrls(_url)
.Configure(app =>
{
app.UseDownstreamRouteFinderMiddleware();
});
_server = new TestServer(builder);
_client = _server.CreateClient();
} }
[Fact] [Fact]
@ -73,16 +41,17 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder
.BDDfy(); .BDDfy();
} }
protected override void GivenTheTestServerServicesAreConfigured(IServiceCollection services)
private void ThenTheScopedDataRepositoryIsCalledCorrectly()
{ {
_scopedRepository services.AddSingleton<IOcelotLoggerFactory, AspDotNetLoggerFactory>();
.Verify(x => x.Add("DownstreamRoute", _downstreamRoute.Data), Times.Once()); services.AddLogging();
services.AddSingleton(_downstreamRouteFinder.Object);
services.AddSingleton(ScopedRepository.Object);
} }
private void WhenICallTheMiddleware() protected override void GivenTheTestServerPipelineIsConfigured(IApplicationBuilder app)
{ {
_result = _client.GetAsync(_url).Result; app.UseDownstreamRouteFinderMiddleware();
} }
private void GivenTheDownStreamRouteFinderReturns(DownstreamRoute downstreamRoute) private void GivenTheDownStreamRouteFinderReturns(DownstreamRoute downstreamRoute)
@ -93,10 +62,10 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder
.ReturnsAsync(_downstreamRoute); .ReturnsAsync(_downstreamRoute);
} }
public void Dispose() private void ThenTheScopedDataRepositoryIsCalledCorrectly()
{ {
_client.Dispose(); ScopedRepository
_server.Dispose(); .Verify(x => x.Add("DownstreamRoute", _downstreamRoute.Data), Times.Once());
} }
} }
} }

View File

@ -1,9 +1,9 @@
using System; namespace Ocelot.UnitTests.DownstreamUrlCreator
{
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO;
using System.Net.Http; using System.Net.Http;
using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.TestHost;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Moq; using Moq;
using Ocelot.Configuration.Builder; using Ocelot.Configuration.Builder;
@ -20,54 +20,26 @@ using TestStack.BDDfy;
using Xunit; using Xunit;
using Shouldly; using Shouldly;
namespace Ocelot.UnitTests.DownstreamUrlCreator public class DownstreamUrlCreatorMiddlewareTests : ServerHostedMiddlewareTest
{
public class DownstreamUrlCreatorMiddlewareTests : IDisposable
{ {
private readonly Mock<IDownstreamPathPlaceholderReplacer> _downstreamUrlTemplateVariableReplacer; private readonly Mock<IDownstreamPathPlaceholderReplacer> _downstreamUrlTemplateVariableReplacer;
private readonly Mock<IRequestScopedDataRepository> _scopedRepository;
private readonly Mock<IUrlBuilder> _urlBuilder; private readonly Mock<IUrlBuilder> _urlBuilder;
private readonly string _url;
private readonly TestServer _server;
private readonly HttpClient _client;
private Response<DownstreamRoute> _downstreamRoute; private Response<DownstreamRoute> _downstreamRoute;
private OkResponse<DownstreamPath> _downstreamPath; private OkResponse<DownstreamPath> _downstreamPath;
private HttpRequestMessage _downstreamRequest; private HttpRequestMessage _downstreamRequest;
private HttpResponseMessage _result;
public DownstreamUrlCreatorMiddlewareTests() public DownstreamUrlCreatorMiddlewareTests()
{ {
_url = "http://localhost:51879";
_downstreamUrlTemplateVariableReplacer = new Mock<IDownstreamPathPlaceholderReplacer>(); _downstreamUrlTemplateVariableReplacer = new Mock<IDownstreamPathPlaceholderReplacer>();
_scopedRepository = new Mock<IRequestScopedDataRepository>();
_urlBuilder = new Mock<IUrlBuilder>(); _urlBuilder = new Mock<IUrlBuilder>();
var builder = new WebHostBuilder()
.ConfigureServices(x =>
{
x.AddSingleton<IOcelotLoggerFactory, AspDotNetLoggerFactory>();
x.AddLogging();
x.AddSingleton(_downstreamUrlTemplateVariableReplacer.Object);
x.AddSingleton(_scopedRepository.Object);
x.AddSingleton(_urlBuilder.Object);
})
.UseUrls(_url)
.UseKestrel()
.UseContentRoot(Directory.GetCurrentDirectory())
.UseIISIntegration()
.UseUrls(_url)
.Configure(app =>
{
app.UseDownstreamUrlCreatorMiddleware();
});
_downstreamRequest = new HttpRequestMessage(HttpMethod.Get, "https://my.url/abc/?q=123"); _downstreamRequest = new HttpRequestMessage(HttpMethod.Get, "https://my.url/abc/?q=123");
_scopedRepository ScopedRepository
.Setup(sr => sr.Get<HttpRequestMessage>("DownstreamRequest")) .Setup(sr => sr.Get<HttpRequestMessage>("DownstreamRequest"))
.Returns(new OkResponse<HttpRequestMessage>(_downstreamRequest)); .Returns(new OkResponse<HttpRequestMessage>(_downstreamRequest));
_server = new TestServer(builder); GivenTheTestServerIsConfigured();
_client = _server.CreateClient();
} }
[Fact] [Fact]
@ -88,10 +60,24 @@ namespace Ocelot.UnitTests.DownstreamUrlCreator
.BDDfy(); .BDDfy();
} }
protected override void GivenTheTestServerServicesAreConfigured(IServiceCollection services)
{
services.AddSingleton<IOcelotLoggerFactory, AspDotNetLoggerFactory>();
services.AddLogging();
services.AddSingleton(_downstreamUrlTemplateVariableReplacer.Object);
services.AddSingleton(ScopedRepository.Object);
services.AddSingleton(_urlBuilder.Object);
}
protected override void GivenTheTestServerPipelineIsConfigured(IApplicationBuilder app)
{
app.UseDownstreamUrlCreatorMiddleware();
}
private void GivenTheDownStreamRouteIs(DownstreamRoute downstreamRoute) private void GivenTheDownStreamRouteIs(DownstreamRoute downstreamRoute)
{ {
_downstreamRoute = new OkResponse<DownstreamRoute>(downstreamRoute); _downstreamRoute = new OkResponse<DownstreamRoute>(downstreamRoute);
_scopedRepository ScopedRepository
.Setup(x => x.Get<DownstreamRoute>(It.IsAny<string>())) .Setup(x => x.Get<DownstreamRoute>(It.IsAny<string>()))
.Returns(_downstreamRoute); .Returns(_downstreamRoute);
} }
@ -109,20 +95,9 @@ namespace Ocelot.UnitTests.DownstreamUrlCreator
.Returns(_downstreamPath); .Returns(_downstreamPath);
} }
private void WhenICallTheMiddleware()
{
_result = _client.GetAsync(_url).Result;
}
private void ThenTheDownstreamRequestUriIs(string expectedUri) private void ThenTheDownstreamRequestUriIs(string expectedUri)
{ {
_downstreamRequest.RequestUri.OriginalString.ShouldBe(expectedUri); _downstreamRequest.RequestUri.OriginalString.ShouldBe(expectedUri);
} }
public void Dispose()
{
_client.Dispose();
_server.Dispose();
}
} }
} }

View File

@ -0,0 +1,87 @@
namespace Ocelot.UnitTests.Errors
{
using System;
using System.Net;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;
using Ocelot.Errors.Middleware;
using Ocelot.Logging;
using Shouldly;
using TestStack.BDDfy;
using Xunit;
using Microsoft.AspNetCore.Http;
public class ExceptionHandlerMiddlewareTests : ServerHostedMiddlewareTest
{
bool _shouldThrowAnException = false;
public ExceptionHandlerMiddlewareTests()
{
GivenTheTestServerIsConfigured();
}
[Fact]
public void NoDownstreamException()
{
this.Given(_ => GivenAnExceptionWillNotBeThrownDownstream())
.When(_ => WhenICallTheMiddleware())
.Then(_ => ThenTheResponseIsOk())
.BDDfy();
}
[Fact]
public void DownstreamException()
{
this.Given(_ => GivenAnExceptionWillBeThrownDownstream())
.When(_ => WhenICallTheMiddleware())
.Then(_ => ThenTheResponseIsError())
.BDDfy();
}
protected override void GivenTheTestServerServicesAreConfigured(IServiceCollection services)
{
services.AddSingleton<IOcelotLoggerFactory, AspDotNetLoggerFactory>();
services.AddLogging();
services.AddSingleton(ScopedRepository.Object);
}
protected override void GivenTheTestServerPipelineIsConfigured(IApplicationBuilder app)
{
app.UseExceptionHandlerMiddleware();
app.Run(DownstreamExceptionSimulator);
}
private async Task DownstreamExceptionSimulator(HttpContext context)
{
await Task.CompletedTask;
if (_shouldThrowAnException)
{
throw new Exception("BOOM");
}
context.Response.StatusCode = (int)HttpStatusCode.OK;
}
private void GivenAnExceptionWillNotBeThrownDownstream()
{
_shouldThrowAnException = false;
}
private void GivenAnExceptionWillBeThrownDownstream()
{
_shouldThrowAnException = true;
}
private void ThenTheResponseIsOk()
{
ResponseMessage.StatusCode.ShouldBe(HttpStatusCode.OK);
}
private void ThenTheResponseIsError()
{
ResponseMessage.StatusCode.ShouldBe(HttpStatusCode.InternalServerError);
}
}
}

View File

@ -1,9 +1,8 @@
using System; namespace Ocelot.UnitTests.Headers
{
using System.Collections.Generic; using System.Collections.Generic;
using System.IO;
using System.Net.Http; using System.Net.Http;
using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.TestHost;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Moq; using Moq;
using Ocelot.Configuration; using Ocelot.Configuration;
@ -12,58 +11,27 @@ using Ocelot.DownstreamRouteFinder;
using Ocelot.DownstreamRouteFinder.UrlMatcher; using Ocelot.DownstreamRouteFinder.UrlMatcher;
using Ocelot.Headers; using Ocelot.Headers;
using Ocelot.Headers.Middleware; using Ocelot.Headers.Middleware;
using Ocelot.Infrastructure.RequestData;
using Ocelot.Logging; using Ocelot.Logging;
using Ocelot.Responses; using Ocelot.Responses;
using TestStack.BDDfy; using TestStack.BDDfy;
using Xunit; using Xunit;
namespace Ocelot.UnitTests.Headers public class HttpRequestHeadersBuilderMiddlewareTests : ServerHostedMiddlewareTest
{ {
public class HttpRequestHeadersBuilderMiddlewareTests : IDisposable
{
private readonly Mock<IRequestScopedDataRepository> _scopedRepository;
private readonly Mock<IAddHeadersToRequest> _addHeaders; private readonly Mock<IAddHeadersToRequest> _addHeaders;
private readonly HttpRequestMessage _downstreamRequest; private readonly HttpRequestMessage _downstreamRequest;
private readonly string _url;
private readonly TestServer _server;
private readonly HttpClient _client;
private Response<DownstreamRoute> _downstreamRoute; private Response<DownstreamRoute> _downstreamRoute;
private HttpResponseMessage _result;
public HttpRequestHeadersBuilderMiddlewareTests() public HttpRequestHeadersBuilderMiddlewareTests()
{ {
_url = "http://localhost:51879";
_scopedRepository = new Mock<IRequestScopedDataRepository>();
_addHeaders = new Mock<IAddHeadersToRequest>(); _addHeaders = new Mock<IAddHeadersToRequest>();
var builder = new WebHostBuilder()
.ConfigureServices(x =>
{
x.AddSingleton<IOcelotLoggerFactory, AspDotNetLoggerFactory>();
x.AddLogging();
x.AddSingleton(_addHeaders.Object);
x.AddSingleton(_scopedRepository.Object);
})
.UseUrls(_url)
.UseKestrel()
.UseContentRoot(Directory.GetCurrentDirectory())
.UseIISIntegration()
.UseUrls(_url)
.Configure(app =>
{
app.UseHttpRequestHeadersBuilderMiddleware();
});
_downstreamRequest = new HttpRequestMessage(); _downstreamRequest = new HttpRequestMessage();
ScopedRepository
_scopedRepository
.Setup(sr => sr.Get<HttpRequestMessage>("DownstreamRequest")) .Setup(sr => sr.Get<HttpRequestMessage>("DownstreamRequest"))
.Returns(new OkResponse<HttpRequestMessage>(_downstreamRequest)); .Returns(new OkResponse<HttpRequestMessage>(_downstreamRequest));
_server = new TestServer(builder); GivenTheTestServerIsConfigured();
_client = _server.CreateClient();
} }
[Fact] [Fact]
@ -86,6 +54,27 @@ namespace Ocelot.UnitTests.Headers
.BDDfy(); .BDDfy();
} }
protected override void GivenTheTestServerServicesAreConfigured(IServiceCollection services)
{
services.AddSingleton<IOcelotLoggerFactory, AspDotNetLoggerFactory>();
services.AddLogging();
services.AddSingleton(_addHeaders.Object);
services.AddSingleton(ScopedRepository.Object);
}
protected override void GivenTheTestServerPipelineIsConfigured(IApplicationBuilder app)
{
app.UseHttpRequestHeadersBuilderMiddleware();
}
private void GivenTheDownStreamRouteIs(DownstreamRoute downstreamRoute)
{
_downstreamRoute = new OkResponse<DownstreamRoute>(downstreamRoute);
ScopedRepository
.Setup(x => x.Get<DownstreamRoute>(It.IsAny<string>()))
.Returns(_downstreamRoute);
}
private void GivenTheAddHeadersToDownstreamRequestReturnsOk() private void GivenTheAddHeadersToDownstreamRequestReturnsOk()
{ {
_addHeaders _addHeaders
@ -104,24 +93,5 @@ namespace Ocelot.UnitTests.Headers
It.IsAny<IEnumerable<System.Security.Claims.Claim>>(), It.IsAny<IEnumerable<System.Security.Claims.Claim>>(),
_downstreamRequest), Times.Once); _downstreamRequest), Times.Once);
} }
private void WhenICallTheMiddleware()
{
_result = _client.GetAsync(_url).Result;
}
private void GivenTheDownStreamRouteIs(DownstreamRoute downstreamRoute)
{
_downstreamRoute = new OkResponse<DownstreamRoute>(downstreamRoute);
_scopedRepository
.Setup(x => x.Get<DownstreamRoute>(It.IsAny<string>()))
.Returns(_downstreamRoute);
}
public void Dispose()
{
_client.Dispose();
_server.Dispose();
}
} }
} }

View File

@ -1,34 +1,26 @@
namespace Ocelot.UnitTests.LoadBalancer
{
using System.Collections.Generic; using System.Collections.Generic;
using System.IO;
using System.Net.Http; using System.Net.Http;
using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.TestHost;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Moq; using Moq;
using Ocelot.Configuration.Builder; using Ocelot.Configuration.Builder;
using Ocelot.DownstreamRouteFinder; using Ocelot.DownstreamRouteFinder;
using Ocelot.Errors; using Ocelot.Errors;
using Ocelot.Infrastructure.RequestData;
using Ocelot.LoadBalancer.LoadBalancers; using Ocelot.LoadBalancer.LoadBalancers;
using Ocelot.LoadBalancer.Middleware; using Ocelot.LoadBalancer.Middleware;
using Ocelot.Logging; using Ocelot.Logging;
using Ocelot.Responses; using Ocelot.Responses;
using Ocelot.Values; using Ocelot.Values;
using Shouldly;
using TestStack.BDDfy; using TestStack.BDDfy;
using Xunit; using Xunit;
using Shouldly;
namespace Ocelot.UnitTests.LoadBalancer public class LoadBalancerMiddlewareTests : ServerHostedMiddlewareTest
{
public class LoadBalancerMiddlewareTests
{ {
private readonly Mock<ILoadBalancerHouse> _loadBalancerHouse; private readonly Mock<ILoadBalancerHouse> _loadBalancerHouse;
private readonly Mock<IRequestScopedDataRepository> _scopedRepository;
private readonly Mock<ILoadBalancer> _loadBalancer; private readonly Mock<ILoadBalancer> _loadBalancer;
private readonly string _url;
private readonly TestServer _server;
private readonly HttpClient _client;
private HttpResponseMessage _result;
private HostAndPort _hostAndPort; private HostAndPort _hostAndPort;
private OkResponse<DownstreamRoute> _downstreamRoute; private OkResponse<DownstreamRoute> _downstreamRoute;
private ErrorResponse<ILoadBalancer> _getLoadBalancerHouseError; private ErrorResponse<ILoadBalancer> _getLoadBalancerHouseError;
@ -37,35 +29,17 @@ namespace Ocelot.UnitTests.LoadBalancer
public LoadBalancerMiddlewareTests() public LoadBalancerMiddlewareTests()
{ {
_url = "http://localhost:51879";
_loadBalancerHouse = new Mock<ILoadBalancerHouse>(); _loadBalancerHouse = new Mock<ILoadBalancerHouse>();
_scopedRepository = new Mock<IRequestScopedDataRepository>();
_loadBalancer = new Mock<ILoadBalancer>(); _loadBalancer = new Mock<ILoadBalancer>();
_loadBalancerHouse = new Mock<ILoadBalancerHouse>(); _loadBalancerHouse = new Mock<ILoadBalancerHouse>();
var builder = new WebHostBuilder()
.ConfigureServices(x =>
{
x.AddSingleton<IOcelotLoggerFactory, AspDotNetLoggerFactory>();
x.AddLogging();
x.AddSingleton(_loadBalancerHouse.Object);
x.AddSingleton(_scopedRepository.Object);
})
.UseUrls(_url)
.UseKestrel()
.UseContentRoot(Directory.GetCurrentDirectory())
.UseIISIntegration()
.UseUrls(_url)
.Configure(app =>
{
app.UseLoadBalancingMiddleware();
});
_downstreamRequest = new HttpRequestMessage(HttpMethod.Get, ""); _downstreamRequest = new HttpRequestMessage(HttpMethod.Get, "");
_scopedRepository
ScopedRepository
.Setup(sr => sr.Get<HttpRequestMessage>("DownstreamRequest")) .Setup(sr => sr.Get<HttpRequestMessage>("DownstreamRequest"))
.Returns(new OkResponse<HttpRequestMessage>(_downstreamRequest)); .Returns(new OkResponse<HttpRequestMessage>(_downstreamRequest));
_server = new TestServer(builder);
_client = _server.CreateClient(); GivenTheTestServerIsConfigured();
} }
[Fact] [Fact]
@ -118,6 +92,19 @@ namespace Ocelot.UnitTests.LoadBalancer
.BDDfy(); .BDDfy();
} }
protected override void GivenTheTestServerServicesAreConfigured(IServiceCollection services)
{
services.AddSingleton<IOcelotLoggerFactory, AspDotNetLoggerFactory>();
services.AddLogging();
services.AddSingleton(_loadBalancerHouse.Object);
services.AddSingleton(ScopedRepository.Object);
}
protected override void GivenTheTestServerPipelineIsConfigured(IApplicationBuilder app)
{
app.UseLoadBalancingMiddleware();
}
private void GivenTheDownStreamUrlIs(string downstreamUrl) private void GivenTheDownStreamUrlIs(string downstreamUrl)
{ {
_downstreamRequest.RequestUri = new System.Uri(downstreamUrl); _downstreamRequest.RequestUri = new System.Uri(downstreamUrl);
@ -142,7 +129,7 @@ namespace Ocelot.UnitTests.LoadBalancer
private void GivenTheDownStreamRouteIs(DownstreamRoute downstreamRoute) private void GivenTheDownStreamRouteIs(DownstreamRoute downstreamRoute)
{ {
_downstreamRoute = new OkResponse<DownstreamRoute>(downstreamRoute); _downstreamRoute = new OkResponse<DownstreamRoute>(downstreamRoute);
_scopedRepository ScopedRepository
.Setup(x => x.Get<DownstreamRoute>(It.IsAny<string>())) .Setup(x => x.Get<DownstreamRoute>(It.IsAny<string>()))
.Returns(_downstreamRoute); .Returns(_downstreamRoute);
} }
@ -154,7 +141,6 @@ namespace Ocelot.UnitTests.LoadBalancer
.Returns(new OkResponse<ILoadBalancer>(_loadBalancer.Object)); .Returns(new OkResponse<ILoadBalancer>(_loadBalancer.Object));
} }
private void GivenTheLoadBalancerHouseReturnsAnError() private void GivenTheLoadBalancerHouseReturnsAnError()
{ {
_getLoadBalancerHouseError = new ErrorResponse<ILoadBalancer>(new List<Ocelot.Errors.Error>() _getLoadBalancerHouseError = new ErrorResponse<ILoadBalancer>(new List<Ocelot.Errors.Error>()
@ -167,49 +153,36 @@ namespace Ocelot.UnitTests.LoadBalancer
.Returns(_getLoadBalancerHouseError); .Returns(_getLoadBalancerHouseError);
} }
private void WhenICallTheMiddleware()
{
_result = _client.GetAsync(_url).Result;
}
private void ThenAnErrorStatingLoadBalancerCouldNotBeFoundIsSetOnPipeline() private void ThenAnErrorStatingLoadBalancerCouldNotBeFoundIsSetOnPipeline()
{ {
_scopedRepository ScopedRepository
.Verify(x => x.Add("OcelotMiddlewareError", true), Times.Once); .Verify(x => x.Add("OcelotMiddlewareError", true), Times.Once);
_scopedRepository ScopedRepository
.Verify(x => x.Add("OcelotMiddlewareErrors", _getLoadBalancerHouseError.Errors), Times.Once); .Verify(x => x.Add("OcelotMiddlewareErrors", _getLoadBalancerHouseError.Errors), Times.Once);
} }
private void ThenAnErrorSayingReleaseFailedIsSetOnThePipeline() private void ThenAnErrorSayingReleaseFailedIsSetOnThePipeline()
{ {
_scopedRepository ScopedRepository
.Verify(x => x.Add("OcelotMiddlewareError", true), Times.Once); .Verify(x => x.Add("OcelotMiddlewareError", true), Times.Once);
_scopedRepository ScopedRepository
.Verify(x => x.Add("OcelotMiddlewareErrors", It.IsAny<List<Error>>()), Times.Once); .Verify(x => x.Add("OcelotMiddlewareErrors", It.IsAny<List<Error>>()), Times.Once);
} }
private void ThenAnErrorStatingHostAndPortCouldNotBeFoundIsSetOnPipeline() private void ThenAnErrorStatingHostAndPortCouldNotBeFoundIsSetOnPipeline()
{ {
_scopedRepository ScopedRepository
.Verify(x => x.Add("OcelotMiddlewareError", true), Times.Once); .Verify(x => x.Add("OcelotMiddlewareError", true), Times.Once);
_scopedRepository ScopedRepository
.Verify(x => x.Add("OcelotMiddlewareErrors", _getHostAndPortError.Errors), Times.Once); .Verify(x => x.Add("OcelotMiddlewareErrors", _getHostAndPortError.Errors), Times.Once);
} }
private void ThenTheDownstreamUrlIsReplacedWith(string expectedUri) private void ThenTheDownstreamUrlIsReplacedWith(string expectedUri)
{ {
_downstreamRequest.RequestUri.OriginalString.ShouldBe(expectedUri); _downstreamRequest.RequestUri.OriginalString.ShouldBe(expectedUri);
} }
public void Dispose()
{
_client.Dispose();
_server.Dispose();
}
} }
} }

View File

@ -19,6 +19,10 @@
<DebugSymbols>True</DebugSymbols> <DebugSymbols>True</DebugSymbols>
</PropertyGroup> </PropertyGroup>
<ItemGroup>
<Compile Remove="Responder\HttpContextResponderTests.cs" />
</ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\..\src\Ocelot\Ocelot.csproj" /> <ProjectReference Include="..\..\src\Ocelot\Ocelot.csproj" />
</ItemGroup> </ItemGroup>

View File

@ -1,16 +1,13 @@
using System; namespace Ocelot.UnitTests.QueryStrings
{
using System.Collections.Generic; using System.Collections.Generic;
using System.IO;
using System.Net.Http; using System.Net.Http;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.TestHost;
using Microsoft.Extensions.DependencyInjection; 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.UrlMatcher; using Ocelot.DownstreamRouteFinder.UrlMatcher;
using Ocelot.Infrastructure.RequestData;
using Ocelot.Logging; using Ocelot.Logging;
using Ocelot.QueryStrings; using Ocelot.QueryStrings;
using Ocelot.QueryStrings.Middleware; using Ocelot.QueryStrings.Middleware;
@ -18,50 +15,23 @@ using Ocelot.Responses;
using TestStack.BDDfy; using TestStack.BDDfy;
using Xunit; using Xunit;
using System.Security.Claims; using System.Security.Claims;
using Microsoft.AspNetCore.Builder;
namespace Ocelot.UnitTests.QueryStrings public class QueryStringBuilderMiddlewareTests : ServerHostedMiddlewareTest
{ {
public class QueryStringBuilderMiddlewareTests : IDisposable
{
private readonly Mock<IRequestScopedDataRepository> _scopedRepository;
private readonly Mock<IAddQueriesToRequest> _addQueries; private readonly Mock<IAddQueriesToRequest> _addQueries;
private readonly string _url;
private readonly TestServer _server;
private readonly HttpClient _client;
private readonly HttpRequestMessage _downstreamRequest; private readonly HttpRequestMessage _downstreamRequest;
private Response<DownstreamRoute> _downstreamRoute; private Response<DownstreamRoute> _downstreamRoute;
private HttpResponseMessage _result;
public QueryStringBuilderMiddlewareTests() public QueryStringBuilderMiddlewareTests()
{ {
_url = "http://localhost:51879";
_scopedRepository = new Mock<IRequestScopedDataRepository>();
_addQueries = new Mock<IAddQueriesToRequest>(); _addQueries = new Mock<IAddQueriesToRequest>();
var builder = new WebHostBuilder()
.ConfigureServices(x =>
{
x.AddSingleton<IOcelotLoggerFactory, AspDotNetLoggerFactory>();
x.AddLogging();
x.AddSingleton(_addQueries.Object);
x.AddSingleton(_scopedRepository.Object);
})
.UseUrls(_url)
.UseKestrel()
.UseContentRoot(Directory.GetCurrentDirectory())
.UseIISIntegration()
.UseUrls(_url)
.Configure(app =>
{
app.UseQueryStringBuilderMiddleware();
});
_downstreamRequest = new HttpRequestMessage(); _downstreamRequest = new HttpRequestMessage();
ScopedRepository.Setup(sr => sr.Get<HttpRequestMessage>("DownstreamRequest"))
_scopedRepository.Setup(sr => sr.Get<HttpRequestMessage>("DownstreamRequest"))
.Returns(new OkResponse<HttpRequestMessage>(_downstreamRequest)); .Returns(new OkResponse<HttpRequestMessage>(_downstreamRequest));
_server = new TestServer(builder); GivenTheTestServerIsConfigured();
_client = _server.CreateClient();
} }
[Fact] [Fact]
@ -84,6 +54,19 @@ namespace Ocelot.UnitTests.QueryStrings
.BDDfy(); .BDDfy();
} }
protected override void GivenTheTestServerServicesAreConfigured(IServiceCollection services)
{
services.AddSingleton<IOcelotLoggerFactory, AspDotNetLoggerFactory>();
services.AddLogging();
services.AddSingleton(_addQueries.Object);
services.AddSingleton(ScopedRepository.Object);
}
protected override void GivenTheTestServerPipelineIsConfigured(IApplicationBuilder app)
{
app.UseQueryStringBuilderMiddleware();
}
private void GivenTheAddHeadersToRequestReturnsOk() private void GivenTheAddHeadersToRequestReturnsOk()
{ {
_addQueries _addQueries
@ -103,23 +86,12 @@ namespace Ocelot.UnitTests.QueryStrings
_downstreamRequest), Times.Once); _downstreamRequest), Times.Once);
} }
private void WhenICallTheMiddleware()
{
_result = _client.GetAsync(_url).Result;
}
private void GivenTheDownStreamRouteIs(DownstreamRoute downstreamRoute) private void GivenTheDownStreamRouteIs(DownstreamRoute downstreamRoute)
{ {
_downstreamRoute = new OkResponse<DownstreamRoute>(downstreamRoute); _downstreamRoute = new OkResponse<DownstreamRoute>(downstreamRoute);
_scopedRepository ScopedRepository
.Setup(x => x.Get<DownstreamRoute>(It.IsAny<string>())) .Setup(x => x.Get<DownstreamRoute>(It.IsAny<string>()))
.Returns(_downstreamRoute); .Returns(_downstreamRoute);
} }
public void Dispose()
{
_client.Dispose();
_server.Dispose();
}
} }
} }

View File

@ -1,71 +1,33 @@
using Microsoft.AspNetCore.Hosting; namespace Ocelot.UnitTests.RateLimit
using Microsoft.AspNetCore.Builder; {
using Microsoft.AspNetCore.TestHost;
using Microsoft.AspNetCore.Http;
using Moq;
using Ocelot.Infrastructure.RequestData;
using Ocelot.RateLimit;
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using System.Net.Http; using System.Net.Http;
using System.Threading.Tasks; using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Ocelot.Logging; using Moq;
using System.IO;
using Ocelot.RateLimit.Middleware;
using Ocelot.DownstreamRouteFinder;
using Ocelot.Responses;
using Xunit;
using TestStack.BDDfy;
using Ocelot.Configuration.Builder;
using Shouldly;
using Ocelot.Configuration; using Ocelot.Configuration;
using Ocelot.Configuration.Builder;
using Ocelot.DownstreamRouteFinder;
using Ocelot.Logging;
using Ocelot.RateLimit;
using Ocelot.RateLimit.Middleware;
using Ocelot.Responses;
using Shouldly;
using TestStack.BDDfy;
using Xunit;
namespace Ocelot.UnitTests.RateLimit public class ClientRateLimitMiddlewareTests : ServerHostedMiddlewareTest
{ {
public class ClientRateLimitMiddlewareTests
{
private readonly Mock<IRequestScopedDataRepository> _scopedRepository;
private readonly string _url;
private readonly TestServer _server;
private readonly HttpClient _client;
private OkResponse<DownstreamRoute> _downstreamRoute; private OkResponse<DownstreamRoute> _downstreamRoute;
private int responseStatusCode; private int responseStatusCode;
public ClientRateLimitMiddlewareTests() public ClientRateLimitMiddlewareTests()
{ {
_url = "http://localhost:51879/api/ClientRateLimit"; GivenTheTestServerIsConfigured();
_scopedRepository = new Mock<IRequestScopedDataRepository>();
var builder = new WebHostBuilder()
.ConfigureServices(x =>
{
x.AddSingleton<IOcelotLoggerFactory, AspDotNetLoggerFactory>();
x.AddLogging();
x.AddMemoryCache();
x.AddSingleton<IRateLimitCounterHandler, MemoryCacheRateLimitCounterHandler>();
x.AddSingleton(_scopedRepository.Object);
})
.UseUrls(_url)
.UseKestrel()
.UseContentRoot(Directory.GetCurrentDirectory())
.UseIISIntegration()
.UseUrls(_url)
.Configure(app =>
{
app.UseRateLimiting();
app.Run(async context =>
{
context.Response.StatusCode = 200;
await context.Response.WriteAsync("This is ratelimit test");
});
});
_server = new TestServer(builder);
_client = _server.CreateClient();
} }
[Fact] [Fact]
public void should_call_middleware_and_ratelimiting() public void should_call_middleware_and_ratelimiting()
{ {
@ -98,11 +60,29 @@ namespace Ocelot.UnitTests.RateLimit
.BDDfy(); .BDDfy();
} }
protected override void GivenTheTestServerServicesAreConfigured(IServiceCollection services)
{
services.AddSingleton<IOcelotLoggerFactory, AspDotNetLoggerFactory>();
services.AddLogging();
services.AddMemoryCache();
services.AddSingleton<IRateLimitCounterHandler, MemoryCacheRateLimitCounterHandler>();
services.AddSingleton(ScopedRepository.Object);
}
protected override void GivenTheTestServerPipelineIsConfigured(IApplicationBuilder app)
{
app.UseRateLimiting();
app.Run(async context =>
{
context.Response.StatusCode = 200;
await context.Response.WriteAsync("This is ratelimit test");
});
}
private void GivenTheDownStreamRouteIs(DownstreamRoute downstreamRoute) private void GivenTheDownStreamRouteIs(DownstreamRoute downstreamRoute)
{ {
_downstreamRoute = new OkResponse<DownstreamRoute>(downstreamRoute); _downstreamRoute = new OkResponse<DownstreamRoute>(downstreamRoute);
_scopedRepository ScopedRepository
.Setup(x => x.Get<DownstreamRoute>(It.IsAny<string>())) .Setup(x => x.Get<DownstreamRoute>(It.IsAny<string>()))
.Returns(_downstreamRoute); .Returns(_downstreamRoute);
} }
@ -110,28 +90,27 @@ namespace Ocelot.UnitTests.RateLimit
private void WhenICallTheMiddlewareMultipleTime(int times) private void WhenICallTheMiddlewareMultipleTime(int times)
{ {
var clientId = "ocelotclient1"; var clientId = "ocelotclient1";
// Act
for (int i = 0; i < times; i++) for (int i = 0; i < times; i++)
{ {
var request = new HttpRequestMessage(new HttpMethod("GET"), _url); var request = new HttpRequestMessage(new HttpMethod("GET"), Url);
request.Headers.Add("ClientId", clientId); request.Headers.Add("ClientId", clientId);
var response = _client.SendAsync(request); var response = Client.SendAsync(request);
responseStatusCode = (int)response.Result.StatusCode; responseStatusCode = (int)response.Result.StatusCode;
} }
} }
private void WhenICallTheMiddlewareWithWhiteClient() private void WhenICallTheMiddlewareWithWhiteClient()
{ {
var clientId = "ocelotclient2"; var clientId = "ocelotclient2";
// Act
for (int i = 0; i < 10; i++) for (int i = 0; i < 10; i++)
{ {
var request = new HttpRequestMessage(new HttpMethod("GET"), _url); var request = new HttpRequestMessage(new HttpMethod("GET"), Url);
request.Headers.Add("ClientId", clientId); request.Headers.Add("ClientId", clientId);
var response = _client.SendAsync(request); var response = Client.SendAsync(request);
responseStatusCode = (int)response.Result.StatusCode; responseStatusCode = (int)response.Result.StatusCode;
} }
} }

View File

@ -1,9 +1,7 @@
using System; namespace Ocelot.UnitTests.Request
{
using System.Collections.Generic; using System.Collections.Generic;
using System.IO;
using System.Net.Http; using System.Net.Http;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.TestHost;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Moq; using Moq;
using Ocelot.Configuration.Builder; using Ocelot.Configuration.Builder;
@ -17,47 +15,23 @@ using Ocelot.Responses;
using TestStack.BDDfy; using TestStack.BDDfy;
using Xunit; using Xunit;
using Ocelot.Requester.QoS; using Ocelot.Requester.QoS;
using Microsoft.AspNetCore.Builder;
namespace Ocelot.UnitTests.Request public class HttpRequestBuilderMiddlewareTests : ServerHostedMiddlewareTest
{
public class HttpRequestBuilderMiddlewareTests : IDisposable
{ {
private readonly Mock<IRequestCreator> _requestBuilder; private readonly Mock<IRequestCreator> _requestBuilder;
private readonly Mock<IRequestScopedDataRepository> _scopedRepository; private readonly Mock<IRequestScopedDataRepository> _scopedRepository;
private readonly Mock<IQosProviderHouse> _qosProviderHouse; private readonly Mock<IQosProviderHouse> _qosProviderHouse;
private readonly HttpRequestMessage _downstreamRequest; private readonly HttpRequestMessage _downstreamRequest;
private readonly string _url;
private readonly TestServer _server;
private readonly HttpClient _client;
private HttpResponseMessage _result;
private OkResponse<Ocelot.Request.Request> _request; private OkResponse<Ocelot.Request.Request> _request;
private OkResponse<string> _downstreamUrl; private OkResponse<string> _downstreamUrl;
private OkResponse<DownstreamRoute> _downstreamRoute; private OkResponse<DownstreamRoute> _downstreamRoute;
public HttpRequestBuilderMiddlewareTests() public HttpRequestBuilderMiddlewareTests()
{ {
_url = "http://localhost:51879";
_qosProviderHouse = new Mock<IQosProviderHouse>(); _qosProviderHouse = new Mock<IQosProviderHouse>();
_requestBuilder = new Mock<IRequestCreator>(); _requestBuilder = new Mock<IRequestCreator>();
_scopedRepository = new Mock<IRequestScopedDataRepository>(); _scopedRepository = new Mock<IRequestScopedDataRepository>();
var builder = new WebHostBuilder()
.ConfigureServices(x =>
{
x.AddSingleton<IOcelotLoggerFactory, AspDotNetLoggerFactory>();
x.AddLogging();
x.AddSingleton(_qosProviderHouse.Object);
x.AddSingleton(_requestBuilder.Object);
x.AddSingleton(_scopedRepository.Object);
})
.UseUrls(_url)
.UseKestrel()
.UseContentRoot(Directory.GetCurrentDirectory())
.UseIISIntegration()
.UseUrls(_url)
.Configure(app =>
{
app.UseHttpRequestBuilderMiddleware();
});
_downstreamRequest = new HttpRequestMessage(); _downstreamRequest = new HttpRequestMessage();
@ -65,8 +39,7 @@ namespace Ocelot.UnitTests.Request
.Setup(sr => sr.Get<HttpRequestMessage>("DownstreamRequest")) .Setup(sr => sr.Get<HttpRequestMessage>("DownstreamRequest"))
.Returns(new OkResponse<HttpRequestMessage>(_downstreamRequest)); .Returns(new OkResponse<HttpRequestMessage>(_downstreamRequest));
_server = new TestServer(builder); GivenTheTestServerIsConfigured();
_client = _server.CreateClient();
} }
[Fact] [Fact]
@ -88,6 +61,28 @@ namespace Ocelot.UnitTests.Request
.BDDfy(); .BDDfy();
} }
protected override void GivenTheTestServerServicesAreConfigured(IServiceCollection services)
{
services.AddSingleton<IOcelotLoggerFactory, AspDotNetLoggerFactory>();
services.AddLogging();
services.AddSingleton(_qosProviderHouse.Object);
services.AddSingleton(_requestBuilder.Object);
services.AddSingleton(_scopedRepository.Object);
}
protected override void GivenTheTestServerPipelineIsConfigured(IApplicationBuilder app)
{
app.UseHttpRequestBuilderMiddleware();
}
private void GivenTheDownStreamUrlIs(string downstreamUrl)
{
_downstreamUrl = new OkResponse<string>(downstreamUrl);
_scopedRepository
.Setup(x => x.Get<string>(It.IsAny<string>()))
.Returns(_downstreamUrl);
}
private void GivenTheQosProviderHouseReturns(Response<IQoSProvider> qosProvider) private void GivenTheQosProviderHouseReturns(Response<IQoSProvider> qosProvider)
{ {
_qosProviderHouse _qosProviderHouse
@ -117,24 +112,5 @@ namespace Ocelot.UnitTests.Request
_scopedRepository _scopedRepository
.Verify(x => x.Add("Request", _request.Data), Times.Once()); .Verify(x => x.Add("Request", _request.Data), Times.Once());
} }
private void WhenICallTheMiddleware()
{
_result = _client.GetAsync(_url).Result;
}
private void GivenTheDownStreamUrlIs(string downstreamUrl)
{
_downstreamUrl = new OkResponse<string>(downstreamUrl);
_scopedRepository
.Setup(x => x.Get<string>(It.IsAny<string>()))
.Returns(_downstreamUrl);
}
public void Dispose()
{
_client.Dispose();
_server.Dispose();
}
} }
} }

View File

@ -1,18 +1,17 @@
using System; namespace Ocelot.UnitTests.RequestId
{
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO;
using System.Linq; using System.Linq;
using System.Net.Http; using System.Net.Http;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.TestHost;
using Microsoft.Extensions.DependencyInjection; 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.Infrastructure.RequestData;
using Ocelot.Logging; using Ocelot.Logging;
using Ocelot.RequestId.Middleware; using Ocelot.RequestId.Middleware;
using Ocelot.Responses; using Ocelot.Responses;
@ -20,55 +19,22 @@ using Shouldly;
using TestStack.BDDfy; using TestStack.BDDfy;
using Xunit; using Xunit;
namespace Ocelot.UnitTests.RequestId public class RequestIdMiddlewareTests : ServerHostedMiddlewareTest
{ {
public class RequestIdMiddlewareTests
{
private readonly Mock<IRequestScopedDataRepository> _scopedRepository;
private readonly HttpRequestMessage _downstreamRequest; private readonly HttpRequestMessage _downstreamRequest;
private readonly string _url;
private readonly TestServer _server;
private readonly HttpClient _client;
private Response<DownstreamRoute> _downstreamRoute; private Response<DownstreamRoute> _downstreamRoute;
private HttpResponseMessage _result;
private string _value; private string _value;
private string _key; private string _key;
public RequestIdMiddlewareTests() public RequestIdMiddlewareTests()
{ {
_url = "http://localhost:51879";
_scopedRepository = new Mock<IRequestScopedDataRepository>();
var builder = new WebHostBuilder()
.ConfigureServices(x =>
{
x.AddSingleton<IOcelotLoggerFactory, AspDotNetLoggerFactory>();
x.AddLogging();
x.AddSingleton(_scopedRepository.Object);
})
.UseUrls(_url)
.UseKestrel()
.UseContentRoot(Directory.GetCurrentDirectory())
.UseIISIntegration()
.UseUrls(_url)
.Configure(app =>
{
app.UseRequestIdMiddleware();
app.Run(x =>
{
x.Response.Headers.Add("LSRequestId", x.TraceIdentifier);
return Task.CompletedTask;
});
});
_server = new TestServer(builder);
_client = _server.CreateClient();
_downstreamRequest = new HttpRequestMessage(); _downstreamRequest = new HttpRequestMessage();
_scopedRepository ScopedRepository
.Setup(sr => sr.Get<HttpRequestMessage>("DownstreamRequest")) .Setup(sr => sr.Get<HttpRequestMessage>("DownstreamRequest"))
.Returns(new OkResponse<HttpRequestMessage>(_downstreamRequest)); .Returns(new OkResponse<HttpRequestMessage>(_downstreamRequest));
GivenTheTestServerIsConfigured();
} }
[Fact] [Fact]
@ -106,10 +72,28 @@ namespace Ocelot.UnitTests.RequestId
.BDDfy(); .BDDfy();
} }
protected override void GivenTheTestServerServicesAreConfigured(IServiceCollection services)
{
services.AddSingleton<IOcelotLoggerFactory, AspDotNetLoggerFactory>();
services.AddLogging();
services.AddSingleton(ScopedRepository.Object);
}
protected override void GivenTheTestServerPipelineIsConfigured(IApplicationBuilder app)
{
app.UseRequestIdMiddleware();
app.Run(x =>
{
x.Response.Headers.Add("LSRequestId", x.TraceIdentifier);
return Task.CompletedTask;
});
}
private void GivenTheDownStreamRouteIs(DownstreamRoute downstreamRoute) private void GivenTheDownStreamRouteIs(DownstreamRoute downstreamRoute)
{ {
_downstreamRoute = new OkResponse<DownstreamRoute>(downstreamRoute); _downstreamRoute = new OkResponse<DownstreamRoute>(downstreamRoute);
_scopedRepository ScopedRepository
.Setup(x => x.Get<DownstreamRoute>(It.IsAny<string>())) .Setup(x => x.Get<DownstreamRoute>(It.IsAny<string>()))
.Returns(_downstreamRoute); .Returns(_downstreamRoute);
} }
@ -118,28 +102,17 @@ namespace Ocelot.UnitTests.RequestId
{ {
_key = key; _key = key;
_value = value; _value = value;
_client.DefaultRequestHeaders.TryAddWithoutValidation(_key, _value); Client.DefaultRequestHeaders.TryAddWithoutValidation(_key, _value);
}
private void WhenICallTheMiddleware()
{
_result = _client.GetAsync(_url).Result;
} }
private void ThenTheTraceIdIsAnything() private void ThenTheTraceIdIsAnything()
{ {
_result.Headers.GetValues("LSRequestId").First().ShouldNotBeNullOrEmpty(); ResponseMessage.Headers.GetValues("LSRequestId").First().ShouldNotBeNullOrEmpty();
} }
private void ThenTheTraceIdIs(string expected) private void ThenTheTraceIdIs(string expected)
{ {
_result.Headers.GetValues("LSRequestId").First().ShouldBe(expected); ResponseMessage.Headers.GetValues("LSRequestId").First().ShouldBe(expected);
}
public void Dispose()
{
_client.Dispose();
_server.Dispose();
} }
} }
} }

View File

@ -1,62 +1,28 @@
using System; namespace Ocelot.UnitTests.Requester
using System.IO; {
using System.Net;
using System.Net.Http; using System.Net.Http;
using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.TestHost;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Moq; using Moq;
using Ocelot.Infrastructure.RequestData;
using Ocelot.Logging; using Ocelot.Logging;
using Ocelot.QueryStrings.Middleware;
using Ocelot.Requester; using Ocelot.Requester;
using Ocelot.Requester.Middleware; using Ocelot.Requester.Middleware;
using Ocelot.Requester.QoS; using Ocelot.Requester.QoS;
using Ocelot.Responder;
using Ocelot.Responses; using Ocelot.Responses;
using TestStack.BDDfy; using TestStack.BDDfy;
using Xunit; using Xunit;
namespace Ocelot.UnitTests.Requester public class HttpRequesterMiddlewareTests : ServerHostedMiddlewareTest
{
public class HttpRequesterMiddlewareTests : IDisposable
{ {
private readonly Mock<IHttpRequester> _requester; private readonly Mock<IHttpRequester> _requester;
private readonly Mock<IRequestScopedDataRepository> _scopedRepository;
private readonly string _url;
private readonly TestServer _server;
private readonly HttpClient _client;
private HttpResponseMessage _result;
private OkResponse<HttpResponseMessage> _response; private OkResponse<HttpResponseMessage> _response;
private OkResponse<Ocelot.Request.Request> _request; private OkResponse<Ocelot.Request.Request> _request;
public HttpRequesterMiddlewareTests() public HttpRequesterMiddlewareTests()
{ {
_url = "http://localhost:51879";
_requester = new Mock<IHttpRequester>(); _requester = new Mock<IHttpRequester>();
_scopedRepository = new Mock<IRequestScopedDataRepository>();
var builder = new WebHostBuilder()
.ConfigureServices(x =>
{
x.AddSingleton<IOcelotLoggerFactory, AspDotNetLoggerFactory>();
x.AddLogging();
x.AddSingleton(_requester.Object);
x.AddSingleton(_scopedRepository.Object);
})
.UseUrls(_url)
.UseKestrel()
.UseContentRoot(Directory.GetCurrentDirectory())
.UseIISIntegration()
.UseUrls(_url)
.Configure(app =>
{
app.UseHttpRequesterMiddleware();
});
_server = new TestServer(builder); GivenTheTestServerIsConfigured();
_client = _server.CreateClient();
} }
[Fact] [Fact]
@ -70,6 +36,27 @@ namespace Ocelot.UnitTests.Requester
.BDDfy(); .BDDfy();
} }
protected override void GivenTheTestServerServicesAreConfigured(IServiceCollection services)
{
services.AddSingleton<IOcelotLoggerFactory, AspDotNetLoggerFactory>();
services.AddLogging();
services.AddSingleton(_requester.Object);
services.AddSingleton(ScopedRepository.Object);
}
protected override void GivenTheTestServerPipelineIsConfigured(IApplicationBuilder app)
{
app.UseHttpRequesterMiddleware();
}
private void GivenTheRequestIs(Ocelot.Request.Request request)
{
_request = new OkResponse<Ocelot.Request.Request>(request);
ScopedRepository
.Setup(x => x.Get<Ocelot.Request.Request>(It.IsAny<string>()))
.Returns(_request);
}
private void GivenTheRequesterReturns(HttpResponseMessage response) private void GivenTheRequesterReturns(HttpResponseMessage response)
{ {
_response = new OkResponse<HttpResponseMessage>(response); _response = new OkResponse<HttpResponseMessage>(response);
@ -80,34 +67,15 @@ namespace Ocelot.UnitTests.Requester
private void GivenTheScopedRepoReturns() private void GivenTheScopedRepoReturns()
{ {
_scopedRepository ScopedRepository
.Setup(x => x.Add(It.IsAny<string>(), _response.Data)) .Setup(x => x.Add(It.IsAny<string>(), _response.Data))
.Returns(new OkResponse()); .Returns(new OkResponse());
} }
private void ThenTheScopedRepoIsCalledCorrectly() private void ThenTheScopedRepoIsCalledCorrectly()
{ {
_scopedRepository ScopedRepository
.Verify(x => x.Add("HttpResponseMessage", _response.Data), Times.Once()); .Verify(x => x.Add("HttpResponseMessage", _response.Data), Times.Once());
} }
private void WhenICallTheMiddleware()
{
_result = _client.GetAsync(_url).Result;
}
private void GivenTheRequestIs(Ocelot.Request.Request request)
{
_request = new OkResponse<Ocelot.Request.Request>(request);
_scopedRepository
.Setup(x => x.Get<Ocelot.Request.Request>(It.IsAny<string>()))
.Returns(_request);
}
public void Dispose()
{
_client.Dispose();
_server.Dispose();
}
} }
} }

View File

@ -0,0 +1,15 @@
using Ocelot.Errors;
namespace Ocelot.UnitTests.Responder
{
class AnyError : Error
{
public AnyError() : base("blahh", OcelotErrorCode.UnknownError)
{
}
public AnyError(OcelotErrorCode errorCode) : base("blah", errorCode)
{
}
}
}

View File

@ -1,8 +1,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Net;
using Ocelot.Errors; using Ocelot.Errors;
using Ocelot.Middleware;
using Ocelot.Requester;
using Ocelot.Responder; using Ocelot.Responder;
using Shouldly; using Shouldly;
using TestStack.BDDfy; using TestStack.BDDfy;
@ -21,47 +20,127 @@ namespace Ocelot.UnitTests.Responder
_codeMapper = new ErrorsToHttpStatusCodeMapper(); _codeMapper = new ErrorsToHttpStatusCodeMapper();
} }
[Fact] [Theory]
public void should_return_timeout() [InlineData(OcelotErrorCode.UnauthenticatedError)]
public void should_return_unauthorized(OcelotErrorCode errorCode)
{ {
this.Given(x => x.GivenThereAreErrors(new List<Error> ShouldMapErrorToStatusCode(errorCode, HttpStatusCode.Unauthorized);
}
[Theory]
[InlineData(OcelotErrorCode.CannotFindClaimError)]
[InlineData(OcelotErrorCode.ClaimValueNotAuthorisedError)]
[InlineData(OcelotErrorCode.ScopeNotAuthorisedError)]
[InlineData(OcelotErrorCode.UnauthorizedError)]
[InlineData(OcelotErrorCode.UserDoesNotHaveClaimError)]
public void should_return_forbidden(OcelotErrorCode errorCode)
{ {
new RequestTimedOutError(new Exception()) ShouldMapErrorToStatusCode(errorCode, HttpStatusCode.Forbidden);
})) }
.When(x => x.WhenIGetErrorStatusCode())
.Then(x => x.ThenTheResponseIsStatusCodeIs(503)) [Theory]
.BDDfy(); [InlineData(OcelotErrorCode.RequestTimedOutError)]
public void should_return_service_unavailable(OcelotErrorCode errorCode)
{
ShouldMapErrorToStatusCode(errorCode, HttpStatusCode.ServiceUnavailable);
}
[Theory]
[InlineData(OcelotErrorCode.CannotAddDataError)]
[InlineData(OcelotErrorCode.CannotFindDataError)]
[InlineData(OcelotErrorCode.DownstreamHostNullOrEmptyError)]
[InlineData(OcelotErrorCode.DownstreamPathNullOrEmptyError)]
[InlineData(OcelotErrorCode.DownstreampathTemplateAlreadyUsedError)]
[InlineData(OcelotErrorCode.DownstreamPathTemplateContainsSchemeError)]
[InlineData(OcelotErrorCode.DownstreamSchemeNullOrEmptyError)]
[InlineData(OcelotErrorCode.InstructionNotForClaimsError)]
[InlineData(OcelotErrorCode.NoInstructionsError)]
[InlineData(OcelotErrorCode.ParsingConfigurationHeaderError)]
[InlineData(OcelotErrorCode.RateLimitOptionsError)]
[InlineData(OcelotErrorCode.ServicesAreEmptyError)]
[InlineData(OcelotErrorCode.ServicesAreNullError)]
[InlineData(OcelotErrorCode.UnableToCompleteRequestError)]
[InlineData(OcelotErrorCode.UnableToCreateAuthenticationHandlerError)]
[InlineData(OcelotErrorCode.UnableToFindDownstreamRouteError)]
[InlineData(OcelotErrorCode.UnableToFindLoadBalancerError)]
[InlineData(OcelotErrorCode.UnableToFindServiceDiscoveryProviderError)]
[InlineData(OcelotErrorCode.UnableToFindQoSProviderError)]
[InlineData(OcelotErrorCode.UnableToSetConfigInConsulError)]
[InlineData(OcelotErrorCode.UnknownError)]
[InlineData(OcelotErrorCode.UnmappableRequestError)]
[InlineData(OcelotErrorCode.UnsupportedAuthenticationProviderError)]
public void should_return_not_found(OcelotErrorCode errorCode)
{
ShouldMapErrorToStatusCode(errorCode, HttpStatusCode.NotFound);
} }
[Fact] [Fact]
public void should_create_unauthenticated_response_code() public void AuthenticationErrorsHaveHighestPriority()
{ {
this.Given(x => x.GivenThereAreErrors(new List<Error> var errors = new List<OcelotErrorCode>
{ {
new UnauthenticatedError("no matter") OcelotErrorCode.CannotAddDataError,
})) OcelotErrorCode.CannotFindClaimError,
.When(x => x.WhenIGetErrorStatusCode()) OcelotErrorCode.UnauthenticatedError,
.Then(x => x.ThenTheResponseIsStatusCodeIs(401)) OcelotErrorCode.RequestTimedOutError,
.BDDfy(); };
ShouldMapErrorsToStatusCode(errors, HttpStatusCode.Unauthorized);
} }
[Fact] [Fact]
public void should_create_not_found_response_response_code() public void AuthorisationErrorsHaveSecondHighestPriority()
{ {
this.Given(x => x.GivenThereAreErrors(new List<Error> var errors = new List<OcelotErrorCode>
{ {
new AnyError() OcelotErrorCode.CannotAddDataError,
})) OcelotErrorCode.CannotFindClaimError,
.When(x => x.WhenIGetErrorStatusCode()) OcelotErrorCode.RequestTimedOutError
.Then(x => x.ThenTheResponseIsStatusCodeIs(404)) };
.BDDfy();
ShouldMapErrorsToStatusCode(errors, HttpStatusCode.Forbidden);
} }
class AnyError : Error [Fact]
public void ServiceUnavailableErrorsHaveThirdHighestPriority()
{ {
public AnyError() : base("blahh", OcelotErrorCode.UnknownError) var errors = new List<OcelotErrorCode>
{ {
OcelotErrorCode.CannotAddDataError,
OcelotErrorCode.RequestTimedOutError
};
ShouldMapErrorsToStatusCode(errors, HttpStatusCode.ServiceUnavailable);
} }
[Fact]
public void check_we_have_considered_all_errors_in_these_tests()
{
// If this test fails then it's because the number of error codes has changed.
// You should make the appropriate changes to the test cases here to ensure
// they cover all the error codes, and then modify this assertion.
Enum.GetNames(typeof(OcelotErrorCode)).Length.ShouldBe(30, "Looks like the number of error codes has changed. Do you need to modify ErrorsToHttpStatusCodeMapper?");
}
private void ShouldMapErrorToStatusCode(OcelotErrorCode errorCode, HttpStatusCode expectedHttpStatusCode)
{
ShouldMapErrorsToStatusCode(new List<OcelotErrorCode> { errorCode }, expectedHttpStatusCode);
}
private void ShouldMapErrorsToStatusCode(List<OcelotErrorCode> errorCodes, HttpStatusCode expectedHttpStatusCode)
{
var errors = new List<Error>();
foreach(var errorCode in errorCodes)
{
errors.Add(new AnyError(errorCode));
}
this.Given(x => x.GivenThereAreErrors(errors))
.When(x => x.WhenIGetErrorStatusCode())
.Then(x => x.ThenTheResponseIsStatusCodeIs(expectedHttpStatusCode))
.BDDfy();
} }
private void GivenThereAreErrors(List<Error> errors) private void GivenThereAreErrors(List<Error> errors)
@ -78,5 +157,10 @@ namespace Ocelot.UnitTests.Responder
{ {
_result.ShouldBe(expectedCode); _result.ShouldBe(expectedCode);
} }
private void ThenTheResponseIsStatusCodeIs(HttpStatusCode expectedCode)
{
_result.ShouldBe((int)expectedCode);
}
} }
} }

View File

@ -1,13 +1,9 @@
using System; namespace Ocelot.UnitTests.Responder
using System.IO; {
using System.Net.Http; using System.Net.Http;
using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.TestHost;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Moq; using Moq;
using Ocelot.Infrastructure.RequestData;
using Ocelot.Logging; using Ocelot.Logging;
using Ocelot.Responder; using Ocelot.Responder;
using Ocelot.Responder.Middleware; using Ocelot.Responder.Middleware;
@ -15,46 +11,18 @@ using Ocelot.Responses;
using TestStack.BDDfy; using TestStack.BDDfy;
using Xunit; using Xunit;
namespace Ocelot.UnitTests.Responder public class ResponderMiddlewareTests : ServerHostedMiddlewareTest
{
public class ResponderMiddlewareTests : IDisposable
{ {
private readonly Mock<IHttpResponder> _responder; private readonly Mock<IHttpResponder> _responder;
private readonly Mock<IRequestScopedDataRepository> _scopedRepository;
private readonly Mock<IErrorsToHttpStatusCodeMapper> _codeMapper; private readonly Mock<IErrorsToHttpStatusCodeMapper> _codeMapper;
private readonly string _url;
private readonly TestServer _server;
private readonly HttpClient _client;
private HttpResponseMessage _result;
private OkResponse<HttpResponseMessage> _response; private OkResponse<HttpResponseMessage> _response;
public ResponderMiddlewareTests() public ResponderMiddlewareTests()
{ {
_url = "http://localhost:51879";
_responder = new Mock<IHttpResponder>(); _responder = new Mock<IHttpResponder>();
_scopedRepository = new Mock<IRequestScopedDataRepository>();
_codeMapper = new Mock<IErrorsToHttpStatusCodeMapper>(); _codeMapper = new Mock<IErrorsToHttpStatusCodeMapper>();
var builder = new WebHostBuilder()
.ConfigureServices(x =>
{
x.AddSingleton<IOcelotLoggerFactory, AspDotNetLoggerFactory>();
x.AddLogging();
x.AddSingleton(_codeMapper.Object);
x.AddSingleton(_responder.Object);
x.AddSingleton(_scopedRepository.Object);
})
.UseUrls(_url)
.UseKestrel()
.UseContentRoot(Directory.GetCurrentDirectory())
.UseIISIntegration()
.UseUrls(_url)
.Configure(app =>
{
app.UseResponderMiddleware();
});
_server = new TestServer(builder); GivenTheTestServerIsConfigured();
_client = _server.CreateClient();
} }
[Fact] [Fact]
@ -67,9 +35,31 @@ namespace Ocelot.UnitTests.Responder
.BDDfy(); .BDDfy();
} }
protected override void GivenTheTestServerServicesAreConfigured(IServiceCollection services)
{
services.AddSingleton<IOcelotLoggerFactory, AspDotNetLoggerFactory>();
services.AddLogging();
services.AddSingleton(_codeMapper.Object);
services.AddSingleton(_responder.Object);
services.AddSingleton(ScopedRepository.Object);
}
protected override void GivenTheTestServerPipelineIsConfigured(IApplicationBuilder app)
{
app.UseResponderMiddleware();
}
private void GivenTheHttpResponseMessageIs(HttpResponseMessage response)
{
_response = new OkResponse<HttpResponseMessage>(response);
ScopedRepository
.Setup(x => x.Get<HttpResponseMessage>(It.IsAny<string>()))
.Returns(_response);
}
private void GivenThereAreNoPipelineErrors() private void GivenThereAreNoPipelineErrors()
{ {
_scopedRepository ScopedRepository
.Setup(x => x.Get<bool>(It.IsAny<string>())) .Setup(x => x.Get<bool>(It.IsAny<string>()))
.Returns(new OkResponse<bool>(false)); .Returns(new OkResponse<bool>(false));
} }
@ -78,24 +68,5 @@ namespace Ocelot.UnitTests.Responder
{ {
//todo a better assert? //todo a better assert?
} }
private void WhenICallTheMiddleware()
{
_result = _client.GetAsync(_url).Result;
}
private void GivenTheHttpResponseMessageIs(HttpResponseMessage response)
{
_response = new OkResponse<HttpResponseMessage>(response);
_scopedRepository
.Setup(x => x.Get<HttpResponseMessage>(It.IsAny<string>()))
.Returns(_response);
}
public void Dispose()
{
_client.Dispose();
_server.Dispose();
}
} }
} }

View File

@ -0,0 +1,62 @@
namespace Ocelot.UnitTests
{
using System;
using System.IO;
using System.Net.Http;
using Microsoft.AspNetCore.TestHost;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.AspNetCore.Builder;
using Moq;
using Ocelot.Infrastructure.RequestData;
public abstract class ServerHostedMiddlewareTest : IDisposable
{
protected TestServer Server { get; private set; }
protected HttpClient Client { get; private set; }
protected string Url { get; private set; }
protected HttpResponseMessage ResponseMessage { get; private set; }
protected Mock<IRequestScopedDataRepository> ScopedRepository { get; private set; }
public ServerHostedMiddlewareTest()
{
Url = "http://localhost:51879";
ScopedRepository = new Mock<IRequestScopedDataRepository>();
}
protected virtual void GivenTheTestServerIsConfigured()
{
var builder = new WebHostBuilder()
.ConfigureServices(x => GivenTheTestServerServicesAreConfigured(x))
.UseUrls(Url)
.UseKestrel()
.UseContentRoot(Directory.GetCurrentDirectory())
.UseIISIntegration()
.Configure(app => GivenTheTestServerPipelineIsConfigured(app));
Server = new TestServer(builder);
Client = Server.CreateClient();
}
protected virtual void GivenTheTestServerServicesAreConfigured(IServiceCollection services)
{
// override this in your test fixture to set up service dependencies
}
protected virtual void GivenTheTestServerPipelineIsConfigured(IApplicationBuilder app)
{
// override this in your test fixture to set up the test server pipeline
}
protected void WhenICallTheMiddleware()
{
ResponseMessage = Client.GetAsync(Url).Result;
}
public void Dispose()
{
Client.Dispose();
Server.Dispose();
}
}
}

View File

@ -28,6 +28,7 @@ namespace Ocelot.UnitTests.ServiceDiscovery
.BDDfy(); .BDDfy();
} }
[Fact]
public void should_lookup_service() public void should_lookup_service()
{ {
this.Given(x => x.GivenAServiceIsRegistered("product", "localhost:600", 80)) this.Given(x => x.GivenAServiceIsRegistered("product", "localhost:600", 80))

View File

@ -0,0 +1,89 @@
namespace Ocelot.UnitTests.TestData
{
using System.Collections.Generic;
using Ocelot.Configuration.Builder;
using Ocelot.Configuration.File;
public class AuthenticationConfigTestData
{
public static IEnumerable<object[]> GetAuthenticationData()
{
yield return new object[]
{
"IdentityServer",
new IdentityServerConfigBuilder()
.WithRequireHttps(true)
.WithApiName("test")
.WithApiSecret("test")
.WithProviderRootUrl("test")
.Build(),
new FileConfiguration
{
ReRoutes = new List<FileReRoute>
{
new FileReRoute
{
UpstreamPathTemplate = "/api/products/{productId}",
DownstreamPathTemplate = "/products/{productId}",
UpstreamHttpMethod = new List<string> { "Get" },
ReRouteIsCaseSensitive = true,
AuthenticationOptions = new FileAuthenticationOptions
{
AllowedScopes = new List<string>(),
Provider = "IdentityServer",
IdentityServerConfig = new FileIdentityServerConfig
{
ProviderRootUrl = "http://localhost:51888",
RequireHttps = false,
ApiName = "api",
ApiSecret = "secret"
}
},
AddHeadersToRequest =
{
{ "CustomerId", "Claims[CustomerId] > value" },
}
}
}
}
};
yield return new object[]
{
"Jwt",
new JwtConfigBuilder()
.WithAudience("a")
.WithAuthority("au")
.Build(),
new FileConfiguration
{
ReRoutes = new List<FileReRoute>
{
new FileReRoute
{
UpstreamPathTemplate = "/api/products/{productId}",
DownstreamPathTemplate = "/products/{productId}",
UpstreamHttpMethod = new List<string> { "Get" },
ReRouteIsCaseSensitive = true,
AuthenticationOptions = new FileAuthenticationOptions
{
AllowedScopes = new List<string>(),
Provider = "IdentityServer",
JwtConfig = new FileJwtConfig
{
Audience = "a",
Authority = "au"
}
},
AddHeadersToRequest =
{
{ "CustomerId", "Claims[CustomerId] > value" },
}
}
}
}
};
}
}
}