Monitoring (#219)

* feat:  use Https://github.com/ButterflyAPM to monitor each API request monitoring metrics

* feat: using DiagnosticSource and Butterfly.OpenTracing

* refactor:refactor Ocelot tracing, merge code into OcelotDiagnosticListener

* refactor: move OcelotHttpTracingHandler to Requester

* fix: Requester\HttpClientBuilder.cs(10,14): error CS0234: The type or namespace name 'Tracing' does not exist in the namespace

* feat: add test should_set_up_tracing

* feat : Remove extraneous code

* feat: remove unused DiagnosticSource diagnostic

* fix : test UseTracing

* add test should_call_scoped_data_repository_QosProviderError

* add test should_return_any_errors

* add test HttpClientHttpRequesterTest

*  it should keep it can not be deleted
This commit is contained in:
geffzhang 2018-02-13 02:33:23 +08:00 committed by Tom Pallister
parent f179b7d0d0
commit ef3c4f614a
30 changed files with 1513 additions and 1211 deletions

View File

@ -1,7 +1,7 @@
 
Microsoft Visual Studio Solution File, Format Version 12.00 Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15 # Visual Studio 15
VisualStudioVersion = 15.0.26730.15 VisualStudioVersion = 15.0.27130.2024
MinimumVisualStudioVersion = 10.0.40219.1 MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{5CFB79B7-C9DC-45A4-9A75-625D92471702}" Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{5CFB79B7-C9DC-45A4-9A75-625D92471702}"
EndProject EndProject

View File

@ -7,7 +7,7 @@ namespace Ocelot.Configuration.Creator
public HttpHandlerOptions Create(FileReRoute fileReRoute) public HttpHandlerOptions Create(FileReRoute fileReRoute)
{ {
return new HttpHandlerOptions(fileReRoute.HttpHandlerOptions.AllowAutoRedirect, return new HttpHandlerOptions(fileReRoute.HttpHandlerOptions.AllowAutoRedirect,
fileReRoute.HttpHandlerOptions.UseCookieContainer); fileReRoute.HttpHandlerOptions.UseCookieContainer, fileReRoute.HttpHandlerOptions.UseTracing);
} }
} }
} }

View File

@ -11,5 +11,7 @@
public bool AllowAutoRedirect { get; set; } public bool AllowAutoRedirect { get; set; }
public bool UseCookieContainer { get; set; } public bool UseCookieContainer { get; set; }
public bool UseTracing { get; set; }
} }
} }

View File

@ -6,10 +6,11 @@
/// </summary> /// </summary>
public class HttpHandlerOptions public class HttpHandlerOptions
{ {
public HttpHandlerOptions(bool allowAutoRedirect, bool useCookieContainer) public HttpHandlerOptions(bool allowAutoRedirect, bool useCookieContainer, bool useTracing)
{ {
AllowAutoRedirect = allowAutoRedirect; AllowAutoRedirect = allowAutoRedirect;
UseCookieContainer = useCookieContainer; UseCookieContainer = useCookieContainer;
UseTracing = useTracing;
} }
/// <summary> /// <summary>
@ -21,5 +22,10 @@
/// Specify is handler has to use a cookie container /// Specify is handler has to use a cookie container
/// </summary> /// </summary>
public bool UseCookieContainer { get; private set; } public bool UseCookieContainer { get; private set; }
// <summary>
/// Specify is handler has to use a opentracing
/// </summary>
public bool UseTracing { get; private set; }
} }
} }

View File

@ -1,12 +1,14 @@
using CacheManager.Core; using Butterfly.Client.AspNetCore;
using System; using CacheManager.Core;
using System;
namespace Ocelot.DependencyInjection
{ namespace Ocelot.DependencyInjection
public interface IOcelotBuilder {
{ public interface IOcelotBuilder
IOcelotBuilder AddStoreOcelotConfigurationInConsul(); {
IOcelotBuilder AddCacheManager(Action<ConfigurationBuilderCachePart> settings); IOcelotBuilder AddStoreOcelotConfigurationInConsul();
IOcelotAdministrationBuilder AddAdministration(string path, string secret); IOcelotBuilder AddCacheManager(Action<ConfigurationBuilderCachePart> settings);
} IOcelotBuilder AddOpenTracing(Action<ButterflyOptions> settings);
} IOcelotAdministrationBuilder AddAdministration(string path, string secret);
}
}

View File

