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
*.dbproj.schemaview
*.pfx
!idsrv3test.pfx
*.publishsettings
node_modules/
orleans.codegen.cs

View File

@ -16,7 +16,7 @@ var artifactsDir = Directory("artifacts");
// unit testing
var artifactsForUnitTestsDir = artifactsDir + Directory("UnitTests");
var unitTestAssemblies = @"./test/Ocelot.UnitTests/Ocelot.UnitTests.csproj";
var minCodeCoverage = 75d;
var minCodeCoverage = 76.4d;
var coverallsRepoToken = "coveralls-repo-token-ocelot";
var coverallsRepo = "https://coveralls.io/github/TomPallister/Ocelot";
@ -229,11 +229,11 @@ Task("CreatePackages")
EnsureDirectoryExists(packagesDir);
CopyFiles("./src/**/Ocelot.*.nupkg", packagesDir);
GenerateReleaseNotes(releaseNotesFile);
//GenerateReleaseNotes(releaseNotesFile);
System.IO.File.WriteAllLines(artifactsFile, new[]{
"nuget:Ocelot." + buildVersion + ".nupkg",
"releaseNotes:releasenotes.md"
//"releaseNotes:releasenotes.md"
});
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 Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
@ -5,6 +6,8 @@ using Ocelot.Responses;
namespace Ocelot.Authentication.Handler.Creator
{
using Ocelot.Configuration;
using AuthenticationOptions = Configuration.AuthenticationOptions;
/// <summary>
@ -16,15 +19,31 @@ namespace Ocelot.Authentication.Handler.Creator
{
var builder = app.New();
builder.UseIdentityServerAuthentication(new IdentityServerAuthenticationOptions
if (authOptions.Provider.ToLower() == "jwt")
{
Authority = authOptions.ProviderRootUrl,
ApiName = authOptions.ApiName,
RequireHttpsMetadata = authOptions.RequireHttps,
AllowedScopes = authOptions.AllowedScopes,
SupportedTokens = SupportedTokens.Both,
ApiSecret = authOptions.ApiSecret
});
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
{
Authority = authenticationConfig.ProviderRootUrl,
ApiName = authenticationConfig.ApiName,
RequireHttpsMetadata = authenticationConfig.RequireHttps,
AllowedScopes = authOptions.AllowedScopes,
SupportedTokens = SupportedTokens.Both,
ApiSecret = authenticationConfig.ApiSecret
});
}
var authenticationNext = builder.Build();

View File

@ -2,6 +2,7 @@
{
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)
{
_logger.TraceMiddlewareEntry();
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);
@ -46,7 +44,6 @@ namespace Ocelot.Authentication.Middleware
{
_logger.LogError($"Error getting authentication handler for {context.Request.Path}. {authenticationHandler.Errors.ToErrorString()}");
SetPipelineError(authenticationHandler.Errors);
_logger.TraceMiddlewareCompleted();
return;
}
@ -56,11 +53,7 @@ namespace Ocelot.Authentication.Middleware
if (context.User.Identity.IsAuthenticated)
{
_logger.LogDebug($"Client has been authenticated for {context.Request.Path}");
_logger.TraceInvokeNext();
await _next.Invoke(context);
_logger.TraceInvokeNextCompleted();
_logger.TraceMiddlewareCompleted();
await _next.Invoke(context);
}
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()}");
SetPipelineError(error);
_logger.TraceMiddlewareCompleted();
return;
}
}
@ -81,10 +72,7 @@ namespace Ocelot.Authentication.Middleware
{
_logger.LogTrace($"No authentication needed for {context.Request.Path}");
_logger.TraceInvokeNext();
await _next.Invoke(context);
_logger.TraceInvokeNextCompleted();
_logger.TraceMiddlewareCompleted();
await _next.Invoke(context);
}
}

View File

@ -33,8 +33,6 @@ namespace Ocelot.Authorisation.Middleware
public async Task Invoke(HttpContext context)
{
_logger.LogDebug("started authorisation");
if (IsAuthenticatedRoute(DownstreamRoute.ReRoute))
{
_logger.LogDebug("route is authenticated scopes must be checked");
@ -73,7 +71,7 @@ namespace Ocelot.Authorisation.Middleware
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);
return;
@ -81,30 +79,23 @@ namespace Ocelot.Authorisation.Middleware
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);
_logger.LogDebug("succesfully called next middleware");
}
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>
{
new UnauthorisedError(
$"{context.User.Identity.Name} unable to access {DownstreamRoute.ReRoute.UpstreamPathTemplate.Value}")
new UnauthorisedError($"{context.User.Identity.Name} is not authorised to access {DownstreamRoute.ReRoute.UpstreamPathTemplate.Value}")
});
}
}
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);
_logger.LogDebug("succesfully called next middleware");
}
}

View File

@ -1,11 +1,13 @@
using System;
using System.Collections.Generic;
namespace Ocelot.Cache
{
public interface IOcelotCache<T>
{
void Add(string key, T value, TimeSpan ttl);
void AddAndDelete(string key, T value, TimeSpan ttl);
T Get(string key);
void Add(string key, T value, TimeSpan ttl, string region);
void AddAndDelete(string key, T value, TimeSpan ttl, string region);
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.Linq;
using System.Net.Http;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
@ -13,16 +14,19 @@ namespace Ocelot.Cache.Middleware
private readonly RequestDelegate _next;
private readonly IOcelotLogger _logger;
private readonly IOcelotCache<HttpResponseMessage> _outputCache;
private readonly IRegionCreator _regionCreator;
public OutputCacheMiddleware(RequestDelegate next,
IOcelotLoggerFactory loggerFactory,
IRequestScopedDataRepository scopedDataRepository,
IOcelotCache<HttpResponseMessage> outputCache)
IOcelotCache<HttpResponseMessage> outputCache,
IRegionCreator regionCreator)
:base(scopedDataRepository)
{
_next = next;
_outputCache = outputCache;
_logger = loggerFactory.CreateLogger<OutputCacheMiddleware>();
_regionCreator = regionCreator;
}
public async Task Invoke(HttpContext context)
@ -33,11 +37,11 @@ namespace Ocelot.Cache.Middleware
return;
}
var downstreamUrlKey = DownstreamRequest.RequestUri.OriginalString;
var downstreamUrlKey = $"{DownstreamRequest.Method.Method}-{DownstreamRequest.RequestUri.OriginalString}";
_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)
{
@ -54,8 +58,6 @@ namespace Ocelot.Cache.Middleware
await _next.Invoke(context);
_logger.LogDebug("succesfully called next middleware");
if (PipelineError)
{
_logger.LogDebug("there was a pipeline error for {downstreamUrlKey}", downstreamUrlKey);
@ -65,7 +67,7 @@ namespace Ocelot.Cache.Middleware
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);
}

View File

@ -1,4 +1,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
using CacheManager.Core;
namespace Ocelot.Cache
@ -12,12 +14,12 @@ namespace Ocelot.Cache
_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);
@ -26,12 +28,17 @@ namespace Ocelot.Cache
_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)
{
_logger.LogDebug("started claims middleware");
if (DownstreamRoute.ReRoute.ClaimsToClaims.Any())
{
_logger.LogDebug("this route has instructions to convert claims to other claims");
@ -42,12 +40,7 @@ namespace Ocelot.Claims.Middleware
return;
}
}
_logger.LogDebug("calling next middleware");
await _next.Invoke(context);
_logger.LogDebug("succesfully called next middleware");
}
}
}

View File

@ -2,24 +2,52 @@
namespace Ocelot.Configuration
{
using Newtonsoft.Json;
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;
ProviderRootUrl = providerRootUrl;
ApiName = apiName;
RequireHttps = requireHttps;
AllowedScopes = allowedScopes;
ApiSecret = apiSecret;
AllowedScopes = allowedScopes;
Config = config;
}
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 ApiName { get; private set; }
public string ApiSecret { 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 _providerRootUrl;
private string _apiName;
private string _apiSecret;
private bool _requireHttps;
private List<string> _allowedScopes;
private IAuthenticationConfig _identityServerConfig;
public AuthenticationOptionsBuilder WithProvider(string provider)
{
_provider = provider;
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)
{
_allowedScopes = allowedScopes;
return this;
}
public AuthenticationOptionsBuilder WithConfig(IAuthenticationConfig config)
{
_identityServerConfig = config;
return this;
}
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 CacheOptions(int ttlSeconds)
public CacheOptions(int ttlSeconds, string region)
{
TtlSeconds = ttlSeconds;
Region = region;
}
public int TtlSeconds { get; private set; }
public string Region {get;private set;}
}
}

View File

@ -1,20 +1,27 @@
using Ocelot.Configuration.Builder;
using Ocelot.Configuration.File;
using Ocelot.Creator.Configuration;
namespace Ocelot.Configuration.Creator
{
public class AuthenticationOptionsCreator : IAuthenticationOptionsCreator
{
private readonly IAuthenticationProviderConfigCreator _creator;
public AuthenticationOptionsCreator(IAuthenticationProviderConfigCreator creator)
{
_creator = creator;
}
public AuthenticationOptions Create(FileReRoute fileReRoute)
{
var authenticationConfig = _creator.Create(fileReRoute.AuthenticationOptions);
return new AuthenticationOptionsBuilder()
.WithProvider(fileReRoute.AuthenticationOptions?.Provider)
.WithProviderRootUrl(fileReRoute.AuthenticationOptions?.ProviderRootUrl)
.WithApiName(fileReRoute.AuthenticationOptions?.ApiName)
.WithRequireHttps(fileReRoute.AuthenticationOptions.RequireHttps)
.WithAllowedScopes(fileReRoute.AuthenticationOptions?.AllowedScopes)
.WithApiSecret(fileReRoute.AuthenticationOptions?.ApiSecret)
.Build();
}
.WithProvider(fileReRoute.AuthenticationOptions?.Provider)
.WithAllowedScopes(fileReRoute.AuthenticationOptions?.AllowedScopes)
.WithConfig(authenticationConfig)
.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 Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Ocelot.Cache;
using Ocelot.Configuration.Builder;
using Ocelot.Configuration.File;
using Ocelot.Configuration.Parser;
@ -30,12 +31,13 @@ namespace Ocelot.Configuration.Creator
private readonly IQosProviderHouse _qosProviderHouse;
private readonly IClaimsToThingCreator _claimsToThingCreator;
private readonly IAuthenticationOptionsCreator _authOptionsCreator;
private IUpstreamTemplatePatternCreator _upstreamTemplatePatternCreator;
private IRequestIdKeyCreator _requestIdKeyCreator;
private IServiceProviderConfigurationCreator _serviceProviderConfigCreator;
private IQoSOptionsCreator _qosOptionsCreator;
private IReRouteOptionsCreator _fileReRouteOptionsCreator;
private IRateLimitOptionsCreator _rateLimitOptionsCreator;
private readonly IUpstreamTemplatePatternCreator _upstreamTemplatePatternCreator;
private readonly IRequestIdKeyCreator _requestIdKeyCreator;
private readonly IServiceProviderConfigurationCreator _serviceProviderConfigCreator;
private readonly IQoSOptionsCreator _qosOptionsCreator;
private readonly IReRouteOptionsCreator _fileReRouteOptionsCreator;
private readonly IRateLimitOptionsCreator _rateLimitOptionsCreator;
private readonly IRegionCreator _regionCreator;
public FileOcelotConfigurationCreator(
IOptions<FileConfiguration> options,
@ -52,9 +54,11 @@ namespace Ocelot.Configuration.Creator
IServiceProviderConfigurationCreator serviceProviderConfigCreator,
IQoSOptionsCreator qosOptionsCreator,
IReRouteOptionsCreator fileReRouteOptionsCreator,
IRateLimitOptionsCreator rateLimitOptionsCreator
IRateLimitOptionsCreator rateLimitOptionsCreator,
IRegionCreator regionCreator
)
{
_regionCreator = regionCreator;
_rateLimitOptionsCreator = rateLimitOptionsCreator;
_requestIdKeyCreator = requestIdKeyCreator;
_upstreamTemplatePatternCreator = upstreamTemplatePatternCreator;
@ -137,6 +141,8 @@ namespace Ocelot.Configuration.Creator
var rateLimitOption = _rateLimitOptionsCreator.Create(fileReRoute, globalConfiguration, fileReRouteOptions.EnableRateLimiting);
var region = _regionCreator.Create(fileReRoute);
var reRoute = new ReRouteBuilder()
.WithDownstreamPathTemplate(fileReRoute.DownstreamPathTemplate)
.WithUpstreamPathTemplate(fileReRoute.UpstreamPathTemplate)
@ -151,7 +157,7 @@ namespace Ocelot.Configuration.Creator
.WithClaimsToQueries(claimsToQueries)
.WithRequestIdKey(requestIdKey)
.WithIsCached(fileReRouteOptions.IsCached)
.WithCacheOptions(new CacheOptions(fileReRoute.FileCacheOptions.TtlSeconds))
.WithCacheOptions(new CacheOptions(fileReRoute.FileCacheOptions.TtlSeconds, region))
.WithDownstreamScheme(fileReRoute.DownstreamScheme)
.WithLoadBalancer(fileReRoute.LoadBalancer)
.WithDownstreamHost(fileReRoute.DownstreamHost)

View File

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

View File

@ -6,14 +6,14 @@ namespace Ocelot.Configuration.File
{
public FileAuthenticationOptions()
{
AllowedScopes = new List<string>();
AllowedScopes = new List<string>();
IdentityServerConfig = new FileIdentityServerConfig();
JwtConfig = new FileJwtConfig();
}
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 string ApiSecret { get; set; }
public FileIdentityServerConfig IdentityServerConfig { get; set; }
public FileJwtConfig JwtConfig { get; set; }
}
}

View File

@ -3,5 +3,6 @@
public class FileCacheOptions
{
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;}
bool RequireClientSecret {get;}
List<User> Users {get;}
string CredentialsSigningCertificateLocation { get; }
string CredentialsSigningCertificatePassword { get; }
}
}

