mirror of
https://github.com/nsnail/Ocelot.git
synced 2025-04-23 00:32:50 +08:00
commit
5fb54e8032
@ -107,7 +107,7 @@ Instead of adding the configuration directly e.g. AddJsonFile("ocelot.json") you
|
||||
.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath)
|
||||
.AddJsonFile("appsettings.json", true, true)
|
||||
.AddJsonFile($"appsettings.{hostingContext.HostingEnvironment.EnvironmentName}.json", true, true)
|
||||
.AddOcelot()
|
||||
.AddOcelot(hostingContext.HostingEnvironment)
|
||||
.AddEnvironmentVariables();
|
||||
})
|
||||
|
||||
@ -117,6 +117,22 @@ The way Ocelot merges the files is basically load them, loop over them, add any
|
||||
|
||||
At the moment there is no validation at this stage it only happens when Ocelot validates the final merged configuration. This is something to be aware of when you are investigating problems. I would advise always checking what is in ocelot.json if you have any problems.
|
||||
|
||||
You can also give Ocelot a specific path to look in for the configuration files like below.
|
||||
|
||||
.. code-block:: csharp
|
||||
|
||||
.ConfigureAppConfiguration((hostingContext, config) =>
|
||||
{
|
||||
config
|
||||
.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath)
|
||||
.AddJsonFile("appsettings.json", true, true)
|
||||
.AddJsonFile($"appsettings.{hostingContext.HostingEnvironment.EnvironmentName}.json", true, true)
|
||||
.AddOcelot("/foo/bar", hostingContext.HostingEnvironment)
|
||||
.AddEnvironmentVariables();
|
||||
})
|
||||
|
||||
Ocelot needs the HostingEnvironment so it know's to exclude anything environment specific from the algorithm.
|
||||
|
||||
Store configuration in consul
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
|
@ -81,6 +81,7 @@ Placeholders
|
||||
|
||||
Ocelot allows placeholders that can be used in header transformation.
|
||||
|
||||
{RemoteIpAddress} - This will find the clients IP address using _httpContextAccessor.HttpContext.Connection.RemoteIpAddress.ToString() so you will get back some IP.
|
||||
{BaseUrl} - This will use Ocelot's base url e.g. http://localhost:5000 as its value.
|
||||
{DownstreamBaseUrl} - This will use the downstream services base url e.g. http://localhost:5000 as its value. This only works for DownstreamHeaderTransform at the moment.
|
||||
{TraceId} - This will use the Butterfly APM Trace Id. This only works for DownstreamHeaderTransform at the moment.
|
||||
@ -120,6 +121,17 @@ finally if you are using a load balancer with Ocelot you will get multiple downs
|
||||
"AllowAutoRedirect": false,
|
||||
},
|
||||
|
||||
X-Forwarded-For
|
||||
^^^^^^^^^^^^^^^
|
||||
|
||||
An example of using {RemoteIpAddress} placeholder...
|
||||
|
||||
.. code-block:: json
|
||||
|
||||
"UpstreamHeaderTransform": {
|
||||
"X-Forwarded-For": "{RemoteIpAddress}"
|
||||
}
|
||||
|
||||
Future
|
||||
^^^^^^
|
||||
|
||||
|
@ -22,7 +22,7 @@ namespace Ocelot.Authentication.Middleware
|
||||
|
||||
public async Task Invoke(DownstreamContext context)
|
||||
{
|
||||
if (IsAuthenticatedRoute(context.DownstreamReRoute))
|
||||
if (context.HttpContext.Request.Method.ToUpper() != "OPTIONS" && IsAuthenticatedRoute(context.DownstreamReRoute))
|
||||
{
|
||||
Logger.LogInformation($"{context.HttpContext.Request.Path} is an authenticated route. {MiddlewareName} checking if client is authenticated");
|
||||
|
||||
|
@ -82,7 +82,7 @@
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.LogInformation($"{context.DownstreamReRoute.DownstreamDownstreamPathTemplate.Value} route does not require user to be authorised");
|
||||
Logger.LogInformation($"{context.DownstreamReRoute.DownstreamPathTemplate.Value} route does not require user to be authorised");
|
||||
await _next.Invoke(context);
|
||||
}
|
||||
}
|
||||
|
@ -9,14 +9,15 @@ namespace Ocelot.Cache
|
||||
HttpStatusCode statusCode,
|
||||
Dictionary<string, IEnumerable<string>> headers,
|
||||
string body,
|
||||
Dictionary<string, IEnumerable<string>> contentHeaders
|
||||
|
||||
Dictionary<string, IEnumerable<string>> contentHeaders,
|
||||
string reasonPhrase
|
||||
)
|
||||
{
|
||||
StatusCode = statusCode;
|
||||
Headers = headers ?? new Dictionary<string, IEnumerable<string>>();
|
||||
ContentHeaders = contentHeaders ?? new Dictionary<string, IEnumerable<string>>();
|
||||
Body = body ?? "";
|
||||
ReasonPhrase = reasonPhrase;
|
||||
}
|
||||
|
||||
public HttpStatusCode StatusCode { get; private set; }
|
||||
@ -26,5 +27,7 @@ namespace Ocelot.Cache
|
||||
public Dictionary<string, IEnumerable<string>> ContentHeaders { get; private set; }
|
||||
|
||||
public string Body { get; private set; }
|
||||
|
||||
public string ReasonPhrase { get; private set; }
|
||||
}
|
||||
}
|
||||
|
@ -87,7 +87,7 @@
|
||||
streamContent.Headers.TryAddWithoutValidation(header.Key, header.Value);
|
||||
}
|
||||
|
||||
return new DownstreamResponse(streamContent, cached.StatusCode, cached.Headers.ToList());
|
||||
return new DownstreamResponse(streamContent, cached.StatusCode, cached.Headers.ToList(), cached.ReasonPhrase);
|
||||
}
|
||||
|
||||
internal async Task<CachedResponse> CreateCachedResponse(DownstreamResponse response)
|
||||
@ -109,7 +109,7 @@
|
||||
|
||||
var contentHeaders = response?.Content?.Headers.ToDictionary(v => v.Key, v => v.Value);
|
||||
|
||||
var cached = new CachedResponse(statusCode, headers, body, contentHeaders);
|
||||
var cached = new CachedResponse(statusCode, headers, body, contentHeaders, response.ReasonPhrase);
|
||||
return cached;
|
||||
}
|
||||
}
|
||||
|
@ -3,11 +3,11 @@ using Ocelot.Middleware.Pipeline;
|
||||
|
||||
namespace Ocelot.Claims.Middleware
|
||||
{
|
||||
public static class ClaimsBuilderMiddlewareExtensions
|
||||
public static class ClaimsToClaimsMiddlewareExtensions
|
||||
{
|
||||
public static IOcelotPipelineBuilder UseClaimsBuilderMiddleware(this IOcelotPipelineBuilder builder)
|
||||
public static IOcelotPipelineBuilder UseClaimsToClaimsMiddleware(this IOcelotPipelineBuilder builder)
|
||||
{
|
||||
return builder.UseMiddleware<ClaimsBuilderMiddleware>();
|
||||
return builder.UseMiddleware<ClaimsToClaimsMiddleware>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -8,15 +8,15 @@ using Ocelot.Middleware;
|
||||
|
||||
namespace Ocelot.Claims.Middleware
|
||||
{
|
||||
public class ClaimsBuilderMiddleware : OcelotMiddleware
|
||||
public class ClaimsToClaimsMiddleware : OcelotMiddleware
|
||||
{
|
||||
private readonly OcelotRequestDelegate _next;
|
||||
private readonly IAddClaimsToRequest _addClaimsToRequest;
|
||||
|
||||
public ClaimsBuilderMiddleware(OcelotRequestDelegate next,
|
||||
public ClaimsToClaimsMiddleware(OcelotRequestDelegate next,
|
||||
IOcelotLoggerFactory loggerFactory,
|
||||
IAddClaimsToRequest addClaimsToRequest)
|
||||
:base(loggerFactory.CreateLogger<ClaimsBuilderMiddleware>())
|
||||
:base(loggerFactory.CreateLogger<ClaimsToClaimsMiddleware>())
|
||||
{
|
||||
_next = next;
|
||||
_addClaimsToRequest = addClaimsToRequest;
|
@ -33,13 +33,12 @@ namespace Ocelot.Configuration.Builder
|
||||
private List<HeaderFindAndReplace> _upstreamHeaderFindAndReplace;
|
||||
private List<HeaderFindAndReplace> _downstreamHeaderFindAndReplace;
|
||||
private readonly List<DownstreamHostAndPort> _downstreamAddresses;
|
||||
private string _upstreamHost;
|
||||
private string _key;
|
||||
private List<string> _delegatingHandlers;
|
||||
private List<AddHeader> _addHeadersToDownstream;
|
||||
private List<AddHeader> _addHeadersToUpstream;
|
||||
private bool _dangerousAcceptAnyServerCertificateValidator;
|
||||
|
||||
private SecurityOptions _securityOptions;
|
||||
public DownstreamReRouteBuilder()
|
||||
{
|
||||
_downstreamAddresses = new List<DownstreamHostAndPort>();
|
||||
@ -54,12 +53,6 @@ namespace Ocelot.Configuration.Builder
|
||||
return this;
|
||||
}
|
||||
|
||||
public DownstreamReRouteBuilder WithUpstreamHost(string upstreamAddresses)
|
||||
{
|
||||
_upstreamHost = upstreamAddresses;
|
||||
return this;
|
||||
}
|
||||
|
||||
public DownstreamReRouteBuilder WithLoadBalancerOptions(LoadBalancerOptions loadBalancerOptions)
|
||||
{
|
||||
_loadBalancerOptions = loadBalancerOptions;
|
||||
@ -234,6 +227,12 @@ namespace Ocelot.Configuration.Builder
|
||||
return this;
|
||||
}
|
||||
|
||||
public DownstreamReRouteBuilder WithSecurityOptions(SecurityOptions securityOptions)
|
||||
{
|
||||
_securityOptions = securityOptions;
|
||||
return this;
|
||||
}
|
||||
|
||||
public DownstreamReRoute Build()
|
||||
{
|
||||
return new DownstreamReRoute(
|
||||
@ -265,7 +264,8 @@ namespace Ocelot.Configuration.Builder
|
||||
_delegatingHandlers,
|
||||
_addHeadersToDownstream,
|
||||
_addHeadersToUpstream,
|
||||
_dangerousAcceptAnyServerCertificateValidator);
|
||||
_dangerousAcceptAnyServerCertificateValidator,
|
||||
_securityOptions);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -6,6 +6,7 @@ namespace Ocelot.Configuration.Builder
|
||||
private bool _isAuthorised;
|
||||
private bool _isCached;
|
||||
private bool _enableRateLimiting;
|
||||
private bool _useServiceDiscovery;
|
||||
|
||||
public ReRouteOptionsBuilder WithIsCached(bool isCached)
|
||||
{
|
||||
@ -31,9 +32,15 @@ namespace Ocelot.Configuration.Builder
|
||||
return this;
|
||||
}
|
||||
|
||||
public ReRouteOptionsBuilder WithUseServiceDiscovery(bool useServiceDiscovery)
|
||||
{
|
||||
_useServiceDiscovery = useServiceDiscovery;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ReRouteOptions Build()
|
||||
{
|
||||
return new ReRouteOptions(_isAuthenticated, _isAuthorised, _isCached, _enableRateLimiting);
|
||||
return new ReRouteOptions(_isAuthenticated, _isAuthorised, _isCached, _enableRateLimiting, _useServiceDiscovery);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
50
src/Ocelot/Configuration/Creator/AggregatesCreator.cs
Normal file
50
src/Ocelot/Configuration/Creator/AggregatesCreator.cs
Normal file
@ -0,0 +1,50 @@
|
||||
namespace Ocelot.Configuration.Creator
|
||||
{
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Builder;
|
||||
using File;
|
||||
|
||||
public class AggregatesCreator : IAggregatesCreator
|
||||
{
|
||||
private readonly IUpstreamTemplatePatternCreator _creator;
|
||||
|
||||
public AggregatesCreator(IUpstreamTemplatePatternCreator creator)
|
||||
{
|
||||
_creator = creator;
|
||||
}
|
||||
|
||||
public List<ReRoute> Create(FileConfiguration fileConfiguration, List<ReRoute> reRoutes)
|
||||
{
|
||||
return fileConfiguration.Aggregates
|
||||
.Select(aggregate => SetUpAggregateReRoute(reRoutes, aggregate, fileConfiguration.GlobalConfiguration))
|
||||
.Where(aggregate => aggregate != null)
|
||||
.ToList();
|
||||
}
|
||||
|
||||
private ReRoute SetUpAggregateReRoute(IEnumerable<ReRoute> reRoutes, FileAggregateReRoute aggregateReRoute, FileGlobalConfiguration globalConfiguration)
|
||||
{
|
||||
var applicableReRoutes = reRoutes
|
||||
.SelectMany(x => x.DownstreamReRoute)
|
||||
.Where(r => aggregateReRoute.ReRouteKeys.Contains(r.Key))
|
||||
.ToList();
|
||||
|
||||
if (applicableReRoutes.Count != aggregateReRoute.ReRouteKeys.Count)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var upstreamTemplatePattern = _creator.Create(aggregateReRoute);
|
||||
|
||||
var reRoute = new ReRouteBuilder()
|
||||
.WithUpstreamHttpMethod(aggregateReRoute.UpstreamHttpMethod)
|
||||
.WithUpstreamPathTemplate(upstreamTemplatePattern)
|
||||
.WithDownstreamReRoutes(applicableReRoutes)
|
||||
.WithUpstreamHost(aggregateReRoute.UpstreamHost)
|
||||
.WithAggregator(aggregateReRoute.Aggregator)
|
||||
.Build();
|
||||
|
||||
return reRoute;
|
||||
}
|
||||
}
|
||||
}
|
55
src/Ocelot/Configuration/Creator/ConfigurationCreator.cs
Normal file
55
src/Ocelot/Configuration/Creator/ConfigurationCreator.cs
Normal file
@ -0,0 +1,55 @@
|
||||
namespace Ocelot.Configuration.Creator
|
||||
{
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using File;
|
||||
using DependencyInjection;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
public class ConfigurationCreator : IConfigurationCreator
|
||||
{
|
||||
private readonly IServiceProviderConfigurationCreator _serviceProviderConfigCreator;
|
||||
private readonly IQoSOptionsCreator _qosOptionsCreator;
|
||||
private readonly IHttpHandlerOptionsCreator _httpHandlerOptionsCreator;
|
||||
private readonly IAdministrationPath _adminPath;
|
||||
private readonly ILoadBalancerOptionsCreator _loadBalancerOptionsCreator;
|
||||
|
||||
public ConfigurationCreator(
|
||||
IServiceProviderConfigurationCreator serviceProviderConfigCreator,
|
||||
IQoSOptionsCreator qosOptionsCreator,
|
||||
IHttpHandlerOptionsCreator httpHandlerOptionsCreator,
|
||||
IServiceProvider serviceProvider,
|
||||
ILoadBalancerOptionsCreator loadBalancerOptionsCreator
|
||||
)
|
||||
{
|
||||
_adminPath = serviceProvider.GetService<IAdministrationPath>();
|
||||
_loadBalancerOptionsCreator = loadBalancerOptionsCreator;
|
||||
_serviceProviderConfigCreator = serviceProviderConfigCreator;
|
||||
_qosOptionsCreator = qosOptionsCreator;
|
||||
_httpHandlerOptionsCreator = httpHandlerOptionsCreator;
|
||||
}
|
||||
|
||||
public InternalConfiguration Create(FileConfiguration fileConfiguration, List<ReRoute> reRoutes)
|
||||
{
|
||||
var serviceProviderConfiguration = _serviceProviderConfigCreator.Create(fileConfiguration.GlobalConfiguration);
|
||||
|
||||
var lbOptions = _loadBalancerOptionsCreator.Create(fileConfiguration.GlobalConfiguration.LoadBalancerOptions);
|
||||
|
||||
var qosOptions = _qosOptionsCreator.Create(fileConfiguration.GlobalConfiguration.QoSOptions);
|
||||
|
||||
var httpHandlerOptions = _httpHandlerOptionsCreator.Create(fileConfiguration.GlobalConfiguration.HttpHandlerOptions);
|
||||
|
||||
var adminPath = _adminPath != null ? _adminPath.Path : null;
|
||||
|
||||
return new InternalConfiguration(reRoutes,
|
||||
adminPath,
|
||||
serviceProviderConfiguration,
|
||||
fileConfiguration.GlobalConfiguration.RequestIdKey,
|
||||
lbOptions,
|
||||
fileConfiguration.GlobalConfiguration.DownstreamScheme,
|
||||
qosOptions,
|
||||
httpHandlerOptions
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
42
src/Ocelot/Configuration/Creator/DynamicsCreator.cs
Normal file
42
src/Ocelot/Configuration/Creator/DynamicsCreator.cs
Normal file
@ -0,0 +1,42 @@
|
||||
namespace Ocelot.Configuration.Creator
|
||||
{
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Builder;
|
||||
using File;
|
||||
|
||||
public class DynamicsCreator : IDynamicsCreator
|
||||
{
|
||||
private readonly IRateLimitOptionsCreator _rateLimitOptionsCreator;
|
||||
|
||||
public DynamicsCreator(IRateLimitOptionsCreator rateLimitOptionsCreator)
|
||||
{
|
||||
_rateLimitOptionsCreator = rateLimitOptionsCreator;
|
||||
}
|
||||
|
||||
public List<ReRoute> Create(FileConfiguration fileConfiguration)
|
||||
{
|
||||
return fileConfiguration.DynamicReRoutes
|
||||
.Select(dynamic => SetUpDynamicReRoute(dynamic, fileConfiguration.GlobalConfiguration))
|
||||
.ToList();
|
||||
}
|
||||
|
||||
private ReRoute SetUpDynamicReRoute(FileDynamicReRoute fileDynamicReRoute, FileGlobalConfiguration globalConfiguration)
|
||||
{
|
||||
var rateLimitOption = _rateLimitOptionsCreator
|
||||
.Create(fileDynamicReRoute.RateLimitRule, globalConfiguration);
|
||||
|
||||
var downstreamReRoute = new DownstreamReRouteBuilder()
|
||||
.WithEnableRateLimiting(rateLimitOption.EnableRateLimiting)
|
||||
.WithRateLimitOptions(rateLimitOption)
|
||||
.WithServiceName(fileDynamicReRoute.ServiceName)
|
||||
.Build();
|
||||
|
||||
var reRoute = new ReRouteBuilder()
|
||||
.WithDownstreamReRoute(downstreamReRoute)
|
||||
.Build();
|
||||
|
||||
return reRoute;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,84 +1,35 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Ocelot.Cache;
|
||||
using Ocelot.Configuration.Builder;
|
||||
using Ocelot.Configuration.File;
|
||||
using Ocelot.Configuration.Validator;
|
||||
using Ocelot.DependencyInjection;
|
||||
using Ocelot.Logging;
|
||||
using Ocelot.Responses;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace Ocelot.Configuration.Creator
|
||||
{
|
||||
using LoadBalancer.LoadBalancers;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using File;
|
||||
using Validator;
|
||||
using Responses;
|
||||
|
||||
/// <summary>
|
||||
/// Register as singleton
|
||||
/// </summary>
|
||||
public class FileInternalConfigurationCreator : IInternalConfigurationCreator
|
||||
{
|
||||
private readonly IConfigurationValidator _configurationValidator;
|
||||
private readonly IOcelotLogger _logger;
|
||||
private readonly IClaimsToThingCreator _claimsToThingCreator;
|
||||
private readonly IAuthenticationOptionsCreator _authOptionsCreator;
|
||||
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;
|
||||
private readonly IHttpHandlerOptionsCreator _httpHandlerOptionsCreator;
|
||||
private readonly IAdministrationPath _adminPath;
|
||||
private readonly IHeaderFindAndReplaceCreator _headerFAndRCreator;
|
||||
private readonly IDownstreamAddressesCreator _downstreamAddressesCreator;
|
||||
private readonly IConfigurationCreator _configCreator;
|
||||
private readonly IDynamicsCreator _dynamicsCreator;
|
||||
private readonly IReRoutesCreator _reRoutesCreator;
|
||||
private readonly IAggregatesCreator _aggregatesCreator;
|
||||
|
||||
public FileInternalConfigurationCreator(
|
||||
IConfigurationValidator configurationValidator,
|
||||
IOcelotLoggerFactory loggerFactory,
|
||||
IClaimsToThingCreator claimsToThingCreator,
|
||||
IAuthenticationOptionsCreator authOptionsCreator,
|
||||
IUpstreamTemplatePatternCreator upstreamTemplatePatternCreator,
|
||||
IRequestIdKeyCreator requestIdKeyCreator,
|
||||
IServiceProviderConfigurationCreator serviceProviderConfigCreator,
|
||||
IQoSOptionsCreator qosOptionsCreator,
|
||||
IReRouteOptionsCreator fileReRouteOptionsCreator,
|
||||
IRateLimitOptionsCreator rateLimitOptionsCreator,
|
||||
IRegionCreator regionCreator,
|
||||
IHttpHandlerOptionsCreator httpHandlerOptionsCreator,
|
||||
IServiceProvider serviceProvider,
|
||||
IHeaderFindAndReplaceCreator headerFAndRCreator,
|
||||
IDownstreamAddressesCreator downstreamAddressesCreator
|
||||
IReRoutesCreator reRoutesCreator,
|
||||
IAggregatesCreator aggregatesCreator,
|
||||
IDynamicsCreator dynamicsCreator,
|
||||
IConfigurationCreator configCreator
|
||||
)
|
||||
{
|
||||
_downstreamAddressesCreator = downstreamAddressesCreator;
|
||||
_headerFAndRCreator = headerFAndRCreator;
|
||||
_adminPath = serviceProvider.GetService<IAdministrationPath>();
|
||||
_regionCreator = regionCreator;
|
||||
_rateLimitOptionsCreator = rateLimitOptionsCreator;
|
||||
_requestIdKeyCreator = requestIdKeyCreator;
|
||||
_upstreamTemplatePatternCreator = upstreamTemplatePatternCreator;
|
||||
_authOptionsCreator = authOptionsCreator;
|
||||
_configCreator = configCreator;
|
||||
_dynamicsCreator = dynamicsCreator;
|
||||
_aggregatesCreator = aggregatesCreator;
|
||||
_reRoutesCreator = reRoutesCreator;
|
||||
_configurationValidator = configurationValidator;
|
||||
_logger = loggerFactory.CreateLogger<FileInternalConfigurationCreator>();
|
||||
_claimsToThingCreator = claimsToThingCreator;
|
||||
_serviceProviderConfigCreator = serviceProviderConfigCreator;
|
||||
_qosOptionsCreator = qosOptionsCreator;
|
||||
_fileReRouteOptionsCreator = fileReRouteOptionsCreator;
|
||||
_httpHandlerOptionsCreator = httpHandlerOptionsCreator;
|
||||
}
|
||||
|
||||
public async Task<Response<IInternalConfiguration>> Create(FileConfiguration fileConfiguration)
|
||||
{
|
||||
var config = await SetUpConfiguration(fileConfiguration);
|
||||
return config;
|
||||
}
|
||||
|
||||
private async Task<Response<IInternalConfiguration>> SetUpConfiguration(FileConfiguration fileConfiguration)
|
||||
{
|
||||
var response = await _configurationValidator.IsValid(fileConfiguration);
|
||||
|
||||
@ -87,197 +38,20 @@ namespace Ocelot.Configuration.Creator
|
||||
return new ErrorResponse<IInternalConfiguration>(response.Data.Errors);
|
||||
}
|
||||
|
||||
var reRoutes = new List<ReRoute>();
|
||||
var reRoutes = _reRoutesCreator.Create(fileConfiguration);
|
||||
|
||||
foreach (var reRoute in fileConfiguration.ReRoutes)
|
||||
{
|
||||
var downstreamReRoute = SetUpDownstreamReRoute(reRoute, fileConfiguration.GlobalConfiguration);
|
||||
var aggregates = _aggregatesCreator.Create(fileConfiguration, reRoutes);
|
||||
|
||||
var ocelotReRoute = SetUpReRoute(reRoute, downstreamReRoute);
|
||||
var dynamicReRoute = _dynamicsCreator.Create(fileConfiguration);
|
||||
|
||||
reRoutes.Add(ocelotReRoute);
|
||||
}
|
||||
var mergedReRoutes = reRoutes
|
||||
.Union(aggregates)
|
||||
.Union(dynamicReRoute)
|
||||
.ToList();
|
||||
|
||||
foreach (var aggregate in fileConfiguration.Aggregates)
|
||||
{
|
||||
var ocelotReRoute = SetUpAggregateReRoute(reRoutes, aggregate, fileConfiguration.GlobalConfiguration);
|
||||
reRoutes.Add(ocelotReRoute);
|
||||
}
|
||||
|
||||
foreach(var fileDynamicReRoute in fileConfiguration.DynamicReRoutes)
|
||||
{
|
||||
var reRoute = SetUpDynamicReRoute(fileDynamicReRoute, fileConfiguration.GlobalConfiguration);
|
||||
reRoutes.Add(reRoute);
|
||||
}
|
||||
|
||||
var serviceProviderConfiguration = _serviceProviderConfigCreator.Create(fileConfiguration.GlobalConfiguration);
|
||||
|
||||
var lbOptions = CreateLoadBalancerOptions(fileConfiguration.GlobalConfiguration.LoadBalancerOptions);
|
||||
|
||||
var qosOptions = _qosOptionsCreator.Create(fileConfiguration.GlobalConfiguration.QoSOptions);
|
||||
|
||||
var httpHandlerOptions = _httpHandlerOptionsCreator.Create(fileConfiguration.GlobalConfiguration.HttpHandlerOptions);
|
||||
|
||||
var adminPath = _adminPath != null ? _adminPath.Path : null;
|
||||
|
||||
var config = new InternalConfiguration(reRoutes,
|
||||
adminPath,
|
||||
serviceProviderConfiguration,
|
||||
fileConfiguration.GlobalConfiguration.RequestIdKey,
|
||||
lbOptions,
|
||||
fileConfiguration.GlobalConfiguration.DownstreamScheme,
|
||||
qosOptions,
|
||||
httpHandlerOptions
|
||||
);
|
||||
var config = _configCreator.Create(fileConfiguration, mergedReRoutes);
|
||||
|
||||
return new OkResponse<IInternalConfiguration>(config);
|
||||
}
|
||||
|
||||
private ReRoute SetUpDynamicReRoute(FileDynamicReRoute fileDynamicReRoute, FileGlobalConfiguration globalConfiguration)
|
||||
{
|
||||
var rateLimitOption = _rateLimitOptionsCreator.Create(fileDynamicReRoute.RateLimitRule, globalConfiguration);
|
||||
|
||||
var downstreamReRoute = new DownstreamReRouteBuilder()
|
||||
.WithEnableRateLimiting(true)
|
||||
.WithRateLimitOptions(rateLimitOption)
|
||||
.WithServiceName(fileDynamicReRoute.ServiceName)
|
||||
.Build();
|
||||
|
||||
var reRoute = new ReRouteBuilder()
|
||||
.WithDownstreamReRoute(downstreamReRoute)
|
||||
.Build();
|
||||
|
||||
return reRoute;
|
||||
}
|
||||
|
||||
private ReRoute SetUpAggregateReRoute(List<ReRoute> reRoutes, FileAggregateReRoute aggregateReRoute, FileGlobalConfiguration globalConfiguration)
|
||||
{
|
||||
var applicableReRoutes = reRoutes
|
||||
.SelectMany(x => x.DownstreamReRoute)
|
||||
.Where(r => aggregateReRoute.ReRouteKeys.Contains(r.Key))
|
||||
.ToList();
|
||||
|
||||
if(applicableReRoutes.Count != aggregateReRoute.ReRouteKeys.Count)
|
||||
{
|
||||
//todo - log or throw or return error whatever?
|
||||
}
|
||||
|
||||
//make another re route out of these
|
||||
var upstreamTemplatePattern = _upstreamTemplatePatternCreator.Create(aggregateReRoute);
|
||||
|
||||
var reRoute = new ReRouteBuilder()
|
||||
.WithUpstreamHttpMethod(aggregateReRoute.UpstreamHttpMethod)
|
||||
.WithUpstreamPathTemplate(upstreamTemplatePattern)
|
||||
.WithDownstreamReRoutes(applicableReRoutes)
|
||||
.WithUpstreamHost(aggregateReRoute.UpstreamHost)
|
||||
.WithAggregator(aggregateReRoute.Aggregator)
|
||||
.Build();
|
||||
|
||||
return reRoute;
|
||||
}
|
||||
|
||||
private ReRoute SetUpReRoute(FileReRoute fileReRoute, DownstreamReRoute downstreamReRoutes)
|
||||
{
|
||||
var upstreamTemplatePattern = _upstreamTemplatePatternCreator.Create(fileReRoute);
|
||||
|
||||
var reRoute = new ReRouteBuilder()
|
||||
.WithUpstreamHttpMethod(fileReRoute.UpstreamHttpMethod)
|
||||
.WithUpstreamPathTemplate(upstreamTemplatePattern)
|
||||
.WithDownstreamReRoute(downstreamReRoutes)
|
||||
.WithUpstreamHost(fileReRoute.UpstreamHost)
|
||||
.Build();
|
||||
|
||||
return reRoute;
|
||||
}
|
||||
|
||||
private DownstreamReRoute SetUpDownstreamReRoute(FileReRoute fileReRoute, FileGlobalConfiguration globalConfiguration)
|
||||
{
|
||||
var fileReRouteOptions = _fileReRouteOptionsCreator.Create(fileReRoute);
|
||||
|
||||
var requestIdKey = _requestIdKeyCreator.Create(fileReRoute, globalConfiguration);
|
||||
|
||||
var reRouteKey = CreateReRouteKey(fileReRoute);
|
||||
|
||||
var upstreamTemplatePattern = _upstreamTemplatePatternCreator.Create(fileReRoute);
|
||||
|
||||
var authOptionsForRoute = _authOptionsCreator.Create(fileReRoute);
|
||||
|
||||
var claimsToHeaders = _claimsToThingCreator.Create(fileReRoute.AddHeadersToRequest);
|
||||
|
||||
var claimsToClaims = _claimsToThingCreator.Create(fileReRoute.AddClaimsToRequest);
|
||||
|
||||
var claimsToQueries = _claimsToThingCreator.Create(fileReRoute.AddQueriesToRequest);
|
||||
|
||||
var qosOptions = _qosOptionsCreator.Create(fileReRoute.QoSOptions, fileReRoute.UpstreamPathTemplate, fileReRoute.UpstreamHttpMethod.ToArray());
|
||||
|
||||
var rateLimitOption = _rateLimitOptionsCreator.Create(fileReRoute.RateLimitOptions, globalConfiguration);
|
||||
|
||||
var region = _regionCreator.Create(fileReRoute);
|
||||
|
||||
var httpHandlerOptions = _httpHandlerOptionsCreator.Create(fileReRoute.HttpHandlerOptions);
|
||||
|
||||
var hAndRs = _headerFAndRCreator.Create(fileReRoute);
|
||||
|
||||
var downstreamAddresses = _downstreamAddressesCreator.Create(fileReRoute);
|
||||
|
||||
var lbOptions = CreateLoadBalancerOptions(fileReRoute.LoadBalancerOptions);
|
||||
|
||||
var useServiceDiscovery = !string.IsNullOrEmpty(fileReRoute.ServiceName);
|
||||
|
||||
var reRoute = new DownstreamReRouteBuilder()
|
||||
.WithKey(fileReRoute.Key)
|
||||
.WithDownstreamPathTemplate(fileReRoute.DownstreamPathTemplate)
|
||||
.WithUpstreamHttpMethod(fileReRoute.UpstreamHttpMethod)
|
||||
.WithUpstreamPathTemplate(upstreamTemplatePattern)
|
||||
.WithIsAuthenticated(fileReRouteOptions.IsAuthenticated)
|
||||
.WithAuthenticationOptions(authOptionsForRoute)
|
||||
.WithClaimsToHeaders(claimsToHeaders)
|
||||
.WithClaimsToClaims(claimsToClaims)
|
||||
.WithRouteClaimsRequirement(fileReRoute.RouteClaimsRequirement)
|
||||
.WithIsAuthorised(fileReRouteOptions.IsAuthorised)
|
||||
.WithClaimsToQueries(claimsToQueries)
|
||||
.WithRequestIdKey(requestIdKey)
|
||||
.WithIsCached(fileReRouteOptions.IsCached)
|
||||
.WithCacheOptions(new CacheOptions(fileReRoute.FileCacheOptions.TtlSeconds, region))
|
||||
.WithDownstreamScheme(fileReRoute.DownstreamScheme)
|
||||
.WithLoadBalancerOptions(lbOptions)
|
||||
.WithDownstreamAddresses(downstreamAddresses)
|
||||
.WithLoadBalancerKey(reRouteKey)
|
||||
.WithQosOptions(qosOptions)
|
||||
.WithEnableRateLimiting(fileReRouteOptions.EnableRateLimiting)
|
||||
.WithRateLimitOptions(rateLimitOption)
|
||||
.WithHttpHandlerOptions(httpHandlerOptions)
|
||||
.WithServiceName(fileReRoute.ServiceName)
|
||||
.WithUseServiceDiscovery(useServiceDiscovery)
|
||||
.WithUpstreamHeaderFindAndReplace(hAndRs.Upstream)
|
||||
.WithDownstreamHeaderFindAndReplace(hAndRs.Downstream)
|
||||
.WithUpstreamHost(fileReRoute.UpstreamHost)
|
||||
.WithDelegatingHandlers(fileReRoute.DelegatingHandlers)
|
||||
.WithAddHeadersToDownstream(hAndRs.AddHeadersToDownstream)
|
||||
.WithAddHeadersToUpstream(hAndRs.AddHeadersToUpstream)
|
||||
.WithDangerousAcceptAnyServerCertificateValidator(fileReRoute.DangerousAcceptAnyServerCertificateValidator)
|
||||
.Build();
|
||||
|
||||
return reRoute;
|
||||
}
|
||||
|
||||
private LoadBalancerOptions CreateLoadBalancerOptions(FileLoadBalancerOptions options)
|
||||
{
|
||||
return new LoadBalancerOptionsBuilder()
|
||||
.WithType(options.Type)
|
||||
.WithKey(options.Key)
|
||||
.WithExpiryInMs(options.Expiry)
|
||||
.Build();
|
||||
}
|
||||
|
||||
private string CreateReRouteKey(FileReRoute fileReRoute)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(fileReRoute.LoadBalancerOptions.Type) && !string.IsNullOrEmpty(fileReRoute.LoadBalancerOptions.Key) && fileReRoute.LoadBalancerOptions.Type == nameof(CookieStickySessions))
|
||||
{
|
||||
return $"{nameof(CookieStickySessions)}:{fileReRoute.LoadBalancerOptions.Key}";
|
||||
}
|
||||
|
||||
return $"{fileReRoute.UpstreamPathTemplate}|{string.Join(",", fileReRoute.UpstreamHttpMethod)}|{string.Join(",", fileReRoute.DownstreamHostAndPorts.Select(x => $"{x.Host}:{x.Port}"))}";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
10
src/Ocelot/Configuration/Creator/IAggregatesCreator.cs
Normal file
10
src/Ocelot/Configuration/Creator/IAggregatesCreator.cs
Normal file
@ -0,0 +1,10 @@
|
||||
using System.Collections.Generic;
|
||||
using Ocelot.Configuration.File;
|
||||
|
||||
namespace Ocelot.Configuration.Creator
|
||||
{
|
||||
public interface IAggregatesCreator
|
||||
{
|
||||
List<ReRoute> Create(FileConfiguration fileConfiguration, List<ReRoute> reRoutes);
|
||||
}
|
||||
}
|
10
src/Ocelot/Configuration/Creator/IConfigurationCreator.cs
Normal file
10
src/Ocelot/Configuration/Creator/IConfigurationCreator.cs
Normal file
@ -0,0 +1,10 @@
|
||||
using System.Collections.Generic;
|
||||
using Ocelot.Configuration.File;
|
||||
|
||||
namespace Ocelot.Configuration.Creator
|
||||
{
|
||||
public interface IConfigurationCreator
|
||||
{
|
||||
InternalConfiguration Create(FileConfiguration fileConfiguration, List<ReRoute> reRoutes);
|
||||
}
|
||||
}
|
10
src/Ocelot/Configuration/Creator/IDynamicsCreator.cs
Normal file
10
src/Ocelot/Configuration/Creator/IDynamicsCreator.cs
Normal file
@ -0,0 +1,10 @@
|
||||
using System.Collections.Generic;
|
||||
using Ocelot.Configuration.File;
|
||||
|
||||
namespace Ocelot.Configuration.Creator
|
||||
{
|
||||
public interface IDynamicsCreator
|
||||
{
|
||||
List<ReRoute> Create(FileConfiguration fileConfiguration);
|
||||
}
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
using Ocelot.Configuration.File;
|
||||
|
||||
namespace Ocelot.Configuration.Creator
|
||||
{
|
||||
public interface ILoadBalancerOptionsCreator
|
||||
{
|
||||
LoadBalancerOptions Create(FileLoadBalancerOptions options);
|
||||
}
|
||||
}
|
@ -1,3 +1,4 @@
|
||||
using System.Collections.Generic;
|
||||
using Ocelot.Configuration.File;
|
||||
|
||||
namespace Ocelot.Configuration.Creator
|
||||
@ -5,7 +6,7 @@ namespace Ocelot.Configuration.Creator
|
||||
public interface IQoSOptionsCreator
|
||||
{
|
||||
QoSOptions Create(FileQoSOptions options);
|
||||
QoSOptions Create(FileQoSOptions options, string pathTemplate, string[] httpMethods);
|
||||
QoSOptions Create(QoSOptions options, string pathTemplate, string[] httpMethods);
|
||||
QoSOptions Create(FileQoSOptions options, string pathTemplate, List<string> httpMethods);
|
||||
QoSOptions Create(QoSOptions options, string pathTemplate, List<string> httpMethods);
|
||||
}
|
||||
}
|
||||
|
9
src/Ocelot/Configuration/Creator/IReRouteKeyCreator.cs
Normal file
9
src/Ocelot/Configuration/Creator/IReRouteKeyCreator.cs
Normal file
@ -0,0 +1,9 @@
|
||||
using Ocelot.Configuration.File;
|
||||
|
||||
namespace Ocelot.Configuration.Creator
|
||||
{
|
||||
public interface IReRouteKeyCreator
|
||||
{
|
||||
string Create(FileReRoute fileReRoute);
|
||||
}
|
||||
}
|
10
src/Ocelot/Configuration/Creator/IReRoutesCreator.cs
Normal file
10
src/Ocelot/Configuration/Creator/IReRoutesCreator.cs
Normal file
@ -0,0 +1,10 @@
|
||||
using System.Collections.Generic;
|
||||
using Ocelot.Configuration.File;
|
||||
|
||||
namespace Ocelot.Configuration.Creator
|
||||
{
|
||||
public interface IReRoutesCreator
|
||||
{
|
||||
List<ReRoute> Create(FileConfiguration fileConfiguration);
|
||||
}
|
||||
}
|
12
src/Ocelot/Configuration/Creator/ISecurityOptionsCreator.cs
Normal file
12
src/Ocelot/Configuration/Creator/ISecurityOptionsCreator.cs
Normal file
@ -0,0 +1,12 @@
|
||||
using Ocelot.Configuration.File;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Ocelot.Configuration.Creator
|
||||
{
|
||||
public interface ISecurityOptionsCreator
|
||||
{
|
||||
SecurityOptions Create(FileSecurityOptions securityOptions);
|
||||
}
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
using Ocelot.Configuration.File;
|
||||
|
||||
namespace Ocelot.Configuration.Creator
|
||||
{
|
||||
|
||||
public class LoadBalancerOptionsCreator : ILoadBalancerOptionsCreator
|
||||
{
|
||||
public LoadBalancerOptions Create(FileLoadBalancerOptions options)
|
||||
{
|
||||
return new LoadBalancerOptionsBuilder()
|
||||
.WithType(options.Type)
|
||||
.WithKey(options.Key)
|
||||
.WithExpiryInMs(options.Expiry)
|
||||
.Build();
|
||||
}
|
||||
}
|
||||
}
|
@ -2,6 +2,7 @@ namespace Ocelot.Configuration.Creator
|
||||
{
|
||||
using Ocelot.Configuration.Builder;
|
||||
using Ocelot.Configuration.File;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
public class QoSOptionsCreator : IQoSOptionsCreator
|
||||
@ -15,14 +16,14 @@ namespace Ocelot.Configuration.Creator
|
||||
.Build();
|
||||
}
|
||||
|
||||
public QoSOptions Create(FileQoSOptions options, string pathTemplate, string[] httpMethods)
|
||||
public QoSOptions Create(FileQoSOptions options, string pathTemplate, List<string> httpMethods)
|
||||
{
|
||||
var key = CreateKey(pathTemplate, httpMethods);
|
||||
|
||||
return Map(key, options.TimeoutValue, options.DurationOfBreak, options.ExceptionsAllowedBeforeBreaking);
|
||||
}
|
||||
|
||||
public QoSOptions Create(QoSOptions options, string pathTemplate, string[] httpMethods)
|
||||
public QoSOptions Create(QoSOptions options, string pathTemplate, List<string> httpMethods)
|
||||
{
|
||||
var key = CreateKey(pathTemplate, httpMethods);
|
||||
|
||||
@ -39,7 +40,7 @@ namespace Ocelot.Configuration.Creator
|
||||
.Build();
|
||||
}
|
||||
|
||||
private string CreateKey(string pathTemplate, string[] httpMethods)
|
||||
private string CreateKey(string pathTemplate, List<string> httpMethods)
|
||||
{
|
||||
return $"{pathTemplate.FirstOrDefault()}|{string.Join(",", httpMethods)}";
|
||||
}
|
||||
|
@ -1,5 +1,4 @@
|
||||
using System;
|
||||
using Ocelot.Configuration.Builder;
|
||||
using Ocelot.Configuration.Builder;
|
||||
using Ocelot.Configuration.File;
|
||||
|
||||
namespace Ocelot.Configuration.Creator
|
||||
@ -8,11 +7,9 @@ namespace Ocelot.Configuration.Creator
|
||||
{
|
||||
public RateLimitOptions Create(FileRateLimitRule fileRateLimitRule, FileGlobalConfiguration globalConfiguration)
|
||||
{
|
||||
RateLimitOptions rateLimitOption = null;
|
||||
|
||||
if (fileRateLimitRule != null && fileRateLimitRule.EnableRateLimiting)
|
||||
{
|
||||
rateLimitOption = new RateLimitOptionsBuilder()
|
||||
return new RateLimitOptionsBuilder()
|
||||
.WithClientIdHeader(globalConfiguration.RateLimitOptions.ClientIdHeader)
|
||||
.WithClientWhiteList(fileRateLimitRule.ClientWhitelist)
|
||||
.WithDisableRateLimitHeaders(globalConfiguration.RateLimitOptions.DisableRateLimitHeaders)
|
||||
@ -26,7 +23,7 @@ namespace Ocelot.Configuration.Creator
|
||||
.Build();
|
||||
}
|
||||
|
||||
return rateLimitOption;
|
||||
return new RateLimitOptionsBuilder().WithEnableRateLimiting(false).Build();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
31
src/Ocelot/Configuration/Creator/ReRouteKeyCreator.cs
Normal file
31
src/Ocelot/Configuration/Creator/ReRouteKeyCreator.cs
Normal file
@ -0,0 +1,31 @@
|
||||
using System.Linq;
|
||||
using Ocelot.Configuration.File;
|
||||
using Ocelot.LoadBalancer.LoadBalancers;
|
||||
|
||||
namespace Ocelot.Configuration.Creator
|
||||
{
|
||||
public class ReRouteKeyCreator : IReRouteKeyCreator
|
||||
{
|
||||
public string Create(FileReRoute fileReRoute)
|
||||
{
|
||||
if (IsStickySession(fileReRoute))
|
||||
{
|
||||
return $"{nameof(CookieStickySessions)}:{fileReRoute.LoadBalancerOptions.Key}";
|
||||
}
|
||||
|
||||
return $"{fileReRoute.UpstreamPathTemplate}|{string.Join(",", fileReRoute.UpstreamHttpMethod)}|{string.Join(",", fileReRoute.DownstreamHostAndPorts.Select(x => $"{x.Host}:{x.Port}"))}";
|
||||
}
|
||||
|
||||
private bool IsStickySession(FileReRoute fileReRoute)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(fileReRoute.LoadBalancerOptions.Type)
|
||||
&& !string.IsNullOrEmpty(fileReRoute.LoadBalancerOptions.Key)
|
||||
&& fileReRoute.LoadBalancerOptions.Type == nameof(CookieStickySessions))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
@ -11,12 +11,14 @@ namespace Ocelot.Configuration.Creator
|
||||
var isAuthorised = IsAuthorised(fileReRoute);
|
||||
var isCached = IsCached(fileReRoute);
|
||||
var enableRateLimiting = IsEnableRateLimiting(fileReRoute);
|
||||
var useServiceDiscovery = !string.IsNullOrEmpty(fileReRoute.ServiceName);
|
||||
|
||||
var options = new ReRouteOptionsBuilder()
|
||||
.WithIsAuthenticated(isAuthenticated)
|
||||
.WithIsAuthorised(isAuthorised)
|
||||
.WithIsCached(isCached)
|
||||
.WithRateLimiting(enableRateLimiting)
|
||||
.WithUseServiceDiscovery(useServiceDiscovery)
|
||||
.Build();
|
||||
|
||||
return options;
|
||||
|
156
src/Ocelot/Configuration/Creator/ReRoutesCreator.cs
Normal file
156
src/Ocelot/Configuration/Creator/ReRoutesCreator.cs
Normal file
@ -0,0 +1,156 @@
|
||||
namespace Ocelot.Configuration.Creator
|
||||
{
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Cache;
|
||||
using Builder;
|
||||
using File;
|
||||
|
||||
public class ReRoutesCreator : IReRoutesCreator
|
||||
{
|
||||
private readonly ILoadBalancerOptionsCreator _loadBalancerOptionsCreator;
|
||||
private readonly IClaimsToThingCreator _claimsToThingCreator;
|
||||
private readonly IAuthenticationOptionsCreator _authOptionsCreator;
|
||||
private readonly IUpstreamTemplatePatternCreator _upstreamTemplatePatternCreator;
|
||||
private readonly IRequestIdKeyCreator _requestIdKeyCreator;
|
||||
private readonly IQoSOptionsCreator _qosOptionsCreator;
|
||||
private readonly IReRouteOptionsCreator _fileReRouteOptionsCreator;
|
||||
private readonly IRateLimitOptionsCreator _rateLimitOptionsCreator;
|
||||
private readonly IRegionCreator _regionCreator;
|
||||
private readonly IHttpHandlerOptionsCreator _httpHandlerOptionsCreator;
|
||||
private readonly IHeaderFindAndReplaceCreator _headerFAndRCreator;
|
||||
private readonly IDownstreamAddressesCreator _downstreamAddressesCreator;
|
||||
private readonly IReRouteKeyCreator _reRouteKeyCreator;
|
||||
private readonly ISecurityOptionsCreator _securityOptionsCreator;
|
||||
|
||||
public ReRoutesCreator(
|
||||
IClaimsToThingCreator claimsToThingCreator,
|
||||
IAuthenticationOptionsCreator authOptionsCreator,
|
||||
IUpstreamTemplatePatternCreator upstreamTemplatePatternCreator,
|
||||
IRequestIdKeyCreator requestIdKeyCreator,
|
||||
IQoSOptionsCreator qosOptionsCreator,
|
||||
IReRouteOptionsCreator fileReRouteOptionsCreator,
|
||||
IRateLimitOptionsCreator rateLimitOptionsCreator,
|
||||
IRegionCreator regionCreator,
|
||||
IHttpHandlerOptionsCreator httpHandlerOptionsCreator,
|
||||
IHeaderFindAndReplaceCreator headerFAndRCreator,
|
||||
IDownstreamAddressesCreator downstreamAddressesCreator,
|
||||
ILoadBalancerOptionsCreator loadBalancerOptionsCreator,
|
||||
IReRouteKeyCreator reRouteKeyCreator,
|
||||
ISecurityOptionsCreator securityOptionsCreator
|
||||
)
|
||||
{
|
||||
_reRouteKeyCreator = reRouteKeyCreator;
|
||||
_loadBalancerOptionsCreator = loadBalancerOptionsCreator;
|
||||
_downstreamAddressesCreator = downstreamAddressesCreator;
|
||||
_headerFAndRCreator = headerFAndRCreator;
|
||||
_regionCreator = regionCreator;
|
||||
_rateLimitOptionsCreator = rateLimitOptionsCreator;
|
||||
_requestIdKeyCreator = requestIdKeyCreator;
|
||||
_upstreamTemplatePatternCreator = upstreamTemplatePatternCreator;
|
||||
_authOptionsCreator = authOptionsCreator;
|
||||
_claimsToThingCreator = claimsToThingCreator;
|
||||
_qosOptionsCreator = qosOptionsCreator;
|
||||
_fileReRouteOptionsCreator = fileReRouteOptionsCreator;
|
||||
_httpHandlerOptionsCreator = httpHandlerOptionsCreator;
|
||||
_loadBalancerOptionsCreator = loadBalancerOptionsCreator;
|
||||
_securityOptionsCreator = securityOptionsCreator;
|
||||
}
|
||||
|
||||
public List<ReRoute> Create(FileConfiguration fileConfiguration)
|
||||
{
|
||||
return fileConfiguration.ReRoutes
|
||||
.Select(reRoute =>
|
||||
{
|
||||
var downstreamReRoute = SetUpDownstreamReRoute(reRoute, fileConfiguration.GlobalConfiguration);
|
||||
return SetUpReRoute(reRoute, downstreamReRoute);
|
||||
})
|
||||
.ToList();
|
||||
}
|
||||
|
||||
private DownstreamReRoute SetUpDownstreamReRoute(FileReRoute fileReRoute, FileGlobalConfiguration globalConfiguration)
|
||||
{
|
||||
var fileReRouteOptions = _fileReRouteOptionsCreator.Create(fileReRoute);
|
||||
|
||||
var requestIdKey = _requestIdKeyCreator.Create(fileReRoute, globalConfiguration);
|
||||
|
||||
var reRouteKey = _reRouteKeyCreator.Create(fileReRoute);
|
||||
|
||||
var upstreamTemplatePattern = _upstreamTemplatePatternCreator.Create(fileReRoute);
|
||||
|
||||
var authOptionsForRoute = _authOptionsCreator.Create(fileReRoute);
|
||||
|
||||
var claimsToHeaders = _claimsToThingCreator.Create(fileReRoute.AddHeadersToRequest);
|
||||
|
||||
var claimsToClaims = _claimsToThingCreator.Create(fileReRoute.AddClaimsToRequest);
|
||||
|
||||
var claimsToQueries = _claimsToThingCreator.Create(fileReRoute.AddQueriesToRequest);
|
||||
|
||||
var qosOptions = _qosOptionsCreator.Create(fileReRoute.QoSOptions, fileReRoute.UpstreamPathTemplate, fileReRoute.UpstreamHttpMethod);
|
||||
|
||||
var rateLimitOption = _rateLimitOptionsCreator.Create(fileReRoute.RateLimitOptions, globalConfiguration);
|
||||
|
||||
var region = _regionCreator.Create(fileReRoute);
|
||||
|
||||
var httpHandlerOptions = _httpHandlerOptionsCreator.Create(fileReRoute.HttpHandlerOptions);
|
||||
|
||||
var hAndRs = _headerFAndRCreator.Create(fileReRoute);
|
||||
|
||||
var downstreamAddresses = _downstreamAddressesCreator.Create(fileReRoute);
|
||||
|
||||
var lbOptions = _loadBalancerOptionsCreator.Create(fileReRoute.LoadBalancerOptions);
|
||||
|
||||
var securityOptions = _securityOptionsCreator.Create(fileReRoute.SecurityOptions);
|
||||
|
||||
var reRoute = new DownstreamReRouteBuilder()
|
||||
.WithKey(fileReRoute.Key)
|
||||
.WithDownstreamPathTemplate(fileReRoute.DownstreamPathTemplate)
|
||||
.WithUpstreamHttpMethod(fileReRoute.UpstreamHttpMethod)
|
||||
.WithUpstreamPathTemplate(upstreamTemplatePattern)
|
||||
.WithIsAuthenticated(fileReRouteOptions.IsAuthenticated)
|
||||
.WithAuthenticationOptions(authOptionsForRoute)
|
||||
.WithClaimsToHeaders(claimsToHeaders)
|
||||
.WithClaimsToClaims(claimsToClaims)
|
||||
.WithRouteClaimsRequirement(fileReRoute.RouteClaimsRequirement)
|
||||
.WithIsAuthorised(fileReRouteOptions.IsAuthorised)
|
||||
.WithClaimsToQueries(claimsToQueries)
|
||||
.WithRequestIdKey(requestIdKey)
|
||||
.WithIsCached(fileReRouteOptions.IsCached)
|
||||
.WithCacheOptions(new CacheOptions(fileReRoute.FileCacheOptions.TtlSeconds, region))
|
||||
.WithDownstreamScheme(fileReRoute.DownstreamScheme)
|
||||
.WithLoadBalancerOptions(lbOptions)
|
||||
.WithDownstreamAddresses(downstreamAddresses)
|
||||
.WithLoadBalancerKey(reRouteKey)
|
||||
.WithQosOptions(qosOptions)
|
||||
.WithEnableRateLimiting(fileReRouteOptions.EnableRateLimiting)
|
||||
.WithRateLimitOptions(rateLimitOption)
|
||||
.WithHttpHandlerOptions(httpHandlerOptions)
|
||||
.WithServiceName(fileReRoute.ServiceName)
|
||||
.WithUseServiceDiscovery(fileReRouteOptions.UseServiceDiscovery)
|
||||
.WithUpstreamHeaderFindAndReplace(hAndRs.Upstream)
|
||||
.WithDownstreamHeaderFindAndReplace(hAndRs.Downstream)
|
||||
.WithDelegatingHandlers(fileReRoute.DelegatingHandlers)
|
||||
.WithAddHeadersToDownstream(hAndRs.AddHeadersToDownstream)
|
||||
.WithAddHeadersToUpstream(hAndRs.AddHeadersToUpstream)
|
||||
.WithDangerousAcceptAnyServerCertificateValidator(fileReRoute.DangerousAcceptAnyServerCertificateValidator)
|
||||
.WithSecurityOptions(securityOptions)
|
||||
.Build();
|
||||
|
||||
return reRoute;
|
||||
}
|
||||
|
||||
private ReRoute SetUpReRoute(FileReRoute fileReRoute, DownstreamReRoute downstreamReRoutes)
|
||||
{
|
||||
var upstreamTemplatePattern = _upstreamTemplatePatternCreator.Create(fileReRoute);
|
||||
|
||||
var reRoute = new ReRouteBuilder()
|
||||
.WithUpstreamHttpMethod(fileReRoute.UpstreamHttpMethod)
|
||||
.WithUpstreamPathTemplate(upstreamTemplatePattern)
|
||||
.WithDownstreamReRoute(downstreamReRoutes)
|
||||
.WithUpstreamHost(fileReRoute.UpstreamHost)
|
||||
.Build();
|
||||
|
||||
return reRoute;
|
||||
}
|
||||
}
|
||||
}
|
15
src/Ocelot/Configuration/Creator/SecurityOptionsCreator.cs
Normal file
15
src/Ocelot/Configuration/Creator/SecurityOptionsCreator.cs
Normal file
@ -0,0 +1,15 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using Ocelot.Configuration.File;
|
||||
|
||||
namespace Ocelot.Configuration.Creator
|
||||
{
|
||||
public class SecurityOptionsCreator : ISecurityOptionsCreator
|
||||
{
|
||||
public SecurityOptions Create(FileSecurityOptions securityOptions)
|
||||
{
|
||||
return new SecurityOptions(securityOptions.IPAllowedList, securityOptions.IPBlockedList);
|
||||
}
|
||||
}
|
||||
}
|
@ -30,12 +30,13 @@ namespace Ocelot.Configuration
|
||||
bool isAuthenticated,
|
||||
bool isAuthorised,
|
||||
AuthenticationOptions authenticationOptions,
|
||||
DownstreamPathTemplate downstreamDownstreamPathTemplate,
|
||||
DownstreamPathTemplate downstreamPathTemplate,
|
||||
string loadBalancerKey,
|
||||
List<string> delegatingHandlers,
|
||||
List<AddHeader> addHeadersToDownstream,
|
||||
List<AddHeader> addHeadersToUpstream,
|
||||
bool dangerousAcceptAnyServerCertificateValidator)
|
||||
bool dangerousAcceptAnyServerCertificateValidator,
|
||||
SecurityOptions securityOptions)
|
||||
{
|
||||
DangerousAcceptAnyServerCertificateValidator = dangerousAcceptAnyServerCertificateValidator;
|
||||
AddHeadersToDownstream = addHeadersToDownstream;
|
||||
@ -63,9 +64,10 @@ namespace Ocelot.Configuration
|
||||
IsAuthenticated = isAuthenticated;
|
||||
IsAuthorised = isAuthorised;
|
||||
AuthenticationOptions = authenticationOptions;
|
||||
DownstreamDownstreamPathTemplate = downstreamDownstreamPathTemplate;
|
||||
DownstreamPathTemplate = downstreamPathTemplate;
|
||||
LoadBalancerKey = loadBalancerKey;
|
||||
AddHeadersToUpstream = addHeadersToUpstream;
|
||||
SecurityOptions = securityOptions;
|
||||
}
|
||||
|
||||
public string Key { get; }
|
||||
@ -91,11 +93,12 @@ namespace Ocelot.Configuration
|
||||
public bool IsAuthenticated { get; }
|
||||
public bool IsAuthorised { get; }
|
||||
public AuthenticationOptions AuthenticationOptions { get; }
|
||||
public DownstreamPathTemplate DownstreamDownstreamPathTemplate { get; }
|
||||
public DownstreamPathTemplate DownstreamPathTemplate { get; }
|
||||
public string LoadBalancerKey { get; }
|
||||
public List<string> DelegatingHandlers { get; }
|
||||
public List<AddHeader> AddHeadersToDownstream { get; }
|
||||
public List<AddHeader> AddHeadersToUpstream { get; }
|
||||
public bool DangerousAcceptAnyServerCertificateValidator { get; }
|
||||
public SecurityOptions SecurityOptions { get; }
|
||||
}
|
||||
}
|
||||
|
@ -21,6 +21,7 @@ namespace Ocelot.Configuration.File
|
||||
DownstreamHostAndPorts = new List<FileHostAndPort>();
|
||||
DelegatingHandlers = new List<string>();
|
||||
LoadBalancerOptions = new FileLoadBalancerOptions();
|
||||
SecurityOptions = new FileSecurityOptions();
|
||||
Priority = 1;
|
||||
}
|
||||
|
||||
@ -50,5 +51,6 @@ namespace Ocelot.Configuration.File
|
||||
public int Priority { get;set; }
|
||||
public int Timeout { get; set; }
|
||||
public bool DangerousAcceptAnyServerCertificateValidator { get; set; }
|
||||
public FileSecurityOptions SecurityOptions { get; set; }
|
||||
}
|
||||
}
|
||||
|
19
src/Ocelot/Configuration/File/FileSecurityOptions.cs
Normal file
19
src/Ocelot/Configuration/File/FileSecurityOptions.cs
Normal file
@ -0,0 +1,19 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Ocelot.Configuration.File
|
||||
{
|
||||
public class FileSecurityOptions
|
||||
{
|
||||
public FileSecurityOptions()
|
||||
{
|
||||
IPAllowedList = new List<string>();
|
||||
IPBlockedList = new List<string>();
|
||||
}
|
||||
|
||||
public List<string> IPAllowedList { get; set; }
|
||||
|
||||
public List<string> IPBlockedList { get; set; }
|
||||
}
|
||||
}
|
@ -24,7 +24,7 @@
|
||||
|
||||
public string TimeoutStrategy { get; }
|
||||
|
||||
public bool UseQos => ExceptionsAllowedBeforeBreaking > 0 && TimeoutValue > 0;
|
||||
public bool UseQos => ExceptionsAllowedBeforeBreaking > 0 || TimeoutValue > 0;
|
||||
|
||||
public string Key { get; }
|
||||
}
|
||||
|
@ -2,17 +2,19 @@ namespace Ocelot.Configuration
|
||||
{
|
||||
public class ReRouteOptions
|
||||
{
|
||||
public ReRouteOptions(bool isAuthenticated, bool isAuthorised, bool isCached, bool isEnableRateLimiting)
|
||||
public ReRouteOptions(bool isAuthenticated, bool isAuthorised, bool isCached, bool isEnableRateLimiting, bool useServiceDiscovery)
|
||||
{
|
||||
IsAuthenticated = isAuthenticated;
|
||||
IsAuthorised = isAuthorised;
|
||||
IsCached = isCached;
|
||||
EnableRateLimiting = isEnableRateLimiting;
|
||||
UseServiceDiscovery = useServiceDiscovery;
|
||||
}
|
||||
|
||||
public bool IsAuthenticated { get; private set; }
|
||||
public bool IsAuthorised { get; private set; }
|
||||
public bool IsCached { get; private set; }
|
||||
public bool EnableRateLimiting { get; private set; }
|
||||
public bool UseServiceDiscovery { get; private set; }
|
||||
}
|
||||
}
|
||||
|
19
src/Ocelot/Configuration/SecurityOptions.cs
Normal file
19
src/Ocelot/Configuration/SecurityOptions.cs
Normal file
@ -0,0 +1,19 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Ocelot.Configuration
|
||||
{
|
||||
public class SecurityOptions
|
||||
{
|
||||
public SecurityOptions(List<string> allowedList, List<string> blockedList)
|
||||
{
|
||||
this.IPAllowedList = allowedList;
|
||||
this.IPBlockedList = blockedList;
|
||||
}
|
||||
|
||||
public List<string> IPAllowedList { get; private set; }
|
||||
|
||||
public List<string> IPBlockedList { get; private set; }
|
||||
}
|
||||
}
|
@ -9,6 +9,7 @@ namespace Ocelot.DependencyInjection
|
||||
using System.Text.RegularExpressions;
|
||||
using Configuration.File;
|
||||
using Newtonsoft.Json;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
|
||||
public static class ConfigurationBuilderExtensions
|
||||
{
|
||||
@ -28,37 +29,42 @@ namespace Ocelot.DependencyInjection
|
||||
return builder;
|
||||
}
|
||||
|
||||
public static IConfigurationBuilder AddOcelot(this IConfigurationBuilder builder)
|
||||
public static IConfigurationBuilder AddOcelot(this IConfigurationBuilder builder, IHostingEnvironment env)
|
||||
{
|
||||
return builder.AddOcelot(".");
|
||||
return builder.AddOcelot(".", env);
|
||||
}
|
||||
|
||||
public static IConfigurationBuilder AddOcelot(this IConfigurationBuilder builder, string folder)
|
||||
public static IConfigurationBuilder AddOcelot(this IConfigurationBuilder builder, string folder, IHostingEnvironment env)
|
||||
{
|
||||
const string pattern = "(?i)ocelot\\.([a-zA-Z0-9]*)(\\.json)$";
|
||||
const string primaryConfigFile = "ocelot.json";
|
||||
|
||||
var reg = new Regex(pattern);
|
||||
const string globalConfigFile = "ocelot.global.json";
|
||||
|
||||
var files = Directory.GetFiles(folder)
|
||||
.Where(path => reg.IsMatch(path))
|
||||
const string subConfigPattern = @"^ocelot\.[a-zA-Z0-9]+\.json$";
|
||||
|
||||
string excludeConfigName = env?.EnvironmentName != null ? $"ocelot.{env.EnvironmentName}.json" : string.Empty;
|
||||
|
||||
var reg = new Regex(subConfigPattern, RegexOptions.IgnoreCase | RegexOptions.Singleline);
|
||||
|
||||
var files = new DirectoryInfo(folder)
|
||||
.EnumerateFiles()
|
||||
.Where(fi => reg.IsMatch(fi.Name) && (fi.Name != excludeConfigName))
|
||||
.ToList();
|
||||
|
||||
var fileConfiguration = new FileConfiguration();
|
||||
|
||||
foreach (var file in files)
|
||||
{
|
||||
// windows and unix sigh...
|
||||
if(files.Count > 1 && (Path.GetFileName(file) == "ocelot.json"))
|
||||
if(files.Count > 1 && file.Name.Equals(primaryConfigFile, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var lines = File.ReadAllText(file);
|
||||
var lines = File.ReadAllText(file.FullName);
|
||||
|
||||
var config = JsonConvert.DeserializeObject<FileConfiguration>(lines);
|
||||
|
||||
// windows and unix sigh...
|
||||
if (Path.GetFileName(file) == "ocelot.global.json")
|
||||
if (file.Name.Equals(globalConfigFile, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
fileConfiguration.GlobalConfiguration = config.GlobalConfiguration;
|
||||
}
|
||||
@ -69,9 +75,9 @@ namespace Ocelot.DependencyInjection
|
||||
|
||||
var json = JsonConvert.SerializeObject(fileConfiguration);
|
||||
|
||||
File.WriteAllText("ocelot.json", json);
|
||||
File.WriteAllText(primaryConfigFile, json);
|
||||
|
||||
builder.AddJsonFile("ocelot.json", false, false);
|
||||
builder.AddJsonFile(primaryConfigFile, false, false);
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
@ -35,6 +35,8 @@ namespace Ocelot.DependencyInjection
|
||||
using Ocelot.Infrastructure;
|
||||
using Ocelot.Middleware.Multiplexer;
|
||||
using Ocelot.Request.Creator;
|
||||
using Ocelot.Security.IPSecurity;
|
||||
using Ocelot.Security;
|
||||
|
||||
public class OcelotBuilder : IOcelotBuilder
|
||||
{
|
||||
@ -55,10 +57,16 @@ namespace Ocelot.DependencyInjection
|
||||
Services.TryAddSingleton<IInternalConfigurationCreator, FileInternalConfigurationCreator>();
|
||||
Services.TryAddSingleton<IInternalConfigurationRepository, InMemoryInternalConfigurationRepository>();
|
||||
Services.TryAddSingleton<IConfigurationValidator, FileConfigurationFluentValidator>();
|
||||
Services.AddSingleton<HostAndPortValidator>();
|
||||
Services.AddSingleton<ReRouteFluentValidator>();
|
||||
Services.AddSingleton<FileGlobalConfigurationFluentValidator>();
|
||||
Services.AddSingleton<FileQoSOptionsFluentValidator>();
|
||||
Services.TryAddSingleton<HostAndPortValidator>();
|
||||
Services.TryAddSingleton<IReRoutesCreator, ReRoutesCreator>();
|
||||
Services.TryAddSingleton<IAggregatesCreator, AggregatesCreator>();
|
||||
Services.TryAddSingleton<IReRouteKeyCreator, ReRouteKeyCreator>();
|
||||
Services.TryAddSingleton<IConfigurationCreator, ConfigurationCreator>();
|
||||
Services.TryAddSingleton<IDynamicsCreator, DynamicsCreator>();
|
||||
Services.TryAddSingleton<ILoadBalancerOptionsCreator, LoadBalancerOptionsCreator>();
|
||||
Services.TryAddSingleton<ReRouteFluentValidator>();
|
||||
Services.TryAddSingleton<FileGlobalConfigurationFluentValidator>();
|
||||
Services.TryAddSingleton<FileQoSOptionsFluentValidator>();
|
||||
Services.TryAddSingleton<IClaimsToThingCreator, ClaimsToThingCreator>();
|
||||
Services.TryAddSingleton<IAuthenticationOptionsCreator, AuthenticationOptionsCreator>();
|
||||
Services.TryAddSingleton<IUpstreamTemplatePatternCreator, UpstreamTemplatePatternCreator>();
|
||||
@ -86,8 +94,8 @@ namespace Ocelot.DependencyInjection
|
||||
Services.TryAddSingleton<IUrlPathToUrlTemplateMatcher, RegExUrlMatcher>();
|
||||
Services.TryAddSingleton<IPlaceholderNameAndValueFinder, UrlPathPlaceholderNameAndValueFinder>();
|
||||
Services.TryAddSingleton<IDownstreamPathPlaceholderReplacer, DownstreamTemplatePathPlaceholderReplacer>();
|
||||
Services.AddSingleton<IDownstreamRouteProvider, DownstreamRouteFinder>();
|
||||
Services.AddSingleton<IDownstreamRouteProvider, DownstreamRouteCreator>();
|
||||
Services.TryAddSingleton<IDownstreamRouteProvider, DownstreamRouteFinder>();
|
||||
Services.TryAddSingleton<IDownstreamRouteProvider, DownstreamRouteCreator>();
|
||||
Services.TryAddSingleton<IDownstreamRouteProviderFactory, DownstreamRouteProviderFactory>();
|
||||
Services.TryAddSingleton<IHttpRequester, HttpClientHttpRequester>();
|
||||
Services.TryAddSingleton<IHttpResponder, HttpContextResponder>();
|
||||
@ -108,7 +116,7 @@ namespace Ocelot.DependencyInjection
|
||||
Services.TryAddSingleton<OcelotDiagnosticListener>();
|
||||
Services.TryAddSingleton<IMultiplexer, Multiplexer>();
|
||||
Services.TryAddSingleton<IResponseAggregator, SimpleJsonResponseAggregator>();
|
||||
Services.AddSingleton<ITracingHandlerFactory, TracingHandlerFactory>();
|
||||
Services.TryAddSingleton<ITracingHandlerFactory, TracingHandlerFactory>();
|
||||
Services.TryAddSingleton<IFileConfigurationPollerOptions, InMemoryFileConfigurationPollerOptions>();
|
||||
Services.TryAddSingleton<IAddHeadersToResponse, AddHeadersToResponse>();
|
||||
Services.TryAddSingleton<IPlaceholders, Placeholders>();
|
||||
@ -119,6 +127,9 @@ namespace Ocelot.DependencyInjection
|
||||
Services.TryAddSingleton<IQoSFactory, QoSFactory>();
|
||||
Services.TryAddSingleton<IExceptionToErrorMapper, HttpExeptionToErrorMapper>();
|
||||
|
||||
//add security
|
||||
this.AddSecurity();
|
||||
|
||||
//add asp.net services..
|
||||
var assembly = typeof(FileConfigurationController).GetTypeInfo().Assembly;
|
||||
|
||||
@ -147,6 +158,12 @@ namespace Ocelot.DependencyInjection
|
||||
return this;
|
||||
}
|
||||
|
||||
private void AddSecurity()
|
||||
{
|
||||
Services.TryAddSingleton<ISecurityOptionsCreator, SecurityOptionsCreator>();
|
||||
Services.TryAddSingleton<ISecurityPolicy, IPSecurityPolicy>();
|
||||
}
|
||||
|
||||
public IOcelotBuilder AddDelegatingHandler<THandler>(bool global = false)
|
||||
where THandler : DelegatingHandler
|
||||
{
|
||||
|
@ -42,7 +42,7 @@
|
||||
return downstreamRoute;
|
||||
}
|
||||
|
||||
var qosOptions = _qoSOptionsCreator.Create(configuration.QoSOptions, downstreamPathForKeys, new []{ upstreamHttpMethod });
|
||||
var qosOptions = _qoSOptionsCreator.Create(configuration.QoSOptions, downstreamPathForKeys, new List<string> { upstreamHttpMethod });
|
||||
|
||||
var upstreamPathTemplate = new UpstreamPathTemplateBuilder().WithOriginalValue(upstreamUrlPath).Build();
|
||||
|
||||
|
@ -13,17 +13,14 @@ namespace Ocelot.DownstreamRouteFinder.Middleware
|
||||
{
|
||||
private readonly OcelotRequestDelegate _next;
|
||||
private readonly IDownstreamRouteProviderFactory _factory;
|
||||
private readonly IInternalConfigurationRepository _repo;
|
||||
private readonly IMultiplexer _multiplexer;
|
||||
|
||||
public DownstreamRouteFinderMiddleware(OcelotRequestDelegate next,
|
||||
IOcelotLoggerFactory loggerFactory,
|
||||
IDownstreamRouteProviderFactory downstreamRouteFinder,
|
||||
IInternalConfigurationRepository repo,
|
||||
IMultiplexer multiplexer)
|
||||
:base(loggerFactory.CreateLogger<DownstreamRouteFinderMiddleware>())
|
||||
{
|
||||
_repo = repo;
|
||||
_multiplexer = multiplexer;
|
||||
_next = next;
|
||||
_factory = downstreamRouteFinder;
|
||||
@ -51,7 +48,7 @@ namespace Ocelot.DownstreamRouteFinder.Middleware
|
||||
return;
|
||||
}
|
||||
|
||||
var downstreamPathTemplates = string.Join(", ", downstreamRoute.Data.ReRoute.DownstreamReRoute.Select(r => r.DownstreamDownstreamPathTemplate.Value));
|
||||
var downstreamPathTemplates = string.Join(", ", downstreamRoute.Data.ReRoute.DownstreamReRoute.Select(r => r.DownstreamPathTemplate.Value));
|
||||
|
||||
Logger.LogDebug($"downstream templates are {downstreamPathTemplates}");
|
||||
|
||||
|
@ -27,7 +27,7 @@ namespace Ocelot.DownstreamUrlCreator.Middleware
|
||||
public async Task Invoke(DownstreamContext context)
|
||||
{
|
||||
var response = _replacer
|
||||
.Replace(context.DownstreamReRoute.DownstreamDownstreamPathTemplate, context.TemplatePlaceholderNameAndValues);
|
||||
.Replace(context.DownstreamReRoute.DownstreamPathTemplate, context.TemplatePlaceholderNameAndValues);
|
||||
|
||||
if (response.IsError)
|
||||
{
|
||||
|
@ -8,11 +8,11 @@ namespace Ocelot.DownstreamUrlCreator.UrlTemplateReplacer
|
||||
{
|
||||
public class DownstreamTemplatePathPlaceholderReplacer : IDownstreamPathPlaceholderReplacer
|
||||
{
|
||||
public Response<DownstreamPath> Replace(DownstreamPathTemplate downstreamDownstreamPathTemplate, List<PlaceholderNameAndValue> urlPathPlaceholderNameAndValues)
|
||||
public Response<DownstreamPath> Replace(DownstreamPathTemplate downstreamPathTemplate, List<PlaceholderNameAndValue> urlPathPlaceholderNameAndValues)
|
||||
{
|
||||
var downstreamPath = new StringBuilder();
|
||||
|
||||
downstreamPath.Append(downstreamDownstreamPathTemplate.Value);
|
||||
downstreamPath.Append(downstreamPathTemplate.Value);
|
||||
|
||||
foreach (var placeholderVariableAndValue in urlPathPlaceholderNameAndValues)
|
||||
{
|
||||
|
@ -7,6 +7,6 @@ namespace Ocelot.DownstreamUrlCreator.UrlTemplateReplacer
|
||||
{
|
||||
public interface IDownstreamPathPlaceholderReplacer
|
||||
{
|
||||
Response<DownstreamPath> Replace(DownstreamPathTemplate downstreamDownstreamPathTemplate, List<PlaceholderNameAndValue> urlPathPlaceholderNameAndValues);
|
||||
Response<DownstreamPath> Replace(DownstreamPathTemplate downstreamPathTemplate, List<PlaceholderNameAndValue> urlPathPlaceholderNameAndValues);
|
||||
}
|
||||
}
|
||||
|
@ -1,22 +1,29 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Ocelot.Configuration;
|
||||
using Ocelot.Infrastructure.Claims.Parser;
|
||||
using Ocelot.Responses;
|
||||
using System.Net.Http;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Ocelot.Configuration.Creator;
|
||||
using Ocelot.Request.Middleware;
|
||||
|
||||
namespace Ocelot.Headers
|
||||
namespace Ocelot.Headers
|
||||
{
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Infrastructure;
|
||||
using Logging;
|
||||
using Ocelot.Configuration;
|
||||
using Ocelot.Infrastructure.Claims.Parser;
|
||||
using Ocelot.Responses;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.Extensions.Primitives;
|
||||
using Ocelot.Configuration.Creator;
|
||||
using Ocelot.Middleware;
|
||||
using Ocelot.Request.Middleware;
|
||||
|
||||
public class AddHeadersToRequest : IAddHeadersToRequest
|
||||
{
|
||||
private readonly IClaimsParser _claimsParser;
|
||||
private readonly IPlaceholders _placeholders;
|
||||
private readonly IOcelotLogger _logger;
|
||||
|
||||
public AddHeadersToRequest(IClaimsParser claimsParser)
|
||||
public AddHeadersToRequest(IClaimsParser claimsParser, IPlaceholders placeholders, IOcelotLoggerFactory factory)
|
||||
{
|
||||
_logger = factory.CreateLogger<AddHeadersToRequest>();
|
||||
_claimsParser = claimsParser;
|
||||
_placeholders = placeholders;
|
||||
}
|
||||
|
||||
public Response SetHeadersOnDownstreamRequest(List<ClaimToThing> claimsToThings, IEnumerable<System.Security.Claims.Claim> claims, DownstreamRequest downstreamRequest)
|
||||
@ -46,6 +53,7 @@ namespace Ocelot.Headers
|
||||
public void SetHeadersOnDownstreamRequest(IEnumerable<AddHeader> headers, HttpContext context)
|
||||
{
|
||||
var requestHeader = context.Request.Headers;
|
||||
|
||||
foreach (var header in headers)
|
||||
{
|
||||
if (requestHeader.ContainsKey(header.Key))
|
||||
@ -53,8 +61,23 @@ namespace Ocelot.Headers
|
||||
requestHeader.Remove(header.Key);
|
||||
}
|
||||
|
||||
if (header.Value.StartsWith("{") && header.Value.EndsWith("}"))
|
||||
{
|
||||
var value = _placeholders.Get(header.Value);
|
||||
|
||||
if (value.IsError)
|
||||
{
|
||||
_logger.LogWarning($"Unable to add header to response {header.Key}: {header.Value}");
|
||||
continue;
|
||||
}
|
||||
|
||||
requestHeader.Add(header.Key, new StringValues(value.Data));
|
||||
}
|
||||
else
|
||||
{
|
||||
requestHeader.Add(header.Key, header.Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,15 +1,13 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Ocelot.Configuration;
|
||||
using Ocelot.Infrastructure;
|
||||
using Ocelot.Infrastructure.Extensions;
|
||||
using Ocelot.Middleware;
|
||||
using Ocelot.Middleware.Multiplexer;
|
||||
using Ocelot.Request.Middleware;
|
||||
using Ocelot.Responses;
|
||||
|
||||
namespace Ocelot.Headers
|
||||
{
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Ocelot.Configuration;
|
||||
using Ocelot.Infrastructure;
|
||||
using Ocelot.Infrastructure.Extensions;
|
||||
using Ocelot.Middleware;
|
||||
using Ocelot.Responses;
|
||||
|
||||
public class HttpResponseHeaderReplacer : IHttpResponseHeaderReplacer
|
||||
{
|
||||
private readonly IPlaceholders _placeholders;
|
||||
@ -19,8 +17,11 @@ namespace Ocelot.Headers
|
||||
_placeholders = placeholders;
|
||||
}
|
||||
|
||||
public Response Replace(DownstreamResponse response, List<HeaderFindAndReplace> fAndRs, DownstreamRequest request)
|
||||
public Response Replace(DownstreamContext context, List<HeaderFindAndReplace> fAndRs)
|
||||
{
|
||||
var response = context.DownstreamResponse;
|
||||
var request = context.DownstreamRequest;
|
||||
|
||||
foreach (var f in fAndRs)
|
||||
{
|
||||
var dict = response.Headers.ToDictionary(x => x.Key);
|
||||
|
@ -1,14 +1,12 @@
|
||||
using System.Collections.Generic;
|
||||
using Ocelot.Configuration;
|
||||
using Ocelot.Middleware;
|
||||
using Ocelot.Middleware.Multiplexer;
|
||||
using Ocelot.Request.Middleware;
|
||||
using Ocelot.Responses;
|
||||
|
||||
namespace Ocelot.Headers
|
||||
{
|
||||
using System.Collections.Generic;
|
||||
using Ocelot.Configuration;
|
||||
using Ocelot.Middleware;
|
||||
using Ocelot.Responses;
|
||||
|
||||
public interface IHttpResponseHeaderReplacer
|
||||
{
|
||||
Response Replace(DownstreamResponse response, List<HeaderFindAndReplace> fAndRs, DownstreamRequest httpRequestMessage);
|
||||
Response Replace(DownstreamContext context, List<HeaderFindAndReplace> fAndRs);
|
||||
}
|
||||
}
|
||||
|
@ -8,15 +8,15 @@ using Ocelot.Middleware;
|
||||
|
||||
namespace Ocelot.Headers.Middleware
|
||||
{
|
||||
public class HttpRequestHeadersBuilderMiddleware : OcelotMiddleware
|
||||
public class ClaimsToHeadersMiddleware : OcelotMiddleware
|
||||
{
|
||||
private readonly OcelotRequestDelegate _next;
|
||||
private readonly IAddHeadersToRequest _addHeadersToRequest;
|
||||
|
||||
public HttpRequestHeadersBuilderMiddleware(OcelotRequestDelegate next,
|
||||
public ClaimsToHeadersMiddleware(OcelotRequestDelegate next,
|
||||
IOcelotLoggerFactory loggerFactory,
|
||||
IAddHeadersToRequest addHeadersToRequest)
|
||||
:base(loggerFactory.CreateLogger<HttpRequestHeadersBuilderMiddleware>())
|
||||
:base(loggerFactory.CreateLogger<ClaimsToHeadersMiddleware>())
|
||||
{
|
||||
_next = next;
|
||||
_addHeadersToRequest = addHeadersToRequest;
|
||||
@ -26,7 +26,7 @@ namespace Ocelot.Headers.Middleware
|
||||
{
|
||||
if (context.DownstreamReRoute.ClaimsToHeaders.Any())
|
||||
{
|
||||
Logger.LogInformation($"{context.DownstreamReRoute.DownstreamDownstreamPathTemplate.Value} has instructions to convert claims to headers");
|
||||
Logger.LogInformation($"{context.DownstreamReRoute.DownstreamPathTemplate.Value} has instructions to convert claims to headers");
|
||||
|
||||
var response = _addHeadersToRequest.SetHeadersOnDownstreamRequest(context.DownstreamReRoute.ClaimsToHeaders, context.HttpContext.User.Claims, context.DownstreamRequest);
|
||||
|
@ -0,0 +1,13 @@
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Ocelot.Middleware.Pipeline;
|
||||
|
||||
namespace Ocelot.Headers.Middleware
|
||||
{
|
||||
public static class ClaimsToHeadersMiddlewareExtensions
|
||||
{
|
||||
public static IOcelotPipelineBuilder UseClaimsToHeadersMiddleware(this IOcelotPipelineBuilder builder)
|
||||
{
|
||||
return builder.UseMiddleware<ClaimsToHeadersMiddleware>();
|
||||
}
|
||||
}
|
||||
}
|
@ -45,7 +45,7 @@ namespace Ocelot.Headers.Middleware
|
||||
|
||||
var postFAndRs = context.DownstreamReRoute.DownstreamHeadersFindAndReplace;
|
||||
|
||||
_postReplacer.Replace(context.DownstreamResponse, postFAndRs, context.DownstreamRequest);
|
||||
_postReplacer.Replace(context, postFAndRs);
|
||||
|
||||
_addHeadersToResponse.Add(context.DownstreamReRoute.AddHeadersToDownstream, context.DownstreamResponse);
|
||||
}
|
||||
|
@ -1,13 +0,0 @@
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Ocelot.Middleware.Pipeline;
|
||||
|
||||
namespace Ocelot.Headers.Middleware
|
||||
{
|
||||
public static class HttpRequestHeadersBuilderMiddlewareExtensions
|
||||
{
|
||||
public static IOcelotPipelineBuilder UseHttpRequestHeadersBuilderMiddleware(this IOcelotPipelineBuilder builder)
|
||||
{
|
||||
return builder.UseMiddleware<HttpRequestHeadersBuilderMiddleware>();
|
||||
}
|
||||
}
|
||||
}
|
@ -1,47 +1,38 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Net.Http;
|
||||
using Ocelot.Infrastructure.RequestData;
|
||||
using Ocelot.Middleware;
|
||||
using Ocelot.Request.Middleware;
|
||||
using Ocelot.Responses;
|
||||
|
||||
namespace Ocelot.Infrastructure
|
||||
{
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Ocelot.Infrastructure.RequestData;
|
||||
using Ocelot.Middleware;
|
||||
using Ocelot.Request.Middleware;
|
||||
using Ocelot.Responses;
|
||||
|
||||
public class Placeholders : IPlaceholders
|
||||
{
|
||||
private readonly Dictionary<string, Func<Response<string>>> _placeholders;
|
||||
private readonly Dictionary<string, Func<DownstreamRequest, string>> _requestPlaceholders;
|
||||
private readonly IBaseUrlFinder _finder;
|
||||
private readonly IRequestScopedDataRepository _repo;
|
||||
private readonly IHttpContextAccessor _httpContextAccessor;
|
||||
|
||||
public Placeholders(IBaseUrlFinder finder, IRequestScopedDataRepository repo)
|
||||
public Placeholders(IBaseUrlFinder finder, IRequestScopedDataRepository repo, IHttpContextAccessor httpContextAccessor)
|
||||
{
|
||||
_repo = repo;
|
||||
_httpContextAccessor = httpContextAccessor;
|
||||
_finder = finder;
|
||||
_placeholders = new Dictionary<string, Func<Response<string>>>();
|
||||
_placeholders.Add("{BaseUrl}", () => new OkResponse<string>(_finder.Find()));
|
||||
_placeholders.Add("{TraceId}", () => {
|
||||
var traceId = _repo.Get<string>("TraceId");
|
||||
if(traceId.IsError)
|
||||
_placeholders = new Dictionary<string, Func<Response<string>>>
|
||||
{
|
||||
return new ErrorResponse<string>(traceId.Errors);
|
||||
}
|
||||
{ "{BaseUrl}", GetBaseUrl() },
|
||||
{ "{TraceId}", GetTraceId() },
|
||||
{ "{RemoteIpAddress}", GetRemoteIpAddress() }
|
||||
|
||||
return new OkResponse<string>(traceId.Data);
|
||||
});
|
||||
};
|
||||
|
||||
_requestPlaceholders = new Dictionary<string, Func<DownstreamRequest, string>>();
|
||||
_requestPlaceholders.Add("{DownstreamBaseUrl}", x => {
|
||||
var downstreamUrl = $"{x.Scheme}://{x.Host}";
|
||||
|
||||
if(x.Port != 80 && x.Port != 443)
|
||||
_requestPlaceholders = new Dictionary<string, Func<DownstreamRequest, string>>
|
||||
{
|
||||
downstreamUrl = $"{downstreamUrl}:{x.Port}";
|
||||
}
|
||||
|
||||
return $"{downstreamUrl}/";
|
||||
});
|
||||
{ "{DownstreamBaseUrl}", GetDownstreamBaseUrl() }
|
||||
};
|
||||
}
|
||||
|
||||
public Response<string> Get(string key)
|
||||
@ -67,5 +58,56 @@ namespace Ocelot.Infrastructure
|
||||
|
||||
return new ErrorResponse<string>(new CouldNotFindPlaceholderError(key));
|
||||
}
|
||||
|
||||
private Func<Response<string>> GetRemoteIpAddress()
|
||||
{
|
||||
return () =>
|
||||
{
|
||||
// this can blow up so adding try catch and return error
|
||||
try
|
||||
{
|
||||
var remoteIdAddress = _httpContextAccessor.HttpContext.Connection.RemoteIpAddress.ToString();
|
||||
return new OkResponse<string>(remoteIdAddress);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
return new ErrorResponse<string>(new CouldNotFindPlaceholderError("{RemoteIpAddress}"));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private Func<DownstreamRequest, string> GetDownstreamBaseUrl()
|
||||
{
|
||||
return x =>
|
||||
{
|
||||
var downstreamUrl = $"{x.Scheme}://{x.Host}";
|
||||
|
||||
if (x.Port != 80 && x.Port != 443)
|
||||
{
|
||||
downstreamUrl = $"{downstreamUrl}:{x.Port}";
|
||||
}
|
||||
|
||||
return $"{downstreamUrl}/";
|
||||
};
|
||||
}
|
||||
|
||||
private Func<Response<string>> GetTraceId()
|
||||
{
|
||||
return () =>
|
||||
{
|
||||
var traceId = _repo.Get<string>("TraceId");
|
||||
if (traceId.IsError)
|
||||
{
|
||||
return new ErrorResponse<string>(traceId.Errors);
|
||||
}
|
||||
|
||||
return new OkResponse<string>(traceId.Data);
|
||||
};
|
||||
}
|
||||
|
||||
private Func<Response<string>> GetBaseUrl()
|
||||
{
|
||||
return () => new OkResponse<string>(_finder.Find());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -7,25 +7,27 @@ namespace Ocelot.Middleware
|
||||
{
|
||||
public class DownstreamResponse
|
||||
{
|
||||
public DownstreamResponse(HttpContent content, HttpStatusCode statusCode, List<Header> headers)
|
||||
public DownstreamResponse(HttpContent content, HttpStatusCode statusCode, List<Header> headers, string reasonPhrase)
|
||||
{
|
||||
Content = content;
|
||||
StatusCode = statusCode;
|
||||
Headers = headers ?? new List<Header>();
|
||||
ReasonPhrase = reasonPhrase;
|
||||
}
|
||||
|
||||
public DownstreamResponse(HttpResponseMessage response)
|
||||
:this(response.Content, response.StatusCode, response.Headers.Select(x => new Header(x.Key, x.Value)).ToList())
|
||||
:this(response.Content, response.StatusCode, response.Headers.Select(x => new Header(x.Key, x.Value)).ToList(), response.ReasonPhrase)
|
||||
{
|
||||
}
|
||||
|
||||
public DownstreamResponse(HttpContent content, HttpStatusCode statusCode, IEnumerable<KeyValuePair<string, IEnumerable<string>>> headers)
|
||||
:this(content, statusCode, headers.Select(x => new Header(x.Key, x.Value)).ToList())
|
||||
public DownstreamResponse(HttpContent content, HttpStatusCode statusCode, IEnumerable<KeyValuePair<string, IEnumerable<string>>> headers, string reasonPhrase)
|
||||
:this(content, statusCode, headers.Select(x => new Header(x.Key, x.Value)).ToList(), reasonPhrase)
|
||||
{
|
||||
}
|
||||
|
||||
public HttpContent Content { get; }
|
||||
public HttpStatusCode StatusCode { get; }
|
||||
public List<Header> Headers { get; }
|
||||
public string ReasonPhrase {get;}
|
||||
}
|
||||
}
|
||||
|
@ -46,7 +46,7 @@ namespace Ocelot.Middleware.Multiplexer
|
||||
Headers = {ContentType = new MediaTypeHeaderValue("application/json")}
|
||||
};
|
||||
|
||||
originalContext.DownstreamResponse = new DownstreamResponse(stringContent, HttpStatusCode.OK, new List<KeyValuePair<string, IEnumerable<string>>>());
|
||||
originalContext.DownstreamResponse = new DownstreamResponse(stringContent, HttpStatusCode.OK, new List<KeyValuePair<string, IEnumerable<string>>>(), "cannot return from aggregate..which reason phrase would you use?");
|
||||
}
|
||||
|
||||
private static void MapAggregateError(DownstreamContext originalContext, List<DownstreamContext> downstreamContexts, int i)
|
||||
|
@ -15,6 +15,7 @@ using Ocelot.Request.Middleware;
|
||||
using Ocelot.Requester.Middleware;
|
||||
using Ocelot.RequestId.Middleware;
|
||||
using Ocelot.Responder.Middleware;
|
||||
using Ocelot.Security.Middleware;
|
||||
using Ocelot.WebSockets.Middleware;
|
||||
|
||||
namespace Ocelot.Middleware.Pipeline
|
||||
@ -48,6 +49,9 @@ namespace Ocelot.Middleware.Pipeline
|
||||
// Then we get the downstream route information
|
||||
builder.UseDownstreamRouteFinderMiddleware();
|
||||
|
||||
// This security module, IP whitelist blacklist, extended security mechanism
|
||||
builder.UseSecurityMiddleware();
|
||||
|
||||
//Expand other branch pipes
|
||||
if (pipelineConfiguration.MapWhenOcelotPipeline != null)
|
||||
{
|
||||
@ -87,7 +91,7 @@ namespace Ocelot.Middleware.Pipeline
|
||||
}
|
||||
|
||||
// The next thing we do is look at any claims transforms in case this is important for authorisation
|
||||
builder.UseClaimsBuilderMiddleware();
|
||||
builder.UseClaimsToClaimsMiddleware();
|
||||
|
||||
// Allow pre authorisation logic. The idea being people might want to run something custom before what is built in.
|
||||
builder.UseIfNotNull(pipelineConfiguration.PreAuthorisationMiddleware);
|
||||
@ -105,14 +109,14 @@ namespace Ocelot.Middleware.Pipeline
|
||||
builder.Use(pipelineConfiguration.AuthorisationMiddleware);
|
||||
}
|
||||
|
||||
// Now we can run any header transformation logic
|
||||
builder.UseHttpRequestHeadersBuilderMiddleware();
|
||||
// Now we can run the claims to headers transformation middleware
|
||||
builder.UseClaimsToHeadersMiddleware();
|
||||
|
||||
// Allow the user to implement their own query string manipulation logic
|
||||
builder.UseIfNotNull(pipelineConfiguration.PreQueryStringBuilderMiddleware);
|
||||
|
||||
// Now we can run any query string transformation logic
|
||||
builder.UseQueryStringBuilderMiddleware();
|
||||
// Now we can run any claims to query string transformation middleware
|
||||
builder.UseClaimsToQueryStringMiddleware();
|
||||
|
||||
// Get the load balancer for this request
|
||||
builder.UseLoadBalancingMiddleware();
|
||||
|
@ -1,22 +1,22 @@
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Ocelot.DownstreamRouteFinder.Middleware;
|
||||
using Ocelot.Infrastructure.RequestData;
|
||||
using Ocelot.Logging;
|
||||
using Ocelot.Middleware;
|
||||
|
||||
namespace Ocelot.QueryStrings.Middleware
|
||||
namespace Ocelot.QueryStrings.Middleware
|
||||
{
|
||||
public class QueryStringBuilderMiddleware : OcelotMiddleware
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Ocelot.DownstreamRouteFinder.Middleware;
|
||||
using Ocelot.Infrastructure.RequestData;
|
||||
using Ocelot.Logging;
|
||||
using Ocelot.Middleware;
|
||||
|
||||
public class ClaimsToQueryStringMiddleware : OcelotMiddleware
|
||||
{
|
||||
private readonly OcelotRequestDelegate _next;
|
||||
private readonly IAddQueriesToRequest _addQueriesToRequest;
|
||||
|
||||
public QueryStringBuilderMiddleware(OcelotRequestDelegate next,
|
||||
public ClaimsToQueryStringMiddleware(OcelotRequestDelegate next,
|
||||
IOcelotLoggerFactory loggerFactory,
|
||||
IAddQueriesToRequest addQueriesToRequest)
|
||||
: base(loggerFactory.CreateLogger<QueryStringBuilderMiddleware>())
|
||||
: base(loggerFactory.CreateLogger<ClaimsToQueryStringMiddleware>())
|
||||
{
|
||||
_next = next;
|
||||
_addQueriesToRequest = addQueriesToRequest;
|
||||
@ -26,7 +26,7 @@ namespace Ocelot.QueryStrings.Middleware
|
||||
{
|
||||
if (context.DownstreamReRoute.ClaimsToQueries.Any())
|
||||
{
|
||||
Logger.LogInformation($"{context.DownstreamReRoute.DownstreamDownstreamPathTemplate.Value} has instructions to convert claims to queries");
|
||||
Logger.LogInformation($"{context.DownstreamReRoute.DownstreamPathTemplate.Value} has instructions to convert claims to queries");
|
||||
|
||||
var response = _addQueriesToRequest.SetQueriesOnDownstreamRequest(context.DownstreamReRoute.ClaimsToQueries, context.HttpContext.User.Claims, context.DownstreamRequest);
|
||||
|
@ -0,0 +1,13 @@
|
||||
namespace Ocelot.QueryStrings.Middleware
|
||||
{
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Ocelot.Middleware.Pipeline;
|
||||
|
||||
public static class ClaimsToQueryStringMiddlewareExtensions
|
||||
{
|
||||
public static IOcelotPipelineBuilder UseClaimsToQueryStringMiddleware(this IOcelotPipelineBuilder builder)
|
||||
{
|
||||
return builder.UseMiddleware<ClaimsToQueryStringMiddleware>();
|
||||
}
|
||||
}
|
||||
}
|
@ -1,13 +0,0 @@
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Ocelot.Middleware.Pipeline;
|
||||
|
||||
namespace Ocelot.QueryStrings.Middleware
|
||||
{
|
||||
public static class QueryStringBuilderMiddlewareExtensions
|
||||
{
|
||||
public static IOcelotPipelineBuilder UseQueryStringBuilderMiddleware(this IOcelotPipelineBuilder builder)
|
||||
{
|
||||
return builder.UseMiddleware<QueryStringBuilderMiddleware>();
|
||||
}
|
||||
}
|
||||
}
|
@ -34,7 +34,7 @@ namespace Ocelot.RateLimit.Middleware
|
||||
// check if rate limiting is enabled
|
||||
if (!context.DownstreamReRoute.EnableEndpointEndpointRateLimiting)
|
||||
{
|
||||
Logger.LogInformation($"EndpointRateLimiting is not enabled for {context.DownstreamReRoute.DownstreamDownstreamPathTemplate.Value}");
|
||||
Logger.LogInformation($"EndpointRateLimiting is not enabled for {context.DownstreamReRoute.DownstreamPathTemplate.Value}");
|
||||
await _next.Invoke(context);
|
||||
return;
|
||||
}
|
||||
@ -45,7 +45,7 @@ namespace Ocelot.RateLimit.Middleware
|
||||
// check white list
|
||||
if (IsWhitelisted(identity, options))
|
||||
{
|
||||
Logger.LogInformation($"{context.DownstreamReRoute.DownstreamDownstreamPathTemplate.Value} is white listed from rate limiting");
|
||||
Logger.LogInformation($"{context.DownstreamReRoute.DownstreamPathTemplate.Value} is white listed from rate limiting");
|
||||
await _next.Invoke(context);
|
||||
return;
|
||||
}
|
||||
|
@ -2,6 +2,7 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Ocelot.RateLimit
|
||||
{
|
||||
@ -10,10 +11,11 @@ namespace Ocelot.RateLimit
|
||||
/// </summary>
|
||||
public struct RateLimitCounter
|
||||
{
|
||||
public RateLimitCounter(DateTime timestamp, long totalRequest)
|
||||
[JsonConstructor]
|
||||
public RateLimitCounter(DateTime timestamp, long totalRequests)
|
||||
{
|
||||
Timestamp = timestamp;
|
||||
TotalRequests = totalRequest;
|
||||
TotalRequests = totalRequests;
|
||||
}
|
||||
|
||||
public DateTime Timestamp { get; private set; }
|
||||
|
@ -51,7 +51,7 @@ namespace Ocelot.Requester
|
||||
handler.ServerCertificateCustomValidationCallback = (request, certificate, chain, errors) => true;
|
||||
|
||||
_logger
|
||||
.LogWarning($"You have ignored all SSL warnings by using DangerousAcceptAnyServerCertificateValidator for this DownstreamReRoute, UpstreamPathTemplate: {context.DownstreamReRoute.UpstreamPathTemplate}, DownstreamDownstreamPathTemplate: {context.DownstreamReRoute.DownstreamDownstreamPathTemplate}");
|
||||
.LogWarning($"You have ignored all SSL warnings by using DangerousAcceptAnyServerCertificateValidator for this DownstreamReRoute, UpstreamPathTemplate: {context.DownstreamReRoute.UpstreamPathTemplate}, DownstreamPathTemplate: {context.DownstreamReRoute.DownstreamPathTemplate}");
|
||||
}
|
||||
|
||||
var timeout = context.DownstreamReRoute.QosOptions.TimeoutValue == 0
|
||||
|
@ -27,7 +27,7 @@ namespace Ocelot.Requester.QoS
|
||||
return new OkResponse<DelegatingHandler>(handler(request, _ocelotLoggerFactory));
|
||||
}
|
||||
|
||||
return new ErrorResponse<DelegatingHandler>(new UnableToFindQoSProviderError($"could not find qosProvider for {request.DownstreamScheme}{request.DownstreamAddresses}{request.DownstreamDownstreamPathTemplate}"));
|
||||
return new ErrorResponse<DelegatingHandler>(new UnableToFindQoSProviderError($"could not find qosProvider for {request.DownstreamScheme}{request.DownstreamAddresses}{request.DownstreamPathTemplate}"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3,6 +3,7 @@ using System.Linq;
|
||||
using System.Net;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Http.Features;
|
||||
using Microsoft.Extensions.Primitives;
|
||||
using Ocelot.Headers;
|
||||
using Ocelot.Middleware;
|
||||
@ -43,14 +44,9 @@ namespace Ocelot.Responder
|
||||
AddHeaderIfDoesntExist(context, new Header("Content-Length", new []{ response.Content.Headers.ContentLength.ToString() }) );
|
||||
}
|
||||
|
||||
context.Response.OnStarting(state =>
|
||||
{
|
||||
var httpContext = (HttpContext)state;
|
||||
SetStatusCode(context, (int)response.StatusCode);
|
||||
|
||||
httpContext.Response.StatusCode = (int)response.StatusCode;
|
||||
|
||||
return Task.CompletedTask;
|
||||
}, context);
|
||||
context.Response.HttpContext.Features.Get<IHttpResponseFeature>().ReasonPhrase = response.ReasonPhrase;
|
||||
|
||||
using(content)
|
||||
{
|
||||
@ -62,9 +58,17 @@ namespace Ocelot.Responder
|
||||
}
|
||||
|
||||
public void SetErrorResponseOnContext(HttpContext context, int statusCode)
|
||||
{
|
||||
SetStatusCode(context, statusCode);
|
||||
}
|
||||
|
||||
private void SetStatusCode(HttpContext context, int statusCode)
|
||||
{
|
||||
if (!context.Response.HasStarted)
|
||||
{
|
||||
context.Response.StatusCode = statusCode;
|
||||
}
|
||||
}
|
||||
|
||||
private static void AddHeaderIfDoesntExist(HttpContext context, Header httpResponseHeader)
|
||||
{
|
||||
|
44
src/Ocelot/Security/IPSecurity/IPSecurityPolicy.cs
Normal file
44
src/Ocelot/Security/IPSecurity/IPSecurityPolicy.cs
Normal file
@ -0,0 +1,44 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Net;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Ocelot.Configuration;
|
||||
using Ocelot.Middleware;
|
||||
using Ocelot.Responses;
|
||||
|
||||
namespace Ocelot.Security.IPSecurity
|
||||
{
|
||||
public class IPSecurityPolicy : ISecurityPolicy
|
||||
{
|
||||
public async Task<Response> Security(DownstreamContext context)
|
||||
{
|
||||
IPAddress clientIp = context.HttpContext.Connection.RemoteIpAddress;
|
||||
SecurityOptions securityOptions = context.DownstreamReRoute.SecurityOptions;
|
||||
if (securityOptions == null)
|
||||
{
|
||||
return new OkResponse();
|
||||
}
|
||||
|
||||
if (securityOptions.IPBlockedList != null)
|
||||
{
|
||||
if (securityOptions.IPBlockedList.Exists(f => f == clientIp.ToString()))
|
||||
{
|
||||
var error = new UnauthenticatedError($" This request rejects access to {clientIp.ToString()} IP");
|
||||
return new ErrorResponse(error);
|
||||
}
|
||||
}
|
||||
|
||||
if (securityOptions.IPAllowedList != null && securityOptions.IPAllowedList.Count > 0)
|
||||
{
|
||||
if (!securityOptions.IPAllowedList.Exists(f => f == clientIp.ToString()))
|
||||
{
|
||||
var error = new UnauthenticatedError($"{clientIp.ToString()} does not allow access, the request is invalid");
|
||||
return new ErrorResponse(error);
|
||||
}
|
||||
}
|
||||
|
||||
return await Task.FromResult(new OkResponse());
|
||||
}
|
||||
}
|
||||
}
|
14
src/Ocelot/Security/ISecurityPolicy.cs
Normal file
14
src/Ocelot/Security/ISecurityPolicy.cs
Normal file
@ -0,0 +1,14 @@
|
||||
using Ocelot.Middleware;
|
||||
using Ocelot.Responses;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Ocelot.Security
|
||||
{
|
||||
public interface ISecurityPolicy
|
||||
{
|
||||
Task<Response> Security(DownstreamContext context);
|
||||
}
|
||||
}
|
43
src/Ocelot/Security/Middleware/SecurityMiddleware.cs
Normal file
43
src/Ocelot/Security/Middleware/SecurityMiddleware.cs
Normal file
@ -0,0 +1,43 @@
|
||||
using Ocelot.Logging;
|
||||
using Ocelot.Middleware;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Ocelot.Security.Middleware
|
||||
{
|
||||
public class SecurityMiddleware : OcelotMiddleware
|
||||
{
|
||||
private readonly OcelotRequestDelegate _next;
|
||||
private readonly IOcelotLogger _logger;
|
||||
private readonly IEnumerable<ISecurityPolicy> _securityPolicies;
|
||||
public SecurityMiddleware(IOcelotLoggerFactory loggerFactory,
|
||||
IEnumerable<ISecurityPolicy> securityPolicies,
|
||||
OcelotRequestDelegate next)
|
||||
: base(loggerFactory.CreateLogger<SecurityMiddleware>())
|
||||
{
|
||||
_logger = loggerFactory.CreateLogger<SecurityMiddleware>();
|
||||
_securityPolicies = securityPolicies;
|
||||
_next = next;
|
||||
}
|
||||
|
||||
public async Task Invoke(DownstreamContext context)
|
||||
{
|
||||
if (_securityPolicies != null)
|
||||
{
|
||||
foreach (var policie in _securityPolicies)
|
||||
{
|
||||
var result = await policie.Security(context);
|
||||
if (!result.IsError)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
this.SetPipelineError(context, result.Errors);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
await _next.Invoke(context);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,15 @@
|
||||
using Ocelot.Middleware.Pipeline;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Ocelot.Security.Middleware
|
||||
{
|
||||
public static class SecurityMiddlewareExtensions
|
||||
{
|
||||
public static IOcelotPipelineBuilder UseSecurityMiddleware(this IOcelotPipelineBuilder builder)
|
||||
{
|
||||
return builder.UseMiddleware<SecurityMiddleware>();
|
||||
}
|
||||
}
|
||||
}
|
@ -1,4 +1,9 @@
|
||||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
// Modified https://github.com/aspnet/Proxy websockets class to use in Ocelot.
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Net.WebSockets;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
@ -10,7 +15,10 @@ namespace Ocelot.WebSockets.Middleware
|
||||
{
|
||||
public class WebSocketsProxyMiddleware : OcelotMiddleware
|
||||
{
|
||||
private OcelotRequestDelegate _next;
|
||||
private static readonly string[] NotForwardedWebSocketHeaders = new[] { "Connection", "Host", "Upgrade", "Sec-WebSocket-Accept", "Sec-WebSocket-Protocol", "Sec-WebSocket-Key", "Sec-WebSocket-Version", "Sec-WebSocket-Extensions" };
|
||||
private const int DefaultWebSocketBufferSize = 4096;
|
||||
private const int StreamCopyBufferSize = 81920;
|
||||
private readonly OcelotRequestDelegate _next;
|
||||
|
||||
public WebSocketsProxyMiddleware(OcelotRequestDelegate next,
|
||||
IOcelotLoggerFactory loggerFactory)
|
||||
@ -19,6 +27,37 @@ namespace Ocelot.WebSockets.Middleware
|
||||
_next = next;
|
||||
}
|
||||
|
||||
private static async Task PumpWebSocket(WebSocket source, WebSocket destination, int bufferSize, CancellationToken cancellationToken)
|
||||
{
|
||||
if (bufferSize <= 0)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(bufferSize));
|
||||
}
|
||||
|
||||
var buffer = new byte[bufferSize];
|
||||
while (true)
|
||||
{
|
||||
WebSocketReceiveResult result;
|
||||
try
|
||||
{
|
||||
result = await source.ReceiveAsync(new ArraySegment<byte>(buffer), cancellationToken);
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
await destination.CloseOutputAsync(WebSocketCloseStatus.EndpointUnavailable, null, cancellationToken);
|
||||
return;
|
||||
}
|
||||
|
||||
if (result.MessageType == WebSocketMessageType.Close)
|
||||
{
|
||||
await destination.CloseOutputAsync(source.CloseStatus.Value, source.CloseStatusDescription, cancellationToken);
|
||||
return;
|
||||
}
|
||||
|
||||
await destination.SendAsync(new ArraySegment<byte>(buffer, 0, result.Count), result.MessageType, result.EndOfMessage, cancellationToken);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task Invoke(DownstreamContext context)
|
||||
{
|
||||
await Proxy(context.HttpContext, context.DownstreamRequest.ToUri());
|
||||
@ -26,88 +65,42 @@ namespace Ocelot.WebSockets.Middleware
|
||||
|
||||
private async Task Proxy(HttpContext context, string serverEndpoint)
|
||||
{
|
||||
var wsToUpstreamClient = await context.WebSockets.AcceptWebSocketAsync();
|
||||
|
||||
var wsToDownstreamService = new ClientWebSocket();
|
||||
|
||||
foreach (var requestHeader in context.Request.Headers)
|
||||
if (context == null)
|
||||
{
|
||||
// Do not copy the Sec-Websocket headers because it is specified by the own connection it will fail when you copy this one.
|
||||
if (requestHeader.Key.StartsWith("Sec-WebSocket"))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
wsToDownstreamService.Options.SetRequestHeader(requestHeader.Key, requestHeader.Value);
|
||||
throw new ArgumentNullException(nameof(context));
|
||||
}
|
||||
|
||||
var uri = new Uri(serverEndpoint);
|
||||
await wsToDownstreamService.ConnectAsync(uri, CancellationToken.None);
|
||||
|
||||
var receiveFromUpstreamSendToDownstream = Task.Run(async () =>
|
||||
if (serverEndpoint == null)
|
||||
{
|
||||
var buffer = new byte[1024 * 4];
|
||||
|
||||
var receiveSegment = new ArraySegment<byte>(buffer);
|
||||
|
||||
while (wsToUpstreamClient.State == WebSocketState.Open || wsToUpstreamClient.State == WebSocketState.CloseSent)
|
||||
{
|
||||
var result = await wsToUpstreamClient.ReceiveAsync(receiveSegment, CancellationToken.None);
|
||||
|
||||
var sendSegment = new ArraySegment<byte>(buffer, 0, result.Count);
|
||||
|
||||
if(result.MessageType == WebSocketMessageType.Close)
|
||||
{
|
||||
await wsToUpstreamClient.CloseOutputAsync(WebSocketCloseStatus.NormalClosure, "",
|
||||
CancellationToken.None);
|
||||
|
||||
await wsToDownstreamService.CloseOutputAsync(WebSocketCloseStatus.NormalClosure, "",
|
||||
CancellationToken.None);
|
||||
|
||||
break;
|
||||
throw new ArgumentNullException(nameof(serverEndpoint));
|
||||
}
|
||||
|
||||
await wsToDownstreamService.SendAsync(sendSegment, result.MessageType, result.EndOfMessage,
|
||||
CancellationToken.None);
|
||||
|
||||
if (wsToUpstreamClient.State != WebSocketState.Open)
|
||||
if (!context.WebSockets.IsWebSocketRequest)
|
||||
{
|
||||
await wsToDownstreamService.CloseAsync(WebSocketCloseStatus.Empty, "",
|
||||
CancellationToken.None);
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
var receiveFromDownstreamAndSendToUpstream = Task.Run(async () =>
|
||||
{
|
||||
var buffer = new byte[1024 * 4];
|
||||
|
||||
while (wsToDownstreamService.State == WebSocketState.Open || wsToDownstreamService.State == WebSocketState.CloseSent)
|
||||
{
|
||||
if (wsToUpstreamClient.State != WebSocketState.Open)
|
||||
{
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
var receiveSegment = new ArraySegment<byte>(buffer);
|
||||
var result = await wsToDownstreamService.ReceiveAsync(receiveSegment, CancellationToken.None);
|
||||
|
||||
if (result.MessageType == WebSocketMessageType.Close)
|
||||
{
|
||||
break;
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
|
||||
var sendSegment = new ArraySegment<byte>(buffer, 0, result.Count);
|
||||
var client = new ClientWebSocket();
|
||||
foreach (var protocol in context.WebSockets.WebSocketRequestedProtocols)
|
||||
{
|
||||
client.Options.AddSubProtocol(protocol);
|
||||
}
|
||||
|
||||
//send to upstream client
|
||||
await wsToUpstreamClient.SendAsync(sendSegment, result.MessageType, result.EndOfMessage,
|
||||
CancellationToken.None);
|
||||
foreach (var headerEntry in context.Request.Headers)
|
||||
{
|
||||
if (!NotForwardedWebSocketHeaders.Contains(headerEntry.Key, StringComparer.OrdinalIgnoreCase))
|
||||
{
|
||||
client.Options.SetRequestHeader(headerEntry.Key, headerEntry.Value);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
await Task.WhenAll(receiveFromDownstreamAndSendToUpstream, receiveFromUpstreamSendToDownstream);
|
||||
var destinationUri = new Uri(serverEndpoint);
|
||||
await client.ConnectAsync(destinationUri, context.RequestAborted);
|
||||
using (var server = await context.WebSockets.AcceptWebSocketAsync(client.SubProtocol))
|
||||
{
|
||||
var bufferSize = DefaultWebSocketBufferSize;
|
||||
await Task.WhenAll(PumpWebSocket(client, server, bufferSize, context.RequestAborted), PumpWebSocket(server, client, bufferSize, context.RequestAborted));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -31,6 +31,116 @@ namespace Ocelot.AcceptanceTests
|
||||
_steps = new Steps();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_fix_issue_597()
|
||||
{
|
||||
var configuration = new FileConfiguration
|
||||
{
|
||||
ReRoutes = new List<FileReRoute>
|
||||
{
|
||||
new FileReRoute
|
||||
{
|
||||
DownstreamPathTemplate = "/api/values?MailId={userid}",
|
||||
UpstreamPathTemplate = "/key1data/{userid}",
|
||||
UpstreamHttpMethod = new List<string> {"Get"},
|
||||
DownstreamScheme = "http",
|
||||
DownstreamHostAndPorts = new List<FileHostAndPort>
|
||||
{
|
||||
new FileHostAndPort
|
||||
{
|
||||
Host = "localhost",
|
||||
Port = 8571
|
||||
}
|
||||
},
|
||||
Key = "key1"
|
||||
},
|
||||
new FileReRoute
|
||||
{
|
||||
DownstreamPathTemplate = "/api/values?MailId={userid}",
|
||||
UpstreamPathTemplate = "/key2data/{userid}",
|
||||
UpstreamHttpMethod = new List<string> {"Get"},
|
||||
DownstreamScheme = "http",
|
||||
DownstreamHostAndPorts = new List<FileHostAndPort>
|
||||
{
|
||||
new FileHostAndPort
|
||||
{
|
||||
Host = "localhost",
|
||||
Port = 8571
|
||||
}
|
||||
},
|
||||
Key = "key2"
|
||||
},
|
||||
new FileReRoute
|
||||
{
|
||||
DownstreamPathTemplate = "/api/values?MailId={userid}",
|
||||
UpstreamPathTemplate = "/key3data/{userid}",
|
||||
UpstreamHttpMethod = new List<string> {"Get"},
|
||||
DownstreamScheme = "http",
|
||||
DownstreamHostAndPorts = new List<FileHostAndPort>
|
||||
{
|
||||
new FileHostAndPort
|
||||
{
|
||||
Host = "localhost",
|
||||
Port = 8571
|
||||
}
|
||||
},
|
||||
Key = "key3"
|
||||
},
|
||||
new FileReRoute
|
||||
{
|
||||
DownstreamPathTemplate = "/api/values?MailId={userid}",
|
||||
UpstreamPathTemplate = "/key4data/{userid}",
|
||||
UpstreamHttpMethod = new List<string> {"Get"},
|
||||
DownstreamScheme = "http",
|
||||
DownstreamHostAndPorts = new List<FileHostAndPort>
|
||||
{
|
||||
new FileHostAndPort
|
||||
{
|
||||
Host = "localhost",
|
||||
Port = 8571
|
||||
}
|
||||
},
|
||||
Key = "key4"
|
||||
},
|
||||
},
|
||||
Aggregates = new List<FileAggregateReRoute>
|
||||
{
|
||||
new FileAggregateReRoute
|
||||
{
|
||||
ReRouteKeys = new List<string>{
|
||||
"key1",
|
||||
"key2",
|
||||
"key3",
|
||||
"key4"
|
||||
},
|
||||
UpstreamPathTemplate = "/EmpDetail/IN/{userid}"
|
||||
},
|
||||
new FileAggregateReRoute
|
||||
{
|
||||
ReRouteKeys = new List<string>{
|
||||
"key1",
|
||||
"key2",
|
||||
},
|
||||
UpstreamPathTemplate = "/EmpDetail/US/{userid}"
|
||||
}
|
||||
},
|
||||
GlobalConfiguration = new FileGlobalConfiguration
|
||||
{
|
||||
RequestIdKey = "CorrelationID"
|
||||
}
|
||||
};
|
||||
|
||||
var expected = "{\"key1\":some_data,\"key2\":some_data}";
|
||||
|
||||
this.Given(x => x.GivenServiceIsRunning("http://localhost:8571", 200, "some_data"))
|
||||
.And(x => _steps.GivenThereIsAConfiguration(configuration))
|
||||
.And(x => _steps.GivenOcelotIsRunning())
|
||||
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/EmpDetail/US/1"))
|
||||
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
|
||||
.And(x => _steps.ThenTheResponseBodyShouldBe(expected))
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_return_response_200_with_simple_url_user_defined_aggregate()
|
||||
{
|
||||
@ -368,6 +478,15 @@ namespace Ocelot.AcceptanceTests
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
private void GivenServiceIsRunning(string baseUrl, int statusCode, string responseBody)
|
||||
{
|
||||
_serviceHandler.GivenThereIsAServiceRunningOn(baseUrl, async context =>
|
||||
{
|
||||
context.Response.StatusCode = statusCode;
|
||||
await context.Response.WriteAsync(responseBody);
|
||||
});
|
||||
}
|
||||
|
||||
private void GivenServiceOneIsRunning(string baseUrl, string basePath, int statusCode, string responseBody)
|
||||
{
|
||||
_serviceHandler.GivenThereIsAServiceRunningOn(baseUrl, basePath, async context =>
|
||||
@ -440,7 +559,7 @@ namespace Ocelot.AcceptanceTests
|
||||
var merge = $"{one}, {two}";
|
||||
merge = merge.Replace("Hello", "Bye").Replace("{", "").Replace("}", "");
|
||||
var headers = responses.SelectMany(x => x.Headers).ToList();
|
||||
return new DownstreamResponse(new StringContent(merge), HttpStatusCode.OK, headers);
|
||||
return new DownstreamResponse(new StringContent(merge), HttpStatusCode.OK, headers, "some reason");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -4,6 +4,7 @@ namespace Ocelot.AcceptanceTests
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Ocelot.Configuration.File;
|
||||
|
76
test/Ocelot.AcceptanceTests/ReasonPhraseTests.cs
Normal file
76
test/Ocelot.AcceptanceTests/ReasonPhraseTests.cs
Normal file
@ -0,0 +1,76 @@
|
||||
namespace Ocelot.AcceptanceTests
|
||||
{
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Net;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Http.Features;
|
||||
using Ocelot.Configuration.File;
|
||||
using Shouldly;
|
||||
using TestStack.BDDfy;
|
||||
using Xunit;
|
||||
|
||||
public class ReasonPhraseTests : IDisposable
|
||||
{
|
||||
private readonly Steps _steps;
|
||||
private string _contentType;
|
||||
private long? _contentLength;
|
||||
private bool _contentTypeHeaderExists;
|
||||
private readonly ServiceHandler _serviceHandler;
|
||||
|
||||
public ReasonPhraseTests()
|
||||
{
|
||||
_serviceHandler = new ServiceHandler();
|
||||
_steps = new Steps();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_return_reason_phrase()
|
||||
{
|
||||
var configuration = new FileConfiguration
|
||||
{
|
||||
ReRoutes = new List<FileReRoute>
|
||||
{
|
||||
new FileReRoute
|
||||
{
|
||||
DownstreamPathTemplate = "/",
|
||||
DownstreamScheme = "http",
|
||||
DownstreamHostAndPorts = new List<FileHostAndPort>
|
||||
{
|
||||
new FileHostAndPort
|
||||
{
|
||||
Host = "localhost",
|
||||
Port = 51339,
|
||||
}
|
||||
},
|
||||
UpstreamPathTemplate = "/",
|
||||
UpstreamHttpMethod = new List<string> { "Get" },
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:51339", "/", "some reason"))
|
||||
.And(x => _steps.GivenThereIsAConfiguration(configuration))
|
||||
.And(x => _steps.GivenOcelotIsRunning())
|
||||
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
|
||||
.And(_ => _steps.ThenTheReasonPhraseIs("some reason"))
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
private void GivenThereIsAServiceRunningOn(string baseUrl, string basePath, string reasonPhrase)
|
||||
{
|
||||
_serviceHandler.GivenThereIsAServiceRunningOn(baseUrl, basePath, async context =>
|
||||
{
|
||||
context.Response.HttpContext.Features.Get<IHttpResponseFeature>().ReasonPhrase = reasonPhrase;
|
||||
|
||||
await context.Response.WriteAsync("YOYO!");
|
||||
});
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_serviceHandler?.Dispose();
|
||||
_steps.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
@ -452,6 +452,11 @@
|
||||
header.First().ShouldBe(value);
|
||||
}
|
||||
|
||||
public void ThenTheReasonPhraseIs(string expected)
|
||||
{
|
||||
_response.ReasonPhrase.ShouldBe(expected);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This is annoying cos it should be in the constructor but we need to set up the file before calling startup so its a step.
|
||||
/// </summary>
|
||||
|
@ -0,0 +1,83 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using BenchmarkDotNet.Attributes;
|
||||
using BenchmarkDotNet.Columns;
|
||||
using BenchmarkDotNet.Configs;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Newtonsoft.Json;
|
||||
using Ocelot.Configuration.File;
|
||||
using Ocelot.DownstreamRouteFinder.UrlMatcher;
|
||||
using Ocelot.Middleware;
|
||||
using Ocelot.DependencyInjection;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using BenchmarkDotNet.Attributes.Jobs;
|
||||
using Ocelot.Configuration.Repository;
|
||||
using Ocelot.Infrastructure.RequestData;
|
||||
using Ocelot.Logging;
|
||||
using Ocelot.Errors.Middleware;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using BenchmarkDotNet.Diagnosers;
|
||||
using BenchmarkDotNet.Validators;
|
||||
using Ocelot.DownstreamRouteFinder.Middleware;
|
||||
using Ocelot.DownstreamRouteFinder.Finder;
|
||||
using Ocelot.Middleware.Multiplexer;
|
||||
using Ocelot.Configuration;
|
||||
|
||||
namespace Ocelot.Benchmarks
|
||||
{
|
||||
[SimpleJob(launchCount: 1, warmupCount: 2, targetCount: 5)]
|
||||
[Config(typeof(DownstreamRouteFinderMiddlewareBenchmarks))]
|
||||
public class DownstreamRouteFinderMiddlewareBenchmarks : ManualConfig
|
||||
{
|
||||
private DownstreamRouteFinderMiddleware _middleware;
|
||||
private DownstreamContext _downstreamContext;
|
||||
private OcelotRequestDelegate _next;
|
||||
|
||||
public DownstreamRouteFinderMiddlewareBenchmarks()
|
||||
{
|
||||
Add(StatisticColumn.AllStatistics);
|
||||
Add(MemoryDiagnoser.Default);
|
||||
Add(BaselineValidator.FailOnError);
|
||||
}
|
||||
|
||||
[GlobalSetup]
|
||||
public void SetUp()
|
||||
{
|
||||
var serviceCollection = new ServiceCollection();
|
||||
var config = new ConfigurationRoot(new List<IConfigurationProvider>());
|
||||
var builder = new OcelotBuilder(serviceCollection, config);
|
||||
var services = serviceCollection.BuildServiceProvider();
|
||||
var loggerFactory = services.GetService<IOcelotLoggerFactory>();
|
||||
var drpf = services.GetService<IDownstreamRouteProviderFactory>();
|
||||
var multiplexer = services.GetService<IMultiplexer>();
|
||||
|
||||
_next = async context => {
|
||||
await Task.CompletedTask;
|
||||
throw new Exception("BOOM");
|
||||
};
|
||||
|
||||
_middleware = new DownstreamRouteFinderMiddleware(_next, loggerFactory, drpf, multiplexer);
|
||||
var httpContext = new DefaultHttpContext();
|
||||
httpContext.Request.Path = new PathString("/test");
|
||||
httpContext.Request.QueryString = new QueryString("?a=b");
|
||||
httpContext.Request.Headers.Add("Host", "most");
|
||||
|
||||
_downstreamContext = new DownstreamContext(httpContext)
|
||||
{
|
||||
Configuration = new InternalConfiguration(new List<ReRoute>(), null, null, null, null, null, null, null)
|
||||
};
|
||||
}
|
||||
|
||||
[Benchmark(Baseline = true)]
|
||||
public async Task Baseline()
|
||||
{
|
||||
await _middleware.Invoke(_downstreamContext);
|
||||
}
|
||||
}
|
||||
}
|
@ -10,7 +10,8 @@ namespace Ocelot.Benchmarks
|
||||
typeof(DictionaryBenchmarks),
|
||||
typeof(UrlPathToUrlPathTemplateMatcherBenchmarks),
|
||||
typeof(AllTheThingsBenchmarks),
|
||||
typeof(ExceptionHandlerMiddlewareBenchmarks)
|
||||
typeof(ExceptionHandlerMiddlewareBenchmarks),
|
||||
typeof(DownstreamRouteFinderMiddlewareBenchmarks)
|
||||
});
|
||||
|
||||
switcher.Run(args);
|
||||
|
199
test/Ocelot.IntegrationTests/HeaderTests.cs
Normal file
199
test/Ocelot.IntegrationTests/HeaderTests.cs
Normal file
@ -0,0 +1,199 @@
|
||||
using Xunit;
|
||||
|
||||
[assembly: CollectionBehavior(DisableTestParallelization = true)]
|
||||
|
||||
namespace Ocelot.IntegrationTests
|
||||
{
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Net.Http;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Newtonsoft.Json;
|
||||
using Ocelot.Configuration.File;
|
||||
using Shouldly;
|
||||
using TestStack.BDDfy;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Ocelot.DependencyInjection;
|
||||
using Ocelot.Middleware;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
|
||||
public class HeaderTests : IDisposable
|
||||
{
|
||||
private readonly HttpClient _httpClient;
|
||||
private IWebHost _builder;
|
||||
private IWebHostBuilder _webHostBuilder;
|
||||
private readonly string _ocelotBaseUrl;
|
||||
private IWebHost _downstreamBuilder;
|
||||
private HttpResponseMessage _response;
|
||||
|
||||
public HeaderTests()
|
||||
{
|
||||
_httpClient = new HttpClient();
|
||||
_ocelotBaseUrl = "http://localhost:5005";
|
||||
_httpClient.BaseAddress = new Uri(_ocelotBaseUrl);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_pass_remote_ip_address_if_as_x_forwarded_for_header()
|
||||
{
|
||||
var configuration = new FileConfiguration
|
||||
{
|
||||
ReRoutes = new List<FileReRoute>
|
||||
{
|
||||
new FileReRoute
|
||||
{
|
||||
DownstreamPathTemplate = "/",
|
||||
DownstreamScheme = "http",
|
||||
DownstreamHostAndPorts = new List<FileHostAndPort>
|
||||
{
|
||||
new FileHostAndPort
|
||||
{
|
||||
Host = "localhost",
|
||||
Port = 6773,
|
||||
}
|
||||
},
|
||||
UpstreamPathTemplate = "/",
|
||||
UpstreamHttpMethod = new List<string> { "Get" },
|
||||
UpstreamHeaderTransform = new Dictionary<string,string>
|
||||
{
|
||||
{"X-Forwarded-For", "{RemoteIpAddress}"}
|
||||
},
|
||||
HttpHandlerOptions = new FileHttpHandlerOptions
|
||||
{
|
||||
AllowAutoRedirect = false
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
this.Given(x => GivenThereIsAServiceRunningOn("http://localhost:6773", 200, "X-Forwarded-For"))
|
||||
.And(x => GivenThereIsAConfiguration(configuration))
|
||||
.And(x => GivenOcelotIsRunning())
|
||||
.When(x => WhenIGetUrlOnTheApiGateway("/"))
|
||||
.Then(x => ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
|
||||
.And(x => ThenXForwardedForIsSet())
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
private void GivenThereIsAServiceRunningOn(string url, int statusCode, string headerKey)
|
||||
{
|
||||
_downstreamBuilder = new WebHostBuilder()
|
||||
.UseUrls(url)
|
||||
.UseKestrel()
|
||||
.UseContentRoot(Directory.GetCurrentDirectory())
|
||||
.UseIISIntegration()
|
||||
.UseUrls(url)
|
||||
.Configure(app =>
|
||||
{
|
||||
app.Run(async context =>
|
||||
{
|
||||
if (context.Request.Headers.TryGetValue(headerKey, out var values))
|
||||
{
|
||||
var result = values.First();
|
||||
context.Response.StatusCode = statusCode;
|
||||
await context.Response.WriteAsync(result);
|
||||
}
|
||||
});
|
||||
})
|
||||
.Build();
|
||||
|
||||
_downstreamBuilder.Start();
|
||||
}
|
||||
|
||||
private void GivenOcelotIsRunning()
|
||||
{
|
||||
_webHostBuilder = new WebHostBuilder()
|
||||
.UseUrls(_ocelotBaseUrl)
|
||||
.UseKestrel()
|
||||
.UseContentRoot(Directory.GetCurrentDirectory())
|
||||
.ConfigureAppConfiguration((hostingContext, config) =>
|
||||
{
|
||||
config.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath);
|
||||
var env = hostingContext.HostingEnvironment;
|
||||
config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: false)
|
||||
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: false);
|
||||
config.AddJsonFile("ocelot.json", false, false);
|
||||
config.AddEnvironmentVariables();
|
||||
})
|
||||
.ConfigureServices(x =>
|
||||
{
|
||||
x.AddOcelot();
|
||||
})
|
||||
.Configure(app =>
|
||||
{
|
||||
app.UseOcelot().Wait();
|
||||
});
|
||||
|
||||
_builder = _webHostBuilder.Build();
|
||||
|
||||
_builder.Start();
|
||||
}
|
||||
|
||||
private void GivenThereIsAConfiguration(FileConfiguration fileConfiguration)
|
||||
{
|
||||
var configurationPath = $"{Directory.GetCurrentDirectory()}/ocelot.json";
|
||||
|
||||
var jsonConfiguration = JsonConvert.SerializeObject(fileConfiguration);
|
||||
|
||||
if (File.Exists(configurationPath))
|
||||
{
|
||||
File.Delete(configurationPath);
|
||||
}
|
||||
|
||||
File.WriteAllText(configurationPath, jsonConfiguration);
|
||||
|
||||
var text = File.ReadAllText(configurationPath);
|
||||
|
||||
configurationPath = $"{AppContext.BaseDirectory}/ocelot.json";
|
||||
|
||||
if (File.Exists(configurationPath))
|
||||
{
|
||||
File.Delete(configurationPath);
|
||||
}
|
||||
|
||||
File.WriteAllText(configurationPath, jsonConfiguration);
|
||||
|
||||
text = File.ReadAllText(configurationPath);
|
||||
}
|
||||
|
||||
public async Task WhenIGetUrlOnTheApiGateway(string url)
|
||||
{
|
||||
var request = new HttpRequestMessage(HttpMethod.Get, url);
|
||||
_response = await _httpClient.SendAsync(request);
|
||||
}
|
||||
|
||||
private void ThenTheStatusCodeShouldBe(HttpStatusCode code)
|
||||
{
|
||||
_response.StatusCode.ShouldBe(code);
|
||||
}
|
||||
|
||||
private void ThenXForwardedForIsSet()
|
||||
{
|
||||
var windowsOrMac = "::1";
|
||||
var linux = "127.0.0.1";
|
||||
|
||||
var header = _response.Content.ReadAsStringAsync().Result;
|
||||
|
||||
bool passed = false;
|
||||
|
||||
if(header == windowsOrMac || header == linux)
|
||||
{
|
||||
passed = true;
|
||||
}
|
||||
|
||||
passed.ShouldBeTrue();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_builder?.Dispose();
|
||||
_httpClient?.Dispose();
|
||||
_downstreamBuilder?.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
@ -46,6 +46,20 @@ namespace Ocelot.UnitTests.Authentication
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_call_next_middleware_if_route_is_using_options_method()
|
||||
{
|
||||
this.Given(x => GivenTheDownStreamRouteIs(
|
||||
new DownstreamReRouteBuilder()
|
||||
.WithUpstreamHttpMethod(new List<string> { "Options" })
|
||||
.WithIsAuthenticated(true)
|
||||
.Build()))
|
||||
.And(x => GivenTheRequestIsUsingOptionsMethod())
|
||||
.When(x => WhenICallTheMiddleware())
|
||||
.Then(x => ThenTheUserIsAuthenticated())
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
private void WhenICallTheMiddleware()
|
||||
{
|
||||
_next = (context) => {
|
||||
@ -68,6 +82,11 @@ namespace Ocelot.UnitTests.Authentication
|
||||
};
|
||||
}
|
||||
|
||||
private void GivenTheRequestIsUsingOptionsMethod()
|
||||
{
|
||||
_downstreamContext.HttpContext.Request.Method = "OPTIONS";
|
||||
}
|
||||
|
||||
private void ThenTheUserIsAuthenticated()
|
||||
{
|
||||
var content = _downstreamContext.HttpContext.Response.Body.AsString();
|
||||
@ -84,7 +103,7 @@ namespace Ocelot.UnitTests.Authentication
|
||||
{
|
||||
public static string AsString(this Stream stream)
|
||||
{
|
||||
using(var reader = new StreamReader(stream))
|
||||
using (var reader = new StreamReader(stream))
|
||||
{
|
||||
string text = reader.ReadToEnd();
|
||||
return text;
|
||||
|
@ -53,7 +53,7 @@
|
||||
{ "content-type", new List<string> { "application/json" } }
|
||||
};
|
||||
|
||||
var cachedResponse = new CachedResponse(HttpStatusCode.OK, headers, "", contentHeaders);
|
||||
var cachedResponse = new CachedResponse(HttpStatusCode.OK, headers, "", contentHeaders, "some reason");
|
||||
this.Given(x => x.GivenThereIsACachedResponse(cachedResponse))
|
||||
.And(x => x.GivenTheDownstreamRouteIs())
|
||||
.When(x => x.WhenICallTheMiddleware())
|
||||
@ -69,7 +69,7 @@
|
||||
{ "Expires", new List<string> { "-1" } }
|
||||
};
|
||||
|
||||
var cachedResponse = new CachedResponse(HttpStatusCode.OK, new Dictionary<string, IEnumerable<string>>(), "", contentHeaders);
|
||||
var cachedResponse = new CachedResponse(HttpStatusCode.OK, new Dictionary<string, IEnumerable<string>>(), "", contentHeaders, "some reason");
|
||||
this.Given(x => x.GivenThereIsACachedResponse(cachedResponse))
|
||||
.And(x => x.GivenTheDownstreamRouteIs())
|
||||
.When(x => x.WhenICallTheMiddleware())
|
||||
|
@ -17,24 +17,24 @@ namespace Ocelot.UnitTests.Claims
|
||||
using TestStack.BDDfy;
|
||||
using Xunit;
|
||||
|
||||
public class ClaimsBuilderMiddlewareTests
|
||||
public class ClaimsToClaimsMiddlewareTests
|
||||
{
|
||||
private readonly Mock<IAddClaimsToRequest> _addHeaders;
|
||||
private Mock<IOcelotLoggerFactory> _loggerFactory;
|
||||
private Mock<IOcelotLogger> _logger;
|
||||
private readonly ClaimsBuilderMiddleware _middleware;
|
||||
private readonly ClaimsToClaimsMiddleware _middleware;
|
||||
private readonly DownstreamContext _downstreamContext;
|
||||
private OcelotRequestDelegate _next;
|
||||
|
||||
public ClaimsBuilderMiddlewareTests()
|
||||
public ClaimsToClaimsMiddlewareTests()
|
||||
{
|
||||
_addHeaders = new Mock<IAddClaimsToRequest>();
|
||||
_downstreamContext = new DownstreamContext(new DefaultHttpContext());
|
||||
_loggerFactory = new Mock<IOcelotLoggerFactory>();
|
||||
_logger = new Mock<IOcelotLogger>();
|
||||
_loggerFactory.Setup(x => x.CreateLogger<ClaimsBuilderMiddleware>()).Returns(_logger.Object);
|
||||
_loggerFactory.Setup(x => x.CreateLogger<ClaimsToClaimsMiddleware>()).Returns(_logger.Object);
|
||||
_next = context => Task.CompletedTask;
|
||||
_middleware = new ClaimsBuilderMiddleware(_next, _loggerFactory.Object, _addHeaders.Object);
|
||||
_middleware = new ClaimsToClaimsMiddleware(_next, _loggerFactory.Object, _addHeaders.Object);
|
||||
}
|
||||
|
||||
[Fact]
|
164
test/Ocelot.UnitTests/Configuration/AggregatesCreatorTests.cs
Normal file
164
test/Ocelot.UnitTests/Configuration/AggregatesCreatorTests.cs
Normal file
@ -0,0 +1,164 @@
|
||||
namespace Ocelot.UnitTests.Configuration
|
||||
{
|
||||
using System.Collections.Generic;
|
||||
using System.Net.Http;
|
||||
using Moq;
|
||||
using Ocelot.Configuration;
|
||||
using Ocelot.Configuration.Builder;
|
||||
using Ocelot.Configuration.Creator;
|
||||
using Ocelot.Configuration.File;
|
||||
using Values;
|
||||
using Shouldly;
|
||||
using TestStack.BDDfy;
|
||||
using Xunit;
|
||||
|
||||
public class AggregatesCreatorTests
|
||||
{
|
||||
private readonly AggregatesCreator _creator;
|
||||
private readonly Mock<IUpstreamTemplatePatternCreator> _utpCreator;
|
||||
private FileConfiguration _fileConfiguration;
|
||||
private List<ReRoute> _reRoutes;
|
||||
private List<ReRoute> _result;
|
||||
private UpstreamPathTemplate _aggregate1Utp;
|
||||
private UpstreamPathTemplate _aggregate2Utp;
|
||||
|
||||
public AggregatesCreatorTests()
|
||||
{
|
||||
_utpCreator = new Mock<IUpstreamTemplatePatternCreator>();
|
||||
_creator = new AggregatesCreator(_utpCreator.Object);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_return_no_aggregates()
|
||||
{
|
||||
var fileConfig = new FileConfiguration
|
||||
{
|
||||
Aggregates = new List<FileAggregateReRoute>
|
||||
{
|
||||
new FileAggregateReRoute
|
||||
{
|
||||
ReRouteKeys = new List<string>{"key1"}
|
||||
}
|
||||
}
|
||||
};
|
||||
var reRoutes = new List<ReRoute>();
|
||||
|
||||
this.Given(_ => GivenThe(fileConfig))
|
||||
.And(_ => GivenThe(reRoutes))
|
||||
.When(_ => WhenICreate())
|
||||
.Then(_ => TheUtpCreatorIsNotCalled())
|
||||
.And(_ => ThenTheResultIsNotNull())
|
||||
.And(_ => ThenTheResultIsEmpty())
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_create_aggregates()
|
||||
{
|
||||
var fileConfig = new FileConfiguration
|
||||
{
|
||||
Aggregates = new List<FileAggregateReRoute>
|
||||
{
|
||||
new FileAggregateReRoute
|
||||
{
|
||||
ReRouteKeys = new List<string>{"key1", "key2"},
|
||||
UpstreamHost = "hosty",
|
||||
UpstreamPathTemplate = "templatey",
|
||||
Aggregator = "aggregatory",
|
||||
ReRouteIsCaseSensitive = true
|
||||
},
|
||||
new FileAggregateReRoute
|
||||
{
|
||||
ReRouteKeys = new List<string>{"key3", "key4"},
|
||||
UpstreamHost = "hosty",
|
||||
UpstreamPathTemplate = "templatey",
|
||||
Aggregator = "aggregatory",
|
||||
ReRouteIsCaseSensitive = true
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var reRoutes = new List<ReRoute>
|
||||
{
|
||||
new ReRouteBuilder().WithDownstreamReRoute(new DownstreamReRouteBuilder().WithKey("key1").Build()).Build(),
|
||||
new ReRouteBuilder().WithDownstreamReRoute(new DownstreamReRouteBuilder().WithKey("key2").Build()).Build(),
|
||||
new ReRouteBuilder().WithDownstreamReRoute(new DownstreamReRouteBuilder().WithKey("key3").Build()).Build(),
|
||||
new ReRouteBuilder().WithDownstreamReRoute(new DownstreamReRouteBuilder().WithKey("key4").Build()).Build()
|
||||
};
|
||||
|
||||
this.Given(_ => GivenThe(fileConfig))
|
||||
.And(_ => GivenThe(reRoutes))
|
||||
.And(_ => GivenTheUtpCreatorReturns())
|
||||
.When(_ => WhenICreate())
|
||||
.Then(_ => ThenTheUtpCreatorIsCalledCorrectly())
|
||||
.And(_ => ThenTheAggregatesAreCreated())
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
private void ThenTheAggregatesAreCreated()
|
||||
{
|
||||
_result.ShouldNotBeNull();
|
||||
_result.Count.ShouldBe(2);
|
||||
|
||||
_result[0].UpstreamHttpMethod.ShouldContain(x => x == HttpMethod.Get);
|
||||
_result[0].UpstreamHost.ShouldBe(_fileConfiguration.Aggregates[0].UpstreamHost);
|
||||
_result[0].UpstreamTemplatePattern.ShouldBe(_aggregate1Utp);
|
||||
_result[0].Aggregator.ShouldBe(_fileConfiguration.Aggregates[0].Aggregator);
|
||||
_result[0].DownstreamReRoute.ShouldContain(x => x == _reRoutes[0].DownstreamReRoute[0]);
|
||||
_result[0].DownstreamReRoute.ShouldContain(x => x == _reRoutes[1].DownstreamReRoute[0]);
|
||||
|
||||
_result[1].UpstreamHttpMethod.ShouldContain(x => x == HttpMethod.Get);
|
||||
_result[1].UpstreamHost.ShouldBe(_fileConfiguration.Aggregates[1].UpstreamHost);
|
||||
_result[1].UpstreamTemplatePattern.ShouldBe(_aggregate2Utp);
|
||||
_result[1].Aggregator.ShouldBe(_fileConfiguration.Aggregates[1].Aggregator);
|
||||
_result[1].DownstreamReRoute.ShouldContain(x => x == _reRoutes[2].DownstreamReRoute[0]);
|
||||
_result[1].DownstreamReRoute.ShouldContain(x => x == _reRoutes[3].DownstreamReRoute[0]);
|
||||
}
|
||||
|
||||
private void ThenTheUtpCreatorIsCalledCorrectly()
|
||||
{
|
||||
_utpCreator.Verify(x => x.Create(_fileConfiguration.Aggregates[0]), Times.Once);
|
||||
_utpCreator.Verify(x => x.Create(_fileConfiguration.Aggregates[1]), Times.Once);
|
||||
}
|
||||
|
||||
private void GivenTheUtpCreatorReturns()
|
||||
{
|
||||
_aggregate1Utp = new UpstreamPathTemplateBuilder().Build();
|
||||
_aggregate2Utp = new UpstreamPathTemplateBuilder().Build();
|
||||
|
||||
_utpCreator.SetupSequence(x => x.Create(It.IsAny<IReRoute>()))
|
||||
.Returns(_aggregate1Utp)
|
||||
.Returns(_aggregate2Utp);
|
||||
}
|
||||
|
||||
private void ThenTheResultIsEmpty()
|
||||
{
|
||||
_result.Count.ShouldBe(0);
|
||||
}
|
||||
|
||||
private void ThenTheResultIsNotNull()
|
||||
{
|
||||
_result.ShouldNotBeNull();
|
||||
}
|
||||
|
||||
private void TheUtpCreatorIsNotCalled()
|
||||
{
|
||||
_utpCreator.Verify(x => x.Create(It.IsAny<FileAggregateReRoute>()), Times.Never);
|
||||
}
|
||||
|
||||
private void GivenThe(FileConfiguration fileConfiguration)
|
||||
{
|
||||
_fileConfiguration = fileConfiguration;
|
||||
}
|
||||
|
||||
private void GivenThe(List<ReRoute> reRoutes)
|
||||
{
|
||||
_reRoutes = reRoutes;
|
||||
}
|
||||
|
||||
private void WhenICreate()
|
||||
{
|
||||
_result = _creator.Create(_fileConfiguration, _reRoutes);
|
||||
}
|
||||
}
|
||||
}
|
124
test/Ocelot.UnitTests/Configuration/ConfigurationCreatorTests.cs
Normal file
124
test/Ocelot.UnitTests/Configuration/ConfigurationCreatorTests.cs
Normal file
@ -0,0 +1,124 @@
|
||||
namespace Ocelot.UnitTests.Configuration
|
||||
{
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Moq;
|
||||
using Ocelot.Configuration;
|
||||
using Ocelot.Configuration.Creator;
|
||||
using Ocelot.Configuration.File;
|
||||
using Ocelot.DependencyInjection;
|
||||
using Shouldly;
|
||||
using TestStack.BDDfy;
|
||||
using Xunit;
|
||||
|
||||
public class ConfigurationCreatorTests
|
||||
{
|
||||
private ConfigurationCreator _creator;
|
||||
private InternalConfiguration _result;
|
||||
private readonly Mock<IServiceProviderConfigurationCreator> _spcCreator;
|
||||
private readonly Mock<IQoSOptionsCreator> _qosCreator;
|
||||
private readonly Mock<IHttpHandlerOptionsCreator> _hhoCreator;
|
||||
private readonly Mock<ILoadBalancerOptionsCreator> _lboCreator;
|
||||
private FileConfiguration _fileConfig;
|
||||
private List<ReRoute> _reRoutes;
|
||||
private ServiceProviderConfiguration _spc;
|
||||
private LoadBalancerOptions _lbo;
|
||||
private QoSOptions _qoso;
|
||||
private HttpHandlerOptions _hho;
|
||||
private AdministrationPath _adminPath;
|
||||
private readonly ServiceCollection _serviceCollection;
|
||||
|
||||
public ConfigurationCreatorTests()
|
||||
{
|
||||
_lboCreator = new Mock<ILoadBalancerOptionsCreator>();
|
||||
_hhoCreator = new Mock<IHttpHandlerOptionsCreator>();
|
||||
_qosCreator = new Mock<IQoSOptionsCreator>();
|
||||
_spcCreator = new Mock<IServiceProviderConfigurationCreator>();
|
||||
_serviceCollection = new ServiceCollection();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_build_configuration_with_no_admin_path()
|
||||
{
|
||||
this.Given(_ => GivenTheDependenciesAreSetUp())
|
||||
.When(_ => WhenICreate())
|
||||
.Then(_ => ThenTheDepdenciesAreCalledCorrectly())
|
||||
.And(_ => ThenThePropertiesAreSetCorrectly())
|
||||
.And(_ => ThenTheAdminPathIsNull())
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_build_configuration_with_admin_path()
|
||||
{
|
||||
this.Given(_ => GivenTheDependenciesAreSetUp())
|
||||
.And(_ => GivenTheAdminPath())
|
||||
.When(_ => WhenICreate())
|
||||
.Then(_ => ThenTheDepdenciesAreCalledCorrectly())
|
||||
.And(_ => ThenThePropertiesAreSetCorrectly())
|
||||
.And(_ => ThenTheAdminPathIsSet())
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
private void ThenTheAdminPathIsNull()
|
||||
{
|
||||
_result.AdministrationPath.ShouldBeNull();
|
||||
}
|
||||
|
||||
private void ThenThePropertiesAreSetCorrectly()
|
||||
{
|
||||
_result.ShouldNotBeNull();
|
||||
_result.ServiceProviderConfiguration.ShouldBe(_spc);
|
||||
_result.LoadBalancerOptions.ShouldBe(_lbo);
|
||||
_result.QoSOptions.ShouldBe(_qoso);
|
||||
_result.HttpHandlerOptions.ShouldBe(_hho);
|
||||
_result.ReRoutes.ShouldBe(_reRoutes);
|
||||
_result.RequestId.ShouldBe(_fileConfig.GlobalConfiguration.RequestIdKey);
|
||||
_result.DownstreamScheme.ShouldBe(_fileConfig.GlobalConfiguration.DownstreamScheme);
|
||||
}
|
||||
|
||||
private void ThenTheAdminPathIsSet()
|
||||
{
|
||||
_result.AdministrationPath.ShouldBe("wooty");
|
||||
}
|
||||
|
||||
private void ThenTheDepdenciesAreCalledCorrectly()
|
||||
{
|
||||
_spcCreator.Verify(x => x.Create(_fileConfig.GlobalConfiguration), Times.Once);
|
||||
_lboCreator.Verify(x => x.Create(_fileConfig.GlobalConfiguration.LoadBalancerOptions), Times.Once);
|
||||
_qosCreator.Verify(x => x.Create(_fileConfig.GlobalConfiguration.QoSOptions), Times.Once);
|
||||
_hhoCreator.Verify(x => x.Create(_fileConfig.GlobalConfiguration.HttpHandlerOptions), Times.Once);
|
||||
}
|
||||
|
||||
private void GivenTheAdminPath()
|
||||
{
|
||||
_adminPath = new AdministrationPath("wooty");
|
||||
_serviceCollection.AddSingleton<IAdministrationPath>(_adminPath);
|
||||
}
|
||||
|
||||
private void GivenTheDependenciesAreSetUp()
|
||||
{
|
||||
_fileConfig = new FileConfiguration
|
||||
{
|
||||
GlobalConfiguration = new FileGlobalConfiguration()
|
||||
};
|
||||
_reRoutes = new List<ReRoute>();
|
||||
_spc = new ServiceProviderConfiguration("", "", 1, "", "", 1);
|
||||
_lbo = new LoadBalancerOptionsBuilder().Build();
|
||||
_qoso = new QoSOptions(1, 1, 1, "");
|
||||
_hho = new HttpHandlerOptionsBuilder().Build();
|
||||
|
||||
_spcCreator.Setup(x => x.Create(It.IsAny<FileGlobalConfiguration>())).Returns(_spc);
|
||||
_lboCreator.Setup(x => x.Create(It.IsAny<FileLoadBalancerOptions>())).Returns(_lbo);
|
||||
_qosCreator.Setup(x => x.Create(It.IsAny<FileQoSOptions>())).Returns(_qoso);
|
||||
_hhoCreator.Setup(x => x.Create(It.IsAny<FileHttpHandlerOptions>())).Returns(_hho);
|
||||
}
|
||||
|
||||
private void WhenICreate()
|
||||
{
|
||||
var serviceProvider = _serviceCollection.BuildServiceProvider();
|
||||
_creator = new ConfigurationCreator(_spcCreator.Object, _qosCreator.Object, _hhoCreator.Object, serviceProvider, _lboCreator.Object);
|
||||
_result = _creator.Create(_fileConfig, _reRoutes);
|
||||
}
|
||||
}
|
||||
}
|
126
test/Ocelot.UnitTests/Configuration/DynamicsCreatorTests.cs
Normal file
126
test/Ocelot.UnitTests/Configuration/DynamicsCreatorTests.cs
Normal file
@ -0,0 +1,126 @@
|
||||
namespace Ocelot.UnitTests.Configuration
|
||||
{
|
||||
using System.Collections.Generic;
|
||||
using Moq;
|
||||
using Ocelot.Configuration;
|
||||
using Ocelot.Configuration.Builder;
|
||||
using Ocelot.Configuration.Creator;
|
||||
using Ocelot.Configuration.File;
|
||||
using Shouldly;
|
||||
using TestStack.BDDfy;
|
||||
using Xunit;
|
||||
|
||||
public class DynamicsCreatorTests
|
||||
{
|
||||
private readonly DynamicsCreator _creator;
|
||||
private readonly Mock<IRateLimitOptionsCreator> _rloCreator;
|
||||
private List<ReRoute> _result;
|
||||
private FileConfiguration _fileConfig;
|
||||
private RateLimitOptions _rlo1;
|
||||
private RateLimitOptions _rlo2;
|
||||
|
||||
public DynamicsCreatorTests()
|
||||
{
|
||||
_rloCreator = new Mock<IRateLimitOptionsCreator>();
|
||||
_creator = new DynamicsCreator(_rloCreator.Object);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_return_nothing()
|
||||
{
|
||||
var fileConfig = new FileConfiguration();
|
||||
|
||||
this.Given(_ => GivenThe(fileConfig))
|
||||
.When(_ => WhenICreate())
|
||||
.Then(_ => ThenNothingIsReturned())
|
||||
.And(_ => ThenTheRloCreatorIsNotCalled())
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_return_re_routes()
|
||||
{
|
||||
var fileConfig = new FileConfiguration
|
||||
{
|
||||
DynamicReRoutes = new List<FileDynamicReRoute>
|
||||
{
|
||||
new FileDynamicReRoute
|
||||
{
|
||||
ServiceName = "1",
|
||||
RateLimitRule = new FileRateLimitRule
|
||||
{
|
||||
EnableRateLimiting = false
|
||||
}
|
||||
},
|
||||
new FileDynamicReRoute
|
||||
{
|
||||
ServiceName = "2",
|
||||
RateLimitRule = new FileRateLimitRule
|
||||
{
|
||||
EnableRateLimiting = true
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
this.Given(_ => GivenThe(fileConfig))
|
||||
.And(_ => GivenTheRloCreatorReturns())
|
||||
.When(_ => WhenICreate())
|
||||
.Then(_ => ThenTheReRoutesAreReturned())
|
||||
.And(_ => ThenTheRloCreatorIsCalledCorrectly())
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
private void ThenTheRloCreatorIsCalledCorrectly()
|
||||
{
|
||||
_rloCreator.Verify(x => x.Create(_fileConfig.DynamicReRoutes[0].RateLimitRule,
|
||||
_fileConfig.GlobalConfiguration), Times.Once);
|
||||
|
||||
_rloCreator.Verify(x => x.Create(_fileConfig.DynamicReRoutes[1].RateLimitRule,
|
||||
_fileConfig.GlobalConfiguration), Times.Once);
|
||||
}
|
||||
|
||||
private void ThenTheReRoutesAreReturned()
|
||||
{
|
||||
_result.Count.ShouldBe(2);
|
||||
_result[0].DownstreamReRoute[0].EnableEndpointEndpointRateLimiting.ShouldBeFalse();
|
||||
_result[0].DownstreamReRoute[0].RateLimitOptions.ShouldBe(_rlo1);
|
||||
_result[0].DownstreamReRoute[0].ServiceName.ShouldBe(_fileConfig.DynamicReRoutes[0].ServiceName);
|
||||
|
||||
_result[1].DownstreamReRoute[0].EnableEndpointEndpointRateLimiting.ShouldBeTrue();
|
||||
_result[1].DownstreamReRoute[0].RateLimitOptions.ShouldBe(_rlo2);
|
||||
_result[1].DownstreamReRoute[0].ServiceName.ShouldBe(_fileConfig.DynamicReRoutes[1].ServiceName);
|
||||
}
|
||||
|
||||
private void GivenTheRloCreatorReturns()
|
||||
{
|
||||
_rlo1 = new RateLimitOptionsBuilder().Build();
|
||||
_rlo2 = new RateLimitOptionsBuilder().WithEnableRateLimiting(true).Build();
|
||||
|
||||
_rloCreator
|
||||
.SetupSequence(x => x.Create(It.IsAny<FileRateLimitRule>(), It.IsAny<FileGlobalConfiguration>()))
|
||||
.Returns(_rlo1)
|
||||
.Returns(_rlo2);
|
||||
}
|
||||
|
||||
private void ThenTheRloCreatorIsNotCalled()
|
||||
{
|
||||
_rloCreator.Verify(x => x.Create(It.IsAny<FileRateLimitRule>(), It.IsAny<FileGlobalConfiguration>()), Times.Never);
|
||||
}
|
||||
|
||||
private void ThenNothingIsReturned()
|
||||
{
|
||||
_result.Count.ShouldBe(0);
|
||||
}
|
||||
|
||||
private void WhenICreate()
|
||||
{
|
||||
_result = _creator.Create(_fileConfig);
|
||||
}
|
||||
|
||||
private void GivenThe(FileConfiguration fileConfig)
|
||||
{
|
||||
_fileConfig = fileConfig;
|
||||
}
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -70,6 +70,28 @@ namespace Ocelot.UnitTests.Configuration
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_create_with_add_headers_to_request()
|
||||
{
|
||||
const string key = "X-Forwarded-For";
|
||||
const string value = "{RemoteIpAddress}";
|
||||
|
||||
var reRoute = new FileReRoute
|
||||
{
|
||||
UpstreamHeaderTransform = new Dictionary<string, string>
|
||||
{
|
||||
{key, value},
|
||||
}
|
||||
};
|
||||
|
||||
var expected = new AddHeader(key, value);
|
||||
|
||||
this.Given(x => GivenTheReRoute(reRoute))
|
||||
.When(x => WhenICreate())
|
||||
.Then(x => ThenTheFollowingAddHeaderToUpstreamIsReturned(expected))
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_use_base_url_placeholder()
|
||||
{
|
||||
|
@ -42,7 +42,7 @@ namespace Ocelot.UnitTests.Configuration
|
||||
|
||||
private void ThenTheConfigurationIsReturned()
|
||||
{
|
||||
_getResult.Data.ReRoutes[0].DownstreamReRoute[0].DownstreamDownstreamPathTemplate.Value.ShouldBe("initial");
|
||||
_getResult.Data.ReRoutes[0].DownstreamReRoute[0].DownstreamPathTemplate.Value.ShouldBe("initial");
|
||||
}
|
||||
|
||||
private void WhenIGetTheConfiguration()
|
||||
|
@ -0,0 +1,54 @@
|
||||
namespace Ocelot.UnitTests.Configuration
|
||||
{
|
||||
using Ocelot.Configuration;
|
||||
using Ocelot.Configuration.Creator;
|
||||
using Ocelot.Configuration.File;
|
||||
using Shouldly;
|
||||
using TestStack.BDDfy;
|
||||
using Xunit;
|
||||
|
||||
public class LoadBalancerOptionsCreatorTests
|
||||
{
|
||||
private readonly ILoadBalancerOptionsCreator _creator;
|
||||
private FileLoadBalancerOptions _fileLoadBalancerOptions;
|
||||
private LoadBalancerOptions _result;
|
||||
|
||||
public LoadBalancerOptionsCreatorTests()
|
||||
{
|
||||
_creator = new LoadBalancerOptionsCreator();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_create()
|
||||
{
|
||||
var fileLoadBalancerOptions = new FileLoadBalancerOptions
|
||||
{
|
||||
Type = "test",
|
||||
Key = "west",
|
||||
Expiry = 1
|
||||
};
|
||||
|
||||
this.Given(_ => GivenThe(fileLoadBalancerOptions))
|
||||
.When(_ => WhenICreate())
|
||||
.Then(_ => ThenTheOptionsAreCreated(fileLoadBalancerOptions))
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
private void ThenTheOptionsAreCreated(FileLoadBalancerOptions expected)
|
||||
{
|
||||
_result.Type.ShouldBe(expected.Type);
|
||||
_result.Key.ShouldBe(expected.Key);
|
||||
_result.ExpiryInMs.ShouldBe(expected.Expiry);
|
||||
}
|
||||
|
||||
private void WhenICreate()
|
||||
{
|
||||
_result = _creator.Create(_fileLoadBalancerOptions);
|
||||
}
|
||||
|
||||
private void GivenThe(FileLoadBalancerOptions fileLoadBalancerOptions)
|
||||
{
|
||||
_fileLoadBalancerOptions = fileLoadBalancerOptions;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,85 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Ocelot.Configuration.Creator;
|
||||
using Ocelot.Configuration.File;
|
||||
using Ocelot.LoadBalancer.LoadBalancers;
|
||||
using Shouldly;
|
||||
using TestStack.BDDfy;
|
||||
using Xunit;
|
||||
|
||||
namespace Ocelot.UnitTests.Configuration
|
||||
{
|
||||
public class ReRouteKeyCreatorTests
|
||||
{
|
||||
private ReRouteKeyCreator _creator;
|
||||
private FileReRoute _reRoute;
|
||||
private string _result;
|
||||
|
||||
public ReRouteKeyCreatorTests()
|
||||
{
|
||||
_creator = new ReRouteKeyCreator();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_return_sticky_session_key()
|
||||
{
|
||||
var reRoute = new FileReRoute
|
||||
{
|
||||
LoadBalancerOptions = new FileLoadBalancerOptions
|
||||
{
|
||||
Key = "testy",
|
||||
Type = nameof(CookieStickySessions)
|
||||
}
|
||||
};
|
||||
|
||||
this.Given(_ => GivenThe(reRoute))
|
||||
.When(_ => WhenICreate())
|
||||
.Then(_ => ThenTheResultIs($"{nameof(CookieStickySessions)}:{reRoute.LoadBalancerOptions.Key}"))
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_return_re_route_key()
|
||||
{
|
||||
var reRoute = new FileReRoute
|
||||
{
|
||||
UpstreamPathTemplate = "/api/product",
|
||||
UpstreamHttpMethod = new List<string> {"GET", "POST", "PUT"},
|
||||
DownstreamHostAndPorts = new List<FileHostAndPort>
|
||||
{
|
||||
new FileHostAndPort
|
||||
{
|
||||
Host = "localhost",
|
||||
Port = 123
|
||||
},
|
||||
new FileHostAndPort
|
||||
{
|
||||
Host = "localhost",
|
||||
Port = 123
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
this.Given(_ => GivenThe(reRoute))
|
||||
.When(_ => WhenICreate())
|
||||
.Then(_ => ThenTheResultIs($"{reRoute.UpstreamPathTemplate}|{string.Join(",", reRoute.UpstreamHttpMethod)}|{string.Join(",", reRoute.DownstreamHostAndPorts.Select(x => $"{x.Host}:{x.Port}"))}"))
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
private void GivenThe(FileReRoute reRoute)
|
||||
{
|
||||
_reRoute = reRoute;
|
||||
}
|
||||
|
||||
private void WhenICreate()
|
||||
{
|
||||
_result = _creator.Create(_reRoute);
|
||||
}
|
||||
|
||||
private void ThenTheResultIs(string expected)
|
||||
{
|
||||
_result.ShouldBe(expected);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -1,17 +1,17 @@
|
||||
using System.Collections.Generic;
|
||||
using Ocelot.Configuration;
|
||||
using Ocelot.Configuration.Builder;
|
||||
using Ocelot.Configuration.Creator;
|
||||
using Ocelot.Configuration.File;
|
||||
using Shouldly;
|
||||
using TestStack.BDDfy;
|
||||
using Xunit;
|
||||
|
||||
namespace Ocelot.UnitTests.Configuration
|
||||
{
|
||||
using System.Collections.Generic;
|
||||
using Ocelot.Configuration;
|
||||
using Ocelot.Configuration.Builder;
|
||||
using Ocelot.Configuration.Creator;
|
||||
using Ocelot.Configuration.File;
|
||||
using Shouldly;
|
||||
using TestStack.BDDfy;
|
||||
using Xunit;
|
||||
|
||||
public class ReRouteOptionsCreatorTests
|
||||
{
|
||||
private ReRouteOptionsCreator _creator;
|
||||
private readonly ReRouteOptionsCreator _creator;
|
||||
private FileReRoute _reRoute;
|
||||
private ReRouteOptions _result;
|
||||
|
||||
@ -40,7 +40,8 @@ namespace Ocelot.UnitTests.Configuration
|
||||
FileCacheOptions = new FileCacheOptions
|
||||
{
|
||||
TtlSeconds = 1
|
||||
}
|
||||
},
|
||||
ServiceName = "west"
|
||||
};
|
||||
|
||||
var expected = new ReRouteOptionsBuilder()
|
||||
@ -48,6 +49,7 @@ namespace Ocelot.UnitTests.Configuration
|
||||
.WithIsAuthorised(true)
|
||||
.WithIsCached(true)
|
||||
.WithRateLimiting(true)
|
||||
.WithUseServiceDiscovery(true)
|
||||
.Build();
|
||||
|
||||
this.Given(x => x.GivenTheFollowing(reRoute))
|
||||
@ -72,6 +74,7 @@ namespace Ocelot.UnitTests.Configuration
|
||||
_result.IsAuthorised.ShouldBe(expected.IsAuthorised);
|
||||
_result.IsCached.ShouldBe(expected.IsCached);
|
||||
_result.EnableRateLimiting.ShouldBe(expected.EnableRateLimiting);
|
||||
_result.UseServiceDiscovery.ShouldBe(expected.UseServiceDiscovery);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
276
test/Ocelot.UnitTests/Configuration/ReRoutesCreatorTests.cs
Normal file
276
test/Ocelot.UnitTests/Configuration/ReRoutesCreatorTests.cs
Normal file
@ -0,0 +1,276 @@
|
||||
namespace Ocelot.UnitTests.Configuration
|
||||
{
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Moq;
|
||||
using Ocelot.Cache;
|
||||
using Ocelot.Configuration;
|
||||
using Ocelot.Configuration.Builder;
|
||||
using Ocelot.Configuration.Creator;
|
||||
using Ocelot.Configuration.File;
|
||||
using Ocelot.Values;
|
||||
using Shouldly;
|
||||
using TestStack.BDDfy;
|
||||
using Xunit;
|
||||
|
||||
public class ReRoutesCreatorTests
|
||||
{
|
||||
private ReRoutesCreator _creator;
|
||||
private Mock<IClaimsToThingCreator> _cthCreator;
|
||||
private Mock<IAuthenticationOptionsCreator> _aoCreator;
|
||||
private Mock<IUpstreamTemplatePatternCreator> _utpCreator;
|
||||
private Mock<IRequestIdKeyCreator> _ridkCreator;
|
||||
private Mock<IQoSOptionsCreator> _qosoCreator;
|
||||
private Mock<IReRouteOptionsCreator> _rroCreator;
|
||||
private Mock<IRateLimitOptionsCreator> _rloCreator;
|
||||
private Mock<IRegionCreator> _rCreator;
|
||||
private Mock<IHttpHandlerOptionsCreator> _hhoCreator;
|
||||
private Mock<IHeaderFindAndReplaceCreator> _hfarCreator;
|
||||
private Mock<IDownstreamAddressesCreator> _daCreator;
|
||||
private Mock<ILoadBalancerOptionsCreator> _lboCreator;
|
||||
private Mock<IReRouteKeyCreator> _rrkCreator;
|
||||
private Mock<ISecurityOptionsCreator> _soCreator;
|
||||
private FileConfiguration _fileConfig;
|
||||
private ReRouteOptions _rro;
|
||||
private string _requestId;
|
||||
private string _rrk;
|
||||
private UpstreamPathTemplate _upt;
|
||||
private AuthenticationOptions _ao;
|
||||
private List<ClaimToThing> _ctt;
|
||||
private QoSOptions _qoso;
|
||||
private RateLimitOptions _rlo;
|
||||
private string _region;
|
||||
private HttpHandlerOptions _hho;
|
||||
private HeaderTransformations _ht;
|
||||
private List<DownstreamHostAndPort> _dhp;
|
||||
private LoadBalancerOptions _lbo;
|
||||
private List<ReRoute> _result;
|
||||
private SecurityOptions _securityOptions;
|
||||
|
||||
public ReRoutesCreatorTests()
|
||||
{
|
||||
_cthCreator = new Mock<IClaimsToThingCreator>();
|
||||
_aoCreator = new Mock<IAuthenticationOptionsCreator>();
|
||||
_utpCreator = new Mock<IUpstreamTemplatePatternCreator>();
|
||||
_ridkCreator = new Mock<IRequestIdKeyCreator>();
|
||||
_qosoCreator = new Mock<IQoSOptionsCreator>();
|
||||
_rroCreator = new Mock<IReRouteOptionsCreator>();
|
||||
_rloCreator = new Mock<IRateLimitOptionsCreator>();
|
||||
_rCreator = new Mock<IRegionCreator>();
|
||||
_hhoCreator = new Mock<IHttpHandlerOptionsCreator>();
|
||||
_hfarCreator = new Mock<IHeaderFindAndReplaceCreator>();
|
||||
_daCreator = new Mock<IDownstreamAddressesCreator>();
|
||||
_lboCreator = new Mock<ILoadBalancerOptionsCreator>();
|
||||
_rrkCreator = new Mock<IReRouteKeyCreator>();
|
||||
_soCreator = new Mock<ISecurityOptionsCreator>();
|
||||
|
||||
_creator = new ReRoutesCreator(
|
||||
_cthCreator.Object,
|
||||
_aoCreator.Object,
|
||||
_utpCreator.Object,
|
||||
_ridkCreator.Object,
|
||||
_qosoCreator.Object,
|
||||
_rroCreator.Object,
|
||||
_rloCreator.Object,
|
||||
_rCreator.Object,
|
||||
_hhoCreator.Object,
|
||||
_hfarCreator.Object,
|
||||
_daCreator.Object,
|
||||
_lboCreator.Object,
|
||||
_rrkCreator.Object,
|
||||
_soCreator.Object
|
||||
);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_return_nothing()
|
||||
{
|
||||
var fileConfig = new FileConfiguration();
|
||||
|
||||
this.Given(_ => GivenThe(fileConfig))
|
||||
.When(_ => WhenICreate())
|
||||
.Then(_ => ThenNoReRoutesAreReturned())
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_return_re_routes()
|
||||
{
|
||||
var fileConfig = new FileConfiguration
|
||||
{
|
||||
ReRoutes = new List<FileReRoute>
|
||||
{
|
||||
new FileReRoute
|
||||
{
|
||||
ServiceName = "dave",
|
||||
DangerousAcceptAnyServerCertificateValidator = true,
|
||||
AddClaimsToRequest = new Dictionary<string, string>
|
||||
{
|
||||
{ "a","b" }
|
||||
},
|
||||
AddHeadersToRequest = new Dictionary<string, string>
|
||||
{
|
||||
{ "c","d" }
|
||||
},
|
||||
AddQueriesToRequest = new Dictionary<string, string>
|
||||
{
|
||||
{ "e","f" }
|
||||
},
|
||||
UpstreamHttpMethod = new List<string> { "GET", "POST" }
|
||||
},
|
||||
new FileReRoute
|
||||
{
|
||||
ServiceName = "wave",
|
||||
DangerousAcceptAnyServerCertificateValidator = false,
|
||||
AddClaimsToRequest = new Dictionary<string, string>
|
||||
{
|
||||
{ "g","h" }
|
||||
},
|
||||
AddHeadersToRequest = new Dictionary<string, string>
|
||||
{
|
||||
{ "i","j" }
|
||||
},
|
||||
AddQueriesToRequest = new Dictionary<string, string>
|
||||
{
|
||||
{ "k","l" }
|
||||
},
|
||||
UpstreamHttpMethod = new List<string> { "PUT", "DELETE" }
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
this.Given(_ => GivenThe(fileConfig))
|
||||
.And(_ => GivenTheDependenciesAreSetUpCorrectly())
|
||||
.When(_ => WhenICreate())
|
||||
.Then(_ => ThenTheDependenciesAreCalledCorrectly())
|
||||
.And(_ => ThenTheReRoutesAreCreated())
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
private void ThenTheDependenciesAreCalledCorrectly()
|
||||
{
|
||||
ThenTheDepsAreCalledFor(_fileConfig.ReRoutes[0], _fileConfig.GlobalConfiguration);
|
||||
ThenTheDepsAreCalledFor(_fileConfig.ReRoutes[1], _fileConfig.GlobalConfiguration);
|
||||
}
|
||||
|
||||
private void GivenTheDependenciesAreSetUpCorrectly()
|
||||
{
|
||||
_rro = new ReRouteOptions(false, false, false, false, false);
|
||||
_requestId = "testy";
|
||||
_rrk = "besty";
|
||||
_upt = new UpstreamPathTemplateBuilder().Build();
|
||||
_ao = new AuthenticationOptionsBuilder().Build();
|
||||
_ctt = new List<ClaimToThing>();
|
||||
_qoso = new QoSOptionsBuilder().Build();
|
||||
_rlo = new RateLimitOptionsBuilder().Build();
|
||||
_region = "vesty";
|
||||
_hho = new HttpHandlerOptionsBuilder().Build();
|
||||
_ht = new HeaderTransformations(new List<HeaderFindAndReplace>(), new List<HeaderFindAndReplace>(), new List<AddHeader>(), new List<AddHeader>());
|
||||
_dhp = new List<DownstreamHostAndPort>();
|
||||
_lbo = new LoadBalancerOptionsBuilder().Build();
|
||||
|
||||
_rroCreator.Setup(x => x.Create(It.IsAny<FileReRoute>())).Returns(_rro);
|
||||
_ridkCreator.Setup(x => x.Create(It.IsAny<FileReRoute>(), It.IsAny<FileGlobalConfiguration>())).Returns(_requestId);
|
||||
_rrkCreator.Setup(x => x.Create(It.IsAny<FileReRoute>())).Returns(_rrk);
|
||||
_utpCreator.Setup(x => x.Create(It.IsAny<IReRoute>())).Returns(_upt);
|
||||
_aoCreator.Setup(x => x.Create(It.IsAny<FileReRoute>())).Returns(_ao);
|
||||
_cthCreator.Setup(x => x.Create(It.IsAny<Dictionary<string, string>>())).Returns(_ctt);
|
||||
_qosoCreator.Setup(x => x.Create(It.IsAny<FileQoSOptions>(), It.IsAny<string>(), It.IsAny<List<string>>())).Returns(_qoso);
|
||||
_rloCreator.Setup(x => x.Create(It.IsAny<FileRateLimitRule>(), It.IsAny<FileGlobalConfiguration>())).Returns(_rlo);
|
||||
_rCreator.Setup(x => x.Create(It.IsAny<FileReRoute>())).Returns(_region);
|
||||
_hhoCreator.Setup(x => x.Create(It.IsAny<FileHttpHandlerOptions>())).Returns(_hho);
|
||||
_hfarCreator.Setup(x => x.Create(It.IsAny<FileReRoute>())).Returns(_ht);
|
||||
_daCreator.Setup(x => x.Create(It.IsAny<FileReRoute>())).Returns(_dhp);
|
||||
_lboCreator.Setup(x => x.Create(It.IsAny<FileLoadBalancerOptions>())).Returns(_lbo);
|
||||
}
|
||||
|
||||
private void ThenTheReRoutesAreCreated()
|
||||
{
|
||||
_result.Count.ShouldBe(2);
|
||||
|
||||
ThenTheReRouteIsSet(_fileConfig.ReRoutes[0], 0);
|
||||
ThenTheReRouteIsSet(_fileConfig.ReRoutes[1], 1);
|
||||
}
|
||||
|
||||
private void ThenNoReRoutesAreReturned()
|
||||
{
|
||||
_result.ShouldBeEmpty();
|
||||
}
|
||||
|
||||
private void GivenThe(FileConfiguration fileConfig)
|
||||
{
|
||||
_fileConfig = fileConfig;
|
||||
}
|
||||
|
||||
private void WhenICreate()
|
||||
{
|
||||
_result = _creator.Create(_fileConfig);
|
||||
}
|
||||
|
||||
private void ThenTheReRouteIsSet(FileReRoute expected, int reRouteIndex)
|
||||
{
|
||||
_result[reRouteIndex].DownstreamReRoute[0].IsAuthenticated.ShouldBe(_rro.IsAuthenticated);
|
||||
_result[reRouteIndex].DownstreamReRoute[0].IsAuthorised.ShouldBe(_rro.IsAuthorised);
|
||||
_result[reRouteIndex].DownstreamReRoute[0].IsCached.ShouldBe(_rro.IsCached);
|
||||
_result[reRouteIndex].DownstreamReRoute[0].EnableEndpointEndpointRateLimiting.ShouldBe(_rro.EnableRateLimiting);
|
||||
_result[reRouteIndex].DownstreamReRoute[0].RequestIdKey.ShouldBe(_requestId);
|
||||
_result[reRouteIndex].DownstreamReRoute[0].LoadBalancerKey.ShouldBe(_rrk);
|
||||
_result[reRouteIndex].DownstreamReRoute[0].UpstreamPathTemplate.ShouldBe(_upt);
|
||||
_result[reRouteIndex].DownstreamReRoute[0].AuthenticationOptions.ShouldBe(_ao);
|
||||
_result[reRouteIndex].DownstreamReRoute[0].ClaimsToHeaders.ShouldBe(_ctt);
|
||||
_result[reRouteIndex].DownstreamReRoute[0].ClaimsToQueries.ShouldBe(_ctt);
|
||||
_result[reRouteIndex].DownstreamReRoute[0].ClaimsToClaims.ShouldBe(_ctt);
|
||||
_result[reRouteIndex].DownstreamReRoute[0].QosOptions.ShouldBe(_qoso);
|
||||
_result[reRouteIndex].DownstreamReRoute[0].RateLimitOptions.ShouldBe(_rlo);
|
||||
_result[reRouteIndex].DownstreamReRoute[0].CacheOptions.Region.ShouldBe(_region);
|
||||
_result[reRouteIndex].DownstreamReRoute[0].CacheOptions.TtlSeconds.ShouldBe(expected.FileCacheOptions.TtlSeconds);
|
||||
_result[reRouteIndex].DownstreamReRoute[0].HttpHandlerOptions.ShouldBe(_hho);
|
||||
_result[reRouteIndex].DownstreamReRoute[0].UpstreamHeadersFindAndReplace.ShouldBe(_ht.Upstream);
|
||||
_result[reRouteIndex].DownstreamReRoute[0].DownstreamHeadersFindAndReplace.ShouldBe(_ht.Downstream);
|
||||
_result[reRouteIndex].DownstreamReRoute[0].AddHeadersToUpstream.ShouldBe(_ht.AddHeadersToUpstream);
|
||||
_result[reRouteIndex].DownstreamReRoute[0].AddHeadersToDownstream.ShouldBe(_ht.AddHeadersToDownstream);
|
||||
_result[reRouteIndex].DownstreamReRoute[0].DownstreamAddresses.ShouldBe(_dhp);
|
||||
_result[reRouteIndex].DownstreamReRoute[0].LoadBalancerOptions.ShouldBe(_lbo);
|
||||
_result[reRouteIndex].DownstreamReRoute[0].UseServiceDiscovery.ShouldBe(_rro.UseServiceDiscovery);
|
||||
_result[reRouteIndex].DownstreamReRoute[0].DangerousAcceptAnyServerCertificateValidator.ShouldBe(expected.DangerousAcceptAnyServerCertificateValidator);
|
||||
_result[reRouteIndex].DownstreamReRoute[0].DelegatingHandlers.ShouldBe(expected.DelegatingHandlers);
|
||||
_result[reRouteIndex].DownstreamReRoute[0].ServiceName.ShouldBe(expected.ServiceName);
|
||||
_result[reRouteIndex].DownstreamReRoute[0].DownstreamScheme.ShouldBe(expected.DownstreamScheme);
|
||||
_result[reRouteIndex].DownstreamReRoute[0].RouteClaimsRequirement.ShouldBe(expected.RouteClaimsRequirement);
|
||||
_result[reRouteIndex].DownstreamReRoute[0].DownstreamPathTemplate.Value.ShouldBe(expected.DownstreamPathTemplate);
|
||||
_result[reRouteIndex].DownstreamReRoute[0].Key.ShouldBe(expected.Key);
|
||||
_result[reRouteIndex].UpstreamHttpMethod
|
||||
.Select(x => x.Method)
|
||||
.ToList()
|
||||
.ShouldContain(x => x == expected.UpstreamHttpMethod[0]);
|
||||
_result[reRouteIndex].UpstreamHttpMethod
|
||||
.Select(x => x.Method)
|
||||
.ToList()
|
||||
.ShouldContain(x => x == expected.UpstreamHttpMethod[1]);
|
||||
_result[reRouteIndex].UpstreamHost.ShouldBe(expected.UpstreamHost);
|
||||
_result[reRouteIndex].DownstreamReRoute.Count.ShouldBe(1);
|
||||
_result[reRouteIndex].UpstreamTemplatePattern.ShouldBe(_upt);
|
||||
}
|
||||
|
||||
private void ThenTheDepsAreCalledFor(FileReRoute fileReRoute, FileGlobalConfiguration globalConfig)
|
||||
{
|
||||
_rroCreator.Verify(x => x.Create(fileReRoute), Times.Once);
|
||||
_ridkCreator.Verify(x => x.Create(fileReRoute, globalConfig), Times.Once);
|
||||
_rrkCreator.Verify(x => x.Create(fileReRoute), Times.Once);
|
||||
_utpCreator.Verify(x => x.Create(fileReRoute), Times.Exactly(2));
|
||||
_aoCreator.Verify(x => x.Create(fileReRoute), Times.Once);
|
||||
_cthCreator.Verify(x => x.Create(fileReRoute.AddHeadersToRequest), Times.Once);
|
||||
_cthCreator.Verify(x => x.Create(fileReRoute.AddClaimsToRequest), Times.Once);
|
||||
_cthCreator.Verify(x => x.Create(fileReRoute.AddQueriesToRequest), Times.Once);
|
||||
_qosoCreator.Verify(x => x.Create(fileReRoute.QoSOptions, fileReRoute.UpstreamPathTemplate, fileReRoute.UpstreamHttpMethod));
|
||||
_rloCreator.Verify(x => x.Create(fileReRoute.RateLimitOptions, globalConfig), Times.Once);
|
||||
_rCreator.Verify(x => x.Create(fileReRoute), Times.Once);
|
||||
_hhoCreator.Verify(x => x.Create(fileReRoute.HttpHandlerOptions), Times.Once);
|
||||
_hfarCreator.Verify(x => x.Create(fileReRoute), Times.Once);
|
||||
_daCreator.Verify(x => x.Create(fileReRoute), Times.Once);
|
||||
_lboCreator.Verify(x => x.Create(fileReRoute.LoadBalancerOptions), Times.Once);
|
||||
_soCreator.Verify(x => x.Create(fileReRoute.SecurityOptions), Times.Once);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,72 @@
|
||||
using Ocelot.Configuration;
|
||||
using Ocelot.Configuration.Creator;
|
||||
using Ocelot.Configuration.File;
|
||||
using Shouldly;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using TestStack.BDDfy;
|
||||
using Xunit;
|
||||
|
||||
namespace Ocelot.UnitTests.Configuration
|
||||
{
|
||||
public class SecurityOptionsCreatorTests
|
||||
{
|
||||
private FileReRoute _fileReRoute;
|
||||
private FileGlobalConfiguration _fileGlobalConfig;
|
||||
private SecurityOptions _result;
|
||||
private ISecurityOptionsCreator _creator;
|
||||
|
||||
public SecurityOptionsCreatorTests()
|
||||
{
|
||||
_creator = new SecurityOptionsCreator();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_create_security_config()
|
||||
{
|
||||
var ipAllowedList = new List<string>() { "127.0.0.1", "192.168.1.1" };
|
||||
var ipBlockedList = new List<string>() { "127.0.0.1", "192.168.1.1" };
|
||||
var fileReRoute = new FileReRoute
|
||||
{
|
||||
SecurityOptions = new FileSecurityOptions()
|
||||
{
|
||||
IPAllowedList = ipAllowedList,
|
||||
IPBlockedList = ipBlockedList
|
||||
}
|
||||
};
|
||||
|
||||
var expected = new SecurityOptions(ipAllowedList, ipBlockedList);
|
||||
|
||||
this.Given(x => x.GivenThe(fileReRoute))
|
||||
.When(x => x.WhenICreate())
|
||||
.Then(x => x.ThenTheResultIs(expected))
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
private void GivenThe(FileReRoute reRoute)
|
||||
{
|
||||
_fileReRoute = reRoute;
|
||||
}
|
||||
|
||||
|
||||
private void WhenICreate()
|
||||
{
|
||||
_result = _creator.Create(_fileReRoute.SecurityOptions);
|
||||
}
|
||||
|
||||
private void ThenTheResultIs(SecurityOptions expected)
|
||||
{
|
||||
for (int i = 0; i < expected.IPAllowedList.Count; i++)
|
||||
{
|
||||
_result.IPAllowedList[i].ShouldBe(expected.IPAllowedList[i]);
|
||||
}
|
||||
|
||||
for (int i = 0; i < expected.IPBlockedList.Count; i++)
|
||||
{
|
||||
_result.IPBlockedList[i].ShouldBe(expected.IPBlockedList[i]);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -9,6 +9,8 @@
|
||||
using Shouldly;
|
||||
using TestStack.BDDfy;
|
||||
using Xunit;
|
||||
using Moq;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
|
||||
public class ConfigurationBuilderExtensionsTests
|
||||
{
|
||||
@ -19,6 +21,21 @@
|
||||
private FileConfiguration _reRouteA;
|
||||
private FileConfiguration _reRouteB;
|
||||
private FileConfiguration _aggregate;
|
||||
private FileConfiguration _envSpecific;
|
||||
private Mock<IHostingEnvironment> _hostingEnvironment;
|
||||
|
||||
|
||||
public ConfigurationBuilderExtensionsTests()
|
||||
{
|
||||
_hostingEnvironment = new Mock<IHostingEnvironment>();
|
||||
// Clean up config files before each test
|
||||
var subConfigFiles = new DirectoryInfo(".").GetFiles("ocelot.*.json");
|
||||
|
||||
foreach(var config in subConfigFiles)
|
||||
{
|
||||
config.Delete();
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_add_base_url_to_config()
|
||||
@ -32,23 +49,35 @@
|
||||
[Fact]
|
||||
public void should_merge_files()
|
||||
{
|
||||
this.Given(_ => GivenMultipleConfigurationFiles(""))
|
||||
this.Given(_ => GivenMultipleConfigurationFiles("", false))
|
||||
.And(_ => GivenTheEnvironmentIs(null))
|
||||
.When(_ => WhenIAddOcelotConfiguration())
|
||||
.Then(_ => ThenTheConfigsAreMerged())
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_merge_files_except_env()
|
||||
{
|
||||
this.Given(_ => GivenMultipleConfigurationFiles("", true))
|
||||
.And(_ => GivenTheEnvironmentIs("Env"))
|
||||
.When(_ => WhenIAddOcelotConfiguration())
|
||||
.Then(_ => ThenTheConfigsAreMerged())
|
||||
.And(_ => NotContainsEnvSpecificConfig())
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_merge_files_in_specific_folder()
|
||||
{
|
||||
string configFolder = "ConfigFiles";
|
||||
this.Given(_ => GivenMultipleConfigurationFiles(configFolder))
|
||||
this.Given(_ => GivenMultipleConfigurationFiles(configFolder, false))
|
||||
.When(_ => WhenIAddOcelotConfigurationWithSpecificFolder(configFolder))
|
||||
.Then(_ => ThenTheConfigsAreMerged())
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
private void GivenMultipleConfigurationFiles(string folder)
|
||||
private void GivenMultipleConfigurationFiles(string folder, bool addEnvSpecificConfig)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(folder))
|
||||
{
|
||||
@ -85,7 +114,7 @@
|
||||
new FileReRoute
|
||||
{
|
||||
DownstreamScheme = "DownstreamScheme",
|
||||
DownstreamPathTemplate = "DownstreamDownstreamPathTemplate",
|
||||
DownstreamPathTemplate = "DownstreamPathTemplate",
|
||||
Key = "Key",
|
||||
UpstreamHost = "UpstreamHost",
|
||||
UpstreamHttpMethod = new List<string>
|
||||
@ -174,6 +203,32 @@
|
||||
}
|
||||
};
|
||||
|
||||
_envSpecific = new FileConfiguration
|
||||
{
|
||||
ReRoutes = new List<FileReRoute>
|
||||
{
|
||||
new FileReRoute
|
||||
{
|
||||
DownstreamScheme = "DownstreamSchemeSpec",
|
||||
DownstreamPathTemplate = "DownstreamPathTemplateSpec",
|
||||
Key = "KeySpec",
|
||||
UpstreamHost = "UpstreamHostSpec",
|
||||
UpstreamHttpMethod = new List<string>
|
||||
{
|
||||
"UpstreamHttpMethodSpec"
|
||||
},
|
||||
DownstreamHostAndPorts = new List<FileHostAndPort>
|
||||
{
|
||||
new FileHostAndPort
|
||||
{
|
||||
Host = "HostSpec",
|
||||
Port = 80
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
string globalFilename = Path.Combine(folder, "ocelot.global.json");
|
||||
string reroutesAFilename = Path.Combine(folder, "ocelot.reRoutesA.json");
|
||||
string reroutesBFilename = Path.Combine(folder, "ocelot.reRoutesB.json");
|
||||
@ -183,19 +238,32 @@
|
||||
File.WriteAllText(reroutesAFilename, JsonConvert.SerializeObject(_reRouteA));
|
||||
File.WriteAllText(reroutesBFilename, JsonConvert.SerializeObject(_reRouteB));
|
||||
File.WriteAllText(aggregatesFilename, JsonConvert.SerializeObject(_aggregate));
|
||||
|
||||
if (addEnvSpecificConfig)
|
||||
{
|
||||
string envSpecificFilename = Path.Combine(folder, "ocelot.Env.json");
|
||||
File.WriteAllText(envSpecificFilename, JsonConvert.SerializeObject(_envSpecific));
|
||||
}
|
||||
}
|
||||
|
||||
private void GivenTheEnvironmentIs(string env)
|
||||
{
|
||||
_hostingEnvironment.SetupGet(x => x.EnvironmentName).Returns(env);
|
||||
}
|
||||
|
||||
private void WhenIAddOcelotConfiguration()
|
||||
{
|
||||
IConfigurationBuilder builder = new ConfigurationBuilder();
|
||||
builder.AddOcelot();
|
||||
|
||||
builder.AddOcelot(_hostingEnvironment.Object);
|
||||
|
||||
_configRoot = builder.Build();
|
||||
}
|
||||
|
||||
private void WhenIAddOcelotConfigurationWithSpecificFolder(string folder)
|
||||
{
|
||||
IConfigurationBuilder builder = new ConfigurationBuilder();
|
||||
builder.AddOcelot(folder);
|
||||
builder.AddOcelot(folder, _hostingEnvironment.Object);
|
||||
_configRoot = builder.Build();
|
||||
}
|
||||
|
||||
@ -235,6 +303,14 @@
|
||||
fc.Aggregates.Count.ShouldBe(_aggregate.Aggregates.Count);
|
||||
}
|
||||
|
||||
private void NotContainsEnvSpecificConfig()
|
||||
{
|
||||
var fc = (FileConfiguration)_configRoot.Get(typeof(FileConfiguration));
|
||||
fc.ReRoutes.ShouldNotContain(x => x.DownstreamScheme == _envSpecific.ReRoutes[0].DownstreamScheme);
|
||||
fc.ReRoutes.ShouldNotContain(x => x.DownstreamPathTemplate == _envSpecific.ReRoutes[0].DownstreamPathTemplate);
|
||||
fc.ReRoutes.ShouldNotContain(x => x.Key == _envSpecific.ReRoutes[0].Key);
|
||||
}
|
||||
|
||||
private void GivenTheBaseUrl(string baseUrl)
|
||||
{
|
||||
#pragma warning disable CS0618
|
||||
|
@ -37,7 +37,7 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder
|
||||
_handlerOptions = new HttpHandlerOptionsBuilder().Build();
|
||||
_loadBalancerOptions = new LoadBalancerOptionsBuilder().WithType(nameof(NoLoadBalancer)).Build();
|
||||
_qosOptionsCreator
|
||||
.Setup(x => x.Create(It.IsAny<QoSOptions>(), It.IsAny<string>(), It.IsAny<string[]>()))
|
||||
.Setup(x => x.Create(It.IsAny<QoSOptions>(), It.IsAny<string>(), It.IsAny<List<string>>()))
|
||||
.Returns(_qoSOptions);
|
||||
_creator = new DownstreamRouteCreator(_qosOptionsCreator.Object);
|
||||
}
|
||||
@ -198,7 +198,7 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder
|
||||
private void GivenTheQosCreatorReturns(QoSOptions options)
|
||||
{
|
||||
_qosOptionsCreator
|
||||
.Setup(x => x.Create(It.IsAny<QoSOptions>(), It.IsAny<string>(), It.IsAny<string[]>()))
|
||||
.Setup(x => x.Create(It.IsAny<QoSOptions>(), It.IsAny<string>(), It.IsAny<List<string>>()))
|
||||
.Returns(options);
|
||||
}
|
||||
|
||||
@ -211,7 +211,7 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder
|
||||
|
||||
private void ThenTheDownstreamRouteIsCreated()
|
||||
{
|
||||
_result.Data.ReRoute.DownstreamReRoute[0].DownstreamDownstreamPathTemplate.Value.ShouldBe("/test");
|
||||
_result.Data.ReRoute.DownstreamReRoute[0].DownstreamPathTemplate.Value.ShouldBe("/test");
|
||||
_result.Data.ReRoute.UpstreamHttpMethod[0].ShouldBe(HttpMethod.Get);
|
||||
_result.Data.ReRoute.DownstreamReRoute[0].ServiceName.ShouldBe("auth");
|
||||
_result.Data.ReRoute.DownstreamReRoute[0].LoadBalancerKey.ShouldBe("/auth/test|GET");
|
||||
@ -229,21 +229,21 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder
|
||||
|
||||
private void ThenTheDownstreamPathIsForwardSlash()
|
||||
{
|
||||
_result.Data.ReRoute.DownstreamReRoute[0].DownstreamDownstreamPathTemplate.Value.ShouldBe("/");
|
||||
_result.Data.ReRoute.DownstreamReRoute[0].DownstreamPathTemplate.Value.ShouldBe("/");
|
||||
_result.Data.ReRoute.DownstreamReRoute[0].ServiceName.ShouldBe("auth");
|
||||
_result.Data.ReRoute.DownstreamReRoute[0].LoadBalancerKey.ShouldBe("/auth/|GET");
|
||||
}
|
||||
|
||||
private void ThenThePathDoesNotHaveTrailingSlash()
|
||||
{
|
||||
_result.Data.ReRoute.DownstreamReRoute[0].DownstreamDownstreamPathTemplate.Value.ShouldBe("/test");
|
||||
_result.Data.ReRoute.DownstreamReRoute[0].DownstreamPathTemplate.Value.ShouldBe("/test");
|
||||
_result.Data.ReRoute.DownstreamReRoute[0].ServiceName.ShouldBe("auth");
|
||||
_result.Data.ReRoute.DownstreamReRoute[0].LoadBalancerKey.ShouldBe("/auth/test|GET");
|
||||
}
|
||||
|
||||
private void ThenTheQueryStringIsRemoved()
|
||||
{
|
||||
_result.Data.ReRoute.DownstreamReRoute[0].DownstreamDownstreamPathTemplate.Value.ShouldBe("/test");
|
||||
_result.Data.ReRoute.DownstreamReRoute[0].DownstreamPathTemplate.Value.ShouldBe("/test");
|
||||
_result.Data.ReRoute.DownstreamReRoute[0].ServiceName.ShouldBe("auth");
|
||||
_result.Data.ReRoute.DownstreamReRoute[0].LoadBalancerKey.ShouldBe("/auth/test|GET");
|
||||
}
|
||||
@ -260,7 +260,7 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder
|
||||
_result.Data.ReRoute.DownstreamReRoute[0].QosOptions.ShouldBe(expected);
|
||||
_result.Data.ReRoute.DownstreamReRoute[0].QosOptions.UseQos.ShouldBeTrue();
|
||||
_qosOptionsCreator
|
||||
.Verify(x => x.Create(expected, _upstreamUrlPath, It.IsAny<string[]>()), Times.Once);
|
||||
.Verify(x => x.Create(expected, _upstreamUrlPath, It.IsAny<List<string>>()), Times.Once);
|
||||
}
|
||||
|
||||
private void GivenTheConfiguration(IInternalConfiguration config)
|
||||
|
@ -23,7 +23,6 @@
|
||||
{
|
||||
private readonly Mock<IDownstreamRouteProvider> _finder;
|
||||
private readonly Mock<IDownstreamRouteProviderFactory> _factory;
|
||||
private readonly Mock<IInternalConfigurationRepository> _repo;
|
||||
private Response<DownstreamRoute> _downstreamRoute;
|
||||
private IInternalConfiguration _config;
|
||||
private Mock<IOcelotLoggerFactory> _loggerFactory;
|
||||
@ -35,7 +34,6 @@
|
||||
|
||||
public DownstreamRouteFinderMiddlewareTests()
|
||||
{
|
||||
_repo = new Mock<IInternalConfigurationRepository>();
|
||||
_finder = new Mock<IDownstreamRouteProvider>();
|
||||
_factory = new Mock<IDownstreamRouteProviderFactory>();
|
||||
_factory.Setup(x => x.Get(It.IsAny<IInternalConfiguration>())).Returns(_finder.Object);
|
||||
@ -45,7 +43,7 @@
|
||||
_loggerFactory.Setup(x => x.CreateLogger<DownstreamRouteFinderMiddleware>()).Returns(_logger.Object);
|
||||
_next = context => Task.CompletedTask;
|
||||
_multiplexer = new Mock<IMultiplexer>();
|
||||
_middleware = new DownstreamRouteFinderMiddleware(_next, _loggerFactory.Object, _factory.Object, _repo.Object, _multiplexer.Object);
|
||||
_middleware = new DownstreamRouteFinderMiddleware(_next, _loggerFactory.Object, _factory.Object, _multiplexer.Object);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
@ -459,7 +459,6 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder
|
||||
.WithDownstreamPathTemplate("someDownstreamPath")
|
||||
.WithUpstreamHttpMethod(new List<string> { "Get" })
|
||||
.WithUpstreamPathTemplate(new UpstreamPathTemplate("someUpstreamPath", 1, false, "someUpstreamPath"))
|
||||
.WithUpstreamHost("MATCH")
|
||||
.Build())
|
||||
.WithUpstreamHttpMethod(new List<string> { "Get" })
|
||||
.WithUpstreamPathTemplate(new UpstreamPathTemplate("someUpstreamPath", 1, false, "someUpstreamPath"))
|
||||
@ -545,7 +544,6 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder
|
||||
.WithDownstreamPathTemplate("someDownstreamPath")
|
||||
.WithUpstreamHttpMethod(new List<string> { "Get" })
|
||||
.WithUpstreamPathTemplate(new UpstreamPathTemplate("someUpstreamPath", 1, false, "someUpstreamPath"))
|
||||
.WithUpstreamHost("MATCH")
|
||||
.Build())
|
||||
.WithUpstreamHttpMethod(new List<string> { "Get" })
|
||||
.WithUpstreamPathTemplate(new UpstreamPathTemplate("someUpstreamPath", 1, false, "someUpstreamPath"))
|
||||
@ -556,7 +554,6 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder
|
||||
.WithDownstreamPathTemplate("someDownstreamPath")
|
||||
.WithUpstreamHttpMethod(new List<string> { }) // empty list of methods
|
||||
.WithUpstreamPathTemplate(new UpstreamPathTemplate("someUpstreamPath", 1, false, "someUpstreamPath"))
|
||||
.WithUpstreamHost("MATCH")
|
||||
.Build())
|
||||
.WithUpstreamHttpMethod(new List<string> { }) // empty list of methods
|
||||
.WithUpstreamPathTemplate(new UpstreamPathTemplate("someUpstreamPath", 1, false, "someUpstreamPath"))
|
||||
@ -587,7 +584,6 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder
|
||||
.WithDownstreamPathTemplate("someDownstreamPath")
|
||||
.WithUpstreamHttpMethod(new List<string>())
|
||||
.WithUpstreamPathTemplate(new UpstreamPathTemplate("someUpstreamPath", 1, false, "someUpstreamPath"))
|
||||
.WithUpstreamHost("MATCH")
|
||||
.Build())
|
||||
.WithUpstreamHttpMethod(new List<string>())
|
||||
.WithUpstreamPathTemplate(new UpstreamPathTemplate("someUpstreamPath", 1, false, "someUpstreamPath"))
|
||||
@ -618,7 +614,6 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder
|
||||
.WithDownstreamPathTemplate("someDownstreamPath")
|
||||
.WithUpstreamHttpMethod(new List<string>())
|
||||
.WithUpstreamPathTemplate(new UpstreamPathTemplate("someUpstreamPath", 1, false, "someUpstreamPath"))
|
||||
.WithUpstreamHost("MATCH")
|
||||
.Build())
|
||||
.WithUpstreamHttpMethod(new List<string>())
|
||||
.WithUpstreamPathTemplate(new UpstreamPathTemplate("someUpstreamPath", 1, false, "someUpstreamPath"))
|
||||
@ -659,7 +654,6 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder
|
||||
.WithDownstreamPathTemplate("someDownstreamPath")
|
||||
.WithUpstreamHttpMethod(new List<string> { "Get" })
|
||||
.WithUpstreamPathTemplate(new UpstreamPathTemplate("someUpstreamPath", 1, false, "someUpstreamPath"))
|
||||
.WithUpstreamHost("MATCH")
|
||||
.Build())
|
||||
.WithUpstreamHttpMethod(new List<string> { "Get" })
|
||||
.WithUpstreamPathTemplate(new UpstreamPathTemplate("someUpstreamPath", 1, false, "someUpstreamPath"))
|
||||
@ -759,7 +753,7 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder
|
||||
|
||||
private void ThenTheFollowingIsReturned(DownstreamRoute expected)
|
||||
{
|
||||
_result.Data.ReRoute.DownstreamReRoute[0].DownstreamDownstreamPathTemplate.Value.ShouldBe(expected.ReRoute.DownstreamReRoute[0].DownstreamDownstreamPathTemplate.Value);
|
||||
_result.Data.ReRoute.DownstreamReRoute[0].DownstreamPathTemplate.Value.ShouldBe(expected.ReRoute.DownstreamReRoute[0].DownstreamPathTemplate.Value);
|
||||
_result.Data.ReRoute.UpstreamTemplatePattern.Priority.ShouldBe(expected.ReRoute.UpstreamTemplatePattern.Priority);
|
||||
|
||||
for (int i = 0; i < _result.Data.TemplatePlaceholderNameAndValues.Count; i++)
|
||||
|
@ -199,7 +199,7 @@ namespace Ocelot.UnitTests.DownstreamUrlCreator.UrlTemplateReplacer
|
||||
|
||||
private void WhenIReplaceTheTemplateVariables()
|
||||
{
|
||||
_result = _downstreamPathReplacer.Replace(_downstreamRoute.ReRoute.DownstreamReRoute[0].DownstreamDownstreamPathTemplate, _downstreamRoute.TemplatePlaceholderNameAndValues);
|
||||
_result = _downstreamPathReplacer.Replace(_downstreamRoute.ReRoute.DownstreamReRoute[0].DownstreamPathTemplate, _downstreamRoute.TemplatePlaceholderNameAndValues);
|
||||
}
|
||||
|
||||
private void ThenTheDownstreamUrlPathIsReturned(string expected)
|
||||
|
@ -15,6 +15,9 @@ using Ocelot.Request.Middleware;
|
||||
|
||||
namespace Ocelot.UnitTests.Headers
|
||||
{
|
||||
using Ocelot.Infrastructure;
|
||||
using Ocelot.Logging;
|
||||
|
||||
public class AddHeadersToRequestClaimToThingTests
|
||||
{
|
||||
private readonly AddHeadersToRequest _addHeadersToRequest;
|
||||
@ -24,11 +27,15 @@ namespace Ocelot.UnitTests.Headers
|
||||
private List<ClaimToThing> _configuration;
|
||||
private Response _result;
|
||||
private Response<string> _claimValue;
|
||||
private Mock<IPlaceholders> _placeholders;
|
||||
private Mock<IOcelotLoggerFactory> _factory;
|
||||
|
||||
public AddHeadersToRequestClaimToThingTests()
|
||||
{
|
||||
_parser = new Mock<IClaimsParser>();
|
||||
_addHeadersToRequest = new AddHeadersToRequest(_parser.Object);
|
||||
_placeholders = new Mock<IPlaceholders>();
|
||||
_factory = new Mock<IOcelotLoggerFactory>();
|
||||
_addHeadersToRequest = new AddHeadersToRequest(_parser.Object, _placeholders.Object, _factory.Object);
|
||||
_downstreamRequest = new DownstreamRequest(new HttpRequestMessage(HttpMethod.Get, "http://test.com"));
|
||||
}
|
||||
|
||||
|
@ -1,23 +1,56 @@
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Moq;
|
||||
using Ocelot.Configuration.Creator;
|
||||
using Ocelot.Headers;
|
||||
using Ocelot.Infrastructure.Claims.Parser;
|
||||
using Shouldly;
|
||||
using TestStack.BDDfy;
|
||||
using Xunit;
|
||||
|
||||
namespace Ocelot.UnitTests.Headers
|
||||
namespace Ocelot.UnitTests.Headers
|
||||
{
|
||||
using Ocelot.Infrastructure;
|
||||
using Ocelot.Logging;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Moq;
|
||||
using Ocelot.Configuration.Creator;
|
||||
using Ocelot.Headers;
|
||||
using Ocelot.Infrastructure.Claims.Parser;
|
||||
using Responder;
|
||||
using Responses;
|
||||
using Shouldly;
|
||||
using TestStack.BDDfy;
|
||||
using Xunit;
|
||||
|
||||
public class AddHeadersToRequestPlainTests
|
||||
{
|
||||
private readonly AddHeadersToRequest _addHeadersToRequest;
|
||||
private HttpContext _context;
|
||||
private AddHeader _addedHeader;
|
||||
private readonly Mock<IPlaceholders> _placeholders;
|
||||
private Mock<IOcelotLoggerFactory> _factory;
|
||||
private readonly Mock<IOcelotLogger> _logger;
|
||||
|
||||
public AddHeadersToRequestPlainTests()
|
||||
{
|
||||
_addHeadersToRequest = new AddHeadersToRequest(Mock.Of<IClaimsParser>());
|
||||
_placeholders = new Mock<IPlaceholders>();
|
||||
_factory = new Mock<IOcelotLoggerFactory>();
|
||||
_logger = new Mock<IOcelotLogger>();
|
||||
_factory.Setup(x => x.CreateLogger<AddHeadersToRequest>()).Returns(_logger.Object);
|
||||
_addHeadersToRequest = new AddHeadersToRequest(Mock.Of<IClaimsParser>(), _placeholders.Object, _factory.Object);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_log_error_if_cannot_find_placeholder()
|
||||
{
|
||||
_placeholders.Setup(x => x.Get(It.IsAny<string>())).Returns(new ErrorResponse<string>(new AnyError()));
|
||||
|
||||
this.Given(_ => GivenHttpRequestWithoutHeaders())
|
||||
.When(_ => WhenAddingHeader("X-Forwarded-For", "{RemoteIdAddress}"))
|
||||
.Then(_ => ThenAnErrorIsLogged("X-Forwarded-For", "{RemoteIdAddress}"))
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_add_placeholder_to_downstream_request()
|
||||
{
|
||||
_placeholders.Setup(x => x.Get(It.IsAny<string>())).Returns(new OkResponse<string>("replaced"));
|
||||
|
||||
this.Given(_ => GivenHttpRequestWithoutHeaders())
|
||||
.When(_ => WhenAddingHeader("X-Forwarded-For", "{RemoteIdAddress}"))
|
||||
.Then(_ => ThenTheHeaderGetsTakenOverToTheRequestHeaders("replaced"))
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@ -38,9 +71,23 @@ namespace Ocelot.UnitTests.Headers
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
private void ThenAnErrorIsLogged(string key, string value)
|
||||
{
|
||||
_logger.Verify(x => x.LogWarning($"Unable to add header to response {key}: {value}"), Times.Once);
|
||||
}
|
||||
|
||||
|
||||
private void GivenHttpRequestWithoutHeaders()
|
||||
{
|
||||
_context = new DefaultHttpContext();
|
||||
_context = new DefaultHttpContext
|
||||
{
|
||||
Request =
|
||||
{
|
||||
Headers =
|
||||
{
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private void GivenHttpRequestWithHeader(string headerKey, string headerValue)
|
||||
@ -71,5 +118,12 @@ namespace Ocelot.UnitTests.Headers
|
||||
value.ShouldNotBeNull($"Value of header {_addedHeader.Key} was expected to not be null.");
|
||||
value.ToString().ShouldBe(_addedHeader.Value);
|
||||
}
|
||||
|
||||
private void ThenTheHeaderGetsTakenOverToTheRequestHeaders(string expected)
|
||||
{
|
||||
var requestHeaders = _context.Request.Headers;
|
||||
var value = requestHeaders[_addedHeader.Key];
|
||||
value.ToString().ShouldBe(expected);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -19,25 +19,25 @@ namespace Ocelot.UnitTests.Headers
|
||||
using TestStack.BDDfy;
|
||||
using Xunit;
|
||||
|
||||
public class HttpRequestHeadersBuilderMiddlewareTests
|
||||
public class ClaimsToHeadersMiddlewareTests
|
||||
{
|
||||
private readonly Mock<IAddHeadersToRequest> _addHeaders;
|
||||
private Response<DownstreamRoute> _downstreamRoute;
|
||||
private Mock<IOcelotLoggerFactory> _loggerFactory;
|
||||
private Mock<IOcelotLogger> _logger;
|
||||
private HttpRequestHeadersBuilderMiddleware _middleware;
|
||||
private ClaimsToHeadersMiddleware _middleware;
|
||||
private DownstreamContext _downstreamContext;
|
||||
private OcelotRequestDelegate _next;
|
||||
|
||||
public HttpRequestHeadersBuilderMiddlewareTests()
|
||||
public ClaimsToHeadersMiddlewareTests()
|
||||
{
|
||||
_addHeaders = new Mock<IAddHeadersToRequest>();
|
||||
_downstreamContext = new DownstreamContext(new DefaultHttpContext());
|
||||
_loggerFactory = new Mock<IOcelotLoggerFactory>();
|
||||
_logger = new Mock<IOcelotLogger>();
|
||||
_loggerFactory.Setup(x => x.CreateLogger<HttpRequestHeadersBuilderMiddleware>()).Returns(_logger.Object);
|
||||
_loggerFactory.Setup(x => x.CreateLogger<ClaimsToHeadersMiddleware>()).Returns(_logger.Object);
|
||||
_next = context => Task.CompletedTask;
|
||||
_middleware = new HttpRequestHeadersBuilderMiddleware(_next, _loggerFactory.Object, _addHeaders.Object);
|
||||
_middleware = new ClaimsToHeadersMiddleware(_next, _loggerFactory.Object, _addHeaders.Object);
|
||||
_downstreamContext.DownstreamRequest = new DownstreamRequest(new HttpRequestMessage(HttpMethod.Get, "http://test.com"));
|
||||
}
|
||||
|
@ -109,7 +109,7 @@ namespace Ocelot.UnitTests.Headers
|
||||
|
||||
private void ThenTheIHttpResponseHeaderReplacerIsCalledCorrectly()
|
||||
{
|
||||
_postReplacer.Verify(x => x.Replace(It.IsAny<DownstreamResponse>(), It.IsAny<List<HeaderFindAndReplace>>(), It.IsAny<DownstreamRequest>()), Times.Once);
|
||||
_postReplacer.Verify(x => x.Replace(It.IsAny<DownstreamContext>(), It.IsAny<List<HeaderFindAndReplace>>()), Times.Once);
|
||||
}
|
||||
|
||||
private void GivenTheFollowingRequest()
|
||||
|
@ -1,22 +1,22 @@
|
||||
using Xunit;
|
||||
using Shouldly;
|
||||
using TestStack.BDDfy;
|
||||
using System.Net.Http;
|
||||
using Ocelot.Headers;
|
||||
using Ocelot.Configuration;
|
||||
using System.Collections.Generic;
|
||||
using Ocelot.Responses;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using Moq;
|
||||
using Ocelot.Infrastructure;
|
||||
using Ocelot.Middleware;
|
||||
using Ocelot.Infrastructure.RequestData;
|
||||
using Ocelot.Middleware.Multiplexer;
|
||||
using Ocelot.Request.Middleware;
|
||||
|
||||
namespace Ocelot.UnitTests.Headers
|
||||
{
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Ocelot.Infrastructure;
|
||||
using Ocelot.Middleware;
|
||||
using Ocelot.Infrastructure.RequestData;
|
||||
using Ocelot.Request.Middleware;
|
||||
using Xunit;
|
||||
using Shouldly;
|
||||
using TestStack.BDDfy;
|
||||
using System.Net.Http;
|
||||
using Ocelot.Headers;
|
||||
using Ocelot.Configuration;
|
||||
using System.Collections.Generic;
|
||||
using Ocelot.Responses;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using Moq;
|
||||
|
||||
public class HttpResponseHeaderReplacerTests
|
||||
{
|
||||
private DownstreamResponse _response;
|
||||
@ -27,12 +27,14 @@ namespace Ocelot.UnitTests.Headers
|
||||
private DownstreamRequest _request;
|
||||
private Mock<IBaseUrlFinder> _finder;
|
||||
private Mock<IRequestScopedDataRepository> _repo;
|
||||
private Mock<IHttpContextAccessor> _accessor;
|
||||
|
||||
public HttpResponseHeaderReplacerTests()
|
||||
{
|
||||
_accessor = new Mock<IHttpContextAccessor>();
|
||||
_repo = new Mock<IRequestScopedDataRepository>();
|
||||
_finder = new Mock<IBaseUrlFinder>();
|
||||
_placeholders = new Placeholders(_finder.Object, _repo.Object);
|
||||
_placeholders = new Placeholders(_finder.Object, _repo.Object, _accessor.Object);
|
||||
_replacer = new HttpResponseHeaderReplacer(_placeholders);
|
||||
}
|
||||
|
||||
@ -43,7 +45,7 @@ namespace Ocelot.UnitTests.Headers
|
||||
new List<KeyValuePair<string, IEnumerable<string>>>()
|
||||
{
|
||||
new KeyValuePair<string, IEnumerable<string>>("test", new List<string> {"test"})
|
||||
});
|
||||
}, "");
|
||||
|
||||
var fAndRs = new List<HeaderFindAndReplace> {new HeaderFindAndReplace("test", "test", "chiken", 0)};
|
||||
|
||||
@ -61,7 +63,7 @@ namespace Ocelot.UnitTests.Headers
|
||||
new List<KeyValuePair<string, IEnumerable<string>>>()
|
||||
{
|
||||
new KeyValuePair<string, IEnumerable<string>>("test", new List<string> {"test"})
|
||||
});
|
||||
}, "");
|
||||
|
||||
var fAndRs = new List<HeaderFindAndReplace>();
|
||||
|
||||
@ -84,7 +86,7 @@ namespace Ocelot.UnitTests.Headers
|
||||
new List<KeyValuePair<string, IEnumerable<string>>>()
|
||||
{
|
||||
new KeyValuePair<string, IEnumerable<string>>("Location", new List<string> {downstreamUrl})
|
||||
});
|
||||
}, "");
|
||||
|
||||
var fAndRs = new List<HeaderFindAndReplace>
|
||||
{
|
||||
@ -111,7 +113,7 @@ namespace Ocelot.UnitTests.Headers
|
||||
new List<KeyValuePair<string, IEnumerable<string>>>()
|
||||
{
|
||||
new KeyValuePair<string, IEnumerable<string>>("Location", new List<string> {downstreamUrl})
|
||||
});
|
||||
}, "");
|
||||
|
||||
var fAndRs = new List<HeaderFindAndReplace>
|
||||
{
|
||||
@ -138,7 +140,7 @@ namespace Ocelot.UnitTests.Headers
|
||||
new List<KeyValuePair<string, IEnumerable<string>>>()
|
||||
{
|
||||
new KeyValuePair<string, IEnumerable<string>>("Location", new List<string> {downstreamUrl})
|
||||
});
|
||||
}, "");
|
||||
|
||||
var fAndRs = new List<HeaderFindAndReplace>
|
||||
{
|
||||
@ -165,7 +167,7 @@ namespace Ocelot.UnitTests.Headers
|
||||
new List<KeyValuePair<string, IEnumerable<string>>>()
|
||||
{
|
||||
new KeyValuePair<string, IEnumerable<string>>("Location", new List<string> {downstreamUrl})
|
||||
});
|
||||
}, "");
|
||||
|
||||
var fAndRs = new List<HeaderFindAndReplace>
|
||||
{
|
||||
@ -192,7 +194,7 @@ namespace Ocelot.UnitTests.Headers
|
||||
new List<KeyValuePair<string, IEnumerable<string>>>()
|
||||
{
|
||||
new KeyValuePair<string, IEnumerable<string>>("Location", new List<string> {downstreamUrl})
|
||||
});
|
||||
}, "");
|
||||
|
||||
var fAndRs = new List<HeaderFindAndReplace>
|
||||
{
|
||||
@ -219,7 +221,7 @@ namespace Ocelot.UnitTests.Headers
|
||||
new List<KeyValuePair<string, IEnumerable<string>>>()
|
||||
{
|
||||
new KeyValuePair<string, IEnumerable<string>>("Location", new List<string> {downstreamUrl})
|
||||
});
|
||||
}, "");
|
||||
|
||||
var fAndRs = new List<HeaderFindAndReplace>
|
||||
{
|
||||
@ -261,7 +263,8 @@ namespace Ocelot.UnitTests.Headers
|
||||
|
||||
private void WhenICallTheReplacer()
|
||||
{
|
||||
_result = _replacer.Replace(_response, _headerFindAndReplaces, _request);
|
||||
var context = new DownstreamContext(new DefaultHttpContext()) {DownstreamResponse = _response, DownstreamRequest = _request};
|
||||
_result = _replacer.Replace(context, _headerFindAndReplaces);
|
||||
}
|
||||
|
||||
private void ThenTheHeaderShouldBe(string key, string value)
|
||||
|
@ -1,27 +1,31 @@
|
||||
using System;
|
||||
using System.Net.Http;
|
||||
using Moq;
|
||||
using Ocelot.Infrastructure;
|
||||
using Ocelot.Infrastructure.RequestData;
|
||||
using Ocelot.Middleware;
|
||||
using Ocelot.Request.Middleware;
|
||||
using Ocelot.Responses;
|
||||
using Shouldly;
|
||||
using Xunit;
|
||||
|
||||
namespace Ocelot.UnitTests.Infrastructure
|
||||
{
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using System;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using Moq;
|
||||
using Ocelot.Infrastructure;
|
||||
using Ocelot.Infrastructure.RequestData;
|
||||
using Ocelot.Middleware;
|
||||
using Ocelot.Request.Middleware;
|
||||
using Ocelot.Responses;
|
||||
using Shouldly;
|
||||
using Xunit;
|
||||
|
||||
public class PlaceholdersTests
|
||||
{
|
||||
private IPlaceholders _placeholders;
|
||||
private Mock<IBaseUrlFinder> _finder;
|
||||
private Mock<IRequestScopedDataRepository> _repo;
|
||||
private readonly IPlaceholders _placeholders;
|
||||
private readonly Mock<IBaseUrlFinder> _finder;
|
||||
private readonly Mock<IRequestScopedDataRepository> _repo;
|
||||
private readonly Mock<IHttpContextAccessor> _accessor;
|
||||
|
||||
public PlaceholdersTests()
|
||||
{
|
||||
_accessor = new Mock<IHttpContextAccessor>();
|
||||
_repo = new Mock<IRequestScopedDataRepository>();
|
||||
_finder = new Mock<IBaseUrlFinder>();
|
||||
_placeholders = new Placeholders(_finder.Object, _repo.Object);
|
||||
_placeholders = new Placeholders(_finder.Object, _repo.Object, _accessor.Object);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@ -33,6 +37,15 @@ namespace Ocelot.UnitTests.Infrastructure
|
||||
result.Data.ShouldBe(baseUrl);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_return_remote_ip_address()
|
||||
{
|
||||
var httpContext = new DefaultHttpContext(){Connection = { RemoteIpAddress = IPAddress.Any}};
|
||||
_accessor.Setup(x => x.HttpContext).Returns(httpContext);
|
||||
var result = _placeholders.Get("{RemoteIpAddress}");
|
||||
result.Data.ShouldBe(httpContext.Connection.RemoteIpAddress.ToString());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_return_key_does_not_exist()
|
||||
{
|
||||
|
@ -49,13 +49,13 @@ namespace Ocelot.UnitTests.Middleware
|
||||
|
||||
var billDownstreamContext = new DownstreamContext(new DefaultHttpContext())
|
||||
{
|
||||
DownstreamResponse = new DownstreamResponse(new StringContent("Bill says hi"), HttpStatusCode.OK, new EditableList<KeyValuePair<string, IEnumerable<string>>>()),
|
||||
DownstreamResponse = new DownstreamResponse(new StringContent("Bill says hi"), HttpStatusCode.OK, new EditableList<KeyValuePair<string, IEnumerable<string>>>(), "some reason"),
|
||||
DownstreamReRoute = billDownstreamReRoute
|
||||
};
|
||||
|
||||
var georgeDownstreamContext = new DownstreamContext(new DefaultHttpContext())
|
||||
{
|
||||
DownstreamResponse = new DownstreamResponse(new StringContent("George says hi"), HttpStatusCode.OK, new List<KeyValuePair<string, IEnumerable<string>>>()),
|
||||
DownstreamResponse = new DownstreamResponse(new StringContent("George says hi"), HttpStatusCode.OK, new List<KeyValuePair<string, IEnumerable<string>>>(), "some reason"),
|
||||
DownstreamReRoute = georgeDownstreamReRoute
|
||||
};
|
||||
|
||||
@ -69,6 +69,7 @@ namespace Ocelot.UnitTests.Middleware
|
||||
.When(x => WhenIAggregate())
|
||||
.Then(x => ThenTheContentIs(expected))
|
||||
.And(x => ThenTheContentTypeIs("application/json"))
|
||||
.And(x => ThenTheReasonPhraseIs("cannot return from aggregate..which reason phrase would you use?"))
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
@ -91,13 +92,13 @@ namespace Ocelot.UnitTests.Middleware
|
||||
|
||||
var billDownstreamContext = new DownstreamContext(new DefaultHttpContext())
|
||||
{
|
||||
DownstreamResponse = new DownstreamResponse(new StringContent("Bill says hi"), HttpStatusCode.OK, new List<KeyValuePair<string, IEnumerable<string>>>()),
|
||||
DownstreamResponse = new DownstreamResponse(new StringContent("Bill says hi"), HttpStatusCode.OK, new List<KeyValuePair<string, IEnumerable<string>>>(), "some reason"),
|
||||
DownstreamReRoute = billDownstreamReRoute
|
||||
};
|
||||
|
||||
var georgeDownstreamContext = new DownstreamContext(new DefaultHttpContext())
|
||||
{
|
||||
DownstreamResponse = new DownstreamResponse(new StringContent("Error"), HttpStatusCode.OK, new List<KeyValuePair<string, IEnumerable<string>>>()),
|
||||
DownstreamResponse = new DownstreamResponse(new StringContent("Error"), HttpStatusCode.OK, new List<KeyValuePair<string, IEnumerable<string>>>(), "some reason"),
|
||||
DownstreamReRoute = georgeDownstreamReRoute,
|
||||
};
|
||||
|
||||
@ -116,6 +117,11 @@ namespace Ocelot.UnitTests.Middleware
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
private void ThenTheReasonPhraseIs(string expected)
|
||||
{
|
||||
_upstreamContext.DownstreamResponse.ReasonPhrase.ShouldBe(expected);
|
||||
}
|
||||
|
||||
private void ThenTheErrorIsMapped()
|
||||
{
|
||||
_upstreamContext.Errors.ShouldBe(_downstreamContexts[1].Errors);
|
||||
|
@ -43,11 +43,11 @@ namespace Ocelot.UnitTests.Middleware
|
||||
{
|
||||
new DownstreamContext(new DefaultHttpContext())
|
||||
{
|
||||
DownstreamResponse = new DownstreamResponse(new StringContent("Tom"), HttpStatusCode.OK, new List<KeyValuePair<string, IEnumerable<string>>>())
|
||||
DownstreamResponse = new DownstreamResponse(new StringContent("Tom"), HttpStatusCode.OK, new List<KeyValuePair<string, IEnumerable<string>>>(), "some reason")
|
||||
},
|
||||
new DownstreamContext(new DefaultHttpContext())
|
||||
{
|
||||
DownstreamResponse = new DownstreamResponse(new StringContent("Laura"), HttpStatusCode.OK, new List<KeyValuePair<string, IEnumerable<string>>>())
|
||||
DownstreamResponse = new DownstreamResponse(new StringContent("Laura"), HttpStatusCode.OK, new List<KeyValuePair<string, IEnumerable<string>>>(), "some reason")
|
||||
}
|
||||
};
|
||||
|
||||
@ -72,11 +72,11 @@ namespace Ocelot.UnitTests.Middleware
|
||||
{
|
||||
new DownstreamContext(new DefaultHttpContext())
|
||||
{
|
||||
DownstreamResponse = new DownstreamResponse(new StringContent("Tom"), HttpStatusCode.OK, new List<KeyValuePair<string, IEnumerable<string>>>())
|
||||
DownstreamResponse = new DownstreamResponse(new StringContent("Tom"), HttpStatusCode.OK, new List<KeyValuePair<string, IEnumerable<string>>>(), "some reason")
|
||||
},
|
||||
new DownstreamContext(new DefaultHttpContext())
|
||||
{
|
||||
DownstreamResponse = new DownstreamResponse(new StringContent("Laura"), HttpStatusCode.OK, new List<KeyValuePair<string, IEnumerable<string>>>())
|
||||
DownstreamResponse = new DownstreamResponse(new StringContent("Laura"), HttpStatusCode.OK, new List<KeyValuePair<string, IEnumerable<string>>>(), "some reason")
|
||||
}
|
||||
};
|
||||
|
||||
@ -146,7 +146,7 @@ namespace Ocelot.UnitTests.Middleware
|
||||
var laura = await responses[1].Content.ReadAsStringAsync();
|
||||
var content = $"{tom}, {laura}";
|
||||
var headers = responses.SelectMany(x => x.Headers).ToList();
|
||||
return new DownstreamResponse(new StringContent(content), HttpStatusCode.OK, headers);
|
||||
return new DownstreamResponse(new StringContent(content), HttpStatusCode.OK, headers, "some reason");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user