@ -1,307 +1,313 @@
using CacheManager.Core; using Butterfly.Client.AspNetCore;
using IdentityServer4.Models; using CacheManager.Core;
using Microsoft.AspNetCore.Http; using IdentityServer4.AccessTokenValidation;
using Microsoft.Extensions.Configuration; using IdentityServer4.Models;
using Microsoft.Extensions.DependencyInjection; using Microsoft.AspNetCore.Builder;
using Ocelot.Authorisation; using Microsoft.AspNetCore.Hosting;
using Ocelot.Cache; using Microsoft.AspNetCore.Http;
using Ocelot.Claims; using Microsoft.Extensions.Configuration;
using Ocelot.Configuration.Authentication; using Microsoft.Extensions.DependencyInjection;
using Ocelot.Configuration.Creator; using Microsoft.Extensions.DependencyInjection.Extensions;
using Ocelot.Configuration.File; using Ocelot.Authorisation;
using Ocelot.Configuration.Parser; using Ocelot.Cache;
using Ocelot.Configuration.Provider; using Ocelot.Claims;
using Ocelot.Configuration.Repository; using Ocelot.Configuration;
using Ocelot.Configuration.Setter; using Ocelot.Configuration.Authentication;
using Ocelot.Configuration.Validator; using Ocelot.Configuration.Builder;
using Ocelot.DownstreamRouteFinder.Finder; using Ocelot.Configuration.Creator;
using Ocelot.DownstreamRouteFinder.UrlMatcher; using Ocelot.Configuration.File;
using Ocelot.DownstreamUrlCreator; using Ocelot.Configuration.Parser;
using Ocelot.DownstreamUrlCreator.UrlTemplateReplacer; using Ocelot.Configuration.Provider;
using Ocelot.Headers; using Ocelot.Configuration.Repository;
using Ocelot.Infrastructure.Claims.Parser; using Ocelot.Configuration.Setter;
using Ocelot.Infrastructure.RequestData; using Ocelot.Configuration.Validator;
using Ocelot.LoadBalancer.LoadBalancers; using Ocelot.DownstreamRouteFinder.Finder;
using Ocelot.Logging; using Ocelot.DownstreamRouteFinder.UrlMatcher;
using Ocelot.Middleware; using Ocelot.DownstreamUrlCreator;
using Ocelot.QueryStrings; using Ocelot.DownstreamUrlCreator.UrlTemplateReplacer;
using Ocelot.RateLimit; using Ocelot.Headers;
using Ocelot.Request.Builder; using Ocelot.Infrastructure.Claims.Parser;
using Ocelot.Request.Mapper; using Ocelot.Infrastructure.RequestData;
using Ocelot.Requester; using Ocelot.LoadBalancer.LoadBalancers;
using Ocelot.Requester.QoS; using Ocelot.Logging;
using Ocelot.Responder; using Ocelot.Middleware;
using Ocelot.ServiceDiscovery; using Ocelot.QueryStrings;
using System; using Ocelot.Raft;
using System.Collections.Generic; using Ocelot.RateLimit;
using System.IdentityModel.Tokens.Jwt; using Ocelot.Request.Builder;
using System.Net.Http; using Ocelot.Request.Mapper;
using System.Reflection; using Ocelot.Requester;
using System.Security.Cryptography.X509Certificates; using Ocelot.Requester.QoS;
using IdentityServer4.AccessTokenValidation; using Ocelot.Responder;
using Microsoft.AspNetCore.Builder; using Ocelot.ServiceDiscovery;
using Microsoft.AspNetCore.Hosting; using Rafty.Concensus;
using Ocelot.Configuration; using Rafty.FiniteStateMachine;
using Ocelot.Configuration.Builder; using Rafty.Infrastructure;
using FileConfigurationProvider = Ocelot.Configuration.Provider.FileConfigurationProvider; using Rafty.Log;
using Microsoft.Extensions.DependencyInjection.Extensions; using System;
using System.Linq; using System.Collections.Generic;
using Ocelot.Raft; using System.IdentityModel.Tokens.Jwt;
using Rafty.Concensus; using System.Linq;
using Rafty.FiniteStateMachine; using System.Reflection;
using Rafty.Infrastructure; using System.Security.Cryptography.X509Certificates;
using Rafty.Log; using FileConfigurationProvider = Ocelot.Configuration.Provider.FileConfigurationProvider;
using Newtonsoft.Json;
namespace Ocelot.DependencyInjection
namespace Ocelot.DependencyInjection {
{ public class OcelotBuilder : IOcelotBuilder
public class OcelotBuilder : IOcelotBuilder {
{ private IServiceCollection _services;
private IServiceCollection _services; private IConfiguration _configurationRoot;
private IConfiguration _configurationRoot;
public OcelotBuilder(IServiceCollection services, IConfiguration configurationRoot)
public OcelotBuilder(IServiceCollection services, IConfiguration configurationRoot) {
{ _configurationRoot = configurationRoot;
_configurationRoot = configurationRoot; _services = services;
_services = services;
//add default cache settings...
//add default cache settings... Action<ConfigurationBuilderCachePart> defaultCachingSettings = x =>
Action<ConfigurationBuilderCachePart> defaultCachingSettings = x => {
{ x.WithDictionaryHandle();
x.WithDictionaryHandle(); };
};
AddCacheManager(defaultCachingSettings);
AddCacheManager(defaultCachingSettings);
//add ocelot services...
//add ocelot services... _services.Configure<FileConfiguration>(configurationRoot);
_services.Configure<FileConfiguration>(configurationRoot); _services.TryAddSingleton<IHttpResponseHeaderReplacer, HttpResponseHeaderReplacer>();
_services.TryAddSingleton<IHttpResponseHeaderReplacer, HttpResponseHeaderReplacer>(); _services.TryAddSingleton<IHttpContextRequestHeaderReplacer, HttpContextRequestHeaderReplacer>();
_services.TryAddSingleton<IHttpContextRequestHeaderReplacer, HttpContextRequestHeaderReplacer>(); _services.TryAddSingleton<IHeaderFindAndReplaceCreator, HeaderFindAndReplaceCreator>();
_services.TryAddSingleton<IHeaderFindAndReplaceCreator, HeaderFindAndReplaceCreator>(); _services.TryAddSingleton<IOcelotConfigurationCreator, FileOcelotConfigurationCreator>();
_services.TryAddSingleton<IOcelotConfigurationCreator, FileOcelotConfigurationCreator>(); _services.TryAddSingleton<IOcelotConfigurationRepository, InMemoryOcelotConfigurationRepository>();
_services.TryAddSingleton<IOcelotConfigurationRepository, InMemoryOcelotConfigurationRepository>(); _services.TryAddSingleton<IConfigurationValidator, FileConfigurationFluentValidator>();
_services.TryAddSingleton<IConfigurationValidator, FileConfigurationFluentValidator>(); _services.TryAddSingleton<IClaimsToThingCreator, ClaimsToThingCreator>();
_services.TryAddSingleton<IClaimsToThingCreator, ClaimsToThingCreator>(); _services.TryAddSingleton<IAuthenticationOptionsCreator, AuthenticationOptionsCreator>();
_services.TryAddSingleton<IAuthenticationOptionsCreator, AuthenticationOptionsCreator>(); _services.TryAddSingleton<IUpstreamTemplatePatternCreator, UpstreamTemplatePatternCreator>();
_services.TryAddSingleton<IUpstreamTemplatePatternCreator, UpstreamTemplatePatternCreator>(); _services.TryAddSingleton<IRequestIdKeyCreator, RequestIdKeyCreator>();
_services.TryAddSingleton<IRequestIdKeyCreator, RequestIdKeyCreator>(); _services.TryAddSingleton<IServiceProviderConfigurationCreator,ServiceProviderConfigurationCreator>();
_services.TryAddSingleton<IServiceProviderConfigurationCreator,ServiceProviderConfigurationCreator>(); _services.TryAddSingleton<IQoSOptionsCreator, QoSOptionsCreator>();
_services.TryAddSingleton<IQoSOptionsCreator, QoSOptionsCreator>(); _services.TryAddSingleton<IReRouteOptionsCreator, ReRouteOptionsCreator>();
_services.TryAddSingleton<IReRouteOptionsCreator, ReRouteOptionsCreator>(); _services.TryAddSingleton<IRateLimitOptionsCreator, RateLimitOptionsCreator>();
_services.TryAddSingleton<IRateLimitOptionsCreator, RateLimitOptionsCreator>(); _services.TryAddSingleton<IBaseUrlFinder, BaseUrlFinder>();
_services.TryAddSingleton<IBaseUrlFinder, BaseUrlFinder>(); _services.TryAddSingleton<IRegionCreator, RegionCreator>();
_services.TryAddSingleton<IRegionCreator, RegionCreator>(); _services.TryAddSingleton<IFileConfigurationRepository, FileConfigurationRepository>();
_services.TryAddSingleton<IFileConfigurationRepository, FileConfigurationRepository>(); _services.TryAddSingleton<IFileConfigurationSetter, FileConfigurationSetter>();
_services.TryAddSingleton<IFileConfigurationSetter, FileConfigurationSetter>(); _services.TryAddSingleton<IFileConfigurationProvider, FileConfigurationProvider>();
_services.TryAddSingleton<IFileConfigurationProvider, FileConfigurationProvider>(); _services.TryAddSingleton<IQosProviderHouse, QosProviderHouse>();
_services.TryAddSingleton<IQosProviderHouse, QosProviderHouse>(); _services.TryAddSingleton<IQoSProviderFactory, QoSProviderFactory>();
_services.TryAddSingleton<IQoSProviderFactory, QoSProviderFactory>(); _services.TryAddSingleton<IServiceDiscoveryProviderFactory, ServiceDiscoveryProviderFactory>();
_services.TryAddSingleton<IServiceDiscoveryProviderFactory, ServiceDiscoveryProviderFactory>(); _services.TryAddSingleton<ILoadBalancerFactory, LoadBalancerFactory>();
_services.TryAddSingleton<ILoadBalancerFactory, LoadBalancerFactory>(); _services.TryAddSingleton<ILoadBalancerHouse, LoadBalancerHouse>();
_services.TryAddSingleton<ILoadBalancerHouse, LoadBalancerHouse>(); _services.TryAddSingleton<IOcelotLoggerFactory, AspDotNetLoggerFactory>();
_services.TryAddSingleton<IOcelotLoggerFactory, AspDotNetLoggerFactory>(); _services.TryAddSingleton<IUrlBuilder, UrlBuilder>();
_services.TryAddSingleton<IUrlBuilder, UrlBuilder>(); _services.TryAddSingleton<IRemoveOutputHeaders, RemoveOutputHeaders>();
_services.TryAddSingleton<IRemoveOutputHeaders, RemoveOutputHeaders>(); _services.TryAddSingleton<IOcelotConfigurationProvider, OcelotConfigurationProvider>();
_services.TryAddSingleton<IOcelotConfigurationProvider, OcelotConfigurationProvider>(); _services.TryAddSingleton<IClaimToThingConfigurationParser, ClaimToThingConfigurationParser>();
_services.TryAddSingleton<IClaimToThingConfigurationParser, ClaimToThingConfigurationParser>(); _services.TryAddSingleton<IClaimsAuthoriser, ClaimsAuthoriser>();
_services.TryAddSingleton<IClaimsAuthoriser, ClaimsAuthoriser>(); _services.TryAddSingleton<IScopesAuthoriser, ScopesAuthoriser>();
_services.TryAddSingleton<IScopesAuthoriser, ScopesAuthoriser>(); _services.TryAddSingleton<IAddClaimsToRequest, AddClaimsToRequest>();
_services.TryAddSingleton<IAddClaimsToRequest, AddClaimsToRequest>(); _services.TryAddSingleton<IAddHeadersToRequest, AddHeadersToRequest>();
_services.TryAddSingleton<IAddHeadersToRequest, AddHeadersToRequest>(); _services.TryAddSingleton<IAddQueriesToRequest, AddQueriesToRequest>();
_services.TryAddSingleton<IAddQueriesToRequest, AddQueriesToRequest>(); _services.TryAddSingleton<IClaimsParser, ClaimsParser>();
_services.TryAddSingleton<IClaimsParser, ClaimsParser>(); _services.TryAddSingleton<IUrlPathToUrlTemplateMatcher, RegExUrlMatcher>();
_services.TryAddSingleton<IUrlPathToUrlTemplateMatcher, RegExUrlMatcher>(); _services.TryAddSingleton<IPlaceholderNameAndValueFinder, UrlPathPlaceholderNameAndValueFinder>();
_services.TryAddSingleton<IPlaceholderNameAndValueFinder, UrlPathPlaceholderNameAndValueFinder>(); _services.TryAddSingleton<IDownstreamPathPlaceholderReplacer, DownstreamTemplatePathPlaceholderReplacer>();
_services.TryAddSingleton<IDownstreamPathPlaceholderReplacer, DownstreamTemplatePathPlaceholderReplacer>(); _services.TryAddSingleton<IDownstreamRouteFinder, DownstreamRouteFinder.Finder.DownstreamRouteFinder>();
_services.TryAddSingleton<IDownstreamRouteFinder, DownstreamRouteFinder.Finder.DownstreamRouteFinder>(); _services.TryAddSingleton<IHttpRequester, HttpClientHttpRequester>();
_services.TryAddSingleton<IHttpRequester, HttpClientHttpRequester>(); _services.TryAddSingleton<IHttpResponder, HttpContextResponder>();
_services.TryAddSingleton<IHttpResponder, HttpContextResponder>(); _services.TryAddSingleton<IRequestCreator, HttpRequestCreator>();
_services.TryAddSingleton<IRequestCreator, HttpRequestCreator>(); _services.TryAddSingleton<IErrorsToHttpStatusCodeMapper, ErrorsToHttpStatusCodeMapper>();
_services.TryAddSingleton<IErrorsToHttpStatusCodeMapper, ErrorsToHttpStatusCodeMapper>(); _services.TryAddSingleton<IRateLimitCounterHandler, MemoryCacheRateLimitCounterHandler>();
_services.TryAddSingleton<IRateLimitCounterHandler, MemoryCacheRateLimitCounterHandler>(); _services.TryAddSingleton<IHttpClientCache, MemoryHttpClientCache>();
_services.TryAddSingleton<IHttpClientCache, MemoryHttpClientCache>(); _services.TryAddSingleton<IRequestMapper, RequestMapper>();
_services.TryAddSingleton<IRequestMapper, RequestMapper>(); _services.TryAddSingleton<IHttpHandlerOptionsCreator, HttpHandlerOptionsCreator>();
_services.TryAddSingleton<IHttpHandlerOptionsCreator, HttpHandlerOptionsCreator>(); _services.TryAddSingleton<IDownstreamAddressesCreator, DownstreamAddressesCreator>();
_services.TryAddSingleton<IDownstreamAddressesCreator, DownstreamAddressesCreator>(); // see this for why we register this as singleton http://stackoverflow.com/questions/37371264/invalidoperationexception-unable-to-resolve-service-for-type-microsoft-aspnetc
// see this for why we register this as singleton http://stackoverflow.com/questions/37371264/invalidoperationexception-unable-to-resolve-service-for-type-microsoft-aspnetc // could maybe use a scoped data repository
// could maybe use a scoped data repository _services.TryAddSingleton<IHttpContextAccessor, HttpContextAccessor>();
_services.TryAddSingleton<IHttpContextAccessor, HttpContextAccessor>(); _services.TryAddSingleton<IRequestScopedDataRepository, HttpDataRepository>();
_services.TryAddSingleton<IRequestScopedDataRepository, HttpDataRepository>(); _services.AddMemoryCache();
_services.AddMemoryCache(); _services.TryAddSingleton<OcelotDiagnosticListener>();
_services.TryAddSingleton<OcelotDiagnosticListener>();
//add asp.net services..
//add asp.net services.. var assembly = typeof(FileConfigurationController).GetTypeInfo().Assembly;
var assembly = typeof(FileConfigurationController).GetTypeInfo().Assembly;
_services.AddMvcCore()
_services.AddMvcCore() .AddApplicationPart(assembly)
.AddApplicationPart(assembly) .AddControllersAsServices()
.AddControllersAsServices() .AddAuthorization()
.AddAuthorization() .AddJsonFormatters();
.AddJsonFormatters();
_services.AddLogging();
_services.AddLogging(); _services.AddMiddlewareAnalysis();
_services.AddMiddlewareAnalysis(); _services.AddWebEncoders();
_services.AddWebEncoders(); _services.AddSingleton<IAdministrationPath>(new NullAdministrationPath());
_services.AddSingleton<IAdministrationPath>(new NullAdministrationPath()); }
}
public IOcelotAdministrationBuilder AddAdministration(string path, string secret)
public IOcelotAdministrationBuilder AddAdministration(string path, string secret) {
{ var administrationPath = new AdministrationPath(path);
var administrationPath = new AdministrationPath(path);
//add identity server for admin area
//add identity server for admin area var identityServerConfiguration = IdentityServerConfigurationCreator.GetIdentityServerConfiguration(secret);
var identityServerConfiguration = IdentityServerConfigurationCreator.GetIdentityServerConfiguration(secret);
if (identityServerConfiguration != null)
if (identityServerConfiguration != null) {
{ AddIdentityServer(identityServerConfiguration, administrationPath);
AddIdentityServer(identityServerConfiguration, administrationPath); }
}
var descriptor = new ServiceDescriptor(typeof(IAdministrationPath), administrationPath);
var descriptor = new ServiceDescriptor(typeof(IAdministrationPath), administrationPath); _services.Replace(descriptor);
_services.Replace(descriptor); return new OcelotAdministrationBuilder(_services, _configurationRoot);
return new OcelotAdministrationBuilder(_services, _configurationRoot); }
}
public IOcelotBuilder AddStoreOcelotConfigurationInConsul()
public IOcelotBuilder AddStoreOcelotConfigurationInConsul() {
{ var serviceDiscoveryPort = _configurationRoot.GetValue("GlobalConfiguration:ServiceDiscoveryProvider:Port", 0);
var serviceDiscoveryPort = _configurationRoot.GetValue("GlobalConfiguration:ServiceDiscoveryProvider:Port", 0); var serviceDiscoveryHost = _configurationRoot.GetValue("GlobalConfiguration:ServiceDiscoveryProvider:Host", string.Empty);
var serviceDiscoveryHost = _configurationRoot.GetValue("GlobalConfiguration:ServiceDiscoveryProvider:Host", string.Empty);
var config = new ServiceProviderConfigurationBuilder()
var config = new ServiceProviderConfigurationBuilder() .WithServiceDiscoveryProviderPort(serviceDiscoveryPort)
.WithServiceDiscoveryProviderPort(serviceDiscoveryPort) .WithServiceDiscoveryProviderHost(serviceDiscoveryHost)
.WithServiceDiscoveryProviderHost(serviceDiscoveryHost) .Build();
.Build();
_services.AddSingleton<ServiceProviderConfiguration>(config);
_services.AddSingleton<ServiceProviderConfiguration>(config); _services.AddSingleton<ConsulFileConfigurationPoller>();
_services.AddSingleton<ConsulFileConfigurationPoller>(); _services.AddSingleton<IFileConfigurationRepository, ConsulFileConfigurationRepository>();
_services.AddSingleton<IFileConfigurationRepository, ConsulFileConfigurationRepository>(); return this;
return this; }
}
public IOcelotBuilder AddCacheManager(Action<ConfigurationBuilderCachePart> settings)
public IOcelotBuilder AddCacheManager(Action<ConfigurationBuilderCachePart> settings) {
{ var cacheManagerOutputCache = CacheFactory.Build<CachedResponse>("OcelotOutputCache", settings);
var cacheManagerOutputCache = CacheFactory.Build<CachedResponse>("OcelotOutputCache", settings); var ocelotOutputCacheManager = new OcelotCacheManagerCache<CachedResponse>(cacheManagerOutputCache);
var ocelotOutputCacheManager = new OcelotCacheManagerCache<CachedResponse>(cacheManagerOutputCache);
_services.RemoveAll(typeof(ICacheManager<CachedResponse>));
_services.RemoveAll(typeof(ICacheManager<CachedResponse>)); _services.RemoveAll(typeof(IOcelotCache<CachedResponse>));
_services.RemoveAll(typeof(IOcelotCache<CachedResponse>)); _services.AddSingleton<ICacheManager<CachedResponse>>(cacheManagerOutputCache);
_services.AddSingleton<ICacheManager<CachedResponse>>(cacheManagerOutputCache); _services.AddSingleton<IOcelotCache<CachedResponse>>(ocelotOutputCacheManager);
_services.AddSingleton<IOcelotCache<CachedResponse>>(ocelotOutputCacheManager);
var ocelotConfigCacheManagerOutputCache = CacheFactory.Build<IOcelotConfiguration>("OcelotConfigurationCache", settings);
var ocelotConfigCacheManagerOutputCache = CacheFactory.Build<IOcelotConfiguration>("OcelotConfigurationCache", settings); var ocelotConfigCacheManager = new OcelotCacheManagerCache<IOcelotConfiguration>(ocelotConfigCacheManagerOutputCache);
var ocelotConfigCacheManager = new OcelotCacheManagerCache<IOcelotConfiguration>(ocelotConfigCacheManagerOutputCache); _services.RemoveAll(typeof(ICacheManager<IOcelotConfiguration>));
_services.RemoveAll(typeof(ICacheManager<IOcelotConfiguration>)); _services.RemoveAll(typeof(IOcelotCache<IOcelotConfiguration>));
_services.RemoveAll(typeof(IOcelotCache<IOcelotConfiguration>)); _services.AddSingleton<ICacheManager<IOcelotConfiguration>>(ocelotConfigCacheManagerOutputCache);
_services.AddSingleton<ICacheManager<IOcelotConfiguration>>(ocelotConfigCacheManagerOutputCache); _services.AddSingleton<IOcelotCache<IOcelotConfiguration>>(ocelotConfigCacheManager);
_services.AddSingleton<IOcelotCache<IOcelotConfiguration>>(ocelotConfigCacheManager);
var fileConfigCacheManagerOutputCache = CacheFactory.Build<FileConfiguration>("FileConfigurationCache", settings);
var fileConfigCacheManagerOutputCache = CacheFactory.Build<FileConfiguration>("FileConfigurationCache", settings); var fileConfigCacheManager = new OcelotCacheManagerCache<FileConfiguration>(fileConfigCacheManagerOutputCache);
var fileConfigCacheManager = new OcelotCacheManagerCache<FileConfiguration>(fileConfigCacheManagerOutputCache); _services.RemoveAll(typeof(ICacheManager<FileConfiguration>));
_services.RemoveAll(typeof(ICacheManager<FileConfiguration>)); _services.RemoveAll(typeof(IOcelotCache<FileConfiguration>));
_services.RemoveAll(typeof(IOcelotCache<FileConfiguration>)); _services.AddSingleton<ICacheManager<FileConfiguration>>(fileConfigCacheManagerOutputCache);
_services.AddSingleton<ICacheManager<FileConfiguration>>(fileConfigCacheManagerOutputCache); _services.AddSingleton<IOcelotCache<FileConfiguration>>(fileConfigCacheManager);
_services.AddSingleton<IOcelotCache<FileConfiguration>>(fileConfigCacheManager); return this;
return this; }
}
public IOcelotBuilder AddOpenTracing(Action<ButterflyOptions> settings)
private void AddIdentityServer(IIdentityServerConfiguration identityServerConfiguration, IAdministrationPath adminPath) {
{ _services.AddTransient<OcelotHttpTracingHandler>();
_services.TryAddSingleton<IIdentityServerConfiguration>(identityServerConfiguration); _services.AddButterfly(settings);
_services.TryAddSingleton<IHashMatcher, HashMatcher>(); return this;
var identityServerBuilder = _services }
.AddIdentityServer(o => {
o.IssuerUri = "Ocelot"; private void AddIdentityServer(IIdentityServerConfiguration identityServerConfiguration, IAdministrationPath adminPath)
}) {
.AddInMemoryApiResources(Resources(identityServerConfiguration)) _services.TryAddSingleton<IIdentityServerConfiguration>(identityServerConfiguration);
.AddInMemoryClients(Client(identityServerConfiguration)); _services.TryAddSingleton<IHashMatcher, HashMatcher>();
var identityServerBuilder = _services
//todo - refactor a method so we know why this is happening .AddIdentityServer(o => {
var whb = _services.First(x => x.ServiceType == typeof(IWebHostBuilder)); o.IssuerUri = "Ocelot";
var urlFinder = new BaseUrlFinder((IWebHostBuilder)whb.ImplementationInstance); })
var baseSchemeUrlAndPort = urlFinder.Find(); .AddInMemoryApiResources(Resources(identityServerConfiguration))
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear(); .AddInMemoryClients(Client(identityServerConfiguration));
_services.AddAuthentication(IdentityServerAuthenticationDefaults.AuthenticationScheme) //todo - refactor a method so we know why this is happening
.AddIdentityServerAuthentication(o => var whb = _services.First(x => x.ServiceType == typeof(IWebHostBuilder));
{ var urlFinder = new BaseUrlFinder((IWebHostBuilder)whb.ImplementationInstance);
o.Authority = baseSchemeUrlAndPort + adminPath.Path; var baseSchemeUrlAndPort = urlFinder.Find();
o.ApiName = identityServerConfiguration.ApiName; JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
o.RequireHttpsMetadata = identityServerConfiguration.RequireHttps;
o.SupportedTokens = SupportedTokens.Both; _services.AddAuthentication(IdentityServerAuthenticationDefaults.AuthenticationScheme)
o.ApiSecret = identityServerConfiguration.ApiSecret; .AddIdentityServerAuthentication(o =>
}); {
o.Authority = baseSchemeUrlAndPort + adminPath.Path;
//todo - refactor naming.. o.ApiName = identityServerConfiguration.ApiName;
if (string.IsNullOrEmpty(identityServerConfiguration.CredentialsSigningCertificateLocation) || string.IsNullOrEmpty(identityServerConfiguration.CredentialsSigningCertificatePassword)) o.RequireHttpsMetadata = identityServerConfiguration.RequireHttps;
{ o.SupportedTokens = SupportedTokens.Both;
identityServerBuilder.AddDeveloperSigningCredential(); o.ApiSecret = identityServerConfiguration.ApiSecret;
} });
else
{ //todo - refactor naming..
//todo - refactor so calls method? if (string.IsNullOrEmpty(identityServerConfiguration.CredentialsSigningCertificateLocation) || string.IsNullOrEmpty(identityServerConfiguration.CredentialsSigningCertificatePassword))
var cert = new X509Certificate2(identityServerConfiguration.CredentialsSigningCertificateLocation, identityServerConfiguration.CredentialsSigningCertificatePassword); {
identityServerBuilder.AddSigningCredential(cert); identityServerBuilder.AddDeveloperSigningCredential();
} }
} else
{
private List<ApiResource> Resources(IIdentityServerConfiguration identityServerConfiguration) //todo - refactor so calls method?
{ var cert = new X509Certificate2(identityServerConfiguration.CredentialsSigningCertificateLocation, identityServerConfiguration.CredentialsSigningCertificatePassword);
return new List<ApiResource> identityServerBuilder.AddSigningCredential(cert);
{ }
new ApiResource(identityServerConfiguration.ApiName, identityServerConfiguration.ApiName) }
{
ApiSecrets = new List<Secret> private List<ApiResource> Resources(IIdentityServerConfiguration identityServerConfiguration)
{ {
new Secret return new List<ApiResource>
{ {
Value = identityServerConfiguration.ApiSecret.Sha256() new ApiResource(identityServerConfiguration.ApiName, identityServerConfiguration.ApiName)
} {
} ApiSecrets = new List<Secret>
}, {
}; new Secret
} {
Value = identityServerConfiguration.ApiSecret.Sha256()
private List<Client> Client(IIdentityServerConfiguration identityServerConfiguration) }
{ }
return new List<Client> },
{ };
new Client }
{
ClientId = identityServerConfiguration.ApiName, private List<Client> Client(IIdentityServerConfiguration identityServerConfiguration)
AllowedGrantTypes = GrantTypes.ClientCredentials, {
ClientSecrets = new List<Secret> {new Secret(identityServerConfiguration.ApiSecret.Sha256())}, return new List<Client>
AllowedScopes = { identityServerConfiguration.ApiName } {
} new Client
}; {
} ClientId = identityServerConfiguration.ApiName,
} AllowedGrantTypes = GrantTypes.ClientCredentials,
ClientSecrets = new List<Secret> {new Secret(identityServerConfiguration.ApiSecret.Sha256())},
public interface IOcelotAdministrationBuilder AllowedScopes = { identityServerConfiguration.ApiName }
{ }
IOcelotAdministrationBuilder AddRafty(); };
} }
}
public class OcelotAdministrationBuilder : IOcelotAdministrationBuilder
{ public interface IOcelotAdministrationBuilder
private IServiceCollection _services; {
private IConfiguration _configurationRoot; IOcelotAdministrationBuilder AddRafty();
}
public OcelotAdministrationBuilder(IServiceCollection services, IConfiguration configurationRoot)
{ public class OcelotAdministrationBuilder : IOcelotAdministrationBuilder
_configurationRoot = configurationRoot; {
_services = services; private IServiceCollection _services;
} private IConfiguration _configurationRoot;
public IOcelotAdministrationBuilder AddRafty() public OcelotAdministrationBuilder(IServiceCollection services, IConfiguration configurationRoot)
{ {
var settings = new InMemorySettings(4000, 5000, 100, 5000); _configurationRoot = configurationRoot;
_services.AddSingleton<ILog, SqlLiteLog>(); _services = services;
_services.AddSingleton<IFiniteStateMachine, OcelotFiniteStateMachine>(); }
_services.AddSingleton<ISettings>(settings);
_services.AddSingleton<IPeersProvider, FilePeersProvider>(); public IOcelotAdministrationBuilder AddRafty()
_services.AddSingleton<INode, Node>(); {
_services.Configure<FilePeers>(_configurationRoot); var settings = new InMemorySettings(4000, 5000, 100, 5000);
return this; _services.AddSingleton<ILog, SqlLiteLog>();
} _services.AddSingleton<IFiniteStateMachine, OcelotFiniteStateMachine>();
} _services.AddSingleton<ISettings>(settings);
} _services.AddSingleton<IPeersProvider, FilePeersProvider>();
_services.AddSingleton<INode, Node>();
_services.Configure<FilePeers>(_configurationRoot);
return this;
}
}
}

