From de8ec19fbb88810a6cfbbe0d0d99754d26b259d4 Mon Sep 17 00:00:00 2001 From: Roc Lee Date: Tue, 18 Jun 2019 14:36:05 +0800 Subject: [PATCH 01/22] #921 Disagree "Allows access to unpass node (#825)" (#924) --- src/Ocelot.Provider.Consul/Consul.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Ocelot.Provider.Consul/Consul.cs b/src/Ocelot.Provider.Consul/Consul.cs index f300aa6e..72f15d09 100644 --- a/src/Ocelot.Provider.Consul/Consul.cs +++ b/src/Ocelot.Provider.Consul/Consul.cs @@ -26,7 +26,7 @@ public async Task> Get() { - var queryResult = await _consul.Health.Service(_config.KeyOfServiceInConsul, string.Empty, false); + var queryResult = await _consul.Health.Service(_config.KeyOfServiceInConsul, string.Empty, true); var services = new List(); From 959a92ec6a32ae61bf8bac6023551085b0031b85 Mon Sep 17 00:00:00 2001 From: Jesus Angulo Date: Tue, 18 Jun 2019 01:36:14 -0500 Subject: [PATCH 02/22] Fixing docs , a brace is out of code section (#931) A closing brace is out of code section and appears before the description paragraph after JSON code sample. --- docs/features/kubernetes.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/features/kubernetes.rst b/docs/features/kubernetes.rst index 9f8c8f9f..11fbd71d 100644 --- a/docs/features/kubernetes.rst +++ b/docs/features/kubernetes.rst @@ -54,6 +54,7 @@ kubernetes service name. We also need to set up the ServiceDiscoveryProvider in } } } + Service deployment in Namespace Dev , ServiceDiscoveryProvider type is kube, you also can set pollkube ServiceDiscoveryProvider type. Note: Host、 Port and Token are no longer in use。 From e1d7f289517dcafb8b96c6d4ea17d55a945b72d7 Mon Sep 17 00:00:00 2001 From: Jason Morton Date: Tue, 25 Jun 2019 04:08:18 +0100 Subject: [PATCH 03/22] Fix issue #936: Kubernetes service discovery provider doesn't allow cross-namespace discovery (#938) * Allow default k8s namespace to be overridden * Add ServiceNamespace to ReRoute configuration * Remove debug comments * Update unit tests * Unit tests (Eureka) * Update docs * Re-run build --- docs/features/kubernetes.rst | 18 ++++++++++++++++++ .../ConsulProviderFactory.cs | 4 ++-- .../EurekaProviderFactory.cs | 4 ++-- src/Ocelot.Provider.Kubernetes/KubeProvider.cs | 3 +-- .../KubernetesProviderFactory.cs | 11 ++++++----- .../Builder/DownstreamReRouteBuilder.cs | 8 ++++++++ .../Configuration/Creator/ReRoutesCreator.cs | 1 + src/Ocelot/Configuration/DownstreamReRoute.cs | 3 +++ src/Ocelot/Configuration/File/FileReRoute.cs | 1 + .../ServiceDiscoveryFinderDelegate.cs | 2 +- .../ServiceDiscoveryProviderFactory.cs | 8 ++++---- .../Consul/ProviderFactoryTests.cs | 17 ++++++++++++++--- .../Eureka/EurekaProviderFactoryTests.cs | 5 ++++- 13 files changed, 65 insertions(+), 20 deletions(-) diff --git a/docs/features/kubernetes.rst b/docs/features/kubernetes.rst index 11fbd71d..be23845b 100644 --- a/docs/features/kubernetes.rst +++ b/docs/features/kubernetes.rst @@ -75,3 +75,21 @@ The polling interval is in milliseconds and tells Ocelot how often to call kuber Please note there are tradeoffs here. If you poll kubernetes it is possible Ocelot will not know if a service is down depending on your polling interval and you might get more errors than if you get the latest services per request. This really depends on how volatile your services are. I doubt it will matter for most people and polling may give a tiny performance improvement over calling kubernetes per request. There is no way for Ocelot to work these out for you. + +If your downstream service resides in a different namespace you can override the global setting at the ReRoute level by specifying a ServiceNamespace + + +.. code-block:: json + +{ + "ReRoutes": [ + { + "DownstreamPathTemplate": "/api/values", + "DownstreamScheme": "http", + "UpstreamPathTemplate": "/values", + "ServiceName": "downstreamservice", + "ServiceNamespace": "downstream-namespace", + "UpstreamHttpMethod": [ "Get" ] + } + ] +} diff --git a/src/Ocelot.Provider.Consul/ConsulProviderFactory.cs b/src/Ocelot.Provider.Consul/ConsulProviderFactory.cs index cdf422b8..8cffa882 100644 --- a/src/Ocelot.Provider.Consul/ConsulProviderFactory.cs +++ b/src/Ocelot.Provider.Consul/ConsulProviderFactory.cs @@ -6,13 +6,13 @@ public static class ConsulProviderFactory { - public static ServiceDiscoveryFinderDelegate Get = (provider, config, name) => + public static ServiceDiscoveryFinderDelegate Get = (provider, config, reRoute) => { var factory = provider.GetService(); var consulFactory = provider.GetService(); - var consulRegistryConfiguration = new ConsulRegistryConfiguration(config.Host, config.Port, name, config.Token); + var consulRegistryConfiguration = new ConsulRegistryConfiguration(config.Host, config.Port, reRoute.ServiceName, config.Token); var consulServiceDiscoveryProvider = new Consul(consulRegistryConfiguration, factory, consulFactory); diff --git a/src/Ocelot.Provider.Eureka/EurekaProviderFactory.cs b/src/Ocelot.Provider.Eureka/EurekaProviderFactory.cs index 9fdea310..d9bba179 100644 --- a/src/Ocelot.Provider.Eureka/EurekaProviderFactory.cs +++ b/src/Ocelot.Provider.Eureka/EurekaProviderFactory.cs @@ -6,13 +6,13 @@ public static class EurekaProviderFactory { - public static ServiceDiscoveryFinderDelegate Get = (provider, config, name) => + public static ServiceDiscoveryFinderDelegate Get = (provider, config, reRoute) => { var client = provider.GetService(); if (config.Type?.ToLower() == "eureka" && client != null) { - return new Eureka(name, client); + return new Eureka(reRoute.ServiceName, client); } return null; diff --git a/src/Ocelot.Provider.Kubernetes/KubeProvider.cs b/src/Ocelot.Provider.Kubernetes/KubeProvider.cs index b790c147..3083fb32 100644 --- a/src/Ocelot.Provider.Kubernetes/KubeProvider.cs +++ b/src/Ocelot.Provider.Kubernetes/KubeProvider.cs @@ -24,8 +24,7 @@ namespace Ocelot.Provider.Kubernetes public async Task> Get() { - var service = await kubeApi.ServicesV1() - .Get(kubeRegistryConfiguration.KeyOfServiceInK8s, kubeRegistryConfiguration.KubeNamespace); + var service = await kubeApi.ServicesV1().Get(kubeRegistryConfiguration.KeyOfServiceInK8s, kubeRegistryConfiguration.KubeNamespace); var services = new List(); if (IsValid(service)) { diff --git a/src/Ocelot.Provider.Kubernetes/KubernetesProviderFactory.cs b/src/Ocelot.Provider.Kubernetes/KubernetesProviderFactory.cs index c33839c6..f12d0ebc 100644 --- a/src/Ocelot.Provider.Kubernetes/KubernetesProviderFactory.cs +++ b/src/Ocelot.Provider.Kubernetes/KubernetesProviderFactory.cs @@ -3,24 +3,25 @@ using Microsoft.Extensions.DependencyInjection; using Ocelot.Logging; using Ocelot.ServiceDiscovery; using System; +using Ocelot.Configuration; namespace Ocelot.Provider.Kubernetes { public static class KubernetesProviderFactory { - public static ServiceDiscoveryFinderDelegate Get = (provider, config, name) => + public static ServiceDiscoveryFinderDelegate Get = (provider, config, reRoute) => { var factory = provider.GetService(); - return GetkubeProvider(provider, config, name, factory); + return GetkubeProvider(provider, config, reRoute, factory); }; - private static ServiceDiscovery.Providers.IServiceDiscoveryProvider GetkubeProvider(IServiceProvider provider, Configuration.ServiceProviderConfiguration config, string name, IOcelotLoggerFactory factory) + private static ServiceDiscovery.Providers.IServiceDiscoveryProvider GetkubeProvider(IServiceProvider provider, Configuration.ServiceProviderConfiguration config, DownstreamReRoute reRoute, IOcelotLoggerFactory factory) { var kubeClient = provider.GetService(); var k8sRegistryConfiguration = new KubeRegistryConfiguration() { - KeyOfServiceInK8s = name, - KubeNamespace = config.Namespace, + KeyOfServiceInK8s = reRoute.ServiceName, + KubeNamespace = string.IsNullOrEmpty(reRoute.ServiceNamespace) ? config.Namespace : reRoute.ServiceNamespace }; var k8sServiceDiscoveryProvider = new Kube(k8sRegistryConfiguration, factory, kubeClient); diff --git a/src/Ocelot/Configuration/Builder/DownstreamReRouteBuilder.cs b/src/Ocelot/Configuration/Builder/DownstreamReRouteBuilder.cs index 42ad17db..2d7e9207 100644 --- a/src/Ocelot/Configuration/Builder/DownstreamReRouteBuilder.cs +++ b/src/Ocelot/Configuration/Builder/DownstreamReRouteBuilder.cs @@ -30,6 +30,7 @@ namespace Ocelot.Configuration.Builder private RateLimitOptions _rateLimitOptions; private bool _useServiceDiscovery; private string _serviceName; + private string _serviceNamespace; private List _upstreamHeaderFindAndReplace; private List _downstreamHeaderFindAndReplace; private readonly List _downstreamAddresses; @@ -185,6 +186,12 @@ namespace Ocelot.Configuration.Builder _serviceName = serviceName; return this; } + + public DownstreamReRouteBuilder WithServiceNamespace(string serviceNamespace) + { + _serviceNamespace = serviceNamespace; + return this; + } public DownstreamReRouteBuilder WithUpstreamHeaderFindAndReplace(List upstreamHeaderFindAndReplace) { @@ -243,6 +250,7 @@ namespace Ocelot.Configuration.Builder _downstreamHeaderFindAndReplace, _downstreamAddresses, _serviceName, + _serviceNamespace, _httpHandlerOptions, _useServiceDiscovery, _enableRateLimiting, diff --git a/src/Ocelot/Configuration/Creator/ReRoutesCreator.cs b/src/Ocelot/Configuration/Creator/ReRoutesCreator.cs index c6171521..52b81985 100644 --- a/src/Ocelot/Configuration/Creator/ReRoutesCreator.cs +++ b/src/Ocelot/Configuration/Creator/ReRoutesCreator.cs @@ -126,6 +126,7 @@ namespace Ocelot.Configuration.Creator .WithRateLimitOptions(rateLimitOption) .WithHttpHandlerOptions(httpHandlerOptions) .WithServiceName(fileReRoute.ServiceName) + .WithServiceNamespace(fileReRoute.ServiceNamespace) .WithUseServiceDiscovery(fileReRouteOptions.UseServiceDiscovery) .WithUpstreamHeaderFindAndReplace(hAndRs.Upstream) .WithDownstreamHeaderFindAndReplace(hAndRs.Downstream) diff --git a/src/Ocelot/Configuration/DownstreamReRoute.cs b/src/Ocelot/Configuration/DownstreamReRoute.cs index b332b622..cf421285 100644 --- a/src/Ocelot/Configuration/DownstreamReRoute.cs +++ b/src/Ocelot/Configuration/DownstreamReRoute.cs @@ -13,6 +13,7 @@ namespace Ocelot.Configuration List downstreamHeadersFindAndReplace, List downstreamAddresses, string serviceName, + string serviceNamespace, HttpHandlerOptions httpHandlerOptions, bool useServiceDiscovery, bool enableEndpointEndpointRateLimiting, @@ -47,6 +48,7 @@ namespace Ocelot.Configuration DownstreamHeadersFindAndReplace = downstreamHeadersFindAndReplace ?? new List(); DownstreamAddresses = downstreamAddresses ?? new List(); ServiceName = serviceName; + ServiceNamespace = serviceNamespace; HttpHandlerOptions = httpHandlerOptions; UseServiceDiscovery = useServiceDiscovery; EnableEndpointEndpointRateLimiting = enableEndpointEndpointRateLimiting; @@ -76,6 +78,7 @@ namespace Ocelot.Configuration public List DownstreamHeadersFindAndReplace { get; } public List DownstreamAddresses { get; } public string ServiceName { get; } + public string ServiceNamespace { get; } public HttpHandlerOptions HttpHandlerOptions { get; } public bool UseServiceDiscovery { get; } public bool EnableEndpointEndpointRateLimiting { get; } diff --git a/src/Ocelot/Configuration/File/FileReRoute.cs b/src/Ocelot/Configuration/File/FileReRoute.cs index c87bf435..bad0e2af 100644 --- a/src/Ocelot/Configuration/File/FileReRoute.cs +++ b/src/Ocelot/Configuration/File/FileReRoute.cs @@ -38,6 +38,7 @@ namespace Ocelot.Configuration.File public FileCacheOptions FileCacheOptions { get; set; } public bool ReRouteIsCaseSensitive { get; set; } public string ServiceName { get; set; } + public string ServiceNamespace { get; set; } public string DownstreamScheme { get; set; } public FileQoSOptions QoSOptions { get; set; } public FileLoadBalancerOptions LoadBalancerOptions { get; set; } diff --git a/src/Ocelot/ServiceDiscovery/ServiceDiscoveryFinderDelegate.cs b/src/Ocelot/ServiceDiscovery/ServiceDiscoveryFinderDelegate.cs index 848b0eef..a3cf8460 100644 --- a/src/Ocelot/ServiceDiscovery/ServiceDiscoveryFinderDelegate.cs +++ b/src/Ocelot/ServiceDiscovery/ServiceDiscoveryFinderDelegate.cs @@ -4,5 +4,5 @@ namespace Ocelot.ServiceDiscovery using Providers; using System; - public delegate IServiceDiscoveryProvider ServiceDiscoveryFinderDelegate(IServiceProvider provider, ServiceProviderConfiguration config, string key); + public delegate IServiceDiscoveryProvider ServiceDiscoveryFinderDelegate(IServiceProvider provider, ServiceProviderConfiguration config, DownstreamReRoute reRoute); } diff --git a/src/Ocelot/ServiceDiscovery/ServiceDiscoveryProviderFactory.cs b/src/Ocelot/ServiceDiscovery/ServiceDiscoveryProviderFactory.cs index b7f6ef4e..1863a206 100644 --- a/src/Ocelot/ServiceDiscovery/ServiceDiscoveryProviderFactory.cs +++ b/src/Ocelot/ServiceDiscovery/ServiceDiscoveryProviderFactory.cs @@ -27,7 +27,7 @@ namespace Ocelot.ServiceDiscovery { if (reRoute.UseServiceDiscovery) { - return GetServiceDiscoveryProvider(serviceConfig, reRoute.ServiceName); + return GetServiceDiscoveryProvider(serviceConfig, reRoute); } var services = new List(); @@ -42,17 +42,17 @@ namespace Ocelot.ServiceDiscovery return new OkResponse(new ConfigurationServiceProvider(services)); } - private Response GetServiceDiscoveryProvider(ServiceProviderConfiguration config, string key) + private Response GetServiceDiscoveryProvider(ServiceProviderConfiguration config, DownstreamReRoute reRoute) { if (config.Type?.ToLower() == "servicefabric") { - var sfConfig = new ServiceFabricConfiguration(config.Host, config.Port, key); + var sfConfig = new ServiceFabricConfiguration(config.Host, config.Port, reRoute.ServiceName); return new OkResponse(new ServiceFabricServiceDiscoveryProvider(sfConfig)); } if (_delegates != null) { - var provider = _delegates?.Invoke(_provider, config, key); + var provider = _delegates?.Invoke(_provider, config, reRoute); if (provider.GetType().Name.ToLower() == config.Type.ToLower()) { diff --git a/test/Ocelot.UnitTests/Consul/ProviderFactoryTests.cs b/test/Ocelot.UnitTests/Consul/ProviderFactoryTests.cs index 3aeb0751..5d4939d2 100644 --- a/test/Ocelot.UnitTests/Consul/ProviderFactoryTests.cs +++ b/test/Ocelot.UnitTests/Consul/ProviderFactoryTests.cs @@ -1,4 +1,6 @@ -namespace Ocelot.UnitTests.Consul +using Ocelot.Configuration.Builder; + +namespace Ocelot.UnitTests.Consul { using Microsoft.Extensions.DependencyInjection; using Moq; @@ -29,7 +31,11 @@ [Fact] public void should_return_ConsulServiceDiscoveryProvider() { - var provider = ConsulProviderFactory.Get(_provider, new ServiceProviderConfiguration("", "", 1, "", "", 1), ""); + var reRoute = new DownstreamReRouteBuilder() + .WithServiceName("") + .Build(); + + var provider = ConsulProviderFactory.Get(_provider, new ServiceProviderConfiguration("", "", 1, "", "", 1), reRoute); provider.ShouldBeOfType(); } @@ -37,7 +43,12 @@ public void should_return_PollingConsulServiceDiscoveryProvider() { var stopsPollerFromPolling = 10000; - var provider = ConsulProviderFactory.Get(_provider, new ServiceProviderConfiguration("pollconsul", "", 1, "", "", stopsPollerFromPolling), ""); + + var reRoute = new DownstreamReRouteBuilder() + .WithServiceName("") + .Build(); + + var provider = ConsulProviderFactory.Get(_provider, new ServiceProviderConfiguration("pollconsul", "", 1, "", "", stopsPollerFromPolling), reRoute); provider.ShouldBeOfType(); } } diff --git a/test/Ocelot.UnitTests/Eureka/EurekaProviderFactoryTests.cs b/test/Ocelot.UnitTests/Eureka/EurekaProviderFactoryTests.cs index 2dea63cb..04e40db4 100644 --- a/test/Ocelot.UnitTests/Eureka/EurekaProviderFactoryTests.cs +++ b/test/Ocelot.UnitTests/Eureka/EurekaProviderFactoryTests.cs @@ -27,7 +27,10 @@ var services = new ServiceCollection(); services.AddSingleton(client.Object); var sp = services.BuildServiceProvider(); - var provider = EurekaProviderFactory.Get(sp, config, null); + var reRoute = new DownstreamReRouteBuilder() + .WithServiceName("") + .Build(); + var provider = EurekaProviderFactory.Get(sp, config, reRoute); provider.ShouldBeOfType(); } } From 3216dfdc00e39bfd4cc791cc6fcaecf6f97e4cf2 Mon Sep 17 00:00:00 2001 From: Jesus Angulo Date: Sun, 30 Jun 2019 21:06:02 -0500 Subject: [PATCH 04/22] Fix incorrect command rendered (#949) When you read docs the command about enabling RBAC permissions for Kubernetes integration are badly rendered. --- docs/features/kubernetes.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/features/kubernetes.rst b/docs/features/kubernetes.rst index be23845b..75568ec4 100644 --- a/docs/features/kubernetes.rst +++ b/docs/features/kubernetes.rst @@ -25,7 +25,7 @@ If you have services deployed in kubernetes you will normally use the naming ser You can replicate a Permissive. Using RBAC role bindings. `Permissive RBAC Permissions `_, k8s api server and token will read from pod . -.. code-block::json +.. code-block::bash kubectl create clusterrolebinding permissive-binding --clusterrole=cluster-admin --user=admin --user=kubelet --group=system:serviceaccounts The following example shows how to set up a ReRoute that will work in kubernetes. The most important thing is the ServiceName which is made up of the From c08f873dab34638920f09eec86bba96fd2680ee8 Mon Sep 17 00:00:00 2001 From: Thiago Loureiro Date: Mon, 1 Jul 2019 13:19:11 +0800 Subject: [PATCH 05/22] Upgraded to .NET Core 2.2 Runtime/Sdk (#950) --- Dockerfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index 256d2278..ebbd1eab 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,5 +1,5 @@ #This is the base image used for any ran images -FROM microsoft/dotnet:2.1-aspnetcore-runtime AS base +FROM mcr.microsoft.com/dotnet/core/aspnet:2.2-stretch-slim AS base WORKDIR /app EXPOSE 80 @@ -7,7 +7,7 @@ EXPOSE 80 #It can also be used to run other CLI commands on the project, such as packing/deploying nuget packages. Some examples: #Run tests: docker build --target builder -t ocelot-build . && docker run ocelot-build test --logger:trx;LogFileName=results.trx #Run benchmarks: docker build --target builder --build-arg build_configuration=Release -t ocelot-build . && docker run ocelot-build run -c Release --project test/Ocelot.Benchmarks/Ocelot.Benchmarks.csproj -FROM microsoft/dotnet:2.1.502-sdk AS builder +FROM mcr.microsoft.com/dotnet/core/sdk:2.2-stretch AS builder WORKDIR /build #First we add only the project files so that we can cache nuget packages with dotnet restore COPY Ocelot.sln Ocelot.sln From 176a7bb960a8957ba55875600e9ed6025f692314 Mon Sep 17 00:00:00 2001 From: Marcelo Castagna Date: Mon, 1 Jul 2019 08:23:06 -0300 Subject: [PATCH 06/22] allow to add delegating handlers by type (#943) --- .../DependencyInjection/IOcelotBuilder.cs | 3 ++ .../DependencyInjection/OcelotBuilder.cs | 23 +++++++++++++ .../DependencyInjection/OcelotBuilderTests.cs | 32 +++++++++++++++++++ 3 files changed, 58 insertions(+) diff --git a/src/Ocelot/DependencyInjection/IOcelotBuilder.cs b/src/Ocelot/DependencyInjection/IOcelotBuilder.cs index 5e4421be..67585edd 100644 --- a/src/Ocelot/DependencyInjection/IOcelotBuilder.cs +++ b/src/Ocelot/DependencyInjection/IOcelotBuilder.cs @@ -1,6 +1,7 @@ using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Ocelot.Middleware.Multiplexer; +using System; using System.Net.Http; namespace Ocelot.DependencyInjection @@ -13,6 +14,8 @@ namespace Ocelot.DependencyInjection IMvcCoreBuilder MvcCoreBuilder { get; } + IOcelotBuilder AddDelegatingHandler(Type type, bool global = false); + IOcelotBuilder AddDelegatingHandler(bool global = false) where T : DelegatingHandler; diff --git a/src/Ocelot/DependencyInjection/OcelotBuilder.cs b/src/Ocelot/DependencyInjection/OcelotBuilder.cs index 958f6841..c8a4cc50 100644 --- a/src/Ocelot/DependencyInjection/OcelotBuilder.cs +++ b/src/Ocelot/DependencyInjection/OcelotBuilder.cs @@ -35,6 +35,7 @@ namespace Ocelot.DependencyInjection using Ocelot.Security; using Ocelot.Security.IPSecurity; using Ocelot.ServiceDiscovery; + using System; using System.Net.Http; using System.Reflection; @@ -165,6 +166,28 @@ namespace Ocelot.DependencyInjection Services.TryAddSingleton(); } + public IOcelotBuilder AddDelegatingHandler(Type delegateType, bool global = false) + { + if (!typeof(DelegatingHandler).IsAssignableFrom(delegateType)) throw new ArgumentOutOfRangeException(nameof(delegateType), delegateType.Name, "It is not a delegatin handler"); + + if (global) + { + Services.AddTransient(delegateType); + Services.AddTransient(s => + { + + var service = s.GetService(delegateType) as DelegatingHandler; + return new GlobalDelegatingHandler(service); + }); + } + else + { + Services.AddTransient(typeof(DelegatingHandler), delegateType); + } + + return this; + } + public IOcelotBuilder AddDelegatingHandler(bool global = false) where THandler : DelegatingHandler { diff --git a/test/Ocelot.UnitTests/DependencyInjection/OcelotBuilderTests.cs b/test/Ocelot.UnitTests/DependencyInjection/OcelotBuilderTests.cs index e9f39cec..b46649f8 100644 --- a/test/Ocelot.UnitTests/DependencyInjection/OcelotBuilderTests.cs +++ b/test/Ocelot.UnitTests/DependencyInjection/OcelotBuilderTests.cs @@ -47,6 +47,17 @@ namespace Ocelot.UnitTests.DependencyInjection .BDDfy(); } + [Fact] + public void should_add_type_specific_delegating_handlers_transient() + { + this.Given(x => WhenISetUpOcelotServices()) + .When(x => AddTypeSpecificTransientDelegatingHandler(typeof(FakeDelegatingHandler))) + .And(x => AddTypeSpecificTransientDelegatingHandler(typeof(FakeDelegatingHandlerTwo))) + .Then(x => ThenTheProviderIsRegisteredAndReturnsSpecificHandlers()) + .And(x => ThenTheSpecificHandlersAreTransient()) + .BDDfy(); + } + [Fact] public void should_add_global_delegating_handlers_transient() { @@ -58,6 +69,17 @@ namespace Ocelot.UnitTests.DependencyInjection .BDDfy(); } + [Fact] + public void should_add_global_type_delegating_handlers_transient() + { + this.Given(x => WhenISetUpOcelotServices()) + .When(x => AddTransientGlobalDelegatingHandler()) + .And(x => AddTransientGlobalDelegatingHandler()) + .Then(x => ThenTheProviderIsRegisteredAndReturnsHandlers()) + .And(x => ThenTheGlobalHandlersAreTransient()) + .BDDfy(); + } + [Fact] public void should_set_up_services() { @@ -156,6 +178,16 @@ namespace Ocelot.UnitTests.DependencyInjection _ocelotBuilder.AddDelegatingHandler(); } + private void AddTypeTransientGlobalDelegatingHandler(Type type) + { + _ocelotBuilder.AddDelegatingHandler(type, true); + } + + private void AddTypeSpecificTransientDelegatingHandler(Type type) + { + _ocelotBuilder.AddDelegatingHandler(type); + } + private void ThenTheProviderIsRegisteredAndReturnsHandlers() { _serviceProvider = _services.BuildServiceProvider(); From ad00f20a2310d8920ede3e127d5a7296e916ee34 Mon Sep 17 00:00:00 2001 From: Alex Kursov Date: Tue, 23 Jul 2019 20:54:42 -0700 Subject: [PATCH 07/22] Fix tracing wrapper. (#966) --- .../Middleware/Pipeline/OcelotPipelineBuilderExtensions.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Ocelot/Middleware/Pipeline/OcelotPipelineBuilderExtensions.cs b/src/Ocelot/Middleware/Pipeline/OcelotPipelineBuilderExtensions.cs index 2c0b14ed..3dbc4a48 100644 --- a/src/Ocelot/Middleware/Pipeline/OcelotPipelineBuilderExtensions.cs +++ b/src/Ocelot/Middleware/Pipeline/OcelotPipelineBuilderExtensions.cs @@ -79,12 +79,12 @@ namespace Ocelot.Middleware.Pipeline var diagnosticListener = (DiagnosticListener)app.ApplicationServices.GetService(typeof(DiagnosticListener)); var middlewareName = ocelotDelegate.Target.GetType().Name; - OcelotRequestDelegate wrapped = context => + OcelotRequestDelegate wrapped = async context => { try { Write(diagnosticListener, "Ocelot.MiddlewareStarted", middlewareName, context); - return ocelotDelegate(context); + await ocelotDelegate(context); } catch (Exception ex) { From 76fdf2922ac71da99af0aaaef0b2df7bd125fd83 Mon Sep 17 00:00:00 2001 From: Talary Date: Sun, 28 Jul 2019 14:31:00 +0100 Subject: [PATCH 08/22] Fix build errors (#972) * Fix build errors * Remove JSON pkg version as conflicted with Cake.Json in mono build --- build.cake | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/build.cake b/build.cake index 86843018..aabc6a0f 100644 --- a/build.cake +++ b/build.cake @@ -1,7 +1,7 @@ #tool "nuget:?package=GitVersion.CommandLine" #tool "nuget:?package=GitReleaseNotes" #addin nuget:?package=Cake.Json -#addin nuget:?package=Newtonsoft.Json&version=9.0.1 +#addin nuget:?package=Newtonsoft.Json #tool "nuget:?package=OpenCover" #tool "nuget:?package=ReportGenerator" #tool "nuget:?package=coveralls.net&version=0.7.0" @@ -363,8 +363,7 @@ Task("DownloadGitHubReleaseArtifacts") Information("Release url " + releaseUrl); var assets_url = Newtonsoft.Json.Linq.JObject.Parse(GetResource(releaseUrl)) - .GetValue("assets_url") - .Value(); + .Value("assets_url"); Information("Assets url " + assets_url); @@ -372,7 +371,7 @@ Task("DownloadGitHubReleaseArtifacts") Information("Assets " + assets_url); - foreach(var asset in Newtonsoft.Json.JsonConvert.DeserializeObject(assets)) + foreach(var asset in Newtonsoft.Json.JsonConvert.DeserializeObject(assets)) { Information("In the loop.."); From ffecbed43a33d65280a8ce90f98819a5ca24473d Mon Sep 17 00:00:00 2001 From: rjwester Date: Sun, 28 Jul 2019 11:01:17 -0400 Subject: [PATCH 09/22] Fix to AddOcelot(), Config File Merge (#970) * [develop]: Added more functionality to configuration files as suggested by CesarD * [develop]: Clean up --- .../DependencyInjection/ConfigurationBuilderExtensions.cs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/Ocelot/DependencyInjection/ConfigurationBuilderExtensions.cs b/src/Ocelot/DependencyInjection/ConfigurationBuilderExtensions.cs index 8d75bd97..350afd28 100644 --- a/src/Ocelot/DependencyInjection/ConfigurationBuilderExtensions.cs +++ b/src/Ocelot/DependencyInjection/ConfigurationBuilderExtensions.cs @@ -40,15 +40,13 @@ namespace Ocelot.DependencyInjection const string globalConfigFile = "ocelot.global.json"; - const string subConfigPattern = @"^ocelot\.[a-zA-Z0-9]+\.json$"; - - string excludeConfigName = env?.EnvironmentName != null ? $"ocelot.{env.EnvironmentName}.json" : string.Empty; + string subConfigPattern = $@"^ocelot\.([a-zA-Z0-9]+|[a-zA-Z0-9]+\.{env?.EnvironmentName})\.json$"; var reg = new Regex(subConfigPattern, RegexOptions.IgnoreCase | RegexOptions.Singleline); var files = new DirectoryInfo(folder) .EnumerateFiles() - .Where(fi => reg.IsMatch(fi.Name) && (fi.Name != excludeConfigName)) + .Where(fi => reg.IsMatch(fi.Name)) .ToList(); var fileConfiguration = new FileConfiguration(); From 60e3a3737a7f8fd285f7969713fb0f925d5e6aaa Mon Sep 17 00:00:00 2001 From: Thiago Loureiro Date: Sun, 28 Jul 2019 23:27:27 +0800 Subject: [PATCH 10/22] Revert "Fix to AddOcelot(), Config File Merge (#970)" (#974) This reverts commit ffecbed43a33d65280a8ce90f98819a5ca24473d. --- .../DependencyInjection/ConfigurationBuilderExtensions.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Ocelot/DependencyInjection/ConfigurationBuilderExtensions.cs b/src/Ocelot/DependencyInjection/ConfigurationBuilderExtensions.cs index 350afd28..8d75bd97 100644 --- a/src/Ocelot/DependencyInjection/ConfigurationBuilderExtensions.cs +++ b/src/Ocelot/DependencyInjection/ConfigurationBuilderExtensions.cs @@ -40,13 +40,15 @@ namespace Ocelot.DependencyInjection const string globalConfigFile = "ocelot.global.json"; - string subConfigPattern = $@"^ocelot\.([a-zA-Z0-9]+|[a-zA-Z0-9]+\.{env?.EnvironmentName})\.json$"; + const string subConfigPattern = @"^ocelot\.[a-zA-Z0-9]+\.json$"; + + string excludeConfigName = env?.EnvironmentName != null ? $"ocelot.{env.EnvironmentName}.json" : string.Empty; var reg = new Regex(subConfigPattern, RegexOptions.IgnoreCase | RegexOptions.Singleline); var files = new DirectoryInfo(folder) .EnumerateFiles() - .Where(fi => reg.IsMatch(fi.Name)) + .Where(fi => reg.IsMatch(fi.Name) && (fi.Name != excludeConfigName)) .ToList(); var fileConfiguration = new FileConfiguration(); From 437515594d16d33af38e00732dcbd07b4f6d588d Mon Sep 17 00:00:00 2001 From: RobKraft Date: Mon, 12 Aug 2019 11:57:07 -0500 Subject: [PATCH 11/22] Include using statements in the code sample (#984) Include the necessary using statements in the code sample --- docs/introduction/gettingstarted.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/introduction/gettingstarted.rst b/docs/introduction/gettingstarted.rst index bb8d8bd6..cf2239fc 100644 --- a/docs/introduction/gettingstarted.rst +++ b/docs/introduction/gettingstarted.rst @@ -42,6 +42,9 @@ AddOcelot() (adds ocelot services), UseOcelot().Wait() (sets up all the Ocelot m .. code-block:: csharp + using Ocelot.DependencyInjection; + using Ocelot.Middleware; + public class Program { public static void Main(string[] args) From 647a21bb2da7be3b31e2d9c4d27524ec16b1ec25 Mon Sep 17 00:00:00 2001 From: Stjepan Date: Mon, 12 Aug 2019 18:59:28 +0200 Subject: [PATCH 12/22] Update requestid.rst (#973) Typo fix --- docs/features/requestid.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/features/requestid.rst b/docs/features/requestid.rst index 56c97b29..fb3f74cf 100644 --- a/docs/features/requestid.rst +++ b/docs/features/requestid.rst @@ -20,7 +20,7 @@ In your ocelot.json set the following in the GlobalConfiguration section. This w "RequestIdKey": "OcRequestId" } -I reccomend using the GlobalConfiguration unless you really need it to be ReRoute specific. +I recommend using the GlobalConfiguration unless you really need it to be ReRoute specific. *ReRoute* From b32850a8042456c45871f4cc0e7b5b9d56641729 Mon Sep 17 00:00:00 2001 From: liweihan Date: Tue, 13 Aug 2019 01:00:21 +0800 Subject: [PATCH 13/22] fix possible `IndexOutOfRange` bug (#979) --- src/Ocelot/Infrastructure/Claims/Parser/ClaimsParser.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Ocelot/Infrastructure/Claims/Parser/ClaimsParser.cs b/src/Ocelot/Infrastructure/Claims/Parser/ClaimsParser.cs index e25a58c8..157ebbd3 100644 --- a/src/Ocelot/Infrastructure/Claims/Parser/ClaimsParser.cs +++ b/src/Ocelot/Infrastructure/Claims/Parser/ClaimsParser.cs @@ -1,6 +1,6 @@ namespace Ocelot.Infrastructure.Claims.Parser { - using Microsoft.Extensions.Primitives; + using Microsoft.Extensions.Primitives; using Responses; using System.Collections.Generic; using System.Linq; @@ -24,7 +24,7 @@ var splits = claimResponse.Data.Split(delimiter.ToCharArray()); - if (splits.Length < index || index < 0) + if (splits.Length <= index || index < 0) { return new ErrorResponse(new CannotFindClaimError($"Cannot find claim for key: {key}, delimiter: {delimiter}, index: {index}")); } @@ -55,4 +55,4 @@ return new ErrorResponse(new CannotFindClaimError($"Cannot find claim for key: {key}")); } } -} +} From 81173663132787b63404c0fcb1b973d3a0334415 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20Martos?= Date: Mon, 12 Aug 2019 19:03:20 +0200 Subject: [PATCH 14/22] [New feature] Support claims to path transformation (#968) * Add the option to change DownstreamPath based on Claims * Add tests for Claims to downstream path --- .../Builder/DownstreamReRouteBuilder.cs | 10 +- .../Configuration/Creator/ReRoutesCreator.cs | 3 + src/Ocelot/Configuration/DownstreamReRoute.cs | 3 + src/Ocelot/Configuration/File/FileReRoute.cs | 2 + .../DependencyInjection/OcelotBuilder.cs | 2 + .../ChangeDownstreamPathTemplate.cs | 52 +++++ .../IChangeDownstreamPathTemplate.cs | 18 ++ .../ClaimsToDownstreamPathMiddleware.cs | 42 ++++ ...imsToDownstreamPathMiddlewareExtensions.cs | 12 ++ .../Pipeline/OcelotPipelineExtensions.cs | 3 + .../ClaimsToDownstreamPathTests.cs | 201 ++++++++++++++++++ .../ChangeDownstreamPathTemplateTests.cs | 196 +++++++++++++++++ .../ClaimsToDownstreamPathMiddlewareTests.cs | 101 +++++++++ 13 files changed, 644 insertions(+), 1 deletion(-) create mode 100644 src/Ocelot/DownstreamPathManipulation/ChangeDownstreamPathTemplate.cs create mode 100644 src/Ocelot/DownstreamPathManipulation/IChangeDownstreamPathTemplate.cs create mode 100644 src/Ocelot/DownstreamPathManipulation/Middleware/ClaimsToDownstreamPathMiddleware.cs create mode 100644 src/Ocelot/DownstreamPathManipulation/Middleware/ClaimsToDownstreamPathMiddlewareExtensions.cs create mode 100644 test/Ocelot.AcceptanceTests/ClaimsToDownstreamPathTests.cs create mode 100644 test/Ocelot.UnitTests/DownstreamPathManipulation/ChangeDownstreamPathTemplateTests.cs create mode 100644 test/Ocelot.UnitTests/DownstreamPathManipulation/ClaimsToDownstreamPathMiddlewareTests.cs diff --git a/src/Ocelot/Configuration/Builder/DownstreamReRouteBuilder.cs b/src/Ocelot/Configuration/Builder/DownstreamReRouteBuilder.cs index 2d7e9207..4b3e6ea3 100644 --- a/src/Ocelot/Configuration/Builder/DownstreamReRouteBuilder.cs +++ b/src/Ocelot/Configuration/Builder/DownstreamReRouteBuilder.cs @@ -19,6 +19,7 @@ namespace Ocelot.Configuration.Builder private Dictionary _routeClaimRequirement; private bool _isAuthorised; private List _claimToQueries; + private List _claimToDownstreamPath; private string _requestIdHeaderKey; private bool _isCached; private CacheOptions _fileCacheOptions; @@ -127,6 +128,12 @@ namespace Ocelot.Configuration.Builder return this; } + public DownstreamReRouteBuilder WithClaimsToDownstreamPath(List input) + { + _claimToDownstreamPath = input; + return this; + } + public DownstreamReRouteBuilder WithIsCached(bool input) { _isCached = input; @@ -186,7 +193,7 @@ namespace Ocelot.Configuration.Builder _serviceName = serviceName; return this; } - + public DownstreamReRouteBuilder WithServiceNamespace(string serviceNamespace) { _serviceNamespace = serviceNamespace; @@ -265,6 +272,7 @@ namespace Ocelot.Configuration.Builder _claimToQueries, _claimsToHeaders, _claimToClaims, + _claimToDownstreamPath, _isAuthenticated, _isAuthorised, _authenticationOptions, diff --git a/src/Ocelot/Configuration/Creator/ReRoutesCreator.cs b/src/Ocelot/Configuration/Creator/ReRoutesCreator.cs index 52b81985..4443e201 100644 --- a/src/Ocelot/Configuration/Creator/ReRoutesCreator.cs +++ b/src/Ocelot/Configuration/Creator/ReRoutesCreator.cs @@ -86,6 +86,8 @@ namespace Ocelot.Configuration.Creator var claimsToQueries = _claimsToThingCreator.Create(fileReRoute.AddQueriesToRequest); + var claimsToDownstreamPath = _claimsToThingCreator.Create(fileReRoute.ChangeDownstreamPathTemplate); + var qosOptions = _qosOptionsCreator.Create(fileReRoute.QoSOptions, fileReRoute.UpstreamPathTemplate, fileReRoute.UpstreamHttpMethod); var rateLimitOption = _rateLimitOptionsCreator.Create(fileReRoute.RateLimitOptions, globalConfiguration); @@ -114,6 +116,7 @@ namespace Ocelot.Configuration.Creator .WithRouteClaimsRequirement(fileReRoute.RouteClaimsRequirement) .WithIsAuthorised(fileReRouteOptions.IsAuthorised) .WithClaimsToQueries(claimsToQueries) + .WithClaimsToDownstreamPath(claimsToDownstreamPath) .WithRequestIdKey(requestIdKey) .WithIsCached(fileReRouteOptions.IsCached) .WithCacheOptions(new CacheOptions(fileReRoute.FileCacheOptions.TtlSeconds, region)) diff --git a/src/Ocelot/Configuration/DownstreamReRoute.cs b/src/Ocelot/Configuration/DownstreamReRoute.cs index cf421285..e8dfade5 100644 --- a/src/Ocelot/Configuration/DownstreamReRoute.cs +++ b/src/Ocelot/Configuration/DownstreamReRoute.cs @@ -28,6 +28,7 @@ namespace Ocelot.Configuration List claimsToQueries, List claimsToHeaders, List claimsToClaims, + List claimsToPath, bool isAuthenticated, bool isAuthorised, AuthenticationOptions authenticationOptions, @@ -63,6 +64,7 @@ namespace Ocelot.Configuration ClaimsToQueries = claimsToQueries ?? new List(); ClaimsToHeaders = claimsToHeaders ?? new List(); ClaimsToClaims = claimsToClaims ?? new List(); + ClaimsToPath = claimsToPath ?? new List(); IsAuthenticated = isAuthenticated; IsAuthorised = isAuthorised; AuthenticationOptions = authenticationOptions; @@ -93,6 +95,7 @@ namespace Ocelot.Configuration public List ClaimsToQueries { get; } public List ClaimsToHeaders { get; } public List ClaimsToClaims { get; } + public List ClaimsToPath { get; } public bool IsAuthenticated { get; } public bool IsAuthorised { get; } public AuthenticationOptions AuthenticationOptions { get; } diff --git a/src/Ocelot/Configuration/File/FileReRoute.cs b/src/Ocelot/Configuration/File/FileReRoute.cs index bad0e2af..b15653ea 100644 --- a/src/Ocelot/Configuration/File/FileReRoute.cs +++ b/src/Ocelot/Configuration/File/FileReRoute.cs @@ -11,6 +11,7 @@ namespace Ocelot.Configuration.File AddClaimsToRequest = new Dictionary(); RouteClaimsRequirement = new Dictionary(); AddQueriesToRequest = new Dictionary(); + ChangeDownstreamPathTemplate = new Dictionary(); DownstreamHeaderTransform = new Dictionary(); FileCacheOptions = new FileCacheOptions(); QoSOptions = new FileQoSOptions(); @@ -34,6 +35,7 @@ namespace Ocelot.Configuration.File public Dictionary AddClaimsToRequest { get; set; } public Dictionary RouteClaimsRequirement { get; set; } public Dictionary AddQueriesToRequest { get; set; } + public Dictionary ChangeDownstreamPathTemplate { get; set; } public string RequestIdKey { get; set; } public FileCacheOptions FileCacheOptions { get; set; } public bool ReRouteIsCaseSensitive { get; set; } diff --git a/src/Ocelot/DependencyInjection/OcelotBuilder.cs b/src/Ocelot/DependencyInjection/OcelotBuilder.cs index c8a4cc50..3f9d3fb5 100644 --- a/src/Ocelot/DependencyInjection/OcelotBuilder.cs +++ b/src/Ocelot/DependencyInjection/OcelotBuilder.cs @@ -25,6 +25,7 @@ namespace Ocelot.DependencyInjection using Ocelot.Logging; using Ocelot.Middleware; using Ocelot.Middleware.Multiplexer; + using Ocelot.PathManipulation; using Ocelot.QueryStrings; using Ocelot.RateLimit; using Ocelot.Request.Creator; @@ -92,6 +93,7 @@ namespace Ocelot.DependencyInjection Services.TryAddSingleton(); Services.TryAddSingleton(); Services.TryAddSingleton(); + Services.TryAddSingleton(); Services.TryAddSingleton(); Services.TryAddSingleton(); Services.TryAddSingleton(); diff --git a/src/Ocelot/DownstreamPathManipulation/ChangeDownstreamPathTemplate.cs b/src/Ocelot/DownstreamPathManipulation/ChangeDownstreamPathTemplate.cs new file mode 100644 index 00000000..a458992f --- /dev/null +++ b/src/Ocelot/DownstreamPathManipulation/ChangeDownstreamPathTemplate.cs @@ -0,0 +1,52 @@ +using System.Collections.Generic; +using System.Linq; +using System.Security.Claims; +using Ocelot.Configuration; +using Ocelot.DownstreamRouteFinder.UrlMatcher; +using Ocelot.Infrastructure; +using Ocelot.Infrastructure.Claims.Parser; +using Ocelot.Responses; +using Ocelot.Values; + +namespace Ocelot.PathManipulation +{ + public class ChangeDownstreamPathTemplate : IChangeDownstreamPathTemplate + { + private readonly IClaimsParser _claimsParser; + + public ChangeDownstreamPathTemplate(IClaimsParser claimsParser) + { + _claimsParser = claimsParser; + } + + public Response ChangeDownstreamPath(List claimsToThings, IEnumerable claims, + DownstreamPathTemplate downstreamPathTemplate, List placeholders) + { + foreach (var config in claimsToThings) + { + var value = _claimsParser.GetValue(claims, config.NewKey, config.Delimiter, config.Index); + + if (value.IsError) + { + return new ErrorResponse(value.Errors); + } + + var placeholderName = $"{{{config.ExistingKey}}}"; + + if (!downstreamPathTemplate.Value.Contains(placeholderName)) + { + return new ErrorResponse(new CouldNotFindPlaceholderError(placeholderName)); + } + + if (placeholders.Any(ph => ph.Name == placeholderName)) + { + placeholders.RemoveAll(ph => ph.Name == placeholderName); + } + + placeholders.Add(new PlaceholderNameAndValue(placeholderName, value.Data)); + } + + return new OkResponse(); + } + } +} diff --git a/src/Ocelot/DownstreamPathManipulation/IChangeDownstreamPathTemplate.cs b/src/Ocelot/DownstreamPathManipulation/IChangeDownstreamPathTemplate.cs new file mode 100644 index 00000000..36ed7f6d --- /dev/null +++ b/src/Ocelot/DownstreamPathManipulation/IChangeDownstreamPathTemplate.cs @@ -0,0 +1,18 @@ +using Ocelot.Configuration; +using Ocelot.DownstreamRouteFinder.UrlMatcher; +using Ocelot.Request.Middleware; +using Ocelot.Responses; +using Ocelot.Values; +using System; +using System.Collections.Generic; +using System.Security.Claims; +using System.Text; + +namespace Ocelot.PathManipulation +{ + public interface IChangeDownstreamPathTemplate + { + Response ChangeDownstreamPath(List claimsToThings, IEnumerable claims, + DownstreamPathTemplate downstreamPathTemplate, List placeholders); + } +} diff --git a/src/Ocelot/DownstreamPathManipulation/Middleware/ClaimsToDownstreamPathMiddleware.cs b/src/Ocelot/DownstreamPathManipulation/Middleware/ClaimsToDownstreamPathMiddleware.cs new file mode 100644 index 00000000..47107c67 --- /dev/null +++ b/src/Ocelot/DownstreamPathManipulation/Middleware/ClaimsToDownstreamPathMiddleware.cs @@ -0,0 +1,42 @@ +using Ocelot.Logging; +using Ocelot.Middleware; +using System.Linq; +using System.Threading.Tasks; + +namespace Ocelot.PathManipulation.Middleware +{ + public class ClaimsToDownstreamPathMiddleware : OcelotMiddleware + { + private readonly OcelotRequestDelegate _next; + private readonly IChangeDownstreamPathTemplate _changeDownstreamPathTemplate; + + public ClaimsToDownstreamPathMiddleware(OcelotRequestDelegate next, + IOcelotLoggerFactory loggerFactory, + IChangeDownstreamPathTemplate changeDownstreamPathTemplate) + : base(loggerFactory.CreateLogger()) + { + _next = next; + _changeDownstreamPathTemplate = changeDownstreamPathTemplate; + } + + public async Task Invoke(DownstreamContext context) + { + if (context.DownstreamReRoute.ClaimsToPath.Any()) + { + Logger.LogInformation($"{context.DownstreamReRoute.DownstreamPathTemplate.Value} has instructions to convert claims to path"); + var response = _changeDownstreamPathTemplate.ChangeDownstreamPath(context.DownstreamReRoute.ClaimsToPath, context.HttpContext.User.Claims, + context.DownstreamReRoute.DownstreamPathTemplate, context.TemplatePlaceholderNameAndValues); + + if (response.IsError) + { + Logger.LogWarning("there was an error setting queries on context, setting pipeline error"); + + SetPipelineError(context, response.Errors); + return; + } + } + + await _next.Invoke(context); + } + } +} diff --git a/src/Ocelot/DownstreamPathManipulation/Middleware/ClaimsToDownstreamPathMiddlewareExtensions.cs b/src/Ocelot/DownstreamPathManipulation/Middleware/ClaimsToDownstreamPathMiddlewareExtensions.cs new file mode 100644 index 00000000..04fbc78d --- /dev/null +++ b/src/Ocelot/DownstreamPathManipulation/Middleware/ClaimsToDownstreamPathMiddlewareExtensions.cs @@ -0,0 +1,12 @@ +using Ocelot.Middleware.Pipeline; + +namespace Ocelot.PathManipulation.Middleware +{ + public static class ClaimsToDownstreamPathMiddlewareExtensions + { + public static IOcelotPipelineBuilder UseClaimsToDownstreamPathMiddleware(this IOcelotPipelineBuilder builder) + { + return builder.UseMiddleware(); + } + } +} diff --git a/src/Ocelot/Middleware/Pipeline/OcelotPipelineExtensions.cs b/src/Ocelot/Middleware/Pipeline/OcelotPipelineExtensions.cs index b75a5b1d..d52cfb0d 100644 --- a/src/Ocelot/Middleware/Pipeline/OcelotPipelineExtensions.cs +++ b/src/Ocelot/Middleware/Pipeline/OcelotPipelineExtensions.cs @@ -7,6 +7,7 @@ using Ocelot.DownstreamUrlCreator.Middleware; using Ocelot.Errors.Middleware; using Ocelot.Headers.Middleware; using Ocelot.LoadBalancer.Middleware; +using Ocelot.PathManipulation.Middleware; using Ocelot.QueryStrings.Middleware; using Ocelot.RateLimit.Middleware; using Ocelot.Request.Middleware; @@ -118,6 +119,8 @@ namespace Ocelot.Middleware.Pipeline // Now we can run any claims to query string transformation middleware builder.UseClaimsToQueryStringMiddleware(); + builder.UseClaimsToDownstreamPathMiddleware(); + // Get the load balancer for this request builder.UseLoadBalancingMiddleware(); diff --git a/test/Ocelot.AcceptanceTests/ClaimsToDownstreamPathTests.cs b/test/Ocelot.AcceptanceTests/ClaimsToDownstreamPathTests.cs new file mode 100644 index 00000000..5e3f667f --- /dev/null +++ b/test/Ocelot.AcceptanceTests/ClaimsToDownstreamPathTests.cs @@ -0,0 +1,201 @@ +using Xunit; + +namespace Ocelot.AcceptanceTests +{ + using IdentityServer4.AccessTokenValidation; + using IdentityServer4.Models; + using IdentityServer4.Test; + using Microsoft.AspNetCore.Builder; + using Microsoft.AspNetCore.Hosting; + using Microsoft.AspNetCore.Http; + using Microsoft.Extensions.DependencyInjection; + using Ocelot.Configuration.File; + using Shouldly; + using System; + using System.Collections.Generic; + using System.IO; + using System.Net; + using TestStack.BDDfy; + + public class ClaimsToDownstreamPathTests : IDisposable + { + private IWebHost _servicebuilder; + private IWebHost _identityServerBuilder; + private readonly Steps _steps; + private Action _options; + private string _identityServerRootUrl = "http://localhost:57888"; + private string _downstreamFinalPath; + + public ClaimsToDownstreamPathTests() + { + _steps = new Steps(); + _options = o => + { + o.Authority = _identityServerRootUrl; + o.ApiName = "api"; + o.RequireHttpsMetadata = false; + o.SupportedTokens = SupportedTokens.Both; + o.ApiSecret = "secret"; + }; + } + + [Fact] + public void should_return_200_and_change_downstream_path() + { + var user = new TestUser() + { + Username = "test", + Password = "test", + SubjectId = "registered|1231231", + }; + + var configuration = new FileConfiguration + { + ReRoutes = new List + { + new FileReRoute + { + DownstreamPathTemplate = "/users/{userId}", + DownstreamHostAndPorts = new List + { + new FileHostAndPort + { + Host = "localhost", + Port = 57876, + }, + }, + DownstreamScheme = "http", + UpstreamPathTemplate = "/users", + UpstreamHttpMethod = new List { "Get" }, + AuthenticationOptions = new FileAuthenticationOptions + { + AuthenticationProviderKey = "Test", + AllowedScopes = new List + { + "openid", "offline_access", "api", + }, + }, + ChangeDownstreamPathTemplate = + { + {"userId", "Claims[sub] > value[1] > |"}, + }, + }, + }, + }; + + this.Given(x => x.GivenThereIsAnIdentityServerOn("http://localhost:57888", "api", AccessTokenType.Jwt, user)) + .And(x => x.GivenThereIsAServiceRunningOn("http://localhost:57876", 200)) + .And(x => _steps.GivenIHaveAToken("http://localhost:57888")) + .And(x => _steps.GivenThereIsAConfiguration(configuration)) + .And(x => _steps.GivenOcelotIsRunning(_options, "Test")) + .And(x => _steps.GivenIHaveAddedATokenToMyRequest()) + .When(x => _steps.WhenIGetUrlOnTheApiGateway("/users")) + .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) + .And(x => _steps.ThenTheResponseBodyShouldBe("UserId: 1231231")) + .And(x => _downstreamFinalPath.ShouldBe("/users/1231231")) + .BDDfy(); + } + + private void GivenThereIsAServiceRunningOn(string url, int statusCode) + { + _servicebuilder = new WebHostBuilder() + .UseUrls(url) + .UseKestrel() + .UseContentRoot(Directory.GetCurrentDirectory()) + .UseIISIntegration() + .UseUrls(url) + .Configure(app => + { + app.Run(async context => + { + _downstreamFinalPath = context.Request.Path.Value; + + string userId = _downstreamFinalPath.Replace("/users/", string.Empty); + + var responseBody = $"UserId: {userId}"; + context.Response.StatusCode = statusCode; + await context.Response.WriteAsync(responseBody); + }); + }) + .Build(); + + _servicebuilder.Start(); + } + + private void GivenThereIsAnIdentityServerOn(string url, string apiName, AccessTokenType tokenType, TestUser user) + { + _identityServerBuilder = new WebHostBuilder() + .UseUrls(url) + .UseKestrel() + .UseContentRoot(Directory.GetCurrentDirectory()) + .UseIISIntegration() + .UseUrls(url) + .ConfigureServices(services => + { + services.AddLogging(); + services.AddIdentityServer() + .AddDeveloperSigningCredential() + .AddInMemoryApiResources(new List + { + new ApiResource + { + Name = apiName, + Description = "My API", + Enabled = true, + DisplayName = "test", + Scopes = new List() + { + new Scope("api"), + new Scope("openid"), + new Scope("offline_access") + }, + ApiSecrets = new List() + { + new Secret + { + Value = "secret".Sha256() + } + }, + UserClaims = new List() + { + "CustomerId", "LocationId", "UserType", "UserId" + } + } + }) + .AddInMemoryClients(new List + { + new Client + { + ClientId = "client", + AllowedGrantTypes = GrantTypes.ResourceOwnerPassword, + ClientSecrets = new List {new Secret("secret".Sha256())}, + AllowedScopes = new List { apiName, "openid", "offline_access" }, + AccessTokenType = tokenType, + Enabled = true, + RequireClientSecret = false + } + }) + .AddTestUsers(new List + { + user + }); + }) + .Configure(app => + { + app.UseIdentityServer(); + }) + .Build(); + + _identityServerBuilder.Start(); + + _steps.VerifyIdentiryServerStarted(url); + } + + public void Dispose() + { + _servicebuilder?.Dispose(); + _steps.Dispose(); + _identityServerBuilder?.Dispose(); + } + } +} diff --git a/test/Ocelot.UnitTests/DownstreamPathManipulation/ChangeDownstreamPathTemplateTests.cs b/test/Ocelot.UnitTests/DownstreamPathManipulation/ChangeDownstreamPathTemplateTests.cs new file mode 100644 index 00000000..6e6f6c12 --- /dev/null +++ b/test/Ocelot.UnitTests/DownstreamPathManipulation/ChangeDownstreamPathTemplateTests.cs @@ -0,0 +1,196 @@ +using Moq; +using Ocelot.Configuration; +using Ocelot.DownstreamRouteFinder.UrlMatcher; +using Ocelot.Errors; +using Ocelot.Infrastructure; +using Ocelot.Infrastructure.Claims.Parser; +using Ocelot.PathManipulation; +using Ocelot.Responses; +using Ocelot.UnitTests.Responder; +using Ocelot.Values; +using Shouldly; +using System.Collections.Generic; +using System.Linq; +using System.Security.Claims; +using TestStack.BDDfy; +using Xunit; + +namespace Ocelot.UnitTests.DownstreamPathManipulation +{ + public class ChangeDownstreamPathTemplateTests + { + private readonly ChangeDownstreamPathTemplate _changeDownstreamPath; + private DownstreamPathTemplate _downstreamPathTemplate; + private readonly Mock _parser; + private List _configuration; + private List _claims; + private Response _result; + private Response _claimValue; + private List _placeholderValues; + + public ChangeDownstreamPathTemplateTests() + { + _parser = new Mock(); + _changeDownstreamPath = new ChangeDownstreamPathTemplate(_parser.Object); + } + + [Fact] + public void should_change_downstream_path_request() + { + var claims = new List + { + new Claim("test", "data"), + }; + var placeHolderValues = new List(); + this.Given( + x => x.GivenAClaimToThing(new List + { + new ClaimToThing("path-key", "", "", 0), + })) + .And(x => x.GivenClaims(claims)) + .And(x => x.GivenDownstreamPathTemplate("/api/test/{path-key}")) + .And(x => x.GivenPlaceholderNameAndValues(placeHolderValues)) + .And(x => x.GivenTheClaimParserReturns(new OkResponse("value"))) + .When(x => x.WhenIChangeDownstreamPath()) + .Then(x => x.ThenTheResultIsSuccess()) + .And(x => x.ThenClaimDataIsContainedInPlaceHolder("{path-key}", "value")) + .BDDfy(); + } + + [Fact] + public void should_replace_existing_placeholder_value() + { + var claims = new List + { + new Claim("test", "data"), + }; + var placeHolderValues = new List + { + new PlaceholderNameAndValue ("{path-key}", "old_value"), + }; + this.Given( + x => x.GivenAClaimToThing(new List + { + new ClaimToThing("path-key", "", "", 0), + })) + .And(x => x.GivenClaims(claims)) + .And(x => x.GivenDownstreamPathTemplate("/api/test/{path-key}")) + .And(x => x.GivenPlaceholderNameAndValues(placeHolderValues)) + .And(x => x.GivenTheClaimParserReturns(new OkResponse("value"))) + .When(x => x.WhenIChangeDownstreamPath()) + .Then(x => x.ThenTheResultIsSuccess()) + .And(x => x.ThenClaimDataIsContainedInPlaceHolder("{path-key}", "value")) + .BDDfy(); + } + + [Fact] + public void should_return_error_when_no_placeholder_in_downstream_path() + { + var claims = new List + { + new Claim("test", "data"), + }; + var placeHolderValues = new List(); + this.Given( + x => x.GivenAClaimToThing(new List + { + new ClaimToThing("path-key", "", "", 0), + })) + .And(x => x.GivenClaims(claims)) + .And(x => x.GivenDownstreamPathTemplate("/api/test")) + .And(x => x.GivenPlaceholderNameAndValues(placeHolderValues)) + .And(x => x.GivenTheClaimParserReturns(new OkResponse("value"))) + .When(x => x.WhenIChangeDownstreamPath()) + .Then(x => x.ThenTheResultIsCouldNotFindPlaceholderError()) + .BDDfy(); + } + + [Fact] + private void should_return_error_when_claim_parser_returns_error() + { + var claims = new List + { + new Claim("test", "data"), + }; + var placeHolderValues = new List(); + this.Given( + x => x.GivenAClaimToThing(new List + { + new ClaimToThing("path-key", "", "", 0), + })) + .And(x => x.GivenClaims(claims)) + .And(x => x.GivenDownstreamPathTemplate("/api/test/{path-key}")) + .And(x => x.GivenPlaceholderNameAndValues(placeHolderValues)) + .And(x => x.GivenTheClaimParserReturns(new ErrorResponse(new List + { + new AnyError(), + }))) + .When(x => x.WhenIChangeDownstreamPath()) + .Then(x => x.ThenTheResultIsError()) + .BDDfy(); + } + + private void GivenAClaimToThing(List configuration) + { + _configuration = configuration; + } + + private void GivenClaims(List claims) + { + _claims = claims; + } + + private void GivenDownstreamPathTemplate(string template) + { + _downstreamPathTemplate = new DownstreamPathTemplate(template); + } + + private void GivenPlaceholderNameAndValues(List placeholders) + { + _placeholderValues = placeholders; + } + + private void GivenTheClaimParserReturns(Response claimValue) + { + _claimValue = claimValue; + _parser + .Setup( + x => + x.GetValue(It.IsAny>(), + It.IsAny(), + It.IsAny(), + It.IsAny())) + .Returns(_claimValue); + } + + private void WhenIChangeDownstreamPath() + { + _result = _changeDownstreamPath.ChangeDownstreamPath(_configuration, _claims, + _downstreamPathTemplate, _placeholderValues); + } + + private void ThenTheResultIsSuccess() + { + _result.IsError.ShouldBe(false); + } + + private void ThenTheResultIsCouldNotFindPlaceholderError() + { + _result.IsError.ShouldBe(true); + _result.Errors.Count.ShouldBe(1); + _result.Errors.First().ShouldBeOfType(); + } + + private void ThenTheResultIsError() + { + _result.IsError.ShouldBe(true); + } + + private void ThenClaimDataIsContainedInPlaceHolder(string name, string value) + { + var placeHolder = _placeholderValues.FirstOrDefault(ph => ph.Name == name && ph.Value == value); + placeHolder.ShouldNotBeNull(); + _placeholderValues.Count.ShouldBe(1); + } + } +} diff --git a/test/Ocelot.UnitTests/DownstreamPathManipulation/ClaimsToDownstreamPathMiddlewareTests.cs b/test/Ocelot.UnitTests/DownstreamPathManipulation/ClaimsToDownstreamPathMiddlewareTests.cs new file mode 100644 index 00000000..80003300 --- /dev/null +++ b/test/Ocelot.UnitTests/DownstreamPathManipulation/ClaimsToDownstreamPathMiddlewareTests.cs @@ -0,0 +1,101 @@ +using Microsoft.AspNetCore.Http; +using Moq; +using Ocelot.Configuration; +using Ocelot.Configuration.Builder; +using Ocelot.DownstreamRouteFinder; +using Ocelot.DownstreamRouteFinder.UrlMatcher; +using Ocelot.Logging; +using Ocelot.Middleware; +using Ocelot.PathManipulation; +using Ocelot.PathManipulation.Middleware; +using Ocelot.Request.Middleware; +using Ocelot.Responses; +using Ocelot.Values; +using System.Collections.Generic; +using System.Net.Http; +using System.Security.Claims; +using System.Threading.Tasks; +using TestStack.BDDfy; +using Xunit; + +namespace Ocelot.UnitTests.DownstreamPathManipulation +{ + public class ClaimsToDownstreamPathMiddlewareTests + { + private readonly Mock _changePath; + private Mock _loggerFactory; + private Mock _logger; + private ClaimsToDownstreamPathMiddleware _middleware; + private DownstreamContext _downstreamContext; + private OcelotRequestDelegate _next; + + public ClaimsToDownstreamPathMiddlewareTests() + { + _downstreamContext = new DownstreamContext(new DefaultHttpContext()); + _loggerFactory = new Mock(); + _logger = new Mock(); + _loggerFactory.Setup(x => x.CreateLogger()).Returns(_logger.Object); + _next = context => Task.CompletedTask; + _changePath = new Mock(); + _downstreamContext.DownstreamRequest = new DownstreamRequest(new HttpRequestMessage(HttpMethod.Get, "http://test.com")); + _middleware = new ClaimsToDownstreamPathMiddleware(_next, _loggerFactory.Object, _changePath.Object); + } + + [Fact] + public void should_call_add_queries_correctly() + { + var downstreamRoute = new DownstreamRoute(new List(), + new ReRouteBuilder() + .WithDownstreamReRoute(new DownstreamReRouteBuilder() + .WithDownstreamPathTemplate("any old string") + .WithClaimsToDownstreamPath(new List + { + new ClaimToThing("UserId", "Subject", "", 0), + }) + .WithUpstreamHttpMethod(new List { "Get" }) + .Build()) + .WithUpstreamHttpMethod(new List { "Get" }) + .Build()); + + this.Given(x => x.GivenTheDownStreamRouteIs(downstreamRoute)) + .And(x => x.GivenTheChangeDownstreamPathReturnsOk()) + .When(x => x.WhenICallTheMiddleware()) + .Then(x => x.ThenChangeDownstreamPathIsCalledCorrectly()) + .BDDfy(); + + } + + private void WhenICallTheMiddleware() + { + _middleware.Invoke(_downstreamContext).GetAwaiter().GetResult(); + } + + private void GivenTheChangeDownstreamPathReturnsOk() + { + _changePath + .Setup(x => x.ChangeDownstreamPath( + It.IsAny>(), + It.IsAny>(), + It.IsAny(), + It.IsAny>())) + .Returns(new OkResponse()); + } + + private void ThenChangeDownstreamPathIsCalledCorrectly() + { + _changePath + .Verify(x => x.ChangeDownstreamPath( + It.IsAny>(), + It.IsAny>(), + _downstreamContext.DownstreamReRoute.DownstreamPathTemplate, + _downstreamContext.TemplatePlaceholderNameAndValues), Times.Once); + } + + private void GivenTheDownStreamRouteIs(DownstreamRoute downstreamRoute) + { + _downstreamContext.TemplatePlaceholderNameAndValues = downstreamRoute.TemplatePlaceholderNameAndValues; + _downstreamContext.DownstreamReRoute = downstreamRoute.ReRoute.DownstreamReRoute[0]; + } + + } +} From 7efb2174003271d7c4ef244e8c894f77d331808a Mon Sep 17 00:00:00 2001 From: Rick van Lieshout Date: Fri, 23 Aug 2019 17:50:01 +0200 Subject: [PATCH 15/22] Update bigpicture.rst (spelling) (#994) fixed 2 spelling/grammar mistakes. --- docs/introduction/bigpicture.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/introduction/bigpicture.rst b/docs/introduction/bigpicture.rst index b047abb4..0ea39e17 100644 --- a/docs/introduction/bigpicture.rst +++ b/docs/introduction/bigpicture.rst @@ -15,11 +15,11 @@ it reaches a request builder middleware where it creates a HttpRequestMessage ob used to make a request to a downstream service. The middleware that makes the request is the last thing in the Ocelot pipeline. It does not call the next middleware. The response from the downstream service is stored in a per request scoped repository -and retrived as the requests goes back up the Ocelot pipeline. There is a piece of middleware +and retrieved as the requests goes back up the Ocelot pipeline. There is a piece of middleware that maps the HttpResponseMessage onto the HttpResponse object and that is returned to the client. That is basically it with a bunch of other features. -The following are configuration that you use when deploying Ocelot. +The following are configurations that you use when deploying Ocelot. Basic Implementation ^^^^^^^^^^^^^^^^^^^^ From 0ac77f2e50b56435d982bfd15637477ebea86504 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20Martos?= Date: Fri, 23 Aug 2019 17:51:09 +0200 Subject: [PATCH 16/22] Add the documentation for ChangeDownstreamPathTemplate (#991) --- docs/features/claimstransformation.rst | 36 +++++++++++++++++++++----- 1 file changed, 30 insertions(+), 6 deletions(-) diff --git a/docs/features/claimstransformation.rst b/docs/features/claimstransformation.rst index ea45f46f..8d401317 100644 --- a/docs/features/claimstransformation.rst +++ b/docs/features/claimstransformation.rst @@ -2,22 +2,25 @@ Claims Transformation ===================== Ocelot allows the user to access claims and transform them into headers, query string -parameters and other claims. This is only available once a user has been authenticated. +parameters, other claims and change downstream paths. This is only available once a user +has been authenticated. After the user is authenticated we run the claims to claims transformation middleware. This allows the user to transform claims before the authorisation middleware is called. -After the user is authorised first we call the claims to headers middleware and Finally -the claims to query string parameters middleware. +After the user is authorised first we call the claims to headers middleware, then +the claims to query string parameters middleware, and Finally the claims to downstream path +middleware. The syntax for performing the transforms is the same for each process. In the ReRoute configuration a json dictionary is added with a specific name either AddClaimsToRequest, -AddHeadersToRequest, AddQueriesToRequest. +AddHeadersToRequest, AddQueriesToRequest, or ChangeDownstreamPathTemplate. Note: I'm not a hotshot programmer so have no idea if this syntax is good... Within this dictionary the entries specify how Ocelot should transform things! The key to the dictionary is going to become the key of either a claim, header -or query parameter. +or query parameter. In the case of ChangeDownstreamPathTemplate, the key must be +also specified in the DownstreamPathTemplate, in order to do the transformation. The value of the entry is parsed to logic that will perform the transform. First of all a dictionary accessor is specified e.g. Claims[CustomerId]. This means we want @@ -69,4 +72,25 @@ Below is an example configuration that will transforms claims to query string pa } This shows a transform where Ocelot looks at the users LocationId claim and add it as -a query string parameter to be forwarded onto the downstream service. \ No newline at end of file +a query string parameter to be forwarded onto the downstream service. + +Claims to Downstream Path Transformation +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Below is an example configuration that will transform claims to downstream path custom placeholders + +.. code-block:: json + + "UpstreamPathTemplate": "/api/users/me/{everything}", + "DownstreamPathTemplate": "/api/users/{userId}/{everything}", + "ChangeDownstreamPathTemplate": { + "userId": "Claims[sub] > value[1] > |", + } + +This shows a transform where Ocelot looks at the users userId claim and substitutes the value +to the "{userId}" placeholder specified in the DownstreamPathTemplate. Take into account that the +key specified in the ChangeDownstreamPathTemplate must be the same than the placeholder specified in +the DownstreamPathTemplate. + +Note: if a key specified in the ChangeDownstreamPathTemplate does not exist as a placeholder in DownstreamPathTemplate +it will fail at runtime returning an error in the response. \ No newline at end of file From b6f3f0f28a5a3681175486c8c0a99de40deb040a Mon Sep 17 00:00:00 2001 From: tdejong-tools4ever <54626878+tdejong-tools4ever@users.noreply.github.com> Date: Thu, 12 Sep 2019 17:07:34 +0200 Subject: [PATCH 17/22] Modified WebSocketMiddleWare to close connection on premature close (#998) --- .../WebSockets/Middleware/WebSocketsProxyMiddleware.cs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/Ocelot/WebSockets/Middleware/WebSocketsProxyMiddleware.cs b/src/Ocelot/WebSockets/Middleware/WebSocketsProxyMiddleware.cs index 6d56a489..bc567e36 100644 --- a/src/Ocelot/WebSockets/Middleware/WebSocketsProxyMiddleware.cs +++ b/src/Ocelot/WebSockets/Middleware/WebSocketsProxyMiddleware.cs @@ -47,6 +47,15 @@ namespace Ocelot.WebSockets.Middleware await destination.CloseOutputAsync(WebSocketCloseStatus.EndpointUnavailable, null, cancellationToken); return; } + catch (WebSocketException e) + { + if (e.WebSocketErrorCode == WebSocketError.ConnectionClosedPrematurely) + { + await destination.CloseOutputAsync(WebSocketCloseStatus.EndpointUnavailable, null, cancellationToken); + return; + } + throw; + } if (result.MessageType == WebSocketMessageType.Close) { From bef40041ba9ca3337f925cad618c3da9f7ce86be Mon Sep 17 00:00:00 2001 From: donaldgray Date: Thu, 12 Sep 2019 16:09:38 +0100 Subject: [PATCH 18/22] Add ConfigAwarePlaceholders class (#997) * Add ConfigAwarePlaceholders class This allows placeholder values to be sourced from IConfiguration in addition to set values. * Rework how IPlaceholders is decorated in container --- .../DependencyInjection/IOcelotBuilder.cs | 2 + .../DependencyInjection/OcelotBuilder.cs | 35 +++++++++ .../Infrastructure/ConfigAwarePlaceholders.cs | 61 +++++++++++++++ .../DependencyInjection/OcelotBuilderTests.cs | 23 ++++++ .../ConfigAwarePlaceholdersTests.cs | 78 +++++++++++++++++++ test/Ocelot.UnitTests/appsettings.json | 5 ++ 6 files changed, 204 insertions(+) create mode 100644 src/Ocelot/Infrastructure/ConfigAwarePlaceholders.cs create mode 100644 test/Ocelot.UnitTests/Infrastructure/ConfigAwarePlaceholdersTests.cs diff --git a/src/Ocelot/DependencyInjection/IOcelotBuilder.cs b/src/Ocelot/DependencyInjection/IOcelotBuilder.cs index 67585edd..13ec5a6a 100644 --- a/src/Ocelot/DependencyInjection/IOcelotBuilder.cs +++ b/src/Ocelot/DependencyInjection/IOcelotBuilder.cs @@ -24,5 +24,7 @@ namespace Ocelot.DependencyInjection IOcelotBuilder AddTransientDefinedAggregator() where T : class, IDefinedAggregator; + + IOcelotBuilder AddConfigPlaceholders(); } } diff --git a/src/Ocelot/DependencyInjection/OcelotBuilder.cs b/src/Ocelot/DependencyInjection/OcelotBuilder.cs index 3f9d3fb5..6733b54c 100644 --- a/src/Ocelot/DependencyInjection/OcelotBuilder.cs +++ b/src/Ocelot/DependencyInjection/OcelotBuilder.cs @@ -37,6 +37,7 @@ namespace Ocelot.DependencyInjection using Ocelot.Security.IPSecurity; using Ocelot.ServiceDiscovery; using System; + using System.Linq; using System.Net.Http; using System.Reflection; @@ -209,5 +210,39 @@ namespace Ocelot.DependencyInjection return this; } + + public IOcelotBuilder AddConfigPlaceholders() + { + // see: https://greatrexpectations.com/2018/10/25/decorators-in-net-core-with-dependency-injection + var wrappedDescriptor = Services.First(x => x.ServiceType == typeof(IPlaceholders)); + + var objectFactory = ActivatorUtilities.CreateFactory( + typeof(ConfigAwarePlaceholders), + new[] { typeof(IPlaceholders) }); + + Services.Replace(ServiceDescriptor.Describe( + typeof(IPlaceholders), + s => (IPlaceholders) objectFactory(s, + new[] {CreateInstance(s, wrappedDescriptor)}), + wrappedDescriptor.Lifetime + )); + + return this; + } + + private static object CreateInstance(IServiceProvider services, ServiceDescriptor descriptor) + { + if (descriptor.ImplementationInstance != null) + { + return descriptor.ImplementationInstance; + } + + if (descriptor.ImplementationFactory != null) + { + return descriptor.ImplementationFactory(services); + } + + return ActivatorUtilities.GetServiceOrCreateInstance(services, descriptor.ImplementationType); + } } } diff --git a/src/Ocelot/Infrastructure/ConfigAwarePlaceholders.cs b/src/Ocelot/Infrastructure/ConfigAwarePlaceholders.cs new file mode 100644 index 00000000..254dd4a5 --- /dev/null +++ b/src/Ocelot/Infrastructure/ConfigAwarePlaceholders.cs @@ -0,0 +1,61 @@ +namespace Ocelot.Infrastructure +{ + using System; + using System.Text.RegularExpressions; + using Microsoft.Extensions.Configuration; + using Request.Middleware; + using Responses; + + public class ConfigAwarePlaceholders : IPlaceholders + { + private readonly IConfiguration _configuration; + private readonly IPlaceholders _placeholders; + + public ConfigAwarePlaceholders(IConfiguration configuration, IPlaceholders placeholders) + { + _configuration = configuration; + _placeholders = placeholders; + } + + public Response Get(string key) + { + var placeholderResponse = _placeholders.Get(key); + + if (!placeholderResponse.IsError) + { + return placeholderResponse; + } + + return GetFromConfig(CleanKey(key)); + } + + public Response Get(string key, DownstreamRequest request) + { + var placeholderResponse = _placeholders.Get(key, request); + + if (!placeholderResponse.IsError) + { + return placeholderResponse; + } + + return GetFromConfig(CleanKey(key)); + } + + public Response Add(string key, Func> func) + => _placeholders.Add(key, func); + + public Response Remove(string key) + => _placeholders.Remove(key); + + private string CleanKey(string key) + => Regex.Replace(key, @"[{}]", string.Empty, RegexOptions.None); + + private Response GetFromConfig(string key) + { + var valueFromConfig = _configuration[key]; + return valueFromConfig == null + ? (Response) new ErrorResponse(new CouldNotFindPlaceholderError(key)) + : new OkResponse(valueFromConfig); + } + } +} diff --git a/test/Ocelot.UnitTests/DependencyInjection/OcelotBuilderTests.cs b/test/Ocelot.UnitTests/DependencyInjection/OcelotBuilderTests.cs index b46649f8..3ec06a8b 100644 --- a/test/Ocelot.UnitTests/DependencyInjection/OcelotBuilderTests.cs +++ b/test/Ocelot.UnitTests/DependencyInjection/OcelotBuilderTests.cs @@ -14,6 +14,7 @@ namespace Ocelot.UnitTests.DependencyInjection using System.Collections.Generic; using System.Linq; using System.Net.Http; + using Ocelot.Infrastructure; using TestStack.BDDfy; using Xunit; using static Ocelot.UnitTests.Middleware.UserDefinedResponseAggregatorTests; @@ -136,6 +137,16 @@ namespace Ocelot.UnitTests.DependencyInjection .BDDfy(); } + [Fact] + public void should_replace_iplaceholder() + { + this.Given(x => x.WhenISetUpOcelotServices()) + .When(x => AddConfigPlaceholders()) + .Then(x => ThenAnExceptionIsntThrown()) + .And(x => ThenTheIPlaceholderInstanceIsReplaced()) + .BDDfy(); + } + private void AddSingletonDefinedAggregator() where T : class, IDefinedAggregator { @@ -148,6 +159,11 @@ namespace Ocelot.UnitTests.DependencyInjection _ocelotBuilder.AddTransientDefinedAggregator(); } + private void AddConfigPlaceholders() + { + _ocelotBuilder.AddConfigPlaceholders(); + } + private void ThenTheSpecificHandlersAreTransient() { var handlers = _serviceProvider.GetServices().ToList(); @@ -235,6 +251,13 @@ namespace Ocelot.UnitTests.DependencyInjection _ocelotBuilder.ShouldBeOfType(); } + private void ThenTheIPlaceholderInstanceIsReplaced() + { + _serviceProvider = _services.BuildServiceProvider(); + var placeholders = _serviceProvider.GetService(); + placeholders.ShouldBeOfType(); + } + private void WhenISetUpOcelotServices() { try diff --git a/test/Ocelot.UnitTests/Infrastructure/ConfigAwarePlaceholdersTests.cs b/test/Ocelot.UnitTests/Infrastructure/ConfigAwarePlaceholdersTests.cs new file mode 100644 index 00000000..ed88fc9d --- /dev/null +++ b/test/Ocelot.UnitTests/Infrastructure/ConfigAwarePlaceholdersTests.cs @@ -0,0 +1,78 @@ +namespace Ocelot.UnitTests.Infrastructure +{ + using System; + using Moq; + using Ocelot.Infrastructure; + using Responses; + using Shouldly; + using Microsoft.Extensions.Configuration; + using Xunit; + + public class ConfigAwarePlaceholdersTests + { + private readonly IPlaceholders _placeholders; + private readonly Mock _basePlaceholders; + + public ConfigAwarePlaceholdersTests() + { + var configurationBuilder = new ConfigurationBuilder(); + configurationBuilder.AddJsonFile("appsettings.json"); + var configuration = configurationBuilder.Build(); + + _basePlaceholders = new Mock(); + _placeholders = new ConfigAwarePlaceholders(configuration, _basePlaceholders.Object); + } + + [Fact] + public void should_return_value_from_underlying_placeholders() + { + var baseUrl = "http://www.bbc.co.uk"; + const string key = "{BaseUrl}"; + + _basePlaceholders.Setup(x => x.Get(key)).Returns(new OkResponse(baseUrl)); + var result = _placeholders.Get(key); + result.Data.ShouldBe(baseUrl); + } + + [Fact] + public void should_return_value_from_config_with_same_name_as_placeholder_if_underlying_placeholder_not_found() + { + const string expected = "http://foo-bar.co.uk"; + var baseUrl = "http://www.bbc.co.uk"; + const string key = "{BaseUrl}"; + + _basePlaceholders.Setup(x => x.Get(key)).Returns(new ErrorResponse(new FakeError())); + var result = _placeholders.Get(key); + result.Data.ShouldBe(expected); + } + + [Theory] + [InlineData("{TestConfig}")] + [InlineData("{TestConfigNested:Child}")] + public void should_return_value_from_config(string key) + { + const string expected = "foo"; + + _basePlaceholders.Setup(x => x.Get(key)).Returns(new ErrorResponse(new FakeError())); + var result = _placeholders.Get(key); + result.Data.ShouldBe(expected); + } + + [Fact] + public void should_call_underyling_when_added() + { + const string key = "{Test}"; + Func> func = () => new OkResponse("test)"); + _placeholders.Add(key, func); + _basePlaceholders.Verify(p => p.Add(key, func), Times.Once); + } + + [Fact] + public void should_call_underyling_when_removed() + { + const string key = "{Test}"; + _placeholders.Remove(key); + _basePlaceholders.Verify(p => p.Remove(key), Times.Once); + } + } +} diff --git a/test/Ocelot.UnitTests/appsettings.json b/test/Ocelot.UnitTests/appsettings.json index 247f1ad7..57566b4e 100644 --- a/test/Ocelot.UnitTests/appsettings.json +++ b/test/Ocelot.UnitTests/appsettings.json @@ -20,5 +20,10 @@ "port": 5000, "hostName": "localhost" } + }, + "BaseUrl": "http://foo-bar.co.uk", + "TestConfig": "foo", + "TestConfigNested":{ + "Child": "foo" } } From 88f31966d4cb1e1bd57f96efe94ee3608f35c2d7 Mon Sep 17 00:00:00 2001 From: Ali Rasoulian Date: Mon, 23 Sep 2019 18:59:18 +0330 Subject: [PATCH 19/22] change config file's name regex pattern (#1014) change file name pattern to accept files name by dev's conditions. --- .../DependencyInjection/ConfigurationBuilderExtensions.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Ocelot/DependencyInjection/ConfigurationBuilderExtensions.cs b/src/Ocelot/DependencyInjection/ConfigurationBuilderExtensions.cs index 8d75bd97..54137b14 100644 --- a/src/Ocelot/DependencyInjection/ConfigurationBuilderExtensions.cs +++ b/src/Ocelot/DependencyInjection/ConfigurationBuilderExtensions.cs @@ -40,7 +40,7 @@ namespace Ocelot.DependencyInjection const string globalConfigFile = "ocelot.global.json"; - const string subConfigPattern = @"^ocelot\.[a-zA-Z0-9]+\.json$"; + const string subConfigPattern = @"^ocelot\.(.*?)\.json$"; string excludeConfigName = env?.EnvironmentName != null ? $"ocelot.{env.EnvironmentName}.json" : string.Empty; From c8a2144a4bcc11d3bc0b90510117a2bb3ea5654f Mon Sep 17 00:00:00 2001 From: Anderson de Paiva Date: Mon, 23 Sep 2019 16:33:37 +0100 Subject: [PATCH 20/22] Resolved BugFix #993 (#996) --- .../Authorisation/Middleware/AuthorisationMiddleware.cs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/Ocelot/Authorisation/Middleware/AuthorisationMiddleware.cs b/src/Ocelot/Authorisation/Middleware/AuthorisationMiddleware.cs index cc5cfda8..ca2f118f 100644 --- a/src/Ocelot/Authorisation/Middleware/AuthorisationMiddleware.cs +++ b/src/Ocelot/Authorisation/Middleware/AuthorisationMiddleware.cs @@ -25,7 +25,7 @@ public async Task Invoke(DownstreamContext context) { - if (IsAuthenticatedRoute(context.DownstreamReRoute)) + if (!IsOptionsHttpMethod(context) && IsAuthenticatedRoute(context.DownstreamReRoute)) { Logger.LogInformation("route is authenticated scopes must be checked"); @@ -52,7 +52,7 @@ } } - if (IsAuthorisedRoute(context.DownstreamReRoute)) + if (!IsOptionsHttpMethod(context) && IsAuthorisedRoute(context.DownstreamReRoute)) { Logger.LogInformation("route is authorised"); @@ -98,6 +98,11 @@ private static bool IsAuthorisedRoute(DownstreamReRoute reRoute) { return reRoute.IsAuthorised; + } + + private static bool IsOptionsHttpMethod(DownstreamContext context) + { + return context.HttpContext.Request.Method.ToUpper() == "OPTIONS"; } } } From f4c9e2a7377dd52ab71c7e65b6e322d9744ece72 Mon Sep 17 00:00:00 2001 From: Karol Deland Date: Sat, 5 Oct 2019 08:38:25 -0400 Subject: [PATCH 21/22] Fix typo LeadConnection --> LeastConnection (#1027) --- docs/features/loadbalancer.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/features/loadbalancer.rst b/docs/features/loadbalancer.rst index 947a6ab2..70706033 100644 --- a/docs/features/loadbalancer.rst +++ b/docs/features/loadbalancer.rst @@ -46,7 +46,7 @@ The following shows how to set up multiple downstream services for a ReRoute usi Service Discovery ^^^^^^^^^^^^^^^^^ -The following shows how to set up a ReRoute using service discovery then select the LeadConnection load balancer. +The following shows how to set up a ReRoute using service discovery then select the LeastConnection load balancer. .. code-block:: json From 903b380a5b93d36039989f2d5e6b3c467f249ff5 Mon Sep 17 00:00:00 2001 From: geffzhang Date: Mon, 28 Oct 2019 15:24:30 +0800 Subject: [PATCH 22/22] update .net core 3.0 RTM (#1025) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: update to asp.net core 3.0 preview 9 * fix : AspDotNetLogger unittest * feat: update generic host and useMvc 1、Using 'UseMvc' to configure MVC is not supported while using Endpoint Routing https://github.com/aspnet/AspNetCore/issues/9542 2、 use IHost and IHostBuilder * feat : update .net core 3.0 rc1 * eureka extension * fixed logger formatter error * fixed synchronous operations are disallowed of ReadToEnd method * fix log tests * Flush method of FakeStream should do nothing * Update ContentTests.cs * Fixed ws tests * feat: delelte comment code * feat: update .net core 3.0 RTM * Update OcelotBuilderTests.cs * Update .travis.yml mono 6.0.0 and dotnet 3.0.100 * Update Ocelot.IntegrationTests.csproj update Microsoft.Data.SQLite 3.0.0 * Update .travis.yml * feat: remove FrameworkReference 1、 remove FrameworkReference 2、 update package * add appveyor configuration to use version of VS2019 with dotnet core 3 sdk support * update obsoleted SetCollectionValidator method * Swap out OpenCover for Coverlet * Bump Cake to 0.35.0 * Downgrade coveralls.net to 0.7.0 Fix disposing of PollConsul instance * Remove environment specific path separator * Do not return ReportGenerator on Mac/Linux * Remove direct dependency on IInternalConfiguration * Fix ordering of variable assignment * Fix broken tests * Fix acceptance tests for Consul --- .travis.yml | 6 +- appveyor.yml | 1 + build.cake | 45 +-- .../Ocelot.Administration.csproj | 13 +- .../OcelotBuilderExtensions.cs | 30 +- .../Ocelot.Cache.CacheManager.csproj | 15 +- .../ConsulFileConfigurationRepository.cs | 22 +- .../Ocelot.Provider.Consul.csproj | 9 +- .../PollingConsulServiceDiscoveryProvider.cs | 11 +- .../Ocelot.Provider.Eureka.csproj | 11 +- .../OcelotBuilderExtensions.cs | 4 +- .../Ocelot.Provider.Kubernetes.csproj | 12 +- .../Ocelot.Provider.Polly.csproj | 11 +- .../Ocelot.Provider.Rafty.csproj | 13 +- .../Ocelot.Tracing.Butterfly.csproj | 7 +- .../DiskFileConfigurationRepository.cs | 2 +- .../FileConfigurationFluentValidator.cs | 4 +- .../Validator/ReRouteFluentValidator.cs | 4 +- .../ConfigurationBuilderExtensions.cs | 4 +- .../DependencyInjection/OcelotBuilder.cs | 2 +- .../ServiceCollectionExtensions.cs | 6 +- src/Ocelot/Logging/AspDotNetLogger.cs | 64 +++- .../Middleware/OcelotMiddlewareExtensions.cs | 2 +- src/Ocelot/Ocelot.csproj | 20 +- .../ConfigurationInConsulTests.cs | 49 +-- .../ConsulConfigurationInConsulTests.cs | 4 +- .../ConsulWebSocketTests.cs | 16 +- test/Ocelot.AcceptanceTests/ContentTests.cs | 6 +- test/Ocelot.AcceptanceTests/GzipTests.cs | 4 +- .../Ocelot.AcceptanceTests.csproj | 39 ++- test/Ocelot.AcceptanceTests/Steps.cs | 6 +- test/Ocelot.AcceptanceTests/WebSocketTests.cs | 20 +- .../Ocelot.Benchmarks.csproj | 8 +- .../AdministrationTests.cs | 322 ++++++++++-------- .../CacheManagerTests.cs | 60 ++-- .../Ocelot.IntegrationTests.csproj | 35 +- .../ThreadSafeHeadersTests.cs | 6 +- .../Ocelot.ManualTest.csproj | 22 +- test/Ocelot.ManualTest/Program.cs | 12 +- .../OcelotAdministrationBuilderTests.cs | 15 +- .../OcelotBuilderExtensionsTests.cs | 16 +- .../DiskFileConfigurationRepositoryTests.cs | 4 +- .../ConsulFileConfigurationRepositoryTests.cs | 30 +- .../Consul/OcelotBuilderExtensionsTests.cs | 16 +- ...lingConsulServiceDiscoveryProviderTests.cs | 34 +- .../Consul/ProviderFactoryTests.cs | 4 +- .../ConfigurationBuilderExtensionsTests.cs | 4 +- .../DependencyInjection/OcelotBuilderTests.cs | 17 +- ...ekaMiddlewareConfigurationProviderTests.cs | 2 +- .../OcelotBuilderExtensionsTests.cs | 15 +- .../Logging/AspDotNetLoggerTests.cs | 16 +- .../Middleware/OcelotPiplineBuilderTests.cs | 16 +- test/Ocelot.UnitTests/Ocelot.UnitTests.csproj | 50 +-- ...lotAdministrationBuilderExtensionsTests.cs | 15 +- .../ClientRateLimitMiddlewareTests.cs | 7 +- .../Request/Mapper/RequestMapperTests.cs | 6 +- test/Ocelot.UnitTests/UnitTests.runsettings | 23 ++ tools/packages.config | 2 +- 58 files changed, 711 insertions(+), 508 deletions(-) create mode 100644 appveyor.yml create mode 100644 test/Ocelot.UnitTests/UnitTests.runsettings diff --git a/.travis.yml b/.travis.yml index bd3ea077..813fa571 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,12 +8,12 @@ sudo: required dist: bionic # OS X 10.12 -osx_image: xcode9.2 +osx_image: xcode9.4 mono: - - 5.10.0 + - 6.0.0 -dotnet: 2.2.105 +dotnet: 3.0.100 before_install: - git fetch --unshallow # Travis always does a shallow clone, but GitVersion needs the full history including branches and tags diff --git a/appveyor.yml b/appveyor.yml new file mode 100644 index 00000000..6e79d909 --- /dev/null +++ b/appveyor.yml @@ -0,0 +1 @@ +image: Visual Studio 2019 \ No newline at end of file diff --git a/build.cake b/build.cake index aabc6a0f..5eb3d77c 100644 --- a/build.cake +++ b/build.cake @@ -2,10 +2,9 @@ #tool "nuget:?package=GitReleaseNotes" #addin nuget:?package=Cake.Json #addin nuget:?package=Newtonsoft.Json -#tool "nuget:?package=OpenCover" #tool "nuget:?package=ReportGenerator" #tool "nuget:?package=coveralls.net&version=0.7.0" -#addin Cake.Coveralls&version=0.7.0 +#addin Cake.Coveralls&version=0.10.1 // compile var compileConfig = Argument("configuration", "Release"); @@ -119,27 +118,20 @@ Task("RunUnitTests") .IsDependentOn("Compile") .Does(() => { + var testSettings = new DotNetCoreTestSettings + { + Configuration = compileConfig, + ResultsDirectory = artifactsForUnitTestsDir, + ArgumentCustomization = args => args + .Append("--settings test/Ocelot.UnitTests/UnitTests.runsettings") + }; + + EnsureDirectoryExists(artifactsForUnitTestsDir); + DotNetCoreTest(unitTestAssemblies, testSettings); + if (IsRunningOnWindows()) { - var coverageSummaryFile = artifactsForUnitTestsDir + File("coverage.xml"); - - EnsureDirectoryExists(artifactsForUnitTestsDir); - - OpenCover(tool => - { - tool.DotNetCoreTest(unitTestAssemblies); - }, - new FilePath(coverageSummaryFile), - new OpenCoverSettings() - { - Register="user", - ArgumentCustomization=args=>args.Append(@"-oldstyle -returntargetcode -excludebyattribute:*.ExcludeFromCoverage*") - } - .WithFilter("+[Ocelot*]*") - .WithFilter("-[xunit*]*") - .WithFilter("-[Ocelot*Tests]*") - ); - + var coverageSummaryFile = GetSubDirectories(artifactsForUnitTestsDir).First().CombineWithFilePath(File("coverage.opencover.xml")); ReportGenerator(coverageSummaryFile, artifactsForUnitTestsDir); if (AppVeyor.IsRunningOnAppVeyor) @@ -171,17 +163,6 @@ Task("RunUnitTests") var whereToCheck = !AppVeyor.IsRunningOnAppVeyor ? coverallsRepo : artifactsForUnitTestsDir; throw new Exception(string.Format("Code coverage fell below the threshold of {0}%. You can find the code coverage report at {1}", minCodeCoverage, whereToCheck)); }; - - } - else - { - var settings = new DotNetCoreTestSettings - { - Configuration = compileConfig, - }; - - EnsureDirectoryExists(artifactsForUnitTestsDir); - DotNetCoreTest(unitTestAssemblies, settings); } }); diff --git a/src/Ocelot.Administration/Ocelot.Administration.csproj b/src/Ocelot.Administration/Ocelot.Administration.csproj index a4aabbd8..abf0f33f 100644 --- a/src/Ocelot.Administration/Ocelot.Administration.csproj +++ b/src/Ocelot.Administration/Ocelot.Administration.csproj @@ -1,8 +1,6 @@  - netstandard2.0 - 2.0.0 - 2.0.0 + netcoreapp3.0 true Provides Ocelot extensions to use the administration API and IdentityService dependencies that come with it Ocelot.Administration @@ -29,10 +27,13 @@ - + all - - + + + + + diff --git a/src/Ocelot.Administration/OcelotBuilderExtensions.cs b/src/Ocelot.Administration/OcelotBuilderExtensions.cs index 207b3bcf..da25beaa 100644 --- a/src/Ocelot.Administration/OcelotBuilderExtensions.cs +++ b/src/Ocelot.Administration/OcelotBuilderExtensions.cs @@ -1,18 +1,18 @@ -namespace Ocelot.Administration -{ - using DependencyInjection; - using IdentityServer4.AccessTokenValidation; - using IdentityServer4.Models; - using Microsoft.AspNetCore.Builder; - using Microsoft.Extensions.Configuration; - using Microsoft.Extensions.DependencyInjection; - using Microsoft.Extensions.DependencyInjection.Extensions; - using Ocelot.Middleware; - using System; - using System.Collections.Generic; - using System.IdentityModel.Tokens.Jwt; - using System.Security.Cryptography.X509Certificates; +using Ocelot.DependencyInjection; +using IdentityServer4.AccessTokenValidation; +using IdentityServer4.Models; +using Microsoft.AspNetCore.Builder; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.DependencyInjection.Extensions; +using Ocelot.Middleware; +using System; +using System.Collections.Generic; +using System.IdentityModel.Tokens.Jwt; +using System.Security.Cryptography.X509Certificates; +namespace Ocelot.Administration +{ public static class OcelotBuilderExtensions { public static IOcelotAdministrationBuilder AddAdministration(this IOcelotBuilder builder, string path, string secret) @@ -86,7 +86,7 @@ else { //todo - refactor so calls method? - var cert = new X509Certificate2(identityServerConfiguration.CredentialsSigningCertificateLocation, identityServerConfiguration.CredentialsSigningCertificatePassword); + var cert = new X509Certificate2(identityServerConfiguration.CredentialsSigningCertificateLocation, identityServerConfiguration.CredentialsSigningCertificatePassword, X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.Exportable); identityServerBuilder.AddSigningCredential(cert); } } diff --git a/src/Ocelot.Cache.CacheManager/Ocelot.Cache.CacheManager.csproj b/src/Ocelot.Cache.CacheManager/Ocelot.Cache.CacheManager.csproj index 7941dcab..dab96133 100644 --- a/src/Ocelot.Cache.CacheManager/Ocelot.Cache.CacheManager.csproj +++ b/src/Ocelot.Cache.CacheManager/Ocelot.Cache.CacheManager.csproj @@ -1,8 +1,6 @@  - netstandard2.0 - 2.0.0 - 2.0.0 + netcoreapp3.0 true Provides Ocelot extensions to use CacheManager.Net Ocelot.Cache.CacheManager @@ -28,11 +26,14 @@ - + all - - - + + + + + + diff --git a/src/Ocelot.Provider.Consul/ConsulFileConfigurationRepository.cs b/src/Ocelot.Provider.Consul/ConsulFileConfigurationRepository.cs index 7d7f4d44..1044b7b6 100644 --- a/src/Ocelot.Provider.Consul/ConsulFileConfigurationRepository.cs +++ b/src/Ocelot.Provider.Consul/ConsulFileConfigurationRepository.cs @@ -4,6 +4,7 @@ using Configuration.Repository; using global::Consul; using Logging; + using Microsoft.Extensions.Options; using Newtonsoft.Json; using Responses; using System; @@ -18,29 +19,20 @@ private readonly IOcelotLogger _logger; public ConsulFileConfigurationRepository( + IOptions fileConfiguration, Cache.IOcelotCache cache, - IInternalConfigurationRepository repo, IConsulClientFactory factory, IOcelotLoggerFactory loggerFactory) { _logger = loggerFactory.CreateLogger(); _cache = cache; - var internalConfig = repo.Get(); + var serviceDiscoveryProvider = fileConfiguration.Value.GlobalConfiguration.ServiceDiscoveryProvider; + _configurationKey = string.IsNullOrWhiteSpace(serviceDiscoveryProvider.ConfigurationKey) ? "InternalConfiguration" : + serviceDiscoveryProvider.ConfigurationKey; - _configurationKey = "InternalConfiguration"; - - string token = null; - - if (!internalConfig.IsError) - { - token = internalConfig.Data.ServiceProviderConfiguration.Token; - _configurationKey = !string.IsNullOrEmpty(internalConfig.Data.ServiceProviderConfiguration.ConfigurationKey) ? - internalConfig.Data.ServiceProviderConfiguration.ConfigurationKey : _configurationKey; - } - - var config = new ConsulRegistryConfiguration(internalConfig.Data.ServiceProviderConfiguration.Host, - internalConfig.Data.ServiceProviderConfiguration.Port, _configurationKey, token); + var config = new ConsulRegistryConfiguration(serviceDiscoveryProvider.Host, + serviceDiscoveryProvider.Port, _configurationKey, serviceDiscoveryProvider.Token); _consul = factory.Get(config); } diff --git a/src/Ocelot.Provider.Consul/Ocelot.Provider.Consul.csproj b/src/Ocelot.Provider.Consul/Ocelot.Provider.Consul.csproj index 2df87ad4..61df395b 100644 --- a/src/Ocelot.Provider.Consul/Ocelot.Provider.Consul.csproj +++ b/src/Ocelot.Provider.Consul/Ocelot.Provider.Consul.csproj @@ -1,8 +1,6 @@  - netstandard2.0 - 2.0.0 - 2.0.0 + netcoreapp3.0 true Provides Ocelot extensions to use Consul Ocelot.Provider.Consul @@ -30,8 +28,11 @@ - + all + + + diff --git a/src/Ocelot.Provider.Consul/PollingConsulServiceDiscoveryProvider.cs b/src/Ocelot.Provider.Consul/PollingConsulServiceDiscoveryProvider.cs index 15abc777..5bace802 100644 --- a/src/Ocelot.Provider.Consul/PollingConsulServiceDiscoveryProvider.cs +++ b/src/Ocelot.Provider.Consul/PollingConsulServiceDiscoveryProvider.cs @@ -2,16 +2,17 @@ { using Logging; using ServiceDiscovery.Providers; + using System; using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; using Values; - public class PollConsul : IServiceDiscoveryProvider + public sealed class PollConsul : IServiceDiscoveryProvider, IDisposable { private readonly IOcelotLogger _logger; private readonly IServiceDiscoveryProvider _consulServiceDiscoveryProvider; - private readonly Timer _timer; + private Timer _timer; private bool _polling; private List _services; @@ -34,6 +35,12 @@ }, null, pollingInterval, pollingInterval); } + public void Dispose() + { + _timer?.Dispose(); + _timer = null; + } + public Task> Get() { return Task.FromResult(_services); diff --git a/src/Ocelot.Provider.Eureka/Ocelot.Provider.Eureka.csproj b/src/Ocelot.Provider.Eureka/Ocelot.Provider.Eureka.csproj index d25efc5a..2254cc2a 100644 --- a/src/Ocelot.Provider.Eureka/Ocelot.Provider.Eureka.csproj +++ b/src/Ocelot.Provider.Eureka/Ocelot.Provider.Eureka.csproj @@ -1,8 +1,6 @@  - netstandard2.0 - 2.0.0 - 2.0.0 + netcoreapp3.0 true Provides Ocelot extensions to use Eureka Ocelot.Provider.Eureka @@ -29,9 +27,12 @@ - - + + all + + + diff --git a/src/Ocelot.Provider.Eureka/OcelotBuilderExtensions.cs b/src/Ocelot.Provider.Eureka/OcelotBuilderExtensions.cs index 4a246e4f..e141d194 100644 --- a/src/Ocelot.Provider.Eureka/OcelotBuilderExtensions.cs +++ b/src/Ocelot.Provider.Eureka/OcelotBuilderExtensions.cs @@ -12,9 +12,7 @@ { public static IOcelotBuilder AddEureka(this IOcelotBuilder builder) { - var service = builder.Services.First(x => x.ServiceType == typeof(IConfiguration)); - var configuration = (IConfiguration)service.ImplementationInstance; - builder.Services.AddDiscoveryClient(configuration); + builder.Services.AddDiscoveryClient(builder.Configuration); builder.Services.AddSingleton(EurekaProviderFactory.Get); builder.Services.AddSingleton(EurekaMiddlewareConfigurationProvider.Get); return builder; diff --git a/src/Ocelot.Provider.Kubernetes/Ocelot.Provider.Kubernetes.csproj b/src/Ocelot.Provider.Kubernetes/Ocelot.Provider.Kubernetes.csproj index 436585da..6dba88d1 100644 --- a/src/Ocelot.Provider.Kubernetes/Ocelot.Provider.Kubernetes.csproj +++ b/src/Ocelot.Provider.Kubernetes/Ocelot.Provider.Kubernetes.csproj @@ -1,9 +1,7 @@  - netstandard2.0 - 2.0.0 - 2.0.0 + netcoreapp3.0 true Ocelot Provides Ocelot extensions to use kubernetes @@ -30,12 +28,16 @@ - - + + + + + + diff --git a/src/Ocelot.Provider.Polly/Ocelot.Provider.Polly.csproj b/src/Ocelot.Provider.Polly/Ocelot.Provider.Polly.csproj index fa15a189..36143432 100644 --- a/src/Ocelot.Provider.Polly/Ocelot.Provider.Polly.csproj +++ b/src/Ocelot.Provider.Polly/Ocelot.Provider.Polly.csproj @@ -1,8 +1,6 @@  - netstandard2.0 - 2.0.0 - 2.0.0 + netcoreapp3.0 true Provides Ocelot extensions to use Polly.NET Ocelot.Provider.Polly @@ -29,9 +27,12 @@ - + all - + + + + diff --git a/src/Ocelot.Provider.Rafty/Ocelot.Provider.Rafty.csproj b/src/Ocelot.Provider.Rafty/Ocelot.Provider.Rafty.csproj index f7e0996c..e6699562 100644 --- a/src/Ocelot.Provider.Rafty/Ocelot.Provider.Rafty.csproj +++ b/src/Ocelot.Provider.Rafty/Ocelot.Provider.Rafty.csproj @@ -1,8 +1,6 @@ - + - netstandard2.0 - 2.0.0 - 2.0.0 + netcoreapp3.0 true Provides Ocelot extensions to use Rafty Ocelot.Provider.Rafty @@ -30,10 +28,13 @@ - + - + all + + + diff --git a/src/Ocelot.Tracing.Butterfly/Ocelot.Tracing.Butterfly.csproj b/src/Ocelot.Tracing.Butterfly/Ocelot.Tracing.Butterfly.csproj index c161f183..6f2e8141 100644 --- a/src/Ocelot.Tracing.Butterfly/Ocelot.Tracing.Butterfly.csproj +++ b/src/Ocelot.Tracing.Butterfly/Ocelot.Tracing.Butterfly.csproj @@ -1,9 +1,7 @@  - netstandard2.0 - 2.0.0 - 2.0.0 + netcoreapp3.0 true This package provides methods to integrate Butterfly tracing with Ocelot. Ocelot.Tracing.Butterfly @@ -33,5 +31,8 @@ + + + diff --git a/src/Ocelot/Configuration/Repository/DiskFileConfigurationRepository.cs b/src/Ocelot/Configuration/Repository/DiskFileConfigurationRepository.cs index 8df82935..770d7b7a 100644 --- a/src/Ocelot/Configuration/Repository/DiskFileConfigurationRepository.cs +++ b/src/Ocelot/Configuration/Repository/DiskFileConfigurationRepository.cs @@ -14,7 +14,7 @@ namespace Ocelot.Configuration.Repository private static readonly object _lock = new object(); private const string ConfigurationFileName = "ocelot"; - public DiskFileConfigurationRepository(IHostingEnvironment hostingEnvironment) + public DiskFileConfigurationRepository(IWebHostEnvironment hostingEnvironment) { _environmentFilePath = $"{AppContext.BaseDirectory}{ConfigurationFileName}{(string.IsNullOrEmpty(hostingEnvironment.EnvironmentName) ? string.Empty : ".")}{hostingEnvironment.EnvironmentName}.json"; diff --git a/src/Ocelot/Configuration/Validator/FileConfigurationFluentValidator.cs b/src/Ocelot/Configuration/Validator/FileConfigurationFluentValidator.cs index 49e92355..add4fe0b 100644 --- a/src/Ocelot/Configuration/Validator/FileConfigurationFluentValidator.cs +++ b/src/Ocelot/Configuration/Validator/FileConfigurationFluentValidator.cs @@ -21,8 +21,8 @@ .GetServices() .ToList(); - RuleFor(configuration => configuration.ReRoutes) - .SetCollectionValidator(reRouteFluentValidator); + RuleForEach(configuration => configuration.ReRoutes) + .SetValidator(reRouteFluentValidator); RuleFor(configuration => configuration.GlobalConfiguration) .SetValidator(fileGlobalConfigurationFluentValidator); diff --git a/src/Ocelot/Configuration/Validator/ReRouteFluentValidator.cs b/src/Ocelot/Configuration/Validator/ReRouteFluentValidator.cs index 724d92bb..746c430c 100644 --- a/src/Ocelot/Configuration/Validator/ReRouteFluentValidator.cs +++ b/src/Ocelot/Configuration/Validator/ReRouteFluentValidator.cs @@ -80,8 +80,8 @@ When(reRoute => string.IsNullOrEmpty(reRoute.ServiceName), () => { - RuleFor(reRoute => reRoute.DownstreamHostAndPorts) - .SetCollectionValidator(hostAndPortValidator); + RuleForEach(reRoute => reRoute.DownstreamHostAndPorts) + .SetValidator(hostAndPortValidator); }); } diff --git a/src/Ocelot/DependencyInjection/ConfigurationBuilderExtensions.cs b/src/Ocelot/DependencyInjection/ConfigurationBuilderExtensions.cs index 54137b14..3d29ef51 100644 --- a/src/Ocelot/DependencyInjection/ConfigurationBuilderExtensions.cs +++ b/src/Ocelot/DependencyInjection/ConfigurationBuilderExtensions.cs @@ -29,12 +29,12 @@ namespace Ocelot.DependencyInjection return builder; } - public static IConfigurationBuilder AddOcelot(this IConfigurationBuilder builder, IHostingEnvironment env) + public static IConfigurationBuilder AddOcelot(this IConfigurationBuilder builder, IWebHostEnvironment env) { return builder.AddOcelot(".", env); } - public static IConfigurationBuilder AddOcelot(this IConfigurationBuilder builder, string folder, IHostingEnvironment env) + public static IConfigurationBuilder AddOcelot(this IConfigurationBuilder builder, string folder, IWebHostEnvironment env) { const string primaryConfigFile = "ocelot.json"; diff --git a/src/Ocelot/DependencyInjection/OcelotBuilder.cs b/src/Ocelot/DependencyInjection/OcelotBuilder.cs index 6733b54c..8f120997 100644 --- a/src/Ocelot/DependencyInjection/OcelotBuilder.cs +++ b/src/Ocelot/DependencyInjection/OcelotBuilder.cs @@ -142,7 +142,7 @@ namespace Ocelot.DependencyInjection .AddApplicationPart(assembly) .AddControllersAsServices() .AddAuthorization() - .AddJsonFormatters(); + .AddNewtonsoftJson(); Services.AddLogging(); Services.AddMiddlewareAnalysis(); diff --git a/src/Ocelot/DependencyInjection/ServiceCollectionExtensions.cs b/src/Ocelot/DependencyInjection/ServiceCollectionExtensions.cs index 030e9957..0d7a3bb7 100644 --- a/src/Ocelot/DependencyInjection/ServiceCollectionExtensions.cs +++ b/src/Ocelot/DependencyInjection/ServiceCollectionExtensions.cs @@ -7,9 +7,9 @@ namespace Ocelot.DependencyInjection public static class ServiceCollectionExtensions { public static IOcelotBuilder AddOcelot(this IServiceCollection services) - { - var service = services.First(x => x.ServiceType == typeof(IConfiguration)); - var configuration = (IConfiguration)service.ImplementationInstance; + { + var configuration = services.BuildServiceProvider() + .GetRequiredService(); return new OcelotBuilder(services, configuration); } diff --git a/src/Ocelot/Logging/AspDotNetLogger.cs b/src/Ocelot/Logging/AspDotNetLogger.cs index 598cbdd0..35375d45 100644 --- a/src/Ocelot/Logging/AspDotNetLogger.cs +++ b/src/Ocelot/Logging/AspDotNetLogger.cs @@ -8,53 +8,83 @@ namespace Ocelot.Logging { private readonly ILogger _logger; private readonly IRequestScopedDataRepository _scopedDataRepository; + private readonly Func _func; public AspDotNetLogger(ILogger logger, IRequestScopedDataRepository scopedDataRepository) { _logger = logger; - _scopedDataRepository = scopedDataRepository; + _scopedDataRepository = scopedDataRepository; + _func = (state, exception) => + { + if (exception == null) + { + return state; + } + else + { + return $"{state}, exception: {exception}"; + } + }; } public void LogTrace(string message) - { + { var requestId = GetOcelotRequestId(); - var previousRequestId = GetOcelotPreviousRequestId(); - _logger.LogTrace("requestId: {requestId}, previousRequestId: {previousRequestId}, message: {message}", requestId, previousRequestId, message); + var previousRequestId = GetOcelotPreviousRequestId(); + + var state = $"requestId: {requestId}, previousRequestId: {previousRequestId}, message: {message}"; + + _logger.Log(LogLevel.Trace, default(EventId), state, null, _func); } public void LogDebug(string message) - { + { var requestId = GetOcelotRequestId(); - var previousRequestId = GetOcelotPreviousRequestId(); - _logger.LogDebug("requestId: {requestId}, previousRequestId: {previousRequestId}, message: {message}", requestId, previousRequestId, message); + var previousRequestId = GetOcelotPreviousRequestId(); + + var state = $"requestId: {requestId}, previousRequestId: {previousRequestId}, message: {message}"; + + _logger.Log(LogLevel.Debug, default(EventId), state, null, _func); } public void LogInformation(string message) - { + { var requestId = GetOcelotRequestId(); - var previousRequestId = GetOcelotPreviousRequestId(); - _logger.LogInformation("requestId: {requestId}, previousRequestId: {previousRequestId}, message: {message}", requestId, previousRequestId, message); + var previousRequestId = GetOcelotPreviousRequestId(); + + var state = $"requestId: {requestId}, previousRequestId: {previousRequestId}, message: {message}"; + + _logger.Log(LogLevel.Information, default(EventId), state, null, _func); } public void LogWarning(string message) { var requestId = GetOcelotRequestId(); - var previousRequestId = GetOcelotPreviousRequestId(); - _logger.LogWarning("requestId: {requestId}, previousRequestId: {previousRequestId}, message: {message}", requestId, previousRequestId, message); + var previousRequestId = GetOcelotPreviousRequestId(); + + var state = $"requestId: {requestId}, previousRequestId: {previousRequestId}, message: {message}"; + + _logger.Log(LogLevel.Warning, default(EventId), state, null, _func); } public void LogError(string message, Exception exception) { var requestId = GetOcelotRequestId(); - var previousRequestId = GetOcelotPreviousRequestId(); - _logger.LogError("requestId: {requestId}, previousRequestId: {previousRequestId}, message: {message}, exception: {exception}", requestId, previousRequestId, message, exception); + var previousRequestId = GetOcelotPreviousRequestId(); + + var state = $"requestId: {requestId}, previousRequestId: {previousRequestId}, message: {message}"; + + _logger.Log(LogLevel.Error,default(EventId), state, exception, _func); } public void LogCritical(string message, Exception exception) { var requestId = GetOcelotRequestId(); - var previousRequestId = GetOcelotPreviousRequestId(); - _logger.LogCritical("requestId: {requestId}, previousRequestId: {previousRequestId}, message: {message}, exception: {exception}", requestId, previousRequestId, message, exception); + var previousRequestId = GetOcelotPreviousRequestId(); + + var state = $"requestId: {requestId}, previousRequestId: {previousRequestId}, message: {message}"; + + _logger.Log(LogLevel.Critical, default(EventId), state, exception, _func); } private string GetOcelotRequestId() @@ -81,4 +111,4 @@ namespace Ocelot.Logging return requestId.Data; } } -} +} diff --git a/src/Ocelot/Middleware/OcelotMiddlewareExtensions.cs b/src/Ocelot/Middleware/OcelotMiddlewareExtensions.cs index a841df5a..42bc6b71 100644 --- a/src/Ocelot/Middleware/OcelotMiddlewareExtensions.cs +++ b/src/Ocelot/Middleware/OcelotMiddlewareExtensions.cs @@ -178,7 +178,7 @@ private static void ConfigureDiagnosticListener(IApplicationBuilder builder) { - var env = builder.ApplicationServices.GetService(); + var env = builder.ApplicationServices.GetService(); var listener = builder.ApplicationServices.GetService(); var diagnosticListener = builder.ApplicationServices.GetService(); diagnosticListener.SubscribeWithAdapter(listener); diff --git a/src/Ocelot/Ocelot.csproj b/src/Ocelot/Ocelot.csproj index b9d9d2ce..fb0d541a 100644 --- a/src/Ocelot/Ocelot.csproj +++ b/src/Ocelot/Ocelot.csproj @@ -1,8 +1,6 @@  - netstandard2.0 - 2.0.0 - 2.0.0 + netcoreapp3.0 true Ocelot is an API Gateway. The project is aimed at people using .NET running a micro services / service orientated architecture that need a unified point of entry into their system. In particular I want easy integration with IdentityServer reference and bearer tokens. reference tokens. Ocelot is a bunch of middlewares in a specific order. Ocelot manipulates the HttpRequest object into a state specified by its configuration until it reaches a request builder middleware where it creates a HttpRequestMessage object which is used to make a request to a downstream service. The middleware that makes the request is the last thing in the Ocelot pipeline. It does not call the next middleware. The response from the downstream service is stored in a per request scoped repository and retrived as the requests goes back up the Ocelot pipeline. There is a piece of middleware that maps the HttpResponseMessage onto the HttpResponse object and that is returned to the client. That is basically it with a bunch of other features. Ocelot @@ -25,16 +23,20 @@ full True + - - - - - + + + NU1701 - + + all + + + + diff --git a/test/Ocelot.AcceptanceTests/ConfigurationInConsulTests.cs b/test/Ocelot.AcceptanceTests/ConfigurationInConsulTests.cs index ae7d6b0a..e72aab33 100644 --- a/test/Ocelot.AcceptanceTests/ConfigurationInConsulTests.cs +++ b/test/Ocelot.AcceptanceTests/ConfigurationInConsulTests.cs @@ -5,6 +5,7 @@ namespace Ocelot.AcceptanceTests using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; + using Microsoft.Extensions.Hosting; using Newtonsoft.Json; using System; using System.Collections.Generic; @@ -16,9 +17,9 @@ namespace Ocelot.AcceptanceTests public class ConfigurationInConsulTests : IDisposable { - private IWebHost _builder; + private IHost _builder; private readonly Steps _steps; - private IWebHost _fakeConsulBuilder; + private IHost _fakeConsulBuilder; private FileConfiguration _config; private readonly List _consulServices; @@ -75,8 +76,10 @@ namespace Ocelot.AcceptanceTests private void GivenThereIsAFakeConsulServiceDiscoveryProvider(string url, string serviceName) { - _fakeConsulBuilder = new WebHostBuilder() - .UseUrls(url) + _fakeConsulBuilder = Host.CreateDefaultBuilder() + .ConfigureWebHost(webBuilder => + { + webBuilder.UseUrls(url) .UseKestrel() .UseContentRoot(Directory.GetCurrentDirectory()) .UseIISIntegration() @@ -103,7 +106,9 @@ namespace Ocelot.AcceptanceTests { var reader = new StreamReader(context.Request.Body); - var json = reader.ReadToEnd(); + // Synchronous operations are disallowed. Call ReadAsync or set AllowSynchronousIO to true instead. + // var json = reader.ReadToEnd(); + var json = await reader.ReadToEndAsync(); _config = JsonConvert.DeserializeObject(json); @@ -122,8 +127,8 @@ namespace Ocelot.AcceptanceTests await context.Response.WriteJsonAsync(_consulServices); } }); - }) - .Build(); + }); + }).Build(); _fakeConsulBuilder.Start(); } @@ -146,22 +151,24 @@ namespace Ocelot.AcceptanceTests private void GivenThereIsAServiceRunningOn(string url, string basePath, int statusCode, string responseBody) { - _builder = new WebHostBuilder() - .UseUrls(url) - .UseKestrel() - .UseContentRoot(Directory.GetCurrentDirectory()) - .UseIISIntegration() - .UseUrls(url) - .Configure(app => + _builder = Host.CreateDefaultBuilder() + .ConfigureWebHost(webBuilder => { - app.UsePathBase(basePath); - - app.Run(async context => + webBuilder.UseUrls(url) + .UseKestrel() + .UseContentRoot(Directory.GetCurrentDirectory()) + .UseIISIntegration() + .UseUrls(url) + .Configure(app => { - context.Response.StatusCode = statusCode; - await context.Response.WriteAsync(responseBody); - }); - }) + app.UsePathBase(basePath); + app.Run(async context => + { + context.Response.StatusCode = statusCode; + await context.Response.WriteAsync(responseBody); + }); + }); + }) .Build(); _builder.Start(); diff --git a/test/Ocelot.AcceptanceTests/ConsulConfigurationInConsulTests.cs b/test/Ocelot.AcceptanceTests/ConsulConfigurationInConsulTests.cs index 27f8dee8..976bd909 100644 --- a/test/Ocelot.AcceptanceTests/ConsulConfigurationInConsulTests.cs +++ b/test/Ocelot.AcceptanceTests/ConsulConfigurationInConsulTests.cs @@ -373,7 +373,9 @@ { var reader = new StreamReader(context.Request.Body); - var json = reader.ReadToEnd(); + // Synchronous operations are disallowed. Call ReadAsync or set AllowSynchronousIO to true instead. + // var json = reader.ReadToEnd(); + var json = await reader.ReadToEndAsync(); _config = JsonConvert.DeserializeObject(json); diff --git a/test/Ocelot.AcceptanceTests/ConsulWebSocketTests.cs b/test/Ocelot.AcceptanceTests/ConsulWebSocketTests.cs index 8ed9b140..7ebb4737 100644 --- a/test/Ocelot.AcceptanceTests/ConsulWebSocketTests.cs +++ b/test/Ocelot.AcceptanceTests/ConsulWebSocketTests.cs @@ -181,7 +181,13 @@ } else if (result.MessageType == WebSocketMessageType.Close) { - await client.CloseAsync(WebSocketCloseStatus.NormalClosure, "", CancellationToken.None); + if (client.State != WebSocketState.Closed) + { + // Last version, the client state is CloseReceived + // Valid states are: Open, CloseReceived, CloseSent + await client.CloseAsync(WebSocketCloseStatus.NormalClosure, "", CancellationToken.None); + } + break; } } @@ -227,7 +233,13 @@ } else if (result.MessageType == WebSocketMessageType.Close) { - await client.CloseAsync(WebSocketCloseStatus.NormalClosure, "", CancellationToken.None); + if (client.State != WebSocketState.Closed) + { + // Last version, the client state is CloseReceived + // Valid states are: Open, CloseReceived, CloseSent + await client.CloseAsync(WebSocketCloseStatus.NormalClosure, "", CancellationToken.None); + } + break; } } diff --git a/test/Ocelot.AcceptanceTests/ContentTests.cs b/test/Ocelot.AcceptanceTests/ContentTests.cs index 39299f74..7a4d8365 100644 --- a/test/Ocelot.AcceptanceTests/ContentTests.cs +++ b/test/Ocelot.AcceptanceTests/ContentTests.cs @@ -55,7 +55,7 @@ namespace Ocelot.AcceptanceTests .Then(x => _steps.ThenTheStatusCodeShouldBe(HttpStatusCode.OK)) .And(x => _steps.ThenTheResponseBodyShouldBe("Hello from Laura")) .And(x => ThenTheContentTypeShouldBeEmpty()) - .And(x => ThenTheContentLengthShouldBeEmpty()) + .And(x => ThenTheContentLengthShouldBeZero()) .BDDfy(); } @@ -139,9 +139,9 @@ namespace Ocelot.AcceptanceTests _contentType.ShouldBe(expected); } - private void ThenTheContentLengthShouldBeEmpty() + private void ThenTheContentLengthShouldBeZero() { - _contentLength.ShouldBeNull(); + _contentLength.ShouldBeEquivalentTo(0L); } private void ThenTheContentLengthIs(int expected) diff --git a/test/Ocelot.AcceptanceTests/GzipTests.cs b/test/Ocelot.AcceptanceTests/GzipTests.cs index bb03c9c5..51ab1723 100644 --- a/test/Ocelot.AcceptanceTests/GzipTests.cs +++ b/test/Ocelot.AcceptanceTests/GzipTests.cs @@ -73,7 +73,9 @@ namespace Ocelot.AcceptanceTests { using (var sr = new StreamReader(decompress)) { - text = sr.ReadToEnd(); + // Synchronous operations are disallowed. Call ReadAsync or set AllowSynchronousIO to true instead. + // text = sr.ReadToEnd(); + text = await sr.ReadToEndAsync(); } } diff --git a/test/Ocelot.AcceptanceTests/Ocelot.AcceptanceTests.csproj b/test/Ocelot.AcceptanceTests/Ocelot.AcceptanceTests.csproj index 762448e7..a41029d4 100644 --- a/test/Ocelot.AcceptanceTests/Ocelot.AcceptanceTests.csproj +++ b/test/Ocelot.AcceptanceTests/Ocelot.AcceptanceTests.csproj @@ -1,7 +1,7 @@  0.0.0-dev - netcoreapp2.2 + netcoreapp3.0 Ocelot.AcceptanceTests Exe Ocelot.AcceptanceTests @@ -36,36 +36,39 @@ + - - - - + + + all all runtime; build; native; contentfiles; analyzers; buildtransitive - - - - - - - - + + + + + + + + - + - - + + - - + + + + + \ No newline at end of file diff --git a/test/Ocelot.AcceptanceTests/Steps.cs b/test/Ocelot.AcceptanceTests/Steps.cs index 6b9870bd..5bddcd92 100644 --- a/test/Ocelot.AcceptanceTests/Steps.cs +++ b/test/Ocelot.AcceptanceTests/Steps.cs @@ -37,7 +37,7 @@ using System.Threading.Tasks; using static Ocelot.AcceptanceTests.HttpDelegatingHandlersTests; using ConfigurationBuilder = Microsoft.Extensions.Configuration.ConfigurationBuilder; - using CookieHeaderValue = System.Net.Http.Headers.CookieHeaderValue; + using CookieHeaderValue = Microsoft.Net.Http.Headers.CookieHeaderValue; using MediaTypeHeaderValue = System.Net.Http.Headers.MediaTypeHeaderValue; public class Steps : IDisposable @@ -338,7 +338,7 @@ { x.WithMicrosoftLogging(log => { - log.AddConsole(LogLevel.Debug); + //log.AddConsole(LogLevel.Debug); }) .WithJsonSerializer() .WithHandle(typeof(InMemoryJsonHandle<>)); @@ -424,7 +424,7 @@ { x.WithMicrosoftLogging(log => { - log.AddConsole(LogLevel.Debug); + //log.AddConsole(LogLevel.Debug); }) .WithJsonSerializer() .WithHandle(typeof(InMemoryJsonHandle<>)); diff --git a/test/Ocelot.AcceptanceTests/WebSocketTests.cs b/test/Ocelot.AcceptanceTests/WebSocketTests.cs index e98a1bf9..425416e2 100644 --- a/test/Ocelot.AcceptanceTests/WebSocketTests.cs +++ b/test/Ocelot.AcceptanceTests/WebSocketTests.cs @@ -164,7 +164,13 @@ namespace Ocelot.AcceptanceTests } else if (result.MessageType == WebSocketMessageType.Close) { - await client.CloseAsync(WebSocketCloseStatus.NormalClosure, "", CancellationToken.None); + if (client.State != WebSocketState.Closed) + { + // Last version, the client state is CloseReceived + // Valid states are: Open, CloseReceived, CloseSent + await client.CloseAsync(WebSocketCloseStatus.NormalClosure, "", CancellationToken.None); + } + break; } } @@ -210,7 +216,13 @@ namespace Ocelot.AcceptanceTests } else if (result.MessageType == WebSocketMessageType.Close) { - await client.CloseAsync(WebSocketCloseStatus.NormalClosure, "", CancellationToken.None); + if (client.State != WebSocketState.Closed) + { + // Last version, the client state is CloseReceived + // Valid states are: Open, CloseReceived, CloseSent + await client.CloseAsync(WebSocketCloseStatus.NormalClosure, "", CancellationToken.None); + } + break; } } @@ -273,13 +285,13 @@ namespace Ocelot.AcceptanceTests var result = await webSocket.ReceiveAsync(new ArraySegment(buffer), CancellationToken.None); - while (!result.CloseStatus.HasValue) + while (!result.CloseStatus.HasValue) { await webSocket.SendAsync(new ArraySegment(buffer, 0, result.Count), result.MessageType, result.EndOfMessage, CancellationToken.None); result = await webSocket.ReceiveAsync(new ArraySegment(buffer), CancellationToken.None); } - + await webSocket.CloseAsync(result.CloseStatus.Value, result.CloseStatusDescription, CancellationToken.None); } catch (Exception e) diff --git a/test/Ocelot.Benchmarks/Ocelot.Benchmarks.csproj b/test/Ocelot.Benchmarks/Ocelot.Benchmarks.csproj index 32f4f556..48df8a92 100644 --- a/test/Ocelot.Benchmarks/Ocelot.Benchmarks.csproj +++ b/test/Ocelot.Benchmarks/Ocelot.Benchmarks.csproj @@ -2,7 +2,7 @@ 0.0.0-dev - netcoreapp2.2 + netcoreapp3.0 Ocelot.Benchmarks Exe Ocelot.Benchmarks @@ -19,8 +19,12 @@ - + all + + + + \ No newline at end of file diff --git a/test/Ocelot.IntegrationTests/AdministrationTests.cs b/test/Ocelot.IntegrationTests/AdministrationTests.cs index 5b5895cd..b874f787 100644 --- a/test/Ocelot.IntegrationTests/AdministrationTests.cs +++ b/test/Ocelot.IntegrationTests/AdministrationTests.cs @@ -6,6 +6,7 @@ using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; using Newtonsoft.Json; using Ocelot.Administration; using Ocelot.Cache; @@ -29,15 +30,15 @@ namespace Ocelot.IntegrationTests private HttpClient _httpClient; private readonly HttpClient _httpClientTwo; private HttpResponseMessage _response; - private IWebHost _builder; - private IWebHostBuilder _webHostBuilder; + private IHost _builder; + private IHostBuilder _webHostBuilder; private string _ocelotBaseUrl; private BearerToken _token; - private IWebHostBuilder _webHostBuilderTwo; - private IWebHost _builderTwo; - private IWebHost _identityServerBuilder; - private IWebHost _fooServiceBuilder; - private IWebHost _barServiceBuilder; + private IHostBuilder _webHostBuilderTwo; + private IHost _builderTwo; + private IHost _identityServerBuilder; + private IHost _fooServiceBuilder; + private IHost _barServiceBuilder; public AdministrationTests() { @@ -220,7 +221,7 @@ namespace Ocelot.IntegrationTests UpstreamHttpMethod = new List { "get" }, UpstreamPathTemplate = "/test" } - } + }, }; var updatedConfiguration = new FileConfiguration @@ -476,56 +477,59 @@ namespace Ocelot.IntegrationTests private void GivenThereIsAnIdentityServerOn(string url, string apiName) { - _identityServerBuilder = new WebHostBuilder() - .UseUrls(url) - .UseKestrel() - .UseContentRoot(Directory.GetCurrentDirectory()) - .ConfigureServices(services => + _identityServerBuilder = Host.CreateDefaultBuilder() + .ConfigureWebHost(webBuilder => { - services.AddLogging(); - services.AddIdentityServer() - .AddDeveloperSigningCredential() + webBuilder.UseUrls(url) + .UseKestrel() + .UseContentRoot(Directory.GetCurrentDirectory()) + .ConfigureServices(services => + { + services.AddLogging(); + services.AddIdentityServer() + .AddDeveloperSigningCredential() .AddInMemoryApiResources(new List { - new ApiResource + new ApiResource + { + Name = apiName, + Description = apiName, + Enabled = true, + DisplayName = apiName, + Scopes = new List() { - Name = apiName, - Description = apiName, - Enabled = true, - DisplayName = apiName, - Scopes = new List() - { - new Scope(apiName) - } - } + new Scope(apiName), + }, + }, }) .AddInMemoryClients(new List { - new Client - { - ClientId = apiName, - AllowedGrantTypes = GrantTypes.ResourceOwnerPassword, - ClientSecrets = new List {new Secret("secret".Sha256())}, - AllowedScopes = new List { apiName }, - AccessTokenType = AccessTokenType.Jwt, - Enabled = true - } + new Client + { + ClientId = apiName, + AllowedGrantTypes = GrantTypes.ResourceOwnerPassword, + ClientSecrets = new List { new Secret("secret".Sha256()) }, + AllowedScopes = new List { apiName }, + AccessTokenType = AccessTokenType.Jwt, + Enabled = true + }, }) .AddTestUsers(new List { - new TestUser - { - Username = "test", - Password = "test", - SubjectId = "1231231" - } + new TestUser + { + Username = "test", + Password = "test", + SubjectId = "1231231" + }, }); - }) - .Configure(app => - { - app.UseIdentityServer(); - }) - .Build(); + }) + .Configure(app => + { + app.UseIdentityServer(); + } + ); + }).Build(); _identityServerBuilder.Start(); @@ -540,28 +544,32 @@ namespace Ocelot.IntegrationTests { _httpClientTwo.BaseAddress = new Uri(baseUrl); - _webHostBuilderTwo = new WebHostBuilder() - .UseUrls(baseUrl) - .UseKestrel() - .UseContentRoot(Directory.GetCurrentDirectory()) - .ConfigureAppConfiguration((hostingContext, config) => - { - config.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath); - var env = hostingContext.HostingEnvironment; - config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: false) - .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: false); - config.AddJsonFile("ocelot.json", false, false); - config.AddEnvironmentVariables(); - }) - .ConfigureServices(x => - { - x.AddOcelot() + _webHostBuilderTwo = Host.CreateDefaultBuilder() + .ConfigureWebHost(webBuilder => + { + webBuilder.UseUrls(baseUrl) + .UseKestrel() + .UseContentRoot(Directory.GetCurrentDirectory()) + .ConfigureAppConfiguration((hostingContext, config) => + { + config.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath); + var env = hostingContext.HostingEnvironment; + config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: false) + .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: false); + config.AddJsonFile("ocelot.json", false, false); + config.AddEnvironmentVariables(); + }) + .ConfigureServices(x => + { + x.AddMvc(option => option.EnableEndpointRouting = false); + x.AddOcelot() .AddAdministration("/administration", "secret"); - }) - .Configure(app => - { - app.UseOcelot().Wait(); - }); + }) + .Configure(app => + { + app.UseOcelot().Wait(); + }); + }); _builderTwo = _webHostBuilderTwo.Build(); @@ -654,29 +662,33 @@ namespace Ocelot.IntegrationTests private void GivenOcelotIsRunningWithIdentityServerSettings(Action configOptions) { - _webHostBuilder = new WebHostBuilder() - .UseUrls(_ocelotBaseUrl) - .UseKestrel() - .UseContentRoot(Directory.GetCurrentDirectory()) - .ConfigureAppConfiguration((hostingContext, config) => + _webHostBuilder = Host.CreateDefaultBuilder() + .ConfigureWebHost(webBuilder => { - config.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath); - var env = hostingContext.HostingEnvironment; - config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: false) + webBuilder.UseUrls(_ocelotBaseUrl) + .UseKestrel() + .UseContentRoot(Directory.GetCurrentDirectory()) + .ConfigureAppConfiguration((hostingContext, config) => + { + config.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath); + var env = hostingContext.HostingEnvironment; + config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: false) .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: false); - config.AddJsonFile("ocelot.json", false, false); - config.AddEnvironmentVariables(); - }) - .ConfigureServices(x => - { - x.AddSingleton(_webHostBuilder); - x.AddOcelot() - .AddAdministration("/administration", configOptions); - }) + config.AddJsonFile("ocelot.json", false, false); + config.AddEnvironmentVariables(); + }) + .ConfigureServices(x => + { + x.AddMvc(option => option.EnableEndpointRouting = false); + x.AddSingleton(_webHostBuilder); + x.AddOcelot() + .AddAdministration("/administration", configOptions); + }) .Configure(app => { app.UseOcelot().Wait(); }); + }); _builder = _webHostBuilder.Build(); @@ -685,27 +697,31 @@ namespace Ocelot.IntegrationTests private void GivenOcelotIsRunning() { - _webHostBuilder = new WebHostBuilder() - .UseUrls(_ocelotBaseUrl) - .UseKestrel() - .UseContentRoot(Directory.GetCurrentDirectory()) - .ConfigureAppConfiguration((hostingContext, config) => + _webHostBuilder = Host.CreateDefaultBuilder() + .ConfigureWebHost(webBuilder => { - config.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath); - var env = hostingContext.HostingEnvironment; - config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: false) + webBuilder.UseUrls(_ocelotBaseUrl) + .UseKestrel() + .UseContentRoot(Directory.GetCurrentDirectory()) + .ConfigureAppConfiguration((hostingContext, config) => + { + config.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath); + var env = hostingContext.HostingEnvironment; + config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: false) .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: false); - config.AddJsonFile("ocelot.json", false, false); - config.AddEnvironmentVariables(); - }) - .ConfigureServices(x => - { - x.AddOcelot() + config.AddJsonFile("ocelot.json", false, false); + config.AddEnvironmentVariables(); + }) + .ConfigureServices(x => + { + x.AddMvc(s => s.EnableEndpointRouting = false); + x.AddOcelot() .AddAdministration("/administration", "secret"); - }) - .Configure(app => - { - app.UseOcelot().Wait(); + }) + .Configure(app => + { + app.UseOcelot().Wait(); + }); }); _builder = _webHostBuilder.Build(); @@ -715,30 +731,34 @@ namespace Ocelot.IntegrationTests private void GivenOcelotIsRunningWithNoWebHostBuilder(string baseUrl) { - _webHostBuilder = new WebHostBuilder() - .UseUrls(_ocelotBaseUrl) - .UseKestrel() - .UseContentRoot(Directory.GetCurrentDirectory()) - .ConfigureAppConfiguration((hostingContext, config) => + _webHostBuilder = Host.CreateDefaultBuilder() + .ConfigureWebHost(webBuilder => { - config.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath); - var env = hostingContext.HostingEnvironment; - config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: false) + webBuilder.UseUrls(_ocelotBaseUrl) + .UseKestrel() + .UseContentRoot(Directory.GetCurrentDirectory()) + .ConfigureAppConfiguration((hostingContext, config) => + { + config.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath); + var env = hostingContext.HostingEnvironment; + config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: false) .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: false); - config.AddJsonFile("ocelot.json", false, false); - config.AddEnvironmentVariables(); - }) - .ConfigureServices(x => - { - x.AddSingleton(_webHostBuilder); - x.AddOcelot() + config.AddJsonFile("ocelot.json", false, false); + config.AddEnvironmentVariables(); + }) + .ConfigureServices(x => + { + x.AddMvc(option => option.EnableEndpointRouting = false); + x.AddSingleton(_webHostBuilder); + x.AddOcelot() .AddAdministration("/administration", "secret"); - }) - .Configure(app => - { - app.UseOcelot().Wait(); - }); - + }) + .Configure(app => + { + app.UseOcelot().Wait(); + }); + }); + _builder = _webHostBuilder.Build(); _builder.Start(); @@ -797,42 +817,46 @@ namespace Ocelot.IntegrationTests private void GivenThereIsAFooServiceRunningOn(string baseUrl) { - _fooServiceBuilder = new WebHostBuilder() - .UseUrls(baseUrl) - .UseKestrel() - .UseContentRoot(Directory.GetCurrentDirectory()) - .UseIISIntegration() - .Configure(app => + _fooServiceBuilder = Host.CreateDefaultBuilder() + .ConfigureWebHost(webBuilder => { - app.UsePathBase("/foo"); - app.Run(async context => + webBuilder.UseUrls(baseUrl) + .UseKestrel() + .UseContentRoot(Directory.GetCurrentDirectory()) + .UseIISIntegration() + .Configure(app => { - context.Response.StatusCode = 200; - await context.Response.WriteAsync("foo"); + app.UsePathBase("/foo"); + app.Run(async context => + { + context.Response.StatusCode = 200; + await context.Response.WriteAsync("foo"); + }); }); - }) - .Build(); - + }).Build(); + _fooServiceBuilder.Start(); } private void GivenThereIsABarServiceRunningOn(string baseUrl) { - _barServiceBuilder = new WebHostBuilder() - .UseUrls(baseUrl) - .UseKestrel() - .UseContentRoot(Directory.GetCurrentDirectory()) - .UseIISIntegration() - .Configure(app => + _barServiceBuilder = Host.CreateDefaultBuilder() + .ConfigureWebHost(webBuilder => { - app.UsePathBase("/bar"); - app.Run(async context => + webBuilder.UseUrls(baseUrl) + .UseKestrel() + .UseContentRoot(Directory.GetCurrentDirectory()) + .UseIISIntegration() + .Configure(app => { - context.Response.StatusCode = 200; - await context.Response.WriteAsync("bar"); + app.UsePathBase("/bar"); + app.Run(async context => + { + context.Response.StatusCode = 200; + await context.Response.WriteAsync("bar"); + }); }); - }) - .Build(); + }).Build(); _barServiceBuilder.Start(); } diff --git a/test/Ocelot.IntegrationTests/CacheManagerTests.cs b/test/Ocelot.IntegrationTests/CacheManagerTests.cs index beab8ca3..6be90f01 100644 --- a/test/Ocelot.IntegrationTests/CacheManagerTests.cs +++ b/test/Ocelot.IntegrationTests/CacheManagerTests.cs @@ -5,6 +5,8 @@ namespace Ocelot.IntegrationTests using global::CacheManager.Core; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.Configuration; + using Microsoft.Extensions.DependencyInjection; + using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; using Newtonsoft.Json; using Ocelot.Administration; @@ -25,15 +27,10 @@ namespace Ocelot.IntegrationTests private HttpClient _httpClient; private readonly HttpClient _httpClientTwo; private HttpResponseMessage _response; - private IWebHost _builder; - private IWebHostBuilder _webHostBuilder; + private IHost _builder; + private IHostBuilder _webHostBuilder; private string _ocelotBaseUrl; private BearerToken _token; - private IWebHostBuilder _webHostBuilderTwo; - private IWebHost _builderTwo; - private IWebHost _identityServerBuilder; - private IWebHost _fooServiceBuilder; - private IWebHost _barServiceBuilder; public CacheManagerTests() { @@ -61,7 +58,7 @@ namespace Ocelot.IntegrationTests { Host = "localhost", Port = 80, - } + }, }, DownstreamScheme = "https", DownstreamPathTemplate = "/", @@ -69,8 +66,8 @@ namespace Ocelot.IntegrationTests UpstreamPathTemplate = "/", FileCacheOptions = new FileCacheOptions { - TtlSeconds = 10 - } + TtlSeconds = 10, + }, }, new FileReRoute() { @@ -80,7 +77,7 @@ namespace Ocelot.IntegrationTests { Host = "localhost", Port = 80, - } + }, }, DownstreamScheme = "https", DownstreamPathTemplate = "/", @@ -88,10 +85,10 @@ namespace Ocelot.IntegrationTests UpstreamPathTemplate = "/test", FileCacheOptions = new FileCacheOptions { - TtlSeconds = 10 - } - } - } + TtlSeconds = 10, + }, + }, + }, }; var regionToClear = "gettest"; @@ -118,7 +115,7 @@ namespace Ocelot.IntegrationTests new KeyValuePair("client_id", "admin"), new KeyValuePair("client_secret", "secret"), new KeyValuePair("scope", "admin"), - new KeyValuePair("grant_type", "client_credentials") + new KeyValuePair("grant_type", "client_credentials"), }; var content = new FormUrlEncodedContent(formData); @@ -133,16 +130,13 @@ namespace Ocelot.IntegrationTests private void GivenOcelotIsRunning() { - _webHostBuilder = new WebHostBuilder() - .UseUrls(_ocelotBaseUrl) - .UseKestrel() - .UseContentRoot(Directory.GetCurrentDirectory()) + _webHostBuilder = Host.CreateDefaultBuilder() .ConfigureAppConfiguration((hostingContext, config) => { config.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath); var env = hostingContext.HostingEnvironment; config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: false) - .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: false); + .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: false); config.AddJsonFile("ocelot.json", false, false); config.AddEnvironmentVariables(); }) @@ -151,20 +145,26 @@ namespace Ocelot.IntegrationTests Action settings = (s) => { s.WithMicrosoftLogging(log => - { - log.AddConsole(LogLevel.Debug); - }) - .WithDictionaryHandle(); + { + //log.AddConsole(LogLevel.Debug); + }) + .WithDictionaryHandle(); }; - + x.AddMvc(option => option.EnableEndpointRouting = false); x.AddOcelot() - .AddCacheManager(settings) - .AddAdministration("/administration", "secret"); - }) + .AddCacheManager(settings) + .AddAdministration("/administration", "secret"); + }) + .ConfigureWebHost(webBuilder => + { + webBuilder.UseUrls(_ocelotBaseUrl) + .UseKestrel() + .UseContentRoot(Directory.GetCurrentDirectory()) .Configure(app => { app.UseOcelot().Wait(); }); + }); _builder = _webHostBuilder.Build(); @@ -214,7 +214,7 @@ namespace Ocelot.IntegrationTests Environment.SetEnvironmentVariable("OCELOT_CERTIFICATE_PASSWORD", ""); _builder?.Dispose(); _httpClient?.Dispose(); - _identityServerBuilder?.Dispose(); + //_identityServerBuilder?.Dispose(); } } } diff --git a/test/Ocelot.IntegrationTests/Ocelot.IntegrationTests.csproj b/test/Ocelot.IntegrationTests/Ocelot.IntegrationTests.csproj index 81c50e05..f38ede57 100644 --- a/test/Ocelot.IntegrationTests/Ocelot.IntegrationTests.csproj +++ b/test/Ocelot.IntegrationTests/Ocelot.IntegrationTests.csproj @@ -1,7 +1,7 @@  0.0.0-dev - netcoreapp2.2 + netcoreapp3.0 Ocelot.IntegrationTests Exe Ocelot.IntegrationTests @@ -29,30 +29,33 @@ + - - - - + + + all all runtime; build; native; contentfiles; analyzers; buildtransitive - - - - - - - + + + + + + + - + - - - + + + + + + \ No newline at end of file diff --git a/test/Ocelot.IntegrationTests/ThreadSafeHeadersTests.cs b/test/Ocelot.IntegrationTests/ThreadSafeHeadersTests.cs index 2f170229..644d1d90 100644 --- a/test/Ocelot.IntegrationTests/ThreadSafeHeadersTests.cs +++ b/test/Ocelot.IntegrationTests/ThreadSafeHeadersTests.cs @@ -54,12 +54,12 @@ namespace Ocelot.IntegrationTests { Host = "localhost", Port = 51879, - } + }, }, UpstreamPathTemplate = "/", UpstreamHttpMethod = new List { "Get" }, - } - } + }, + }, }; this.Given(x => GivenThereIsAConfiguration(configuration)) diff --git a/test/Ocelot.ManualTest/Ocelot.ManualTest.csproj b/test/Ocelot.ManualTest/Ocelot.ManualTest.csproj index 478d425f..ccfdce53 100644 --- a/test/Ocelot.ManualTest/Ocelot.ManualTest.csproj +++ b/test/Ocelot.ManualTest/Ocelot.ManualTest.csproj @@ -1,7 +1,7 @@  0.0.0-dev - netcoreapp2.2 + netcoreapp3.0 true Ocelot.ManualTest Exe @@ -28,16 +28,18 @@ - - - - - - - - - + + + + + + + + all + + + \ No newline at end of file diff --git a/test/Ocelot.ManualTest/Program.cs b/test/Ocelot.ManualTest/Program.cs index 659e250a..7d3fc969 100644 --- a/test/Ocelot.ManualTest/Program.cs +++ b/test/Ocelot.ManualTest/Program.cs @@ -32,12 +32,12 @@ namespace Ocelot.ManualTest }) .ConfigureServices(s => { - s.AddAuthentication() - .AddJwtBearer("TestKey", x => - { - x.Authority = "test"; - x.Audience = "test"; - }); + s.AddAuthentication(); + //.AddJwtBearer("TestKey", x => + //{ + // x.Authority = "test"; + // x.Audience = "test"; + //}); s.AddSingleton((x, t) => new FakeHandler()); s.AddOcelot() diff --git a/test/Ocelot.UnitTests/Administration/OcelotAdministrationBuilderTests.cs b/test/Ocelot.UnitTests/Administration/OcelotAdministrationBuilderTests.cs index 1eea1619..e19025d1 100644 --- a/test/Ocelot.UnitTests/Administration/OcelotAdministrationBuilderTests.cs +++ b/test/Ocelot.UnitTests/Administration/OcelotAdministrationBuilderTests.cs @@ -2,14 +2,15 @@ namespace Ocelot.UnitTests.Administration { using IdentityServer4.AccessTokenValidation; using Microsoft.AspNetCore.Hosting; - using Microsoft.AspNetCore.Hosting.Internal; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; + using Moq; using Ocelot.Administration; using Ocelot.DependencyInjection; using Shouldly; using System; using System.Collections.Generic; + using System.Reflection; using TestStack.BDDfy; using Xunit; @@ -25,9 +26,19 @@ namespace Ocelot.UnitTests.Administration { _configRoot = new ConfigurationRoot(new List()); _services = new ServiceCollection(); - _services.AddSingleton(); + _services.AddSingleton(GetHostingEnvironment()); _services.AddSingleton(_configRoot); } + + private IWebHostEnvironment GetHostingEnvironment() + { + var environment = new Mock(); + environment + .Setup(e => e.ApplicationName) + .Returns(typeof(OcelotAdministrationBuilderTests).GetTypeInfo().Assembly.GetName().Name); + + return environment.Object; + } //keep [Fact] diff --git a/test/Ocelot.UnitTests/CacheManager/OcelotBuilderExtensionsTests.cs b/test/Ocelot.UnitTests/CacheManager/OcelotBuilderExtensionsTests.cs index efa5631c..b85ee1f4 100644 --- a/test/Ocelot.UnitTests/CacheManager/OcelotBuilderExtensionsTests.cs +++ b/test/Ocelot.UnitTests/CacheManager/OcelotBuilderExtensionsTests.cs @@ -2,9 +2,10 @@ { using global::CacheManager.Core; using Microsoft.AspNetCore.Hosting; - using Microsoft.AspNetCore.Hosting.Internal; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; + using Microsoft.Extensions.Hosting.Internal; + using Moq; using Ocelot.Cache; using Ocelot.Cache.CacheManager; using Ocelot.Configuration; @@ -14,6 +15,7 @@ using System; using System.Collections.Generic; using System.Linq; + using System.Reflection; using TestStack.BDDfy; using Xunit; @@ -30,11 +32,21 @@ { _configRoot = new ConfigurationRoot(new List()); _services = new ServiceCollection(); - _services.AddSingleton(); + _services.AddSingleton(GetHostingEnvironment()); _services.AddSingleton(_configRoot); _maxRetries = 100; } + private IWebHostEnvironment GetHostingEnvironment() + { + var environment = new Mock(); + environment + .Setup(e => e.ApplicationName) + .Returns(typeof(OcelotBuilderExtensionsTests).GetTypeInfo().Assembly.GetName().Name); + + return environment.Object; + } + [Fact] public void should_set_up_cache_manager() { diff --git a/test/Ocelot.UnitTests/Configuration/DiskFileConfigurationRepositoryTests.cs b/test/Ocelot.UnitTests/Configuration/DiskFileConfigurationRepositoryTests.cs index d084c2d6..b2eb06b5 100644 --- a/test/Ocelot.UnitTests/Configuration/DiskFileConfigurationRepositoryTests.cs +++ b/test/Ocelot.UnitTests/Configuration/DiskFileConfigurationRepositoryTests.cs @@ -15,7 +15,7 @@ namespace Ocelot.UnitTests.Configuration public class DiskFileConfigurationRepositoryTests : IDisposable { - private readonly Mock _hostingEnvironment; + private readonly Mock _hostingEnvironment; private IFileConfigurationRepository _repo; private string _environmentSpecificPath; private string _ocelotJsonPath; @@ -33,7 +33,7 @@ namespace Ocelot.UnitTests.Configuration { _semaphore = new SemaphoreSlim(1, 1); _semaphore.Wait(); - _hostingEnvironment = new Mock(); + _hostingEnvironment = new Mock(); _hostingEnvironment.Setup(he => he.EnvironmentName).Returns(_environmentName); _repo = new DiskFileConfigurationRepository(_hostingEnvironment.Object); } diff --git a/test/Ocelot.UnitTests/Consul/ConsulFileConfigurationRepositoryTests.cs b/test/Ocelot.UnitTests/Consul/ConsulFileConfigurationRepositoryTests.cs index 8a011026..e655f123 100644 --- a/test/Ocelot.UnitTests/Consul/ConsulFileConfigurationRepositoryTests.cs +++ b/test/Ocelot.UnitTests/Consul/ConsulFileConfigurationRepositoryTests.cs @@ -1,6 +1,7 @@ namespace Ocelot.UnitTests.Consul { using global::Consul; + using Microsoft.Extensions.Options; using Moq; using Newtonsoft.Json; using Ocelot.Cache; @@ -23,8 +24,8 @@ public class ConsulFileConfigurationRepositoryTests { private ConsulFileConfigurationRepository _repo; + private Mock> _options; private Mock> _cache; - private Mock _internalRepo; private Mock _factory; private Mock _loggerFactory; private Mock _client; @@ -36,9 +37,9 @@ public ConsulFileConfigurationRepositoryTests() { _cache = new Mock>(); - _internalRepo = new Mock(); _loggerFactory = new Mock(); + _options = new Mock>(); _factory = new Mock(); _client = new Mock(); _kvEndpoint = new Mock(); @@ -51,11 +52,9 @@ .Setup(x => x.Get(It.IsAny())) .Returns(_client.Object); - _internalRepo - .Setup(x => x.Get()) - .Returns(new OkResponse(new InternalConfiguration(new List(), "", new ServiceProviderConfigurationBuilder().Build(), "", It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny()))); - - _repo = new ConsulFileConfigurationRepository(_cache.Object, _internalRepo.Object, _factory.Object, _loggerFactory.Object); + _options + .SetupGet(x => x.Value) + .Returns(() => _fileConfiguration); } [Fact] @@ -85,7 +84,10 @@ [Fact] public void should_get_null_config() { - this.Given(_ => GivenFetchFromConsulReturnsNull()) + var config = FakeFileConfiguration(); + + this.Given(_ => GivenIHaveAConfiguration(config)) + .Given(_ => GivenFetchFromConsulReturnsNull()) .When(_ => WhenIGetTheConfiguration()) .Then(_ => ThenTheConfigurationIsNull()) .BDDfy(); @@ -136,14 +138,8 @@ private void GivenTheConfigKeyComesFromFileConfig(string key) { - _internalRepo - .Setup(x => x.Get()) - .Returns(new OkResponse(new InternalConfiguration(new List(), "", - new ServiceProviderConfigurationBuilder().WithConfigurationKey(key).Build(), "", - new LoadBalancerOptionsBuilder().Build(), "", new QoSOptionsBuilder().Build(), - new HttpHandlerOptionsBuilder().Build()))); - - _repo = new ConsulFileConfigurationRepository(_cache.Object, _internalRepo.Object, _factory.Object, _loggerFactory.Object); + _fileConfiguration.GlobalConfiguration.ServiceDiscoveryProvider.ConfigurationKey = key; + _repo = new ConsulFileConfigurationRepository(_options.Object, _cache.Object, _factory.Object, _loggerFactory.Object); } private void ThenTheConfigurationIsNull() @@ -221,6 +217,8 @@ private void GivenIHaveAConfiguration(FileConfiguration config) { _fileConfiguration = config; + + _repo = new ConsulFileConfigurationRepository(_options.Object, _cache.Object, _factory.Object, _loggerFactory.Object); } private FileConfiguration FakeFileConfiguration() diff --git a/test/Ocelot.UnitTests/Consul/OcelotBuilderExtensionsTests.cs b/test/Ocelot.UnitTests/Consul/OcelotBuilderExtensionsTests.cs index 58a4f8a9..cb5fc0b5 100644 --- a/test/Ocelot.UnitTests/Consul/OcelotBuilderExtensionsTests.cs +++ b/test/Ocelot.UnitTests/Consul/OcelotBuilderExtensionsTests.cs @@ -1,14 +1,15 @@ namespace Ocelot.UnitTests.Consul { using Microsoft.AspNetCore.Hosting; - using Microsoft.AspNetCore.Hosting.Internal; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; + using Moq; using Ocelot.DependencyInjection; using Provider.Consul; using Shouldly; using System; using System.Collections.Generic; + using System.Reflection; using TestStack.BDDfy; using Xunit; @@ -24,10 +25,21 @@ { _configRoot = new ConfigurationRoot(new List()); _services = new ServiceCollection(); - _services.AddSingleton(); + _services.AddSingleton(GetHostingEnvironment()); _services.AddSingleton(_configRoot); } + + private IWebHostEnvironment GetHostingEnvironment() + { + var environment = new Mock(); + environment + .Setup(e => e.ApplicationName) + .Returns(typeof(OcelotBuilderExtensionsTests).GetTypeInfo().Assembly.GetName().Name); + + return environment.Object; + } + [Fact] public void should_set_up_consul() { diff --git a/test/Ocelot.UnitTests/Consul/PollingConsulServiceDiscoveryProviderTests.cs b/test/Ocelot.UnitTests/Consul/PollingConsulServiceDiscoveryProviderTests.cs index ab77d7d0..cc38e29c 100644 --- a/test/Ocelot.UnitTests/Consul/PollingConsulServiceDiscoveryProviderTests.cs +++ b/test/Ocelot.UnitTests/Consul/PollingConsulServiceDiscoveryProviderTests.cs @@ -15,7 +15,6 @@ public class PollingConsulServiceDiscoveryProviderTests { private readonly int _delay; - private PollConsul _provider; private readonly List _services; private readonly Mock _factory; private readonly Mock _logger; @@ -56,27 +55,28 @@ private void WhenIGetTheServices(int expected) { - _provider = new PollConsul(_delay, _factory.Object, _consulServiceDiscoveryProvider.Object); - - var result = Wait.WaitFor(3000).Until(() => + using (var provider = new PollConsul(_delay, _factory.Object, _consulServiceDiscoveryProvider.Object)) { - try + var result = Wait.WaitFor(3000).Until(() => { - _result = _provider.Get().GetAwaiter().GetResult(); - if (_result.Count == expected) + try { - return true; + _result = provider.Get().GetAwaiter().GetResult(); + if (_result.Count == expected) + { + return true; + } + + return false; } + catch (Exception) + { + return false; + } + }); - return false; - } - catch (Exception) - { - return false; - } - }); - - result.ShouldBeTrue(); + result.ShouldBeTrue(); + } } } } diff --git a/test/Ocelot.UnitTests/Consul/ProviderFactoryTests.cs b/test/Ocelot.UnitTests/Consul/ProviderFactoryTests.cs index 5d4939d2..c51e9cd7 100644 --- a/test/Ocelot.UnitTests/Consul/ProviderFactoryTests.cs +++ b/test/Ocelot.UnitTests/Consul/ProviderFactoryTests.cs @@ -49,7 +49,9 @@ namespace Ocelot.UnitTests.Consul .Build(); var provider = ConsulProviderFactory.Get(_provider, new ServiceProviderConfiguration("pollconsul", "", 1, "", "", stopsPollerFromPolling), reRoute); - provider.ShouldBeOfType(); + var pollProvider = provider as PollConsul; + pollProvider.ShouldNotBeNull(); + pollProvider.Dispose(); } } } diff --git a/test/Ocelot.UnitTests/DependencyInjection/ConfigurationBuilderExtensionsTests.cs b/test/Ocelot.UnitTests/DependencyInjection/ConfigurationBuilderExtensionsTests.cs index efb8ff9a..1d408ba4 100644 --- a/test/Ocelot.UnitTests/DependencyInjection/ConfigurationBuilderExtensionsTests.cs +++ b/test/Ocelot.UnitTests/DependencyInjection/ConfigurationBuilderExtensionsTests.cs @@ -22,11 +22,11 @@ private FileConfiguration _reRouteB; private FileConfiguration _aggregate; private FileConfiguration _envSpecific; - private Mock _hostingEnvironment; + private Mock _hostingEnvironment; public ConfigurationBuilderExtensionsTests() { - _hostingEnvironment = new Mock(); + _hostingEnvironment = new Mock(); // Clean up config files before each test var subConfigFiles = new DirectoryInfo(".").GetFiles("ocelot.*.json"); diff --git a/test/Ocelot.UnitTests/DependencyInjection/OcelotBuilderTests.cs b/test/Ocelot.UnitTests/DependencyInjection/OcelotBuilderTests.cs index 3ec06a8b..0f1ddb60 100644 --- a/test/Ocelot.UnitTests/DependencyInjection/OcelotBuilderTests.cs +++ b/test/Ocelot.UnitTests/DependencyInjection/OcelotBuilderTests.cs @@ -1,11 +1,12 @@ namespace Ocelot.UnitTests.DependencyInjection { using Microsoft.AspNetCore.Hosting; - using Microsoft.AspNetCore.Hosting.Internal; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; + using Moq; using Ocelot.Configuration.Setter; using Ocelot.DependencyInjection; + using Ocelot.Infrastructure; using Ocelot.Middleware.Multiplexer; using Ocelot.Requester; using Ocelot.UnitTests.Requester; @@ -14,7 +15,7 @@ namespace Ocelot.UnitTests.DependencyInjection using System.Collections.Generic; using System.Linq; using System.Net.Http; - using Ocelot.Infrastructure; + using System.Reflection; using TestStack.BDDfy; using Xunit; using static Ocelot.UnitTests.Middleware.UserDefinedResponseAggregatorTests; @@ -32,11 +33,21 @@ namespace Ocelot.UnitTests.DependencyInjection { _configRoot = new ConfigurationRoot(new List()); _services = new ServiceCollection(); - _services.AddSingleton(); + _services.AddSingleton(GetHostingEnvironment()); _services.AddSingleton(_configRoot); _maxRetries = 100; } + private IWebHostEnvironment GetHostingEnvironment() + { + var environment = new Mock(); + environment + .Setup(e => e.ApplicationName) + .Returns(typeof(OcelotBuilderTests).GetTypeInfo().Assembly.GetName().Name); + + return environment.Object; + } + [Fact] public void should_add_specific_delegating_handlers_transient() { diff --git a/test/Ocelot.UnitTests/Eureka/EurekaMiddlewareConfigurationProviderTests.cs b/test/Ocelot.UnitTests/Eureka/EurekaMiddlewareConfigurationProviderTests.cs index 3f1a6da0..fc7f3c82 100644 --- a/test/Ocelot.UnitTests/Eureka/EurekaMiddlewareConfigurationProviderTests.cs +++ b/test/Ocelot.UnitTests/Eureka/EurekaMiddlewareConfigurationProviderTests.cs @@ -1,6 +1,6 @@ namespace Ocelot.UnitTests.Eureka { - using Microsoft.AspNetCore.Builder.Internal; + using Microsoft.AspNetCore.Builder; using Microsoft.Extensions.DependencyInjection; using Moq; using Ocelot.Configuration; diff --git a/test/Ocelot.UnitTests/Kubernetes/OcelotBuilderExtensionsTests.cs b/test/Ocelot.UnitTests/Kubernetes/OcelotBuilderExtensionsTests.cs index a2cb9e94..f858c1be 100644 --- a/test/Ocelot.UnitTests/Kubernetes/OcelotBuilderExtensionsTests.cs +++ b/test/Ocelot.UnitTests/Kubernetes/OcelotBuilderExtensionsTests.cs @@ -1,12 +1,13 @@ using Microsoft.AspNetCore.Hosting; -using Microsoft.AspNetCore.Hosting.Internal; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; +using Moq; using Ocelot.DependencyInjection; using Ocelot.Provider.Kubernetes; using Shouldly; using System; using System.Collections.Generic; +using System.Reflection; using TestStack.BDDfy; using Xunit; @@ -24,10 +25,20 @@ namespace Ocelot.UnitTests.Kubernetes { _configRoot = new ConfigurationRoot(new List()); _services = new ServiceCollection(); - _services.AddSingleton(); + _services.AddSingleton(GetHostingEnvironment()); _services.AddSingleton(_configRoot); } + private IWebHostEnvironment GetHostingEnvironment() + { + var environment = new Mock(); + environment + .Setup(e => e.ApplicationName) + .Returns(typeof(OcelotBuilderExtensionsTests).GetTypeInfo().Assembly.GetName().Name); + + return environment.Object; + } + [Fact] public void should_set_up_kubernetes() { diff --git a/test/Ocelot.UnitTests/Logging/AspDotNetLoggerTests.cs b/test/Ocelot.UnitTests/Logging/AspDotNetLoggerTests.cs index 13d9f874..c4e2d8bf 100644 --- a/test/Ocelot.UnitTests/Logging/AspDotNetLoggerTests.cs +++ b/test/Ocelot.UnitTests/Logging/AspDotNetLoggerTests.cs @@ -10,8 +10,8 @@ namespace Ocelot.UnitTests.Logging public class AspDotNetLoggerTests { private readonly Mock> _coreLogger; + private readonly Mock _repo; private readonly AspDotNetLogger _logger; - private Mock _repo; private readonly string _b; private readonly string _a; private readonly Exception _ex; @@ -55,7 +55,7 @@ namespace Ocelot.UnitTests.Logging { _logger.LogError($"a message from {_a} to {_b}", _ex); - ThenLevelIsLogged("requestId: no request id, previousRequestId: no previous request id, message: a message from tom to laura, exception: System.Exception: oh no", LogLevel.Error); + ThenLevelIsLogged("requestId: no request id, previousRequestId: no previous request id, message: a message from tom to laura", LogLevel.Error, _ex); } [Fact] @@ -63,18 +63,18 @@ namespace Ocelot.UnitTests.Logging { _logger.LogCritical($"a message from {_a} to {_b}", _ex); - ThenLevelIsLogged("requestId: no request id, previousRequestId: no previous request id, message: a message from tom to laura, exception: System.Exception: oh no", LogLevel.Critical); + ThenLevelIsLogged("requestId: no request id, previousRequestId: no previous request id, message: a message from tom to laura", LogLevel.Critical, _ex); } - private void ThenLevelIsLogged(string expected, LogLevel expectedLogLevel) + private void ThenLevelIsLogged(string expected, LogLevel expectedLogLevel, Exception ex = null) { _coreLogger.Verify( x => x.Log( expectedLogLevel, - It.IsAny(), - It.Is(o => o.ToString() == expected), - It.IsAny(), - It.IsAny>()), Times.Once); + default(EventId), + expected, + ex, + It.IsAny>()), Times.Once); } } } diff --git a/test/Ocelot.UnitTests/Middleware/OcelotPiplineBuilderTests.cs b/test/Ocelot.UnitTests/Middleware/OcelotPiplineBuilderTests.cs index bafc3532..21e8dd63 100644 --- a/test/Ocelot.UnitTests/Middleware/OcelotPiplineBuilderTests.cs +++ b/test/Ocelot.UnitTests/Middleware/OcelotPiplineBuilderTests.cs @@ -4,16 +4,17 @@ using System.Threading.Tasks; namespace Ocelot.UnitTests.Middleware { using Microsoft.AspNetCore.Hosting; - using Microsoft.AspNetCore.Hosting.Internal; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; + using Moq; using Ocelot.DependencyInjection; using Ocelot.Logging; using Ocelot.Middleware; using Ocelot.Middleware.Pipeline; using Shouldly; using System.Collections.Generic; + using System.Reflection; using TestStack.BDDfy; using Xunit; @@ -28,11 +29,22 @@ namespace Ocelot.UnitTests.Middleware { _configRoot = new ConfigurationRoot(new List()); _services = new ServiceCollection(); - _services.AddSingleton(); + _services.AddSingleton(GetHostingEnvironment()); _services.AddSingleton(_configRoot); _services.AddOcelot(); } + + private IWebHostEnvironment GetHostingEnvironment() + { + var environment = new Mock(); + environment + .Setup(e => e.ApplicationName) + .Returns(typeof(OcelotPiplineBuilderTests).GetTypeInfo().Assembly.GetName().Name); + + return environment.Object; + } + [Fact] public void should_build_generic() { diff --git a/test/Ocelot.UnitTests/Ocelot.UnitTests.csproj b/test/Ocelot.UnitTests/Ocelot.UnitTests.csproj index 0809d120..d067d869 100644 --- a/test/Ocelot.UnitTests/Ocelot.UnitTests.csproj +++ b/test/Ocelot.UnitTests/Ocelot.UnitTests.csproj @@ -2,7 +2,7 @@ 0.0.0-dev - netcoreapp2.2 + netcoreapp3.0 Ocelot.UnitTests Ocelot.UnitTests Exe @@ -46,13 +46,13 @@ PreserveNewest - + - - - - + + + + all @@ -60,30 +60,38 @@ runtime; build; native; contentfiles; analyzers; buildtransitive - - - - - - - - - + + + + + + + + + - - + + - - - - + + + + + + + + + all + runtime; build; native; contentfiles; analyzers + + \ No newline at end of file diff --git a/test/Ocelot.UnitTests/Rafty/OcelotAdministrationBuilderExtensionsTests.cs b/test/Ocelot.UnitTests/Rafty/OcelotAdministrationBuilderExtensionsTests.cs index feb8603c..212d9293 100644 --- a/test/Ocelot.UnitTests/Rafty/OcelotAdministrationBuilderExtensionsTests.cs +++ b/test/Ocelot.UnitTests/Rafty/OcelotAdministrationBuilderExtensionsTests.cs @@ -1,15 +1,16 @@ namespace Ocelot.UnitTests.Rafty { using Microsoft.AspNetCore.Hosting; - using Microsoft.AspNetCore.Hosting.Internal; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; + using Moq; using Ocelot.Administration; using Ocelot.DependencyInjection; using Provider.Rafty; using Shouldly; using System; using System.Collections.Generic; + using System.Reflection; using TestStack.BDDfy; using Xunit; @@ -25,10 +26,20 @@ { _configRoot = new ConfigurationRoot(new List()); _services = new ServiceCollection(); - _services.AddSingleton(); + _services.AddSingleton(GetHostingEnvironment()); _services.AddSingleton(_configRoot); } + private IWebHostEnvironment GetHostingEnvironment() + { + var environment = new Mock(); + environment + .Setup(e => e.ApplicationName) + .Returns(typeof(OcelotAdministrationBuilderExtensionsTests).GetTypeInfo().Assembly.GetName().Name); + + return environment.Object; + } + [Fact] public void should_set_up_rafty() { diff --git a/test/Ocelot.UnitTests/RateLimit/ClientRateLimitMiddlewareTests.cs b/test/Ocelot.UnitTests/RateLimit/ClientRateLimitMiddlewareTests.cs index 1f928ff5..2b54bbfa 100644 --- a/test/Ocelot.UnitTests/RateLimit/ClientRateLimitMiddlewareTests.cs +++ b/test/Ocelot.UnitTests/RateLimit/ClientRateLimitMiddlewareTests.cs @@ -69,7 +69,7 @@ namespace Ocelot.UnitTests.RateLimit this.Given(x => x.GivenTheDownStreamRouteIs(downstreamRoute)) .When(x => x.WhenICallTheMiddlewareMultipleTime(2)) .Then(x => x.ThenresponseStatusCodeIs200()) - .When(x => x.WhenICallTheMiddlewareMultipleTime(2)) + .When(x => x.WhenICallTheMiddlewareMultipleTime(3)) .Then(x => x.ThenresponseStatusCodeIs429()) .BDDfy(); } @@ -145,8 +145,9 @@ namespace Ocelot.UnitTests.RateLimit internal class FakeStream : Stream { public override void Flush() - { - throw new System.NotImplementedException(); + { + //do nothing + //throw new System.NotImplementedException(); } public override int Read(byte[] buffer, int offset, int count) diff --git a/test/Ocelot.UnitTests/Request/Mapper/RequestMapperTests.cs b/test/Ocelot.UnitTests/Request/Mapper/RequestMapperTests.cs index 1727d503..ac81a4d4 100644 --- a/test/Ocelot.UnitTests/Request/Mapper/RequestMapperTests.cs +++ b/test/Ocelot.UnitTests/Request/Mapper/RequestMapperTests.cs @@ -1,7 +1,6 @@ namespace Ocelot.UnitTests.Request.Mapper { using Microsoft.AspNetCore.Http; - using Microsoft.AspNetCore.Http.Internal; using Microsoft.Extensions.Primitives; using Ocelot.Request.Mapper; using Ocelot.Responses; @@ -19,6 +18,7 @@ public class RequestMapperTests { + private readonly HttpContext _httpContext; private readonly HttpRequest _inputRequest; private readonly RequestMapper _requestMapper; @@ -29,8 +29,8 @@ public RequestMapperTests() { - _inputRequest = new DefaultHttpRequest(new DefaultHttpContext()); - + _httpContext = new DefaultHttpContext(); + _inputRequest = _httpContext.Request; _requestMapper = new RequestMapper(); } diff --git a/test/Ocelot.UnitTests/UnitTests.runsettings b/test/Ocelot.UnitTests/UnitTests.runsettings new file mode 100644 index 00000000..1cba21cc --- /dev/null +++ b/test/Ocelot.UnitTests/UnitTests.runsettings @@ -0,0 +1,23 @@ + + + + + + + opencover + false + true + + + + + + + + + + + \ No newline at end of file diff --git a/tools/packages.config b/tools/packages.config index 1d2d1534..238f21ca 100644 --- a/tools/packages.config +++ b/tools/packages.config @@ -1,4 +1,4 @@ - +