mirror of
https://github.com/nsnail/Ocelot.git
synced 2025-06-19 19:08:16 +08:00
* #568 Ocelot wont start if QoSOptions specified and no QoS DelegatingHandler registered e.g. no use of Ocelot.Provider.Polly. Also adds a NoQosDelegatingHandler and logs an error if ive missed something and the user can get to making the request * #568 sadly something wierd with IServiceProvider and FluentValidation so I'm just defaulting to warning and noqosdelegatinghandler if someone doesnt register but provides options, also added basic in memory cache in case people dont use a specific package
This commit is contained in:
16
src/Ocelot/Cache/CacheObject.cs
Normal file
16
src/Ocelot/Cache/CacheObject.cs
Normal file
@ -0,0 +1,16 @@
|
||||
namespace Ocelot.Cache
|
||||
{
|
||||
using System;
|
||||
|
||||
class CacheObject<T>
|
||||
{
|
||||
public CacheObject(T value, DateTime expires)
|
||||
{
|
||||
Value = value;
|
||||
Expires = expires;
|
||||
}
|
||||
|
||||
public T Value { get; }
|
||||
public DateTime Expires { get; }
|
||||
}
|
||||
}
|
@ -5,28 +5,7 @@ namespace Ocelot.Cache
|
||||
public interface IOcelotCache<T>
|
||||
{
|
||||
void Add(string key, T value, TimeSpan ttl, string region);
|
||||
void AddAndDelete(string key, T value, TimeSpan ttl, string region);
|
||||
T Get(string key, string region);
|
||||
void ClearRegion(string region);
|
||||
}
|
||||
|
||||
public class NoCache<T> : IOcelotCache<T>
|
||||
{
|
||||
public void Add(string key, T value, TimeSpan ttl, string region)
|
||||
{
|
||||
}
|
||||
|
||||
public void AddAndDelete(string key, T value, TimeSpan ttl, string region)
|
||||
{
|
||||
}
|
||||
|
||||
public void ClearRegion(string region)
|
||||
{
|
||||
}
|
||||
|
||||
public T Get(string key, string region)
|
||||
{
|
||||
return default(T);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
71
src/Ocelot/Cache/InMemoryCache.cs
Normal file
71
src/Ocelot/Cache/InMemoryCache.cs
Normal file
@ -0,0 +1,71 @@
|
||||
namespace Ocelot.Cache
|
||||
{
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
public class InMemoryCache<T> : IOcelotCache<T>
|
||||
{
|
||||
private readonly Dictionary<string, CacheObject<T>> _cache;
|
||||
private readonly Dictionary<string, List<string>> _regions;
|
||||
|
||||
public InMemoryCache()
|
||||
{
|
||||
_cache = new Dictionary<string, CacheObject<T>>();
|
||||
_regions = new Dictionary<string, List<string>>();
|
||||
}
|
||||
|
||||
public void Add(string key, T value, TimeSpan ttl, string region)
|
||||
{
|
||||
if (ttl.TotalMilliseconds <= 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var expires = DateTime.UtcNow.Add(ttl);
|
||||
|
||||
_cache.Add(key, new CacheObject<T>(value, expires));
|
||||
|
||||
if (_regions.ContainsKey(region))
|
||||
{
|
||||
var current = _regions[region];
|
||||
if (!current.Contains(key))
|
||||
{
|
||||
current.Add(key);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_regions.Add(region, new List<string>{ key });
|
||||
}
|
||||
}
|
||||
|
||||
public void ClearRegion(string region)
|
||||
{
|
||||
if (_regions.ContainsKey(region))
|
||||
{
|
||||
var keys = _regions[region];
|
||||
foreach (var key in keys)
|
||||
{
|
||||
_cache.Remove(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public T Get(string key, string region)
|
||||
{
|
||||
if (_cache.ContainsKey(key))
|
||||
{
|
||||
var cached = _cache[key];
|
||||
|
||||
if (cached.Expires > DateTime.UtcNow)
|
||||
{
|
||||
return cached.Value;
|
||||
}
|
||||
|
||||
_cache.Remove(key);
|
||||
}
|
||||
|
||||
return default(T);
|
||||
}
|
||||
}
|
||||
}
|
@ -17,8 +17,8 @@ namespace Ocelot.Configuration.Creator
|
||||
|
||||
public QoSOptions Create(FileQoSOptions options, string pathTemplate, string[] httpMethods)
|
||||
{
|
||||
var key = CreateKey(pathTemplate, httpMethods);
|
||||
|
||||
var key = CreateKey(pathTemplate, httpMethods);
|
||||
|
||||
return Map(key, options.TimeoutValue, options.DurationOfBreak, options.ExceptionsAllowedBeforeBreaking);
|
||||
}
|
||||
|
||||
|
@ -23,7 +23,7 @@
|
||||
|
||||
public FileLoadBalancerOptions LoadBalancerOptions { get; set; }
|
||||
|
||||
public string DownstreamScheme { get; set; }
|
||||
public string DownstreamScheme { get; set; }
|
||||
|
||||
public FileHttpHandlerOptions HttpHandlerOptions { get; set; }
|
||||
}
|
||||
|
@ -9,13 +9,20 @@ using System.Threading.Tasks;
|
||||
|
||||
namespace Ocelot.Configuration.Validator
|
||||
{
|
||||
using System;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Requester;
|
||||
|
||||
public class FileConfigurationFluentValidator : AbstractValidator<FileConfiguration>, IConfigurationValidator
|
||||
{
|
||||
public FileConfigurationFluentValidator(IAuthenticationSchemeProvider authenticationSchemeProvider)
|
||||
private readonly IServiceProvider _provider;
|
||||
|
||||
public FileConfigurationFluentValidator(IAuthenticationSchemeProvider authenticationSchemeProvider, IServiceProvider provider)
|
||||
{
|
||||
_provider = provider;
|
||||
RuleFor(configuration => configuration.ReRoutes)
|
||||
.SetCollectionValidator(new ReRouteFluentValidator(authenticationSchemeProvider));
|
||||
|
||||
.SetCollectionValidator(new ReRouteFluentValidator(authenticationSchemeProvider, provider));
|
||||
|
||||
RuleForEach(configuration => configuration.ReRoutes)
|
||||
.Must((config, reRoute) => IsNotDuplicateIn(reRoute, config.ReRoutes))
|
||||
.WithMessage((config, reRoute) => $"{nameof(reRoute)} {reRoute.UpstreamPathTemplate} has duplicate");
|
||||
|
@ -1,19 +1,24 @@
|
||||
using FluentValidation;
|
||||
using Microsoft.AspNetCore.Authentication;
|
||||
using Ocelot.Configuration.File;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Ocelot.Configuration.Validator
|
||||
namespace Ocelot.Configuration.Validator
|
||||
{
|
||||
using FluentValidation;
|
||||
using Microsoft.AspNetCore.Authentication;
|
||||
using Ocelot.Configuration.File;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Requester;
|
||||
|
||||
public class ReRouteFluentValidator : AbstractValidator<FileReRoute>
|
||||
{
|
||||
private readonly IAuthenticationSchemeProvider _authenticationSchemeProvider;
|
||||
private readonly IServiceProvider _serviceProvider;
|
||||
|
||||
public ReRouteFluentValidator(IAuthenticationSchemeProvider authenticationSchemeProvider)
|
||||
public ReRouteFluentValidator(IAuthenticationSchemeProvider authenticationSchemeProvider, IServiceProvider serviceProvider)
|
||||
{
|
||||
_authenticationSchemeProvider = authenticationSchemeProvider;
|
||||
_serviceProvider = serviceProvider;
|
||||
|
||||
RuleFor(reRoute => reRoute.DownstreamPathTemplate)
|
||||
.Must(path => path.StartsWith("/"))
|
||||
@ -48,11 +53,13 @@ namespace Ocelot.Configuration.Validator
|
||||
.WithMessage("{PropertyValue} is unsupported authentication provider");
|
||||
|
||||
When(reRoute => reRoute.UseServiceDiscovery, () => {
|
||||
RuleFor(r => r.ServiceName).NotEmpty().WithMessage("ServiceName cannot be empty or null when using service discovery or Ocelot cannot look up your service!");
|
||||
RuleFor(r => r.ServiceName).NotEmpty()
|
||||
.WithMessage("ServiceName cannot be empty or null when using service discovery or Ocelot cannot look up your service!");
|
||||
});
|
||||
|
||||
When(reRoute => !reRoute.UseServiceDiscovery, () => {
|
||||
RuleFor(r => r.DownstreamHostAndPorts).NotEmpty().WithMessage("When not using service discovery DownstreamHostAndPorts must be set and not empty or Ocelot cannot find your service!");
|
||||
RuleFor(r => r.DownstreamHostAndPorts).NotEmpty()
|
||||
.WithMessage("When not using service discovery DownstreamHostAndPorts must be set and not empty or Ocelot cannot find your service!");
|
||||
});
|
||||
|
||||
When(reRoute => !reRoute.UseServiceDiscovery, () => {
|
||||
|
@ -45,13 +45,10 @@ namespace Ocelot.DependencyInjection
|
||||
{
|
||||
Configuration = configurationRoot;
|
||||
Services = services;
|
||||
|
||||
Services.Configure<FileConfiguration>(configurationRoot);
|
||||
|
||||
//default no caches...
|
||||
Services.TryAddSingleton<IOcelotCache<FileConfiguration>, NoCache<FileConfiguration>>();
|
||||
Services.TryAddSingleton<IOcelotCache<CachedResponse>, NoCache<CachedResponse>>();
|
||||
|
||||
Services.TryAddSingleton<IOcelotCache<FileConfiguration>, InMemoryCache<FileConfiguration>>();
|
||||
Services.TryAddSingleton<IOcelotCache<CachedResponse>, InMemoryCache<CachedResponse>>();
|
||||
Services.TryAddSingleton<IHttpResponseHeaderReplacer, HttpResponseHeaderReplacer>();
|
||||
Services.TryAddSingleton<IHttpContextRequestHeaderReplacer, HttpContextRequestHeaderReplacer>();
|
||||
Services.TryAddSingleton<IHeaderFindAndReplaceCreator, HeaderFindAndReplaceCreator>();
|
||||
@ -105,6 +102,18 @@ namespace Ocelot.DependencyInjection
|
||||
Services.TryAddSingleton<IRequestScopedDataRepository, HttpDataRepository>();
|
||||
Services.AddMemoryCache();
|
||||
Services.TryAddSingleton<OcelotDiagnosticListener>();
|
||||
Services.TryAddSingleton<IMultiplexer, Multiplexer>();
|
||||
Services.TryAddSingleton<IResponseAggregator, SimpleJsonResponseAggregator>();
|
||||
Services.AddSingleton<ITracingHandlerFactory, TracingHandlerFactory>();
|
||||
Services.TryAddSingleton<IFileConfigurationPollerOptions, InMemoryFileConfigurationPollerOptions>();
|
||||
Services.TryAddSingleton<IAddHeadersToResponse, AddHeadersToResponse>();
|
||||
Services.TryAddSingleton<IPlaceholders, Placeholders>();
|
||||
Services.TryAddSingleton<IResponseAggregatorFactory, InMemoryResponseAggregatorFactory>();
|
||||
Services.TryAddSingleton<IDefinedAggregatorProvider, ServiceLocatorDefinedAggregatorProvider>();
|
||||
Services.TryAddSingleton<IDownstreamRequestCreator, DownstreamRequestCreator>();
|
||||
Services.TryAddSingleton<IFrameworkDescription, FrameworkDescription>();
|
||||
Services.TryAddSingleton<IQoSFactory, QoSFactory>();
|
||||
Services.TryAddSingleton<IExceptionToErrorMapper, HttpExeptionToErrorMapper>();
|
||||
|
||||
//add asp.net services..
|
||||
var assembly = typeof(FileConfigurationController).GetTypeInfo().Assembly;
|
||||
@ -118,19 +127,6 @@ namespace Ocelot.DependencyInjection
|
||||
Services.AddLogging();
|
||||
Services.AddMiddlewareAnalysis();
|
||||
Services.AddWebEncoders();
|
||||
|
||||
Services.TryAddSingleton<IMultiplexer, Multiplexer>();
|
||||
Services.TryAddSingleton<IResponseAggregator, SimpleJsonResponseAggregator>();
|
||||
Services.AddSingleton<ITracingHandlerFactory, TracingHandlerFactory>();
|
||||
Services.TryAddSingleton<IFileConfigurationPollerOptions, InMemoryFileConfigurationPollerOptions>();
|
||||
Services.TryAddSingleton<IAddHeadersToResponse, AddHeadersToResponse>();
|
||||
Services.TryAddSingleton<IPlaceholders, Placeholders>();
|
||||
Services.TryAddSingleton<IResponseAggregatorFactory, InMemoryResponseAggregatorFactory>();
|
||||
Services.TryAddSingleton<IDefinedAggregatorProvider, ServiceLocatorDefinedAggregatorProvider>();
|
||||
Services.TryAddSingleton<IDownstreamRequestCreator, DownstreamRequestCreator>();
|
||||
Services.TryAddSingleton<IFrameworkDescription, FrameworkDescription>();
|
||||
Services.TryAddSingleton<IQoSFactory, QoSFactory>();
|
||||
Services.TryAddSingleton<IExceptionToErrorMapper, HttpExeptionToErrorMapper>();
|
||||
}
|
||||
|
||||
public IOcelotBuilder AddSingletonDefinedAggregator<T>()
|
||||
|
@ -4,6 +4,7 @@ namespace Ocelot.Requester
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using Logging;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Ocelot.Configuration;
|
||||
using Ocelot.Responses;
|
||||
@ -14,18 +15,21 @@ namespace Ocelot.Requester
|
||||
private readonly ITracingHandlerFactory _tracingFactory;
|
||||
private readonly IQoSFactory _qoSFactory;
|
||||
private readonly IServiceProvider _serviceProvider;
|
||||
private readonly IOcelotLogger _logger;
|
||||
|
||||
public DelegatingHandlerHandlerFactory(
|
||||
ITracingHandlerFactory tracingFactory,
|
||||
IQoSFactory qoSFactory,
|
||||
IServiceProvider serviceProvider)
|
||||
IServiceProvider serviceProvider,
|
||||
IOcelotLoggerFactory loggerFactory)
|
||||
{
|
||||
_logger = loggerFactory.CreateLogger<DelegatingHandlerHandlerFactory>();
|
||||
_serviceProvider = serviceProvider;
|
||||
_tracingFactory = tracingFactory;
|
||||
_qoSFactory = qoSFactory;
|
||||
}
|
||||
|
||||
public Response<List<Func<DelegatingHandler>>> Get(DownstreamReRoute request)
|
||||
public Response<List<Func<DelegatingHandler>>> Get(DownstreamReRoute downstreamReRoute)
|
||||
{
|
||||
var globalDelegatingHandlers = _serviceProvider
|
||||
.GetServices<GlobalDelegatingHandler>()
|
||||
@ -39,7 +43,7 @@ namespace Ocelot.Requester
|
||||
|
||||
foreach (var handler in globalDelegatingHandlers)
|
||||
{
|
||||
if (GlobalIsInHandlersConfig(request, handler))
|
||||
if (GlobalIsInHandlersConfig(downstreamReRoute, handler))
|
||||
{
|
||||
reRouteSpecificHandlers.Add(handler.DelegatingHandler);
|
||||
}
|
||||
@ -49,9 +53,9 @@ namespace Ocelot.Requester
|
||||
}
|
||||
}
|
||||
|
||||
if (request.DelegatingHandlers.Any())
|
||||
if (downstreamReRoute.DelegatingHandlers.Any())
|
||||
{
|
||||
var sorted = SortByConfigOrder(request, reRouteSpecificHandlers);
|
||||
var sorted = SortByConfigOrder(downstreamReRoute, reRouteSpecificHandlers);
|
||||
|
||||
foreach (var handler in sorted)
|
||||
{
|
||||
@ -59,14 +63,14 @@ namespace Ocelot.Requester
|
||||
}
|
||||
}
|
||||
|
||||
if (request.HttpHandlerOptions.UseTracing)
|
||||
if (downstreamReRoute.HttpHandlerOptions.UseTracing)
|
||||
{
|
||||
handlers.Add(() => (DelegatingHandler)_tracingFactory.Get());
|
||||
}
|
||||
|
||||
if (request.QosOptions.UseQos)
|
||||
if (downstreamReRoute.QosOptions.UseQos)
|
||||
{
|
||||
var handler = _qoSFactory.Get(request);
|
||||
var handler = _qoSFactory.Get(downstreamReRoute);
|
||||
|
||||
if (handler != null && !handler.IsError)
|
||||
{
|
||||
@ -74,7 +78,8 @@ namespace Ocelot.Requester
|
||||
}
|
||||
else
|
||||
{
|
||||
return new ErrorResponse<List<Func<DelegatingHandler>>>(handler?.Errors);
|
||||
_logger.LogWarning($"ReRoute {downstreamReRoute.UpstreamPathTemplate} specifies use QoS but no QosHandler found in DI container. Will use not use a QosHandler, please check your setup!");
|
||||
handlers.Add(() => new NoQosDelegatingHandler());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,13 +1,13 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Net.Http;
|
||||
using Ocelot.Configuration;
|
||||
using Ocelot.Responses;
|
||||
|
||||
namespace Ocelot.Requester
|
||||
{
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Net.Http;
|
||||
using Ocelot.Configuration;
|
||||
using Ocelot.Responses;
|
||||
|
||||
public interface IDelegatingHandlerHandlerFactory
|
||||
{
|
||||
Response<List<Func<DelegatingHandler>>> Get(DownstreamReRoute request);
|
||||
Response<List<Func<DelegatingHandler>>> Get(DownstreamReRoute downstreamReRoute);
|
||||
}
|
||||
}
|
||||
|
8
src/Ocelot/Requester/NoQosDelegatingHandler.cs
Normal file
8
src/Ocelot/Requester/NoQosDelegatingHandler.cs
Normal file
@ -0,0 +1,8 @@
|
||||
namespace Ocelot.Requester
|
||||
{
|
||||
using System.Net.Http;
|
||||
|
||||
public class NoQosDelegatingHandler : DelegatingHandler
|
||||
{
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user