View File

@ -1,6 +1,8 @@
using System; using System;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DiagnosticAdapter; using Microsoft.Extensions.DiagnosticAdapter;
using Butterfly.Client.AspNetCore;
using Butterfly.OpenTracing;
namespace Ocelot.Logging namespace Ocelot.Logging
{ {
@ -17,6 +19,7 @@ namespace Ocelot.Logging
public virtual void OnMiddlewareStarting(HttpContext httpContext, string name) public virtual void OnMiddlewareStarting(HttpContext httpContext, string name)
{ {
_logger.LogTrace($"MiddlewareStarting: {name}; {httpContext.Request.Path}"); _logger.LogTrace($"MiddlewareStarting: {name}; {httpContext.Request.Path}");
Event(httpContext, $"MiddlewareStarting: {name}; {httpContext.Request.Path}");
} }
[DiagnosticName("Microsoft.AspNetCore.MiddlewareAnalysis.MiddlewareException")] [DiagnosticName("Microsoft.AspNetCore.MiddlewareAnalysis.MiddlewareException")]
@ -29,6 +32,13 @@ namespace Ocelot.Logging
public virtual void OnMiddlewareFinished(HttpContext httpContext, string name) public virtual void OnMiddlewareFinished(HttpContext httpContext, string name)
{ {
_logger.LogTrace($"MiddlewareFinished: {name}; {httpContext.Response.StatusCode}"); _logger.LogTrace($"MiddlewareFinished: {name}; {httpContext.Response.StatusCode}");
Event(httpContext, $"MiddlewareFinished: {name}; {httpContext.Response.StatusCode}");
}
private void Event(HttpContext httpContext, string @event)
{
var span = httpContext.GetSpan();
span?.Log(LogField.CreateNew().Event(@event));
} }
} }
} }

View File

@ -1,46 +1,47 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>netcoreapp2.0</TargetFramework> <TargetFramework>netcoreapp2.0</TargetFramework>
<RuntimeFrameworkVersion>2.0.0</RuntimeFrameworkVersion> <RuntimeFrameworkVersion>2.0.0</RuntimeFrameworkVersion>
<NETStandardImplicitPackageVersion>2.0.0</NETStandardImplicitPackageVersion> <NETStandardImplicitPackageVersion>2.0.0</NETStandardImplicitPackageVersion>
<Description>This 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. We have been unable to find this in my current workplace without having to write our own Javascript middlewares to handle the IdentityServer reference tokens. We would rather use the IdentityServer code that already exists to do this. 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.</Description> <Description>This 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. We have been unable to find this in my current workplace without having to write our own Javascript middlewares to handle the IdentityServer reference tokens. We would rather use the IdentityServer code that already exists to do this. 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.</Description>
<AssemblyTitle>Ocelot</AssemblyTitle> <AssemblyTitle>Ocelot</AssemblyTitle>
<VersionPrefix>0.0.0-dev</VersionPrefix> <VersionPrefix>0.0.0-dev</VersionPrefix>
<AssemblyName>Ocelot</AssemblyName> <AssemblyName>Ocelot</AssemblyName>
<PackageId>Ocelot</PackageId> <PackageId>Ocelot</PackageId>
<PackageTags>API Gateway;.NET core</PackageTags> <PackageTags>API Gateway;.NET core</PackageTags>
<PackageProjectUrl>https://github.com/TomPallister/Ocelot</PackageProjectUrl> <PackageProjectUrl>https://github.com/TomPallister/Ocelot</PackageProjectUrl>
<PackageProjectUrl>https://github.com/TomPallister/Ocelot</PackageProjectUrl> <PackageProjectUrl>https://github.com/TomPallister/Ocelot</PackageProjectUrl>
<RuntimeIdentifiers>win10-x64;osx.10.11-x64;osx.10.12-x64;win7-x64</RuntimeIdentifiers> <RuntimeIdentifiers>win10-x64;osx.10.11-x64;osx.10.12-x64;win7-x64</RuntimeIdentifiers>
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute> <GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute>
<GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute> <GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute>
<GeneratePackageOnBuild>True</GeneratePackageOnBuild> <GeneratePackageOnBuild>True</GeneratePackageOnBuild>
<GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute> <GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute>
<Authors>Tom Pallister</Authors> <Authors>Tom Pallister</Authors>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<DebugType>full</DebugType> <DebugType>full</DebugType>
<DebugSymbols>True</DebugSymbols> <DebugSymbols>True</DebugSymbols>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="FluentValidation" Version="7.2.1"/> <PackageReference Include="Butterfly.Client.AspNetCore" Version="0.0.5" />
<PackageReference Include="IdentityServer4.AccessTokenValidation" Version="2.1.0"/> <PackageReference Include="FluentValidation" Version="7.2.1" />
<PackageReference Include="Microsoft.AspNetCore.All" Version="2.0.0"/> <PackageReference Include="IdentityServer4.AccessTokenValidation" Version="2.1.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="2.0.0"/> <PackageReference Include="Microsoft.AspNetCore.All" Version="2.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.FileExtensions" Version="2.0.0"/> <PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="2.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="2.0.0"/> <PackageReference Include="Microsoft.Extensions.Configuration.FileExtensions" Version="2.0.0" />
<PackageReference Include="Microsoft.Extensions.DiagnosticAdapter" Version="2.0.0"/> <PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="2.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="2.0.0"/> <PackageReference Include="Microsoft.Extensions.DiagnosticAdapter" Version="2.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="2.0.0"/> <PackageReference Include="Microsoft.Extensions.Logging" Version="2.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="2.0.0"/> <PackageReference Include="Microsoft.Extensions.Logging.Console" Version="2.0.0" />
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="2.0.0"/> <PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="2.0.0" />
<PackageReference Include="System.Text.RegularExpressions" Version="4.3.0"/> <PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="2.0.0" />
<PackageReference Include="CacheManager.Core" Version="1.1.1"/> <PackageReference Include="System.Text.RegularExpressions" Version="4.3.0" />
<PackageReference Include="CacheManager.Microsoft.Extensions.Configuration" Version="1.1.1"/> <PackageReference Include="CacheManager.Core" Version="1.1.1" />
<PackageReference Include="CacheManager.Microsoft.Extensions.Logging" Version="1.1.1"/> <PackageReference Include="CacheManager.Microsoft.Extensions.Configuration" Version="1.1.1" />
<PackageReference Include="Consul" Version="0.7.2.3"/> <PackageReference Include="CacheManager.Microsoft.Extensions.Logging" Version="1.1.1" />
<PackageReference Include="Polly" Version="5.3.1"/> <PackageReference Include="Consul" Version="0.7.2.3" />
<PackageReference Include="IdentityServer4" Version="2.0.2"/> <PackageReference Include="Polly" Version="5.3.1" />
<PackageReference Include="Rafty" Version="0.4.2"/> <PackageReference Include="IdentityServer4" Version="2.0.2" />
</ItemGroup> <PackageReference Include="Rafty" Version="0.4.2" />
</ItemGroup>
</Project> </Project>

