Skip to content

Commit

Permalink
Feature/expose http handlers (#224)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
TomPallister authored Feb 13, 2018
1 parent ef3c4f6 commit 98133d9
Show file tree
Hide file tree
Showing 48 changed files with 1,352 additions and 266 deletions.
43 changes: 43 additions & 0 deletions docs/features/delegatinghandlers.rst
Original file line number Diff line number Diff line change
@@ -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!
2 changes: 2 additions & 0 deletions docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ Thanks for taking a look at the Ocelot documentation. Please use the left hand n
features/requestid
features/middlewareinjection
features/loadbalancer
features/delegatinghandlers


.. toctree::
:maxdepth: 2
Expand Down
2 changes: 1 addition & 1 deletion global.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"projects": [ "src", "test" ],
"sdk": {
"version": "2.0.2"
"version": "2.1.4"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
namespace Ocelot.DependencyInjection
{
public interface IOcelotAdministrationBuilder
{
IOcelotAdministrationBuilder AddRafty();
}
}
2 changes: 2 additions & 0 deletions src/Ocelot/DependencyInjection/IOcelotBuilder.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using Butterfly.Client.AspNetCore;
using CacheManager.Core;
using System;
using System.Net.Http;

namespace Ocelot.DependencyInjection
{
Expand All @@ -10,5 +11,6 @@ public interface IOcelotBuilder
IOcelotBuilder AddCacheManager(Action<ConfigurationBuilderCachePart> settings);
IOcelotBuilder AddOpenTracing(Action<ButterflyOptions> settings);
IOcelotAdministrationBuilder AddAdministration(string path, string secret);
IOcelotBuilder AddDelegatingHandler(Func<DelegatingHandler> delegatingHandler);
}
}
34 changes: 34 additions & 0 deletions src/Ocelot/DependencyInjection/OcelotAdministrationBuilder.cs
Original file line number Diff line number Diff line change
@@ -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;
}
}
}
84 changes: 33 additions & 51 deletions src/Ocelot/DependencyInjection/OcelotBuilder.cs
Original file line number Diff line number Diff line change
@@ -1,19 +1,12 @@
using Butterfly.Client.AspNetCore;
using CacheManager.Core;
using IdentityServer4.AccessTokenValidation;
using IdentityServer4.Models;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Ocelot.Authorisation;
using Ocelot.Cache;
using Ocelot.Claims;
using Ocelot.Configuration;
using Ocelot.Configuration.Authentication;
using Ocelot.Configuration.Builder;
using Ocelot.Configuration.Creator;
using Ocelot.Configuration.File;
using Ocelot.Configuration.Parser;
Expand All @@ -32,32 +25,36 @@
using Ocelot.Logging;
using Ocelot.Middleware;
using Ocelot.QueryStrings;
using Ocelot.Raft;
using Ocelot.RateLimit;
using Ocelot.Request.Builder;
using Ocelot.Request.Mapper;
using Ocelot.Requester;
using Ocelot.Requester.QoS;
using Ocelot.Responder;
using Ocelot.ServiceDiscovery;
using Rafty.Concensus;
using Rafty.FiniteStateMachine;
using Rafty.Infrastructure;
using Rafty.Log;
using System;
using System.Collections.Generic;
using System.IdentityModel.Tokens.Jwt;
using System.Linq;
using System.Reflection;
using System.Security.Cryptography.X509Certificates;
using IdentityServer4.AccessTokenValidation;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Ocelot.Configuration;
using Ocelot.Configuration.Builder;
using FileConfigurationProvider = Ocelot.Configuration.Provider.FileConfigurationProvider;
using Microsoft.Extensions.DependencyInjection.Extensions;
using System.Linq;
using System.Net.Http;
using Butterfly.Client.AspNetCore;