View File

@ -17,7 +17,7 @@ namespace Ocelot.Configuration.Provider
IEnumerable<string> grantType,
AccessTokenType accessTokenType,
bool requireClientSecret,
List<User> users)
List<User> users, string credentialsSigningCertificateLocation, string credentialsSigningCertificatePassword)
{
ApiName = apiName;
RequireHttps = requireHttps;
@ -30,6 +30,8 @@ namespace Ocelot.Configuration.Provider
AccessTokenType = accessTokenType;
RequireClientSecret = requireClientSecret;
Users = users;
CredentialsSigningCertificateLocation = credentialsSigningCertificateLocation;
CredentialsSigningCertificatePassword = credentialsSigningCertificatePassword;
}
public string ApiName { get; private set; }
@ -43,5 +45,7 @@ namespace Ocelot.Configuration.Provider
public AccessTokenType AccessTokenType {get;private set;}
public bool RequireClientSecret {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;
RequestIdKey = requestIdKey;
IsCached = isCached;
FileCacheOptions = fileCacheOptions;
CacheOptions = fileCacheOptions;
ClaimsToQueries = claimsToQueries
?? new List<ClaimToThing>();
ClaimsToClaims = claimsToClaims
@ -74,7 +74,7 @@ namespace Ocelot.Configuration
public Dictionary<string, string> RouteClaimsRequirement { get; private set; }
public string RequestIdKey { 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 bool IsQos { get; private set; }
public QoSOptions QosOptionsOptions { get; private set; }

View File

@ -4,6 +4,7 @@ using System.Threading.Tasks;
using Consul;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Ocelot.Authentication.JsonConverters;
using Ocelot.Responses;
using Ocelot.ServiceDiscovery;
@ -30,7 +31,7 @@ namespace Ocelot.Configuration.Repository
public async Task<Response<IOcelotConfiguration>> Get()
{
var config = _cache.Get(_ocelotConfiguration);
var config = _cache.Get(_ocelotConfiguration, _ocelotConfiguration);
if (config != null)
{
@ -48,7 +49,9 @@ namespace Ocelot.Configuration.Repository
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);
}
@ -68,7 +71,7 @@ namespace Ocelot.Configuration.Repository
if (result.Response)
{
_cache.AddAndDelete(_ocelotConfiguration, ocelotConfiguration, TimeSpan.FromSeconds(3));
_cache.AddAndDelete(_ocelotConfiguration, ocelotConfiguration, TimeSpan.FromSeconds(3), _ocelotConfiguration);
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.Net.Http;
using System.Reflection;
using System.Security.Cryptography.X509Certificates;
using Microsoft.IdentityModel.Tokens;
using Ocelot.Configuration;
using Ocelot.Creator.Configuration;
using FileConfigurationProvider = Ocelot.Configuration.Provider.FileConfigurationProvider;
namespace Ocelot.DependencyInjection
@ -69,6 +72,7 @@ namespace Ocelot.DependencyInjection
services.Configure<FileConfiguration>(configurationRoot);
services.TryAddSingleton<IOcelotConfigurationCreator, FileOcelotConfigurationCreator>();
services.TryAddSingleton<IAuthenticationProviderConfigCreator, AuthenticationProviderConfigCreator>();
services.TryAddSingleton<IOcelotConfigurationRepository, InMemoryOcelotConfigurationRepository>();
services.TryAddSingleton<IConfigurationValidator, FileConfigurationValidator>();
services.TryAddSingleton<IBaseUrlFinder, BaseUrlFinder>();
@ -87,8 +91,10 @@ namespace Ocelot.DependencyInjection
{
services.TryAddSingleton<IIdentityServerConfiguration>(identityServerConfiguration);
services.TryAddSingleton<IHashMatcher, HashMatcher>();
services.AddIdentityServer()
.AddTemporarySigningCredential()
var identityServerBuilder = services
.AddIdentityServer(options => {
options.IssuerUri = "Ocelot";
})
.AddInMemoryApiResources(new List<ApiResource>
{
new ApiResource
@ -120,6 +126,16 @@ namespace Ocelot.DependencyInjection
RequireClientSecret = identityServerConfiguration.RequireClientSecret
}
}).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;
@ -131,6 +147,7 @@ namespace Ocelot.DependencyInjection
.AddJsonFormatters();
services.AddLogging();
services.TryAddSingleton<IRegionCreator, RegionCreator>();
services.TryAddSingleton<IFileConfigurationRepository, FileConfigurationRepository>();
services.TryAddSingleton<IFileConfigurationSetter, FileConfigurationSetter>();
services.TryAddSingleton<IFileConfigurationProvider, FileConfigurationProvider>();
@ -169,6 +186,11 @@ namespace Ocelot.DependencyInjection
services.TryAddSingleton<IHttpContextAccessor, HttpContextAccessor>();
services.TryAddScoped<IRequestScopedDataRepository, HttpDataRepository>();
services.AddMemoryCache();
//Used to log the the start and ending of middleware
services.TryAddSingleton<OcelotDiagnosticListener>();
services.AddMiddlewareAnalysis();
return services;
}
}

View File

@ -30,8 +30,6 @@ namespace Ocelot.DownstreamRouteFinder.Middleware
public async Task Invoke(HttpContext context)
{
_logger.TraceMiddlewareEntry();
var upstreamUrlPath = context.Request.Path.ToString().SetLastCharacterAs('/');
_logger.LogDebug("upstream url path is {upstreamUrlPath}", upstreamUrlPath);
@ -40,11 +38,9 @@ namespace Ocelot.DownstreamRouteFinder.Middleware
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);
_logger.TraceMiddlewareCompleted();
return;
}
@ -52,12 +48,7 @@ namespace Ocelot.DownstreamRouteFinder.Middleware
SetDownstreamRouteForThisRequest(downstreamRoute.Data);
_logger.TraceInvokeNext();
await _next.Invoke(context);
_logger.TraceInvokeNextCompleted();
_logger.TraceMiddlewareCompleted();
}
}
}

View File

@ -30,8 +30,6 @@ namespace Ocelot.DownstreamUrlCreator.Middleware
public async Task Invoke(HttpContext context)
{
_logger.LogDebug("started calling downstream url creator middleware");
var dsPath = _replacer
.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("calling next middleware");
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)
{
_logger.LogDebug("started calling headers builder middleware");
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);
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);
return;
@ -45,11 +43,7 @@ namespace Ocelot.Headers.Middleware
_logger.LogDebug("headers have been set on context");
}
_logger.LogDebug("calling next middleware");
await _next.Invoke(context);
_logger.LogDebug("succesfully called next middleware");
}
}
}

View File

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

View File