View File

@ -1,20 +1,21 @@
using System.Threading.Tasks; using System.Threading.Tasks;
using Ocelot.Responses; using Ocelot.Responses;
using Ocelot.Requester.QoS; using Ocelot.Requester.QoS;
using System.Net.Http; using System.Net.Http;
namespace Ocelot.Request.Builder namespace Ocelot.Request.Builder
{ {
public sealed class HttpRequestCreator : IRequestCreator public sealed class HttpRequestCreator : IRequestCreator
{ {
public async Task<Response<Request>> Build( public async Task<Response<Request>> Build(
HttpRequestMessage httpRequestMessage, HttpRequestMessage httpRequestMessage,
bool isQos, bool isQos,
IQoSProvider qosProvider, IQoSProvider qosProvider,
bool useCookieContainer, bool useCookieContainer,
bool allowAutoRedirect) bool allowAutoRedirect,
{ bool isTracing)
return new OkResponse<Request>(new Request(httpRequestMessage, isQos, qosProvider, allowAutoRedirect, useCookieContainer)); {
} return new OkResponse<Request>(new Request(httpRequestMessage, isQos, qosProvider, allowAutoRedirect, useCookieContainer, isTracing));
} }
} }
}

View File

@ -1,18 +1,19 @@
namespace Ocelot.Request.Builder namespace Ocelot.Request.Builder
{ {
using System.Net.Http; using System.Net.Http;
using System.Threading.Tasks; using System.Threading.Tasks;
using Ocelot.Requester.QoS; using Ocelot.Requester.QoS;
using Ocelot.Responses; using Ocelot.Responses;
public interface IRequestCreator public interface IRequestCreator
{ {
Task<Response<Request>> Build( Task<Response<Request>> Build(
HttpRequestMessage httpRequestMessage, HttpRequestMessage httpRequestMessage,
bool isQos, bool isQos,
IQoSProvider qosProvider, IQoSProvider qosProvider,
bool useCookieContainer, bool useCookieContainer,
bool allowAutoRedirect); bool allowAutoRedirect,
} bool isTracing);
} }
}

View File

@ -1,66 +1,65 @@
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Ocelot.Infrastructure.RequestData; using Ocelot.Infrastructure.RequestData;
using Ocelot.Logging; using Ocelot.Logging;
using Ocelot.Middleware; using Ocelot.Middleware;
using Ocelot.Request.Builder; using Ocelot.Request.Builder;
using Ocelot.Requester.QoS; using Ocelot.Requester.QoS;
namespace Ocelot.Request.Middleware namespace Ocelot.Request.Middleware
{ {
public class HttpRequestBuilderMiddleware : OcelotMiddleware public class HttpRequestBuilderMiddleware : OcelotMiddleware
{ {
private readonly RequestDelegate _next; private readonly RequestDelegate _next;
private readonly IRequestCreator _requestCreator; private readonly IRequestCreator _requestCreator;
private readonly IOcelotLogger _logger; private readonly IOcelotLogger _logger;
private readonly IQosProviderHouse _qosProviderHouse; private readonly IQosProviderHouse _qosProviderHouse;
public HttpRequestBuilderMiddleware(RequestDelegate next, public HttpRequestBuilderMiddleware(RequestDelegate next,
IOcelotLoggerFactory loggerFactory, IOcelotLoggerFactory loggerFactory,
IRequestScopedDataRepository requestScopedDataRepository, IRequestScopedDataRepository requestScopedDataRepository,
IRequestCreator requestCreator, IRequestCreator requestCreator,
IQosProviderHouse qosProviderHouse) IQosProviderHouse qosProviderHouse)
:base(requestScopedDataRepository) :base(requestScopedDataRepository)
{ {
_next = next; _next = next;
_requestCreator = requestCreator; _requestCreator = requestCreator;
_qosProviderHouse = qosProviderHouse; _qosProviderHouse = qosProviderHouse;
_logger = loggerFactory.CreateLogger<HttpRequestBuilderMiddleware>(); _logger = loggerFactory.CreateLogger<HttpRequestBuilderMiddleware>();
} }
public async Task Invoke(HttpContext context) public async Task Invoke(HttpContext context)
{ {
var qosProvider = _qosProviderHouse.Get(DownstreamRoute.ReRoute); var qosProvider = _qosProviderHouse.Get(DownstreamRoute.ReRoute);
if (qosProvider.IsError) if (qosProvider.IsError)
{ {
_logger.LogDebug("IQosProviderHouse returned an error, setting pipeline error"); _logger.LogDebug("IQosProviderHouse returned an error, setting pipeline error");
SetPipelineError(qosProvider.Errors); SetPipelineError(qosProvider.Errors);
return; return;
} }
var buildResult = await _requestCreator.Build( var buildResult = await _requestCreator.Build(
DownstreamRequest, DownstreamRequest,
DownstreamRoute.ReRoute.IsQos, DownstreamRoute.ReRoute.IsQos,
qosProvider.Data, qosProvider.Data,
DownstreamRoute.ReRoute.HttpHandlerOptions.UseCookieContainer, DownstreamRoute.ReRoute.HttpHandlerOptions.UseCookieContainer,
DownstreamRoute.ReRoute.HttpHandlerOptions.AllowAutoRedirect); DownstreamRoute.ReRoute.HttpHandlerOptions.AllowAutoRedirect,
DownstreamRoute.ReRoute.HttpHandlerOptions.UseTracing);
if (buildResult.IsError) if (buildResult.IsError)
{ {
_logger.LogDebug("IRequestCreator returned an error, setting pipeline error"); _logger.LogDebug("IRequestCreator returned an error, setting pipeline error");
SetPipelineError(buildResult.Errors);
SetPipelineError(buildResult.Errors); return;
}
return;
} _logger.LogDebug("setting upstream request");
_logger.LogDebug("setting upstream request");
SetUpstreamRequestForThisRequest(buildResult.Data);
SetUpstreamRequestForThisRequest(buildResult.Data);
await _next.Invoke(context);
await _next.Invoke(context); }
} }
} }
}

View File

@ -1,28 +1,32 @@
using System.Net.Http; using System.Net.Http;
using Ocelot.Requester.QoS; using Ocelot.Requester.QoS;
namespace Ocelot.Request namespace Ocelot.Request
{ {
public class Request public class Request
{ {
public Request( public Request(
HttpRequestMessage httpRequestMessage, HttpRequestMessage httpRequestMessage,
bool isQos, bool isQos,
IQoSProvider qosProvider, IQoSProvider qosProvider,
bool allowAutoRedirect, bool allowAutoRedirect,
bool useCookieContainer) bool useCookieContainer,
{ bool isTracing
HttpRequestMessage = httpRequestMessage; )
IsQos = isQos; {
QosProvider = qosProvider; HttpRequestMessage = httpRequestMessage;
AllowAutoRedirect = allowAutoRedirect; IsQos = isQos;
UseCookieContainer = useCookieContainer; QosProvider = qosProvider;
} AllowAutoRedirect = allowAutoRedirect;
UseCookieContainer = useCookieContainer;
public HttpRequestMessage HttpRequestMessage { get; private set; } IsTracing = isTracing;
public bool IsQos { get; private set; } }
public IQoSProvider QosProvider { get; private set; }
public bool AllowAutoRedirect { get; private set; } public HttpRequestMessage HttpRequestMessage { get; private set; }
public bool UseCookieContainer { get; private set; } public bool IsQos { get; private set; }
} public bool IsTracing { get; private set; }
} public IQoSProvider QosProvider { get; private set; }
public bool AllowAutoRedirect { get; private set; }
public bool UseCookieContainer { get; private set; }
}
}

View File

@ -1,66 +1,75 @@
using System; using Microsoft.Extensions.DependencyInjection;
using System.Collections.Generic; using Ocelot.Logging;
using System.Linq; using Ocelot.Requester.QoS;
using System.Net; using System;
using System.Net.Http; using System.Collections.Generic;
using System.Threading.Tasks; using System.Linq;
using Ocelot.Logging; using System.Net.Http;
using Ocelot.Requester.QoS; using System.Threading.Tasks;
namespace Ocelot.Requester namespace Ocelot.Requester
{ {
internal class HttpClientBuilder : IHttpClientBuilder internal class HttpClientBuilder : IHttpClientBuilder
{ {
private readonly Dictionary<int, Func<DelegatingHandler>> _handlers = new Dictionary<int, Func<DelegatingHandler>>(); private readonly Dictionary<int, Func<DelegatingHandler>> _handlers = new Dictionary<int, Func<DelegatingHandler>>();
public IHttpClientBuilder WithQos(IQoSProvider qosProvider, IOcelotLogger logger) public IHttpClientBuilder WithQos(IQoSProvider qosProvider, IOcelotLogger logger)
{ {
_handlers.Add(5000, () => new PollyCircuitBreakingDelegatingHandler(qosProvider, logger)); _handlers.Add(5000, () => new PollyCircuitBreakingDelegatingHandler(qosProvider, logger));
return this; return this;
} }
public IHttpClient Create(bool useCookies, bool allowAutoRedirect) private IHttpClientBuilder WithTracing(IServiceProvider provider)
{ {
var httpclientHandler = new HttpClientHandler { AllowAutoRedirect = allowAutoRedirect, UseCookies = useCookies}; _handlers.Add(6000, () => provider.GetService<OcelotHttpTracingHandler>());
return this;
var client = new HttpClient(CreateHttpMessageHandler(httpclientHandler)); }
return new HttpClientWrapper(client); public IHttpClient Create(bool useCookies, bool allowAutoRedirect, bool isTracing, IServiceProvider provider)
} {
var httpclientHandler = new HttpClientHandler { AllowAutoRedirect = allowAutoRedirect, UseCookies = useCookies };
private HttpMessageHandler CreateHttpMessageHandler(HttpMessageHandler httpMessageHandler) if (isTracing)
{ {
_handlers WithTracing(provider);
.OrderByDescending(handler => handler.Key) }
.Select(handler => handler.Value) var client = new HttpClient(CreateHttpMessageHandler(httpclientHandler));
.Reverse()
.ToList() return new HttpClientWrapper(client);
.ForEach(handler => }
{
var delegatingHandler = handler(); private HttpMessageHandler CreateHttpMessageHandler(HttpMessageHandler httpMessageHandler)
delegatingHandler.InnerHandler = httpMessageHandler; {
httpMessageHandler = delegatingHandler; _handlers
}); .OrderByDescending(handler => handler.Key)
return httpMessageHandler; .Select(handler => handler.Value)
} .Reverse()
} .ToList()
.ForEach(handler =>
/// <summary> {
/// This class was made to make unit testing easier when HttpClient is used. var delegatingHandler = handler();
/// </summary> delegatingHandler.InnerHandler = httpMessageHandler;
internal class HttpClientWrapper : IHttpClient httpMessageHandler = delegatingHandler;
{ });
public HttpClient Client { get; } return httpMessageHandler;
}
public HttpClientWrapper(HttpClient client) }
{
Client = client; /// <summary>
} /// This class was made to make unit testing easier when HttpClient is used.
/// </summary>
public Task<HttpResponseMessage> SendAsync(HttpRequestMessage request) internal class HttpClientWrapper : IHttpClient
{ {
return Client.SendAsync(request); public HttpClient Client { get; }
}
} public HttpClientWrapper(HttpClient client)
} {
Client = client;
}
public Task<HttpResponseMessage> SendAsync(HttpRequestMessage request)
{
return Client.SendAsync(request);
}
}
}

View File

