mirror of
https://github.com/nsnail/Ocelot.git
synced 2025-04-22 06:22:50 +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:
parent
ef3c4f614a
commit
98133d9473
43
docs/features/delegatinghandlers.rst
Normal file
43
docs/features/delegatinghandlers.rst
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
Delegating Handers
|
||||||
|
==================
|
||||||
|
|
||||||
|
Ocelot allows the user to add delegating handlers to the HttpClient transport. This feature was requested `GitHub #208 <https://github.com/TomPallister/Ocelot/issues/208>`_ and I decided that it was going to be useful in various ways.
|
||||||
|
|
||||||
|
Usage
|
||||||
|
^^^^^^
|
||||||
|
|
||||||
|
In order to add delegating handlers to the HttpClient transport you need to do the following.
|
||||||
|
|
||||||
|
.. code-block:: csharp
|
||||||
|
|
||||||
|
services.AddOcelot()
|
||||||
|
.AddDelegatingHandler(() => new FakeHandler())
|
||||||
|
.AddDelegatingHandler(() => new FakeHandler());
|
||||||
|
|
||||||
|
Or for singleton like behaviour..
|
||||||
|
|
||||||
|
.. code-block:: csharp
|
||||||
|
|
||||||
|
var handlerOne = new FakeHandler();
|
||||||
|
var handlerTwo = new FakeHandler();
|
||||||
|
|
||||||
|
services.AddOcelot()
|
||||||
|
.AddDelegatingHandler(() => handlerOne)
|
||||||
|
.AddDelegatingHandler(() => handlerTwo);
|
||||||
|
|
||||||
|
You can have as many DelegatingHandlers as you want and they are run in a first in first out order. If you are using Ocelot's QoS functionality then that will always be run after your last delegating handler.
|
||||||
|
|
||||||
|
In order to create a class that can be used a delegating handler it must look as follows
|
||||||
|
|
||||||
|
.. code-block:: csharp
|
||||||
|
|
||||||
|
public class FakeHandler : DelegatingHandler
|
||||||
|
{
|
||||||
|
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
//do stuff and optionally call the base handler..
|
||||||
|
return await base.SendAsync(request, cancellationToken);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Hopefully other people will find this feature useful!
|
@ -33,6 +33,8 @@ Thanks for taking a look at the Ocelot documentation. Please use the left hand n
|
|||||||
features/requestid
|
features/requestid
|
||||||
features/middlewareinjection
|
features/middlewareinjection
|
||||||
features/loadbalancer
|
features/loadbalancer
|
||||||
|
features/delegatinghandlers
|
||||||
|
|
||||||
|
|
||||||
.. toctree::
|
.. toctree::
|
||||||
:maxdepth: 2
|
:maxdepth: 2
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"projects": [ "src", "test" ],
|
"projects": [ "src", "test" ],
|
||||||
"sdk": {
|
"sdk": {
|
||||||
"version": "2.0.2"
|
"version": "2.1.4"
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -0,0 +1,7 @@
|
|||||||
|
namespace Ocelot.DependencyInjection
|
||||||
|
{
|
||||||
|
public interface IOcelotAdministrationBuilder
|
||||||
|
{
|
||||||
|
IOcelotAdministrationBuilder AddRafty();
|
||||||
|
}
|
||||||
|
}
|
@ -1,6 +1,7 @@
|
|||||||
using Butterfly.Client.AspNetCore;
|
using Butterfly.Client.AspNetCore;
|
||||||
using CacheManager.Core;
|
using CacheManager.Core;
|
||||||
using System;
|
using System;
|
||||||
|
using System.Net.Http;
|
||||||
|
|
||||||
namespace Ocelot.DependencyInjection
|
namespace Ocelot.DependencyInjection
|
||||||
{
|
{
|
||||||
@ -10,5 +11,6 @@ namespace Ocelot.DependencyInjection
|
|||||||
IOcelotBuilder AddCacheManager(Action<ConfigurationBuilderCachePart> settings);
|
IOcelotBuilder AddCacheManager(Action<ConfigurationBuilderCachePart> settings);
|
||||||
IOcelotBuilder AddOpenTracing(Action<ButterflyOptions> settings);
|
IOcelotBuilder AddOpenTracing(Action<ButterflyOptions> settings);
|
||||||
IOcelotAdministrationBuilder AddAdministration(string path, string secret);
|
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 CacheManager.Core;
|
||||||
using IdentityServer4.AccessTokenValidation;
|
|
||||||
using IdentityServer4.Models;
|
using IdentityServer4.Models;
|
||||||
using Microsoft.AspNetCore.Builder;
|
|
||||||
using Microsoft.AspNetCore.Hosting;
|
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
using Microsoft.Extensions.Configuration;
|
using Microsoft.Extensions.Configuration;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Microsoft.Extensions.DependencyInjection.Extensions;
|
|
||||||
using Ocelot.Authorisation;
|
using Ocelot.Authorisation;
|
||||||
using Ocelot.Cache;
|
using Ocelot.Cache;
|
||||||
using Ocelot.Claims;
|
using Ocelot.Claims;
|
||||||
using Ocelot.Configuration;
|
|
||||||
using Ocelot.Configuration.Authentication;
|
using Ocelot.Configuration.Authentication;
|
||||||
using Ocelot.Configuration.Builder;
|
|
||||||
using Ocelot.Configuration.Creator;
|
using Ocelot.Configuration.Creator;
|
||||||
using Ocelot.Configuration.File;
|
using Ocelot.Configuration.File;
|
||||||
using Ocelot.Configuration.Parser;
|
using Ocelot.Configuration.Parser;
|
||||||
@ -32,7 +25,6 @@ using Ocelot.LoadBalancer.LoadBalancers;
|
|||||||
using Ocelot.Logging;
|
using Ocelot.Logging;
|
||||||
using Ocelot.Middleware;
|
using Ocelot.Middleware;
|
||||||
using Ocelot.QueryStrings;
|
using Ocelot.QueryStrings;
|
||||||
using Ocelot.Raft;
|
|
||||||
using Ocelot.RateLimit;
|
using Ocelot.RateLimit;
|
||||||
using Ocelot.Request.Builder;
|
using Ocelot.Request.Builder;
|
||||||
using Ocelot.Request.Mapper;
|
using Ocelot.Request.Mapper;
|
||||||
@ -40,24 +32,29 @@ using Ocelot.Requester;
|
|||||||
using Ocelot.Requester.QoS;
|
using Ocelot.Requester.QoS;
|
||||||
using Ocelot.Responder;
|
using Ocelot.Responder;
|
||||||
using Ocelot.ServiceDiscovery;
|
using Ocelot.ServiceDiscovery;
|
||||||
using Rafty.Concensus;
|
|
||||||
using Rafty.FiniteStateMachine;
|
|
||||||
using Rafty.Infrastructure;
|
|
||||||
using Rafty.Log;
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IdentityModel.Tokens.Jwt;
|
using System.IdentityModel.Tokens.Jwt;
|
||||||
using System.Linq;
|
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Security.Cryptography.X509Certificates;
|
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 FileConfigurationProvider = Ocelot.Configuration.Provider.FileConfigurationProvider;
|
||||||
|
using Microsoft.Extensions.DependencyInjection.Extensions;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Net.Http;
|
||||||
|
using Butterfly.Client.AspNetCore;
|
||||||
|
|
||||||
namespace Ocelot.DependencyInjection
|
namespace Ocelot.DependencyInjection
|
||||||
{
|
{
|
||||||
public class OcelotBuilder : IOcelotBuilder
|
public class OcelotBuilder : IOcelotBuilder
|
||||||
{
|
{
|
||||||
private IServiceCollection _services;
|
private readonly IServiceCollection _services;
|
||||||
private IConfiguration _configurationRoot;
|
private readonly IConfiguration _configurationRoot;
|
||||||
|
private IDelegatingHandlerHandlerProvider _provider;
|
||||||
|
|
||||||
public OcelotBuilder(IServiceCollection services, IConfiguration configurationRoot)
|
public OcelotBuilder(IServiceCollection services, IConfiguration configurationRoot)
|
||||||
{
|
{
|
||||||
@ -122,6 +119,9 @@ namespace Ocelot.DependencyInjection
|
|||||||
_services.TryAddSingleton<IRequestMapper, RequestMapper>();
|
_services.TryAddSingleton<IRequestMapper, RequestMapper>();
|
||||||
_services.TryAddSingleton<IHttpHandlerOptionsCreator, HttpHandlerOptionsCreator>();
|
_services.TryAddSingleton<IHttpHandlerOptionsCreator, HttpHandlerOptionsCreator>();
|
||||||
_services.TryAddSingleton<IDownstreamAddressesCreator, DownstreamAddressesCreator>();
|
_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
|
// 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
|
// could maybe use a scoped data repository
|
||||||
_services.TryAddSingleton<IHttpContextAccessor, HttpContextAccessor>();
|
_services.TryAddSingleton<IHttpContextAccessor, HttpContextAccessor>();
|
||||||
@ -142,6 +142,11 @@ namespace Ocelot.DependencyInjection
|
|||||||
_services.AddMiddlewareAnalysis();
|
_services.AddMiddlewareAnalysis();
|
||||||
_services.AddWebEncoders();
|
_services.AddWebEncoders();
|
||||||
_services.AddSingleton<IAdministrationPath>(new NullAdministrationPath());
|
_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)
|
public IOcelotAdministrationBuilder AddAdministration(string path, string secret)
|
||||||
@ -161,6 +166,19 @@ namespace Ocelot.DependencyInjection
|
|||||||
return new OcelotAdministrationBuilder(_services, _configurationRoot);
|
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()
|
public IOcelotBuilder AddStoreOcelotConfigurationInConsul()
|
||||||
{
|
{
|
||||||
var serviceDiscoveryPort = _configurationRoot.GetValue("GlobalConfiguration:ServiceDiscoveryProvider:Port", 0);
|
var serviceDiscoveryPort = _configurationRoot.GetValue("GlobalConfiguration:ServiceDiscoveryProvider:Port", 0);
|
||||||
@ -203,13 +221,6 @@ namespace Ocelot.DependencyInjection
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public IOcelotBuilder AddOpenTracing(Action<ButterflyOptions> settings)
|
|
||||||
{
|
|
||||||
_services.AddTransient<OcelotHttpTracingHandler>();
|
|
||||||
_services.AddButterfly(settings);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void AddIdentityServer(IIdentityServerConfiguration identityServerConfiguration, IAdministrationPath adminPath)
|
private void AddIdentityServer(IIdentityServerConfiguration identityServerConfiguration, IAdministrationPath adminPath)
|
||||||
{
|
{
|
||||||
_services.TryAddSingleton<IIdentityServerConfiguration>(identityServerConfiguration);
|
_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.Linq;
|
||||||
using System.Threading.Tasks;
|
|
||||||
using Ocelot.Configuration;
|
using Ocelot.Configuration;
|
||||||
using Ocelot.Configuration.Provider;
|
|
||||||
using Ocelot.DownstreamRouteFinder.UrlMatcher;
|
using Ocelot.DownstreamRouteFinder.UrlMatcher;
|
||||||
using Ocelot.Errors;
|
using Ocelot.Errors;
|
||||||
using Ocelot.Responses;
|
using Ocelot.Responses;
|
||||||
|
@ -62,7 +62,7 @@ namespace Ocelot.Errors.Middleware
|
|||||||
private async Task TrySetGlobalRequestId(HttpContext context)
|
private async Task TrySetGlobalRequestId(HttpContext context)
|
||||||
{
|
{
|
||||||
//try and get the global request id and set it for logs...
|
//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
|
//first thing is get config
|
||||||
var configuration = await _configProvider.Get();
|
var configuration = await _configProvider.Get();
|
||||||
|
|
||||||
|
@ -33,6 +33,7 @@
|
|||||||
UnmappableRequestError,
|
UnmappableRequestError,
|
||||||
RateLimitOptionsError,
|
RateLimitOptionsError,
|
||||||
PathTemplateDoesntStartWithForwardSlash,
|
PathTemplateDoesntStartWithForwardSlash,
|
||||||
FileValidationFailedError
|
FileValidationFailedError,
|
||||||
|
UnableToFindDelegatingHandlerProviderError
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
using Ocelot.Configuration.Provider;
|
|
||||||
using Ocelot.Infrastructure.RequestData;
|
using Ocelot.Infrastructure.RequestData;
|
||||||
using Ocelot.LoadBalancer.LoadBalancers;
|
using Ocelot.LoadBalancer.LoadBalancers;
|
||||||
using Ocelot.Logging;
|
using Ocelot.Logging;
|
||||||
@ -46,11 +45,14 @@ namespace Ocelot.LoadBalancer.Middleware
|
|||||||
}
|
}
|
||||||
|
|
||||||
var uriBuilder = new UriBuilder(DownstreamRequest.RequestUri);
|
var uriBuilder = new UriBuilder(DownstreamRequest.RequestUri);
|
||||||
|
|
||||||
uriBuilder.Host = hostAndPort.Data.DownstreamHost;
|
uriBuilder.Host = hostAndPort.Data.DownstreamHost;
|
||||||
|
|
||||||
if (hostAndPort.Data.DownstreamPort > 0)
|
if (hostAndPort.Data.DownstreamPort > 0)
|
||||||
{
|
{
|
||||||
uriBuilder.Port = hostAndPort.Data.DownstreamPort;
|
uriBuilder.Port = hostAndPort.Data.DownstreamPort;
|
||||||
}
|
}
|
||||||
|
|
||||||
DownstreamRequest.RequestUri = uriBuilder.Uri;
|
DownstreamRequest.RequestUri = uriBuilder.Uri;
|
||||||
|
|
||||||
try
|
try
|
||||||
|
@ -13,9 +13,10 @@ namespace Ocelot.Request.Builder
|
|||||||
IQoSProvider qosProvider,
|
IQoSProvider qosProvider,
|
||||||
bool useCookieContainer,
|
bool useCookieContainer,
|
||||||
bool allowAutoRedirect,
|
bool allowAutoRedirect,
|
||||||
|
string reRouteKey,
|
||||||
bool isTracing)
|
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,
|
IQoSProvider qosProvider,
|
||||||
bool useCookieContainer,
|
bool useCookieContainer,
|
||||||
bool allowAutoRedirect,
|
bool allowAutoRedirect,
|
||||||
|
string reRouteKe,
|
||||||
bool isTracing);
|
bool isTracing);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -47,7 +47,9 @@ namespace Ocelot.Request.Middleware
|
|||||||
qosProvider.Data,
|
qosProvider.Data,
|
||||||
DownstreamRoute.ReRoute.HttpHandlerOptions.UseCookieContainer,
|
DownstreamRoute.ReRoute.HttpHandlerOptions.UseCookieContainer,
|
||||||
DownstreamRoute.ReRoute.HttpHandlerOptions.AllowAutoRedirect,
|
DownstreamRoute.ReRoute.HttpHandlerOptions.AllowAutoRedirect,
|
||||||
|
DownstreamRoute.ReRoute.ReRouteKey,
|
||||||
DownstreamRoute.ReRoute.HttpHandlerOptions.UseTracing);
|
DownstreamRoute.ReRoute.HttpHandlerOptions.UseTracing);
|
||||||
|
|
||||||
if (buildResult.IsError)
|
if (buildResult.IsError)
|
||||||
{
|
{
|
||||||
_logger.LogDebug("IRequestCreator returned an error, setting pipeline error");
|
_logger.LogDebug("IRequestCreator returned an error, setting pipeline error");
|
||||||
|
@ -11,6 +11,7 @@ namespace Ocelot.Request
|
|||||||
IQoSProvider qosProvider,
|
IQoSProvider qosProvider,
|
||||||
bool allowAutoRedirect,
|
bool allowAutoRedirect,
|
||||||
bool useCookieContainer,
|
bool useCookieContainer,
|
||||||
|
string reRouteKey,
|
||||||
bool isTracing
|
bool isTracing
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
@ -19,6 +20,7 @@ namespace Ocelot.Request
|
|||||||
QosProvider = qosProvider;
|
QosProvider = qosProvider;
|
||||||
AllowAutoRedirect = allowAutoRedirect;
|
AllowAutoRedirect = allowAutoRedirect;
|
||||||
UseCookieContainer = useCookieContainer;
|
UseCookieContainer = useCookieContainer;
|
||||||
|
ReRouteKey = reRouteKey;
|
||||||
IsTracing = isTracing;
|
IsTracing = isTracing;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -28,5 +30,6 @@ namespace Ocelot.Request
|
|||||||
public IQoSProvider QosProvider { get; private set; }
|
public IQoSProvider QosProvider { get; private set; }
|
||||||
public bool AllowAutoRedirect { get; private set; }
|
public bool AllowAutoRedirect { get; private set; }
|
||||||
public bool UseCookieContainer { 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 System.Linq;
|
||||||
using Ocelot.Logging;
|
|
||||||
using Ocelot.Requester.QoS;
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace Ocelot.Requester
|
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));
|
_house = house;
|
||||||
|
|
||||||
return this;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private IHttpClientBuilder WithTracing(IServiceProvider provider)
|
public IHttpClient Create(Request.Request request)
|
||||||
{
|
{
|
||||||
_handlers.Add(6000, () => provider.GetService<OcelotHttpTracingHandler>());
|
var httpclientHandler = new HttpClientHandler { AllowAutoRedirect = request.AllowAutoRedirect, UseCookies = request.UseCookieContainer};
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public IHttpClient Create(bool useCookies, bool allowAutoRedirect, bool isTracing, IServiceProvider provider)
|
var client = new HttpClient(CreateHttpMessageHandler(httpclientHandler, request));
|
||||||
{
|
|
||||||
var httpclientHandler = new HttpClientHandler { AllowAutoRedirect = allowAutoRedirect, UseCookies = useCookies };
|
|
||||||
if (isTracing)
|
|
||||||
{
|
|
||||||
WithTracing(provider);
|
|
||||||
}
|
|
||||||
var client = new HttpClient(CreateHttpMessageHandler(httpclientHandler));
|
|
||||||
|
|
||||||
return new HttpClientWrapper(client);
|
return new HttpClientWrapper(client);
|
||||||
}
|
}
|
||||||
|
|
||||||
private HttpMessageHandler CreateHttpMessageHandler(HttpMessageHandler httpMessageHandler)
|
private HttpMessageHandler CreateHttpMessageHandler(HttpMessageHandler httpMessageHandler, Request.Request request)
|
||||||
{
|
{
|
||||||
_handlers
|
var provider = _house.Get(request);
|
||||||
.OrderByDescending(handler => handler.Key)
|
|
||||||
.Select(handler => handler.Value)
|
//todo handle error
|
||||||
|
provider.Data.Get()
|
||||||
|
.Select(handler => handler)
|
||||||
.Reverse()
|
.Reverse()
|
||||||
.ToList()
|
.ToList()
|
||||||
.ForEach(handler =>
|
.ForEach(handler =>
|
||||||
@ -54,22 +39,4 @@ namespace Ocelot.Requester
|
|||||||
return httpMessageHandler;
|
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,8 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Concurrent;
|
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Ocelot.Configuration;
|
|
||||||
using Ocelot.Logging;
|
using Ocelot.Logging;
|
||||||
using Ocelot.Responses;
|
using Ocelot.Responses;
|
||||||
using Polly.CircuitBreaker;
|
using Polly.CircuitBreaker;
|
||||||
@ -14,22 +12,24 @@ namespace Ocelot.Requester
|
|||||||
{
|
{
|
||||||
private readonly IHttpClientCache _cacheHandlers;
|
private readonly IHttpClientCache _cacheHandlers;
|
||||||
private readonly IOcelotLogger _logger;
|
private readonly IOcelotLogger _logger;
|
||||||
private readonly IServiceProvider _serviceProvider;
|
private readonly IDelegatingHandlerHandlerHouse _house;
|
||||||
|
|
||||||
public HttpClientHttpRequester(IOcelotLoggerFactory loggerFactory, IHttpClientCache cacheHandlers, IServiceProvider provider)
|
public HttpClientHttpRequester(IOcelotLoggerFactory loggerFactory,
|
||||||
|
IHttpClientCache cacheHandlers,
|
||||||
|
IDelegatingHandlerHandlerHouse house)
|
||||||
{
|
{
|
||||||
_logger = loggerFactory.CreateLogger<HttpClientHttpRequester>();
|
_logger = loggerFactory.CreateLogger<HttpClientHttpRequester>();
|
||||||
_cacheHandlers = cacheHandlers;
|
_cacheHandlers = cacheHandlers;
|
||||||
_serviceProvider = provider;
|
_house = house;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<Response<HttpResponseMessage>> GetResponse(Request.Request request)
|
public async Task<Response<HttpResponseMessage>> GetResponse(Request.Request request)
|
||||||
{
|
{
|
||||||
var builder = new HttpClientBuilder();
|
var builder = new HttpClientBuilder(_house);
|
||||||
|
|
||||||
var cacheKey = GetCacheKey(request, builder);
|
var cacheKey = GetCacheKey(request);
|
||||||
|
|
||||||
var httpClient = GetHttpClient(cacheKey, builder, request.UseCookieContainer, request.AllowAutoRedirect, request.IsTracing);
|
var httpClient = GetHttpClient(cacheKey, builder, request);
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@ -57,24 +57,24 @@ namespace Ocelot.Requester
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private IHttpClient GetHttpClient(string cacheKey, IHttpClientBuilder builder, bool useCookieContainer, bool allowAutoRedirect,bool isTracing)
|
private IHttpClient GetHttpClient(string cacheKey, IHttpClientBuilder builder, Request.Request request)
|
||||||
{
|
{
|
||||||
var httpClient = _cacheHandlers.Get(cacheKey);
|
var httpClient = _cacheHandlers.Get(cacheKey);
|
||||||
|
|
||||||
if (httpClient == null)
|
if (httpClient == null)
|
||||||
{
|
{
|
||||||
httpClient = builder.Create(useCookieContainer, allowAutoRedirect, isTracing, _serviceProvider);
|
httpClient = builder.Create(request);
|
||||||
}
|
}
|
||||||
|
|
||||||
return httpClient;
|
return httpClient;
|
||||||
}
|
}
|
||||||
|
|
||||||
private string GetCacheKey(Request.Request request, IHttpClientBuilder builder)
|
private string GetCacheKey(Request.Request request)
|
||||||
{
|
{
|
||||||
string baseUrl = $"{request.HttpRequestMessage.RequestUri.Scheme}://{request.HttpRequestMessage.RequestUri.Authority}";
|
var baseUrl = $"{request.HttpRequestMessage.RequestUri.Scheme}://{request.HttpRequestMessage.RequestUri.Authority}";
|
||||||
|
|
||||||
if (request.IsQos)
|
if (request.IsQos)
|
||||||
{
|
{
|
||||||
builder.WithQos(request.QosProvider, _logger);
|
|
||||||
baseUrl = $"{baseUrl}{request.QosProvider.CircuitBreaker.CircuitBreakerPolicy.PolicyKey}";
|
baseUrl = $"{baseUrl}{request.QosProvider.CircuitBreaker.CircuitBreakerPolicy.PolicyKey}";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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.Net.Http;
|
||||||
using System.Collections.Generic;
|
using Ocelot.Configuration;
|
||||||
using System.Linq;
|
|
||||||
using System.Net.Http;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using Ocelot.Logging;
|
|
||||||
using Ocelot.Requester.QoS;
|
|
||||||
using System.Net;
|
|
||||||
|
|
||||||
namespace Ocelot.Requester
|
namespace Ocelot.Requester
|
||||||
{
|
{
|
||||||
public interface IHttpClientBuilder
|
public interface IHttpClientBuilder
|
||||||
{
|
{
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Sets a PollyCircuitBreakingDelegatingHandler .
|
|
||||||
/// </summary>
|
|
||||||
IHttpClientBuilder WithQos(IQoSProvider qosProvider, IOcelotLogger logger);
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates the <see cref="HttpClient"/>
|
/// Creates the <see cref="HttpClient"/>
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="useCookies">Defines if http client should use cookie container</param>
|
/// <param name="request"></param>
|
||||||
/// <param name="allowAutoRedirect">Defines if http client should allow auto redirect</param>
|
IHttpClient Create(Request.Request request);
|
||||||
IHttpClient Create(bool useCookies, bool allowAutoRedirect, bool isTracing, IServiceProvider provider);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,16 @@ using Butterfly.OpenTracing;
|
|||||||
|
|
||||||
namespace Ocelot.Requester
|
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 readonly IServiceTracer _tracer;
|
||||||
private const string prefix_spanId = "ot-spanId";
|
private const string prefix_spanId = "ot-spanId";
|
||||||
|
@ -16,10 +16,10 @@ namespace Ocelot.Requester
|
|||||||
|
|
||||||
public PollyCircuitBreakingDelegatingHandler(
|
public PollyCircuitBreakingDelegatingHandler(
|
||||||
IQoSProvider qoSProvider,
|
IQoSProvider qoSProvider,
|
||||||
IOcelotLogger logger)
|
IOcelotLoggerFactory loggerFactory)
|
||||||
{
|
{
|
||||||
_qoSProvider = qoSProvider;
|
_qoSProvider = qoSProvider;
|
||||||
_logger = logger;
|
_logger = loggerFactory.CreateLogger<PollyCircuitBreakingDelegatingHandler>();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
|
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 System.Threading.Tasks;
|
||||||
using Consul;
|
using Consul;
|
||||||
using Ocelot.Infrastructure.Extensions;
|
using Ocelot.Infrastructure.Extensions;
|
||||||
|
using Ocelot.Logging;
|
||||||
using Ocelot.Values;
|
using Ocelot.Values;
|
||||||
|
|
||||||
namespace Ocelot.ServiceDiscovery
|
namespace Ocelot.ServiceDiscovery
|
||||||
@ -11,13 +12,18 @@ namespace Ocelot.ServiceDiscovery
|
|||||||
public class ConsulServiceDiscoveryProvider : IServiceDiscoveryProvider
|
public class ConsulServiceDiscoveryProvider : IServiceDiscoveryProvider
|
||||||
{
|
{
|
||||||
private readonly ConsulRegistryConfiguration _consulConfig;
|
private readonly ConsulRegistryConfiguration _consulConfig;
|
||||||
|
private readonly IOcelotLogger _logger;
|
||||||
private readonly ConsulClient _consul;
|
private readonly ConsulClient _consul;
|
||||||
private const string VersionPrefix = "version-";
|
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 consulHost = string.IsNullOrEmpty(consulRegistryConfiguration?.HostName) ? "localhost" : consulRegistryConfiguration.HostName;
|
||||||
|
|
||||||
var consulPort = consulRegistryConfiguration?.Port ?? 8500;
|
var consulPort = consulRegistryConfiguration?.Port ?? 8500;
|
||||||
|
|
||||||
_consulConfig = new ConsulRegistryConfiguration(consulHost, consulPort, consulRegistryConfiguration?.KeyOfServiceInConsul);
|
_consulConfig = new ConsulRegistryConfiguration(consulHost, consulPort, consulRegistryConfiguration?.KeyOfServiceInConsul);
|
||||||
|
|
||||||
_consul = new ConsulClient(config =>
|
_consul = new ConsulClient(config =>
|
||||||
@ -30,7 +36,19 @@ namespace Ocelot.ServiceDiscovery
|
|||||||
{
|
{
|
||||||
var queryResult = await _consul.Health.Service(_consulConfig.KeyOfServiceInConsul, string.Empty, true);
|
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();
|
return services.ToList();
|
||||||
}
|
}
|
||||||
@ -45,6 +63,16 @@ namespace Ocelot.ServiceDiscovery
|
|||||||
serviceEntry.Service.Tags ?? Enumerable.Empty<string>());
|
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)
|
private string GetVersionFromStrings(IEnumerable<string> strings)
|
||||||
{
|
{
|
||||||
return strings
|
return strings
|
||||||
|
@ -1,11 +1,19 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using Ocelot.Configuration;
|
using Ocelot.Configuration;
|
||||||
|
using Ocelot.Logging;
|
||||||
using Ocelot.Values;
|
using Ocelot.Values;
|
||||||
|
|
||||||
namespace Ocelot.ServiceDiscovery
|
namespace Ocelot.ServiceDiscovery
|
||||||
{
|
{
|
||||||
public class ServiceDiscoveryProviderFactory : IServiceDiscoveryProviderFactory
|
public class ServiceDiscoveryProviderFactory : IServiceDiscoveryProviderFactory
|
||||||
{
|
{
|
||||||
|
private readonly IOcelotLoggerFactory _factory;
|
||||||
|
|
||||||
|
public ServiceDiscoveryProviderFactory(IOcelotLoggerFactory factory)
|
||||||
|
{
|
||||||
|
_factory = factory;
|
||||||
|
}
|
||||||
|
|
||||||
public IServiceDiscoveryProvider Get(ServiceProviderConfiguration serviceConfig, ReRoute reRoute)
|
public IServiceDiscoveryProvider Get(ServiceProviderConfiguration serviceConfig, ReRoute reRoute)
|
||||||
{
|
{
|
||||||
if (reRoute.UseServiceDiscovery)
|
if (reRoute.UseServiceDiscovery)
|
||||||
@ -28,7 +36,7 @@ namespace Ocelot.ServiceDiscovery
|
|||||||
private IServiceDiscoveryProvider GetServiceDiscoveryProvider(string keyOfServiceInConsul, string providerHostName, int providerPort)
|
private IServiceDiscoveryProvider GetServiceDiscoveryProvider(string keyOfServiceInConsul, string providerHostName, int providerPort)
|
||||||
{
|
{
|
||||||
var consulRegistryConfiguration = new ConsulRegistryConfiguration(providerHostName, providerPort, keyOfServiceInConsul);
|
var consulRegistryConfiguration = new ConsulRegistryConfiguration(providerHostName, providerPort, keyOfServiceInConsul);
|
||||||
return new ConsulServiceDiscoveryProvider(consulRegistryConfiguration);
|
return new ConsulServiceDiscoveryProvider(consulRegistryConfiguration, _factory);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
116
test/Ocelot.AcceptanceTests/HttpDelegatingHandlersTests.cs
Normal file
116
test/Ocelot.AcceptanceTests/HttpDelegatingHandlersTests.cs
Normal file
@ -0,0 +1,116 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Net;
|
||||||
|
using System.Net.Http;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.AspNetCore.Builder;
|
||||||
|
using Microsoft.AspNetCore.Hosting;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using Ocelot.Configuration.File;
|
||||||
|
using Shouldly;
|
||||||
|
using TestStack.BDDfy;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Ocelot.AcceptanceTests
|
||||||
|
{
|
||||||
|
public class HttpDelegatingHandlersTests
|
||||||
|
{
|
||||||
|
private IWebHost _builder;
|
||||||
|
private readonly Steps _steps;
|
||||||
|
private string _downstreamPath;
|
||||||
|
|
||||||
|
public HttpDelegatingHandlersTests()
|
||||||
|
{
|
||||||
|
_steps = new Steps();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void should_call_handlers()
|
||||||
|
{
|
||||||
|
var configuration = new FileConfiguration
|
||||||
|
{
|
||||||
|
ReRoutes = new List<FileReRoute>
|
||||||
|
{
|
||||||
|
new FileReRoute
|
||||||
|
{
|
||||||
|
DownstreamPathTemplate = "/",
|
||||||
|
DownstreamScheme = "http",
|
||||||
|
DownstreamHostAndPorts = new List<FileHostAndPort>
|
||||||
|
{
|
||||||
|
new FileHostAndPort
|
||||||
|
{
|
||||||
|
Host = "localhost",
|
||||||
|
Port = 61879,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
UpstreamPathTemplate = "/",
|
||||||
|
UpstreamHttpMethod = new List<string> { "Get" },
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var handlerOne = new FakeHandler();
|
||||||
|
var handlerTwo = new FakeHandler();
|
||||||
|
|
||||||
|
this.Given(x => x.GivenThereIsAServiceRunningOn("http://localhost:61879", "/", 200, "Hello from Laura"))
|
||||||
|
.And(x => _steps.GivenThereIsAConfiguration(configuration))
|
||||||
|
.And(x => _steps.GivenOcelotIsRunningWithHandlers(handlerOne, handlerTwo))
|
||||||
|
.When(x => _steps.WhenIGetUrlOnTheApiGateway("/"))
|
||||||
|
.Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK))
|
||||||
|
.And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura"))
|
||||||
|
.And(x => ThenTheHandlersAreCalledCorrectly(handlerOne, handlerTwo))
|
||||||
|
.BDDfy();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ThenTheHandlersAreCalledCorrectly(FakeHandler one, FakeHandler two)
|
||||||
|
{
|
||||||
|
one.TimeCalled.ShouldBeLessThan(two.TimeCalled);
|
||||||
|
}
|
||||||
|
|
||||||
|
class FakeHandler : DelegatingHandler
|
||||||
|
{
|
||||||
|
|
||||||
|
public DateTime TimeCalled { get; private set; }
|
||||||
|
|
||||||
|
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
TimeCalled = DateTime.Now;
|
||||||
|
return await base.SendAsync(request, cancellationToken);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void GivenThereIsAServiceRunningOn(string baseUrl, string basePath, int statusCode, string responseBody)
|
||||||
|
{
|
||||||
|
_builder = new WebHostBuilder()
|
||||||
|
.UseUrls(baseUrl)
|
||||||
|
.UseKestrel()
|
||||||
|
.UseContentRoot(Directory.GetCurrentDirectory())
|
||||||
|
.UseIISIntegration()
|
||||||
|
.Configure(app =>
|
||||||
|
{
|
||||||
|
app.UsePathBase(basePath);
|
||||||
|
app.Run(async context =>
|
||||||
|
{
|
||||||
|
_downstreamPath = !string.IsNullOrEmpty(context.Request.PathBase.Value) ? context.Request.PathBase.Value : context.Request.Path.Value;
|
||||||
|
|
||||||
|
if (_downstreamPath != basePath)
|
||||||
|
{
|
||||||
|
context.Response.StatusCode = statusCode;
|
||||||
|
await context.Response.WriteAsync("downstream path didnt match base path");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
context.Response.StatusCode = statusCode;
|
||||||
|
await context.Response.WriteAsync(responseBody);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
_builder.Start();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -88,6 +88,38 @@ namespace Ocelot.AcceptanceTests
|
|||||||
_ocelotClient = _ocelotServer.CreateClient();
|
_ocelotClient = _ocelotServer.CreateClient();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// This is annoying cos it should be in the constructor but we need to set up the file before calling startup so its a step.
|
||||||
|
/// </summary>
|
||||||
|
public void GivenOcelotIsRunningWithHandlers(DelegatingHandler handlerOne, DelegatingHandler handlerTwo)
|
||||||
|
{
|
||||||
|
_webHostBuilder = new WebHostBuilder();
|
||||||
|
|
||||||
|
_webHostBuilder.ConfigureServices(s =>
|
||||||
|
{
|
||||||
|
s.AddSingleton(_webHostBuilder);
|
||||||
|
s.AddOcelot()
|
||||||
|
.AddDelegatingHandler(() => handlerOne)
|
||||||
|
.AddDelegatingHandler(() => handlerTwo);
|
||||||
|
});
|
||||||
|
_webHostBuilder.ConfigureAppConfiguration((hostingContext, config) =>
|
||||||
|
{
|
||||||
|
config.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath);
|
||||||
|
var env = hostingContext.HostingEnvironment;
|
||||||
|
config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
|
||||||
|
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: true);
|
||||||
|
config.AddJsonFile("configuration.json");
|
||||||
|
config.AddEnvironmentVariables();
|
||||||
|
}).Configure(a =>
|
||||||
|
{
|
||||||
|
a.UseOcelot().Wait();
|
||||||
|
});
|
||||||
|
|
||||||
|
_ocelotServer = new TestServer(_webHostBuilder);
|
||||||
|
|
||||||
|
_ocelotClient = _ocelotServer.CreateClient();
|
||||||
|
}
|
||||||
|
|
||||||
/// <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>
|
||||||
|
@ -1,13 +1,7 @@
|
|||||||
using System;
|
|
||||||
using CacheManager.Core;
|
|
||||||
using Microsoft.AspNetCore.Builder;
|
using Microsoft.AspNetCore.Builder;
|
||||||
using Microsoft.AspNetCore.Hosting;
|
|
||||||
using Microsoft.Extensions.Configuration;
|
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Microsoft.Extensions.Logging;
|
|
||||||
using Ocelot.DependencyInjection;
|
using Ocelot.DependencyInjection;
|
||||||
using Ocelot.Middleware;
|
using Ocelot.Middleware;
|
||||||
using ConfigurationBuilder = Microsoft.Extensions.Configuration.ConfigurationBuilder;
|
|
||||||
|
|
||||||
namespace Ocelot.ManualTest
|
namespace Ocelot.ManualTest
|
||||||
{
|
{
|
||||||
@ -15,11 +9,6 @@ namespace Ocelot.ManualTest
|
|||||||
{
|
{
|
||||||
public void ConfigureServices(IServiceCollection services)
|
public void ConfigureServices(IServiceCollection services)
|
||||||
{
|
{
|
||||||
Action<ConfigurationBuilderCachePart> settings = (x) =>
|
|
||||||
{
|
|
||||||
x.WithDictionaryHandle();
|
|
||||||
};
|
|
||||||
|
|
||||||
services.AddAuthentication()
|
services.AddAuthentication()
|
||||||
.AddJwtBearer("TestKey", x =>
|
.AddJwtBearer("TestKey", x =>
|
||||||
{
|
{
|
||||||
@ -28,7 +17,10 @@ namespace Ocelot.ManualTest
|
|||||||
});
|
});
|
||||||
|
|
||||||
services.AddOcelot()
|
services.AddOcelot()
|
||||||
.AddCacheManager(settings)
|
.AddCacheManager(x =>
|
||||||
|
{
|
||||||
|
x.WithDictionaryHandle();
|
||||||
|
})
|
||||||
.AddOpenTracing(option =>
|
.AddOpenTracing(option =>
|
||||||
{
|
{
|
||||||
option.CollectorUrl = "http://localhost:9618";
|
option.CollectorUrl = "http://localhost:9618";
|
||||||
|
@ -1,4 +1,8 @@
|
|||||||
using CacheManager.Core;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Net.Http;
|
||||||
|
using CacheManager.Core;
|
||||||
using Microsoft.AspNetCore.Hosting;
|
using Microsoft.AspNetCore.Hosting;
|
||||||
using Microsoft.AspNetCore.Hosting.Internal;
|
using Microsoft.AspNetCore.Hosting.Internal;
|
||||||
using Microsoft.Extensions.Configuration;
|
using Microsoft.Extensions.Configuration;
|
||||||
@ -9,10 +13,9 @@ using Ocelot.Configuration.File;
|
|||||||
using Ocelot.Configuration.Setter;
|
using Ocelot.Configuration.Setter;
|
||||||
using Ocelot.DependencyInjection;
|
using Ocelot.DependencyInjection;
|
||||||
using Ocelot.Requester;
|
using Ocelot.Requester;
|
||||||
|
using Ocelot.UnitTests.Requester;
|
||||||
using Shouldly;
|
using Shouldly;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using TestStack.BDDfy;
|
using TestStack.BDDfy;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
@ -20,11 +23,11 @@ namespace Ocelot.UnitTests.DependencyInjection
|
|||||||
{
|
{
|
||||||
public class OcelotBuilderTests
|
public class OcelotBuilderTests
|
||||||
{
|
{
|
||||||
private IServiceCollection _services;
|
private readonly IServiceCollection _services;
|
||||||
private IServiceProvider _serviceProvider;
|
private IServiceProvider _serviceProvider;
|
||||||
private IConfiguration _configRoot;
|
private readonly IConfiguration _configRoot;
|
||||||
private IOcelotBuilder _ocelotBuilder;
|
private IOcelotBuilder _ocelotBuilder;
|
||||||
private int _maxRetries;
|
private readonly int _maxRetries;
|
||||||
|
|
||||||
public OcelotBuilderTests()
|
public OcelotBuilderTests()
|
||||||
{
|
{
|
||||||
@ -38,6 +41,19 @@ namespace Ocelot.UnitTests.DependencyInjection
|
|||||||
}
|
}
|
||||||
private Exception _ex;
|
private Exception _ex;
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void should_add_delegating_handlers()
|
||||||
|
{
|
||||||
|
var fakeOne = new FakeDelegatingHandler(0);
|
||||||
|
var fakeTwo = new FakeDelegatingHandler(1);
|
||||||
|
|
||||||
|
this.Given(x => WhenISetUpOcelotServices())
|
||||||
|
.When(x => AddDelegate(fakeOne))
|
||||||
|
.And(x => AddDelegate(fakeTwo))
|
||||||
|
.Then(x => ThenTheProviderIsRegisteredAndReturnsHandlers())
|
||||||
|
.BDDfy();
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void should_set_up_services()
|
public void should_set_up_services()
|
||||||
{
|
{
|
||||||
@ -74,7 +90,7 @@ namespace Ocelot.UnitTests.DependencyInjection
|
|||||||
.BDDfy();
|
.BDDfy();
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void should_set_up_rafty()
|
public void should_set_up_rafty()
|
||||||
{
|
{
|
||||||
this.Given(x => WhenISetUpOcelotServices())
|
this.Given(x => WhenISetUpOcelotServices())
|
||||||
@ -119,6 +135,17 @@ namespace Ocelot.UnitTests.DependencyInjection
|
|||||||
path.Path.ShouldBe("/administration");
|
path.Path.ShouldBe("/administration");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void ThenTheProviderIsRegisteredAndReturnsHandlers()
|
||||||
|
{
|
||||||
|
_serviceProvider = _services.BuildServiceProvider();
|
||||||
|
var provider = _serviceProvider.GetService<IDelegatingHandlerHandlerProvider>();
|
||||||
|
var handlers = provider.Get();
|
||||||
|
var handler = (FakeDelegatingHandler)handlers[0].Invoke();
|
||||||
|
handler.Order.ShouldBe(0);
|
||||||
|
handler = (FakeDelegatingHandler)handlers[1].Invoke();
|
||||||
|
handler.Order.ShouldBe(1);
|
||||||
|
}
|
||||||
|
|
||||||
private void OnlyOneVersionOfEachCacheIsRegistered()
|
private void OnlyOneVersionOfEachCacheIsRegistered()
|
||||||
{
|
{
|
||||||
var outputCache = _services.Single(x => x.ServiceType == typeof(IOcelotCache<CachedResponse>));
|
var outputCache = _services.Single(x => x.ServiceType == typeof(IOcelotCache<CachedResponse>));
|
||||||
@ -157,6 +184,11 @@ namespace Ocelot.UnitTests.DependencyInjection
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void AddDelegate(DelegatingHandler handler)
|
||||||
|
{
|
||||||
|
_ocelotBuilder.AddDelegatingHandler(() => handler);
|
||||||
|
}
|
||||||
|
|
||||||
private void ThenAnOcelotBuilderIsReturned()
|
private void ThenAnOcelotBuilderIsReturned()
|
||||||
{
|
{
|
||||||
_ocelotBuilder.ShouldBeOfType<OcelotBuilder>();
|
_ocelotBuilder.ShouldBeOfType<OcelotBuilder>();
|
||||||
|
@ -58,7 +58,7 @@
|
|||||||
this.Given(x => x.GivenTheDownStreamUrlIs("any old string"))
|
this.Given(x => x.GivenTheDownStreamUrlIs("any old string"))
|
||||||
.And(x => x.GivenTheQosProviderHouseReturns(new OkResponse<IQoSProvider>(new NoQoSProvider())))
|
.And(x => x.GivenTheQosProviderHouseReturns(new OkResponse<IQoSProvider>(new NoQoSProvider())))
|
||||||
.And(x => x.GivenTheDownStreamRouteIs(downstreamRoute))
|
.And(x => x.GivenTheDownStreamRouteIs(downstreamRoute))
|
||||||
.And(x => x.GivenTheRequestBuilderReturns(new Ocelot.Request.Request(new HttpRequestMessage(), true, new NoQoSProvider(), false, false,false)))
|
.And(x => x.GivenTheRequestBuilderReturns(new Ocelot.Request.Request(new HttpRequestMessage(), true, new NoQoSProvider(), false, false, "", false)))
|
||||||
.When(x => x.WhenICallTheMiddleware())
|
.When(x => x.WhenICallTheMiddleware())
|
||||||
.Then(x => x.ThenTheScopedDataRepositoryIsCalledCorrectly())
|
.Then(x => x.ThenTheScopedDataRepositoryIsCalledCorrectly())
|
||||||
.BDDfy();
|
.BDDfy();
|
||||||
@ -78,7 +78,7 @@
|
|||||||
this.Given(x => x.GivenTheDownStreamUrlIs("any old string"))
|
this.Given(x => x.GivenTheDownStreamUrlIs("any old string"))
|
||||||
.And(x => x.GivenTheQosProviderHouseReturns(new ErrorResponse<IQoSProvider>(It.IsAny<Error>())))
|
.And(x => x.GivenTheQosProviderHouseReturns(new ErrorResponse<IQoSProvider>(It.IsAny<Error>())))
|
||||||
.And(x => x.GivenTheDownStreamRouteIs(downstreamRoute))
|
.And(x => x.GivenTheDownStreamRouteIs(downstreamRoute))
|
||||||
.And(x => x.GivenTheRequestBuilderReturns(new Ocelot.Request.Request(new HttpRequestMessage(), true, new NoQoSProvider(), false, false, false)))
|
.And(x => x.GivenTheRequestBuilderReturns(new Ocelot.Request.Request(new HttpRequestMessage(), true, new NoQoSProvider(), false, false, "", false)))
|
||||||
.When(x => x.WhenICallTheMiddleware())
|
.When(x => x.WhenICallTheMiddleware())
|
||||||
.Then(x => x.ThenTheScopedDataRepositoryQosProviderError())
|
.Then(x => x.ThenTheScopedDataRepositoryQosProviderError())
|
||||||
.BDDfy();
|
.BDDfy();
|
||||||
@ -132,8 +132,8 @@
|
|||||||
It.IsAny<IQoSProvider>(),
|
It.IsAny<IQoSProvider>(),
|
||||||
It.IsAny<bool>(),
|
It.IsAny<bool>(),
|
||||||
It.IsAny<bool>(),
|
It.IsAny<bool>(),
|
||||||
It.IsAny<bool>()
|
It.IsAny<string>(),
|
||||||
))
|
It.IsAny<bool>()))
|
||||||
.ReturnsAsync(_request);
|
.ReturnsAsync(_request);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
namespace Ocelot.UnitTests.Request
|
namespace Ocelot.UnitTests.Request
|
||||||
{
|
{
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
|
|
||||||
@ -17,9 +17,9 @@
|
|||||||
private readonly HttpRequestMessage _requestMessage;
|
private readonly HttpRequestMessage _requestMessage;
|
||||||
private readonly bool _useCookieContainer;
|
private readonly bool _useCookieContainer;
|
||||||
private readonly bool _allowAutoRedirect;
|
private readonly bool _allowAutoRedirect;
|
||||||
private readonly bool _useTracing;
|
|
||||||
|
|
||||||
private Response<Ocelot.Request.Request> _response;
|
private Response<Ocelot.Request.Request> _response;
|
||||||
|
private string _reRouteKey;
|
||||||
|
private readonly bool _useTracing;
|
||||||
|
|
||||||
public HttpRequestCreatorTests()
|
public HttpRequestCreatorTests()
|
||||||
{
|
{
|
||||||
@ -28,7 +28,7 @@
|
|||||||
_qoSProvider = new NoQoSProvider();
|
_qoSProvider = new NoQoSProvider();
|
||||||
_useCookieContainer = false;
|
_useCookieContainer = false;
|
||||||
_allowAutoRedirect = false;
|
_allowAutoRedirect = false;
|
||||||
_useTracing = false;
|
|
||||||
_requestMessage = new HttpRequestMessage();
|
_requestMessage = new HttpRequestMessage();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -48,7 +48,7 @@
|
|||||||
private void WhenIBuildARequest()
|
private void WhenIBuildARequest()
|
||||||
{
|
{
|
||||||
_response = _requestCreator.Build(_requestMessage,
|
_response = _requestCreator.Build(_requestMessage,
|
||||||
_isQos, _qoSProvider, _useCookieContainer, _allowAutoRedirect, _useTracing)
|
_isQos, _qoSProvider, _useCookieContainer, _allowAutoRedirect, _reRouteKey, _useTracing)
|
||||||
.GetAwaiter()
|
.GetAwaiter()
|
||||||
.GetResult();
|
.GetResult();
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,108 @@
|
|||||||
|
using System;
|
||||||
|
using System.Net.Http;
|
||||||
|
using Moq;
|
||||||
|
using Ocelot.Requester;
|
||||||
|
using Ocelot.Responses;
|
||||||
|
using Shouldly;
|
||||||
|
using TestStack.BDDfy;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Ocelot.UnitTests.Requester
|
||||||
|
{
|
||||||
|
public class DelegatingHandlerHandlerHouseTests
|
||||||
|
{
|
||||||
|
private readonly DelegatingHandlerHandlerHouse _house;
|
||||||
|
private Mock<IDelegatingHandlerHandlerProviderFactory> _factory;
|
||||||
|
private readonly Mock<IDelegatingHandlerHandlerProvider> _provider;
|
||||||
|
private Ocelot.Request.Request _request;
|
||||||
|
private Response<IDelegatingHandlerHandlerProvider> _result;
|
||||||
|
|
||||||
|
public DelegatingHandlerHandlerHouseTests()
|
||||||
|
{
|
||||||
|
_provider = new Mock<IDelegatingHandlerHandlerProvider>();
|
||||||
|
_factory = new Mock<IDelegatingHandlerHandlerProviderFactory>();
|
||||||
|
_house = new DelegatingHandlerHandlerHouse(_factory.Object);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void should_create_and_store_provider()
|
||||||
|
{
|
||||||
|
var request = new Ocelot.Request.Request(new HttpRequestMessage(), true, null, true, true, "key", false);
|
||||||
|
|
||||||
|
this.Given(x => GivenTheRequest(request))
|
||||||
|
.And(x => GivenTheProviderReturns())
|
||||||
|
.When(x => WhenIGet())
|
||||||
|
.Then(x => ThenTheFactoryIsCalled(1))
|
||||||
|
.And(x => ThenTheProviderIsNotNull())
|
||||||
|
.BDDfy();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void should_get_provider()
|
||||||
|
{
|
||||||
|
var request = new Ocelot.Request.Request(new HttpRequestMessage(), true, null, true, true, "key", false);
|
||||||
|
|
||||||
|
this.Given(x => GivenTheRequest(request))
|
||||||
|
.And(x => GivenTheProviderReturns())
|
||||||
|
.And(x => WhenIGet())
|
||||||
|
.And(x => GivenTheFactoryIsCleared())
|
||||||
|
.When(x => WhenIGet())
|
||||||
|
.Then(x => ThenTheFactoryIsCalled(0))
|
||||||
|
.And(x => ThenTheProviderIsNotNull())
|
||||||
|
.BDDfy();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void should_return_error()
|
||||||
|
{
|
||||||
|
var request = new Ocelot.Request.Request(new HttpRequestMessage(), true, null, true, true, "key", false);
|
||||||
|
|
||||||
|
this.Given(x => GivenTheRequest(request))
|
||||||
|
.And(x => GivenTheProviderThrows())
|
||||||
|
.When(x => WhenIGet())
|
||||||
|
.And(x => ThenAnErrorIsReturned())
|
||||||
|
.BDDfy();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ThenAnErrorIsReturned()
|
||||||
|
{
|
||||||
|
_result.IsError.ShouldBeTrue();
|
||||||
|
_result.Errors[0].ShouldBeOfType<UnableToFindDelegatingHandlerProviderError>();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void GivenTheProviderThrows()
|
||||||
|
{
|
||||||
|
_factory.Setup(x => x.Get(It.IsAny<Ocelot.Request.Request>())).Throws<Exception>();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void GivenTheFactoryIsCleared()
|
||||||
|
{
|
||||||
|
_factory = new Mock<IDelegatingHandlerHandlerProviderFactory>();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ThenTheProviderIsNotNull()
|
||||||
|
{
|
||||||
|
_result.Data.ShouldBe(_provider.Object);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void WhenIGet()
|
||||||
|
{
|
||||||
|
_result = _house.Get(_request);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void GivenTheRequest(Ocelot.Request.Request request)
|
||||||
|
{
|
||||||
|
_request = request;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void GivenTheProviderReturns()
|
||||||
|
{
|
||||||
|
_factory.Setup(x => x.Get(It.IsAny<Ocelot.Request.Request>())).Returns(_provider.Object);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ThenTheFactoryIsCalled(int times)
|
||||||
|
{
|
||||||
|
_factory.Verify(x => x.Get(_request), Times.Exactly(times));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,124 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Net.Http;
|
||||||
|
using Moq;
|
||||||
|
using Ocelot.Logging;
|
||||||
|
using Ocelot.Requester;
|
||||||
|
using Shouldly;
|
||||||
|
using TestStack.BDDfy;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Ocelot.UnitTests.Requester
|
||||||
|
{
|
||||||
|
public class DelegatingHandlerHandlerProviderFactoryTests
|
||||||
|
{
|
||||||
|
private readonly DelegatingHandlerHandlerProviderFactory _factory;
|
||||||
|
private Mock<IOcelotLoggerFactory> _loggerFactory;
|
||||||
|
private Ocelot.Request.Request _request;
|
||||||
|
private IDelegatingHandlerHandlerProvider _provider;
|
||||||
|
private readonly Mock<IDelegatingHandlerHandlerProvider> _allRoutesProvider;
|
||||||
|
|
||||||
|
public DelegatingHandlerHandlerProviderFactoryTests()
|
||||||
|
{
|
||||||
|
_allRoutesProvider = new Mock<IDelegatingHandlerHandlerProvider>();
|
||||||
|
_loggerFactory = new Mock<IOcelotLoggerFactory>();
|
||||||
|
_factory = new DelegatingHandlerHandlerProviderFactory(_loggerFactory.Object, _allRoutesProvider.Object, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void should_all_from_all_routes_provider_and_qos()
|
||||||
|
{
|
||||||
|
var handlers = new List<Func<DelegatingHandler>>
|
||||||
|
{
|
||||||
|
() => new FakeDelegatingHandler(0),
|
||||||
|
() => new FakeDelegatingHandler(1)
|
||||||
|
};
|
||||||
|
|
||||||
|
var request = new Ocelot.Request.Request(new HttpRequestMessage(), true, null, true, true, "", false);
|
||||||
|
|
||||||
|
this.Given(x => GivenTheFollowingRequest(request))
|
||||||
|
.And(x => GivenTheAllRoutesProviderReturns(handlers))
|
||||||
|
.When(x => WhenIGet())
|
||||||
|
.Then(x => ThenThereIsDelegatesInProvider(3))
|
||||||
|
.And(x => ThenTheDelegatesAreAddedCorrectly())
|
||||||
|
.And(x => ThenItIsPolly(2))
|
||||||
|
.BDDfy();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void should_return_provider_with_no_delegates()
|
||||||
|
{
|
||||||
|
var request = new Ocelot.Request.Request(new HttpRequestMessage(), false, null, true, true, "", false);
|
||||||
|
|
||||||
|
this.Given(x => GivenTheFollowingRequest(request))
|
||||||
|
.And(x => GivenTheAllRoutesProviderReturns())
|
||||||
|
.When(x => WhenIGet())
|
||||||
|
.Then(x => ThenNoDelegatesAreInTheProvider())
|
||||||
|
.BDDfy();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void should_return_provider_with_qos_delegate()
|
||||||
|
{
|
||||||
|
var request = new Ocelot.Request.Request(new HttpRequestMessage(), true, null, true, true, "", false);
|
||||||
|
|
||||||
|
this.Given(x => GivenTheFollowingRequest(request))
|
||||||
|
.And(x => GivenTheAllRoutesProviderReturns())
|
||||||
|
.When(x => WhenIGet())
|
||||||
|
.Then(x => ThenThereIsDelegatesInProvider(1))
|
||||||
|
.And(x => ThenItIsPolly(0))
|
||||||
|
.BDDfy();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ThenTheDelegatesAreAddedCorrectly()
|
||||||
|
{
|
||||||
|
var delegates = _provider.Get();
|
||||||
|
var del = delegates[0].Invoke();
|
||||||
|
var handler = (FakeDelegatingHandler) del;
|
||||||
|
handler.Order.ShouldBe(0);
|
||||||
|
|
||||||
|
del = delegates[1].Invoke();
|
||||||
|
handler = (FakeDelegatingHandler)del;
|
||||||
|
handler.Order.ShouldBe(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void GivenTheAllRoutesProviderReturns()
|
||||||
|
{
|
||||||
|
_allRoutesProvider.Setup(x => x.Get()).Returns(new List<Func<DelegatingHandler>>());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void GivenTheAllRoutesProviderReturns(List<Func<DelegatingHandler>> handlers)
|
||||||
|
{
|
||||||
|
_allRoutesProvider.Setup(x => x.Get()).Returns(handlers);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ThenItIsPolly(int i)
|
||||||
|
{
|
||||||
|
var delegates = _provider.Get();
|
||||||
|
var del = delegates[i].Invoke();
|
||||||
|
del.ShouldBeOfType<PollyCircuitBreakingDelegatingHandler>();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ThenThereIsDelegatesInProvider(int count)
|
||||||
|
{
|
||||||
|
_provider.ShouldNotBeNull();
|
||||||
|
_provider.Get().Count.ShouldBe(count);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void GivenTheFollowingRequest(Ocelot.Request.Request request)
|
||||||
|
{
|
||||||
|
_request = request;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void WhenIGet()
|
||||||
|
{
|
||||||
|
_provider = _factory.Get(_request);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ThenNoDelegatesAreInTheProvider()
|
||||||
|
{
|
||||||
|
_provider.ShouldNotBeNull();
|
||||||
|
_provider.Get().Count.ShouldBe(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,62 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Net.Http;
|
||||||
|
using Ocelot.Requester;
|
||||||
|
using Shouldly;
|
||||||
|
using TestStack.BDDfy;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Ocelot.UnitTests.Requester
|
||||||
|
{
|
||||||
|
public class DelegatingHandlerHandlerProviderTests
|
||||||
|
{
|
||||||
|
private readonly DelegatingHandlerHandlerProvider _provider;
|
||||||
|
private List<Func<DelegatingHandler>> _handlers;
|
||||||
|
|
||||||
|
public DelegatingHandlerHandlerProviderTests()
|
||||||
|
{
|
||||||
|
_provider = new DelegatingHandlerHandlerProvider();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void should_return_empty_list()
|
||||||
|
{
|
||||||
|
this.When(x => WhenIGet())
|
||||||
|
.Then(x => ThenAnEmptyListIsReturned())
|
||||||
|
.BDDfy();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void should_get_delegating_handlers_in_order_first_in_first_out()
|
||||||
|
{
|
||||||
|
this.Given(x => GivenTheHandlers())
|
||||||
|
.When(x => WhenIGet())
|
||||||
|
.Then(x => ThenTheHandlersAreReturnedInOrder())
|
||||||
|
.BDDfy();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ThenAnEmptyListIsReturned()
|
||||||
|
{
|
||||||
|
_handlers.Count.ShouldBe(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ThenTheHandlersAreReturnedInOrder()
|
||||||
|
{
|
||||||
|
var handler = (FakeDelegatingHandler)_handlers[0].Invoke();
|
||||||
|
handler.Order.ShouldBe(0);
|
||||||
|
handler = (FakeDelegatingHandler)_handlers[1].Invoke();
|
||||||
|
handler.Order.ShouldBe(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void WhenIGet()
|
||||||
|
{
|
||||||
|
_handlers = _provider.Get();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void GivenTheHandlers()
|
||||||
|
{
|
||||||
|
_provider.Add(() => new FakeDelegatingHandler(0));
|
||||||
|
_provider.Add(() => new FakeDelegatingHandler(1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
28
test/Ocelot.UnitTests/Requester/FakeDelegatingHandler.cs
Normal file
28
test/Ocelot.UnitTests/Requester/FakeDelegatingHandler.cs
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
using System;
|
||||||
|
using System.Net.Http;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Ocelot.UnitTests.Requester
|
||||||
|
{
|
||||||
|
public class FakeDelegatingHandler : DelegatingHandler
|
||||||
|
{
|
||||||
|
public FakeDelegatingHandler()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public FakeDelegatingHandler(int order)
|
||||||
|
{
|
||||||
|
Order = order;
|
||||||
|
}
|
||||||
|
public int Order {get;private set;}
|
||||||
|
public DateTime TimeCalled {get;private set;}
|
||||||
|
|
||||||
|
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
TimeCalled = DateTime.Now;
|
||||||
|
return new HttpResponseMessage();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
114
test/Ocelot.UnitTests/Requester/HttpClientBuilderTests.cs
Normal file
114
test/Ocelot.UnitTests/Requester/HttpClientBuilderTests.cs
Normal file
@ -0,0 +1,114 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Net.Http;
|
||||||
|
using Moq;
|
||||||
|
using Ocelot.Requester;
|
||||||
|
using Ocelot.Responses;
|
||||||
|
using Shouldly;
|
||||||
|
using TestStack.BDDfy;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Ocelot.UnitTests.Requester
|
||||||
|
{
|
||||||
|
public class HttpClientBuilderTests
|
||||||
|
{
|
||||||
|
private readonly HttpClientBuilder _builder;
|
||||||
|
private readonly Mock<IDelegatingHandlerHandlerHouse> _house;
|
||||||
|
private readonly Mock<IDelegatingHandlerHandlerProvider> _provider;
|
||||||
|
private IHttpClientBuilder _builderResult;
|
||||||
|
private IHttpClient _httpClient;
|
||||||
|
private HttpResponseMessage _response;
|
||||||
|
private Ocelot.Request.Request _request;
|
||||||
|
|
||||||
|
public HttpClientBuilderTests()
|
||||||
|
{
|
||||||
|
_provider = new Mock<IDelegatingHandlerHandlerProvider>();
|
||||||
|
_house = new Mock<IDelegatingHandlerHandlerHouse>();
|
||||||
|
_builder = new HttpClientBuilder(_house.Object);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void should_build_http_client()
|
||||||
|
{
|
||||||
|
this.Given(x => GivenTheProviderReturns())
|
||||||
|
.And(x => GivenARequest())
|
||||||
|
.And(x => GivenTheHouseReturns())
|
||||||
|
.When(x => WhenIBuild())
|
||||||
|
.Then(x => ThenTheHttpClientShouldNotBeNull())
|
||||||
|
.BDDfy();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void should_call_delegating_handlers_in_order()
|
||||||
|
{
|
||||||
|
var fakeOne = new FakeDelegatingHandler();
|
||||||
|
var fakeTwo = new FakeDelegatingHandler();
|
||||||
|
|
||||||
|
var handlers = new List<Func<DelegatingHandler>>()
|
||||||
|
{
|
||||||
|
() => fakeOne,
|
||||||
|
() => fakeTwo
|
||||||
|
};
|
||||||
|
|
||||||
|
this.Given(x => GivenTheProviderReturns(handlers))
|
||||||
|
.And(x => GivenARequest())
|
||||||
|
.And(x => GivenTheHouseReturns())
|
||||||
|
.And(x => WhenIBuild())
|
||||||
|
.When(x => WhenICallTheClient())
|
||||||
|
.Then(x => ThenTheFakeAreHandledInOrder(fakeOne, fakeTwo))
|
||||||
|
.And(x => ThenSomethingIsReturned())
|
||||||
|
.BDDfy();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void GivenARequest()
|
||||||
|
{
|
||||||
|
_request = new Ocelot.Request.Request(null, false, null, false, false, "", false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void GivenTheHouseReturns()
|
||||||
|
{
|
||||||
|
_house
|
||||||
|
.Setup(x => x.Get(It.IsAny<Ocelot.Request.Request>()))
|
||||||
|
.Returns(new OkResponse<IDelegatingHandlerHandlerProvider>(_provider.Object));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ThenSomethingIsReturned()
|
||||||
|
{
|
||||||
|
_response.ShouldNotBeNull();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void WhenICallTheClient()
|
||||||
|
{
|
||||||
|
_response = _httpClient.SendAsync(new HttpRequestMessage(HttpMethod.Get, "http://test.com")).GetAwaiter().GetResult();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ThenTheFakeAreHandledInOrder(FakeDelegatingHandler fakeOne, FakeDelegatingHandler fakeTwo)
|
||||||
|
{
|
||||||
|
fakeOne.TimeCalled.ShouldBeGreaterThan(fakeTwo.TimeCalled);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void GivenTheProviderReturns()
|
||||||
|
{
|
||||||
|
_provider
|
||||||
|
.Setup(x => x.Get())
|
||||||
|
.Returns(new List<Func<DelegatingHandler>>(){ () => new FakeDelegatingHandler()});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void GivenTheProviderReturns(List<Func<DelegatingHandler>> handlers)
|
||||||
|
{
|
||||||
|
_provider
|
||||||
|
.Setup(x => x.Get())
|
||||||
|
.Returns(handlers);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void WhenIBuild()
|
||||||
|
{
|
||||||
|
_httpClient = _builder.Create(_request);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ThenTheHttpClientShouldNotBeNull()
|
||||||
|
{
|
||||||
|
_httpClient.ShouldNotBeNull();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -10,13 +10,15 @@ using System.Net.Http;
|
|||||||
using System.Text;
|
using System.Text;
|
||||||
using TestStack.BDDfy;
|
using TestStack.BDDfy;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
using Shouldly;
|
||||||
|
|
||||||
namespace Ocelot.UnitTests.Requester
|
namespace Ocelot.UnitTests.Requester
|
||||||
{
|
{
|
||||||
public class HttpClientHttpRequesterTest
|
public class HttpClientHttpRequesterTest
|
||||||
{
|
{
|
||||||
private readonly Mock<IHttpClientCache> _cacheHandlers;
|
private readonly Mock<IHttpClientCache> _cacheHandlers;
|
||||||
private Mock<IServiceProvider> _serviceProvider;
|
private Mock<IDelegatingHandlerHandlerHouse> _house;
|
||||||
|
private Mock<IDelegatingHandlerHandlerProvider> _provider;
|
||||||
private Response<HttpResponseMessage> _response;
|
private Response<HttpResponseMessage> _response;
|
||||||
private readonly HttpClientHttpRequester _httpClientRequester;
|
private readonly HttpClientHttpRequester _httpClientRequester;
|
||||||
private Ocelot.Request.Request _request;
|
private Ocelot.Request.Request _request;
|
||||||
@ -25,29 +27,32 @@ namespace Ocelot.UnitTests.Requester
|
|||||||
|
|
||||||
public HttpClientHttpRequesterTest()
|
public HttpClientHttpRequesterTest()
|
||||||
{
|
{
|
||||||
_serviceProvider = new Mock<IServiceProvider>();
|
_provider = new Mock<IDelegatingHandlerHandlerProvider>();
|
||||||
|
_provider.Setup(x => x.Get()).Returns(new List<Func<DelegatingHandler>>());
|
||||||
|
_house = new Mock<IDelegatingHandlerHandlerHouse>();
|
||||||
|
_house.Setup(x => x.Get(It.IsAny<Ocelot.Request.Request>())).Returns(new OkResponse<IDelegatingHandlerHandlerProvider>(_provider.Object));
|
||||||
_logger = new Mock<IOcelotLogger>();
|
_logger = new Mock<IOcelotLogger>();
|
||||||
_loggerFactory = new Mock<IOcelotLoggerFactory>();
|
_loggerFactory = new Mock<IOcelotLoggerFactory>();
|
||||||
_loggerFactory
|
_loggerFactory
|
||||||
.Setup(x => x.CreateLogger<HttpClientHttpRequester>())
|
.Setup(x => x.CreateLogger<HttpClientHttpRequester>())
|
||||||
.Returns(_logger.Object);
|
.Returns(_logger.Object);
|
||||||
_cacheHandlers = new Mock<IHttpClientCache>();
|
_cacheHandlers = new Mock<IHttpClientCache>();
|
||||||
_httpClientRequester = new HttpClientHttpRequester(_loggerFactory.Object, _cacheHandlers.Object, _serviceProvider.Object);
|
_httpClientRequester = new HttpClientHttpRequester(_loggerFactory.Object, _cacheHandlers.Object, _house.Object);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void should_call_request_correctly()
|
public void should_call_request_correctly()
|
||||||
{
|
{
|
||||||
this.Given(x=>x.GivenTheRequestIs(new Ocelot.Request.Request(new HttpRequestMessage() { RequestUri = new Uri("http://www.bbc.co.uk") }, false, new NoQoSProvider(), false, false, false)))
|
this.Given(x=>x.GivenTheRequestIs(new Ocelot.Request.Request(new HttpRequestMessage() { RequestUri = new Uri("http://www.bbc.co.uk") }, false, new NoQoSProvider(), false, false, "", false)))
|
||||||
.When(x=>x.WhenIGetResponse())
|
.When(x=>x.WhenIGetResponse())
|
||||||
.Then(x => x.ThenTheResponseIsCalledCorrectly())
|
.Then(x => x.ThenTheResponseIsCalledCorrectly())
|
||||||
.BDDfy();
|
.BDDfy();
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void should_call_request_UnableToCompleteRequest()
|
public void should_call_request_unable_to_complete_request()
|
||||||
{
|
{
|
||||||
this.Given(x => x.GivenTheRequestIs(new Ocelot.Request.Request(new HttpRequestMessage() { RequestUri = new Uri("http://localhost:60080") }, false, new NoQoSProvider(), false, false, false)))
|
this.Given(x => x.GivenTheRequestIs(new Ocelot.Request.Request(new HttpRequestMessage() { RequestUri = new Uri("http://localhost:60080") }, false, new NoQoSProvider(), false, false, "", false)))
|
||||||
.When(x => x.WhenIGetResponse())
|
.When(x => x.WhenIGetResponse())
|
||||||
.Then(x => x.ThenTheResponseIsCalledError())
|
.Then(x => x.ThenTheResponseIsCalledError())
|
||||||
.BDDfy();
|
.BDDfy();
|
||||||
@ -65,12 +70,12 @@ namespace Ocelot.UnitTests.Requester
|
|||||||
|
|
||||||
private void ThenTheResponseIsCalledCorrectly()
|
private void ThenTheResponseIsCalledCorrectly()
|
||||||
{
|
{
|
||||||
Assert.True(_response.IsError == false);
|
_response.IsError.ShouldBeFalse();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ThenTheResponseIsCalledError()
|
private void ThenTheResponseIsCalledError()
|
||||||
{
|
{
|
||||||
Assert.True(_response.IsError == true);
|
_response.IsError.ShouldBeTrue();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
namespace Ocelot.UnitTests.Requester
|
namespace Ocelot.UnitTests.Requester
|
||||||
{
|
{
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
using Microsoft.AspNetCore.Builder;
|
using Microsoft.AspNetCore.Builder;
|
||||||
@ -28,7 +28,7 @@
|
|||||||
[Fact]
|
[Fact]
|
||||||
public void should_call_scoped_data_repository_correctly()
|
public void should_call_scoped_data_repository_correctly()
|
||||||
{
|
{
|
||||||
this.Given(x => x.GivenTheRequestIs(new Ocelot.Request.Request(new HttpRequestMessage(),true, new NoQoSProvider(), false, false,false)))
|
this.Given(x => x.GivenTheRequestIs(new Ocelot.Request.Request(new HttpRequestMessage(),true, new NoQoSProvider(), false, false, "", false)))
|
||||||
.And(x => x.GivenTheRequesterReturns(new HttpResponseMessage()))
|
.And(x => x.GivenTheRequesterReturns(new HttpResponseMessage()))
|
||||||
.And(x => x.GivenTheScopedRepoReturns())
|
.And(x => x.GivenTheScopedRepoReturns())
|
||||||
.When(x => x.WhenICallTheMiddleware())
|
.When(x => x.WhenICallTheMiddleware())
|
||||||
|
@ -1,10 +1,8 @@
|
|||||||
using Moq;
|
using Moq;
|
||||||
using Ocelot.Configuration;
|
using Ocelot.Configuration;
|
||||||
using Ocelot.Configuration.Builder;
|
using Ocelot.Configuration.Builder;
|
||||||
using Ocelot.LoadBalancer.LoadBalancers;
|
|
||||||
using Ocelot.Requester.QoS;
|
using Ocelot.Requester.QoS;
|
||||||
using Ocelot.Responses;
|
using Ocelot.Responses;
|
||||||
using Ocelot.UnitTests.LoadBalancer;
|
|
||||||
using Shouldly;
|
using Shouldly;
|
||||||
using TestStack.BDDfy;
|
using TestStack.BDDfy;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
@ -121,7 +121,7 @@ namespace Ocelot.UnitTests.Responder
|
|||||||
// If this test fails then it's because the number of error codes has changed.
|
// If this test fails then it's because the number of error codes has changed.
|
||||||
// You should make the appropriate changes to the test cases here to ensure
|
// You should make the appropriate changes to the test cases here to ensure
|
||||||
// they cover all the error codes, and then modify this assertion.
|
// they cover all the error codes, and then modify this assertion.
|
||||||
Enum.GetNames(typeof(OcelotErrorCode)).Length.ShouldBe(32, "Looks like the number of error codes has changed. Do you need to modify ErrorsToHttpStatusCodeMapper?");
|
Enum.GetNames(typeof(OcelotErrorCode)).Length.ShouldBe(33, "Looks like the number of error codes has changed. Do you need to modify ErrorsToHttpStatusCodeMapper?");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ShouldMapErrorToStatusCode(OcelotErrorCode errorCode, HttpStatusCode expectedHttpStatusCode)
|
private void ShouldMapErrorToStatusCode(OcelotErrorCode errorCode, HttpStatusCode expectedHttpStatusCode)
|
||||||
|
@ -0,0 +1,212 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Text;
|
||||||
|
using Consul;
|
||||||
|
using Microsoft.AspNetCore.Builder;
|
||||||
|
using Microsoft.AspNetCore.Hosting;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using Moq;
|
||||||
|
using Ocelot.Logging;
|
||||||
|
using Ocelot.ServiceDiscovery;
|
||||||
|
using Ocelot.Values;
|
||||||
|
using Xunit;
|
||||||
|
using TestStack.BDDfy;
|
||||||
|
using Shouldly;
|
||||||
|
|
||||||
|
namespace Ocelot.UnitTests.ServiceDiscovery
|
||||||
|
{
|
||||||
|
public class ConsulServiceDiscoveryProviderTests : IDisposable
|
||||||
|
{
|
||||||
|
private IWebHost _fakeConsulBuilder;
|
||||||
|
private readonly List<ServiceEntry> _serviceEntries;
|
||||||
|
private readonly ConsulServiceDiscoveryProvider _provider;
|
||||||
|
private readonly string _serviceName;
|
||||||
|
private readonly int _port;
|
||||||
|
private readonly string _consulHost;
|
||||||
|
private readonly string _fakeConsulServiceDiscoveryUrl;
|
||||||
|
private List<Service> _services;
|
||||||
|
private Mock<IOcelotLoggerFactory> _factory;
|
||||||
|
private readonly Mock<IOcelotLogger> _logger;
|
||||||
|
|
||||||
|
public ConsulServiceDiscoveryProviderTests()
|
||||||
|
{
|
||||||
|
_serviceName = "test";
|
||||||
|
_port = 8500;
|
||||||
|
_consulHost = "localhost";
|
||||||
|
_fakeConsulServiceDiscoveryUrl = $"http://{_consulHost}:{_port}";
|
||||||
|
_serviceEntries = new List<ServiceEntry>();
|
||||||
|
|
||||||
|
_factory = new Mock<IOcelotLoggerFactory>();
|
||||||
|
_logger = new Mock<IOcelotLogger>();
|
||||||
|
_factory.Setup(x => x.CreateLogger<ConsulServiceDiscoveryProvider>()).Returns(_logger.Object);
|
||||||
|
|
||||||
|
var config = new ConsulRegistryConfiguration(_consulHost, _port, _serviceName);
|
||||||
|
_provider = new ConsulServiceDiscoveryProvider(config, _factory.Object);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void should_return_service_from_consul()
|
||||||
|
{
|
||||||
|
var serviceEntryOne = new ServiceEntry()
|
||||||
|
{
|
||||||
|
Service = new AgentService()
|
||||||
|
{
|
||||||
|
Service = _serviceName,
|
||||||
|
Address = "localhost",
|
||||||
|
Port = 50881,
|
||||||
|
ID = Guid.NewGuid().ToString(),
|
||||||
|
Tags = new string[0]
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
this.Given(x =>GivenThereIsAFakeConsulServiceDiscoveryProvider(_fakeConsulServiceDiscoveryUrl, _serviceName))
|
||||||
|
.And(x => GivenTheServicesAreRegisteredWithConsul(serviceEntryOne))
|
||||||
|
.When(x => WhenIGetTheServices())
|
||||||
|
.Then(x => ThenTheCountIs(1))
|
||||||
|
.BDDfy();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void should_not_return_services_with_invalid_address()
|
||||||
|
{
|
||||||
|
var serviceEntryOne = new ServiceEntry()
|
||||||
|
{
|
||||||
|
Service = new AgentService()
|
||||||
|
{
|
||||||
|
Service = _serviceName,
|
||||||
|
Address = "http://localhost",
|
||||||
|
Port = 50881,
|
||||||
|
ID = Guid.NewGuid().ToString(),
|
||||||
|
Tags = new string[0]
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
var serviceEntryTwo = new ServiceEntry()
|
||||||
|
{
|
||||||
|
Service = new AgentService()
|
||||||
|
{
|
||||||
|
Service = _serviceName,
|
||||||
|
Address = "http://localhost",
|
||||||
|
Port = 50888,
|
||||||
|
ID = Guid.NewGuid().ToString(),
|
||||||
|
Tags = new string[0]
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
this.Given(x => GivenThereIsAFakeConsulServiceDiscoveryProvider(_fakeConsulServiceDiscoveryUrl, _serviceName))
|
||||||
|
.And(x => GivenTheServicesAreRegisteredWithConsul(serviceEntryOne, serviceEntryTwo))
|
||||||
|
.When(x => WhenIGetTheServices())
|
||||||
|
.Then(x => ThenTheCountIs(0))
|
||||||
|
.And(x => ThenTheLoggerHasBeenCalledCorrectlyForInvalidAddress())
|
||||||
|
.BDDfy();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void should_not_return_services_with_invalid_port()
|
||||||
|
{
|
||||||
|
var serviceEntryOne = new ServiceEntry()
|
||||||
|
{
|
||||||
|
Service = new AgentService()
|
||||||
|
{
|
||||||
|
Service = _serviceName,
|
||||||
|
Address = "localhost",
|
||||||
|
Port = -1,
|
||||||
|
ID = Guid.NewGuid().ToString(),
|
||||||
|
Tags = new string[0]
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
var serviceEntryTwo = new ServiceEntry()
|
||||||
|
{
|
||||||
|
Service = new AgentService()
|
||||||
|
{
|
||||||
|
Service = _serviceName,
|
||||||
|
Address = "localhost",
|
||||||
|
Port = 0,
|
||||||
|
ID = Guid.NewGuid().ToString(),
|
||||||
|
Tags = new string[0]
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
this.Given(x => GivenThereIsAFakeConsulServiceDiscoveryProvider(_fakeConsulServiceDiscoveryUrl, _serviceName))
|
||||||
|
.And(x => GivenTheServicesAreRegisteredWithConsul(serviceEntryOne, serviceEntryTwo))
|
||||||
|
.When(x => WhenIGetTheServices())
|
||||||
|
.Then(x => ThenTheCountIs(0))
|
||||||
|
.And(x => ThenTheLoggerHasBeenCalledCorrectlyForInvalidPorts())
|
||||||
|
.BDDfy();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ThenTheLoggerHasBeenCalledCorrectlyForInvalidAddress()
|
||||||
|
{
|
||||||
|
_logger.Verify(
|
||||||
|
x => x.LogError(
|
||||||
|
"Unable to use service Address: http://localhost and Port: 50881 as it is invalid. Address must contain host only e.g. localhost and port must be greater than 0"),
|
||||||
|
Times.Once);
|
||||||
|
|
||||||
|
_logger.Verify(
|
||||||
|
x => x.LogError(
|
||||||
|
"Unable to use service Address: http://localhost and Port: 50888 as it is invalid. Address must contain host only e.g. localhost and port must be greater than 0"),
|
||||||
|
Times.Once);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ThenTheLoggerHasBeenCalledCorrectlyForInvalidPorts()
|
||||||
|
{
|
||||||
|
_logger.Verify(
|
||||||
|
x => x.LogError(
|
||||||
|
"Unable to use service Address: localhost and Port: -1 as it is invalid. Address must contain host only e.g. localhost and port must be greater than 0"),
|
||||||
|
Times.Once);
|
||||||
|
|
||||||
|
_logger.Verify(
|
||||||
|
x => x.LogError(
|
||||||
|
"Unable to use service Address: localhost and Port: 0 as it is invalid. Address must contain host only e.g. localhost and port must be greater than 0"),
|
||||||
|
Times.Once);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ThenTheCountIs(int count)
|
||||||
|
{
|
||||||
|
_services.Count.ShouldBe(count);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void WhenIGetTheServices()
|
||||||
|
{
|
||||||
|
_services = _provider.Get().GetAwaiter().GetResult();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void GivenTheServicesAreRegisteredWithConsul(params ServiceEntry[] serviceEntries)
|
||||||
|
{
|
||||||
|
foreach (var serviceEntry in serviceEntries)
|
||||||
|
{
|
||||||
|
_serviceEntries.Add(serviceEntry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void GivenThereIsAFakeConsulServiceDiscoveryProvider(string url, string serviceName)
|
||||||
|
{
|
||||||
|
_fakeConsulBuilder = new WebHostBuilder()
|
||||||
|
.UseUrls(url)
|
||||||
|
.UseKestrel()
|
||||||
|
.UseContentRoot(Directory.GetCurrentDirectory())
|
||||||
|
.UseIISIntegration()
|
||||||
|
.UseUrls(url)
|
||||||
|
.Configure(app =>
|
||||||
|
{
|
||||||
|
app.Run(async context =>
|
||||||
|
{
|
||||||
|
if (context.Request.Path.Value == $"/v1/health/service/{serviceName}")
|
||||||
|
{
|
||||||
|
await context.Response.WriteJsonAsync(_serviceEntries);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
_fakeConsulBuilder.Start();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
_fakeConsulBuilder?.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,7 +1,9 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using Moq;
|
||||||
using Ocelot.Configuration;
|
using Ocelot.Configuration;
|
||||||
using Ocelot.Configuration.Builder;
|
using Ocelot.Configuration.Builder;
|
||||||
|
using Ocelot.Logging;
|
||||||
using Ocelot.ServiceDiscovery;
|
using Ocelot.ServiceDiscovery;
|
||||||
using Shouldly;
|
using Shouldly;
|
||||||
using TestStack.BDDfy;
|
using TestStack.BDDfy;
|
||||||
@ -15,10 +17,12 @@ namespace Ocelot.UnitTests.ServiceDiscovery
|
|||||||
private IServiceDiscoveryProvider _result;
|
private IServiceDiscoveryProvider _result;
|
||||||
private readonly ServiceDiscoveryProviderFactory _factory;
|
private readonly ServiceDiscoveryProviderFactory _factory;
|
||||||
private ReRoute _reRoute;
|
private ReRoute _reRoute;
|
||||||
|
private Mock<IOcelotLoggerFactory> _loggerFactory;
|
||||||
|
|
||||||
public ServiceProviderFactoryTests()
|
public ServiceProviderFactoryTests()
|
||||||
{
|
{
|
||||||
_factory = new ServiceDiscoveryProviderFactory();
|
_loggerFactory = new Mock<IOcelotLoggerFactory>();
|
||||||
|
_factory = new ServiceDiscoveryProviderFactory(_loggerFactory.Object);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
Loading…
x
Reference in New Issue
Block a user