@ -4,39 +4,39 @@ using Ocelot.Infrastructure.RequestData;
namespace Ocelot.Logging
{
public class AspDotNetLogger : IOcelotLogger
{
private readonly ILogger _logger;
private readonly IRequestScopedDataRepository _scopedDataRepository;
public string Name { get; }
public class AspDotNetLogger : IOcelotLogger
{
private readonly ILogger _logger;
private readonly IRequestScopedDataRepository _scopedDataRepository;
public string Name { get; }
public AspDotNetLogger(ILogger logger, IRequestScopedDataRepository scopedDataRepository, string typeName)
{
Name = typeName;
_logger = logger;
_scopedDataRepository = scopedDataRepository;
}
public void LogTrace(string message, params object[] args)
{
_logger.LogTrace(GetMessageWithOcelotRequestId(message), args);
}
public void LogDebug(string message, params object[] args)
{
_logger.LogDebug(GetMessageWithOcelotRequestId(message), args);
}
public void LogError(string message, Exception exception)
{
_logger.LogError(GetMessageWithOcelotRequestId(message), exception);
}
public void LogError(string message, params object[] args)
{
_logger.LogError(GetMessageWithOcelotRequestId(message), args);
}
Name = typeName;
_logger = logger;
_scopedDataRepository = scopedDataRepository;
}
public void LogTrace(string message, params object[] args)
{
_logger.LogTrace(GetMessageWithOcelotRequestId(message), args);
}
public void LogDebug(string message, params object[] args)
{
_logger.LogDebug(GetMessageWithOcelotRequestId(message), args);
}
public void LogError(string message, Exception exception)
{
_logger.LogError(GetMessageWithOcelotRequestId(message), exception);
}
public void LogError(string message, params object[] args)
{
_logger.LogError(GetMessageWithOcelotRequestId(message), args);
}
private string GetMessageWithOcelotRequestId(string message)
{
var requestId = _scopedDataRepository.Get<string>("RequestId");

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)
{
_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;

View File

@ -1,4 +1,5 @@
using System.Collections.Generic;
using System.Diagnostics;
using IdentityServer4.AccessTokenValidation;
using Microsoft.AspNetCore.Builder;
using Ocelot.Authentication.Middleware;
@ -8,6 +9,7 @@ using Ocelot.DownstreamRouteFinder.Middleware;
using Ocelot.DownstreamUrlCreator.Middleware;
using Ocelot.Errors.Middleware;
using Ocelot.Headers.Middleware;
using Ocelot.Logging;
using Ocelot.QueryStrings.Middleware;
using Ocelot.Request.Middleware;
using Ocelot.Requester.Middleware;
@ -53,6 +55,8 @@ namespace Ocelot.Middleware
{
await CreateAdministrationArea(builder);
ConfigureDiagnosticListener(builder);
// This is registered to catch any global exceptions that are not handled
builder.UseExceptionHandlerMiddleware();
@ -181,7 +185,6 @@ namespace Ocelot.Middleware
builder.Map(configuration.AdministrationPath, app =>
{
var identityServerUrl = $"{baseSchemeUrlAndPort}/{configuration.AdministrationPath.Remove(0,1)}";
app.UseIdentityServerAuthentication(new IdentityServerAuthenticationOptions
{
Authority = identityServerUrl,
@ -206,5 +209,22 @@ namespace Ocelot.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>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.MiddlewareAnalysis" 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.FileExtensions" 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.Console" 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)
{
_logger.LogDebug("started calling query string builder middleware");
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);
@ -43,11 +41,7 @@ namespace Ocelot.QueryStrings.Middleware
}
}
_logger.LogDebug("calling next middleware");
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)
{
_logger.TraceMiddlewareEntry();
var options = DownstreamRoute.ReRoute.RateLimitOptions;
// check if rate limiting is enabled
if (!DownstreamRoute.ReRoute.EnableEndpointEndpointRateLimiting)
{
_logger.LogDebug($"EndpointRateLimiting is not enabled for {DownstreamRoute.ReRoute.DownstreamPathTemplate}");
_logger.TraceInvokeNext();
await _next.Invoke(context);
_logger.TraceInvokeNextCompleted();
_logger.TraceMiddlewareCompleted();
return;
}
// compute identity from request
@ -52,11 +46,7 @@ namespace Ocelot.RateLimit.Middleware
if (IsWhitelisted(identity, options))
{
_logger.LogDebug($"{DownstreamRoute.ReRoute.DownstreamPathTemplate} is white listed from rate limiting");
_logger.TraceInvokeNext();
await _next.Invoke(context);
_logger.TraceInvokeNextCompleted();
_logger.TraceMiddlewareCompleted();
return;
}
@ -78,7 +68,6 @@ namespace Ocelot.RateLimit.Middleware
var retrystring = retryAfter.ToString(System.Globalization.CultureInfo.InvariantCulture);
// break execution
await ReturnQuotaExceededResponse(context, options, retrystring);
_logger.TraceMiddlewareCompleted();
return;
}
@ -91,10 +80,7 @@ namespace Ocelot.RateLimit.Middleware
context.Response.OnStarting(SetRateLimitHeaders, state: headers);
}
_logger.TraceInvokeNext();
await _next.Invoke(context);
_logger.TraceInvokeNextCompleted();
_logger.TraceMiddlewareCompleted();
}
public virtual ClientRequestIdentity SetIdentity(HttpContext httpContext, RateLimitOptions option)

View File

@ -20,7 +20,7 @@ namespace Ocelot.RequestId.Middleware
public RequestIdMiddleware(RequestDelegate next,
IOcelotLoggerFactory loggerFactory,
IRequestScopedDataRepository requestScopedDataRepository)
:base(requestScopedDataRepository)
: base(requestScopedDataRepository)
{
_next = next;
_logger = loggerFactory.CreateLogger<RequestIdMiddleware>();
@ -28,15 +28,9 @@ namespace Ocelot.RequestId.Middleware
}
public async Task Invoke(HttpContext context)
{
_logger.TraceMiddlewareEntry();
{
SetOcelotRequestId(context);
_logger.TraceInvokeNext();
await _next.Invoke(context);
_logger.TraceInvokeNextCompleted();
_logger.TraceMiddlewareCompleted();
await _next.Invoke(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)
{
context.Response.OnStarting(x =>
@ -78,5 +70,13 @@ namespace Ocelot.Responder
return Task.CompletedTask;
}, 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)
{
_logger.TraceMiddlewareEntry();
_logger.TraceInvokeNext();
await _next.Invoke(context);
_logger.TraceInvokeNextCompleted();
await _next.Invoke(context);
if (PipelineError)
{
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);
}
@ -51,13 +48,11 @@ namespace Ocelot.Responder.Middleware
_logger.LogDebug("no pipeline errors, setting and returning completed response");
await _responder.SetResponseOnHttpContext(context, HttpResponseMessage);
}
_logger.TraceMiddlewareCompleted();
}
private void SetErrorResponse(HttpContext context, List<Error> errors)
{
var statusCode = _codeMapper.Map(errors);
_responder.SetErrorResponseOnContext(context, statusCode);
}
}

View File

@ -37,237 +37,209 @@ namespace Ocelot.AcceptanceTests
[Fact]
public void should_return_401_using_identity_server_access_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"
}
}
}
};
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",
IdentityServerConfig = new FileIdentityServerConfig{
ProviderRootUrl = _identityServerRootUrl,
RequireHttps = false,
ApiName = "api",
ApiSecret = "secret"
}
}
}
}
};
this.Given(x => x.GivenThereIsAnIdentityServerOn(_identityServerRootUrl, "api", "api2", AccessTokenType.Jwt))
.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]
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();
this.Given(x => x.GivenThereIsAnIdentityServerOn(_identityServerRootUrl, "api", "api2", AccessTokenType.Jwt))
.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]
public void should_return_response_200_using_identity_server()
{
var configuration = new FileConfiguration
{
ReRoutes = new List<FileReRoute>
{
new FileReRoute
{
DownstreamPathTemplate = _downstreamServicePath,
DownstreamPort = _downstreamServicePort,
DownstreamHost = _downstreamServiceHost,
DownstreamScheme = _downstreamServiceScheme,
UpstreamPathTemplate = "/",
UpstreamHttpMethod = new List<string> { "Get" },
AuthenticationOptions = new FileAuthenticationOptions
{
AllowedScopes = new List<string>(),
Provider = "IdentityServer",
ProviderRootUrl = _identityServerRootUrl,
RequireHttps = false,
ApiName = "api",
ApiSecret = "secret"
}
}
}
};
var configuration = new FileConfiguration
{
ReRoutes = new List<FileReRoute>
{
new FileReRoute
{
DownstreamPathTemplate = _downstreamServicePath,
DownstreamPort = _downstreamServicePort,
DownstreamHost = _downstreamServiceHost,
DownstreamScheme = _downstreamServiceScheme,
UpstreamPathTemplate = "/",
UpstreamHttpMethod = new List<string> { "Get" },
AuthenticationOptions = new FileAuthenticationOptions
{
AllowedScopes = new List<string>(),
Provider = "IdentityServer",
IdentityServerConfig = new FileIdentityServerConfig{
ProviderRootUrl = _identityServerRootUrl,
RequireHttps = false,
ApiName = "api",
ApiSecret = "secret"
}
}
}
}
};
this.Given(x => x.GivenThereIsAnIdentityServerOn(_identityServerRootUrl, "api", "api2", AccessTokenType.Jwt))
.And(x => x.GivenThereIsAServiceRunningOn(_downstreamServiceUrl, 200, "Hello from Laura"))
.And(x => _steps.GivenIHaveAToken(_identityServerRootUrl))
.And(x => _steps.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunning())
.And(x => _steps.GivenIHaveAddedATokenToMyRequest())
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
.And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura"))
.BDDfy();
this.Given(x => x.GivenThereIsAnIdentityServerOn(_identityServerRootUrl, "api", "api2", AccessTokenType.Jwt))
.And(x => x.GivenThereIsAServiceRunningOn(_downstreamServiceUrl, 200, "Hello from Laura"))
.And(x => _steps.GivenIHaveAToken(_identityServerRootUrl))
.And(x => _steps.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunning())
.And(x => _steps.GivenIHaveAddedATokenToMyRequest())
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
.And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura"))
.BDDfy();
}
[Fact]
public void should_return_response_401_using_identity_server_with_token_requested_for_other_api()
{
var configuration = new FileConfiguration
{
ReRoutes = new List<FileReRoute>
{
new FileReRoute
{
DownstreamPathTemplate = _downstreamServicePath,
DownstreamPort = _downstreamServicePort,
DownstreamHost = _downstreamServiceHost,
DownstreamScheme = _downstreamServiceScheme,
UpstreamPathTemplate = "/",
UpstreamHttpMethod = new List<string> { "Get" },
AuthenticationOptions = new FileAuthenticationOptions
{
AllowedScopes = new List<string>(),
Provider = "IdentityServer",
ProviderRootUrl = _identityServerRootUrl,
RequireHttps = false,
ApiName = "api",
ApiSecret = "secret"
}
}
}
};
var configuration = new FileConfiguration
{
ReRoutes = new List<FileReRoute>
{
new FileReRoute
{
DownstreamPathTemplate = _downstreamServicePath,
DownstreamPort = _downstreamServicePort,
DownstreamHost = _downstreamServiceHost,
DownstreamScheme = _downstreamServiceScheme,
UpstreamPathTemplate = "/",
UpstreamHttpMethod = new List<string> { "Get" },
AuthenticationOptions = new FileAuthenticationOptions
{
AllowedScopes = new List<string>(),
Provider = "IdentityServer",
IdentityServerConfig = new FileIdentityServerConfig{
ProviderRootUrl = _identityServerRootUrl,
RequireHttps = false,
ApiName = "api",
ApiSecret = "secret"
}
}
}
}
};
this.Given(x => x.GivenThereIsAnIdentityServerOn(_identityServerRootUrl, "api", "api2", AccessTokenType.Jwt))
.And(x => x.GivenThereIsAServiceRunningOn(_downstreamServiceUrl, 200, "Hello from Laura"))
.And(x => _steps.GivenIHaveATokenForApi2(_identityServerRootUrl))
.And(x => _steps.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunning())
.And(x => _steps.GivenIHaveAddedATokenToMyRequest())
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.Unauthorized))
.BDDfy();
this.Given(x => x.GivenThereIsAnIdentityServerOn(_identityServerRootUrl, "api", "api2", AccessTokenType.Jwt))
.And(x => x.GivenThereIsAServiceRunningOn(_downstreamServiceUrl, 200, "Hello from Laura"))
.And(x => _steps.GivenIHaveATokenForApi2(_identityServerRootUrl))
.And(x => _steps.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunning())
.And(x => _steps.GivenIHaveAddedATokenToMyRequest())
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.Unauthorized))
.BDDfy();
}
[Fact]
public void should_return_201_using_identity_server_access_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" },
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"
}
}
}
};
AuthenticationOptions = new FileAuthenticationOptions
{
AllowedScopes = new List<string>(),
Provider = "IdentityServer",
IdentityServerConfig = new FileIdentityServerConfig{
ProviderRootUrl = _identityServerRootUrl,
RequireHttps = false,
ApiName = "api",
ApiSecret = "secret"
}
}
}
}
};
this.Given(x => x.GivenThereIsAnIdentityServerOn(_identityServerRootUrl, "api", "api2", AccessTokenType.Jwt))
.And(x => x.GivenThereIsAServiceRunningOn(_downstreamServiceUrl, 201, string.Empty))
.And(x => _steps.GivenIHaveAToken(_identityServerRootUrl))
.And(x => _steps.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunning())
.And(x => _steps.GivenIHaveAddedATokenToMyRequest())
.And(x => _steps.GivenThePostHasContent("postContent"))
.When(x => _steps.WhenIPostUrlOnTheApiGateway("/"))
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.Created))
.BDDfy();
this.Given(x => x.GivenThereIsAnIdentityServerOn(_identityServerRootUrl, "api", "api2", AccessTokenType.Jwt))
.And(x => x.GivenThereIsAServiceRunningOn(_downstreamServiceUrl, 201, string.Empty))
.And(x => _steps.GivenIHaveAToken(_identityServerRootUrl))
.And(x => _steps.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunning())
.And(x => _steps.GivenIHaveAddedATokenToMyRequest())
.And(x => _steps.GivenThePostHasContent("postContent"))
.When(x => _steps.WhenIPostUrlOnTheApiGateway("/"))
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.Created))
.BDDfy();
}
[Fact]
public void should_return_201_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"
}
}
}
};
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",
IdentityServerConfig = new FileIdentityServerConfig{
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.GivenIHaveAToken(_identityServerRootUrl))
.And(x => _steps.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunning())
.And(x => _steps.GivenIHaveAddedATokenToMyRequest())
.And(x => _steps.GivenThePostHasContent("postContent"))
.When(x => _steps.WhenIPostUrlOnTheApiGateway("/"))
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.Created))
.BDDfy();
this.Given(x => x.GivenThereIsAnIdentityServerOn(_identityServerRootUrl, "api", "api2", AccessTokenType.Reference))
.And(x => x.GivenThereIsAServiceRunningOn(_downstreamServiceUrl, 201, string.Empty))
.And(x => _steps.GivenIHaveAToken(_identityServerRootUrl))
.And(x => _steps.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunning())
.And(x => _steps.GivenIHaveAddedATokenToMyRequest())
.And(x => _steps.GivenThePostHasContent("postContent"))
.When(x => _steps.WhenIPostUrlOnTheApiGateway("/"))
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.Created))
.BDDfy();
}
private void GivenThereIsAServiceRunningOn(string url, int statusCode, string responseBody)

View File