@ -1,83 +1,84 @@
using System; using System;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Net.Http; using System.Net.Http;
using System.Threading.Tasks; using System.Threading.Tasks;
using Ocelot.Configuration; using Ocelot.Configuration;
using Ocelot.Logging; using Ocelot.Logging;
using Ocelot.Responses; using Ocelot.Responses;
using Polly.CircuitBreaker; using Polly.CircuitBreaker;
using Polly.Timeout; using Polly.Timeout;
namespace Ocelot.Requester namespace Ocelot.Requester
{ {
public class HttpClientHttpRequester : IHttpRequester public class HttpClientHttpRequester : IHttpRequester
{ {
private readonly IHttpClientCache _cacheHandlers; private readonly IHttpClientCache _cacheHandlers;
private readonly IOcelotLogger _logger; private readonly IOcelotLogger _logger;
private readonly IServiceProvider _serviceProvider;
public HttpClientHttpRequester(IOcelotLoggerFactory loggerFactory,
IHttpClientCache cacheHandlers) public HttpClientHttpRequester(IOcelotLoggerFactory loggerFactory, IHttpClientCache cacheHandlers, IServiceProvider provider)
{ {
_logger = loggerFactory.CreateLogger<HttpClientHttpRequester>(); _logger = loggerFactory.CreateLogger<HttpClientHttpRequester>();
_cacheHandlers = cacheHandlers; _cacheHandlers = cacheHandlers;
} _serviceProvider = provider;
}
public async Task<Response<HttpResponseMessage>> GetResponse(Request.Request request)
{ public async Task<Response<HttpResponseMessage>> GetResponse(Request.Request request)
var builder = new HttpClientBuilder(); {
var builder = new HttpClientBuilder();
var cacheKey = GetCacheKey(request, builder);
var cacheKey = GetCacheKey(request, builder);
var httpClient = GetHttpClient(cacheKey, builder, request.UseCookieContainer, request.AllowAutoRedirect);
var httpClient = GetHttpClient(cacheKey, builder, request.UseCookieContainer, request.AllowAutoRedirect, request.IsTracing);
try
{ try
var response = await httpClient.SendAsync(request.HttpRequestMessage); {
return new OkResponse<HttpResponseMessage>(response); var response = await httpClient.SendAsync(request.HttpRequestMessage);
} return new OkResponse<HttpResponseMessage>(response);
catch (TimeoutRejectedException exception) }
{ catch (TimeoutRejectedException exception)
return {
new ErrorResponse<HttpResponseMessage>(new RequestTimedOutError(exception)); return
} new ErrorResponse<HttpResponseMessage>(new RequestTimedOutError(exception));
catch (BrokenCircuitException exception) }
{ catch (BrokenCircuitException exception)
return {
new ErrorResponse<HttpResponseMessage>(new RequestTimedOutError(exception)); return
} new ErrorResponse<HttpResponseMessage>(new RequestTimedOutError(exception));
catch (Exception exception) }
{ catch (Exception exception)
return new ErrorResponse<HttpResponseMessage>(new UnableToCompleteRequestError(exception)); {
} return new ErrorResponse<HttpResponseMessage>(new UnableToCompleteRequestError(exception));
finally }
{ finally
_cacheHandlers.Set(cacheKey, httpClient, TimeSpan.FromHours(24)); {
} _cacheHandlers.Set(cacheKey, httpClient, TimeSpan.FromHours(24));
}
}
}
private IHttpClient GetHttpClient(string cacheKey, IHttpClientBuilder builder, bool useCookieContainer, bool allowAutoRedirect)
{ private IHttpClient GetHttpClient(string cacheKey, IHttpClientBuilder builder, bool useCookieContainer, bool allowAutoRedirect,bool isTracing)
var httpClient = _cacheHandlers.Get(cacheKey); {
var httpClient = _cacheHandlers.Get(cacheKey);
if (httpClient == null)
{ if (httpClient == null)
httpClient = builder.Create(useCookieContainer, allowAutoRedirect); {
} httpClient = builder.Create(useCookieContainer, allowAutoRedirect, isTracing, _serviceProvider);
return httpClient; }
} return httpClient;
}
private string GetCacheKey(Request.Request request, IHttpClientBuilder builder)
{ private string GetCacheKey(Request.Request request, IHttpClientBuilder builder)
string baseUrl = $"{request.HttpRequestMessage.RequestUri.Scheme}://{request.HttpRequestMessage.RequestUri.Authority}"; {
string baseUrl = $"{request.HttpRequestMessage.RequestUri.Scheme}://{request.HttpRequestMessage.RequestUri.Authority}";
if (request.IsQos)
{ if (request.IsQos)
builder.WithQos(request.QosProvider, _logger); {
baseUrl = $"{baseUrl}{request.QosProvider.CircuitBreaker.CircuitBreakerPolicy.PolicyKey}"; builder.WithQos(request.QosProvider, _logger);
} baseUrl = $"{baseUrl}{request.QosProvider.CircuitBreaker.CircuitBreakerPolicy.PolicyKey}";
}
return baseUrl;
} return baseUrl;
} }
} }
}

View File

@ -22,6 +22,6 @@ namespace Ocelot.Requester
/// </summary> /// </summary>
/// <param name="useCookies">Defines if http client should use cookie container</param> /// <param name="useCookies">Defines if http client should use cookie container</param>
/// <param name="allowAutoRedirect">Defines if http client should allow auto redirect</param> /// <param name="allowAutoRedirect">Defines if http client should allow auto redirect</param>
IHttpClient Create(bool useCookies, bool allowAutoRedirect); IHttpClient Create(bool useCookies, bool allowAutoRedirect, bool isTracing, IServiceProvider provider);
} }
} }

View File

@ -7,7 +7,5 @@ namespace Ocelot.Requester
public interface IHttpRequester public interface IHttpRequester
{ {
Task<Response<HttpResponseMessage>> GetResponse(Request.Request request); Task<Response<HttpResponseMessage>> GetResponse(Request.Request request);
} }
} }

View File

@ -1,9 +1,8 @@
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using Ocelot.Infrastructure.RequestData; using Ocelot.Infrastructure.RequestData;
using Ocelot.Logging; using Ocelot.Logging;
using Ocelot.Middleware; using Ocelot.Middleware;
using System.Threading.Tasks;
namespace Ocelot.Requester.Middleware namespace Ocelot.Requester.Middleware
{ {
@ -16,7 +15,8 @@ namespace Ocelot.Requester.Middleware
public HttpRequesterMiddleware(RequestDelegate next, public HttpRequesterMiddleware(RequestDelegate next,
IOcelotLoggerFactory loggerFactory, IOcelotLoggerFactory loggerFactory,
IHttpRequester requester, IHttpRequester requester,
IRequestScopedDataRepository requestScopedDataRepository) IRequestScopedDataRepository requestScopedDataRepository
)
:base(requestScopedDataRepository) :base(requestScopedDataRepository)
{ {
_next = next; _next = next;
@ -25,7 +25,7 @@ namespace Ocelot.Requester.Middleware
} }
public async Task Invoke(HttpContext context) public async Task Invoke(HttpContext context)
{ {
var response = await _requester.GetResponse(Request); var response = await _requester.GetResponse(Request);
if (response.IsError) if (response.IsError)
@ -41,4 +41,4 @@ namespace Ocelot.Requester.Middleware
SetHttpResponseMessageThisRequest(response.Data); SetHttpResponseMessageThisRequest(response.Data);
} }
} }
} }

View File

@ -0,0 +1,62 @@
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using Butterfly.Client.Tracing;
using Butterfly.OpenTracing;
namespace Ocelot.Requester
{
public class OcelotHttpTracingHandler : DelegatingHandler
{
private readonly IServiceTracer _tracer;
private const string prefix_spanId = "ot-spanId";
public OcelotHttpTracingHandler(IServiceTracer tracer, HttpMessageHandler httpMessageHandler = null)
{
_tracer = tracer ?? throw new ArgumentNullException(nameof(tracer));
InnerHandler = httpMessageHandler ?? new HttpClientHandler();
}
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
return _tracer.ChildTraceAsync($"httpclient {request.Method}", DateTimeOffset.UtcNow, span => TracingSendAsync(span, request, cancellationToken));
}
protected virtual async Task<HttpResponseMessage> TracingSendAsync(ISpan span, HttpRequestMessage request, CancellationToken cancellationToken)
{
IEnumerable<string> traceIdVals = null;
if (request.Headers.TryGetValues(prefix_spanId, out traceIdVals))
{
request.Headers.Remove(prefix_spanId);
request.Headers.TryAddWithoutValidation(prefix_spanId, span.SpanContext.SpanId);
};
span.Tags.Client().Component("HttpClient")
.HttpMethod(request.Method.Method)
.HttpUrl(request.RequestUri.OriginalString)
.HttpHost(request.RequestUri.Host)
.HttpPath(request.RequestUri.PathAndQuery)
.PeerAddress(request.RequestUri.OriginalString)
.PeerHostName(request.RequestUri.Host)
.PeerPort(request.RequestUri.Port);
_tracer.Tracer.Inject(span.SpanContext, request.Headers, (c, k, v) =>
{
if (!c.Contains(k))
{
c.Add(k, v);
};
});
span.Log(LogField.CreateNew().ClientSend());
var responseMessage = await base.SendAsync(request, cancellationToken);
span.Log(LogField.CreateNew().ClientReceive());
return responseMessage;
}
}
}

View File

@ -1,10 +1,10 @@
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Ocelot.Errors; using Ocelot.Errors;
using Ocelot.Infrastructure.RequestData; using Ocelot.Infrastructure.RequestData;
using Ocelot.Logging; using Ocelot.Logging;
using Ocelot.Middleware; using Ocelot.Middleware;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace Ocelot.Responder.Middleware namespace Ocelot.Responder.Middleware
{ {
@ -22,7 +22,8 @@ namespace Ocelot.Responder.Middleware
IHttpResponder responder, IHttpResponder responder,
IOcelotLoggerFactory loggerFactory, IOcelotLoggerFactory loggerFactory,
IRequestScopedDataRepository requestScopedDataRepository, IRequestScopedDataRepository requestScopedDataRepository,
IErrorsToHttpStatusCodeMapper codeMapper) IErrorsToHttpStatusCodeMapper codeMapper
)
:base(requestScopedDataRepository) :base(requestScopedDataRepository)
{ {
_next = next; _next = next;
@ -33,14 +34,13 @@ namespace Ocelot.Responder.Middleware
} }
public async Task Invoke(HttpContext context) public async Task Invoke(HttpContext context)
{ {
await _next.Invoke(context); await _next.Invoke(context);
if (PipelineError) if (PipelineError)
{ {
var errors = PipelineErrors; var errors = PipelineErrors;
_logger.LogError($"{PipelineErrors.Count} pipeline errors found in {MiddlewareName}. Setting error response status code"); _logger.LogError($"{PipelineErrors.Count} pipeline errors found in {MiddlewareName}. Setting error response status code");
SetErrorResponse(context, errors); SetErrorResponse(context, errors);
} }
else else
@ -50,10 +50,10 @@ namespace Ocelot.Responder.Middleware
} }
} }
private void SetErrorResponse(HttpContext context, List<Error> errors) private void SetErrorResponse(HttpContext context, List<Error> errors)
{ {
var statusCode = _codeMapper.Map(errors); var statusCode = _codeMapper.Map(errors);
_responder.SetErrorResponseOnContext(context, statusCode); _responder.SetErrorResponseOnContext(context, statusCode);
} }
} }
} }

View File

@ -1,52 +1,53 @@
using System; using System;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Newtonsoft.Json; using Newtonsoft.Json;
using Ocelot.DependencyInjection; using Ocelot.DependencyInjection;
using Ocelot.Middleware; using Ocelot.Middleware;
using Ocelot.Raft; using Ocelot.Raft;
using Rafty.Concensus; using Rafty.Concensus;
using Rafty.FiniteStateMachine; using Rafty.FiniteStateMachine;
using Rafty.Infrastructure; using Rafty.Infrastructure;
using Rafty.Log; using Rafty.Log;
using ConfigurationBuilder = Microsoft.Extensions.Configuration.ConfigurationBuilder; using ConfigurationBuilder = Microsoft.Extensions.Configuration.ConfigurationBuilder;
namespace Ocelot.IntegrationTests namespace Ocelot.IntegrationTests
{ {
public class RaftStartup public class RaftStartup
{ {
public RaftStartup(IHostingEnvironment env) public RaftStartup(IHostingEnvironment env)
{ {
var builder = new ConfigurationBuilder() var builder = new ConfigurationBuilder()
.SetBasePath(env.ContentRootPath) .SetBasePath(env.ContentRootPath)
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true) .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true) .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
.AddJsonFile("peers.json", optional: true, reloadOnChange: true) .AddJsonFile("peers.json", optional: true, reloadOnChange: true)
.AddJsonFile("configuration.json") .AddJsonFile("configuration.json")
.AddEnvironmentVariables(); .AddEnvironmentVariables();
Configuration = builder.Build(); Configuration = builder.Build();
} }
public IConfiguration Configuration { get; } public IConfiguration Configuration { get; }
public virtual void ConfigureServices(IServiceCollection services) public virtual void ConfigureServices(IServiceCollection services)
{ {
services services
.AddOcelot(Configuration) .AddOcelot(Configuration)
.AddAdministration("/administration", "secret") .AddAdministration("/administration", "secret")
.AddRafty(); .AddRafty()
} ;
}
public virtual void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{ public virtual void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
app.UseOcelot().Wait(); {
} app.UseOcelot().Wait();
} }
} }
}

View File

@ -1,40 +1,45 @@
using System; using System;
using CacheManager.Core; using CacheManager.Core;
using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Ocelot.DependencyInjection; using Ocelot.DependencyInjection;
using Ocelot.Middleware; using Ocelot.Middleware;
using ConfigurationBuilder = Microsoft.Extensions.Configuration.ConfigurationBuilder; using ConfigurationBuilder = Microsoft.Extensions.Configuration.ConfigurationBuilder;
namespace Ocelot.ManualTest namespace Ocelot.ManualTest
{ {
public class ManualTestStartup public class ManualTestStartup
{ {
public void ConfigureServices(IServiceCollection services) public void ConfigureServices(IServiceCollection services)
{ {
Action<ConfigurationBuilderCachePart> settings = (x) => Action<ConfigurationBuilderCachePart> settings = (x) =>
{ {
x.WithDictionaryHandle(); x.WithDictionaryHandle();
}; };
services.AddAuthentication() services.AddAuthentication()
.AddJwtBearer("TestKey", x => .AddJwtBearer("TestKey", x =>
{ {
x.Authority = "test"; x.Authority = "test";
x.Audience = "test"; x.Audience = "test";
}); });
services.AddOcelot() services.AddOcelot()
.AddCacheManager(settings) .AddCacheManager(settings)
.AddAdministration("/administration", "secret"); .AddOpenTracing(option =>
} {
option.CollectorUrl = "http://localhost:9618";
public void Configure(IApplicationBuilder app) option.Service = "Ocelot.ManualTest";
{ })
app.UseOcelot().Wait(); .AddAdministration("/administration", "secret");
} }
}
} public void Configure(IApplicationBuilder app)
{
app.UseOcelot().Wait();
}
}
}