namespace Ocelot.DependencyInjection
{
public class OcelotBuilder : IOcelotBuilder
{
private IServiceCollection _services;
private IConfiguration _configurationRoot;
private readonly IServiceCollection _services;
private readonly IConfiguration _configurationRoot;
private IDelegatingHandlerHandlerProvider _provider;

public OcelotBuilder(IServiceCollection services, IConfiguration configurationRoot)
{
Expand Down Expand Up @@ -122,6 +119,9 @@ public OcelotBuilder(IServiceCollection services, IConfiguration configurationRo
_services.TryAddSingleton<IRequestMapper, RequestMapper>();
_services.TryAddSingleton<IHttpHandlerOptionsCreator, HttpHandlerOptionsCreator>();
_services.TryAddSingleton<IDownstreamAddressesCreator, DownstreamAddressesCreator>();
_services.TryAddSingleton<IDelegatingHandlerHandlerProviderFactory, DelegatingHandlerHandlerProviderFactory>();
_services.TryAddSingleton<IDelegatingHandlerHandlerHouse, DelegatingHandlerHandlerHouse>();

// see this for why we register this as singleton http://stackoverflow.com/questions/37371264/invalidoperationexception-unable-to-resolve-service-for-type-microsoft-aspnetc
// could maybe use a scoped data repository
_services.TryAddSingleton<IHttpContextAccessor, HttpContextAccessor>();
Expand All @@ -142,6 +142,11 @@ public OcelotBuilder(IServiceCollection services, IConfiguration configurationRo
_services.AddMiddlewareAnalysis();
_services.AddWebEncoders();
_services.AddSingleton<IAdministrationPath>(new NullAdministrationPath());

//these get picked out later and added to http request
_provider = new DelegatingHandlerHandlerProvider();
_services.TryAddSingleton<IDelegatingHandlerHandlerProvider>(_provider);
_services.AddTransient<ITracingHandler, NoTracingHandler>();
}

public IOcelotAdministrationBuilder AddAdministration(string path, string secret)
Expand All @@ -161,6 +166,19 @@ public IOcelotAdministrationBuilder AddAdministration(string path, string secret
return new OcelotAdministrationBuilder(_services, _configurationRoot);
}

public IOcelotBuilder AddDelegatingHandler(Func<DelegatingHandler> delegatingHandler)
{
_provider.Add(delegatingHandler);
return this;
}

public IOcelotBuilder AddOpenTracing(Action<ButterflyOptions> settings)
{
_services.AddTransient<ITracingHandler, OcelotHttpTracingHandler>();
_services.AddButterfly(settings);
return this;
}

public IOcelotBuilder AddStoreOcelotConfigurationInConsul()
{
var serviceDiscoveryPort = _configurationRoot.GetValue("GlobalConfiguration:ServiceDiscoveryProvider:Port", 0);
Expand Down Expand Up @@ -203,13 +221,6 @@ public IOcelotBuilder AddCacheManager(Action<ConfigurationBuilderCachePart> sett
return this;
}

public IOcelotBuilder AddOpenTracing(Action<ButterflyOptions> settings)
{
_services.AddTransient<OcelotHttpTracingHandler>();
_services.AddButterfly(settings);
return this;
}

private void AddIdentityServer(IIdentityServerConfiguration identityServerConfiguration, IAdministrationPath adminPath)
{
_services.TryAddSingleton<IIdentityServerConfiguration>(identityServerConfiguration);
Expand Down Expand Up @@ -281,33 +292,4 @@ private List<Client> Client(IIdentityServerConfiguration identityServerConfigura
};
}
}

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;
}
}
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
using System;
using System.Collections.Generic;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Ocelot.Configuration;
using Ocelot.Configuration.Provider;
using Ocelot.DownstreamRouteFinder.UrlMatcher;
using Ocelot.Errors;
using Ocelot.Responses;
Expand Down
2 changes: 1 addition & 1 deletion src/Ocelot/Errors/Middleware/ExceptionHandlerMiddleware.cs
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ public async Task Invoke(HttpContext context)
private async Task TrySetGlobalRequestId(HttpContext context)
{
//try and get the global request id and set it for logs...
//shoudl this basically be immutable per request...i guess it should!
//should this basically be immutable per request...i guess it should!
//first thing is get config
var configuration = await _configProvider.Get();

Expand Down
3 changes: 2 additions & 1 deletion src/Ocelot/Errors/OcelotErrorCode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ public enum OcelotErrorCode
UnmappableRequestError,
RateLimitOptionsError,
PathTemplateDoesntStartWithForwardSlash,
FileValidationFailedError
FileValidationFailedError,
UnableToFindDelegatingHandlerProviderError
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,4 @@ public UnableToFindLoadBalancerError(string message)
{
}
}
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
using System;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Ocelot.Configuration.Provider;
using Ocelot.Infrastructure.RequestData;
using Ocelot.LoadBalancer.LoadBalancers;
using Ocelot.Logging;
Expand Down Expand Up @@ -46,11 +45,14 @@ public async Task Invoke(HttpContext context)
}

var uriBuilder = new UriBuilder(DownstreamRequest.RequestUri);

uriBuilder.Host = hostAndPort.Data.DownstreamHost;

if (hostAndPort.Data.DownstreamPort > 0)
{
uriBuilder.Port = hostAndPort.Data.DownstreamPort;
}

DownstreamRequest.RequestUri = uriBuilder.Uri;

try
Expand Down
3 changes: 2 additions & 1 deletion src/Ocelot/Request/Builder/HttpRequestCreator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,10 @@ public async Task<Response<Request>> Build(
IQoSProvider qosProvider,
bool useCookieContainer,
bool allowAutoRedirect,
string reRouteKey,
bool isTracing)
{
return new OkResponse<Request>(new Request(httpRequestMessage, isQos, qosProvider, allowAutoRedirect, useCookieContainer, isTracing));
return new OkResponse<Request>(new Request(httpRequestMessage, isQos, qosProvider, allowAutoRedirect, useCookieContainer, reRouteKey, isTracing));
}
}
}
1 change: 1 addition & 0 deletions src/Ocelot/Request/Builder/IRequestCreator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ Task<Response<Request>> Build(
IQoSProvider qosProvider,
bool useCookieContainer,
bool allowAutoRedirect,
string reRouteKe,
bool isTracing);
}
}
2 changes: 2 additions & 0 deletions src/Ocelot/Request/Middleware/HttpRequestBuilderMiddleware.cs
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,9 @@ public async Task Invoke(HttpContext context)
qosProvider.Data,
DownstreamRoute.ReRoute.HttpHandlerOptions.UseCookieContainer,
DownstreamRoute.ReRoute.HttpHandlerOptions.AllowAutoRedirect,
DownstreamRoute.ReRoute.ReRouteKey,
DownstreamRoute.ReRoute.HttpHandlerOptions.UseTracing);

if (buildResult.IsError)
{
_logger.LogDebug("IRequestCreator returned an error, setting pipeline error");
Expand Down
5 changes: 4 additions & 1 deletion src/Ocelot/Request/Request.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ public Request(
IQoSProvider qosProvider,
bool allowAutoRedirect,
bool useCookieContainer,
string reRouteKey,
bool isTracing
)
{
Expand All @@ -19,6 +20,7 @@ bool isTracing
QosProvider = qosProvider;
AllowAutoRedirect = allowAutoRedirect;
UseCookieContainer = useCookieContainer;
ReRouteKey = reRouteKey;
IsTracing = isTracing;
}

Expand All @@ -28,5 +30,6 @@ bool isTracing
public IQoSProvider QosProvider { get; private set; }
public bool AllowAutoRedirect { get; private set; }
public bool UseCookieContainer { get; private set; }
public string ReRouteKey { get; private set; }
}
}
}
Loading

0 comments on commit 98133d9

Please sign in to comment.