@ -31,191 +31,199 @@ namespace Ocelot.AcceptanceTests
[Fact]
public void should_return_response_200_authorising_route()
{
var configuration = new FileConfiguration
{
ReRoutes = new List<FileReRoute>
{
new FileReRoute
{
DownstreamPathTemplate = "/",
DownstreamPort = 51876,
DownstreamScheme = "http",
DownstreamHost = "localhost",
UpstreamPathTemplate = "/",
UpstreamHttpMethod = new List<string> { "Get" },
AuthenticationOptions = new FileAuthenticationOptions
{
AllowedScopes = new List<string>(),
Provider = "IdentityServer",
ProviderRootUrl = "http://localhost:51888",
RequireHttps = false,
ApiName = "api",
ApiSecret = "secret"
},
AddHeadersToRequest =
{
{"CustomerId", "Claims[CustomerId] > value"},
{"LocationId", "Claims[LocationId] > value"},
{"UserType", "Claims[sub] > value[0] > |"},
{"UserId", "Claims[sub] > value[1] > |"}
},
AddClaimsToRequest =
{
{"CustomerId", "Claims[CustomerId] > value"},
{"UserType", "Claims[sub] > value[0] > |"},
{"UserId", "Claims[sub] > value[1] > |"}
},
RouteClaimsRequirement =
{
{"UserType", "registered"}
}
}
}
};
var configuration = new FileConfiguration
{
ReRoutes = new List<FileReRoute>
{
new FileReRoute
{
DownstreamPathTemplate = "/",
DownstreamPort = 51876,
DownstreamScheme = "http",
DownstreamHost = "localhost",
UpstreamPathTemplate = "/",
UpstreamHttpMethod = new List<string> { "Get" },
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"},
{"LocationId", "Claims[LocationId] > value"},
{"UserType", "Claims[sub] > value[0] > |"},
{"UserId", "Claims[sub] > value[1] > |"}
},
AddClaimsToRequest =
{
{"CustomerId", "Claims[CustomerId] > value"},
{"UserType", "Claims[sub] > value[0] > |"},
{"UserId", "Claims[sub] > value[1] > |"}
},
RouteClaimsRequirement =
{
{"UserType", "registered"}
}
}
}
};
this.Given(x => x.GivenThereIsAnIdentityServerOn("http://localhost:51888", "api", AccessTokenType.Jwt))
.And(x => x.GivenThereIsAServiceRunningOn("http://localhost:51876", 200, "Hello from Laura"))
.And(x => _steps.GivenIHaveAToken("http://localhost:51888"))
.And(x => _steps.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunning())
.And(x => _steps.GivenIHaveAddedATokenToMyRequest())
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
.And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura"))
.BDDfy();
this.Given(x => x.GivenThereIsAnIdentityServerOn("http://localhost:51888", "api", AccessTokenType.Jwt))
.And(x => x.GivenThereIsAServiceRunningOn("http://localhost:51876", 200, "Hello from Laura"))
.And(x => _steps.GivenIHaveAToken("http://localhost:51888"))
.And(x => _steps.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunning())
.And(x => _steps.GivenIHaveAddedATokenToMyRequest())
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
.And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura"))
.BDDfy();
}
[Fact]
public void should_return_response_403_authorising_route()
{
var configuration = new FileConfiguration
{
ReRoutes = new List<FileReRoute>
{
new FileReRoute
{
DownstreamPathTemplate = "/",
DownstreamPort = 51876,
DownstreamScheme = "http",
DownstreamHost = "localhost",
UpstreamPathTemplate = "/",
UpstreamHttpMethod = new List<string> { "Get" },
AuthenticationOptions = new FileAuthenticationOptions
{
AllowedScopes = new List<string>(),
var configuration = new FileConfiguration
{
ReRoutes = new List<FileReRoute>
{
new FileReRoute
{
DownstreamPathTemplate = "/",
DownstreamPort = 51876,
DownstreamScheme = "http",
DownstreamHost = "localhost",
UpstreamPathTemplate = "/",
UpstreamHttpMethod = new List<string> { "Get" },
AuthenticationOptions = new FileAuthenticationOptions
{
AllowedScopes = new List<string>(),
Provider = "IdentityServer",
ProviderRootUrl = "http://localhost:51888",
RequireHttps = false,
ApiName = "api",
ApiSecret = "secret"
},
AddHeadersToRequest =
{
{"CustomerId", "Claims[CustomerId] > value"},
{"LocationId", "Claims[LocationId] > value"},
{"UserType", "Claims[sub] > value[0] > |"},
{"UserId", "Claims[sub] > value[1] > |"}
},
AddClaimsToRequest =
{
{"CustomerId", "Claims[CustomerId] > value"},
{"UserId", "Claims[sub] > value[1] > |"}
},
RouteClaimsRequirement =
{
{"UserType", "registered"}
}
}
}
};
IdentityServerConfig = new FileIdentityServerConfig{
ProviderRootUrl = "http://localhost:51888",
RequireHttps = false,
ApiName = "api",
ApiSecret = "secret"
}
},
AddHeadersToRequest =
{
{"CustomerId", "Claims[CustomerId] > value"},
{"LocationId", "Claims[LocationId] > value"},
{"UserType", "Claims[sub] > value[0] > |"},
{"UserId", "Claims[sub] > value[1] > |"}
},
AddClaimsToRequest =
{
{"CustomerId", "Claims[CustomerId] > value"},
{"UserId", "Claims[sub] > value[1] > |"}
},
RouteClaimsRequirement =
{
{"UserType", "registered"}
}
}
}
};
this.Given(x => x.GivenThereIsAnIdentityServerOn("http://localhost:51888", "api", AccessTokenType.Jwt))
.And(x => x.GivenThereIsAServiceRunningOn("http://localhost:51876", 200, "Hello from Laura"))
.And(x => _steps.GivenIHaveAToken("http://localhost:51888"))
.And(x => _steps.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunning())
.And(x => _steps.GivenIHaveAddedATokenToMyRequest())
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.Forbidden))
.BDDfy();
this.Given(x => x.GivenThereIsAnIdentityServerOn("http://localhost:51888", "api", AccessTokenType.Jwt))
.And(x => x.GivenThereIsAServiceRunningOn("http://localhost:51876", 200, "Hello from Laura"))
.And(x => _steps.GivenIHaveAToken("http://localhost:51888"))
.And(x => _steps.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunning())
.And(x => _steps.GivenIHaveAddedATokenToMyRequest())
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.Forbidden))
.BDDfy();
}
[Fact]
public void should_return_response_200_using_identity_server_with_allowed_scope()
{
var configuration = new FileConfiguration
{
ReRoutes = new List<FileReRoute>
{
new FileReRoute
{
DownstreamPathTemplate = "/",
DownstreamPort = 51876,
DownstreamHost = "localhost",
DownstreamScheme = "http",
UpstreamPathTemplate = "/",
UpstreamHttpMethod = new List<string> { "Get" },
AuthenticationOptions = new FileAuthenticationOptions
{
AllowedScopes = new List<string>{ "api", "api.readOnly", "openid", "offline_access" },
Provider = "IdentityServer",
ProviderRootUrl = "http://localhost:51888",
RequireHttps = false,
ApiName = "api",
ApiSecret = "secret"
}
}
}
};
var configuration = new FileConfiguration
{
ReRoutes = new List<FileReRoute>
{
new FileReRoute
{
DownstreamPathTemplate = "/",
DownstreamPort = 51876,
DownstreamHost = "localhost",
DownstreamScheme = "http",
UpstreamPathTemplate = "/",
UpstreamHttpMethod = new List<string> { "Get" },
AuthenticationOptions = new FileAuthenticationOptions
{
AllowedScopes = new List<string>{ "api", "api.readOnly", "openid", "offline_access" },
Provider = "IdentityServer",
IdentityServerConfig = new FileIdentityServerConfig{
ProviderRootUrl = "http://localhost:51888",
RequireHttps = false,
ApiName = "api",
ApiSecret = "secret"
}
}
}
}
};
this.Given(x => x.GivenThereIsAnIdentityServerOn("http://localhost:51888", "api", AccessTokenType.Jwt))
.And(x => x.GivenThereIsAServiceRunningOn("http://localhost:51876", 200, "Hello from Laura"))
.And(x => _steps.GivenIHaveATokenForApiReadOnlyScope("http://localhost:51888"))
.And(x => _steps.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunning())
.And(x => _steps.GivenIHaveAddedATokenToMyRequest())
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
.BDDfy();
this.Given(x => x.GivenThereIsAnIdentityServerOn("http://localhost:51888", "api", AccessTokenType.Jwt))
.And(x => x.GivenThereIsAServiceRunningOn("http://localhost:51876", 200, "Hello from Laura"))
.And(x => _steps.GivenIHaveATokenForApiReadOnlyScope("http://localhost:51888"))
.And(x => _steps.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunning())
.And(x => _steps.GivenIHaveAddedATokenToMyRequest())
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
.BDDfy();
}
[Fact]
public void should_return_response_403_using_identity_server_with_scope_not_allowed()
{
var configuration = new FileConfiguration
{
ReRoutes = new List<FileReRoute>
{
new FileReRoute
{
DownstreamPathTemplate = "/",
DownstreamPort = 51876,
DownstreamHost = "localhost",
DownstreamScheme = "http",
UpstreamPathTemplate = "/",
UpstreamHttpMethod = new List<string> { "Get" },
AuthenticationOptions = new FileAuthenticationOptions
{
AllowedScopes = new List<string>{ "api", "openid", "offline_access" },
Provider = "IdentityServer",
ProviderRootUrl = "http://localhost:51888",
RequireHttps = false,
ApiName = "api",
ApiSecret = "secret"
}
}
}
};
var configuration = new FileConfiguration
{
ReRoutes = new List<FileReRoute>
{
new FileReRoute
{
DownstreamPathTemplate = "/",
DownstreamPort = 51876,
DownstreamHost = "localhost",
DownstreamScheme = "http",
UpstreamPathTemplate = "/",
UpstreamHttpMethod = new List<string> { "Get" },
AuthenticationOptions = new FileAuthenticationOptions
{
AllowedScopes = new List<string>{ "api", "openid", "offline_access" },
Provider = "IdentityServer",
IdentityServerConfig = new FileIdentityServerConfig{
ProviderRootUrl = "http://localhost:51888",
RequireHttps = false,
ApiName = "api",
ApiSecret = "secret"
}
}
}
}
};
this.Given(x => x.GivenThereIsAnIdentityServerOn("http://localhost:51888", "api", AccessTokenType.Jwt))
.And(x => x.GivenThereIsAServiceRunningOn("http://localhost:51876", 200, "Hello from Laura"))
.And(x => _steps.GivenIHaveATokenForApiReadOnlyScope("http://localhost:51888"))
.And(x => _steps.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunning())
.And(x => _steps.GivenIHaveAddedATokenToMyRequest())
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.Forbidden))
.BDDfy();
this.Given(x => x.GivenThereIsAnIdentityServerOn("http://localhost:51888", "api", AccessTokenType.Jwt))
.And(x => x.GivenThereIsAServiceRunningOn("http://localhost:51876", 200, "Hello from Laura"))
.And(x => _steps.GivenIHaveATokenForApiReadOnlyScope("http://localhost:51888"))
.And(x => _steps.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunning())
.And(x => _steps.GivenIHaveAddedATokenToMyRequest())
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.Forbidden))
.BDDfy();
}
private void GivenThereIsAServiceRunningOn(string url, int statusCode, string responseBody)

View File

@ -33,63 +33,65 @@ namespace Ocelot.AcceptanceTests
[Fact]
public void should_return_response_200_and_foward_claim_as_header()
{
var user = new TestUser()
{
Username = "test",
Password = "test",
SubjectId = "registered|1231231",
Claims = new List<Claim>
{
new Claim("CustomerId", "123"),
new Claim("LocationId", "1")
}
};
var user = new TestUser()
{
Username = "test",
Password = "test",
SubjectId = "registered|1231231",
Claims = new List<Claim>
{
new Claim("CustomerId", "123"),
new Claim("LocationId", "1")
}
};
var configuration = new FileConfiguration
{
ReRoutes = new List<FileReRoute>
{
new FileReRoute
{
DownstreamPathTemplate = "/",
DownstreamPort = 52876,
DownstreamScheme = "http",
DownstreamHost = "localhost",
UpstreamPathTemplate = "/",
UpstreamHttpMethod = new List<string> { "Get" },
AuthenticationOptions = new FileAuthenticationOptions
{
var configuration = new FileConfiguration
{
ReRoutes = new List<FileReRoute>
{
new FileReRoute
{
DownstreamPathTemplate = "/",
DownstreamPort = 52876,
DownstreamScheme = "http",
DownstreamHost = "localhost",
UpstreamPathTemplate = "/",
UpstreamHttpMethod = new List<string> { "Get" },
AuthenticationOptions = new FileAuthenticationOptions
{
AllowedScopes = new List<string>
{
"openid", "offline_access", "api"
},
Provider = "IdentityServer",
ProviderRootUrl = "http://localhost:52888",
RequireHttps = false,
ApiName = "api",
ApiSecret = "secret",
},
AddHeadersToRequest =
{
{"CustomerId", "Claims[CustomerId] > value"},
{"LocationId", "Claims[LocationId] > value"},
{"UserType", "Claims[sub] > value[0] > |"},
{"UserId", "Claims[sub] > value[1] > |"}
}
}
}
};
{
"openid", "offline_access", "api"
},
Provider = "IdentityServer",
IdentityServerConfig = new FileIdentityServerConfig{
ProviderRootUrl = "http://localhost:52888",
RequireHttps = false,
ApiName = "api",
ApiSecret = "secret"
}
},
AddHeadersToRequest =
{
{"CustomerId", "Claims[CustomerId] > value"},
{"LocationId", "Claims[LocationId] > value"},
{"UserType", "Claims[sub] > value[0] > |"},
{"UserId", "Claims[sub] > value[1] > |"}
}
}
}
};
this.Given(x => x.GivenThereIsAnIdentityServerOn("http://localhost:52888", "api", AccessTokenType.Jwt, user))
.And(x => x.GivenThereIsAServiceRunningOn("http://localhost:52876", 200))
.And(x => _steps.GivenIHaveAToken("http://localhost:52888"))
.And(x => _steps.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunning())
.And(x => _steps.GivenIHaveAddedATokenToMyRequest())
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
.And(x => _steps.ThenTheResponseBodyShouldBe("CustomerId: 123 LocationId: 1 UserType: registered UserId: 1231231"))
.BDDfy();
this.Given(x => x.GivenThereIsAnIdentityServerOn("http://localhost:52888", "api", AccessTokenType.Jwt, user))
.And(x => x.GivenThereIsAServiceRunningOn("http://localhost:52876", 200))
.And(x => _steps.GivenIHaveAToken("http://localhost:52888"))
.And(x => _steps.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunning())
.And(x => _steps.GivenIHaveAddedATokenToMyRequest())
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
.And(x => _steps.ThenTheResponseBodyShouldBe("CustomerId: 123 LocationId: 1 UserType: registered UserId: 1231231"))
.BDDfy();
}
private void GivenThereIsAServiceRunningOn(string url, int statusCode)

View File

@ -33,63 +33,65 @@ namespace Ocelot.AcceptanceTests
[Fact]
public void should_return_response_200_and_foward_claim_as_query_string()
{
var user = new TestUser()
{
Username = "test",
Password = "test",
SubjectId = "registered|1231231",
Claims = new List<Claim>
{
new Claim("CustomerId", "123"),
new Claim("LocationId", "1")
}
};
var user = new TestUser()
{
Username = "test",
Password = "test",
SubjectId = "registered|1231231",
Claims = new List<Claim>
{
new Claim("CustomerId", "123"),
new Claim("LocationId", "1")
}
};
var configuration = new FileConfiguration
{
ReRoutes = new List<FileReRoute>
{
new FileReRoute
{
DownstreamPathTemplate = "/",
DownstreamPort = 57876,
DownstreamScheme = "http",
DownstreamHost = "localhost",
UpstreamPathTemplate = "/",
UpstreamHttpMethod = new List<string> { "Get" },
AuthenticationOptions = new FileAuthenticationOptions
{
var configuration = new FileConfiguration
{
ReRoutes = new List<FileReRoute>
{
new FileReRoute
{
DownstreamPathTemplate = "/",
DownstreamPort = 57876,
DownstreamScheme = "http",
DownstreamHost = "localhost",
UpstreamPathTemplate = "/",
UpstreamHttpMethod = new List<string> { "Get" },
AuthenticationOptions = new FileAuthenticationOptions
{
AllowedScopes = new List<string>
{
"openid", "offline_access", "api"
},
Provider = "IdentityServer",
ProviderRootUrl = "http://localhost:57888",
RequireHttps = false,
ApiName = "api",
ApiSecret = "secret",
},
AddQueriesToRequest =
{
{"CustomerId", "Claims[CustomerId] > value"},
{"LocationId", "Claims[LocationId] > value"},
{"UserType", "Claims[sub] > value[0] > |"},
{"UserId", "Claims[sub] > value[1] > |"}
}
}
}
};
{
"openid", "offline_access", "api"
},
Provider = "IdentityServer",
IdentityServerConfig = new FileIdentityServerConfig{
ProviderRootUrl = "http://localhost:57888",
RequireHttps = false,
ApiName = "api",
ApiSecret = "secret"
}
},
AddQueriesToRequest =
{
{"CustomerId", "Claims[CustomerId] > value"},
{"LocationId", "Claims[LocationId] > value"},
{"UserType", "Claims[sub] > value[0] > |"},
{"UserId", "Claims[sub] > value[1] > |"}
}
}
}
};
this.Given(x => x.GivenThereIsAnIdentityServerOn("http://localhost:57888", "api", AccessTokenType.Jwt, user))
.And(x => x.GivenThereIsAServiceRunningOn("http://localhost:57876", 200))
.And(x => _steps.GivenIHaveAToken("http://localhost:57888"))
.And(x => _steps.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunning())
.And(x => _steps.GivenIHaveAddedATokenToMyRequest())
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
.And(x => _steps.ThenTheResponseBodyShouldBe("CustomerId: 123 LocationId: 1 UserType: registered UserId: 1231231"))
.BDDfy();
this.Given(x => x.GivenThereIsAnIdentityServerOn("http://localhost:57888", "api", AccessTokenType.Jwt, user))
.And(x => x.GivenThereIsAServiceRunningOn("http://localhost:57876", 200))
.And(x => _steps.GivenIHaveAToken("http://localhost:57888"))
.And(x => _steps.GivenThereIsAConfiguration(configuration))
.And(x => _steps.GivenOcelotIsRunning())
.And(x => _steps.GivenIHaveAddedATokenToMyRequest())
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
.And(x => _steps.ThenTheResponseBodyShouldBe("CustomerId: 123 LocationId: 1 UserType: registered UserId: 1231231"))
.BDDfy();
}
private void GivenThereIsAServiceRunningOn(string url, int statusCode)

View File