View File

@ -1,287 +1,304 @@
{ {
"ReRoutes": [ "ReRoutes": [
{ {
"DownstreamPathTemplate": "/", "DownstreamPathTemplate": "/api/values",
"DownstreamScheme": "http", "DownstreamScheme": "http",
"DownstreamHostAndPorts": [ "UpstreamPathTemplate": "/api/values",
{ "UpstreamHttpMethod": [ "Get" ],
"Host": "localhost", "DownstreamHostAndPorts": [
"Port": 52876 {
} "Host": "localhost",
], "Port": 5002
"UpstreamPathTemplate": "/identityserverexample", }
"UpstreamHttpMethod": [ "Get" ], ],
"QoSOptions": { "HttpHandlerOptions": {
"ExceptionsAllowedBeforeBreaking": 3, "AllowAutoRedirect": true,
"DurationOfBreak": 10, "UseCookieContainer": true,
"TimeoutValue": 5000 "UseTracing": true
}, }
"AuthenticationOptions": { },
"AuthenticationProviderKey": "TestKey", {
"AllowedScopes": [ "DownstreamPathTemplate": "/",
"openid", "DownstreamScheme": "http",
"offline_access" "DownstreamHostAndPorts": [
] {
}, "Host": "localhost",
"AddHeadersToRequest": { "Port": 52876
"CustomerId": "Claims[CustomerId] > value", }
"LocationId": "Claims[LocationId] > value", ],
"UserType": "Claims[sub] > value[0] > |", "UpstreamPathTemplate": "/identityserverexample",
"UserId": "Claims[sub] > value[1] > |" "UpstreamHttpMethod": [ "Get" ],
}, "QoSOptions": {
"AddClaimsToRequest": { "ExceptionsAllowedBeforeBreaking": 3,
"CustomerId": "Claims[CustomerId] > value", "DurationOfBreak": 10,
"LocationId": "Claims[LocationId] > value", "TimeoutValue": 5000
"UserType": "Claims[sub] > value[0] > |", },
"UserId": "Claims[sub] > value[1] > |" "AuthenticationOptions": {
}, "AuthenticationProviderKey": "TestKey",
"AddQueriesToRequest": { "AllowedScopes": [
"CustomerId": "Claims[CustomerId] > value", "openid",
"LocationId": "Claims[LocationId] > value", "offline_access"
"UserType": "Claims[sub] > value[0] > |", ]
"UserId": "Claims[sub] > value[1] > |" },
}, "AddHeadersToRequest": {
"RouteClaimsRequirement": { "CustomerId": "Claims[CustomerId] > value",
"UserType": "registered" "LocationId": "Claims[LocationId] > value",
}, "UserType": "Claims[sub] > value[0] > |",
"RequestIdKey": "OcRequestId" "UserId": "Claims[sub] > value[1] > |"
}, },
{ "AddClaimsToRequest": {
"DownstreamPathTemplate": "/posts", "CustomerId": "Claims[CustomerId] > value",
"DownstreamScheme": "https", "LocationId": "Claims[LocationId] > value",
"DownstreamHostAndPorts": [ "UserType": "Claims[sub] > value[0] > |",
{ "UserId": "Claims[sub] > value[1] > |"
"Host": "jsonplaceholder.typicode.com", },
"Port": 443 "AddQueriesToRequest": {
} "CustomerId": "Claims[CustomerId] > value",
], "LocationId": "Claims[LocationId] > value",
"UpstreamPathTemplate": "/posts", "UserType": "Claims[sub] > value[0] > |",
"UpstreamHttpMethod": [ "Get" ], "UserId": "Claims[sub] > value[1] > |"
"QoSOptions": { },
"ExceptionsAllowedBeforeBreaking": 3, "RouteClaimsRequirement": {
"DurationOfBreak": 10, "UserType": "registered"
"TimeoutValue": 5000 },
} "RequestIdKey": "OcRequestId"
}, },
{ {
"DownstreamPathTemplate": "/posts/{postId}", "DownstreamPathTemplate": "/posts",
"DownstreamScheme": "http", "DownstreamScheme": "https",
"DownstreamHostAndPorts": [ "DownstreamHostAndPorts": [
{ {
"Host": "jsonplaceholder.typicode.com", "Host": "jsonplaceholder.typicode.com",
"Port": 80 "Port": 443
} }
], ],
"UpstreamPathTemplate": "/posts/{postId}", "UpstreamPathTemplate": "/posts",
"UpstreamHttpMethod": [ "Get" ], "UpstreamHttpMethod": [ "Get" ],
"RequestIdKey": "ReRouteRequestId", "QoSOptions": {
"QoSOptions": { "ExceptionsAllowedBeforeBreaking": 3,
"ExceptionsAllowedBeforeBreaking": 3, "DurationOfBreak": 10,
"DurationOfBreak": 10, "TimeoutValue": 5000
"TimeoutValue": 5000 }
} },
}, {
{ "DownstreamPathTemplate": "/posts/{postId}",
"DownstreamPathTemplate": "/posts/{postId}/comments", "DownstreamScheme": "http",
"DownstreamScheme": "http", "DownstreamHostAndPorts": [
"DownstreamHostAndPorts": [ {
{ "Host": "jsonplaceholder.typicode.com",
"Host": "jsonplaceholder.typicode.com", "Port": 80
"Port": 80 }
} ],
], "UpstreamPathTemplate": "/posts/{postId}",
"UpstreamPathTemplate": "/posts/{postId}/comments", "UpstreamHttpMethod": [ "Get" ],
"UpstreamHttpMethod": [ "Get" ], "RequestIdKey": "ReRouteRequestId",
"QoSOptions": { "QoSOptions": {
"ExceptionsAllowedBeforeBreaking": 3, "ExceptionsAllowedBeforeBreaking": 3,
"DurationOfBreak": 10, "DurationOfBreak": 10,
"TimeoutValue": 5000 "TimeoutValue": 5000
} }
}, },
{ {
"DownstreamPathTemplate": "/comments", "DownstreamPathTemplate": "/posts/{postId}/comments",
"DownstreamScheme": "http", "DownstreamScheme": "http",
"DownstreamHostAndPorts": [ "DownstreamHostAndPorts": [
{ {
"Host": "jsonplaceholder.typicode.com", "Host": "jsonplaceholder.typicode.com",
"Port": 80 "Port": 80
} }
], ],
"UpstreamPathTemplate": "/comments", "UpstreamPathTemplate": "/posts/{postId}/comments",
"UpstreamHttpMethod": [ "Get" ], "UpstreamHttpMethod": [ "Get" ],
"QoSOptions": { "QoSOptions": {
"ExceptionsAllowedBeforeBreaking": 3, "ExceptionsAllowedBeforeBreaking": 3,
"DurationOfBreak": 10, "DurationOfBreak": 10,
"TimeoutValue": 5000 "TimeoutValue": 5000
} }
}, },
{ {
"DownstreamPathTemplate": "/posts", "DownstreamPathTemplate": "/comments",
"DownstreamScheme": "http", "DownstreamScheme": "http",
"DownstreamHostAndPorts": [ "DownstreamHostAndPorts": [
{ {
"Host": "jsonplaceholder.typicode.com", "Host": "jsonplaceholder.typicode.com",
"Port": 80 "Port": 80
} }
], ],
"UpstreamPathTemplate": "/posts", "UpstreamPathTemplate": "/comments",
"UpstreamHttpMethod": [ "Post" ], "UpstreamHttpMethod": [ "Get" ],
"QoSOptions": { "QoSOptions": {
"ExceptionsAllowedBeforeBreaking": 3, "ExceptionsAllowedBeforeBreaking": 3,
"DurationOfBreak": 10, "DurationOfBreak": 10,
"TimeoutValue": 5000 "TimeoutValue": 5000
} }
}, },
{ {
"DownstreamPathTemplate": "/posts/{postId}", "DownstreamPathTemplate": "/posts",
"DownstreamScheme": "http", "DownstreamScheme": "http",
"DownstreamHostAndPorts": [ "DownstreamHostAndPorts": [
{ {
"Host": "jsonplaceholder.typicode.com", "Host": "jsonplaceholder.typicode.com",
"Port": 80 "Port": 80
} }
], ],
"UpstreamPathTemplate": "/posts/{postId}", "UpstreamPathTemplate": "/posts",
"UpstreamHttpMethod": [ "Put" ], "UpstreamHttpMethod": [ "Post" ],
"QoSOptions": { "QoSOptions": {
"ExceptionsAllowedBeforeBreaking": 3, "ExceptionsAllowedBeforeBreaking": 3,
"DurationOfBreak": 10, "DurationOfBreak": 10,
"TimeoutValue": 5000 "TimeoutValue": 5000
} }
}, },
{ {
"DownstreamPathTemplate": "/posts/{postId}", "DownstreamPathTemplate": "/posts/{postId}",
"DownstreamScheme": "http", "DownstreamScheme": "http",
"DownstreamHostAndPorts": [ "DownstreamHostAndPorts": [
{ {
"Host": "jsonplaceholder.typicode.com", "Host": "jsonplaceholder.typicode.com",
"Port": 80 "Port": 80
} }
], ],
"UpstreamPathTemplate": "/posts/{postId}", "UpstreamPathTemplate": "/posts/{postId}",
"UpstreamHttpMethod": [ "Patch" ], "UpstreamHttpMethod": [ "Put" ],
"QoSOptions": { "QoSOptions": {
"ExceptionsAllowedBeforeBreaking": 3, "ExceptionsAllowedBeforeBreaking": 3,
"DurationOfBreak": 10, "DurationOfBreak": 10,
"TimeoutValue": 5000 "TimeoutValue": 5000
} }
}, },
{ {
"DownstreamPathTemplate": "/posts/{postId}", "DownstreamPathTemplate": "/posts/{postId}",
"DownstreamScheme": "http", "DownstreamScheme": "http",
"DownstreamHostAndPorts": [ "DownstreamHostAndPorts": [
{ {
"Host": "jsonplaceholder.typicode.com", "Host": "jsonplaceholder.typicode.com",
"Port": 80 "Port": 80
} }
], ],
"UpstreamPathTemplate": "/posts/{postId}", "UpstreamPathTemplate": "/posts/{postId}",
"UpstreamHttpMethod": [ "Delete" ], "UpstreamHttpMethod": [ "Patch" ],
"QoSOptions": { "QoSOptions": {
"ExceptionsAllowedBeforeBreaking": 3, "ExceptionsAllowedBeforeBreaking": 3,
"DurationOfBreak": 10, "DurationOfBreak": 10,
"TimeoutValue": 5000 "TimeoutValue": 5000
} }
}, },
{ {
"DownstreamPathTemplate": "/api/products", "DownstreamPathTemplate": "/posts/{postId}",
"DownstreamScheme": "http", "DownstreamScheme": "http",
"DownstreamHostAndPorts": [ "DownstreamHostAndPorts": [
{ {
"Host": "jsonplaceholder.typicode.com", "Host": "jsonplaceholder.typicode.com",
"Port": 80 "Port": 80
} }
], ],
"UpstreamPathTemplate": "/products", "UpstreamPathTemplate": "/posts/{postId}",
"UpstreamHttpMethod": [ "Get" ], "UpstreamHttpMethod": [ "Delete" ],
"QoSOptions": { "QoSOptions": {
"ExceptionsAllowedBeforeBreaking": 3, "ExceptionsAllowedBeforeBreaking": 3,
"DurationOfBreak": 10, "DurationOfBreak": 10,
"TimeoutValue": 5000 "TimeoutValue": 5000
}, }
"FileCacheOptions": { "TtlSeconds": 15 } },
}, {
{ "DownstreamPathTemplate": "/api/products",
"DownstreamPathTemplate": "/api/products/{productId}", "DownstreamScheme": "http",
"DownstreamScheme": "http", "DownstreamHostAndPorts": [
"DownstreamHostAndPorts": [ {
{ "Host": "jsonplaceholder.typicode.com",
"Host": "jsonplaceholder.typicode.com", "Port": 80
"Port": 80 }
} ],
], "UpstreamPathTemplate": "/products",
"UpstreamPathTemplate": "/products/{productId}", "UpstreamHttpMethod": [ "Get" ],
"UpstreamHttpMethod": [ "Get" ], "QoSOptions": {
"FileCacheOptions": { "TtlSeconds": 15 } "ExceptionsAllowedBeforeBreaking": 3,
}, "DurationOfBreak": 10,
{ "TimeoutValue": 5000
"DownstreamPathTemplate": "/api/products", },
"DownstreamScheme": "http", "FileCacheOptions": { "TtlSeconds": 15 }
"DownstreamHostAndPorts": [ },
{ {
"Host": "jsonplaceholder.typicode.com", "DownstreamPathTemplate": "/api/products/{productId}",
"Port": 80 "DownstreamScheme": "http",
} "DownstreamHostAndPorts": [
], {
"UpstreamPathTemplate": "/products", "Host": "jsonplaceholder.typicode.com",
"UpstreamHttpMethod": [ "Post" ], "Port": 80
"QoSOptions": { }
"ExceptionsAllowedBeforeBreaking": 3, ],
"DurationOfBreak": 10, "UpstreamPathTemplate": "/products/{productId}",
"TimeoutValue": 5000 "UpstreamHttpMethod": [ "Get" ],
} "FileCacheOptions": { "TtlSeconds": 15 }
}, },
{ {
"DownstreamPathTemplate": "/api/products/{productId}", "DownstreamPathTemplate": "/api/products",
"DownstreamScheme": "http", "DownstreamScheme": "http",
"DownstreamHostAndPorts": [ "DownstreamHostAndPorts": [
{ {
"Host": "jsonplaceholder.typicode.com", "Host": "jsonplaceholder.typicode.com",
"Port": 80 "Port": 80
} }
], ],
"UpstreamPathTemplate": "/products/{productId}", "UpstreamPathTemplate": "/products",
"UpstreamHttpMethod": [ "Put" ], "UpstreamHttpMethod": [ "Post" ],
"QoSOptions": { "QoSOptions": {
"ExceptionsAllowedBeforeBreaking": 3, "ExceptionsAllowedBeforeBreaking": 3,
"DurationOfBreak": 10, "DurationOfBreak": 10,
"TimeoutValue": 5000 "TimeoutValue": 5000
}, }
"FileCacheOptions": { "TtlSeconds": 15 } },
}, {
{ "DownstreamPathTemplate": "/api/products/{productId}",
"DownstreamPathTemplate": "/posts", "DownstreamScheme": "http",
"DownstreamScheme": "http", "DownstreamHostAndPorts": [
"DownstreamHostAndPorts": [ {
{ "Host": "jsonplaceholder.typicode.com",
"Host": "jsonplaceholder.typicode.com", "Port": 80
"Port": 80 }
} ],
], "UpstreamPathTemplate": "/products/{productId}",
"UpstreamPathTemplate": "/posts/", "UpstreamHttpMethod": [ "Put" ],
"UpstreamHttpMethod": [ "Get" ], "QoSOptions": {
"QoSOptions": { "ExceptionsAllowedBeforeBreaking": 3,
"ExceptionsAllowedBeforeBreaking": 3, "DurationOfBreak": 10,
"DurationOfBreak": 10, "TimeoutValue": 5000
"TimeoutValue": 5000 },
}, "FileCacheOptions": { "TtlSeconds": 15 }
"FileCacheOptions": { "TtlSeconds": 15 } },
}, {
{ "DownstreamPathTemplate": "/posts",
"DownstreamPathTemplate": "/", "DownstreamScheme": "http",
"DownstreamScheme": "http", "DownstreamHostAndPorts": [
"DownstreamHostAndPorts": [ {
{ "Host": "jsonplaceholder.typicode.com",
"Host": "www.bbc.co.uk", "Port": 80
"Port": 80 }
} ],
], "UpstreamPathTemplate": "/posts/",
"UpstreamPathTemplate": "/bbc/", "UpstreamHttpMethod": [ "Get" ],
"UpstreamHttpMethod": [ "Get" ] "QoSOptions": {
} "ExceptionsAllowedBeforeBreaking": 3,
], "DurationOfBreak": 10,
"TimeoutValue": 5000
"GlobalConfiguration": { },
"RequestIdKey": "OcRequestId" "FileCacheOptions": { "TtlSeconds": 15 }
} },
} {
"DownstreamPathTemplate": "/",
"DownstreamScheme": "http",
"DownstreamHostAndPorts": [
{
"Host": "www.bbc.co.uk",
"Port": 80
}
],
"UpstreamPathTemplate": "/bbc/",
"UpstreamHttpMethod": [ "Get" ]
}
],
"GlobalConfiguration": {
"RequestIdKey": "ot-traceid"
}
}

