From 5c940acf0e60a6cb737609a70cad689e92efb42e Mon Sep 17 00:00:00 2001 From: aqa510415008 <510415008@qq.com> Date: Fri, 20 Jul 2018 00:45:46 +0800 Subject: [PATCH] Expand other branch pipes (#416) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Expand other branch pipes * Expand other branch pipes * Expand other branch pipes * optimization Expand other branch pipes ,Add Unit test * I hope to add two attributes to IOcelotBuilder for easy extension. --- .../Builder/QoSOptionsBuilder.cs | 2 +- .../DependencyInjection/IOcelotBuilder.cs | 4 +++ .../DependencyInjection/OcelotBuilder.cs | 4 +++ .../Middleware/ExceptionHandlerMiddleware.cs | 4 +-- .../Middleware/OcelotMiddlewareExtensions.cs | 32 +++++++++++-------- .../Middleware/OcelotPipelineConfiguration.cs | 8 ++++- .../OcelotPipelineBuilderExtensions.cs | 31 +++++++++++++++--- .../Pipeline/OcelotPipelineExtensions.cs | 9 ++++++ .../OcelotPipelineExtensionsTests.cs | 32 +++++++++++++++++++ .../Middleware/OcelotPiplineBuilderTests.cs | 2 ++ 10 files changed, 107 insertions(+), 21 deletions(-) diff --git a/src/Ocelot/Configuration/Builder/QoSOptionsBuilder.cs b/src/Ocelot/Configuration/Builder/QoSOptionsBuilder.cs index 69660d83c..e1b86d1a2 100644 --- a/src/Ocelot/Configuration/Builder/QoSOptionsBuilder.cs +++ b/src/Ocelot/Configuration/Builder/QoSOptionsBuilder.cs @@ -6,7 +6,7 @@ public class QoSOptionsBuilder private int _durationOfBreak; - private int _timeoutValue; + private int _timeoutValue; private string _key; diff --git a/src/Ocelot/DependencyInjection/IOcelotBuilder.cs b/src/Ocelot/DependencyInjection/IOcelotBuilder.cs index 20eafd36c..9a4a60b84 100644 --- a/src/Ocelot/DependencyInjection/IOcelotBuilder.cs +++ b/src/Ocelot/DependencyInjection/IOcelotBuilder.cs @@ -4,11 +4,15 @@ using System.Net.Http; using IdentityServer4.AccessTokenValidation; using Ocelot.Middleware.Multiplexer; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Configuration; namespace Ocelot.DependencyInjection { public interface IOcelotBuilder { + IServiceCollection Services { get; } + IConfiguration Configuration { get; } IOcelotBuilder AddStoreOcelotConfigurationInConsul(); IOcelotBuilder AddCacheManager(Action settings); diff --git a/src/Ocelot/DependencyInjection/OcelotBuilder.cs b/src/Ocelot/DependencyInjection/OcelotBuilder.cs index 1b93ddbb0..72064a9dd 100644 --- a/src/Ocelot/DependencyInjection/OcelotBuilder.cs +++ b/src/Ocelot/DependencyInjection/OcelotBuilder.cs @@ -55,6 +55,10 @@ public class OcelotBuilder : IOcelotBuilder private readonly IServiceCollection _services; private readonly IConfiguration _configurationRoot; + public IServiceCollection Services => _services; + + public IConfiguration Configuration => _configurationRoot; + public OcelotBuilder(IServiceCollection services, IConfiguration configurationRoot) { _configurationRoot = configurationRoot; diff --git a/src/Ocelot/Errors/Middleware/ExceptionHandlerMiddleware.cs b/src/Ocelot/Errors/Middleware/ExceptionHandlerMiddleware.cs index b7e51ef93..f13533464 100644 --- a/src/Ocelot/Errors/Middleware/ExceptionHandlerMiddleware.cs +++ b/src/Ocelot/Errors/Middleware/ExceptionHandlerMiddleware.cs @@ -34,7 +34,7 @@ public ExceptionHandlerMiddleware(OcelotRequestDelegate next, public async Task Invoke(DownstreamContext context) { try - { + { //try and get the global request id and set it for logs... //should this basically be immutable per request...i guess it should! //first thing is get config @@ -44,7 +44,7 @@ public async Task Invoke(DownstreamContext context) { throw new Exception($"{MiddlewareName} setting pipeline errors. IOcelotConfigurationProvider returned {configuration.Errors.ToErrorString()}"); } - + TrySetGlobalRequestId(context, configuration.Data); context.Configuration = configuration.Data; diff --git a/src/Ocelot/Middleware/OcelotMiddlewareExtensions.cs b/src/Ocelot/Middleware/OcelotMiddlewareExtensions.cs index a35a8e1a7..d97dd71ee 100644 --- a/src/Ocelot/Middleware/OcelotMiddlewareExtensions.cs +++ b/src/Ocelot/Middleware/OcelotMiddlewareExtensions.cs @@ -27,15 +27,21 @@ public static async Task UseOcelot(this IApplicationBuilder await builder.UseOcelot(new OcelotPipelineConfiguration()); return builder; - } - + } + + public static async Task UseOcelot(this IApplicationBuilder builder, Action pipelineConfiguration) + { + var config = new OcelotPipelineConfiguration(); + pipelineConfiguration?.Invoke(config); + return await builder.UseOcelot(config); + } public static async Task UseOcelot(this IApplicationBuilder builder, OcelotPipelineConfiguration pipelineConfiguration) { - var configuration = await CreateConfiguration(builder); - + var configuration = await CreateConfiguration(builder); + CreateAdministrationArea(builder, configuration); - if(UsingRafty(builder)) + if (UsingRafty(builder)) { SetUpRafty(builder); } @@ -78,7 +84,7 @@ private static bool UsingEurekaServiceDiscoveryProvider(IInternalConfiguration c private static bool UsingRafty(IApplicationBuilder builder) { var possible = builder.ApplicationServices.GetService(typeof(INode)) as INode; - if(possible != null) + if (possible != null) { return true; } @@ -99,9 +105,9 @@ private static async Task CreateConfiguration(IApplicati { // make configuration from file system? // earlier user needed to add ocelot files in startup configuration stuff, asp.net will map it to this - var fileConfig = (IOptions)builder.ApplicationServices.GetService(typeof(IOptions)); - - // now create the config + var fileConfig = (IOptions)builder.ApplicationServices.GetService(typeof(IOptions)); + + // now create the config var internalConfigCreator = (IInternalConfigurationCreator)builder.ApplicationServices.GetService(typeof(IInternalConfigurationCreator)); var internalConfig = await internalConfigCreator.Create(fileConfig.Value); @@ -196,7 +202,7 @@ private static IInternalConfiguration GetOcelotConfigAndReturn(IInternalConfigur { var ocelotConfiguration = provider.Get(); - if(ocelotConfiguration?.Data == null || ocelotConfiguration.IsError) + if (ocelotConfiguration?.Data == null || ocelotConfiguration.IsError) { ThrowToStopOcelotStarting(ocelotConfiguration); } @@ -216,7 +222,7 @@ private static bool UsingConsul(IFileConfigurationRepository fileConfigRepo) private static void CreateAdministrationArea(IApplicationBuilder builder, IInternalConfiguration configuration) { - if(!string.IsNullOrEmpty(configuration.AdministrationPath)) + if (!string.IsNullOrEmpty(configuration.AdministrationPath)) { builder.Map(configuration.AdministrationPath, app => { @@ -239,8 +245,8 @@ private static void ConfigureDiagnosticListener(IApplicationBuilder builder) var listener = (OcelotDiagnosticListener)builder.ApplicationServices.GetService(typeof(OcelotDiagnosticListener)); var diagnosticListener = (DiagnosticListener)builder.ApplicationServices.GetService(typeof(DiagnosticListener)); diagnosticListener.SubscribeWithAdapter(listener); - } - + } + private static void OnShutdown(IApplicationBuilder app) { var node = (INode)app.ApplicationServices.GetService(typeof(INode)); diff --git a/src/Ocelot/Middleware/OcelotPipelineConfiguration.cs b/src/Ocelot/Middleware/OcelotPipelineConfiguration.cs index d58b8b092..c7f6e231f 100644 --- a/src/Ocelot/Middleware/OcelotPipelineConfiguration.cs +++ b/src/Ocelot/Middleware/OcelotPipelineConfiguration.cs @@ -1,6 +1,8 @@ namespace Ocelot.Middleware { + using Ocelot.Middleware.Pipeline; using System; + using System.Collections.Generic; using System.Threading.Tasks; public class OcelotPipelineConfiguration @@ -37,6 +39,10 @@ public class OcelotPipelineConfiguration /// /// This allows the user to implement there own query string manipulation logic /// - public Func, Task> PreQueryStringBuilderMiddleware { get; set; } + public Func, Task> PreQueryStringBuilderMiddleware { get; set; } + /// + /// This is an extension that will branch to different pipes + /// + public List>> MapWhenOcelotPipeline { get; } = new List>>(); } } diff --git a/src/Ocelot/Middleware/Pipeline/OcelotPipelineBuilderExtensions.cs b/src/Ocelot/Middleware/Pipeline/OcelotPipelineBuilderExtensions.cs index b9aabfe37..de2a35eb2 100644 --- a/src/Ocelot/Middleware/Pipeline/OcelotPipelineBuilderExtensions.cs +++ b/src/Ocelot/Middleware/Pipeline/OcelotPipelineBuilderExtensions.cs @@ -80,13 +80,14 @@ public static IOcelotPipelineBuilder UseMiddleware(this IOcelotPipelineBuilder a var diagnosticListener = (DiagnosticListener)app.ApplicationServices.GetService(typeof(DiagnosticListener)); var middlewareName = ocelotDelegate.Target.GetType().Name; - OcelotRequestDelegate wrapped = context => { + OcelotRequestDelegate wrapped = context => + { try { Write(diagnosticListener, "Ocelot.MiddlewareStarted", middlewareName, context); return ocelotDelegate(context); } - catch(Exception ex) + catch (Exception ex) { WriteException(diagnosticListener, ex, "Ocelot.MiddlewareException", middlewareName, context); throw ex; @@ -117,7 +118,7 @@ public static IOcelotPipelineBuilder UseMiddleware(this IOcelotPipelineBuilder a private static void Write(DiagnosticListener diagnosticListener, string message, string middlewareName, DownstreamContext context) { - if(diagnosticListener != null) + if (diagnosticListener != null) { diagnosticListener.Write(message, new { name = middlewareName, context = context }); } @@ -125,7 +126,7 @@ private static void Write(DiagnosticListener diagnosticListener, string message, private static void WriteException(DiagnosticListener diagnosticListener, Exception exception, string message, string middlewareName, DownstreamContext context) { - if(diagnosticListener != null) + if (diagnosticListener != null) { diagnosticListener.Write(message, new { name = middlewareName, context = context, exception = exception }); } @@ -160,6 +161,28 @@ public static IOcelotPipelineBuilder MapWhen(this IOcelotPipelineBuilder app, Pr return app.Use(next => new MapWhenMiddleware(next, options).Invoke); } + public static IOcelotPipelineBuilder MapWhen(this IOcelotPipelineBuilder app, Func pipelineBuilderFunc) + { + if (app == null) + { + throw new ArgumentNullException(nameof(app)); + } + if (pipelineBuilderFunc == null) + { + throw new ArgumentNullException(nameof(pipelineBuilderFunc)); + } + var branchBuilder = app.New(); + var predicate = pipelineBuilderFunc.Invoke(app); + var branch = branchBuilder.Build(); + + var options = new MapWhenOptions + { + Predicate = predicate, + Branch = branch + }; + return app.Use(next => new MapWhenMiddleware(next, options).Invoke); + } + private static Func Compile(MethodInfo methodinfo, ParameterInfo[] parameters) { var middleware = typeof(T); diff --git a/src/Ocelot/Middleware/Pipeline/OcelotPipelineExtensions.cs b/src/Ocelot/Middleware/Pipeline/OcelotPipelineExtensions.cs index f9a74235e..5c10d7c04 100644 --- a/src/Ocelot/Middleware/Pipeline/OcelotPipelineExtensions.cs +++ b/src/Ocelot/Middleware/Pipeline/OcelotPipelineExtensions.cs @@ -28,6 +28,15 @@ public static OcelotRequestDelegate BuildOcelotPipeline(this IOcelotPipelineBuil // It also sets the Request Id if anything is set globally builder.UseExceptionHandlerMiddleware(); + //Expand other branch pipes + if (pipelineConfiguration.MapWhenOcelotPipeline != null) + { + foreach (var pipeline in pipelineConfiguration.MapWhenOcelotPipeline) + { + builder.MapWhen(pipeline); + } + } + // If the request is for websockets upgrade we fork into a different pipeline builder.MapWhen(context => context.HttpContext.WebSockets.IsWebSocketRequest, app => diff --git a/test/Ocelot.UnitTests/Middleware/OcelotPipelineExtensionsTests.cs b/test/Ocelot.UnitTests/Middleware/OcelotPipelineExtensionsTests.cs index 74f6c7879..fd6fd8bfe 100644 --- a/test/Ocelot.UnitTests/Middleware/OcelotPipelineExtensionsTests.cs +++ b/test/Ocelot.UnitTests/Middleware/OcelotPipelineExtensionsTests.cs @@ -3,8 +3,13 @@ namespace Ocelot.UnitTests.Middleware using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Ocelot.DependencyInjection; + using Ocelot.DownstreamRouteFinder.Middleware; + using Ocelot.DownstreamUrlCreator.Middleware; + using Ocelot.LoadBalancer.Middleware; using Ocelot.Middleware; using Ocelot.Middleware.Pipeline; + using Ocelot.Request.Middleware; + using Ocelot.WebSockets.Middleware; using Pivotal.Discovery.Client; using Shouldly; using Steeltoe.Common.Discovery; @@ -26,6 +31,16 @@ public void should_set_up_pipeline() .BDDfy(); } + [Fact] + public void should_expand_pipeline() + { + this.Given(_ => GivenTheDepedenciesAreSetUp()) + .When(_ => WhenIExpandBuild()) + .Then(_ => ThenThePipelineIsBuilt()) + .BDDfy(); + } + + private void ThenThePipelineIsBuilt() { _handlers.ShouldNotBeNull(); @@ -36,6 +51,23 @@ private void WhenIBuild() _handlers = _builder.BuildOcelotPipeline(new OcelotPipelineConfiguration()); } + private void WhenIExpandBuild() + { + OcelotPipelineConfiguration configuration = new OcelotPipelineConfiguration(); + configuration.MapWhenOcelotPipeline.Add((app) => + { + app.UseDownstreamRouteFinderMiddleware(); + app.UseDownstreamRequestInitialiser(); + app.UseLoadBalancingMiddleware(); + app.UseDownstreamUrlCreatorMiddleware(); + app.UseWebSocketsProxyMiddleware(); + + return context => context.HttpContext.WebSockets.IsWebSocketRequest; + }); + _handlers = _builder.BuildOcelotPipeline(new OcelotPipelineConfiguration()); + } + + private void GivenTheDepedenciesAreSetUp() { IConfigurationBuilder test = new ConfigurationBuilder(); diff --git a/test/Ocelot.UnitTests/Middleware/OcelotPiplineBuilderTests.cs b/test/Ocelot.UnitTests/Middleware/OcelotPiplineBuilderTests.cs index 21649a808..a798ae446 100644 --- a/test/Ocelot.UnitTests/Middleware/OcelotPiplineBuilderTests.cs +++ b/test/Ocelot.UnitTests/Middleware/OcelotPiplineBuilderTests.cs @@ -79,6 +79,8 @@ private void WhenIUseAFunc() del.Invoke(_downstreamContext); } + + private void ThenTheFuncIsInThePipeline() { _counter.ShouldBe(1);