@ -8,6 +8,7 @@ using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Newtonsoft.Json;
using Ocelot.Authentication.JsonConverters;
using Ocelot.Configuration;
using Ocelot.Configuration.File;
using Ocelot.Configuration.Repository;
@ -29,7 +30,6 @@ namespace Ocelot.AcceptanceTests
_steps = new Steps();
}
[Fact]
public void should_return_response_200_with_simple_url()
{
@ -105,7 +105,9 @@ namespace Ocelot.AcceptanceTests
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);
@ -167,4 +169,4 @@ namespace Ocelot.AcceptanceTests
_steps.Dispose();
}
}
}
}

View File

@ -15,7 +15,7 @@
</PropertyGroup>
<ItemGroup>
<None Update="configuration.json">
<None Update="configuration.json;appsettings.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
@ -30,6 +30,7 @@
</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="xunit.runner.visualstudio" Version="2.3.0-beta2-build1317" />
<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.Extensions.DependencyInjection;
using Newtonsoft.Json;
using Ocelot.Cache;
using Ocelot.Configuration.File;
using Ocelot.ManualTest;
using Shouldly;
@ -19,15 +20,19 @@ namespace Ocelot.IntegrationTests
public class AdministrationTests : IDisposable
{
private readonly HttpClient _httpClient;
private readonly HttpClient _httpClientTwo;
private HttpResponseMessage _response;
private IWebHost _builder;
private IWebHostBuilder _webHostBuilder;
private readonly string _ocelotBaseUrl;
private BearerToken _token;
private IWebHostBuilder _webHostBuilderTwo;
private IWebHost _builderTwo;
public AdministrationTests()
{
_httpClient = new HttpClient();
_httpClientTwo = new HttpClient();
_ocelotBaseUrl = "http://localhost:5000";
_httpClient.BaseAddress = new Uri(_ocelotBaseUrl);
}
@ -70,6 +75,27 @@ namespace Ocelot.IntegrationTests
.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]
public void should_return_file_configuration()
{
@ -95,7 +121,12 @@ namespace Ocelot.IntegrationTests
DownstreamScheme = "https",
DownstreamPathTemplate = "/",
UpstreamHttpMethod = new List<string> { "get" },
UpstreamPathTemplate = "/"
UpstreamPathTemplate = "/",
FileCacheOptions = new FileCacheOptions
{
TtlSeconds = 10,
Region = "Geoff"
}
},
new FileReRoute()
{
@ -104,7 +135,12 @@ namespace Ocelot.IntegrationTests
DownstreamScheme = "https",
DownstreamPathTemplate = "/",
UpstreamHttpMethod = new List<string> { "get" },
UpstreamPathTemplate = "/test"
UpstreamPathTemplate = "/test",
FileCacheOptions = new FileCacheOptions
{
TtlSeconds = 10,
Region = "Dave"
}
}
}
};
@ -193,6 +229,87 @@ namespace Ocelot.IntegrationTests
.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)
{
var json = JsonConvert.SerializeObject(updatedConfiguration);
@ -201,6 +318,13 @@ namespace Ocelot.IntegrationTests
_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)
{
var response = JsonConvert.DeserializeObject<FileConfiguration>(_response.Content.ReadAsStringAsync().Result);
@ -298,6 +422,11 @@ namespace Ocelot.IntegrationTests
_response = _httpClient.GetAsync(url).Result;
}
private void WhenIDeleteOnTheApiGateway(string url)
{
_response = _httpClient.DeleteAsync(url).Result;
}
private void ThenTheStatusCodeShouldBe(HttpStatusCode expectedHttpStatusCode)
{
_response.StatusCode.ShouldBe(expectedHttpStatusCode);
@ -305,6 +434,8 @@ namespace Ocelot.IntegrationTests
public void Dispose()
{
Environment.SetEnvironmentVariable("OCELOT_CERTIFICATE", "");
Environment.SetEnvironmentVariable("OCELOT_CERTIFICATE_PASSWORD", "");
_builder?.Dispose();
_httpClient?.Dispose();
}

View File

@ -15,7 +15,7 @@
</PropertyGroup>
<ItemGroup>
<None Update="configuration.json;appsettings.json">
<None Update="configuration.json;appsettings.json;idsrv3test.pfx">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
@ -30,6 +30,7 @@
</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="xunit.runner.visualstudio" Version="2.3.0-beta2-build1317" />
<PackageReference Include="Microsoft.AspNetCore.Server.IISIntegration" Version="1.1.1" />

View File

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

View File

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

Binary file not shown.

View File

@ -22,6 +22,7 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.MiddlewareAnalysis" 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.FileExtensions" Version="1.1.1" />

View File

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

View File

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

View File

@ -1,108 +1,76 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Net.Http;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.TestHost;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Moq;
using Ocelot.Authentication.Handler;
using Ocelot.Authentication.Handler.Factory;
using Ocelot.Authentication.Middleware;
using Ocelot.Cache.Middleware;
using Ocelot.Configuration;
using Ocelot.Configuration.Builder;
using Ocelot.DownstreamRouteFinder;
using Ocelot.DownstreamRouteFinder.UrlMatcher;
using Ocelot.Infrastructure.RequestData;
using Ocelot.Logging;
using Ocelot.Responses;
using Shouldly;
using TestStack.BDDfy;
using Xunit;
namespace Ocelot.UnitTests.Authentication
namespace Ocelot.UnitTests.Authentication
{
public class AuthenticationMiddlewareTests : IDisposable
using System.Collections.Generic;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using Moq;
using Ocelot.Authentication.Handler.Factory;
using Ocelot.Authentication.Middleware;
using Ocelot.Configuration.Builder;
using Ocelot.DownstreamRouteFinder;
using Ocelot.DownstreamRouteFinder.UrlMatcher;
using Ocelot.Logging;
using Ocelot.Responses;
using Shouldly;
using TestStack.BDDfy;
using Xunit;
public class AuthenticationMiddlewareTests : ServerHostedMiddlewareTest
{
private readonly Mock<IRequestScopedDataRepository> _scopedRepository;
private readonly Mock<IAuthenticationHandlerFactory> _authFactory;
private readonly string _url;
private readonly TestServer _server;
private readonly HttpClient _client;
private HttpResponseMessage _result;
private OkResponse<DownstreamRoute> _downstreamRoute;
public AuthenticationMiddlewareTests()
{
_url = "http://localhost:51879";
_scopedRepository = new Mock<IRequestScopedDataRepository>();
_authFactory = new Mock<IAuthenticationHandlerFactory>();
var builder = new WebHostBuilder()
.ConfigureServices(x =>
{
x.AddSingleton<IOcelotLoggerFactory, AspDotNetLoggerFactory>();
x.AddLogging();
x.AddSingleton(_authFactory.Object);
x.AddSingleton(_scopedRepository.Object);
})
.UseUrls(_url)
.UseKestrel()
.UseContentRoot(Directory.GetCurrentDirectory())
.UseIISIntegration()
.UseUrls(_url)
.Configure(app =>
{
app.UseAuthenticationMiddleware();
app.Run(async x =>
{
await x.Response.WriteAsync("The user is authenticated");
});
});
_server = new TestServer(builder);
_client = _server.CreateClient();
GivenTheTestServerIsConfigured();
}
[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())))
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();
}
protected override void GivenTheTestServerServicesAreConfigured(IServiceCollection services)
{
services.AddSingleton<IOcelotLoggerFactory, AspDotNetLoggerFactory>();
services.AddLogging();
services.AddSingleton(_authFactory.Object);
services.AddSingleton(ScopedRepository.Object);
}
protected override void GivenTheTestServerPipelineIsConfigured(IApplicationBuilder app)
{
app.UseAuthenticationMiddleware();
app.Run(async x =>
{
await x.Response.WriteAsync("The user is authenticated");
});
}
private void ThenTheUserIsAuthenticated()
{
var content = _result.Content.ReadAsStringAsync().Result;
var content = ResponseMessage.Content.ReadAsStringAsync().Result;
content.ShouldBe("The user is authenticated");
}
private void GivenTheDownStreamRouteIs(DownstreamRoute downstreamRoute)
{
_downstreamRoute = new OkResponse<DownstreamRoute>(downstreamRoute);
_scopedRepository
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

@ -1,66 +1,32 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Net.Http;
using System.Security.Claims;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.TestHost;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Moq;
using Ocelot.Authorisation;
using Ocelot.Configuration.Builder;
using Ocelot.DownstreamRouteFinder;
using Ocelot.DownstreamRouteFinder.UrlMatcher;
using Ocelot.Infrastructure.RequestData;
using Ocelot.Logging;
using Ocelot.Responses;
using TestStack.BDDfy;
using Xunit;
namespace Ocelot.UnitTests.Authorization
namespace Ocelot.UnitTests.Authorization
{
using Authorisation.Middleware;
using System.Collections.Generic;
using System.Security.Claims;
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;
using Moq;
using Ocelot.Authorisation;
using Ocelot.Authorisation.Middleware;
using Ocelot.Configuration.Builder;
using Ocelot.DownstreamRouteFinder;
using Ocelot.DownstreamRouteFinder.UrlMatcher;
using Ocelot.Logging;
using Ocelot.Responses;
using TestStack.BDDfy;
using Xunit;
public class AuthorisationMiddlewareTests : IDisposable
public class AuthorisationMiddlewareTests : ServerHostedMiddlewareTest
{
private readonly Mock<IRequestScopedDataRepository> _scopedRepository;
private readonly Mock<IClaimsAuthoriser> _authService;
private readonly Mock<IScopesAuthoriser> _authScopesService;
private readonly string _url;
private readonly TestServer _server;
private readonly HttpClient _client;
private HttpResponseMessage _result;
private OkResponse<DownstreamRoute> _downstreamRoute;
public AuthorisationMiddlewareTests()
{
_url = "http://localhost:51879";
_scopedRepository = new Mock<IRequestScopedDataRepository>();
_authService = new Mock<IClaimsAuthoriser>();
_authScopesService = new Mock<IScopesAuthoriser>();
var builder = new WebHostBuilder()
.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();
GivenTheTestServerIsConfigured();
}
[Fact]
@ -77,6 +43,28 @@ namespace Ocelot.UnitTests.Authorization
.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)
{
_authService
@ -90,25 +78,5 @@ namespace Ocelot.UnitTests.Authorization
.Verify(x => x.Authorise(It.IsAny<ClaimsPrincipal>(),
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 _resultGet;
private TimeSpan _ttlSeconds;
private string _region;
public CacheManagerCacheTests()
{
_mockCacheManager = new Mock<ICacheManager<string>>();
_ocelotOcelotCacheManager = new OcelotCacheManagerCache<string>(_mockCacheManager.Object);
}
[Fact]
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())
.Then(x => x.ThenTheResultIs("someValue"))
.BDDfy();
}
[Fact]
@ -40,13 +41,37 @@ namespace Ocelot.UnitTests.Cache
.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)
{
_key = key;
_value = value;
_ttlSeconds = ttlSeconds;
_ocelotOcelotCacheManager.Add(_key, _value, _ttlSeconds);
_ocelotOcelotCacheManager.Add(_key, _value, _ttlSeconds, "region");
}
private void ThenTheCacheIsCalledCorrectly()
@ -62,15 +87,16 @@ namespace Ocelot.UnitTests.Cache
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;
_value = value;
_region = region;
_mockCacheManager
.Setup(x => x.Get<string>(It.IsAny<string>()))
.Setup(x => x.Get<string>(It.IsAny<string>(), It.IsAny<string>()))
.Returns(value);
}
}

View File