View File

@ -471,7 +471,7 @@ namespace Ocelot.UnitTests.Configuration
{ {
var reRouteOptions = new ReRouteOptionsBuilder() var reRouteOptions = new ReRouteOptionsBuilder()
.Build(); .Build();
var httpHandlerOptions = new HttpHandlerOptions(true, true); var httpHandlerOptions = new HttpHandlerOptions(true, true,false);
this.Given(x => x.GivenTheConfigIs(new FileConfiguration this.Given(x => x.GivenTheConfigIs(new FileConfiguration
{ {

View File

@ -23,7 +23,7 @@ namespace Ocelot.UnitTests.Configuration
public void should_create_options_with_useCookie_and_allowAutoRedirect_true_as_default() public void should_create_options_with_useCookie_and_allowAutoRedirect_true_as_default()
{ {
var fileReRoute = new FileReRoute(); var fileReRoute = new FileReRoute();
var expectedOptions = new HttpHandlerOptions(true, true); var expectedOptions = new HttpHandlerOptions(true, true, false);
this.Given(x => GivenTheFollowing(fileReRoute)) this.Given(x => GivenTheFollowing(fileReRoute))
.When(x => WhenICreateHttpHandlerOptions()) .When(x => WhenICreateHttpHandlerOptions())
@ -39,11 +39,12 @@ namespace Ocelot.UnitTests.Configuration
HttpHandlerOptions = new FileHttpHandlerOptions HttpHandlerOptions = new FileHttpHandlerOptions
{ {
AllowAutoRedirect = false, AllowAutoRedirect = false,
UseCookieContainer = false UseCookieContainer = false,
UseTracing = false
} }
}; };
var expectedOptions = new HttpHandlerOptions(false, false); var expectedOptions = new HttpHandlerOptions(false, false, false);
this.Given(x => GivenTheFollowing(fileReRoute)) this.Given(x => GivenTheFollowing(fileReRoute))
.When(x => WhenICreateHttpHandlerOptions()) .When(x => WhenICreateHttpHandlerOptions())
@ -66,6 +67,7 @@ namespace Ocelot.UnitTests.Configuration
_httpHandlerOptions.ShouldNotBeNull(); _httpHandlerOptions.ShouldNotBeNull();
_httpHandlerOptions.AllowAutoRedirect.ShouldBe(options.AllowAutoRedirect); _httpHandlerOptions.AllowAutoRedirect.ShouldBe(options.AllowAutoRedirect);
_httpHandlerOptions.UseCookieContainer.ShouldBe(options.UseCookieContainer); _httpHandlerOptions.UseCookieContainer.ShouldBe(options.UseCookieContainer);
_httpHandlerOptions.UseTracing.ShouldBe(options.UseTracing);
} }
} }
} }

View File

@ -1,9 +1,4 @@
using System; using CacheManager.Core;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Text;
using CacheManager.Core;
using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Hosting.Internal; using Microsoft.AspNetCore.Hosting.Internal;
using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration;
@ -13,8 +8,11 @@ using Ocelot.Configuration;
using Ocelot.Configuration.File; using Ocelot.Configuration.File;
using Ocelot.Configuration.Setter; using Ocelot.Configuration.Setter;
using Ocelot.DependencyInjection; using Ocelot.DependencyInjection;
using Ocelot.Logging; using Ocelot.Requester;
using Shouldly; using Shouldly;
using System;
using System.Collections.Generic;
using System.Linq;
using TestStack.BDDfy; using TestStack.BDDfy;
using Xunit; using Xunit;
@ -96,6 +94,16 @@ namespace Ocelot.UnitTests.DependencyInjection
.BDDfy(); .BDDfy();
} }
[Fact]
public void should_set_up_tracing()
{
this.Given(x => WhenISetUpOcelotServices())
.When(x => WhenISetUpOpentracing())
.When(x => WhenIAccessOcelotHttpTracingHandler())
.BDDfy();
}
[Fact] [Fact]
public void should_set_up_without_passing_in_config() public void should_set_up_without_passing_in_config()
{ {
@ -193,6 +201,24 @@ namespace Ocelot.UnitTests.DependencyInjection
} }
} }
private void WhenISetUpOpentracing()
{
try
{
_ocelotBuilder.AddOpenTracing(
option =>
{
option.CollectorUrl = "http://localhost:9618";
option.Service = "Ocelot.ManualTest";
}
);
}
catch (Exception e)
{
_ex = e;
}
}
private void WhenIAccessLoggerFactory() private void WhenIAccessLoggerFactory()
{ {
try try
@ -205,6 +231,18 @@ namespace Ocelot.UnitTests.DependencyInjection
} }
} }
private void WhenIAccessOcelotHttpTracingHandler()
{
try
{
var tracingHandler = _serviceProvider.GetService<OcelotHttpTracingHandler>();
}
catch (Exception e)
{
_ex = e;
}
}
private void WhenIValidateScopes() private void WhenIValidateScopes()
{ {
try try

View File

@ -17,6 +17,7 @@
using Ocelot.Requester.QoS; using Ocelot.Requester.QoS;
using Ocelot.Configuration; using Ocelot.Configuration;
using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Builder;
using Ocelot.Errors;
public class HttpRequestBuilderMiddlewareTests : ServerHostedMiddlewareTest public class HttpRequestBuilderMiddlewareTests : ServerHostedMiddlewareTest
{ {
@ -51,18 +52,39 @@
new ReRouteBuilder() new ReRouteBuilder()
.WithRequestIdKey("LSRequestId") .WithRequestIdKey("LSRequestId")
.WithUpstreamHttpMethod(new List<string> { "Get" }) .WithUpstreamHttpMethod(new List<string> { "Get" })
.WithHttpHandlerOptions(new HttpHandlerOptions(true, true)) .WithHttpHandlerOptions(new HttpHandlerOptions(true, true,false))
.Build()); .Build());
this.Given(x => x.GivenTheDownStreamUrlIs("any old string")) this.Given(x => x.GivenTheDownStreamUrlIs("any old string"))
.And(x => x.GivenTheQosProviderHouseReturns(new OkResponse<IQoSProvider>(new NoQoSProvider()))) .And(x => x.GivenTheQosProviderHouseReturns(new OkResponse<IQoSProvider>(new NoQoSProvider())))
.And(x => x.GivenTheDownStreamRouteIs(downstreamRoute)) .And(x => x.GivenTheDownStreamRouteIs(downstreamRoute))
.And(x => x.GivenTheRequestBuilderReturns(new Ocelot.Request.Request(new HttpRequestMessage(), true, new NoQoSProvider(), false, false))) .And(x => x.GivenTheRequestBuilderReturns(new Ocelot.Request.Request(new HttpRequestMessage(), true, new NoQoSProvider(), false, false,false)))
.When(x => x.WhenICallTheMiddleware()) .When(x => x.WhenICallTheMiddleware())
.Then(x => x.ThenTheScopedDataRepositoryIsCalledCorrectly()) .Then(x => x.ThenTheScopedDataRepositoryIsCalledCorrectly())
.BDDfy(); .BDDfy();
} }
[Fact]
public void should_call_scoped_data_repository_QosProviderError()
{
var downstreamRoute = new DownstreamRoute(new List<PlaceholderNameAndValue>(),
new ReRouteBuilder()
.WithRequestIdKey("LSRequestId")
.WithUpstreamHttpMethod(new List<string> { "Get" })
.WithHttpHandlerOptions(new HttpHandlerOptions(true, true, true))
.Build());
this.Given(x => x.GivenTheDownStreamUrlIs("any old string"))
.And(x => x.GivenTheQosProviderHouseReturns(new ErrorResponse<IQoSProvider>(It.IsAny<Error>())))
.And(x => x.GivenTheDownStreamRouteIs(downstreamRoute))
.And(x => x.GivenTheRequestBuilderReturns(new Ocelot.Request.Request(new HttpRequestMessage(), true, new NoQoSProvider(), false, false, false)))
.When(x => x.WhenICallTheMiddleware())
.Then(x => x.ThenTheScopedDataRepositoryQosProviderError())
.BDDfy();
}
protected override void GivenTheTestServerServicesAreConfigured(IServiceCollection services) protected override void GivenTheTestServerServicesAreConfigured(IServiceCollection services)
{ {
services.AddSingleton<IOcelotLoggerFactory, AspDotNetLoggerFactory>(); services.AddSingleton<IOcelotLoggerFactory, AspDotNetLoggerFactory>();
@ -109,7 +131,9 @@
It.IsAny<bool>(), It.IsAny<bool>(),
It.IsAny<IQoSProvider>(), It.IsAny<IQoSProvider>(),
It.IsAny<bool>(), It.IsAny<bool>(),
It.IsAny<bool>())) It.IsAny<bool>(),
It.IsAny<bool>()
))
.ReturnsAsync(_request); .ReturnsAsync(_request);
} }
@ -118,5 +142,11 @@
_scopedRepository _scopedRepository
.Verify(x => x.Add("Request", _request.Data), Times.Once()); .Verify(x => x.Add("Request", _request.Data), Times.Once());
} }
private void ThenTheScopedDataRepositoryQosProviderError()
{
_scopedRepository
.Verify(x => x.Add("OcelotMiddlewareError", true), Times.Once());
}
} }
} }

View File

