mirror of
https://github.com/nsnail/Ocelot.git
synced 2025-04-22 06:22: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)
|
.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath)
|
||||||
.AddJsonFile("appsettings.json", true, true)
|
.AddJsonFile("appsettings.json", true, true)
|
||||||
.AddJsonFile($"appsettings.{hostingContext.HostingEnvironment.EnvironmentName}.json", true, true)
|
.AddJsonFile($"appsettings.{hostingContext.HostingEnvironment.EnvironmentName}.json", true, true)
|
||||||
.AddOcelot()
|
.AddOcelot(hostingContext.HostingEnvironment)
|
||||||
.AddEnvironmentVariables();
|
.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.
|
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
|
Store configuration in consul
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
@ -81,6 +81,7 @@ Placeholders
|
|||||||
|
|
||||||
Ocelot allows placeholders that can be used in header transformation.
|
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.
|
{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.
|
{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.
|
{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,
|
"AllowAutoRedirect": false,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
X-Forwarded-For
|
||||||
|
^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
An example of using {RemoteIpAddress} placeholder...
|
||||||
|
|
||||||
|
.. code-block:: json
|
||||||
|
|
||||||
|
"UpstreamHeaderTransform": {
|
||||||
|
"X-Forwarded-For": "{RemoteIpAddress}"
|
||||||
|
}
|
||||||
|
|
||||||
Future
|
Future
|
||||||
^^^^^^
|
^^^^^^
|
||||||
|
|
||||||
|
@ -22,7 +22,7 @@ namespace Ocelot.Authentication.Middleware
|
|||||||
|
|
||||||
public async Task Invoke(DownstreamContext context)
|
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");
|
Logger.LogInformation($"{context.HttpContext.Request.Path} is an authenticated route. {MiddlewareName} checking if client is authenticated");
|
||||||
|
|
||||||
|
@ -82,7 +82,7 @@
|
|||||||
}
|
}
|
||||||
else
|
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);
|
await _next.Invoke(context);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,14 +9,15 @@ namespace Ocelot.Cache
|
|||||||
HttpStatusCode statusCode,
|
HttpStatusCode statusCode,
|
||||||
Dictionary<string, IEnumerable<string>> headers,
|
Dictionary<string, IEnumerable<string>> headers,
|
||||||
string body,
|
string body,
|
||||||
Dictionary<string, IEnumerable<string>> contentHeaders
|
Dictionary<string, IEnumerable<string>> contentHeaders,
|
||||||
|
string reasonPhrase
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
StatusCode = statusCode;
|
StatusCode = statusCode;
|
||||||
Headers = headers ?? new Dictionary<string, IEnumerable<string>>();
|
Headers = headers ?? new Dictionary<string, IEnumerable<string>>();
|
||||||
ContentHeaders = contentHeaders ?? new Dictionary<string, IEnumerable<string>>();
|
ContentHeaders = contentHeaders ?? new Dictionary<string, IEnumerable<string>>();
|
||||||
Body = body ?? "";
|
Body = body ?? "";
|
||||||
|
ReasonPhrase = reasonPhrase;
|
||||||
}
|
}
|
||||||
|
|
||||||
public HttpStatusCode StatusCode { get; private set; }
|
public HttpStatusCode StatusCode { get; private set; }
|
||||||
@ -26,5 +27,7 @@ namespace Ocelot.Cache
|
|||||||
public Dictionary<string, IEnumerable<string>> ContentHeaders { get; private set; }
|
public Dictionary<string, IEnumerable<string>> ContentHeaders { get; private set; }
|
||||||
|
|
||||||
public string Body { 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);
|
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)
|
internal async Task<CachedResponse> CreateCachedResponse(DownstreamResponse response)
|
||||||
@ -109,7 +109,7 @@
|
|||||||
|
|
||||||
var contentHeaders = response?.Content?.Headers.ToDictionary(v => v.Key, v => v.Value);
|
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;
|
return cached;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,11 +3,11 @@ using Ocelot.Middleware.Pipeline;
|
|||||||
|
|
||||||
namespace Ocelot.Claims.Middleware
|
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
|
namespace Ocelot.Claims.Middleware
|
||||||
{
|
{
|
||||||
public class ClaimsBuilderMiddleware : OcelotMiddleware
|
public class ClaimsToClaimsMiddleware : OcelotMiddleware
|
||||||
{
|
{
|
||||||
private readonly OcelotRequestDelegate _next;
|
private readonly OcelotRequestDelegate _next;
|
||||||
private readonly IAddClaimsToRequest _addClaimsToRequest;
|
private readonly IAddClaimsToRequest _addClaimsToRequest;
|
||||||
|
|
||||||
public ClaimsBuilderMiddleware(OcelotRequestDelegate next,
|
public ClaimsToClaimsMiddleware(OcelotRequestDelegate next,
|
||||||
IOcelotLoggerFactory loggerFactory,
|
IOcelotLoggerFactory loggerFactory,
|
||||||
IAddClaimsToRequest addClaimsToRequest)
|
IAddClaimsToRequest addClaimsToRequest)
|
||||||
:base(loggerFactory.CreateLogger<ClaimsBuilderMiddleware>())
|
:base(loggerFactory.CreateLogger<ClaimsToClaimsMiddleware>())
|
||||||
{
|
{
|
||||||
_next = next;
|
_next = next;
|
||||||
_addClaimsToRequest = addClaimsToRequest;
|
_addClaimsToRequest = addClaimsToRequest;
|
@ -33,13 +33,12 @@ namespace Ocelot.Configuration.Builder
|
|||||||
private List<HeaderFindAndReplace> _upstreamHeaderFindAndReplace;
|
private List<HeaderFindAndReplace> _upstreamHeaderFindAndReplace;
|
||||||
private List<HeaderFindAndReplace> _downstreamHeaderFindAndReplace;
|
private List<HeaderFindAndReplace> _downstreamHeaderFindAndReplace;
|
||||||
private readonly List<DownstreamHostAndPort> _downstreamAddresses;
|
private readonly List<DownstreamHostAndPort> _downstreamAddresses;
|
||||||
private string _upstreamHost;
|
|
||||||
private string _key;
|
private string _key;
|
||||||
private List<string> _delegatingHandlers;
|
private List<string> _delegatingHandlers;
|
||||||
private List<AddHeader> _addHeadersToDownstream;
|
private List<AddHeader> _addHeadersToDownstream;
|
||||||
private List<AddHeader> _addHeadersToUpstream;
|
private List<AddHeader> _addHeadersToUpstream;
|
||||||
private bool _dangerousAcceptAnyServerCertificateValidator;
|
private bool _dangerousAcceptAnyServerCertificateValidator;
|
||||||
|
private SecurityOptions _securityOptions;
|
||||||
public DownstreamReRouteBuilder()
|
public DownstreamReRouteBuilder()
|
||||||
{
|
{
|
||||||
_downstreamAddresses = new List<DownstreamHostAndPort>();
|
_downstreamAddresses = new List<DownstreamHostAndPort>();
|
||||||
@ -54,12 +53,6 @@ namespace Ocelot.Configuration.Builder
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public DownstreamReRouteBuilder WithUpstreamHost(string upstreamAddresses)
|
|
||||||
{
|
|
||||||
_upstreamHost = upstreamAddresses;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public DownstreamReRouteBuilder WithLoadBalancerOptions(LoadBalancerOptions loadBalancerOptions)
|
public DownstreamReRouteBuilder WithLoadBalancerOptions(LoadBalancerOptions loadBalancerOptions)
|
||||||
{
|
{
|
||||||
_loadBalancerOptions = loadBalancerOptions;
|
_loadBalancerOptions = loadBalancerOptions;
|
||||||
@ -234,6 +227,12 @@ namespace Ocelot.Configuration.Builder
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public DownstreamReRouteBuilder WithSecurityOptions(SecurityOptions securityOptions)
|
||||||
|
{
|
||||||
|
_securityOptions = securityOptions;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
public DownstreamReRoute Build()
|
public DownstreamReRoute Build()
|
||||||
{
|
{
|
||||||
return new DownstreamReRoute(
|
return new DownstreamReRoute(
|
||||||
@ -265,7 +264,8 @@ namespace Ocelot.Configuration.Builder
|
|||||||
_delegatingHandlers,
|
_delegatingHandlers,
|
||||||
_addHeadersToDownstream,
|
_addHeadersToDownstream,
|
||||||
_addHeadersToUpstream,
|
_addHeadersToUpstream,
|
||||||
_dangerousAcceptAnyServerCertificateValidator);
|
_dangerousAcceptAnyServerCertificateValidator,
|
||||||
|
_securityOptions);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,7 @@ namespace Ocelot.Configuration.Builder
|
|||||||
private bool _isAuthorised;
|
private bool _isAuthorised;
|
||||||
private bool _isCached;
|
private bool _isCached;
|
||||||
private bool _enableRateLimiting;
|
private bool _enableRateLimiting;
|
||||||
|
private bool _useServiceDiscovery;
|
||||||
|
|
||||||
public ReRouteOptionsBuilder WithIsCached(bool isCached)
|
public ReRouteOptionsBuilder WithIsCached(bool isCached)
|
||||||
{
|
{
|
||||||
@ -31,9 +32,15 @@ namespace Ocelot.Configuration.Builder
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ReRouteOptionsBuilder WithUseServiceDiscovery(bool useServiceDiscovery)
|
||||||
|
{
|
||||||
|
_useServiceDiscovery = useServiceDiscovery;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
public ReRouteOptions Build()
|
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
|
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
|
public class FileInternalConfigurationCreator : IInternalConfigurationCreator
|
||||||
{
|
{
|
||||||
private readonly IConfigurationValidator _configurationValidator;
|
private readonly IConfigurationValidator _configurationValidator;
|
||||||
private readonly IOcelotLogger _logger;
|
private readonly IConfigurationCreator _configCreator;
|
||||||
private readonly IClaimsToThingCreator _claimsToThingCreator;
|
private readonly IDynamicsCreator _dynamicsCreator;
|
||||||
private readonly IAuthenticationOptionsCreator _authOptionsCreator;
|
private readonly IReRoutesCreator _reRoutesCreator;
|
||||||
private readonly IUpstreamTemplatePatternCreator _upstreamTemplatePatternCreator;
|
private readonly IAggregatesCreator _aggregatesCreator;
|
||||||
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;
|
|
||||||
|
|
||||||
public FileInternalConfigurationCreator(
|
public FileInternalConfigurationCreator(
|
||||||
IConfigurationValidator configurationValidator,
|
IConfigurationValidator configurationValidator,
|
||||||
IOcelotLoggerFactory loggerFactory,
|
IReRoutesCreator reRoutesCreator,
|
||||||
IClaimsToThingCreator claimsToThingCreator,
|
IAggregatesCreator aggregatesCreator,
|
||||||
IAuthenticationOptionsCreator authOptionsCreator,
|
IDynamicsCreator dynamicsCreator,
|
||||||
IUpstreamTemplatePatternCreator upstreamTemplatePatternCreator,
|
IConfigurationCreator configCreator
|
||||||
IRequestIdKeyCreator requestIdKeyCreator,
|
|
||||||
IServiceProviderConfigurationCreator serviceProviderConfigCreator,
|
|
||||||
IQoSOptionsCreator qosOptionsCreator,
|
|
||||||
IReRouteOptionsCreator fileReRouteOptionsCreator,
|
|
||||||
IRateLimitOptionsCreator rateLimitOptionsCreator,
|
|
||||||
IRegionCreator regionCreator,
|
|
||||||
IHttpHandlerOptionsCreator httpHandlerOptionsCreator,
|
|
||||||
IServiceProvider serviceProvider,
|
|
||||||
IHeaderFindAndReplaceCreator headerFAndRCreator,
|
|
||||||
IDownstreamAddressesCreator downstreamAddressesCreator
|
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
_downstreamAddressesCreator = downstreamAddressesCreator;
|
_configCreator = configCreator;
|
||||||
_headerFAndRCreator = headerFAndRCreator;
|
_dynamicsCreator = dynamicsCreator;
|
||||||
_adminPath = serviceProvider.GetService<IAdministrationPath>();
|
_aggregatesCreator = aggregatesCreator;
|
||||||
_regionCreator = regionCreator;
|
_reRoutesCreator = reRoutesCreator;
|
||||||
_rateLimitOptionsCreator = rateLimitOptionsCreator;
|
|
||||||
_requestIdKeyCreator = requestIdKeyCreator;
|
|
||||||
_upstreamTemplatePatternCreator = upstreamTemplatePatternCreator;
|
|
||||||
_authOptionsCreator = authOptionsCreator;
|
|
||||||
_configurationValidator = configurationValidator;
|
_configurationValidator = configurationValidator;
|
||||||
_logger = loggerFactory.CreateLogger<FileInternalConfigurationCreator>();
|
|
||||||
_claimsToThingCreator = claimsToThingCreator;
|
|
||||||
_serviceProviderConfigCreator = serviceProviderConfigCreator;
|
|
||||||
_qosOptionsCreator = qosOptionsCreator;
|
|
||||||
_fileReRouteOptionsCreator = fileReRouteOptionsCreator;
|
|
||||||
_httpHandlerOptionsCreator = httpHandlerOptionsCreator;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<Response<IInternalConfiguration>> Create(FileConfiguration fileConfiguration)
|
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);
|
var response = await _configurationValidator.IsValid(fileConfiguration);
|
||||||
|
|
||||||
@ -87,197 +38,20 @@ namespace Ocelot.Configuration.Creator
|
|||||||
return new ErrorResponse<IInternalConfiguration>(response.Data.Errors);
|
return new ErrorResponse<IInternalConfiguration>(response.Data.Errors);
|
||||||
}
|
}
|
||||||
|
|
||||||
var reRoutes = new List<ReRoute>();
|
var reRoutes = _reRoutesCreator.Create(fileConfiguration);
|
||||||
|
|
||||||
foreach (var reRoute in fileConfiguration.ReRoutes)
|
var aggregates = _aggregatesCreator.Create(fileConfiguration, reRoutes);
|
||||||
{
|
|
||||||
var downstreamReRoute = SetUpDownstreamReRoute(reRoute, fileConfiguration.GlobalConfiguration);
|
|
||||||
|
|
||||||
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 config = _configCreator.Create(fileConfiguration, mergedReRoutes);
|
||||||
{
|
|
||||||
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
|
|
||||||
);
|
|
||||||
|
|
||||||
return new OkResponse<IInternalConfiguration>(config);
|
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;
|
using Ocelot.Configuration.File;
|
||||||
|
|
||||||
namespace Ocelot.Configuration.Creator
|
namespace Ocelot.Configuration.Creator
|
||||||
@ -5,7 +6,7 @@ namespace Ocelot.Configuration.Creator
|
|||||||
public interface IQoSOptionsCreator
|
public interface IQoSOptionsCreator
|
||||||
{
|
{
|
||||||
QoSOptions Create(FileQoSOptions options);
|
QoSOptions Create(FileQoSOptions options);
|
||||||
QoSOptions Create(FileQoSOptions options, string pathTemplate, string[] httpMethods);
|
QoSOptions Create(FileQoSOptions options, string pathTemplate, List<string> httpMethods);
|
||||||
QoSOptions Create(QoSOptions options, string pathTemplate, 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.Builder;
|
||||||
using Ocelot.Configuration.File;
|
using Ocelot.Configuration.File;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
|
||||||
public class QoSOptionsCreator : IQoSOptionsCreator
|
public class QoSOptionsCreator : IQoSOptionsCreator
|
||||||
@ -15,14 +16,14 @@ namespace Ocelot.Configuration.Creator
|
|||||||
.Build();
|
.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);
|
var key = CreateKey(pathTemplate, httpMethods);
|
||||||
|
|
||||||
return Map(key, options.TimeoutValue, options.DurationOfBreak, options.ExceptionsAllowedBeforeBreaking);
|
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);
|
var key = CreateKey(pathTemplate, httpMethods);
|
||||||
|
|
||||||
@ -39,7 +40,7 @@ namespace Ocelot.Configuration.Creator
|
|||||||
.Build();
|
.Build();
|
||||||
}
|
}
|
||||||
|
|
||||||
private string CreateKey(string pathTemplate, string[] httpMethods)
|
private string CreateKey(string pathTemplate, List<string> httpMethods)
|
||||||
{
|
{
|
||||||
return $"{pathTemplate.FirstOrDefault()}|{string.Join(",", 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;
|
using Ocelot.Configuration.File;
|
||||||
|
|
||||||
namespace Ocelot.Configuration.Creator
|
namespace Ocelot.Configuration.Creator
|
||||||
@ -8,11 +7,9 @@ namespace Ocelot.Configuration.Creator
|
|||||||
{
|
{
|
||||||
public RateLimitOptions Create(FileRateLimitRule fileRateLimitRule, FileGlobalConfiguration globalConfiguration)
|
public RateLimitOptions Create(FileRateLimitRule fileRateLimitRule, FileGlobalConfiguration globalConfiguration)
|
||||||
{
|
{
|
||||||
RateLimitOptions rateLimitOption = null;
|
|
||||||
|
|
||||||
if (fileRateLimitRule != null && fileRateLimitRule.EnableRateLimiting)
|
if (fileRateLimitRule != null && fileRateLimitRule.EnableRateLimiting)
|
||||||
{
|
{
|
||||||
rateLimitOption = new RateLimitOptionsBuilder()
|
return new RateLimitOptionsBuilder()
|
||||||
.WithClientIdHeader(globalConfiguration.RateLimitOptions.ClientIdHeader)
|
.WithClientIdHeader(globalConfiguration.RateLimitOptions.ClientIdHeader)
|
||||||
.WithClientWhiteList(fileRateLimitRule.ClientWhitelist)
|
.WithClientWhiteList(fileRateLimitRule.ClientWhitelist)
|
||||||
.WithDisableRateLimitHeaders(globalConfiguration.RateLimitOptions.DisableRateLimitHeaders)
|
.WithDisableRateLimitHeaders(globalConfiguration.RateLimitOptions.DisableRateLimitHeaders)
|
||||||
@ -26,7 +23,7 @@ namespace Ocelot.Configuration.Creator
|
|||||||
.Build();
|
.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 isAuthorised = IsAuthorised(fileReRoute);
|
||||||
var isCached = IsCached(fileReRoute);
|
var isCached = IsCached(fileReRoute);
|
||||||
var enableRateLimiting = IsEnableRateLimiting(fileReRoute);
|
var enableRateLimiting = IsEnableRateLimiting(fileReRoute);
|
||||||
|
var useServiceDiscovery = !string.IsNullOrEmpty(fileReRoute.ServiceName);
|
||||||
|
|
||||||
var options = new ReRouteOptionsBuilder()
|
var options = new ReRouteOptionsBuilder()
|
||||||
.WithIsAuthenticated(isAuthenticated)
|
.WithIsAuthenticated(isAuthenticated)
|
||||||
.WithIsAuthorised(isAuthorised)
|
.WithIsAuthorised(isAuthorised)
|
||||||
.WithIsCached(isCached)
|
.WithIsCached(isCached)
|
||||||
.WithRateLimiting(enableRateLimiting)
|
.WithRateLimiting(enableRateLimiting)
|
||||||
|
.WithUseServiceDiscovery(useServiceDiscovery)
|
||||||
.Build();
|
.Build();
|
||||||
|
|
||||||
return options;
|
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 isAuthenticated,
|
||||||
bool isAuthorised,
|
bool isAuthorised,
|
||||||
AuthenticationOptions authenticationOptions,
|
AuthenticationOptions authenticationOptions,
|
||||||
DownstreamPathTemplate downstreamDownstreamPathTemplate,
|
DownstreamPathTemplate downstreamPathTemplate,
|
||||||
string loadBalancerKey,
|
string loadBalancerKey,
|
||||||
List<string> delegatingHandlers,
|
List<string> delegatingHandlers,
|
||||||
List<AddHeader> addHeadersToDownstream,
|
List<AddHeader> addHeadersToDownstream,
|
||||||
List<AddHeader> addHeadersToUpstream,
|
List<AddHeader> addHeadersToUpstream,
|
||||||
bool dangerousAcceptAnyServerCertificateValidator)
|
bool dangerousAcceptAnyServerCertificateValidator,
|
||||||
|
SecurityOptions securityOptions)
|
||||||
{
|
{
|
||||||
DangerousAcceptAnyServerCertificateValidator = dangerousAcceptAnyServerCertificateValidator;
|
DangerousAcceptAnyServerCertificateValidator = dangerousAcceptAnyServerCertificateValidator;
|
||||||
AddHeadersToDownstream = addHeadersToDownstream;
|
AddHeadersToDownstream = addHeadersToDownstream;
|
||||||
@ -63,9 +64,10 @@ namespace Ocelot.Configuration
|
|||||||
IsAuthenticated = isAuthenticated;
|
IsAuthenticated = isAuthenticated;
|
||||||
IsAuthorised = isAuthorised;
|
IsAuthorised = isAuthorised;
|
||||||
AuthenticationOptions = authenticationOptions;
|
AuthenticationOptions = authenticationOptions;
|
||||||
DownstreamDownstreamPathTemplate = downstreamDownstreamPathTemplate;
|
DownstreamPathTemplate = downstreamPathTemplate;
|
||||||
LoadBalancerKey = loadBalancerKey;
|
LoadBalancerKey = loadBalancerKey;
|
||||||
AddHeadersToUpstream = addHeadersToUpstream;
|
AddHeadersToUpstream = addHeadersToUpstream;
|
||||||
|
SecurityOptions = securityOptions;
|
||||||
}
|
}
|
||||||
|
|
||||||
public string Key { get; }
|
public string Key { get; }
|
||||||
@ -91,11 +93,12 @@ namespace Ocelot.Configuration
|
|||||||
public bool IsAuthenticated { get; }
|
public bool IsAuthenticated { get; }
|
||||||
public bool IsAuthorised { get; }
|
public bool IsAuthorised { get; }
|
||||||
public AuthenticationOptions AuthenticationOptions { get; }
|
public AuthenticationOptions AuthenticationOptions { get; }
|
||||||
public DownstreamPathTemplate DownstreamDownstreamPathTemplate { get; }
|
public DownstreamPathTemplate DownstreamPathTemplate { get; }
|
||||||
public string LoadBalancerKey { get; }
|
public string LoadBalancerKey { get; }
|
||||||
public List<string> DelegatingHandlers { get; }
|
public List<string> DelegatingHandlers { get; }
|
||||||
public List<AddHeader> AddHeadersToDownstream { get; }
|
public List<AddHeader> AddHeadersToDownstream { get; }
|
||||||
public List<AddHeader> AddHeadersToUpstream { get; }
|
public List<AddHeader> AddHeadersToUpstream { get; }
|
||||||
public bool DangerousAcceptAnyServerCertificateValidator { get; }
|
public bool DangerousAcceptAnyServerCertificateValidator { get; }
|
||||||
|
public SecurityOptions SecurityOptions { get; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -21,6 +21,7 @@ namespace Ocelot.Configuration.File
|
|||||||
DownstreamHostAndPorts = new List<FileHostAndPort>();
|
DownstreamHostAndPorts = new List<FileHostAndPort>();
|
||||||
DelegatingHandlers = new List<string>();
|
DelegatingHandlers = new List<string>();
|
||||||
LoadBalancerOptions = new FileLoadBalancerOptions();
|
LoadBalancerOptions = new FileLoadBalancerOptions();
|
||||||
|
SecurityOptions = new FileSecurityOptions();
|
||||||
Priority = 1;
|
Priority = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -50,5 +51,6 @@ namespace Ocelot.Configuration.File
|
|||||||
public int Priority { get;set; }
|
public int Priority { get;set; }
|
||||||
public int Timeout { get; set; }
|
public int Timeout { get; set; }
|
||||||
public bool DangerousAcceptAnyServerCertificateValidator { 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 string TimeoutStrategy { get; }
|
||||||
|
|
||||||
public bool UseQos => ExceptionsAllowedBeforeBreaking > 0 && TimeoutValue > 0;
|
public bool UseQos => ExceptionsAllowedBeforeBreaking > 0 || TimeoutValue > 0;
|
||||||
|
|
||||||
public string Key { get; }
|
public string Key { get; }
|
||||||
}
|
}
|
||||||
|
@ -2,17 +2,19 @@ namespace Ocelot.Configuration
|
|||||||
{
|
{
|
||||||
public class ReRouteOptions
|
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;
|
IsAuthenticated = isAuthenticated;
|
||||||
IsAuthorised = isAuthorised;
|
IsAuthorised = isAuthorised;
|
||||||
IsCached = isCached;
|
IsCached = isCached;
|
||||||
EnableRateLimiting = isEnableRateLimiting;
|
EnableRateLimiting = isEnableRateLimiting;
|
||||||
|
UseServiceDiscovery = useServiceDiscovery;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool IsAuthenticated { get; private set; }
|
public bool IsAuthenticated { get; private set; }
|
||||||
public bool IsAuthorised { get; private set; }
|
public bool IsAuthorised { get; private set; }
|
||||||
public bool IsCached { get; private set; }
|
public bool IsCached { get; private set; }
|
||||||
public bool EnableRateLimiting { 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 System.Text.RegularExpressions;
|
||||||
using Configuration.File;
|
using Configuration.File;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
|
using Microsoft.AspNetCore.Hosting;
|
||||||
|
|
||||||
public static class ConfigurationBuilderExtensions
|
public static class ConfigurationBuilderExtensions
|
||||||
{
|
{
|
||||||
@ -28,37 +29,42 @@ namespace Ocelot.DependencyInjection
|
|||||||
return builder;
|
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)
|
const string subConfigPattern = @"^ocelot\.[a-zA-Z0-9]+\.json$";
|
||||||
.Where(path => reg.IsMatch(path))
|
|
||||||
|
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();
|
.ToList();
|
||||||
|
|
||||||
var fileConfiguration = new FileConfiguration();
|
var fileConfiguration = new FileConfiguration();
|
||||||
|
|
||||||
foreach (var file in files)
|
foreach (var file in files)
|
||||||
{
|
{
|
||||||
// windows and unix sigh...
|
if(files.Count > 1 && file.Name.Equals(primaryConfigFile, StringComparison.OrdinalIgnoreCase))
|
||||||
if(files.Count > 1 && (Path.GetFileName(file) == "ocelot.json"))
|
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
var lines = File.ReadAllText(file);
|
var lines = File.ReadAllText(file.FullName);
|
||||||
|
|
||||||
var config = JsonConvert.DeserializeObject<FileConfiguration>(lines);
|
var config = JsonConvert.DeserializeObject<FileConfiguration>(lines);
|
||||||
|
|
||||||
// windows and unix sigh...
|
if (file.Name.Equals(globalConfigFile, StringComparison.OrdinalIgnoreCase))
|
||||||
if (Path.GetFileName(file) == "ocelot.global.json")
|
|
||||||
{
|
{
|
||||||
fileConfiguration.GlobalConfiguration = config.GlobalConfiguration;
|
fileConfiguration.GlobalConfiguration = config.GlobalConfiguration;
|
||||||
}
|
}
|
||||||
@ -69,9 +75,9 @@ namespace Ocelot.DependencyInjection
|
|||||||
|
|
||||||
var json = JsonConvert.SerializeObject(fileConfiguration);
|
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;
|
return builder;
|
||||||
}
|
}
|
||||||
|
@ -35,6 +35,8 @@ namespace Ocelot.DependencyInjection
|
|||||||
using Ocelot.Infrastructure;
|
using Ocelot.Infrastructure;
|
||||||
using Ocelot.Middleware.Multiplexer;
|
using Ocelot.Middleware.Multiplexer;
|
||||||
using Ocelot.Request.Creator;
|
using Ocelot.Request.Creator;
|
||||||
|
using Ocelot.Security.IPSecurity;
|
||||||
|
using Ocelot.Security;
|
||||||
|
|
||||||
public class OcelotBuilder : IOcelotBuilder
|
public class OcelotBuilder : IOcelotBuilder
|
||||||
{
|
{
|
||||||
@ -55,10 +57,16 @@ namespace Ocelot.DependencyInjection
|
|||||||
Services.TryAddSingleton<IInternalConfigurationCreator, FileInternalConfigurationCreator>();
|
Services.TryAddSingleton<IInternalConfigurationCreator, FileInternalConfigurationCreator>();
|
||||||
Services.TryAddSingleton<IInternalConfigurationRepository, InMemoryInternalConfigurationRepository>();
|
Services.TryAddSingleton<IInternalConfigurationRepository, InMemoryInternalConfigurationRepository>();
|
||||||
Services.TryAddSingleton<IConfigurationValidator, FileConfigurationFluentValidator>();
|
Services.TryAddSingleton<IConfigurationValidator, FileConfigurationFluentValidator>();
|
||||||
Services.AddSingleton<HostAndPortValidator>();
|
Services.TryAddSingleton<HostAndPortValidator>();
|
||||||
Services.AddSingleton<ReRouteFluentValidator>();
|
Services.TryAddSingleton<IReRoutesCreator, ReRoutesCreator>();
|
||||||
Services.AddSingleton<FileGlobalConfigurationFluentValidator>();
|
Services.TryAddSingleton<IAggregatesCreator, AggregatesCreator>();
|
||||||
Services.AddSingleton<FileQoSOptionsFluentValidator>();
|
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<IClaimsToThingCreator, ClaimsToThingCreator>();
|
||||||
Services.TryAddSingleton<IAuthenticationOptionsCreator, AuthenticationOptionsCreator>();
|
Services.TryAddSingleton<IAuthenticationOptionsCreator, AuthenticationOptionsCreator>();
|
||||||
Services.TryAddSingleton<IUpstreamTemplatePatternCreator, UpstreamTemplatePatternCreator>();
|
Services.TryAddSingleton<IUpstreamTemplatePatternCreator, UpstreamTemplatePatternCreator>();
|
||||||
@ -86,8 +94,8 @@ namespace Ocelot.DependencyInjection
|
|||||||
Services.TryAddSingleton<IUrlPathToUrlTemplateMatcher, RegExUrlMatcher>();
|
Services.TryAddSingleton<IUrlPathToUrlTemplateMatcher, RegExUrlMatcher>();
|
||||||
Services.TryAddSingleton<IPlaceholderNameAndValueFinder, UrlPathPlaceholderNameAndValueFinder>();
|
Services.TryAddSingleton<IPlaceholderNameAndValueFinder, UrlPathPlaceholderNameAndValueFinder>();
|
||||||
Services.TryAddSingleton<IDownstreamPathPlaceholderReplacer, DownstreamTemplatePathPlaceholderReplacer>();
|
Services.TryAddSingleton<IDownstreamPathPlaceholderReplacer, DownstreamTemplatePathPlaceholderReplacer>();
|
||||||
Services.AddSingleton<IDownstreamRouteProvider, DownstreamRouteFinder>();
|
Services.TryAddSingleton<IDownstreamRouteProvider, DownstreamRouteFinder>();
|
||||||
Services.AddSingleton<IDownstreamRouteProvider, DownstreamRouteCreator>();
|
Services.TryAddSingleton<IDownstreamRouteProvider, DownstreamRouteCreator>();
|
||||||
Services.TryAddSingleton<IDownstreamRouteProviderFactory, DownstreamRouteProviderFactory>();
|
Services.TryAddSingleton<IDownstreamRouteProviderFactory, DownstreamRouteProviderFactory>();
|
||||||
Services.TryAddSingleton<IHttpRequester, HttpClientHttpRequester>();
|
Services.TryAddSingleton<IHttpRequester, HttpClientHttpRequester>();
|
||||||
Services.TryAddSingleton<IHttpResponder, HttpContextResponder>();
|
Services.TryAddSingleton<IHttpResponder, HttpContextResponder>();
|
||||||
@ -108,7 +116,7 @@ namespace Ocelot.DependencyInjection
|
|||||||
Services.TryAddSingleton<OcelotDiagnosticListener>();
|
Services.TryAddSingleton<OcelotDiagnosticListener>();
|
||||||
Services.TryAddSingleton<IMultiplexer, Multiplexer>();
|
Services.TryAddSingleton<IMultiplexer, Multiplexer>();
|
||||||
Services.TryAddSingleton<IResponseAggregator, SimpleJsonResponseAggregator>();
|
Services.TryAddSingleton<IResponseAggregator, SimpleJsonResponseAggregator>();
|
||||||
Services.AddSingleton<ITracingHandlerFactory, TracingHandlerFactory>();
|
Services.TryAddSingleton<ITracingHandlerFactory, TracingHandlerFactory>();
|
||||||
Services.TryAddSingleton<IFileConfigurationPollerOptions, InMemoryFileConfigurationPollerOptions>();
|
Services.TryAddSingleton<IFileConfigurationPollerOptions, InMemoryFileConfigurationPollerOptions>();
|
||||||
Services.TryAddSingleton<IAddHeadersToResponse, AddHeadersToResponse>();
|
Services.TryAddSingleton<IAddHeadersToResponse, AddHeadersToResponse>();
|
||||||
Services.TryAddSingleton<IPlaceholders, Placeholders>();
|
Services.TryAddSingleton<IPlaceholders, Placeholders>();
|
||||||
@ -119,6 +127,9 @@ namespace Ocelot.DependencyInjection
|
|||||||
Services.TryAddSingleton<IQoSFactory, QoSFactory>();
|
Services.TryAddSingleton<IQoSFactory, QoSFactory>();
|
||||||
Services.TryAddSingleton<IExceptionToErrorMapper, HttpExeptionToErrorMapper>();
|
Services.TryAddSingleton<IExceptionToErrorMapper, HttpExeptionToErrorMapper>();
|
||||||
|
|
||||||
|
//add security
|
||||||
|
this.AddSecurity();
|
||||||
|
|
||||||
//add asp.net services..
|
//add asp.net services..
|
||||||
var assembly = typeof(FileConfigurationController).GetTypeInfo().Assembly;
|
var assembly = typeof(FileConfigurationController).GetTypeInfo().Assembly;
|
||||||
|
|
||||||
@ -147,6 +158,12 @@ namespace Ocelot.DependencyInjection
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void AddSecurity()
|
||||||
|
{
|
||||||
|
Services.TryAddSingleton<ISecurityOptionsCreator, SecurityOptionsCreator>();
|
||||||
|
Services.TryAddSingleton<ISecurityPolicy, IPSecurityPolicy>();
|
||||||
|
}
|
||||||
|
|
||||||
public IOcelotBuilder AddDelegatingHandler<THandler>(bool global = false)
|
public IOcelotBuilder AddDelegatingHandler<THandler>(bool global = false)
|
||||||
where THandler : DelegatingHandler
|
where THandler : DelegatingHandler
|
||||||
{
|
{
|
||||||
|
@ -42,7 +42,7 @@
|
|||||||
return downstreamRoute;
|
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();
|
var upstreamPathTemplate = new UpstreamPathTemplateBuilder().WithOriginalValue(upstreamUrlPath).Build();
|
||||||
|
|
||||||
|
@ -13,17 +13,14 @@ namespace Ocelot.DownstreamRouteFinder.Middleware
|
|||||||
{
|
{
|
||||||
private readonly OcelotRequestDelegate _next;
|
private readonly OcelotRequestDelegate _next;
|
||||||
private readonly IDownstreamRouteProviderFactory _factory;
|
private readonly IDownstreamRouteProviderFactory _factory;
|
||||||
private readonly IInternalConfigurationRepository _repo;
|
|
||||||
private readonly IMultiplexer _multiplexer;
|
private readonly IMultiplexer _multiplexer;
|
||||||
|
|
||||||
public DownstreamRouteFinderMiddleware(OcelotRequestDelegate next,
|
public DownstreamRouteFinderMiddleware(OcelotRequestDelegate next,
|
||||||
IOcelotLoggerFactory loggerFactory,
|
IOcelotLoggerFactory loggerFactory,
|
||||||
IDownstreamRouteProviderFactory downstreamRouteFinder,
|
IDownstreamRouteProviderFactory downstreamRouteFinder,
|
||||||
IInternalConfigurationRepository repo,
|
|
||||||
IMultiplexer multiplexer)
|
IMultiplexer multiplexer)
|
||||||
:base(loggerFactory.CreateLogger<DownstreamRouteFinderMiddleware>())
|
:base(loggerFactory.CreateLogger<DownstreamRouteFinderMiddleware>())
|
||||||
{
|
{
|
||||||
_repo = repo;
|
|
||||||
_multiplexer = multiplexer;
|
_multiplexer = multiplexer;
|
||||||
_next = next;
|
_next = next;
|
||||||
_factory = downstreamRouteFinder;
|
_factory = downstreamRouteFinder;
|
||||||
@ -51,7 +48,7 @@ namespace Ocelot.DownstreamRouteFinder.Middleware
|
|||||||
return;
|
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}");
|
Logger.LogDebug($"downstream templates are {downstreamPathTemplates}");
|
||||||
|
|
||||||
|
@ -27,7 +27,7 @@ namespace Ocelot.DownstreamUrlCreator.Middleware
|
|||||||
public async Task Invoke(DownstreamContext context)
|
public async Task Invoke(DownstreamContext context)
|
||||||
{
|
{
|
||||||
var response = _replacer
|
var response = _replacer
|
||||||
.Replace(context.DownstreamReRoute.DownstreamDownstreamPathTemplate, context.TemplatePlaceholderNameAndValues);
|
.Replace(context.DownstreamReRoute.DownstreamPathTemplate, context.TemplatePlaceholderNameAndValues);
|
||||||
|
|
||||||
if (response.IsError)
|
if (response.IsError)
|
||||||
{
|
{
|
||||||
|
@ -8,11 +8,11 @@ namespace Ocelot.DownstreamUrlCreator.UrlTemplateReplacer
|
|||||||
{
|
{
|
||||||
public class DownstreamTemplatePathPlaceholderReplacer : IDownstreamPathPlaceholderReplacer
|
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();
|
var downstreamPath = new StringBuilder();
|
||||||
|
|
||||||
downstreamPath.Append(downstreamDownstreamPathTemplate.Value);
|
downstreamPath.Append(downstreamPathTemplate.Value);
|
||||||
|
|
||||||
foreach (var placeholderVariableAndValue in urlPathPlaceholderNameAndValues)
|
foreach (var placeholderVariableAndValue in urlPathPlaceholderNameAndValues)
|
||||||
{
|
{
|
||||||
|
@ -7,6 +7,6 @@ namespace Ocelot.DownstreamUrlCreator.UrlTemplateReplacer
|
|||||||
{
|
{
|
||||||
public interface IDownstreamPathPlaceholderReplacer
|
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;
|
namespace Ocelot.Headers
|
||||||
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
|
|
||||||
{
|
{
|
||||||
|
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
|
public class AddHeadersToRequest : IAddHeadersToRequest
|
||||||
{
|
{
|
||||||
private readonly IClaimsParser _claimsParser;
|
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;
|
_claimsParser = claimsParser;
|
||||||
|
_placeholders = placeholders;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Response SetHeadersOnDownstreamRequest(List<ClaimToThing> claimsToThings, IEnumerable<System.Security.Claims.Claim> claims, DownstreamRequest downstreamRequest)
|
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)
|
public void SetHeadersOnDownstreamRequest(IEnumerable<AddHeader> headers, HttpContext context)
|
||||||
{
|
{
|
||||||
var requestHeader = context.Request.Headers;
|
var requestHeader = context.Request.Headers;
|
||||||
|
|
||||||
foreach (var header in headers)
|
foreach (var header in headers)
|
||||||
{
|
{
|
||||||
if (requestHeader.ContainsKey(header.Key))
|
if (requestHeader.ContainsKey(header.Key))
|
||||||
@ -53,7 +61,22 @@ namespace Ocelot.Headers
|
|||||||
requestHeader.Remove(header.Key);
|
requestHeader.Remove(header.Key);
|
||||||
}
|
}
|
||||||
|
|
||||||
requestHeader.Add(header.Key, header.Value);
|
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
|
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
|
public class HttpResponseHeaderReplacer : IHttpResponseHeaderReplacer
|
||||||
{
|
{
|
||||||
private readonly IPlaceholders _placeholders;
|
private readonly IPlaceholders _placeholders;
|
||||||
@ -19,8 +17,11 @@ namespace Ocelot.Headers
|
|||||||
_placeholders = placeholders;
|
_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)
|
foreach (var f in fAndRs)
|
||||||
{
|
{
|
||||||
var dict = response.Headers.ToDictionary(x => x.Key);
|
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
|
namespace Ocelot.Headers
|
||||||
{
|
{
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using Ocelot.Configuration;
|
||||||
|
using Ocelot.Middleware;
|
||||||
|
using Ocelot.Responses;
|
||||||
|
|
||||||
public interface IHttpResponseHeaderReplacer
|
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
|
namespace Ocelot.Headers.Middleware
|
||||||
{
|
{
|
||||||
public class HttpRequestHeadersBuilderMiddleware : OcelotMiddleware
|
public class ClaimsToHeadersMiddleware : OcelotMiddleware
|
||||||
{
|
{
|
||||||
private readonly OcelotRequestDelegate _next;
|
private readonly OcelotRequestDelegate _next;
|
||||||
private readonly IAddHeadersToRequest _addHeadersToRequest;
|
private readonly IAddHeadersToRequest _addHeadersToRequest;
|
||||||
|
|
||||||
public HttpRequestHeadersBuilderMiddleware(OcelotRequestDelegate next,
|
public ClaimsToHeadersMiddleware(OcelotRequestDelegate next,
|
||||||
IOcelotLoggerFactory loggerFactory,
|
IOcelotLoggerFactory loggerFactory,
|
||||||
IAddHeadersToRequest addHeadersToRequest)
|
IAddHeadersToRequest addHeadersToRequest)
|
||||||
:base(loggerFactory.CreateLogger<HttpRequestHeadersBuilderMiddleware>())
|
:base(loggerFactory.CreateLogger<ClaimsToHeadersMiddleware>())
|
||||||
{
|
{
|
||||||
_next = next;
|
_next = next;
|
||||||
_addHeadersToRequest = addHeadersToRequest;
|
_addHeadersToRequest = addHeadersToRequest;
|
||||||
@ -26,7 +26,7 @@ namespace Ocelot.Headers.Middleware
|
|||||||
{
|
{
|
||||||
if (context.DownstreamReRoute.ClaimsToHeaders.Any())
|
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);
|
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;
|
var postFAndRs = context.DownstreamReRoute.DownstreamHeadersFindAndReplace;
|
||||||
|
|
||||||
_postReplacer.Replace(context.DownstreamResponse, postFAndRs, context.DownstreamRequest);
|
_postReplacer.Replace(context, postFAndRs);
|
||||||
|
|
||||||
_addHeadersToResponse.Add(context.DownstreamReRoute.AddHeadersToDownstream, context.DownstreamResponse);
|
_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
|
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
|
public class Placeholders : IPlaceholders
|
||||||
{
|
{
|
||||||
private readonly Dictionary<string, Func<Response<string>>> _placeholders;
|
private readonly Dictionary<string, Func<Response<string>>> _placeholders;
|
||||||
private readonly Dictionary<string, Func<DownstreamRequest, string>> _requestPlaceholders;
|
private readonly Dictionary<string, Func<DownstreamRequest, string>> _requestPlaceholders;
|
||||||
private readonly IBaseUrlFinder _finder;
|
private readonly IBaseUrlFinder _finder;
|
||||||
private readonly IRequestScopedDataRepository _repo;
|
private readonly IRequestScopedDataRepository _repo;
|
||||||
|
private readonly IHttpContextAccessor _httpContextAccessor;
|
||||||
|
|
||||||
public Placeholders(IBaseUrlFinder finder, IRequestScopedDataRepository repo)
|
public Placeholders(IBaseUrlFinder finder, IRequestScopedDataRepository repo, IHttpContextAccessor httpContextAccessor)
|
||||||
{
|
{
|
||||||
_repo = repo;
|
_repo = repo;
|
||||||
|
_httpContextAccessor = httpContextAccessor;
|
||||||
_finder = finder;
|
_finder = finder;
|
||||||
_placeholders = new Dictionary<string, Func<Response<string>>>();
|
_placeholders = new Dictionary<string, Func<Response<string>>>
|
||||||
_placeholders.Add("{BaseUrl}", () => new OkResponse<string>(_finder.Find()));
|
{
|
||||||
_placeholders.Add("{TraceId}", () => {
|
{ "{BaseUrl}", GetBaseUrl() },
|
||||||
var traceId = _repo.Get<string>("TraceId");
|
{ "{TraceId}", GetTraceId() },
|
||||||
if(traceId.IsError)
|
{ "{RemoteIpAddress}", GetRemoteIpAddress() }
|
||||||
{
|
|
||||||
return new ErrorResponse<string>(traceId.Errors);
|
|
||||||
}
|
|
||||||
|
|
||||||
return new OkResponse<string>(traceId.Data);
|
};
|
||||||
});
|
|
||||||
|
|
||||||
_requestPlaceholders = new Dictionary<string, Func<DownstreamRequest, string>>();
|
_requestPlaceholders = new Dictionary<string, Func<DownstreamRequest, string>>
|
||||||
_requestPlaceholders.Add("{DownstreamBaseUrl}", x => {
|
{
|
||||||
var downstreamUrl = $"{x.Scheme}://{x.Host}";
|
{ "{DownstreamBaseUrl}", GetDownstreamBaseUrl() }
|
||||||
|
};
|
||||||
if(x.Port != 80 && x.Port != 443)
|
|
||||||
{
|
|
||||||
downstreamUrl = $"{downstreamUrl}:{x.Port}";
|
|
||||||
}
|
|
||||||
|
|
||||||
return $"{downstreamUrl}/";
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Response<string> Get(string key)
|
public Response<string> Get(string key)
|
||||||
@ -67,5 +58,56 @@ namespace Ocelot.Infrastructure
|
|||||||
|
|
||||||
return new ErrorResponse<string>(new CouldNotFindPlaceholderError(key));
|
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 class DownstreamResponse
|
||||||
{
|
{
|
||||||
public DownstreamResponse(HttpContent content, HttpStatusCode statusCode, List<Header> headers)
|
public DownstreamResponse(HttpContent content, HttpStatusCode statusCode, List<Header> headers, string reasonPhrase)
|
||||||
{
|
{
|
||||||
Content = content;
|
Content = content;
|
||||||
StatusCode = statusCode;
|
StatusCode = statusCode;
|
||||||
Headers = headers ?? new List<Header>();
|
Headers = headers ?? new List<Header>();
|
||||||
|
ReasonPhrase = reasonPhrase;
|
||||||
}
|
}
|
||||||
|
|
||||||
public DownstreamResponse(HttpResponseMessage response)
|
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)
|
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())
|
:this(content, statusCode, headers.Select(x => new Header(x.Key, x.Value)).ToList(), reasonPhrase)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public HttpContent Content { get; }
|
public HttpContent Content { get; }
|
||||||
public HttpStatusCode StatusCode { get; }
|
public HttpStatusCode StatusCode { get; }
|
||||||
public List<Header> Headers { get; }
|
public List<Header> Headers { get; }
|
||||||
|
public string ReasonPhrase {get;}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -46,7 +46,7 @@ namespace Ocelot.Middleware.Multiplexer
|
|||||||
Headers = {ContentType = new MediaTypeHeaderValue("application/json")}
|
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)
|
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.Requester.Middleware;
|
||||||
using Ocelot.RequestId.Middleware;
|
using Ocelot.RequestId.Middleware;
|
||||||
using Ocelot.Responder.Middleware;
|
using Ocelot.Responder.Middleware;
|
||||||
|
using Ocelot.Security.Middleware;
|
||||||
using Ocelot.WebSockets.Middleware;
|
using Ocelot.WebSockets.Middleware;
|
||||||
|
|
||||||
namespace Ocelot.Middleware.Pipeline
|
namespace Ocelot.Middleware.Pipeline
|
||||||
@ -48,6 +49,9 @@ namespace Ocelot.Middleware.Pipeline
|
|||||||
// Then we get the downstream route information
|
// Then we get the downstream route information
|
||||||
builder.UseDownstreamRouteFinderMiddleware();
|
builder.UseDownstreamRouteFinderMiddleware();
|
||||||
|
|
||||||
|
// This security module, IP whitelist blacklist, extended security mechanism
|
||||||
|
builder.UseSecurityMiddleware();
|
||||||
|
|
||||||
//Expand other branch pipes
|
//Expand other branch pipes
|
||||||
if (pipelineConfiguration.MapWhenOcelotPipeline != null)
|
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
|
// 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.
|
// Allow pre authorisation logic. The idea being people might want to run something custom before what is built in.
|
||||||
builder.UseIfNotNull(pipelineConfiguration.PreAuthorisationMiddleware);
|
builder.UseIfNotNull(pipelineConfiguration.PreAuthorisationMiddleware);
|
||||||
@ -105,14 +109,14 @@ namespace Ocelot.Middleware.Pipeline
|
|||||||
builder.Use(pipelineConfiguration.AuthorisationMiddleware);
|
builder.Use(pipelineConfiguration.AuthorisationMiddleware);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Now we can run any header transformation logic
|
// Now we can run the claims to headers transformation middleware
|
||||||
builder.UseHttpRequestHeadersBuilderMiddleware();
|
builder.UseClaimsToHeadersMiddleware();
|
||||||
|
|
||||||
// Allow the user to implement their own query string manipulation logic
|
// Allow the user to implement their own query string manipulation logic
|
||||||
builder.UseIfNotNull(pipelineConfiguration.PreQueryStringBuilderMiddleware);
|
builder.UseIfNotNull(pipelineConfiguration.PreQueryStringBuilderMiddleware);
|
||||||
|
|
||||||
// Now we can run any query string transformation logic
|
// Now we can run any claims to query string transformation middleware
|
||||||
builder.UseQueryStringBuilderMiddleware();
|
builder.UseClaimsToQueryStringMiddleware();
|
||||||
|
|
||||||
// Get the load balancer for this request
|
// Get the load balancer for this request
|
||||||
builder.UseLoadBalancingMiddleware();
|
builder.UseLoadBalancingMiddleware();
|
||||||
|
@ -1,22 +1,22 @@
|
|||||||
using System.Linq;
|
namespace Ocelot.QueryStrings.Middleware
|
||||||
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
|
|
||||||
{
|
{
|
||||||
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 OcelotRequestDelegate _next;
|
||||||
private readonly IAddQueriesToRequest _addQueriesToRequest;
|
private readonly IAddQueriesToRequest _addQueriesToRequest;
|
||||||
|
|
||||||
public QueryStringBuilderMiddleware(OcelotRequestDelegate next,
|
public ClaimsToQueryStringMiddleware(OcelotRequestDelegate next,
|
||||||
IOcelotLoggerFactory loggerFactory,
|
IOcelotLoggerFactory loggerFactory,
|
||||||
IAddQueriesToRequest addQueriesToRequest)
|
IAddQueriesToRequest addQueriesToRequest)
|
||||||
: base(loggerFactory.CreateLogger<QueryStringBuilderMiddleware>())
|
: base(loggerFactory.CreateLogger<ClaimsToQueryStringMiddleware>())
|
||||||
{
|
{
|
||||||
_next = next;
|
_next = next;
|
||||||
_addQueriesToRequest = addQueriesToRequest;
|
_addQueriesToRequest = addQueriesToRequest;
|
||||||
@ -26,7 +26,7 @@ namespace Ocelot.QueryStrings.Middleware
|
|||||||
{
|
{
|
||||||
if (context.DownstreamReRoute.ClaimsToQueries.Any())
|
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);
|
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
|
// check if rate limiting is enabled
|
||||||
if (!context.DownstreamReRoute.EnableEndpointEndpointRateLimiting)
|
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);
|
await _next.Invoke(context);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -45,7 +45,7 @@ namespace Ocelot.RateLimit.Middleware
|
|||||||
// check white list
|
// check white list
|
||||||
if (IsWhitelisted(identity, options))
|
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);
|
await _next.Invoke(context);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
namespace Ocelot.RateLimit
|
namespace Ocelot.RateLimit
|
||||||
{
|
{
|
||||||
@ -10,10 +11,11 @@ namespace Ocelot.RateLimit
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public struct RateLimitCounter
|
public struct RateLimitCounter
|
||||||
{
|
{
|
||||||
public RateLimitCounter(DateTime timestamp, long totalRequest)
|
[JsonConstructor]
|
||||||
|
public RateLimitCounter(DateTime timestamp, long totalRequests)
|
||||||
{
|
{
|
||||||
Timestamp = timestamp;
|
Timestamp = timestamp;
|
||||||
TotalRequests = totalRequest;
|
TotalRequests = totalRequests;
|
||||||
}
|
}
|
||||||
|
|
||||||
public DateTime Timestamp { get; private set; }
|
public DateTime Timestamp { get; private set; }
|
||||||
|
@ -51,7 +51,7 @@ namespace Ocelot.Requester
|
|||||||
handler.ServerCertificateCustomValidationCallback = (request, certificate, chain, errors) => true;
|
handler.ServerCertificateCustomValidationCallback = (request, certificate, chain, errors) => true;
|
||||||
|
|
||||||
_logger
|
_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
|
var timeout = context.DownstreamReRoute.QosOptions.TimeoutValue == 0
|
||||||
|
@ -27,7 +27,7 @@ namespace Ocelot.Requester.QoS
|
|||||||
return new OkResponse<DelegatingHandler>(handler(request, _ocelotLoggerFactory));
|
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.Net;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using Microsoft.AspNetCore.Http.Features;
|
||||||
using Microsoft.Extensions.Primitives;
|
using Microsoft.Extensions.Primitives;
|
||||||
using Ocelot.Headers;
|
using Ocelot.Headers;
|
||||||
using Ocelot.Middleware;
|
using Ocelot.Middleware;
|
||||||
@ -43,14 +44,9 @@ namespace Ocelot.Responder
|
|||||||
AddHeaderIfDoesntExist(context, new Header("Content-Length", new []{ response.Content.Headers.ContentLength.ToString() }) );
|
AddHeaderIfDoesntExist(context, new Header("Content-Length", new []{ response.Content.Headers.ContentLength.ToString() }) );
|
||||||
}
|
}
|
||||||
|
|
||||||
context.Response.OnStarting(state =>
|
SetStatusCode(context, (int)response.StatusCode);
|
||||||
{
|
|
||||||
var httpContext = (HttpContext)state;
|
|
||||||
|
|
||||||
httpContext.Response.StatusCode = (int)response.StatusCode;
|
context.Response.HttpContext.Features.Get<IHttpResponseFeature>().ReasonPhrase = response.ReasonPhrase;
|
||||||
|
|
||||||
return Task.CompletedTask;
|
|
||||||
}, context);
|
|
||||||
|
|
||||||
using(content)
|
using(content)
|
||||||
{
|
{
|
||||||
@ -63,7 +59,15 @@ namespace Ocelot.Responder
|
|||||||
|
|
||||||
public void SetErrorResponseOnContext(HttpContext context, int statusCode)
|
public void SetErrorResponseOnContext(HttpContext context, int statusCode)
|
||||||
{
|
{
|
||||||
context.Response.StatusCode = 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)
|
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;
|
||||||
|
using System.Linq;
|
||||||
using System.Net.WebSockets;
|
using System.Net.WebSockets;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
@ -10,7 +15,10 @@ namespace Ocelot.WebSockets.Middleware
|
|||||||
{
|
{
|
||||||
public class WebSocketsProxyMiddleware : OcelotMiddleware
|
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,
|
public WebSocketsProxyMiddleware(OcelotRequestDelegate next,
|
||||||
IOcelotLoggerFactory loggerFactory)
|
IOcelotLoggerFactory loggerFactory)
|
||||||
@ -19,6 +27,37 @@ namespace Ocelot.WebSockets.Middleware
|
|||||||
_next = next;
|
_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)
|
public async Task Invoke(DownstreamContext context)
|
||||||
{
|
{
|
||||||
await Proxy(context.HttpContext, context.DownstreamRequest.ToUri());
|
await Proxy(context.HttpContext, context.DownstreamRequest.ToUri());
|
||||||
@ -26,88 +65,42 @@ namespace Ocelot.WebSockets.Middleware
|
|||||||
|
|
||||||
private async Task Proxy(HttpContext context, string serverEndpoint)
|
private async Task Proxy(HttpContext context, string serverEndpoint)
|
||||||
{
|
{
|
||||||
var wsToUpstreamClient = await context.WebSockets.AcceptWebSocketAsync();
|
if (context == null)
|
||||||
|
|
||||||
var wsToDownstreamService = new ClientWebSocket();
|
|
||||||
|
|
||||||
foreach (var requestHeader in context.Request.Headers)
|
|
||||||
{
|
{
|
||||||
// Do not copy the Sec-Websocket headers because it is specified by the own connection it will fail when you copy this one.
|
throw new ArgumentNullException(nameof(context));
|
||||||
if (requestHeader.Key.StartsWith("Sec-WebSocket"))
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
wsToDownstreamService.Options.SetRequestHeader(requestHeader.Key, requestHeader.Value);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var uri = new Uri(serverEndpoint);
|
if (serverEndpoint == null)
|
||||||
await wsToDownstreamService.ConnectAsync(uri, CancellationToken.None);
|
|
||||||
|
|
||||||
var receiveFromUpstreamSendToDownstream = Task.Run(async () =>
|
|
||||||
{
|
{
|
||||||
var buffer = new byte[1024 * 4];
|
throw new ArgumentNullException(nameof(serverEndpoint));
|
||||||
|
}
|
||||||
|
|
||||||
var receiveSegment = new ArraySegment<byte>(buffer);
|
if (!context.WebSockets.IsWebSocketRequest)
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
await wsToDownstreamService.SendAsync(sendSegment, result.MessageType, result.EndOfMessage,
|
|
||||||
CancellationToken.None);
|
|
||||||
|
|
||||||
if (wsToUpstreamClient.State != WebSocketState.Open)
|
|
||||||
{
|
|
||||||
await wsToDownstreamService.CloseAsync(WebSocketCloseStatus.Empty, "",
|
|
||||||
CancellationToken.None);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
var receiveFromDownstreamAndSendToUpstream = Task.Run(async () =>
|
|
||||||
{
|
{
|
||||||
var buffer = new byte[1024 * 4];
|
throw new InvalidOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
while (wsToDownstreamService.State == WebSocketState.Open || wsToDownstreamService.State == WebSocketState.CloseSent)
|
var client = new ClientWebSocket();
|
||||||
|
foreach (var protocol in context.WebSockets.WebSocketRequestedProtocols)
|
||||||
|
{
|
||||||
|
client.Options.AddSubProtocol(protocol);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var headerEntry in context.Request.Headers)
|
||||||
|
{
|
||||||
|
if (!NotForwardedWebSocketHeaders.Contains(headerEntry.Key, StringComparer.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
if (wsToUpstreamClient.State != WebSocketState.Open)
|
client.Options.SetRequestHeader(headerEntry.Key, headerEntry.Value);
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var receiveSegment = new ArraySegment<byte>(buffer);
|
|
||||||
var result = await wsToDownstreamService.ReceiveAsync(receiveSegment, CancellationToken.None);
|
|
||||||
|
|
||||||
if (result.MessageType == WebSocketMessageType.Close)
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
var sendSegment = new ArraySegment<byte>(buffer, 0, result.Count);
|
|
||||||
|
|
||||||
//send to upstream client
|
|
||||||
await wsToUpstreamClient.SendAsync(sendSegment, result.MessageType, result.EndOfMessage,
|
|
||||||
CancellationToken.None);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
|
||||||
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();
|
_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]
|
[Fact]
|
||||||
public void should_return_response_200_with_simple_url_user_defined_aggregate()
|
public void should_return_response_200_with_simple_url_user_defined_aggregate()
|
||||||
{
|
{
|
||||||
@ -71,7 +181,7 @@ namespace Ocelot.AcceptanceTests
|
|||||||
Key = "Tom"
|
Key = "Tom"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Aggregates = new List<FileAggregateReRoute>
|
Aggregates = new List<FileAggregateReRoute>
|
||||||
{
|
{
|
||||||
new FileAggregateReRoute
|
new FileAggregateReRoute
|
||||||
{
|
{
|
||||||
@ -140,7 +250,7 @@ namespace Ocelot.AcceptanceTests
|
|||||||
Key = "Tom"
|
Key = "Tom"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Aggregates = new List<FileAggregateReRoute>
|
Aggregates = new List<FileAggregateReRoute>
|
||||||
{
|
{
|
||||||
new FileAggregateReRoute
|
new FileAggregateReRoute
|
||||||
{
|
{
|
||||||
@ -368,6 +478,15 @@ namespace Ocelot.AcceptanceTests
|
|||||||
.BDDfy();
|
.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)
|
private void GivenServiceOneIsRunning(string baseUrl, string basePath, int statusCode, string responseBody)
|
||||||
{
|
{
|
||||||
_serviceHandler.GivenThereIsAServiceRunningOn(baseUrl, basePath, async context =>
|
_serviceHandler.GivenThereIsAServiceRunningOn(baseUrl, basePath, async context =>
|
||||||
@ -440,7 +559,7 @@ namespace Ocelot.AcceptanceTests
|
|||||||
var merge = $"{one}, {two}";
|
var merge = $"{one}, {two}";
|
||||||
merge = merge.Replace("Hello", "Bye").Replace("{", "").Replace("}", "");
|
merge = merge.Replace("Hello", "Bye").Replace("{", "").Replace("}", "");
|
||||||
var headers = responses.SelectMany(x => x.Headers).ToList();
|
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.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
|
using System.Net.Sockets;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
using Ocelot.Configuration.File;
|
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);
|
header.First().ShouldBe(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void ThenTheReasonPhraseIs(string expected)
|
||||||
|
{
|
||||||
|
_response.ReasonPhrase.ShouldBe(expected);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <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.
|
/// 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>
|
/// </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(DictionaryBenchmarks),
|
||||||
typeof(UrlPathToUrlPathTemplateMatcherBenchmarks),
|
typeof(UrlPathToUrlPathTemplateMatcherBenchmarks),
|
||||||
typeof(AllTheThingsBenchmarks),
|
typeof(AllTheThingsBenchmarks),
|
||||||
typeof(ExceptionHandlerMiddlewareBenchmarks)
|
typeof(ExceptionHandlerMiddlewareBenchmarks),
|
||||||
|
typeof(DownstreamRouteFinderMiddlewareBenchmarks)
|
||||||
});
|
});
|
||||||
|
|
||||||
switcher.Run(args);
|
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();
|
.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()
|
private void WhenICallTheMiddleware()
|
||||||
{
|
{
|
||||||
_next = (context) => {
|
_next = (context) => {
|
||||||
@ -68,6 +82,11 @@ namespace Ocelot.UnitTests.Authentication
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void GivenTheRequestIsUsingOptionsMethod()
|
||||||
|
{
|
||||||
|
_downstreamContext.HttpContext.Request.Method = "OPTIONS";
|
||||||
|
}
|
||||||
|
|
||||||
private void ThenTheUserIsAuthenticated()
|
private void ThenTheUserIsAuthenticated()
|
||||||
{
|
{
|
||||||
var content = _downstreamContext.HttpContext.Response.Body.AsString();
|
var content = _downstreamContext.HttpContext.Response.Body.AsString();
|
||||||
@ -84,7 +103,7 @@ namespace Ocelot.UnitTests.Authentication
|
|||||||
{
|
{
|
||||||
public static string AsString(this Stream stream)
|
public static string AsString(this Stream stream)
|
||||||
{
|
{
|
||||||
using(var reader = new StreamReader(stream))
|
using (var reader = new StreamReader(stream))
|
||||||
{
|
{
|
||||||
string text = reader.ReadToEnd();
|
string text = reader.ReadToEnd();
|
||||||
return text;
|
return text;
|
||||||
|
@ -53,7 +53,7 @@
|
|||||||
{ "content-type", new List<string> { "application/json" } }
|
{ "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))
|
this.Given(x => x.GivenThereIsACachedResponse(cachedResponse))
|
||||||
.And(x => x.GivenTheDownstreamRouteIs())
|
.And(x => x.GivenTheDownstreamRouteIs())
|
||||||
.When(x => x.WhenICallTheMiddleware())
|
.When(x => x.WhenICallTheMiddleware())
|
||||||
@ -69,7 +69,7 @@
|
|||||||
{ "Expires", new List<string> { "-1" } }
|
{ "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))
|
this.Given(x => x.GivenThereIsACachedResponse(cachedResponse))
|
||||||
.And(x => x.GivenTheDownstreamRouteIs())
|
.And(x => x.GivenTheDownstreamRouteIs())
|
||||||
.When(x => x.WhenICallTheMiddleware())
|
.When(x => x.WhenICallTheMiddleware())
|
||||||
|
@ -17,24 +17,24 @@ namespace Ocelot.UnitTests.Claims
|
|||||||
using TestStack.BDDfy;
|
using TestStack.BDDfy;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
public class ClaimsBuilderMiddlewareTests
|
public class ClaimsToClaimsMiddlewareTests
|
||||||
{
|
{
|
||||||
private readonly Mock<IAddClaimsToRequest> _addHeaders;
|
private readonly Mock<IAddClaimsToRequest> _addHeaders;
|
||||||
private Mock<IOcelotLoggerFactory> _loggerFactory;
|
private Mock<IOcelotLoggerFactory> _loggerFactory;
|
||||||
private Mock<IOcelotLogger> _logger;
|
private Mock<IOcelotLogger> _logger;
|
||||||
private readonly ClaimsBuilderMiddleware _middleware;
|
private readonly ClaimsToClaimsMiddleware _middleware;
|
||||||
private readonly DownstreamContext _downstreamContext;
|
private readonly DownstreamContext _downstreamContext;
|
||||||
private OcelotRequestDelegate _next;
|
private OcelotRequestDelegate _next;
|
||||||
|
|
||||||
public ClaimsBuilderMiddlewareTests()
|
public ClaimsToClaimsMiddlewareTests()
|
||||||
{
|
{
|
||||||
_addHeaders = new Mock<IAddClaimsToRequest>();
|
_addHeaders = new Mock<IAddClaimsToRequest>();
|
||||||
_downstreamContext = new DownstreamContext(new DefaultHttpContext());
|
_downstreamContext = new DownstreamContext(new DefaultHttpContext());
|
||||||
_loggerFactory = new Mock<IOcelotLoggerFactory>();
|
_loggerFactory = new Mock<IOcelotLoggerFactory>();
|
||||||
_logger = new Mock<IOcelotLogger>();
|
_logger = new Mock<IOcelotLogger>();
|
||||||
_loggerFactory.Setup(x => x.CreateLogger<ClaimsBuilderMiddleware>()).Returns(_logger.Object);
|
_loggerFactory.Setup(x => x.CreateLogger<ClaimsToClaimsMiddleware>()).Returns(_logger.Object);
|
||||||
_next = context => Task.CompletedTask;
|
_next = context => Task.CompletedTask;
|
||||||
_middleware = new ClaimsBuilderMiddleware(_next, _loggerFactory.Object, _addHeaders.Object);
|
_middleware = new ClaimsToClaimsMiddleware(_next, _loggerFactory.Object, _addHeaders.Object);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[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();
|
.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]
|
[Fact]
|
||||||
public void should_use_base_url_placeholder()
|
public void should_use_base_url_placeholder()
|
||||||
{
|
{
|
||||||
|
@ -42,7 +42,7 @@ namespace Ocelot.UnitTests.Configuration
|
|||||||
|
|
||||||
private void ThenTheConfigurationIsReturned()
|
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()
|
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
|
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
|
public class ReRouteOptionsCreatorTests
|
||||||
{
|
{
|
||||||
private ReRouteOptionsCreator _creator;
|
private readonly ReRouteOptionsCreator _creator;
|
||||||
private FileReRoute _reRoute;
|
private FileReRoute _reRoute;
|
||||||
private ReRouteOptions _result;
|
private ReRouteOptions _result;
|
||||||
|
|
||||||
@ -40,7 +40,8 @@ namespace Ocelot.UnitTests.Configuration
|
|||||||
FileCacheOptions = new FileCacheOptions
|
FileCacheOptions = new FileCacheOptions
|
||||||
{
|
{
|
||||||
TtlSeconds = 1
|
TtlSeconds = 1
|
||||||
}
|
},
|
||||||
|
ServiceName = "west"
|
||||||
};
|
};
|
||||||
|
|
||||||
var expected = new ReRouteOptionsBuilder()
|
var expected = new ReRouteOptionsBuilder()
|
||||||
@ -48,6 +49,7 @@ namespace Ocelot.UnitTests.Configuration
|
|||||||
.WithIsAuthorised(true)
|
.WithIsAuthorised(true)
|
||||||
.WithIsCached(true)
|
.WithIsCached(true)
|
||||||
.WithRateLimiting(true)
|
.WithRateLimiting(true)
|
||||||
|
.WithUseServiceDiscovery(true)
|
||||||
.Build();
|
.Build();
|
||||||
|
|
||||||
this.Given(x => x.GivenTheFollowing(reRoute))
|
this.Given(x => x.GivenTheFollowing(reRoute))
|
||||||
@ -72,6 +74,7 @@ namespace Ocelot.UnitTests.Configuration
|
|||||||
_result.IsAuthorised.ShouldBe(expected.IsAuthorised);
|
_result.IsAuthorised.ShouldBe(expected.IsAuthorised);
|
||||||
_result.IsCached.ShouldBe(expected.IsCached);
|
_result.IsCached.ShouldBe(expected.IsCached);
|
||||||
_result.EnableRateLimiting.ShouldBe(expected.EnableRateLimiting);
|
_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 Shouldly;
|
||||||
using TestStack.BDDfy;
|
using TestStack.BDDfy;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
using Moq;
|
||||||
|
using Microsoft.AspNetCore.Hosting;
|
||||||
|
|
||||||
public class ConfigurationBuilderExtensionsTests
|
public class ConfigurationBuilderExtensionsTests
|
||||||
{
|
{
|
||||||
@ -19,6 +21,21 @@
|
|||||||
private FileConfiguration _reRouteA;
|
private FileConfiguration _reRouteA;
|
||||||
private FileConfiguration _reRouteB;
|
private FileConfiguration _reRouteB;
|
||||||
private FileConfiguration _aggregate;
|
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]
|
[Fact]
|
||||||
public void should_add_base_url_to_config()
|
public void should_add_base_url_to_config()
|
||||||
@ -32,23 +49,35 @@
|
|||||||
[Fact]
|
[Fact]
|
||||||
public void should_merge_files()
|
public void should_merge_files()
|
||||||
{
|
{
|
||||||
this.Given(_ => GivenMultipleConfigurationFiles(""))
|
this.Given(_ => GivenMultipleConfigurationFiles("", false))
|
||||||
|
.And(_ => GivenTheEnvironmentIs(null))
|
||||||
.When(_ => WhenIAddOcelotConfiguration())
|
.When(_ => WhenIAddOcelotConfiguration())
|
||||||
.Then(_ => ThenTheConfigsAreMerged())
|
.Then(_ => ThenTheConfigsAreMerged())
|
||||||
.BDDfy();
|
.BDDfy();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void should_merge_files_except_env()
|
||||||
|
{
|
||||||
|
this.Given(_ => GivenMultipleConfigurationFiles("", true))
|
||||||
|
.And(_ => GivenTheEnvironmentIs("Env"))
|
||||||
|
.When(_ => WhenIAddOcelotConfiguration())
|
||||||
|
.Then(_ => ThenTheConfigsAreMerged())
|
||||||
|
.And(_ => NotContainsEnvSpecificConfig())
|
||||||
|
.BDDfy();
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void should_merge_files_in_specific_folder()
|
public void should_merge_files_in_specific_folder()
|
||||||
{
|
{
|
||||||
string configFolder = "ConfigFiles";
|
string configFolder = "ConfigFiles";
|
||||||
this.Given(_ => GivenMultipleConfigurationFiles(configFolder))
|
this.Given(_ => GivenMultipleConfigurationFiles(configFolder, false))
|
||||||
.When(_ => WhenIAddOcelotConfigurationWithSpecificFolder(configFolder))
|
.When(_ => WhenIAddOcelotConfigurationWithSpecificFolder(configFolder))
|
||||||
.Then(_ => ThenTheConfigsAreMerged())
|
.Then(_ => ThenTheConfigsAreMerged())
|
||||||
.BDDfy();
|
.BDDfy();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void GivenMultipleConfigurationFiles(string folder)
|
private void GivenMultipleConfigurationFiles(string folder, bool addEnvSpecificConfig)
|
||||||
{
|
{
|
||||||
if (!string.IsNullOrEmpty(folder))
|
if (!string.IsNullOrEmpty(folder))
|
||||||
{
|
{
|
||||||
@ -85,7 +114,7 @@
|
|||||||
new FileReRoute
|
new FileReRoute
|
||||||
{
|
{
|
||||||
DownstreamScheme = "DownstreamScheme",
|
DownstreamScheme = "DownstreamScheme",
|
||||||
DownstreamPathTemplate = "DownstreamDownstreamPathTemplate",
|
DownstreamPathTemplate = "DownstreamPathTemplate",
|
||||||
Key = "Key",
|
Key = "Key",
|
||||||
UpstreamHost = "UpstreamHost",
|
UpstreamHost = "UpstreamHost",
|
||||||
UpstreamHttpMethod = new List<string>
|
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 globalFilename = Path.Combine(folder, "ocelot.global.json");
|
||||||
string reroutesAFilename = Path.Combine(folder, "ocelot.reRoutesA.json");
|
string reroutesAFilename = Path.Combine(folder, "ocelot.reRoutesA.json");
|
||||||
string reroutesBFilename = Path.Combine(folder, "ocelot.reRoutesB.json");
|
string reroutesBFilename = Path.Combine(folder, "ocelot.reRoutesB.json");
|
||||||
@ -183,19 +238,32 @@
|
|||||||
File.WriteAllText(reroutesAFilename, JsonConvert.SerializeObject(_reRouteA));
|
File.WriteAllText(reroutesAFilename, JsonConvert.SerializeObject(_reRouteA));
|
||||||
File.WriteAllText(reroutesBFilename, JsonConvert.SerializeObject(_reRouteB));
|
File.WriteAllText(reroutesBFilename, JsonConvert.SerializeObject(_reRouteB));
|
||||||
File.WriteAllText(aggregatesFilename, JsonConvert.SerializeObject(_aggregate));
|
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()
|
private void WhenIAddOcelotConfiguration()
|
||||||
{
|
{
|
||||||
IConfigurationBuilder builder = new ConfigurationBuilder();
|
IConfigurationBuilder builder = new ConfigurationBuilder();
|
||||||
builder.AddOcelot();
|
|
||||||
|
builder.AddOcelot(_hostingEnvironment.Object);
|
||||||
|
|
||||||
_configRoot = builder.Build();
|
_configRoot = builder.Build();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void WhenIAddOcelotConfigurationWithSpecificFolder(string folder)
|
private void WhenIAddOcelotConfigurationWithSpecificFolder(string folder)
|
||||||
{
|
{
|
||||||
IConfigurationBuilder builder = new ConfigurationBuilder();
|
IConfigurationBuilder builder = new ConfigurationBuilder();
|
||||||
builder.AddOcelot(folder);
|
builder.AddOcelot(folder, _hostingEnvironment.Object);
|
||||||
_configRoot = builder.Build();
|
_configRoot = builder.Build();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -235,6 +303,14 @@
|
|||||||
fc.Aggregates.Count.ShouldBe(_aggregate.Aggregates.Count);
|
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)
|
private void GivenTheBaseUrl(string baseUrl)
|
||||||
{
|
{
|
||||||
#pragma warning disable CS0618
|
#pragma warning disable CS0618
|
||||||
|
@ -37,7 +37,7 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder
|
|||||||
_handlerOptions = new HttpHandlerOptionsBuilder().Build();
|
_handlerOptions = new HttpHandlerOptionsBuilder().Build();
|
||||||
_loadBalancerOptions = new LoadBalancerOptionsBuilder().WithType(nameof(NoLoadBalancer)).Build();
|
_loadBalancerOptions = new LoadBalancerOptionsBuilder().WithType(nameof(NoLoadBalancer)).Build();
|
||||||
_qosOptionsCreator
|
_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);
|
.Returns(_qoSOptions);
|
||||||
_creator = new DownstreamRouteCreator(_qosOptionsCreator.Object);
|
_creator = new DownstreamRouteCreator(_qosOptionsCreator.Object);
|
||||||
}
|
}
|
||||||
@ -198,7 +198,7 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder
|
|||||||
private void GivenTheQosCreatorReturns(QoSOptions options)
|
private void GivenTheQosCreatorReturns(QoSOptions options)
|
||||||
{
|
{
|
||||||
_qosOptionsCreator
|
_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);
|
.Returns(options);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -211,7 +211,7 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder
|
|||||||
|
|
||||||
private void ThenTheDownstreamRouteIsCreated()
|
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.UpstreamHttpMethod[0].ShouldBe(HttpMethod.Get);
|
||||||
_result.Data.ReRoute.DownstreamReRoute[0].ServiceName.ShouldBe("auth");
|
_result.Data.ReRoute.DownstreamReRoute[0].ServiceName.ShouldBe("auth");
|
||||||
_result.Data.ReRoute.DownstreamReRoute[0].LoadBalancerKey.ShouldBe("/auth/test|GET");
|
_result.Data.ReRoute.DownstreamReRoute[0].LoadBalancerKey.ShouldBe("/auth/test|GET");
|
||||||
@ -229,21 +229,21 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder
|
|||||||
|
|
||||||
private void ThenTheDownstreamPathIsForwardSlash()
|
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].ServiceName.ShouldBe("auth");
|
||||||
_result.Data.ReRoute.DownstreamReRoute[0].LoadBalancerKey.ShouldBe("/auth/|GET");
|
_result.Data.ReRoute.DownstreamReRoute[0].LoadBalancerKey.ShouldBe("/auth/|GET");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ThenThePathDoesNotHaveTrailingSlash()
|
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].ServiceName.ShouldBe("auth");
|
||||||
_result.Data.ReRoute.DownstreamReRoute[0].LoadBalancerKey.ShouldBe("/auth/test|GET");
|
_result.Data.ReRoute.DownstreamReRoute[0].LoadBalancerKey.ShouldBe("/auth/test|GET");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ThenTheQueryStringIsRemoved()
|
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].ServiceName.ShouldBe("auth");
|
||||||
_result.Data.ReRoute.DownstreamReRoute[0].LoadBalancerKey.ShouldBe("/auth/test|GET");
|
_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.ShouldBe(expected);
|
||||||
_result.Data.ReRoute.DownstreamReRoute[0].QosOptions.UseQos.ShouldBeTrue();
|
_result.Data.ReRoute.DownstreamReRoute[0].QosOptions.UseQos.ShouldBeTrue();
|
||||||
_qosOptionsCreator
|
_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)
|
private void GivenTheConfiguration(IInternalConfiguration config)
|
||||||
|
@ -23,7 +23,6 @@
|
|||||||
{
|
{
|
||||||
private readonly Mock<IDownstreamRouteProvider> _finder;
|
private readonly Mock<IDownstreamRouteProvider> _finder;
|
||||||
private readonly Mock<IDownstreamRouteProviderFactory> _factory;
|
private readonly Mock<IDownstreamRouteProviderFactory> _factory;
|
||||||
private readonly Mock<IInternalConfigurationRepository> _repo;
|
|
||||||
private Response<DownstreamRoute> _downstreamRoute;
|
private Response<DownstreamRoute> _downstreamRoute;
|
||||||
private IInternalConfiguration _config;
|
private IInternalConfiguration _config;
|
||||||
private Mock<IOcelotLoggerFactory> _loggerFactory;
|
private Mock<IOcelotLoggerFactory> _loggerFactory;
|
||||||
@ -35,7 +34,6 @@
|
|||||||
|
|
||||||
public DownstreamRouteFinderMiddlewareTests()
|
public DownstreamRouteFinderMiddlewareTests()
|
||||||
{
|
{
|
||||||
_repo = new Mock<IInternalConfigurationRepository>();
|
|
||||||
_finder = new Mock<IDownstreamRouteProvider>();
|
_finder = new Mock<IDownstreamRouteProvider>();
|
||||||
_factory = new Mock<IDownstreamRouteProviderFactory>();
|
_factory = new Mock<IDownstreamRouteProviderFactory>();
|
||||||
_factory.Setup(x => x.Get(It.IsAny<IInternalConfiguration>())).Returns(_finder.Object);
|
_factory.Setup(x => x.Get(It.IsAny<IInternalConfiguration>())).Returns(_finder.Object);
|
||||||
@ -45,7 +43,7 @@
|
|||||||
_loggerFactory.Setup(x => x.CreateLogger<DownstreamRouteFinderMiddleware>()).Returns(_logger.Object);
|
_loggerFactory.Setup(x => x.CreateLogger<DownstreamRouteFinderMiddleware>()).Returns(_logger.Object);
|
||||||
_next = context => Task.CompletedTask;
|
_next = context => Task.CompletedTask;
|
||||||
_multiplexer = new Mock<IMultiplexer>();
|
_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]
|
[Fact]
|
||||||
|
@ -459,7 +459,6 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder
|
|||||||
.WithDownstreamPathTemplate("someDownstreamPath")
|
.WithDownstreamPathTemplate("someDownstreamPath")
|
||||||
.WithUpstreamHttpMethod(new List<string> { "Get" })
|
.WithUpstreamHttpMethod(new List<string> { "Get" })
|
||||||
.WithUpstreamPathTemplate(new UpstreamPathTemplate("someUpstreamPath", 1, false, "someUpstreamPath"))
|
.WithUpstreamPathTemplate(new UpstreamPathTemplate("someUpstreamPath", 1, false, "someUpstreamPath"))
|
||||||
.WithUpstreamHost("MATCH")
|
|
||||||
.Build())
|
.Build())
|
||||||
.WithUpstreamHttpMethod(new List<string> { "Get" })
|
.WithUpstreamHttpMethod(new List<string> { "Get" })
|
||||||
.WithUpstreamPathTemplate(new UpstreamPathTemplate("someUpstreamPath", 1, false, "someUpstreamPath"))
|
.WithUpstreamPathTemplate(new UpstreamPathTemplate("someUpstreamPath", 1, false, "someUpstreamPath"))
|
||||||
@ -545,7 +544,6 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder
|
|||||||
.WithDownstreamPathTemplate("someDownstreamPath")
|
.WithDownstreamPathTemplate("someDownstreamPath")
|
||||||
.WithUpstreamHttpMethod(new List<string> { "Get" })
|
.WithUpstreamHttpMethod(new List<string> { "Get" })
|
||||||
.WithUpstreamPathTemplate(new UpstreamPathTemplate("someUpstreamPath", 1, false, "someUpstreamPath"))
|
.WithUpstreamPathTemplate(new UpstreamPathTemplate("someUpstreamPath", 1, false, "someUpstreamPath"))
|
||||||
.WithUpstreamHost("MATCH")
|
|
||||||
.Build())
|
.Build())
|
||||||
.WithUpstreamHttpMethod(new List<string> { "Get" })
|
.WithUpstreamHttpMethod(new List<string> { "Get" })
|
||||||
.WithUpstreamPathTemplate(new UpstreamPathTemplate("someUpstreamPath", 1, false, "someUpstreamPath"))
|
.WithUpstreamPathTemplate(new UpstreamPathTemplate("someUpstreamPath", 1, false, "someUpstreamPath"))
|
||||||
@ -556,7 +554,6 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder
|
|||||||
.WithDownstreamPathTemplate("someDownstreamPath")
|
.WithDownstreamPathTemplate("someDownstreamPath")
|
||||||
.WithUpstreamHttpMethod(new List<string> { }) // empty list of methods
|
.WithUpstreamHttpMethod(new List<string> { }) // empty list of methods
|
||||||
.WithUpstreamPathTemplate(new UpstreamPathTemplate("someUpstreamPath", 1, false, "someUpstreamPath"))
|
.WithUpstreamPathTemplate(new UpstreamPathTemplate("someUpstreamPath", 1, false, "someUpstreamPath"))
|
||||||
.WithUpstreamHost("MATCH")
|
|
||||||
.Build())
|
.Build())
|
||||||
.WithUpstreamHttpMethod(new List<string> { }) // empty list of methods
|
.WithUpstreamHttpMethod(new List<string> { }) // empty list of methods
|
||||||
.WithUpstreamPathTemplate(new UpstreamPathTemplate("someUpstreamPath", 1, false, "someUpstreamPath"))
|
.WithUpstreamPathTemplate(new UpstreamPathTemplate("someUpstreamPath", 1, false, "someUpstreamPath"))
|
||||||
@ -587,7 +584,6 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder
|
|||||||
.WithDownstreamPathTemplate("someDownstreamPath")
|
.WithDownstreamPathTemplate("someDownstreamPath")
|
||||||
.WithUpstreamHttpMethod(new List<string>())
|
.WithUpstreamHttpMethod(new List<string>())
|
||||||
.WithUpstreamPathTemplate(new UpstreamPathTemplate("someUpstreamPath", 1, false, "someUpstreamPath"))
|
.WithUpstreamPathTemplate(new UpstreamPathTemplate("someUpstreamPath", 1, false, "someUpstreamPath"))
|
||||||
.WithUpstreamHost("MATCH")
|
|
||||||
.Build())
|
.Build())
|
||||||
.WithUpstreamHttpMethod(new List<string>())
|
.WithUpstreamHttpMethod(new List<string>())
|
||||||
.WithUpstreamPathTemplate(new UpstreamPathTemplate("someUpstreamPath", 1, false, "someUpstreamPath"))
|
.WithUpstreamPathTemplate(new UpstreamPathTemplate("someUpstreamPath", 1, false, "someUpstreamPath"))
|
||||||
@ -618,7 +614,6 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder
|
|||||||
.WithDownstreamPathTemplate("someDownstreamPath")
|
.WithDownstreamPathTemplate("someDownstreamPath")
|
||||||
.WithUpstreamHttpMethod(new List<string>())
|
.WithUpstreamHttpMethod(new List<string>())
|
||||||
.WithUpstreamPathTemplate(new UpstreamPathTemplate("someUpstreamPath", 1, false, "someUpstreamPath"))
|
.WithUpstreamPathTemplate(new UpstreamPathTemplate("someUpstreamPath", 1, false, "someUpstreamPath"))
|
||||||
.WithUpstreamHost("MATCH")
|
|
||||||
.Build())
|
.Build())
|
||||||
.WithUpstreamHttpMethod(new List<string>())
|
.WithUpstreamHttpMethod(new List<string>())
|
||||||
.WithUpstreamPathTemplate(new UpstreamPathTemplate("someUpstreamPath", 1, false, "someUpstreamPath"))
|
.WithUpstreamPathTemplate(new UpstreamPathTemplate("someUpstreamPath", 1, false, "someUpstreamPath"))
|
||||||
@ -659,7 +654,6 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder
|
|||||||
.WithDownstreamPathTemplate("someDownstreamPath")
|
.WithDownstreamPathTemplate("someDownstreamPath")
|
||||||
.WithUpstreamHttpMethod(new List<string> { "Get" })
|
.WithUpstreamHttpMethod(new List<string> { "Get" })
|
||||||
.WithUpstreamPathTemplate(new UpstreamPathTemplate("someUpstreamPath", 1, false, "someUpstreamPath"))
|
.WithUpstreamPathTemplate(new UpstreamPathTemplate("someUpstreamPath", 1, false, "someUpstreamPath"))
|
||||||
.WithUpstreamHost("MATCH")
|
|
||||||
.Build())
|
.Build())
|
||||||
.WithUpstreamHttpMethod(new List<string> { "Get" })
|
.WithUpstreamHttpMethod(new List<string> { "Get" })
|
||||||
.WithUpstreamPathTemplate(new UpstreamPathTemplate("someUpstreamPath", 1, false, "someUpstreamPath"))
|
.WithUpstreamPathTemplate(new UpstreamPathTemplate("someUpstreamPath", 1, false, "someUpstreamPath"))
|
||||||
@ -759,7 +753,7 @@ namespace Ocelot.UnitTests.DownstreamRouteFinder
|
|||||||
|
|
||||||
private void ThenTheFollowingIsReturned(DownstreamRoute expected)
|
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);
|
_result.Data.ReRoute.UpstreamTemplatePattern.Priority.ShouldBe(expected.ReRoute.UpstreamTemplatePattern.Priority);
|
||||||
|
|
||||||
for (int i = 0; i < _result.Data.TemplatePlaceholderNameAndValues.Count; i++)
|
for (int i = 0; i < _result.Data.TemplatePlaceholderNameAndValues.Count; i++)
|
||||||
|
@ -199,7 +199,7 @@ namespace Ocelot.UnitTests.DownstreamUrlCreator.UrlTemplateReplacer
|
|||||||
|
|
||||||
private void WhenIReplaceTheTemplateVariables()
|
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)
|
private void ThenTheDownstreamUrlPathIsReturned(string expected)
|
||||||
|
@ -15,6 +15,9 @@ using Ocelot.Request.Middleware;
|
|||||||
|
|
||||||
namespace Ocelot.UnitTests.Headers
|
namespace Ocelot.UnitTests.Headers
|
||||||
{
|
{
|
||||||
|
using Ocelot.Infrastructure;
|
||||||
|
using Ocelot.Logging;
|
||||||
|
|
||||||
public class AddHeadersToRequestClaimToThingTests
|
public class AddHeadersToRequestClaimToThingTests
|
||||||
{
|
{
|
||||||
private readonly AddHeadersToRequest _addHeadersToRequest;
|
private readonly AddHeadersToRequest _addHeadersToRequest;
|
||||||
@ -24,11 +27,15 @@ namespace Ocelot.UnitTests.Headers
|
|||||||
private List<ClaimToThing> _configuration;
|
private List<ClaimToThing> _configuration;
|
||||||
private Response _result;
|
private Response _result;
|
||||||
private Response<string> _claimValue;
|
private Response<string> _claimValue;
|
||||||
|
private Mock<IPlaceholders> _placeholders;
|
||||||
|
private Mock<IOcelotLoggerFactory> _factory;
|
||||||
|
|
||||||
public AddHeadersToRequestClaimToThingTests()
|
public AddHeadersToRequestClaimToThingTests()
|
||||||
{
|
{
|
||||||
_parser = new Mock<IClaimsParser>();
|
_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"));
|
_downstreamRequest = new DownstreamRequest(new HttpRequestMessage(HttpMethod.Get, "http://test.com"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,23 +1,56 @@
|
|||||||
using Microsoft.AspNetCore.Http;
|
namespace Ocelot.UnitTests.Headers
|
||||||
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
|
|
||||||
{
|
{
|
||||||
|
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
|
public class AddHeadersToRequestPlainTests
|
||||||
{
|
{
|
||||||
private readonly AddHeadersToRequest _addHeadersToRequest;
|
private readonly AddHeadersToRequest _addHeadersToRequest;
|
||||||
private HttpContext _context;
|
private HttpContext _context;
|
||||||
private AddHeader _addedHeader;
|
private AddHeader _addedHeader;
|
||||||
|
private readonly Mock<IPlaceholders> _placeholders;
|
||||||
|
private Mock<IOcelotLoggerFactory> _factory;
|
||||||
|
private readonly Mock<IOcelotLogger> _logger;
|
||||||
|
|
||||||
public AddHeadersToRequestPlainTests()
|
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]
|
[Fact]
|
||||||
@ -38,9 +71,23 @@ namespace Ocelot.UnitTests.Headers
|
|||||||
.BDDfy();
|
.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()
|
private void GivenHttpRequestWithoutHeaders()
|
||||||
{
|
{
|
||||||
_context = new DefaultHttpContext();
|
_context = new DefaultHttpContext
|
||||||
|
{
|
||||||
|
Request =
|
||||||
|
{
|
||||||
|
Headers =
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private void GivenHttpRequestWithHeader(string headerKey, string headerValue)
|
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.ShouldNotBeNull($"Value of header {_addedHeader.Key} was expected to not be null.");
|
||||||
value.ToString().ShouldBe(_addedHeader.Value);
|
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 TestStack.BDDfy;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
public class HttpRequestHeadersBuilderMiddlewareTests
|
public class ClaimsToHeadersMiddlewareTests
|
||||||
{
|
{
|
||||||
private readonly Mock<IAddHeadersToRequest> _addHeaders;
|
private readonly Mock<IAddHeadersToRequest> _addHeaders;
|
||||||
private Response<DownstreamRoute> _downstreamRoute;
|
private Response<DownstreamRoute> _downstreamRoute;
|
||||||
private Mock<IOcelotLoggerFactory> _loggerFactory;
|
private Mock<IOcelotLoggerFactory> _loggerFactory;
|
||||||
private Mock<IOcelotLogger> _logger;
|
private Mock<IOcelotLogger> _logger;
|
||||||
private HttpRequestHeadersBuilderMiddleware _middleware;
|
private ClaimsToHeadersMiddleware _middleware;
|
||||||
private DownstreamContext _downstreamContext;
|
private DownstreamContext _downstreamContext;
|
||||||
private OcelotRequestDelegate _next;
|
private OcelotRequestDelegate _next;
|
||||||
|
|
||||||
public HttpRequestHeadersBuilderMiddlewareTests()
|
public ClaimsToHeadersMiddlewareTests()
|
||||||
{
|
{
|
||||||
_addHeaders = new Mock<IAddHeadersToRequest>();
|
_addHeaders = new Mock<IAddHeadersToRequest>();
|
||||||
_downstreamContext = new DownstreamContext(new DefaultHttpContext());
|
_downstreamContext = new DownstreamContext(new DefaultHttpContext());
|
||||||
_loggerFactory = new Mock<IOcelotLoggerFactory>();
|
_loggerFactory = new Mock<IOcelotLoggerFactory>();
|
||||||
_logger = new Mock<IOcelotLogger>();
|
_logger = new Mock<IOcelotLogger>();
|
||||||
_loggerFactory.Setup(x => x.CreateLogger<HttpRequestHeadersBuilderMiddleware>()).Returns(_logger.Object);
|
_loggerFactory.Setup(x => x.CreateLogger<ClaimsToHeadersMiddleware>()).Returns(_logger.Object);
|
||||||
_next = context => Task.CompletedTask;
|
_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"));
|
_downstreamContext.DownstreamRequest = new DownstreamRequest(new HttpRequestMessage(HttpMethod.Get, "http://test.com"));
|
||||||
}
|
}
|
||||||
|
|
@ -109,7 +109,7 @@ namespace Ocelot.UnitTests.Headers
|
|||||||
|
|
||||||
private void ThenTheIHttpResponseHeaderReplacerIsCalledCorrectly()
|
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()
|
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
|
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
|
public class HttpResponseHeaderReplacerTests
|
||||||
{
|
{
|
||||||
private DownstreamResponse _response;
|
private DownstreamResponse _response;
|
||||||
@ -27,12 +27,14 @@ namespace Ocelot.UnitTests.Headers
|
|||||||
private DownstreamRequest _request;
|
private DownstreamRequest _request;
|
||||||
private Mock<IBaseUrlFinder> _finder;
|
private Mock<IBaseUrlFinder> _finder;
|
||||||
private Mock<IRequestScopedDataRepository> _repo;
|
private Mock<IRequestScopedDataRepository> _repo;
|
||||||
|
private Mock<IHttpContextAccessor> _accessor;
|
||||||
|
|
||||||
public HttpResponseHeaderReplacerTests()
|
public HttpResponseHeaderReplacerTests()
|
||||||
{
|
{
|
||||||
|
_accessor = new Mock<IHttpContextAccessor>();
|
||||||
_repo = new Mock<IRequestScopedDataRepository>();
|
_repo = new Mock<IRequestScopedDataRepository>();
|
||||||
_finder = new Mock<IBaseUrlFinder>();
|
_finder = new Mock<IBaseUrlFinder>();
|
||||||
_placeholders = new Placeholders(_finder.Object, _repo.Object);
|
_placeholders = new Placeholders(_finder.Object, _repo.Object, _accessor.Object);
|
||||||
_replacer = new HttpResponseHeaderReplacer(_placeholders);
|
_replacer = new HttpResponseHeaderReplacer(_placeholders);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -43,7 +45,7 @@ namespace Ocelot.UnitTests.Headers
|
|||||||
new List<KeyValuePair<string, IEnumerable<string>>>()
|
new List<KeyValuePair<string, IEnumerable<string>>>()
|
||||||
{
|
{
|
||||||
new KeyValuePair<string, IEnumerable<string>>("test", new List<string> {"test"})
|
new KeyValuePair<string, IEnumerable<string>>("test", new List<string> {"test"})
|
||||||
});
|
}, "");
|
||||||
|
|
||||||
var fAndRs = new List<HeaderFindAndReplace> {new HeaderFindAndReplace("test", "test", "chiken", 0)};
|
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 List<KeyValuePair<string, IEnumerable<string>>>()
|
||||||
{
|
{
|
||||||
new KeyValuePair<string, IEnumerable<string>>("test", new List<string> {"test"})
|
new KeyValuePair<string, IEnumerable<string>>("test", new List<string> {"test"})
|
||||||
});
|
}, "");
|
||||||
|
|
||||||
var fAndRs = new List<HeaderFindAndReplace>();
|
var fAndRs = new List<HeaderFindAndReplace>();
|
||||||
|
|
||||||
@ -84,7 +86,7 @@ namespace Ocelot.UnitTests.Headers
|
|||||||
new List<KeyValuePair<string, IEnumerable<string>>>()
|
new List<KeyValuePair<string, IEnumerable<string>>>()
|
||||||
{
|
{
|
||||||
new KeyValuePair<string, IEnumerable<string>>("Location", new List<string> {downstreamUrl})
|
new KeyValuePair<string, IEnumerable<string>>("Location", new List<string> {downstreamUrl})
|
||||||
});
|
}, "");
|
||||||
|
|
||||||
var fAndRs = new List<HeaderFindAndReplace>
|
var fAndRs = new List<HeaderFindAndReplace>
|
||||||
{
|
{
|
||||||
@ -111,7 +113,7 @@ namespace Ocelot.UnitTests.Headers
|
|||||||
new List<KeyValuePair<string, IEnumerable<string>>>()
|
new List<KeyValuePair<string, IEnumerable<string>>>()
|
||||||
{
|
{
|
||||||
new KeyValuePair<string, IEnumerable<string>>("Location", new List<string> {downstreamUrl})
|
new KeyValuePair<string, IEnumerable<string>>("Location", new List<string> {downstreamUrl})
|
||||||
});
|
}, "");
|
||||||
|
|
||||||
var fAndRs = new List<HeaderFindAndReplace>
|
var fAndRs = new List<HeaderFindAndReplace>
|
||||||
{
|
{
|
||||||
@ -138,7 +140,7 @@ namespace Ocelot.UnitTests.Headers
|
|||||||
new List<KeyValuePair<string, IEnumerable<string>>>()
|
new List<KeyValuePair<string, IEnumerable<string>>>()
|
||||||
{
|
{
|
||||||
new KeyValuePair<string, IEnumerable<string>>("Location", new List<string> {downstreamUrl})
|
new KeyValuePair<string, IEnumerable<string>>("Location", new List<string> {downstreamUrl})
|
||||||
});
|
}, "");
|
||||||
|
|
||||||
var fAndRs = new List<HeaderFindAndReplace>
|
var fAndRs = new List<HeaderFindAndReplace>
|
||||||
{
|
{
|
||||||
@ -165,7 +167,7 @@ namespace Ocelot.UnitTests.Headers
|
|||||||
new List<KeyValuePair<string, IEnumerable<string>>>()
|
new List<KeyValuePair<string, IEnumerable<string>>>()
|
||||||
{
|
{
|
||||||
new KeyValuePair<string, IEnumerable<string>>("Location", new List<string> {downstreamUrl})
|
new KeyValuePair<string, IEnumerable<string>>("Location", new List<string> {downstreamUrl})
|
||||||
});
|
}, "");
|
||||||
|
|
||||||
var fAndRs = new List<HeaderFindAndReplace>
|
var fAndRs = new List<HeaderFindAndReplace>
|
||||||
{
|
{
|
||||||
@ -192,7 +194,7 @@ namespace Ocelot.UnitTests.Headers
|
|||||||
new List<KeyValuePair<string, IEnumerable<string>>>()
|
new List<KeyValuePair<string, IEnumerable<string>>>()
|
||||||
{
|
{
|
||||||
new KeyValuePair<string, IEnumerable<string>>("Location", new List<string> {downstreamUrl})
|
new KeyValuePair<string, IEnumerable<string>>("Location", new List<string> {downstreamUrl})
|
||||||
});
|
}, "");
|
||||||
|
|
||||||
var fAndRs = new List<HeaderFindAndReplace>
|
var fAndRs = new List<HeaderFindAndReplace>
|
||||||
{
|
{
|
||||||
@ -219,7 +221,7 @@ namespace Ocelot.UnitTests.Headers
|
|||||||
new List<KeyValuePair<string, IEnumerable<string>>>()
|
new List<KeyValuePair<string, IEnumerable<string>>>()
|
||||||
{
|
{
|
||||||
new KeyValuePair<string, IEnumerable<string>>("Location", new List<string> {downstreamUrl})
|
new KeyValuePair<string, IEnumerable<string>>("Location", new List<string> {downstreamUrl})
|
||||||
});
|
}, "");
|
||||||
|
|
||||||
var fAndRs = new List<HeaderFindAndReplace>
|
var fAndRs = new List<HeaderFindAndReplace>
|
||||||
{
|
{
|
||||||
@ -261,7 +263,8 @@ namespace Ocelot.UnitTests.Headers
|
|||||||
|
|
||||||
private void WhenICallTheReplacer()
|
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)
|
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
|
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
|
public class PlaceholdersTests
|
||||||
{
|
{
|
||||||
private IPlaceholders _placeholders;
|
private readonly IPlaceholders _placeholders;
|
||||||
private Mock<IBaseUrlFinder> _finder;
|
private readonly Mock<IBaseUrlFinder> _finder;
|
||||||
private Mock<IRequestScopedDataRepository> _repo;
|
private readonly Mock<IRequestScopedDataRepository> _repo;
|
||||||
|
private readonly Mock<IHttpContextAccessor> _accessor;
|
||||||
|
|
||||||
public PlaceholdersTests()
|
public PlaceholdersTests()
|
||||||
{
|
{
|
||||||
|
_accessor = new Mock<IHttpContextAccessor>();
|
||||||
_repo = new Mock<IRequestScopedDataRepository>();
|
_repo = new Mock<IRequestScopedDataRepository>();
|
||||||
_finder = new Mock<IBaseUrlFinder>();
|
_finder = new Mock<IBaseUrlFinder>();
|
||||||
_placeholders = new Placeholders(_finder.Object, _repo.Object);
|
_placeholders = new Placeholders(_finder.Object, _repo.Object, _accessor.Object);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
@ -33,6 +37,15 @@ namespace Ocelot.UnitTests.Infrastructure
|
|||||||
result.Data.ShouldBe(baseUrl);
|
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]
|
[Fact]
|
||||||
public void should_return_key_does_not_exist()
|
public void should_return_key_does_not_exist()
|
||||||
{
|
{
|
||||||
|
@ -49,13 +49,13 @@ namespace Ocelot.UnitTests.Middleware
|
|||||||
|
|
||||||
var billDownstreamContext = new DownstreamContext(new DefaultHttpContext())
|
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
|
DownstreamReRoute = billDownstreamReRoute
|
||||||
};
|
};
|
||||||
|
|
||||||
var georgeDownstreamContext = new DownstreamContext(new DefaultHttpContext())
|
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
|
DownstreamReRoute = georgeDownstreamReRoute
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -69,6 +69,7 @@ namespace Ocelot.UnitTests.Middleware
|
|||||||
.When(x => WhenIAggregate())
|
.When(x => WhenIAggregate())
|
||||||
.Then(x => ThenTheContentIs(expected))
|
.Then(x => ThenTheContentIs(expected))
|
||||||
.And(x => ThenTheContentTypeIs("application/json"))
|
.And(x => ThenTheContentTypeIs("application/json"))
|
||||||
|
.And(x => ThenTheReasonPhraseIs("cannot return from aggregate..which reason phrase would you use?"))
|
||||||
.BDDfy();
|
.BDDfy();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -91,13 +92,13 @@ namespace Ocelot.UnitTests.Middleware
|
|||||||
|
|
||||||
var billDownstreamContext = new DownstreamContext(new DefaultHttpContext())
|
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
|
DownstreamReRoute = billDownstreamReRoute
|
||||||
};
|
};
|
||||||
|
|
||||||
var georgeDownstreamContext = new DownstreamContext(new DefaultHttpContext())
|
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,
|
DownstreamReRoute = georgeDownstreamReRoute,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -116,6 +117,11 @@ namespace Ocelot.UnitTests.Middleware
|
|||||||
.BDDfy();
|
.BDDfy();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void ThenTheReasonPhraseIs(string expected)
|
||||||
|
{
|
||||||
|
_upstreamContext.DownstreamResponse.ReasonPhrase.ShouldBe(expected);
|
||||||
|
}
|
||||||
|
|
||||||
private void ThenTheErrorIsMapped()
|
private void ThenTheErrorIsMapped()
|
||||||
{
|
{
|
||||||
_upstreamContext.Errors.ShouldBe(_downstreamContexts[1].Errors);
|
_upstreamContext.Errors.ShouldBe(_downstreamContexts[1].Errors);
|
||||||
|
@ -43,11 +43,11 @@ namespace Ocelot.UnitTests.Middleware
|
|||||||
{
|
{
|
||||||
new DownstreamContext(new DefaultHttpContext())
|
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())
|
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())
|
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())
|
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 laura = await responses[1].Content.ReadAsStringAsync();
|
||||||
var content = $"{tom}, {laura}";
|
var content = $"{tom}, {laura}";
|
||||||
var headers = responses.SelectMany(x => x.Headers).ToList();
|
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