@ -1,65 +1,36 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Net.Http;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.TestHost;
using Microsoft.Extensions.DependencyInjection;
using Moq;
using Ocelot.Cache;
using Ocelot.Cache.Middleware;
using Ocelot.Configuration;
using Ocelot.Configuration.Builder;
using Ocelot.DownstreamRouteFinder;
using Ocelot.DownstreamRouteFinder.UrlMatcher;
using Ocelot.Infrastructure.RequestData;
using Ocelot.Logging;
using Ocelot.Responses;
using TestStack.BDDfy;
using Xunit;
namespace Ocelot.UnitTests.Cache
namespace Ocelot.UnitTests.Cache
{
public class OutputCacheMiddlewareTests
using System;
using System.Collections.Generic;
using System.Net.Http;
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;
using Moq;
using Ocelot.Cache;
using Ocelot.Cache.Middleware;
using Ocelot.Configuration;
using Ocelot.Configuration.Builder;
using Ocelot.DownstreamRouteFinder;
using Ocelot.DownstreamRouteFinder.UrlMatcher;
using Ocelot.Logging;
using Ocelot.Responses;
using TestStack.BDDfy;
using Xunit;
public class OutputCacheMiddlewareTests : ServerHostedMiddlewareTest
{
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;
public OutputCacheMiddlewareTests()
{
_cacheManager = new Mock<IOcelotCache<HttpResponseMessage>>();
_scopedRepo = new Mock<IRequestScopedDataRepository>();
_url = "http://localhost:51879";
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
ScopedRepository
.Setup(sr => sr.Get<HttpRequestMessage>("DownstreamRequest"))
.Returns(new OkResponse<HttpRequestMessage>(new HttpRequestMessage(HttpMethod.Get, "https://some.url/blah?abcd=123")));
_server = new TestServer(builder);
_client = _server.CreateClient();
GivenTheTestServerIsConfigured();
}
[Fact]
@ -85,32 +56,60 @@ namespace Ocelot.UnitTests.Cache
.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()
{
var reRoute = new ReRouteBuilder()
.WithIsCached(true)
.WithCacheOptions(new CacheOptions(100))
.WithCacheOptions(new CacheOptions(100, "kanken"))
.WithUpstreamHttpMethod(new List<string> { "Get" })
.Build();
var downstreamRoute = new DownstreamRoute(new List<UrlPathPlaceholderNameAndValue>(), reRoute);
_scopedRepo
ScopedRepository
.Setup(x => x.Get<DownstreamRoute>(It.IsAny<string>()))
.Returns(new OkResponse<DownstreamRoute>(downstreamRoute));
}
private void GivenThereAreNoErrors()
{
_scopedRepo
ScopedRepository
.Setup(x => x.Get<bool>("OcelotMiddlewareError"))
.Returns(new OkResponse<bool>(false));
}
private void GivenThereIsADownstreamUrl()
{
_scopedRepo
ScopedRepository
.Setup(x => x.Get<string>("DownstreamUrl"))
.Returns(new OkResponse<string>("anything"));
}
@ -118,33 +117,13 @@ namespace Ocelot.UnitTests.Cache
private void ThenTheCacheGetIsCalledCorrectly()
{
_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()
{
_cacheManager
.Verify(x => x.Add(It.IsAny<string>(), It.IsAny<HttpResponseMessage>(), It.IsAny<TimeSpan>()), 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;
.Verify(x => x.Add(It.IsAny<string>(), It.IsAny<HttpResponseMessage>(), It.IsAny<TimeSpan>(), It.IsAny<string>()), Times.Once);
}
}
}

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;
using System.Collections.Generic;
using System.IO;
using System.Net.Http;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.TestHost;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Moq;
using Ocelot.Cache.Middleware;
using Ocelot.Claims;
using Ocelot.Claims.Middleware;
using Ocelot.Configuration;
using Ocelot.Configuration.Builder;
using Ocelot.DownstreamRouteFinder;
using Ocelot.DownstreamRouteFinder.UrlMatcher;
using Ocelot.Infrastructure.RequestData;
using Ocelot.Logging;
using Ocelot.Responses;
using TestStack.BDDfy;
using Xunit;
namespace Ocelot.UnitTests.Claims
namespace Ocelot.UnitTests.Claims
{
public class ClaimsBuilderMiddlewareTests : IDisposable
using System.Collections.Generic;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using Moq;
using Ocelot.Claims;
using Ocelot.Claims.Middleware;
using Ocelot.Configuration;
using Ocelot.Configuration.Builder;
using Ocelot.DownstreamRouteFinder;
using Ocelot.DownstreamRouteFinder.UrlMatcher;
using Ocelot.Logging;
using Ocelot.Responses;
using TestStack.BDDfy;
using Xunit;
public class ClaimsBuilderMiddlewareTests : ServerHostedMiddlewareTest
{
private readonly Mock<IRequestScopedDataRepository> _scopedRepository;
private readonly Mock<IAddClaimsToRequest> _addHeaders;
private readonly string _url;
private readonly TestServer _server;
private readonly HttpClient _client;
private Response<DownstreamRoute> _downstreamRoute;
private HttpResponseMessage _result;
public ClaimsBuilderMiddlewareTests()
{
_url = "http://localhost:51879";
_scopedRepository = new Mock<IRequestScopedDataRepository>();
_addHeaders = new Mock<IAddClaimsToRequest>();
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.UseClaimsBuilderMiddleware();
});
_server = new TestServer(builder);
_client = _server.CreateClient();
GivenTheTestServerIsConfigured();
}
[Fact]
@ -82,6 +48,27 @@ namespace Ocelot.UnitTests.Claims
.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()
{
_addHeaders
@ -96,24 +83,5 @@ namespace Ocelot.UnitTests.Claims
.Verify(x => x.SetClaimsOnContext(It.IsAny<List<ClaimToThing>>(),
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
{
private AuthenticationOptionsCreator _authOptionsCreator;
private readonly AuthenticationOptionsCreator _authOptionsCreator;
private FileReRoute _fileReRoute;
private AuthenticationOptions _result;
public AuthenticationOptionsCreatorTests()
{
_authOptionsCreator = new AuthenticationOptionsCreator();
_authOptionsCreator = new AuthenticationOptionsCreator(new AuthenticationProviderConfigCreator());
}
[Fact]
@ -28,26 +28,68 @@ namespace Ocelot.UnitTests.Configuration
AuthenticationOptions = new FileAuthenticationOptions
{
Provider = "Geoff",
ProviderRootUrl = "http://www.bbc.co.uk/",
ApiName = "Laura",
RequireHttps = true,
AllowedScopes = new List<string> {"cheese"},
ApiSecret = "secret"
IdentityServerConfig = new FileIdentityServerConfig()
{
ProviderRootUrl = "http://www.bbc.co.uk/",
ApiName = "Laura",
RequireHttps = true,
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()
.WithProvider(fileReRoute.AuthenticationOptions?.Provider)
.WithProviderRootUrl(fileReRoute.AuthenticationOptions?.ProviderRootUrl)
.WithApiName(fileReRoute.AuthenticationOptions?.ApiName)
.WithRequireHttps(fileReRoute.AuthenticationOptions.RequireHttps)
.WithAllowedScopes(fileReRoute.AuthenticationOptions?.AllowedScopes)
.WithApiSecret(fileReRoute.AuthenticationOptions?.ApiSecret)
.WithConfig(authenticationConfig)
.Build();
this.Given(x => x.GivenTheFollowing(fileReRoute))
.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();
}
@ -61,14 +103,31 @@ namespace Ocelot.UnitTests.Configuration
_result = _authOptionsCreator.Create(_fileReRoute);
}
private void ThenTheFollowingIsReturned(AuthenticationOptions expected)
private void ThenTheFollowingJwtConfigIsReturned(AuthenticationOptions expected)
{
_result.AllowedScopes.ShouldBe(expected.AllowedScopes);
_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);
var _resultSettings = _result.Config as JwtConfig;
var expectedSettngs = expected.Config as JwtConfig;
_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.Options;
using Moq;
using Ocelot.Cache;
using Ocelot.Configuration;
using Ocelot.Configuration.Builder;
using Ocelot.Configuration.Creator;
@ -17,6 +18,10 @@ using Xunit;
namespace Ocelot.UnitTests.Configuration
{
using System.Collections;
using Ocelot.UnitTests.TestData;
public class FileConfigurationCreatorTests
{
private readonly Mock<IOptions<FileConfiguration>> _fileConfig;
@ -39,6 +44,7 @@ namespace Ocelot.UnitTests.Configuration
private Mock<IQoSOptionsCreator> _qosOptionsCreator;
private Mock<IReRouteOptionsCreator> _fileReRouteOptionsCreator;
private Mock<IRateLimitOptionsCreator> _rateLimitOptions;
private Mock<IRegionCreator> _regionCreator;
public FileConfigurationCreatorTests()
{
@ -59,6 +65,7 @@ namespace Ocelot.UnitTests.Configuration
_qosOptionsCreator = new Mock<IQoSOptionsCreator>();
_fileReRouteOptionsCreator = new Mock<IReRouteOptionsCreator>();
_rateLimitOptions = new Mock<IRateLimitOptionsCreator>();
_regionCreator = new Mock<IRegionCreator>();
_ocelotConfigurationCreator = new FileOcelotConfigurationCreator(
_fileConfig.Object, _validator.Object, _logger.Object,
@ -66,7 +73,51 @@ namespace Ocelot.UnitTests.Configuration
_qosProviderFactory.Object, _qosProviderHouse.Object, _claimsToThingCreator.Object,
_authOptionsCreator.Object, _upstreamTemplatePatternCreator.Object, _requestIdKeyCreator.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]
@ -393,20 +444,18 @@ namespace Ocelot.UnitTests.Configuration
.BDDfy();
}
[Fact]
public void should_create_with_headers_to_extract()
[Theory]
[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()
.WithIsAuthenticated(true)
.Build();
var authenticationOptions = new AuthenticationOptionsBuilder()
.WithProvider("IdentityServer")
.WithProviderRootUrl("http://localhost:51888")
.WithRequireHttps(false)
.WithApiSecret("secret")
.WithApiName("api")
.WithProvider(provider)
.WithAllowedScopes(new List<string>())
.WithConfig(config)
.Build();
var expected = new List<ReRoute>
@ -423,59 +472,32 @@ namespace Ocelot.UnitTests.Configuration
.Build()
};
this.Given(x => x.GivenTheConfigIs(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",
ProviderRootUrl = "http://localhost:51888",
RequireHttps = false,
ApiName= "api",
ApiSecret = "secret"
},
AddHeadersToRequest =
{
{"CustomerId", "Claims[CustomerId] > value"},
}
}
}
}))
this.Given(x => x.GivenTheConfigIs(fileConfig))
.And(x => x.GivenTheConfigIsValid())
.And(x => x.GivenTheAuthOptionsCreatorReturns(authenticationOptions))
.And(x => x.GivenTheFollowingOptionsAreReturned(reRouteOptions))
.And(x => x.GivenTheClaimsToThingCreatorReturns(new List<ClaimToThing>{new ClaimToThing("CustomerId", "CustomerId", "", 0)}))
.And(x => x.GivenTheClaimsToThingCreatorReturns(new List<ClaimToThing> { new ClaimToThing("CustomerId", "CustomerId", "", 0) }))
.And(x => x.GivenTheLoadBalancerFactoryReturns())
.When(x => x.WhenICreateTheConfig())
.Then(x => x.ThenTheReRoutesAre(expected))
.And(x => x.ThenTheAuthenticationOptionsAre(expected))
.And(x => x.ThenTheAuthenticationOptionsAre(provider, expected))
.And(x => x.ThenTheAuthOptionsCreatorIsCalledCorrectly())
.BDDfy();
}
[Fact]
public void should_create_with_authentication_properties()
[Theory]
[MemberData(nameof(AuthenticationConfigTestData.GetAuthenticationData), MemberType = typeof(AuthenticationConfigTestData))]
public void should_create_with_authentication_properties(string provider, IAuthenticationConfig config, FileConfiguration fileConfig)
{
var reRouteOptions = new ReRouteOptionsBuilder()
.WithIsAuthenticated(true)
.Build();
var authenticationOptions = new AuthenticationOptionsBuilder()
.WithProvider("IdentityServer")
.WithProviderRootUrl("http://localhost:51888")
.WithRequireHttps(false)
.WithApiSecret("secret")
.WithApiName("api")
.WithAllowedScopes(new List<string>())
.Build();
var authenticationOptions = new AuthenticationOptionsBuilder()
.WithProvider(provider)
.WithAllowedScopes(new List<string>())
.WithConfig(config)
.Build();
var expected = new List<ReRoute>
{
@ -487,35 +509,14 @@ namespace Ocelot.UnitTests.Configuration
.Build()
};
this.Given(x => x.GivenTheConfigIs(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",
ProviderRootUrl = "http://localhost:51888",
RequireHttps = false,
ApiName= "api",
ApiSecret = "secret"
}
}
}
}))
this.Given(x => x.GivenTheConfigIs(fileConfig))
.And(x => x.GivenTheConfigIsValid())
.And(x => x.GivenTheFollowingOptionsAreReturned(reRouteOptions))
.And(x => x.GivenTheAuthOptionsCreatorReturns(authenticationOptions))
.And(x => x.GivenTheLoadBalancerFactoryReturns())
.When(x => x.WhenICreateTheConfig())
.Then(x => x.ThenTheReRoutesAre(expected))
.And(x => x.ThenTheAuthenticationOptionsAre(expected))
.And(x => x.ThenTheAuthenticationOptionsAre(provider, expected))
.And(x => x.ThenTheAuthOptionsCreatorIsCalledCorrectly())
.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++)
{
@ -595,11 +596,25 @@ namespace Ocelot.UnitTests.Configuration
result.AllowedScopes.ShouldBe(expected.AllowedScopes);
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;
using System.Collections.Generic;
using System.IO;
using System.Net.Http;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.TestHost;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Moq;
using Ocelot.Configuration.Builder;
using Ocelot.DownstreamRouteFinder;
using Ocelot.DownstreamRouteFinder.Finder;
using Ocelot.DownstreamRouteFinder.Middleware;
using Ocelot.DownstreamRouteFinder.UrlMatcher;
using Ocelot.Infrastructure.RequestData;
using Ocelot.Logging;
using Ocelot.Responses;
using TestStack.BDDfy;
using Xunit;
namespace Ocelot.UnitTests.DownstreamRouteFinder
namespace Ocelot.UnitTests.DownstreamRouteFinder
{
public class DownstreamRouteFinderMiddlewareTests : IDisposable
using System.Collections.Generic;
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;
using Moq;
using Ocelot.Configuration.Builder;
using Ocelot.DownstreamRouteFinder;
using Ocelot.DownstreamRouteFinder.Finder;
using Ocelot.DownstreamRouteFinder.Middleware;
using Ocelot.DownstreamRouteFinder.UrlMatcher;
using Ocelot.Logging;
using Ocelot.Responses;
using TestStack.BDDfy;
using Xunit;
public class DownstreamRouteFinderMiddlewareTests : ServerHostedMiddlewareTest
{
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 HttpResponseMessage _result;
public DownstreamRouteFinderMiddlewareTests()
{
_url = "http://localhost:51879";
_downstreamRouteFinder = new Mock<IDownstreamRouteFinder>();
_scopedRepository = new Mock<IRequestScopedDataRepository>();
var builder = new WebHostBuilder()
.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();
GivenTheTestServerIsConfigured();
}
[Fact]
@ -73,16 +41,17 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder
.BDDfy();
}
private void ThenTheScopedDataRepositoryIsCalledCorrectly()
protected override void GivenTheTestServerServicesAreConfigured(IServiceCollection services)
{
_scopedRepository
.Verify(x => x.Add("DownstreamRoute", _downstreamRoute.Data), Times.Once());
services.AddSingleton<IOcelotLoggerFactory, AspDotNetLoggerFactory>();
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)
@ -93,10 +62,10 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder
.ReturnsAsync(_downstreamRoute);
}
public void Dispose()
private void ThenTheScopedDataRepositoryIsCalledCorrectly()
{
_client.Dispose();
_server.Dispose();
ScopedRepository
.Verify(x => x.Add("DownstreamRoute", _downstreamRoute.Data), Times.Once());
}
}
}

View File

