mirror of
https://github.com/nsnail/Ocelot.git
synced 2025-06-19 09:38:14 +08:00
Feature/expose http handlers (#224)
* temp commit * trying to work out how to expose the http handlers in a decent way.. * pissing about at lunch * changed to func so you can instanciate object or new it up each time * docs for dele handlers * upgraded to sdk 2.1.4 * some validation for consul services
This commit is contained in:
@ -0,0 +1,7 @@
|
||||
namespace Ocelot.DependencyInjection
|
||||
{
|
||||
public interface IOcelotAdministrationBuilder
|
||||
{
|
||||
IOcelotAdministrationBuilder AddRafty();
|
||||
}
|
||||
}
|
@ -1,6 +1,7 @@
|
||||
using Butterfly.Client.AspNetCore;
|
||||
using CacheManager.Core;
|
||||
using System;
|
||||
using System.Net.Http;
|
||||
|
||||
namespace Ocelot.DependencyInjection
|
||||
{
|
||||
@ -10,5 +11,6 @@ namespace Ocelot.DependencyInjection
|
||||
IOcelotBuilder AddCacheManager(Action<ConfigurationBuilderCachePart> settings);
|
||||
IOcelotBuilder AddOpenTracing(Action<ButterflyOptions> settings);
|
||||
IOcelotAdministrationBuilder AddAdministration(string path, string secret);
|
||||
IOcelotBuilder AddDelegatingHandler(Func<DelegatingHandler> delegatingHandler);
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,34 @@
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Ocelot.Raft;
|
||||
using Rafty.Concensus;
|
||||
using Rafty.FiniteStateMachine;
|
||||
using Rafty.Infrastructure;
|
||||
using Rafty.Log;
|
||||
|
||||
namespace Ocelot.DependencyInjection
|
||||
{
|
||||
public class OcelotAdministrationBuilder : IOcelotAdministrationBuilder
|
||||
{
|
||||
private readonly IServiceCollection _services;
|
||||
private readonly IConfiguration _configurationRoot;
|
||||
|
||||
public OcelotAdministrationBuilder(IServiceCollection services, IConfiguration configurationRoot)
|
||||
{
|
||||
_configurationRoot = configurationRoot;
|
||||
_services = services;
|
||||
}
|
||||
|
||||
public IOcelotAdministrationBuilder AddRafty()
|
||||
{
|
||||
var settings = new InMemorySettings(4000, 5000, 100, 5000);
|
||||
_services.AddSingleton<ILog, SqlLiteLog>();
|
||||
_services.AddSingleton<IFiniteStateMachine, OcelotFiniteStateMachine>();
|
||||
_services.AddSingleton<ISettings>(settings);
|
||||
_services.AddSingleton<IPeersProvider, FilePeersProvider>();
|
||||
_services.AddSingleton<INode, Node>();
|
||||
_services.Configure<FilePeers>(_configurationRoot);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,19 +1,12 @@
|
||||
using Butterfly.Client.AspNetCore;
|
||||
using CacheManager.Core;
|
||||
using IdentityServer4.AccessTokenValidation;
|
||||
using IdentityServer4.Models;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.DependencyInjection.Extensions;
|
||||
using Ocelot.Authorisation;
|
||||
using Ocelot.Cache;
|
||||
using Ocelot.Claims;
|
||||
using Ocelot.Configuration;
|
||||
using Ocelot.Configuration.Authentication;
|
||||
using Ocelot.Configuration.Builder;
|
||||
using Ocelot.Configuration.Creator;
|
||||
using Ocelot.Configuration.File;
|
||||
using Ocelot.Configuration.Parser;
|
||||
@ -32,7 +25,6 @@ using Ocelot.LoadBalancer.LoadBalancers;
|
||||
using Ocelot.Logging;
|
||||
using Ocelot.Middleware;
|
||||
using Ocelot.QueryStrings;
|
||||
using Ocelot.Raft;
|
||||
using Ocelot.RateLimit;
|
||||
using Ocelot.Request.Builder;
|
||||
using Ocelot.Request.Mapper;
|
||||
@ -40,24 +32,29 @@ using Ocelot.Requester;
|
||||
using Ocelot.Requester.QoS;
|
||||
using Ocelot.Responder;
|
||||
using Ocelot.ServiceDiscovery;
|
||||
using Rafty.Concensus;
|
||||
using Rafty.FiniteStateMachine;
|
||||
using Rafty.Infrastructure;
|
||||
using Rafty.Log;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IdentityModel.Tokens.Jwt;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
using IdentityServer4.AccessTokenValidation;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Ocelot.Configuration;
|
||||
using Ocelot.Configuration.Builder;
|
||||
using FileConfigurationProvider = Ocelot.Configuration.Provider.FileConfigurationProvider;
|
||||
using Microsoft.Extensions.DependencyInjection.Extensions;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using Butterfly.Client.AspNetCore;
|
||||
|
||||
namespace Ocelot.DependencyInjection
|
||||
{
|
||||
public class OcelotBuilder : IOcelotBuilder
|
||||
{
|
||||
private IServiceCollection _services;
|
||||
private IConfiguration _configurationRoot;
|
||||
private readonly IServiceCollection _services;
|
||||
private readonly IConfiguration _configurationRoot;
|
||||
private IDelegatingHandlerHandlerProvider _provider;
|
||||
|
||||
public OcelotBuilder(IServiceCollection services, IConfiguration configurationRoot)
|
||||
{
|
||||
@ -122,6 +119,9 @@ namespace Ocelot.DependencyInjection
|
||||
_services.TryAddSingleton<IRequestMapper, RequestMapper>();
|
||||
_services.TryAddSingleton<IHttpHandlerOptionsCreator, HttpHandlerOptionsCreator>();
|
||||
_services.TryAddSingleton<IDownstreamAddressesCreator, DownstreamAddressesCreator>();
|
||||
_services.TryAddSingleton<IDelegatingHandlerHandlerProviderFactory, DelegatingHandlerHandlerProviderFactory>();
|
||||
_services.TryAddSingleton<IDelegatingHandlerHandlerHouse, DelegatingHandlerHandlerHouse>();
|
||||
|
||||
// see this for why we register this as singleton http://stackoverflow.com/questions/37371264/invalidoperationexception-unable-to-resolve-service-for-type-microsoft-aspnetc
|
||||
// could maybe use a scoped data repository
|
||||
_services.TryAddSingleton<IHttpContextAccessor, HttpContextAccessor>();
|
||||
@ -142,6 +142,11 @@ namespace Ocelot.DependencyInjection
|
||||
_services.AddMiddlewareAnalysis();
|
||||
_services.AddWebEncoders();
|
||||
_services.AddSingleton<IAdministrationPath>(new NullAdministrationPath());
|
||||
|
||||
//these get picked out later and added to http request
|
||||
_provider = new DelegatingHandlerHandlerProvider();
|
||||
_services.TryAddSingleton<IDelegatingHandlerHandlerProvider>(_provider);
|
||||
_services.AddTransient<ITracingHandler, NoTracingHandler>();
|
||||
}
|
||||
|
||||
public IOcelotAdministrationBuilder AddAdministration(string path, string secret)
|
||||
@ -161,6 +166,19 @@ namespace Ocelot.DependencyInjection
|
||||
return new OcelotAdministrationBuilder(_services, _configurationRoot);
|
||||
}
|
||||
|
||||
public IOcelotBuilder AddDelegatingHandler(Func<DelegatingHandler> delegatingHandler)
|
||||
{
|
||||
_provider.Add(delegatingHandler);
|
||||
return this;
|
||||
}
|
||||
|
||||
public IOcelotBuilder AddOpenTracing(Action<ButterflyOptions> settings)
|
||||
{
|
||||
_services.AddTransient<ITracingHandler, OcelotHttpTracingHandler>();
|
||||
_services.AddButterfly(settings);
|
||||
return this;
|
||||
}
|
||||
|
||||
public IOcelotBuilder AddStoreOcelotConfigurationInConsul()
|
||||
{
|
||||
var serviceDiscoveryPort = _configurationRoot.GetValue("GlobalConfiguration:ServiceDiscoveryProvider:Port", 0);
|
||||
@ -203,13 +221,6 @@ namespace Ocelot.DependencyInjection
|
||||
return this;
|
||||
}
|
||||
|
||||
public IOcelotBuilder AddOpenTracing(Action<ButterflyOptions> settings)
|
||||
{
|
||||
_services.AddTransient<OcelotHttpTracingHandler>();
|
||||
_services.AddButterfly(settings);
|
||||
return this;
|
||||
}
|
||||
|
||||
private void AddIdentityServer(IIdentityServerConfiguration identityServerConfiguration, IAdministrationPath adminPath)
|
||||
{
|
||||
_services.TryAddSingleton<IIdentityServerConfiguration>(identityServerConfiguration);
|
||||
@ -281,33 +292,4 @@ namespace Ocelot.DependencyInjection
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public interface IOcelotAdministrationBuilder
|
||||
{
|
||||
IOcelotAdministrationBuilder AddRafty();
|
||||
}
|
||||
|
||||
public class OcelotAdministrationBuilder : IOcelotAdministrationBuilder
|
||||
{
|
||||
private IServiceCollection _services;
|
||||
private IConfiguration _configurationRoot;
|
||||
|
||||
public OcelotAdministrationBuilder(IServiceCollection services, IConfiguration configurationRoot)
|
||||
{
|
||||
_configurationRoot = configurationRoot;
|
||||
_services = services;
|
||||
}
|
||||
|
||||
public IOcelotAdministrationBuilder AddRafty()
|
||||
{
|
||||
var settings = new InMemorySettings(4000, 5000, 100, 5000);
|
||||
_services.AddSingleton<ILog, SqlLiteLog>();
|
||||
_services.AddSingleton<IFiniteStateMachine, OcelotFiniteStateMachine>();
|
||||
_services.AddSingleton<ISettings>(settings);
|
||||
_services.AddSingleton<IPeersProvider, FilePeersProvider>();
|
||||
_services.AddSingleton<INode, Node>();
|
||||
_services.Configure<FilePeers>(_configurationRoot);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,9 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Ocelot.Configuration;
|
||||
using Ocelot.Configuration.Provider;
|
||||
using Ocelot.DownstreamRouteFinder.UrlMatcher;
|
||||
using Ocelot.Errors;
|
||||
using Ocelot.Responses;
|
||||
|
@ -62,7 +62,7 @@ namespace Ocelot.Errors.Middleware
|
||||
private async Task TrySetGlobalRequestId(HttpContext context)
|
||||
{
|
||||
//try and get the global request id and set it for logs...
|
||||
//shoudl this basically be immutable per request...i guess it should!
|
||||
//should this basically be immutable per request...i guess it should!
|
||||
//first thing is get config
|
||||
var configuration = await _configProvider.Get();
|
||||
|
||||
|
@ -33,6 +33,7 @@
|
||||
UnmappableRequestError,
|
||||
RateLimitOptionsError,
|
||||
PathTemplateDoesntStartWithForwardSlash,
|
||||
FileValidationFailedError
|
||||
FileValidationFailedError,
|
||||
UnableToFindDelegatingHandlerProviderError
|
||||
}
|
||||
}
|
||||
|
@ -9,4 +9,4 @@ namespace Ocelot.LoadBalancer.LoadBalancers
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,6 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Ocelot.Configuration.Provider;
|
||||
using Ocelot.Infrastructure.RequestData;
|
||||
using Ocelot.LoadBalancer.LoadBalancers;
|
||||
using Ocelot.Logging;
|
||||
@ -46,11 +45,14 @@ namespace Ocelot.LoadBalancer.Middleware
|
||||
}
|
||||
|
||||
var uriBuilder = new UriBuilder(DownstreamRequest.RequestUri);
|
||||
|
||||
uriBuilder.Host = hostAndPort.Data.DownstreamHost;
|
||||
|
||||
if (hostAndPort.Data.DownstreamPort > 0)
|
||||
{
|
||||
uriBuilder.Port = hostAndPort.Data.DownstreamPort;
|
||||
}
|
||||
|
||||
DownstreamRequest.RequestUri = uriBuilder.Uri;
|
||||
|
||||
try
|
||||
|
@ -13,9 +13,10 @@ namespace Ocelot.Request.Builder
|
||||
IQoSProvider qosProvider,
|
||||
bool useCookieContainer,
|
||||
bool allowAutoRedirect,
|
||||
string reRouteKey,
|
||||
bool isTracing)
|
||||
{
|
||||
return new OkResponse<Request>(new Request(httpRequestMessage, isQos, qosProvider, allowAutoRedirect, useCookieContainer, isTracing));
|
||||
return new OkResponse<Request>(new Request(httpRequestMessage, isQos, qosProvider, allowAutoRedirect, useCookieContainer, reRouteKey, isTracing));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -14,6 +14,7 @@
|
||||
IQoSProvider qosProvider,
|
||||
bool useCookieContainer,
|
||||
bool allowAutoRedirect,
|
||||
string reRouteKe,
|
||||
bool isTracing);
|
||||
}
|
||||
}
|
||||
|
@ -47,7 +47,9 @@ namespace Ocelot.Request.Middleware
|
||||
qosProvider.Data,
|
||||
DownstreamRoute.ReRoute.HttpHandlerOptions.UseCookieContainer,
|
||||
DownstreamRoute.ReRoute.HttpHandlerOptions.AllowAutoRedirect,
|
||||
DownstreamRoute.ReRoute.ReRouteKey,
|
||||
DownstreamRoute.ReRoute.HttpHandlerOptions.UseTracing);
|
||||
|
||||
if (buildResult.IsError)
|
||||
{
|
||||
_logger.LogDebug("IRequestCreator returned an error, setting pipeline error");
|
||||
|
@ -11,6 +11,7 @@ namespace Ocelot.Request
|
||||
IQoSProvider qosProvider,
|
||||
bool allowAutoRedirect,
|
||||
bool useCookieContainer,
|
||||
string reRouteKey,
|
||||
bool isTracing
|
||||
)
|
||||
{
|
||||
@ -19,6 +20,7 @@ namespace Ocelot.Request
|
||||
QosProvider = qosProvider;
|
||||
AllowAutoRedirect = allowAutoRedirect;
|
||||
UseCookieContainer = useCookieContainer;
|
||||
ReRouteKey = reRouteKey;
|
||||
IsTracing = isTracing;
|
||||
}
|
||||
|
||||
@ -28,5 +30,6 @@ namespace Ocelot.Request
|
||||
public IQoSProvider QosProvider { get; private set; }
|
||||
public bool AllowAutoRedirect { get; private set; }
|
||||
public bool UseCookieContainer { get; private set; }
|
||||
public string ReRouteKey { get; private set; }
|
||||
}
|
||||
}
|
||||
}
|
49
src/Ocelot/Requester/DelegatingHandlerHandlerHouse.cs
Normal file
49
src/Ocelot/Requester/DelegatingHandlerHandlerHouse.cs
Normal file
@ -0,0 +1,49 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using Ocelot.Errors;
|
||||
using Ocelot.Responses;
|
||||
|
||||
namespace Ocelot.Requester
|
||||
{
|
||||
public class DelegatingHandlerHandlerHouse : IDelegatingHandlerHandlerHouse
|
||||
{
|
||||
private readonly IDelegatingHandlerHandlerProviderFactory _factory;
|
||||
private readonly ConcurrentDictionary<string, IDelegatingHandlerHandlerProvider> _housed;
|
||||
|
||||
public DelegatingHandlerHandlerHouse(IDelegatingHandlerHandlerProviderFactory factory)
|
||||
{
|
||||
_factory = factory;
|
||||
_housed = new ConcurrentDictionary<string, IDelegatingHandlerHandlerProvider>();
|
||||
}
|
||||
|
||||
public Response<IDelegatingHandlerHandlerProvider> Get(Request.Request request)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (_housed.TryGetValue(request.ReRouteKey, out var provider))
|
||||
{
|
||||
//todo once day we might need a check here to see if we need to create a new provider
|
||||
provider = _housed[request.ReRouteKey];
|
||||
return new OkResponse<IDelegatingHandlerHandlerProvider>(provider);
|
||||
}
|
||||
|
||||
provider = _factory.Get(request);
|
||||
AddHoused(request.ReRouteKey, provider);
|
||||
return new OkResponse<IDelegatingHandlerHandlerProvider>(provider);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return new ErrorResponse<IDelegatingHandlerHandlerProvider>(new List<Error>()
|
||||
{
|
||||
new UnableToFindDelegatingHandlerProviderError($"unabe to find delegating handler provider for {request.ReRouteKey} exception is {ex}")
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private void AddHoused(string key, IDelegatingHandlerHandlerProvider provider)
|
||||
{
|
||||
_housed.AddOrUpdate(key, provider, (k, v) => provider);
|
||||
}
|
||||
}
|
||||
}
|
28
src/Ocelot/Requester/DelegatingHandlerHandlerProvider.cs
Normal file
28
src/Ocelot/Requester/DelegatingHandlerHandlerProvider.cs
Normal file
@ -0,0 +1,28 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
|
||||
namespace Ocelot.Requester
|
||||
{
|
||||
public class DelegatingHandlerHandlerProvider : IDelegatingHandlerHandlerProvider
|
||||
{
|
||||
private readonly Dictionary<int, Func<DelegatingHandler>> _handlers;
|
||||
|
||||
public DelegatingHandlerHandlerProvider()
|
||||
{
|
||||
_handlers = new Dictionary<int, Func<DelegatingHandler>>();
|
||||
}
|
||||
|
||||
public void Add(Func<DelegatingHandler> handler)
|
||||
{
|
||||
var key = _handlers.Count == 0 ? 0 : _handlers.Count + 1;
|
||||
_handlers[key] = handler;
|
||||
}
|
||||
|
||||
public List<Func<DelegatingHandler>> Get()
|
||||
{
|
||||
return _handlers.Count > 0 ? _handlers.OrderBy(x => x.Key).Select(x => x.Value).ToList() : new List<Func<DelegatingHandler>>();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,43 @@
|
||||
using System.Net.Http;
|
||||
using Ocelot.Logging;
|
||||
|
||||
namespace Ocelot.Requester
|
||||
{
|
||||
public class DelegatingHandlerHandlerProviderFactory : IDelegatingHandlerHandlerProviderFactory
|
||||
{
|
||||
private readonly ITracingHandler _tracingHandler;
|
||||
private readonly IOcelotLoggerFactory _loggerFactory;
|
||||
private readonly IDelegatingHandlerHandlerProvider _allRoutesProvider;
|
||||
|
||||
public DelegatingHandlerHandlerProviderFactory(IOcelotLoggerFactory loggerFactory, IDelegatingHandlerHandlerProvider allRoutesProvider, ITracingHandler tracingHandler)
|
||||
{
|
||||
_tracingHandler = tracingHandler;
|
||||
_loggerFactory = loggerFactory;
|
||||
_allRoutesProvider = allRoutesProvider;
|
||||
}
|
||||
|
||||
public IDelegatingHandlerHandlerProvider Get(Request.Request request)
|
||||
{
|
||||
var handlersAppliedToAll = _allRoutesProvider.Get();
|
||||
|
||||
var provider = new DelegatingHandlerHandlerProvider();
|
||||
|
||||
foreach (var handler in handlersAppliedToAll)
|
||||
{
|
||||
provider.Add(handler);
|
||||
}
|
||||
|
||||
if (request.IsTracing)
|
||||
{
|
||||
provider.Add(() => (DelegatingHandler)_tracingHandler);
|
||||
}
|
||||
|
||||
if (request.IsQos)
|
||||
{
|
||||
provider.Add(() => new PollyCircuitBreakingDelegatingHandler(request.QosProvider, _loggerFactory));
|
||||
}
|
||||
|
||||
return provider;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,48 +1,33 @@
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Ocelot.Logging;
|
||||
using Ocelot.Requester.QoS;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Ocelot.Requester
|
||||
{
|
||||
internal class HttpClientBuilder : IHttpClientBuilder
|
||||
public class HttpClientBuilder : IHttpClientBuilder
|
||||
{
|
||||
private readonly Dictionary<int, Func<DelegatingHandler>> _handlers = new Dictionary<int, Func<DelegatingHandler>>();
|
||||
private readonly IDelegatingHandlerHandlerHouse _house;
|
||||
|
||||
public IHttpClientBuilder WithQos(IQoSProvider qosProvider, IOcelotLogger logger)
|
||||
public HttpClientBuilder(IDelegatingHandlerHandlerHouse house)
|
||||
{
|
||||
_handlers.Add(5000, () => new PollyCircuitBreakingDelegatingHandler(qosProvider, logger));
|
||||
|
||||
return this;
|
||||
_house = house;
|
||||
}
|
||||
|
||||
private IHttpClientBuilder WithTracing(IServiceProvider provider)
|
||||
public IHttpClient Create(Request.Request request)
|
||||
{
|
||||
_handlers.Add(6000, () => provider.GetService<OcelotHttpTracingHandler>());
|
||||
return this;
|
||||
}
|
||||
|
||||
public IHttpClient Create(bool useCookies, bool allowAutoRedirect, bool isTracing, IServiceProvider provider)
|
||||
{
|
||||
var httpclientHandler = new HttpClientHandler { AllowAutoRedirect = allowAutoRedirect, UseCookies = useCookies };
|
||||
if (isTracing)
|
||||
{
|
||||
WithTracing(provider);
|
||||
}
|
||||
var client = new HttpClient(CreateHttpMessageHandler(httpclientHandler));
|
||||
var httpclientHandler = new HttpClientHandler { AllowAutoRedirect = request.AllowAutoRedirect, UseCookies = request.UseCookieContainer};
|
||||
|
||||
var client = new HttpClient(CreateHttpMessageHandler(httpclientHandler, request));
|
||||
|
||||
return new HttpClientWrapper(client);
|
||||
}
|
||||
|
||||
private HttpMessageHandler CreateHttpMessageHandler(HttpMessageHandler httpMessageHandler)
|
||||
{
|
||||
_handlers
|
||||
.OrderByDescending(handler => handler.Key)
|
||||
.Select(handler => handler.Value)
|
||||
private HttpMessageHandler CreateHttpMessageHandler(HttpMessageHandler httpMessageHandler, Request.Request request)
|
||||
{
|
||||
var provider = _house.Get(request);
|
||||
|
||||
//todo handle error
|
||||
provider.Data.Get()
|
||||
.Select(handler => handler)
|
||||
.Reverse()
|
||||
.ToList()
|
||||
.ForEach(handler =>
|
||||
@ -54,22 +39,4 @@ namespace Ocelot.Requester
|
||||
return httpMessageHandler;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This class was made to make unit testing easier when HttpClient is used.
|
||||
/// </summary>
|
||||
internal class HttpClientWrapper : IHttpClient
|
||||
{
|
||||
public HttpClient Client { get; }
|
||||
|
||||
public HttpClientWrapper(HttpClient client)
|
||||
{
|
||||
Client = client;
|
||||
}
|
||||
|
||||
public Task<HttpResponseMessage> SendAsync(HttpRequestMessage request)
|
||||
{
|
||||
return Client.SendAsync(request);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,84 +1,84 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using Ocelot.Configuration;
|
||||
using Ocelot.Logging;
|
||||
using Ocelot.Responses;
|
||||
using Polly.CircuitBreaker;
|
||||
using Polly.Timeout;
|
||||
|
||||
namespace Ocelot.Requester
|
||||
{
|
||||
public class HttpClientHttpRequester : IHttpRequester
|
||||
{
|
||||
private readonly IHttpClientCache _cacheHandlers;
|
||||
private readonly IOcelotLogger _logger;
|
||||
private readonly IServiceProvider _serviceProvider;
|
||||
|
||||
public HttpClientHttpRequester(IOcelotLoggerFactory loggerFactory, IHttpClientCache cacheHandlers, IServiceProvider provider)
|
||||
{
|
||||
_logger = loggerFactory.CreateLogger<HttpClientHttpRequester>();
|
||||
_cacheHandlers = cacheHandlers;
|
||||
_serviceProvider = provider;
|
||||
}
|
||||
|
||||
public async Task<Response<HttpResponseMessage>> GetResponse(Request.Request request)
|
||||
{
|
||||
var builder = new HttpClientBuilder();
|
||||
|
||||
var cacheKey = GetCacheKey(request, builder);
|
||||
|
||||
var httpClient = GetHttpClient(cacheKey, builder, request.UseCookieContainer, request.AllowAutoRedirect, request.IsTracing);
|
||||
|
||||
try
|
||||
{
|
||||
var response = await httpClient.SendAsync(request.HttpRequestMessage);
|
||||
return new OkResponse<HttpResponseMessage>(response);
|
||||
}
|
||||
catch (TimeoutRejectedException exception)
|
||||
{
|
||||
return
|
||||
new ErrorResponse<HttpResponseMessage>(new RequestTimedOutError(exception));
|
||||
}
|
||||
catch (BrokenCircuitException exception)
|
||||
{
|
||||
return
|
||||
new ErrorResponse<HttpResponseMessage>(new RequestTimedOutError(exception));
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
return new ErrorResponse<HttpResponseMessage>(new UnableToCompleteRequestError(exception));
|
||||
}
|
||||
finally
|
||||
{
|
||||
_cacheHandlers.Set(cacheKey, httpClient, TimeSpan.FromHours(24));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private IHttpClient GetHttpClient(string cacheKey, IHttpClientBuilder builder, bool useCookieContainer, bool allowAutoRedirect,bool isTracing)
|
||||
{
|
||||
var httpClient = _cacheHandlers.Get(cacheKey);
|
||||
|
||||
if (httpClient == null)
|
||||
{
|
||||
httpClient = builder.Create(useCookieContainer, allowAutoRedirect, isTracing, _serviceProvider);
|
||||
}
|
||||
return httpClient;
|
||||
}
|
||||
|
||||
private string GetCacheKey(Request.Request request, IHttpClientBuilder builder)
|
||||
{
|
||||
string baseUrl = $"{request.HttpRequestMessage.RequestUri.Scheme}://{request.HttpRequestMessage.RequestUri.Authority}";
|
||||
|
||||
if (request.IsQos)
|
||||
{
|
||||
builder.WithQos(request.QosProvider, _logger);
|
||||
baseUrl = $"{baseUrl}{request.QosProvider.CircuitBreaker.CircuitBreakerPolicy.PolicyKey}";
|
||||
}
|
||||
|
||||
return baseUrl;
|
||||
}
|
||||
}
|
||||
}
|
||||
using System;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using Ocelot.Logging;
|
||||
using Ocelot.Responses;
|
||||
using Polly.CircuitBreaker;
|
||||
using Polly.Timeout;
|
||||
|
||||
namespace Ocelot.Requester
|
||||
{
|
||||
public class HttpClientHttpRequester : IHttpRequester
|
||||
{
|
||||
private readonly IHttpClientCache _cacheHandlers;
|
||||
private readonly IOcelotLogger _logger;
|
||||
private readonly IDelegatingHandlerHandlerHouse _house;
|
||||
|
||||
public HttpClientHttpRequester(IOcelotLoggerFactory loggerFactory,
|
||||
IHttpClientCache cacheHandlers,
|
||||
IDelegatingHandlerHandlerHouse house)
|
||||
{
|
||||
_logger = loggerFactory.CreateLogger<HttpClientHttpRequester>();
|
||||
_cacheHandlers = cacheHandlers;
|
||||
_house = house;
|
||||
}
|
||||
|
||||
public async Task<Response<HttpResponseMessage>> GetResponse(Request.Request request)
|
||||
{
|
||||
var builder = new HttpClientBuilder(_house);
|
||||
|
||||
var cacheKey = GetCacheKey(request);
|
||||
|
||||
var httpClient = GetHttpClient(cacheKey, builder, request);
|
||||
|
||||
try
|
||||
{
|
||||
var response = await httpClient.SendAsync(request.HttpRequestMessage);
|
||||
return new OkResponse<HttpResponseMessage>(response);
|
||||
}
|
||||
catch (TimeoutRejectedException exception)
|
||||
{
|
||||
return
|
||||
new ErrorResponse<HttpResponseMessage>(new RequestTimedOutError(exception));
|
||||
}
|
||||
catch (BrokenCircuitException exception)
|
||||
{
|
||||
return
|
||||
new ErrorResponse<HttpResponseMessage>(new RequestTimedOutError(exception));
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
return new ErrorResponse<HttpResponseMessage>(new UnableToCompleteRequestError(exception));
|
||||
}
|
||||
finally
|
||||
{
|
||||
_cacheHandlers.Set(cacheKey, httpClient, TimeSpan.FromHours(24));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private IHttpClient GetHttpClient(string cacheKey, IHttpClientBuilder builder, Request.Request request)
|
||||
{
|
||||
var httpClient = _cacheHandlers.Get(cacheKey);
|
||||
|
||||
if (httpClient == null)
|
||||
{
|
||||
httpClient = builder.Create(request);
|
||||
}
|
||||
|
||||
return httpClient;
|
||||
}
|
||||
|
||||
private string GetCacheKey(Request.Request request)
|
||||
{
|
||||
var baseUrl = $"{request.HttpRequestMessage.RequestUri.Scheme}://{request.HttpRequestMessage.RequestUri.Authority}";
|
||||
|
||||
if (request.IsQos)
|
||||
{
|
||||
baseUrl = $"{baseUrl}{request.QosProvider.CircuitBreaker.CircuitBreakerPolicy.PolicyKey}";
|
||||
}
|
||||
|
||||
return baseUrl;
|
||||
}
|
||||
}
|
||||
}
|
23
src/Ocelot/Requester/HttpClientWrapper.cs
Normal file
23
src/Ocelot/Requester/HttpClientWrapper.cs
Normal file
@ -0,0 +1,23 @@
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Ocelot.Requester
|
||||
{
|
||||
/// <summary>
|
||||
/// This class was made to make unit testing easier when HttpClient is used.
|
||||
/// </summary>
|
||||
internal class HttpClientWrapper : IHttpClient
|
||||
{
|
||||
public HttpClient Client { get; }
|
||||
|
||||
public HttpClientWrapper(HttpClient client)
|
||||
{
|
||||
Client = client;
|
||||
}
|
||||
|
||||
public Task<HttpResponseMessage> SendAsync(HttpRequestMessage request)
|
||||
{
|
||||
return Client.SendAsync(request);
|
||||
}
|
||||
}
|
||||
}
|
9
src/Ocelot/Requester/IDelegatingHandlerHandlerHouse.cs
Normal file
9
src/Ocelot/Requester/IDelegatingHandlerHandlerHouse.cs
Normal file
@ -0,0 +1,9 @@
|
||||
using Ocelot.Responses;
|
||||
|
||||
namespace Ocelot.Requester
|
||||
{
|
||||
public interface IDelegatingHandlerHandlerHouse
|
||||
{
|
||||
Response<IDelegatingHandlerHandlerProvider> Get(Request.Request request);
|
||||
}
|
||||
}
|
12
src/Ocelot/Requester/IDelegatingHandlerHandlerProvider.cs
Normal file
12
src/Ocelot/Requester/IDelegatingHandlerHandlerProvider.cs
Normal file
@ -0,0 +1,12 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Net.Http;
|
||||
|
||||
namespace Ocelot.Requester
|
||||
{
|
||||
public interface IDelegatingHandlerHandlerProvider
|
||||
{
|
||||
void Add(Func<DelegatingHandler> handler);
|
||||
List<Func<DelegatingHandler>> Get();
|
||||
}
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
namespace Ocelot.Requester
|
||||
{
|
||||
public interface IDelegatingHandlerHandlerProviderFactory
|
||||
{
|
||||
IDelegatingHandlerHandlerProvider Get(Request.Request request);
|
||||
}
|
||||
}
|
@ -1,27 +1,14 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using Ocelot.Logging;
|
||||
using Ocelot.Requester.QoS;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using Ocelot.Configuration;
|
||||
|
||||
namespace Ocelot.Requester
|
||||
{
|
||||
public interface IHttpClientBuilder
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Sets a PollyCircuitBreakingDelegatingHandler .
|
||||
/// </summary>
|
||||
IHttpClientBuilder WithQos(IQoSProvider qosProvider, IOcelotLogger logger);
|
||||
|
||||
/// <summary>
|
||||
/// Creates the <see cref="HttpClient"/>
|
||||
/// </summary>
|
||||
/// <param name="useCookies">Defines if http client should use cookie container</param>
|
||||
/// <param name="allowAutoRedirect">Defines if http client should allow auto redirect</param>
|
||||
IHttpClient Create(bool useCookies, bool allowAutoRedirect, bool isTracing, IServiceProvider provider);
|
||||
/// </summary>
|
||||
/// <param name="request"></param>
|
||||
IHttpClient Create(Request.Request request);
|
||||
}
|
||||
}
|
||||
|
@ -8,7 +8,16 @@ using Butterfly.OpenTracing;
|
||||
|
||||
namespace Ocelot.Requester
|
||||
{
|
||||
public class OcelotHttpTracingHandler : DelegatingHandler
|
||||
public interface ITracingHandler
|
||||
{
|
||||
}
|
||||
|
||||
public class NoTracingHandler : DelegatingHandler, ITracingHandler
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public class OcelotHttpTracingHandler : DelegatingHandler, ITracingHandler
|
||||
{
|
||||
private readonly IServiceTracer _tracer;
|
||||
private const string prefix_spanId = "ot-spanId";
|
||||
|
@ -16,10 +16,10 @@ namespace Ocelot.Requester
|
||||
|
||||
public PollyCircuitBreakingDelegatingHandler(
|
||||
IQoSProvider qoSProvider,
|
||||
IOcelotLogger logger)
|
||||
IOcelotLoggerFactory loggerFactory)
|
||||
{
|
||||
_qoSProvider = qoSProvider;
|
||||
_logger = logger;
|
||||
_logger = loggerFactory.CreateLogger<PollyCircuitBreakingDelegatingHandler>();
|
||||
}
|
||||
|
||||
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
|
||||
|
@ -0,0 +1,12 @@
|
||||
using Ocelot.Errors;
|
||||
|
||||
namespace Ocelot.Requester
|
||||
{
|
||||
public class UnableToFindDelegatingHandlerProviderError : Error
|
||||
{
|
||||
public UnableToFindDelegatingHandlerProviderError(string message)
|
||||
: base(message, OcelotErrorCode.UnableToFindDelegatingHandlerProviderError)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
@ -4,6 +4,7 @@ using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Consul;
|
||||
using Ocelot.Infrastructure.Extensions;
|
||||
using Ocelot.Logging;
|
||||
using Ocelot.Values;
|
||||
|
||||
namespace Ocelot.ServiceDiscovery
|
||||
@ -11,13 +12,18 @@ namespace Ocelot.ServiceDiscovery
|
||||
public class ConsulServiceDiscoveryProvider : IServiceDiscoveryProvider
|
||||
{
|
||||
private readonly ConsulRegistryConfiguration _consulConfig;
|
||||
private readonly IOcelotLogger _logger;
|
||||
private readonly ConsulClient _consul;
|
||||
private const string VersionPrefix = "version-";
|
||||
|
||||
public ConsulServiceDiscoveryProvider(ConsulRegistryConfiguration consulRegistryConfiguration)
|
||||
{
|
||||
public ConsulServiceDiscoveryProvider(ConsulRegistryConfiguration consulRegistryConfiguration, IOcelotLoggerFactory factory)
|
||||
{;
|
||||
_logger = factory.CreateLogger<ConsulServiceDiscoveryProvider>();
|
||||
|
||||
var consulHost = string.IsNullOrEmpty(consulRegistryConfiguration?.HostName) ? "localhost" : consulRegistryConfiguration.HostName;
|
||||
|
||||
var consulPort = consulRegistryConfiguration?.Port ?? 8500;
|
||||
|
||||
_consulConfig = new ConsulRegistryConfiguration(consulHost, consulPort, consulRegistryConfiguration?.KeyOfServiceInConsul);
|
||||
|
||||
_consul = new ConsulClient(config =>
|
||||
@ -30,7 +36,19 @@ namespace Ocelot.ServiceDiscovery
|
||||
{
|
||||
var queryResult = await _consul.Health.Service(_consulConfig.KeyOfServiceInConsul, string.Empty, true);
|
||||
|
||||
var services = queryResult.Response.Select(BuildService);
|
||||
var services = new List<Service>();
|
||||
|
||||
foreach (var serviceEntry in queryResult.Response)
|
||||
{
|
||||
if (IsValid(serviceEntry))
|
||||
{
|
||||
services.Add(BuildService(serviceEntry));
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.LogError($"Unable to use service Address: {serviceEntry.Service.Address} and Port: {serviceEntry.Service.Port} as it is invalid. Address must contain host only e.g. localhost and port must be greater than 0");
|
||||
}
|
||||
}
|
||||
|
||||
return services.ToList();
|
||||
}
|
||||
@ -45,6 +63,16 @@ namespace Ocelot.ServiceDiscovery
|
||||
serviceEntry.Service.Tags ?? Enumerable.Empty<string>());
|
||||
}
|
||||
|
||||
private bool IsValid(ServiceEntry serviceEntry)
|
||||
{
|
||||
if (serviceEntry.Service.Address.Contains("http://") || serviceEntry.Service.Address.Contains("https://") || serviceEntry.Service.Port <= 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private string GetVersionFromStrings(IEnumerable<string> strings)
|
||||
{
|
||||
return strings
|
||||
|
@ -1,11 +1,19 @@
|
||||
using System.Collections.Generic;
|
||||
using Ocelot.Configuration;
|
||||
using Ocelot.Logging;
|
||||
using Ocelot.Values;
|
||||
|
||||
namespace Ocelot.ServiceDiscovery
|
||||
{
|
||||
public class ServiceDiscoveryProviderFactory : IServiceDiscoveryProviderFactory
|
||||
{
|
||||
private readonly IOcelotLoggerFactory _factory;
|
||||
|
||||
public ServiceDiscoveryProviderFactory(IOcelotLoggerFactory factory)
|
||||
{
|
||||
_factory = factory;
|
||||
}
|
||||
|
||||
public IServiceDiscoveryProvider Get(ServiceProviderConfiguration serviceConfig, ReRoute reRoute)
|
||||
{
|
||||
if (reRoute.UseServiceDiscovery)
|
||||
@ -28,7 +36,7 @@ namespace Ocelot.ServiceDiscovery
|
||||
private IServiceDiscoveryProvider GetServiceDiscoveryProvider(string keyOfServiceInConsul, string providerHostName, int providerPort)
|
||||
{
|
||||
var consulRegistryConfiguration = new ConsulRegistryConfiguration(providerHostName, providerPort, keyOfServiceInConsul);
|
||||
return new ConsulServiceDiscoveryProvider(consulRegistryConfiguration);
|
||||
return new ConsulServiceDiscoveryProvider(consulRegistryConfiguration, _factory);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user