@ -1,79 +1,86 @@
namespace Ocelot.UnitTests.Request namespace Ocelot.UnitTests.Request
{ {
using System.Net.Http; using System.Net.Http;
using Ocelot.Request.Builder; using Ocelot.Request.Builder;
using Ocelot.Requester.QoS; using Ocelot.Requester.QoS;
using Ocelot.Responses; using Ocelot.Responses;
using Shouldly; using Shouldly;
using TestStack.BDDfy; using TestStack.BDDfy;
using Xunit; using Xunit;
public class HttpRequestCreatorTests public class HttpRequestCreatorTests
{ {
private readonly IRequestCreator _requestCreator; private readonly IRequestCreator _requestCreator;
private readonly bool _isQos; private readonly bool _isQos;
private readonly IQoSProvider _qoSProvider; private readonly IQoSProvider _qoSProvider;
private readonly HttpRequestMessage _requestMessage; private readonly HttpRequestMessage _requestMessage;
private readonly bool _useCookieContainer; private readonly bool _useCookieContainer;
private readonly bool _allowAutoRedirect; private readonly bool _allowAutoRedirect;
private readonly bool _useTracing;
private Response<Ocelot.Request.Request> _response;
private Response<Ocelot.Request.Request> _response;
public HttpRequestCreatorTests()
{ public HttpRequestCreatorTests()
_requestCreator = new HttpRequestCreator(); {
_isQos = true; _requestCreator = new HttpRequestCreator();
_qoSProvider = new NoQoSProvider(); _isQos = true;
_useCookieContainer = false; _qoSProvider = new NoQoSProvider();
_allowAutoRedirect = false; _useCookieContainer = false;
_allowAutoRedirect = false;
_requestMessage = new HttpRequestMessage(); _useTracing = false;
} _requestMessage = new HttpRequestMessage();
}
[Fact]
public void ShouldBuildRequest() [Fact]
{ public void ShouldBuildRequest()
this.When(x => x.WhenIBuildARequest()) {
.Then(x => x.ThenTheRequestContainsTheRequestMessage()) this.When(x => x.WhenIBuildARequest())
.Then(x => x.ThenTheRequestContainsTheIsQos()) .Then(x => x.ThenTheRequestContainsTheRequestMessage())
.Then(x => x.ThenTheRequestContainsTheQosProvider()) .Then(x => x.ThenTheRequestContainsTheIsQos())
.Then(x => x.ThenTheRequestContainsUseCookieContainer()) .Then(x => x.ThenTheRequestContainsTheQosProvider())
.Then(x => x.ThenTheRequestContainsAllowAutoRedirect()) .Then(x => x.ThenTheRequestContainsUseCookieContainer())
.BDDfy(); .Then(x => x.ThenTheRequestContainsUseTracing())
} .Then(x => x.ThenTheRequestContainsAllowAutoRedirect())
.BDDfy();
private void WhenIBuildARequest() }
{
_response = _requestCreator.Build(_requestMessage, private void WhenIBuildARequest()
_isQos, _qoSProvider, _useCookieContainer, _allowAutoRedirect) {
.GetAwaiter() _response = _requestCreator.Build(_requestMessage,
.GetResult(); _isQos, _qoSProvider, _useCookieContainer, _allowAutoRedirect, _useTracing)
} .GetAwaiter()
.GetResult();
private void ThenTheRequestContainsTheRequestMessage() }
{
_response.Data.HttpRequestMessage.ShouldBe(_requestMessage); private void ThenTheRequestContainsTheRequestMessage()
} {
_response.Data.HttpRequestMessage.ShouldBe(_requestMessage);
private void ThenTheRequestContainsTheIsQos() }
{
_response.Data.IsQos.ShouldBe(_isQos); private void ThenTheRequestContainsTheIsQos()
} {
_response.Data.IsQos.ShouldBe(_isQos);
private void ThenTheRequestContainsTheQosProvider() }
{
_response.Data.QosProvider.ShouldBe(_qoSProvider); private void ThenTheRequestContainsTheQosProvider()
} {
_response.Data.QosProvider.ShouldBe(_qoSProvider);
}
private void ThenTheRequestContainsUseCookieContainer() private void ThenTheRequestContainsUseCookieContainer()
{ {
_response.Data.UseCookieContainer.ShouldBe(_useCookieContainer); _response.Data.UseCookieContainer.ShouldBe(_useCookieContainer);
} }
private void ThenTheRequestContainsUseTracing()
{
_response.Data.IsTracing.ShouldBe(_useTracing);
}
private void ThenTheRequestContainsAllowAutoRedirect() private void ThenTheRequestContainsAllowAutoRedirect()
{ {
_response.Data.AllowAutoRedirect.ShouldBe(_allowAutoRedirect); _response.Data.AllowAutoRedirect.ShouldBe(_allowAutoRedirect);
} }
} }
} }

View File

@ -0,0 +1,76 @@
using Microsoft.Extensions.DependencyInjection;
using Moq;
using Ocelot.Logging;
using Ocelot.Requester;
using Ocelot.Requester.QoS;
using Ocelot.Responses;
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Text;
using TestStack.BDDfy;
using Xunit;
namespace Ocelot.UnitTests.Requester
{
public class HttpClientHttpRequesterTest
{
private readonly Mock<IHttpClientCache> _cacheHandlers;
private Mock<IServiceProvider> _serviceProvider;
private Response<HttpResponseMessage> _response;
private readonly HttpClientHttpRequester _httpClientRequester;
private Ocelot.Request.Request _request;
private Mock<IOcelotLoggerFactory> _loggerFactory;
private Mock<IOcelotLogger> _logger;
public HttpClientHttpRequesterTest()
{
_serviceProvider = new Mock<IServiceProvider>();
_logger = new Mock<IOcelotLogger>();
_loggerFactory = new Mock<IOcelotLoggerFactory>();
_loggerFactory
.Setup(x => x.CreateLogger<HttpClientHttpRequester>())
.Returns(_logger.Object);
_cacheHandlers = new Mock<IHttpClientCache>();
_httpClientRequester = new HttpClientHttpRequester(_loggerFactory.Object, _cacheHandlers.Object, _serviceProvider.Object);
}
[Fact]
public void should_call_request_correctly()
{
this.Given(x=>x.GivenTheRequestIs(new Ocelot.Request.Request(new HttpRequestMessage() { RequestUri = new Uri("http://www.bbc.co.uk") }, false, new NoQoSProvider(), false, false, false)))
.When(x=>x.WhenIGetResponse())
.Then(x => x.ThenTheResponseIsCalledCorrectly())
.BDDfy();
}
[Fact]
public void should_call_request_UnableToCompleteRequest()
{
this.Given(x => x.GivenTheRequestIs(new Ocelot.Request.Request(new HttpRequestMessage() { RequestUri = new Uri("http://localhost:60080") }, false, new NoQoSProvider(), false, false, false)))
.When(x => x.WhenIGetResponse())
.Then(x => x.ThenTheResponseIsCalledError())
.BDDfy();
}
private void GivenTheRequestIs(Ocelot.Request.Request request)
{
_request = request;
}
private void WhenIGetResponse()
{
_response = _httpClientRequester.GetResponse(_request).Result;
}
private void ThenTheResponseIsCalledCorrectly()
{
Assert.True(_response.IsError == false);
}
private void ThenTheResponseIsCalledError()
{
Assert.True(_response.IsError == true);
}
}
}

View File

@ -1,81 +1,81 @@
namespace Ocelot.UnitTests.Requester namespace Ocelot.UnitTests.Requester
{ {
using System.Net.Http; using System.Net.Http;
using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Moq; using Moq;
using Ocelot.Logging; using Ocelot.Logging;
using Ocelot.Requester; using Ocelot.Requester;
using Ocelot.Requester.Middleware; using Ocelot.Requester.Middleware;
using Ocelot.Requester.QoS; using Ocelot.Requester.QoS;
using Ocelot.Responses; using Ocelot.Responses;
using TestStack.BDDfy; using TestStack.BDDfy;
using Xunit; using Xunit;
public class HttpRequesterMiddlewareTests : ServerHostedMiddlewareTest public class HttpRequesterMiddlewareTests : ServerHostedMiddlewareTest
{ {
private readonly Mock<IHttpRequester> _requester; private readonly Mock<IHttpRequester> _requester;
private OkResponse<HttpResponseMessage> _response; private OkResponse<HttpResponseMessage> _response;
private OkResponse<Ocelot.Request.Request> _request; private OkResponse<Ocelot.Request.Request> _request;
public HttpRequesterMiddlewareTests() public HttpRequesterMiddlewareTests()
{ {
_requester = new Mock<IHttpRequester>(); _requester = new Mock<IHttpRequester>();
GivenTheTestServerIsConfigured(); GivenTheTestServerIsConfigured();
} }
[Fact] [Fact]
public void should_call_scoped_data_repository_correctly() public void should_call_scoped_data_repository_correctly()
{ {
this.Given(x => x.GivenTheRequestIs(new Ocelot.Request.Request(new HttpRequestMessage(),true, new NoQoSProvider(), false, false))) this.Given(x => x.GivenTheRequestIs(new Ocelot.Request.Request(new HttpRequestMessage(),true, new NoQoSProvider(), false, false,false)))
.And(x => x.GivenTheRequesterReturns(new HttpResponseMessage())) .And(x => x.GivenTheRequesterReturns(new HttpResponseMessage()))
.And(x => x.GivenTheScopedRepoReturns()) .And(x => x.GivenTheScopedRepoReturns())
.When(x => x.WhenICallTheMiddleware()) .When(x => x.WhenICallTheMiddleware())
.Then(x => x.ThenTheScopedRepoIsCalledCorrectly()) .Then(x => x.ThenTheScopedRepoIsCalledCorrectly())
.BDDfy(); .BDDfy();
} }
protected override void GivenTheTestServerServicesAreConfigured(IServiceCollection services) protected override void GivenTheTestServerServicesAreConfigured(IServiceCollection services)
{ {
services.AddSingleton<IOcelotLoggerFactory, AspDotNetLoggerFactory>(); services.AddSingleton<IOcelotLoggerFactory, AspDotNetLoggerFactory>();
services.AddLogging(); services.AddLogging();
services.AddSingleton(_requester.Object); services.AddSingleton(_requester.Object);
services.AddSingleton(ScopedRepository.Object); services.AddSingleton(ScopedRepository.Object);
} }
protected override void GivenTheTestServerPipelineIsConfigured(IApplicationBuilder app) protected override void GivenTheTestServerPipelineIsConfigured(IApplicationBuilder app)
{ {
app.UseHttpRequesterMiddleware(); app.UseHttpRequesterMiddleware();
} }
private void GivenTheRequestIs(Ocelot.Request.Request request) private void GivenTheRequestIs(Ocelot.Request.Request request)
{ {
_request = new OkResponse<Ocelot.Request.Request>(request); _request = new OkResponse<Ocelot.Request.Request>(request);
ScopedRepository ScopedRepository
.Setup(x => x.Get<Ocelot.Request.Request>(It.IsAny<string>())) .Setup(x => x.Get<Ocelot.Request.Request>(It.IsAny<string>()))
.Returns(_request); .Returns(_request);
} }
private void GivenTheRequesterReturns(HttpResponseMessage response) private void GivenTheRequesterReturns(HttpResponseMessage response)
{ {
_response = new OkResponse<HttpResponseMessage>(response); _response = new OkResponse<HttpResponseMessage>(response);
_requester _requester
.Setup(x => x.GetResponse(It.IsAny<Ocelot.Request.Request>())) .Setup(x => x.GetResponse(It.IsAny<Ocelot.Request.Request>()))
.ReturnsAsync(_response); .ReturnsAsync(_response);
} }
private void GivenTheScopedRepoReturns() private void GivenTheScopedRepoReturns()
{ {
ScopedRepository ScopedRepository
.Setup(x => x.Add(It.IsAny<string>(), _response.Data)) .Setup(x => x.Add(It.IsAny<string>(), _response.Data))
.Returns(new OkResponse()); .Returns(new OkResponse());
} }
private void ThenTheScopedRepoIsCalledCorrectly() private void ThenTheScopedRepoIsCalledCorrectly()
{ {
ScopedRepository ScopedRepository
.Verify(x => x.Add("HttpResponseMessage", _response.Data), Times.Once()); .Verify(x => x.Add("HttpResponseMessage", _response.Data), Times.Once());
} }
} }
} }

View File

@ -1,10 +1,14 @@
namespace Ocelot.UnitTests.Responder namespace Ocelot.UnitTests.Responder
{ {
using System.Collections.Generic;
using System.Net.Http; using System.Net.Http;
using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Moq; using Moq;
using Ocelot.DownstreamRouteFinder.Finder;
using Ocelot.Errors;
using Ocelot.Logging; using Ocelot.Logging;
using Ocelot.Requester;
using Ocelot.Responder; using Ocelot.Responder;
using Ocelot.Responder.Middleware; using Ocelot.Responder.Middleware;
using Ocelot.Responses; using Ocelot.Responses;
@ -35,6 +39,17 @@
.BDDfy(); .BDDfy();
} }
[Fact]
public void should_return_any_errors()
{
this.Given(x => x.GivenTheHttpResponseMessageIs(new HttpResponseMessage()))
.And(x => x.GivenThereArePipelineErrors(new UnableToFindDownstreamRouteError()))
.When(x => x.WhenICallTheMiddleware())
.Then(x => x.ThenThereAreNoErrors())
.BDDfy();
}
protected override void GivenTheTestServerServicesAreConfigured(IServiceCollection services) protected override void GivenTheTestServerServicesAreConfigured(IServiceCollection services)
{ {
services.AddSingleton<IOcelotLoggerFactory, AspDotNetLoggerFactory>(); services.AddSingleton<IOcelotLoggerFactory, AspDotNetLoggerFactory>();
@ -68,5 +83,14 @@
{ {
//todo a better assert? //todo a better assert?
} }
private void GivenThereArePipelineErrors(Error error)
{
ScopedRepository
.Setup(x => x.Get<bool>("OcelotMiddlewareError"))
.Returns(new OkResponse<bool>(true));
ScopedRepository.Setup(x => x.Get<List<Error>>("OcelotMiddlewareErrors"))
.Returns(new OkResponse<List<Error>>(new List<Error>() { error }));
}
} }
} }