@ -1,73 +1,45 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Net.Http;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.TestHost;
using Microsoft.Extensions.DependencyInjection;
using Moq;
using Ocelot.Configuration.Builder;
using Ocelot.DownstreamRouteFinder;
using Ocelot.DownstreamRouteFinder.UrlMatcher;
using Ocelot.DownstreamUrlCreator;
using Ocelot.DownstreamUrlCreator.Middleware;
using Ocelot.DownstreamUrlCreator.UrlTemplateReplacer;
using Ocelot.Infrastructure.RequestData;
using Ocelot.Logging;
using Ocelot.Responses;
using Ocelot.Values;
using TestStack.BDDfy;
using Xunit;
using Shouldly;
namespace Ocelot.UnitTests.DownstreamUrlCreator
namespace Ocelot.UnitTests.DownstreamUrlCreator
{
public class DownstreamUrlCreatorMiddlewareTests : IDisposable
using System;
using System.Collections.Generic;
using System.Net.Http;
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;
using Moq;
using Ocelot.Configuration.Builder;
using Ocelot.DownstreamRouteFinder;
using Ocelot.DownstreamRouteFinder.UrlMatcher;
using Ocelot.DownstreamUrlCreator;
using Ocelot.DownstreamUrlCreator.Middleware;
using Ocelot.DownstreamUrlCreator.UrlTemplateReplacer;
using Ocelot.Infrastructure.RequestData;
using Ocelot.Logging;
using Ocelot.Responses;
using Ocelot.Values;
using TestStack.BDDfy;
using Xunit;
using Shouldly;
public class DownstreamUrlCreatorMiddlewareTests : ServerHostedMiddlewareTest
{
private readonly Mock<IDownstreamPathPlaceholderReplacer> _downstreamUrlTemplateVariableReplacer;
private readonly Mock<IRequestScopedDataRepository> _scopedRepository;
private readonly Mock<IUrlBuilder> _urlBuilder;
private readonly string _url;
private readonly TestServer _server;
private readonly HttpClient _client;
private Response<DownstreamRoute> _downstreamRoute;
private OkResponse<DownstreamPath> _downstreamPath;
private HttpRequestMessage _downstreamRequest;
private HttpResponseMessage _result;
public DownstreamUrlCreatorMiddlewareTests()
{
_url = "http://localhost:51879";
_downstreamUrlTemplateVariableReplacer = new Mock<IDownstreamPathPlaceholderReplacer>();
_scopedRepository = new Mock<IRequestScopedDataRepository>();
_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");
_scopedRepository
ScopedRepository
.Setup(sr => sr.Get<HttpRequestMessage>("DownstreamRequest"))
.Returns(new OkResponse<HttpRequestMessage>(_downstreamRequest));
_server = new TestServer(builder);
_client = _server.CreateClient();
GivenTheTestServerIsConfigured();
}
[Fact]
@ -88,10 +60,24 @@ namespace Ocelot.UnitTests.DownstreamUrlCreator
.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)
{
_downstreamRoute = new OkResponse<DownstreamRoute>(downstreamRoute);
_scopedRepository
ScopedRepository
.Setup(x => x.Get<DownstreamRoute>(It.IsAny<string>()))
.Returns(_downstreamRoute);
}
@ -109,20 +95,9 @@ namespace Ocelot.UnitTests.DownstreamUrlCreator
.Returns(_downstreamPath);
}
private void WhenICallTheMiddleware()
{
_result = _client.GetAsync(_url).Result;
}
private void ThenTheDownstreamRequestUriIs(string 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,69 +1,37 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Net.Http;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.TestHost;
using Microsoft.Extensions.DependencyInjection;
using Moq;
using Ocelot.Configuration;
using Ocelot.Configuration.Builder;
using Ocelot.DownstreamRouteFinder;
using Ocelot.DownstreamRouteFinder.UrlMatcher;
using Ocelot.Headers;
using Ocelot.Headers.Middleware;
using Ocelot.Infrastructure.RequestData;
using Ocelot.Logging;
using Ocelot.Responses;
using TestStack.BDDfy;
using Xunit;
namespace Ocelot.UnitTests.Headers
namespace Ocelot.UnitTests.Headers
{
public class HttpRequestHeadersBuilderMiddlewareTests : IDisposable
using System.Collections.Generic;
using System.Net.Http;
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;
using Moq;
using Ocelot.Configuration;
using Ocelot.Configuration.Builder;
using Ocelot.DownstreamRouteFinder;
using Ocelot.DownstreamRouteFinder.UrlMatcher;
using Ocelot.Headers;
using Ocelot.Headers.Middleware;
using Ocelot.Logging;
using Ocelot.Responses;
using TestStack.BDDfy;
using Xunit;
public class HttpRequestHeadersBuilderMiddlewareTests : ServerHostedMiddlewareTest
{
private readonly Mock<IRequestScopedDataRepository> _scopedRepository;
private readonly Mock<IAddHeadersToRequest> _addHeaders;
private readonly HttpRequestMessage _downstreamRequest;
private readonly string _url;
private readonly TestServer _server;
private readonly HttpClient _client;
private Response<DownstreamRoute> _downstreamRoute;
private HttpResponseMessage _result;
public HttpRequestHeadersBuilderMiddlewareTests()
{
_url = "http://localhost:51879";
_scopedRepository = new Mock<IRequestScopedDataRepository>();
_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();
_scopedRepository
ScopedRepository
.Setup(sr => sr.Get<HttpRequestMessage>("DownstreamRequest"))
.Returns(new OkResponse<HttpRequestMessage>(_downstreamRequest));
_server = new TestServer(builder);
_client = _server.CreateClient();
GivenTheTestServerIsConfigured();
}
[Fact]
@ -86,6 +54,27 @@ namespace Ocelot.UnitTests.Headers
.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()
{
_addHeaders
@ -104,24 +93,5 @@ namespace Ocelot.UnitTests.Headers
It.IsAny<IEnumerable<System.Security.Claims.Claim>>(),
_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 @@
using System.Collections.Generic;
using System.IO;
using System.Net.Http;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.TestHost;
using Microsoft.Extensions.DependencyInjection;
using Moq;
using Ocelot.Configuration.Builder;
using Ocelot.DownstreamRouteFinder;
using Ocelot.Errors;
using Ocelot.Infrastructure.RequestData;
using Ocelot.LoadBalancer.LoadBalancers;
using Ocelot.LoadBalancer.Middleware;
using Ocelot.Logging;
using Ocelot.Responses;
using Ocelot.Values;
using TestStack.BDDfy;
using Xunit;
using Shouldly;
namespace Ocelot.UnitTests.LoadBalancer
{
public class LoadBalancerMiddlewareTests
using System.Collections.Generic;
using System.Net.Http;
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;
using Moq;
using Ocelot.Configuration.Builder;
using Ocelot.DownstreamRouteFinder;
using Ocelot.Errors;
using Ocelot.LoadBalancer.LoadBalancers;
using Ocelot.LoadBalancer.Middleware;
using Ocelot.Logging;
using Ocelot.Responses;
using Ocelot.Values;
using Shouldly;
using TestStack.BDDfy;
using Xunit;
public class LoadBalancerMiddlewareTests : ServerHostedMiddlewareTest
{
private readonly Mock<ILoadBalancerHouse> _loadBalancerHouse;
private readonly Mock<IRequestScopedDataRepository> _scopedRepository;
private readonly Mock<ILoadBalancer> _loadBalancer;
private readonly string _url;
private readonly TestServer _server;
private readonly HttpClient _client;
private HttpResponseMessage _result;
private HostAndPort _hostAndPort;
private OkResponse<DownstreamRoute> _downstreamRoute;
private ErrorResponse<ILoadBalancer> _getLoadBalancerHouseError;
@ -37,35 +29,17 @@ namespace Ocelot.UnitTests.LoadBalancer
public LoadBalancerMiddlewareTests()
{
_url = "http://localhost:51879";
_loadBalancerHouse = new Mock<ILoadBalancerHouse>();
_scopedRepository = new Mock<IRequestScopedDataRepository>();
_loadBalancer = new Mock<ILoadBalancer>();
_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, "");
_scopedRepository
ScopedRepository
.Setup(sr => sr.Get<HttpRequestMessage>("DownstreamRequest"))
.Returns(new OkResponse<HttpRequestMessage>(_downstreamRequest));
_server = new TestServer(builder);
_client = _server.CreateClient();
GivenTheTestServerIsConfigured();
}
[Fact]
@ -118,6 +92,19 @@ namespace Ocelot.UnitTests.LoadBalancer
.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)
{
_downstreamRequest.RequestUri = new System.Uri(downstreamUrl);
@ -142,7 +129,7 @@ namespace Ocelot.UnitTests.LoadBalancer
private void GivenTheDownStreamRouteIs(DownstreamRoute downstreamRoute)
{
_downstreamRoute = new OkResponse<DownstreamRoute>(downstreamRoute);
_scopedRepository
ScopedRepository
.Setup(x => x.Get<DownstreamRoute>(It.IsAny<string>()))
.Returns(_downstreamRoute);
}
@ -154,7 +141,6 @@ namespace Ocelot.UnitTests.LoadBalancer
.Returns(new OkResponse<ILoadBalancer>(_loadBalancer.Object));
}
private void GivenTheLoadBalancerHouseReturnsAnError()
{
_getLoadBalancerHouseError = new ErrorResponse<ILoadBalancer>(new List<Ocelot.Errors.Error>()
@ -167,49 +153,36 @@ namespace Ocelot.UnitTests.LoadBalancer
.Returns(_getLoadBalancerHouseError);
}
private void WhenICallTheMiddleware()
{
_result = _client.GetAsync(_url).Result;
}
private void ThenAnErrorStatingLoadBalancerCouldNotBeFoundIsSetOnPipeline()
{
_scopedRepository
ScopedRepository
.Verify(x => x.Add("OcelotMiddlewareError", true), Times.Once);
_scopedRepository
ScopedRepository
.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);
_scopedRepository
ScopedRepository
.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);
_scopedRepository
ScopedRepository
.Verify(x => x.Add("OcelotMiddlewareErrors", _getHostAndPortError.Errors), Times.Once);
}
private void ThenTheDownstreamUrlIsReplacedWith(string expectedUri)
{
_downstreamRequest.RequestUri.OriginalString.ShouldBe(expectedUri);
}
public void Dispose()
{
_client.Dispose();
_server.Dispose();
}
}
}

View File

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

View File

@ -1,67 +1,37 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Net.Http;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.TestHost;
using Microsoft.Extensions.DependencyInjection;
using Moq;
using Ocelot.Configuration;
using Ocelot.Configuration.Builder;
using Ocelot.DownstreamRouteFinder;
using Ocelot.DownstreamRouteFinder.UrlMatcher;
using Ocelot.Infrastructure.RequestData;
using Ocelot.Logging;
using Ocelot.QueryStrings;
using Ocelot.QueryStrings.Middleware;
using Ocelot.Responses;
using TestStack.BDDfy;
using Xunit;
using System.Security.Claims;
namespace Ocelot.UnitTests.QueryStrings
namespace Ocelot.UnitTests.QueryStrings
{
public class QueryStringBuilderMiddlewareTests : IDisposable
using System.Collections.Generic;
using System.Net.Http;
using Microsoft.Extensions.DependencyInjection;
using Moq;
using Ocelot.Configuration;
using Ocelot.Configuration.Builder;
using Ocelot.DownstreamRouteFinder;
using Ocelot.DownstreamRouteFinder.UrlMatcher;
using Ocelot.Logging;
using Ocelot.QueryStrings;
using Ocelot.QueryStrings.Middleware;
using Ocelot.Responses;
using TestStack.BDDfy;
using Xunit;
using System.Security.Claims;
using Microsoft.AspNetCore.Builder;
public class QueryStringBuilderMiddlewareTests : ServerHostedMiddlewareTest
{
private readonly Mock<IRequestScopedDataRepository> _scopedRepository;
private readonly Mock<IAddQueriesToRequest> _addQueries;
private readonly string _url;
private readonly TestServer _server;
private readonly HttpClient _client;
private readonly HttpRequestMessage _downstreamRequest;
private Response<DownstreamRoute> _downstreamRoute;
private HttpResponseMessage _result;
public QueryStringBuilderMiddlewareTests()
{
_url = "http://localhost:51879";
_scopedRepository = new Mock<IRequestScopedDataRepository>();
_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();
_scopedRepository.Setup(sr => sr.Get<HttpRequestMessage>("DownstreamRequest"))
ScopedRepository.Setup(sr => sr.Get<HttpRequestMessage>("DownstreamRequest"))
.Returns(new OkResponse<HttpRequestMessage>(_downstreamRequest));
_server = new TestServer(builder);
_client = _server.CreateClient();
GivenTheTestServerIsConfigured();
}
[Fact]
@ -84,6 +54,19 @@ namespace Ocelot.UnitTests.QueryStrings
.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()
{
_addQueries
@ -103,23 +86,12 @@ namespace Ocelot.UnitTests.QueryStrings
_downstreamRequest), Times.Once);
}
private void WhenICallTheMiddleware()
{
_result = _client.GetAsync(_url).Result;
}
private void GivenTheDownStreamRouteIs(DownstreamRoute downstreamRoute)
{
_downstreamRoute = new OkResponse<DownstreamRoute>(downstreamRoute);
_scopedRepository
ScopedRepository
.Setup(x => x.Get<DownstreamRoute>(It.IsAny<string>()))
.Returns(_downstreamRoute);
}
public void Dispose()
{
_client.Dispose();
_server.Dispose();
}
}
}

View File

@ -1,71 +1,33 @@
using Microsoft.AspNetCore.Hosting;
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.Linq;
using System.Net.Http;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Ocelot.Logging;
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;
namespace Ocelot.UnitTests.RateLimit
namespace Ocelot.UnitTests.RateLimit
{
public class ClientRateLimitMiddlewareTests
using System.Collections.Generic;
using System.Net.Http;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using Moq;
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;
public class ClientRateLimitMiddlewareTests : ServerHostedMiddlewareTest
{
private readonly Mock<IRequestScopedDataRepository> _scopedRepository;
private readonly string _url;
private readonly TestServer _server;
private readonly HttpClient _client;
private OkResponse<DownstreamRoute> _downstreamRoute;
private int responseStatusCode;
public ClientRateLimitMiddlewareTests()
{
_url = "http://localhost:51879/api/ClientRateLimit";
_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();
GivenTheTestServerIsConfigured();
}
[Fact]
public void should_call_middleware_and_ratelimiting()
{
@ -98,11 +60,29 @@ namespace Ocelot.UnitTests.RateLimit
.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)
{
_downstreamRoute = new OkResponse<DownstreamRoute>(downstreamRoute);
_scopedRepository
ScopedRepository
.Setup(x => x.Get<DownstreamRoute>(It.IsAny<string>()))
.Returns(_downstreamRoute);
}
@ -110,28 +90,27 @@ namespace Ocelot.UnitTests.RateLimit
private void WhenICallTheMiddlewareMultipleTime(int times)
{
var clientId = "ocelotclient1";
// Act
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);
var response = _client.SendAsync(request);
var response = Client.SendAsync(request);
responseStatusCode = (int)response.Result.StatusCode;
}
}
private void WhenICallTheMiddlewareWithWhiteClient()
{
var clientId = "ocelotclient2";
// Act
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);
var response = _client.SendAsync(request);
var response = Client.SendAsync(request);
responseStatusCode = (int)response.Result.StatusCode;
}
}

View File

@ -1,63 +1,37 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Net.Http;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.TestHost;
using Microsoft.Extensions.DependencyInjection;
using Moq;
using Ocelot.Configuration.Builder;
using Ocelot.DownstreamRouteFinder;
using Ocelot.DownstreamRouteFinder.UrlMatcher;
using Ocelot.Infrastructure.RequestData;
using Ocelot.Logging;
using Ocelot.Request.Builder;
using Ocelot.Request.Middleware;
using Ocelot.Responses;
using TestStack.BDDfy;
using Xunit;
using Ocelot.Requester.QoS;
namespace Ocelot.UnitTests.Request
namespace Ocelot.UnitTests.Request
{
public class HttpRequestBuilderMiddlewareTests : IDisposable
using System.Collections.Generic;
using System.Net.Http;
using Microsoft.Extensions.DependencyInjection;
using Moq;
using Ocelot.Configuration.Builder;
using Ocelot.DownstreamRouteFinder;
using Ocelot.DownstreamRouteFinder.UrlMatcher;
using Ocelot.Infrastructure.RequestData;
using Ocelot.Logging;
using Ocelot.Request.Builder;
using Ocelot.Request.Middleware;
using Ocelot.Responses;
using TestStack.BDDfy;
using Xunit;
using Ocelot.Requester.QoS;
using Microsoft.AspNetCore.Builder;
public class HttpRequestBuilderMiddlewareTests : ServerHostedMiddlewareTest
{
private readonly Mock<IRequestCreator> _requestBuilder;
private readonly Mock<IRequestScopedDataRepository> _scopedRepository;
private readonly Mock<IQosProviderHouse> _qosProviderHouse;
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<string> _downstreamUrl;
private OkResponse<DownstreamRoute> _downstreamRoute;
public HttpRequestBuilderMiddlewareTests()
{
_url = "http://localhost:51879";
_qosProviderHouse = new Mock<IQosProviderHouse>();
_requestBuilder = new Mock<IRequestCreator>();
_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();
@ -65,8 +39,7 @@ namespace Ocelot.UnitTests.Request
.Setup(sr => sr.Get<HttpRequestMessage>("DownstreamRequest"))
.Returns(new OkResponse<HttpRequestMessage>(_downstreamRequest));
_server = new TestServer(builder);
_client = _server.CreateClient();
GivenTheTestServerIsConfigured();
}
[Fact]
@ -88,6 +61,28 @@ namespace Ocelot.UnitTests.Request
.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)
{
_qosProviderHouse
@ -117,24 +112,5 @@ namespace Ocelot.UnitTests.Request
_scopedRepository
.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,74 +1,40 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.TestHost;
using Microsoft.Extensions.DependencyInjection;
using Moq;
using Ocelot.Configuration.Builder;
using Ocelot.DownstreamRouteFinder;
using Ocelot.DownstreamRouteFinder.UrlMatcher;
using Ocelot.Infrastructure.RequestData;
using Ocelot.Logging;
using Ocelot.RequestId.Middleware;
using Ocelot.Responses;
using Shouldly;
using TestStack.BDDfy;
using Xunit;
namespace Ocelot.UnitTests.RequestId
namespace Ocelot.UnitTests.RequestId
{
public class RequestIdMiddlewareTests
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.DependencyInjection;
using Moq;
using Ocelot.Configuration.Builder;
using Ocelot.DownstreamRouteFinder;
using Ocelot.DownstreamRouteFinder.UrlMatcher;
using Ocelot.Logging;
using Ocelot.RequestId.Middleware;
using Ocelot.Responses;
using Shouldly;
using TestStack.BDDfy;
using Xunit;
public class RequestIdMiddlewareTests : ServerHostedMiddlewareTest
{
private readonly Mock<IRequestScopedDataRepository> _scopedRepository;
private readonly HttpRequestMessage _downstreamRequest;
private readonly string _url;
private readonly TestServer _server;
private readonly HttpClient _client;
private Response<DownstreamRoute> _downstreamRoute;
private HttpResponseMessage _result;
private string _value;
private string _key;
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();
_scopedRepository
ScopedRepository
.Setup(sr => sr.Get<HttpRequestMessage>("DownstreamRequest"))
.Returns(new OkResponse<HttpRequestMessage>(_downstreamRequest));
GivenTheTestServerIsConfigured();
}
[Fact]
@ -106,10 +72,28 @@ namespace Ocelot.UnitTests.RequestId
.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)
{
_downstreamRoute = new OkResponse<DownstreamRoute>(downstreamRoute);
_scopedRepository
ScopedRepository
.Setup(x => x.Get<DownstreamRoute>(It.IsAny<string>()))
.Returns(_downstreamRoute);
}
@ -118,28 +102,17 @@ namespace Ocelot.UnitTests.RequestId
{
_key = key;
_value = value;
_client.DefaultRequestHeaders.TryAddWithoutValidation(_key, _value);
}
private void WhenICallTheMiddleware()
{
_result = _client.GetAsync(_url).Result;
Client.DefaultRequestHeaders.TryAddWithoutValidation(_key, _value);
}
private void ThenTheTraceIdIsAnything()
{
_result.Headers.GetValues("LSRequestId").First().ShouldNotBeNullOrEmpty();
ResponseMessage.Headers.GetValues("LSRequestId").First().ShouldNotBeNullOrEmpty();
}
private void ThenTheTraceIdIs(string expected)
{
_result.Headers.GetValues("LSRequestId").First().ShouldBe(expected);
}
public void Dispose()
{
_client.Dispose();
_server.Dispose();
ResponseMessage.Headers.GetValues("LSRequestId").First().ShouldBe(expected);
}
}
}

View File

@ -1,62 +1,28 @@
using System;
using System.IO;
using System.Net;
using System.Net.Http;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.TestHost;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Moq;
using Ocelot.Infrastructure.RequestData;
using Ocelot.Logging;
using Ocelot.QueryStrings.Middleware;
using Ocelot.Requester;
using Ocelot.Requester.Middleware;
using Ocelot.Requester.QoS;
using Ocelot.Responder;
using Ocelot.Responses;
using TestStack.BDDfy;
using Xunit;
namespace Ocelot.UnitTests.Requester
namespace Ocelot.UnitTests.Requester
{
public class HttpRequesterMiddlewareTests : IDisposable
using System.Net.Http;
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;
using Moq;
using Ocelot.Logging;
using Ocelot.Requester;
using Ocelot.Requester.Middleware;
using Ocelot.Requester.QoS;
using Ocelot.Responses;
using TestStack.BDDfy;
using Xunit;
public class HttpRequesterMiddlewareTests : ServerHostedMiddlewareTest
{
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<Ocelot.Request.Request> _request;
public HttpRequesterMiddlewareTests()
{
_url = "http://localhost:51879";
_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);
_client = _server.CreateClient();
GivenTheTestServerIsConfigured();
}
[Fact]
@ -70,6 +36,27 @@ namespace Ocelot.UnitTests.Requester
.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)
{
_response = new OkResponse<HttpResponseMessage>(response);
@ -80,34 +67,15 @@ namespace Ocelot.UnitTests.Requester
private void GivenTheScopedRepoReturns()
{
_scopedRepository
ScopedRepository
.Setup(x => x.Add(It.IsAny<string>(), _response.Data))
.Returns(new OkResponse());
}
private void ThenTheScopedRepoIsCalledCorrectly()
{
_scopedRepository
ScopedRepository
.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.Collections.Generic;
using System.Net;
using Ocelot.Errors;
using Ocelot.Middleware;
using Ocelot.Requester;
using Ocelot.Responder;
using Shouldly;
using TestStack.BDDfy;
@ -21,47 +20,127 @@ namespace Ocelot.UnitTests.Responder
_codeMapper = new ErrorsToHttpStatusCodeMapper();
}
[Fact]
public void should_return_timeout()
[Theory]
[InlineData(OcelotErrorCode.UnauthenticatedError)]
public void should_return_unauthorized(OcelotErrorCode errorCode)
{
this.Given(x => x.GivenThereAreErrors(new List<Error>
{
new RequestTimedOutError(new Exception())
}))
.When(x => x.WhenIGetErrorStatusCode())
.Then(x => x.ThenTheResponseIsStatusCodeIs(503))
.BDDfy();
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)
{
ShouldMapErrorToStatusCode(errorCode, HttpStatusCode.Forbidden);
}
[Theory]
[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]
public void should_create_unauthenticated_response_code()
public void AuthenticationErrorsHaveHighestPriority()
{
this.Given(x => x.GivenThereAreErrors(new List<Error>
{
new UnauthenticatedError("no matter")
}))
.When(x => x.WhenIGetErrorStatusCode())
.Then(x => x.ThenTheResponseIsStatusCodeIs(401))
.BDDfy();
}
[Fact]
public void should_create_not_found_response_response_code()
{
this.Given(x => x.GivenThereAreErrors(new List<Error>
{
new AnyError()
}))
.When(x => x.WhenIGetErrorStatusCode())
.Then(x => x.ThenTheResponseIsStatusCodeIs(404))
.BDDfy();
}
class AnyError : Error
{
public AnyError() : base("blahh", OcelotErrorCode.UnknownError)
var errors = new List<OcelotErrorCode>
{
OcelotErrorCode.CannotAddDataError,
OcelotErrorCode.CannotFindClaimError,
OcelotErrorCode.UnauthenticatedError,
OcelotErrorCode.RequestTimedOutError,
};
ShouldMapErrorsToStatusCode(errors, HttpStatusCode.Unauthorized);
}
[Fact]
public void AuthorisationErrorsHaveSecondHighestPriority()
{
var errors = new List<OcelotErrorCode>
{
OcelotErrorCode.CannotAddDataError,
OcelotErrorCode.CannotFindClaimError,
OcelotErrorCode.RequestTimedOutError
};
ShouldMapErrorsToStatusCode(errors, HttpStatusCode.Forbidden);
}
[Fact]
public void ServiceUnavailableErrorsHaveThirdHighestPriority()
{
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)
@ -77,6 +156,11 @@ namespace Ocelot.UnitTests.Responder
private void ThenTheResponseIsStatusCodeIs(int expectedCode)
{
_result.ShouldBe(expectedCode);
}
}
private void ThenTheResponseIsStatusCodeIs(HttpStatusCode expectedCode)
{
_result.ShouldBe((int)expectedCode);
}
}
}

View File

@ -1,60 +1,28 @@
using System;
using System.IO;
using System.Net.Http;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.TestHost;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Moq;
using Ocelot.Infrastructure.RequestData;
using Ocelot.Logging;
using Ocelot.Responder;
using Ocelot.Responder.Middleware;
using Ocelot.Responses;
using TestStack.BDDfy;
using Xunit;
namespace Ocelot.UnitTests.Responder
namespace Ocelot.UnitTests.Responder
{
public class ResponderMiddlewareTests : IDisposable
using System.Net.Http;
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;
using Moq;
using Ocelot.Logging;
using Ocelot.Responder;
using Ocelot.Responder.Middleware;
using Ocelot.Responses;
using TestStack.BDDfy;
using Xunit;
public class ResponderMiddlewareTests : ServerHostedMiddlewareTest
{
private readonly Mock<IHttpResponder> _responder;
private readonly Mock<IRequestScopedDataRepository> _scopedRepository;
private readonly Mock<IErrorsToHttpStatusCodeMapper> _codeMapper;
private readonly string _url;
private readonly TestServer _server;
private readonly HttpClient _client;
private HttpResponseMessage _result;
private OkResponse<HttpResponseMessage> _response;
public ResponderMiddlewareTests()
{
_url = "http://localhost:51879";
_responder = new Mock<IHttpResponder>();
_scopedRepository = new Mock<IRequestScopedDataRepository>();
_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);
_client = _server.CreateClient();
GivenTheTestServerIsConfigured();
}
[Fact]
@ -67,9 +35,31 @@ namespace Ocelot.UnitTests.Responder
.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()
{
_scopedRepository
ScopedRepository
.Setup(x => x.Get<bool>(It.IsAny<string>()))
.Returns(new OkResponse<bool>(false));
}
@ -78,24 +68,5 @@ namespace Ocelot.UnitTests.Responder
{
//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();
}
[Fact]
public void should_lookup_service()
{
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" },
}
}
}
}
};
}
}
}