mirror of
https://github.com/nsnail/Ocelot.git
synced 2025-04-22 10:12:51 +08:00
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:
parent
f179b7d0d0
commit
ef3c4f614a
@ -1,7 +1,7 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio 15
|
||||
VisualStudioVersion = 15.0.26730.15
|
||||
VisualStudioVersion = 15.0.27130.2024
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{5CFB79B7-C9DC-45A4-9A75-625D92471702}"
|
||||
EndProject
|
||||
|
@ -7,7 +7,7 @@ namespace Ocelot.Configuration.Creator
|
||||
public HttpHandlerOptions Create(FileReRoute fileReRoute)
|
||||
{
|
||||
return new HttpHandlerOptions(fileReRoute.HttpHandlerOptions.AllowAutoRedirect,
|
||||
fileReRoute.HttpHandlerOptions.UseCookieContainer);
|
||||
fileReRoute.HttpHandlerOptions.UseCookieContainer, fileReRoute.HttpHandlerOptions.UseTracing);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -11,5 +11,7 @@
|
||||
public bool AllowAutoRedirect { get; set; }
|
||||
|
||||
public bool UseCookieContainer { get; set; }
|
||||
|
||||
public bool UseTracing { get; set; }
|
||||
}
|
||||
}
|
||||
|
@ -6,10 +6,11 @@
|
||||
/// </summary>
|
||||
public class HttpHandlerOptions
|
||||
{
|
||||
public HttpHandlerOptions(bool allowAutoRedirect, bool useCookieContainer)
|
||||
public HttpHandlerOptions(bool allowAutoRedirect, bool useCookieContainer, bool useTracing)
|
||||
{
|
||||
AllowAutoRedirect = allowAutoRedirect;
|
||||
UseCookieContainer = useCookieContainer;
|
||||
UseTracing = useTracing;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -21,5 +22,10 @@
|
||||
/// Specify is handler has to use a cookie container
|
||||
/// </summary>
|
||||
public bool UseCookieContainer { get; private set; }
|
||||
|
||||
// <summary>
|
||||
/// Specify is handler has to use a opentracing
|
||||
/// </summary>
|
||||
public bool UseTracing { get; private set; }
|
||||
}
|
||||
}
|
||||
|
@ -1,12 +1,14 @@
|
||||
using CacheManager.Core;
|
||||
using System;
|
||||
|
||||
namespace Ocelot.DependencyInjection
|
||||
{
|
||||
public interface IOcelotBuilder
|
||||
{
|
||||
IOcelotBuilder AddStoreOcelotConfigurationInConsul();
|
||||
IOcelotBuilder AddCacheManager(Action<ConfigurationBuilderCachePart> settings);
|
||||
IOcelotAdministrationBuilder AddAdministration(string path, string secret);
|
||||
}
|
||||
}
|
||||
using Butterfly.Client.AspNetCore;
|
||||
using CacheManager.Core;
|
||||
using System;
|
||||
|
||||
namespace Ocelot.DependencyInjection
|
||||
{
|
||||
public interface IOcelotBuilder
|
||||
{
|
||||
IOcelotBuilder AddStoreOcelotConfigurationInConsul();
|
||||
IOcelotBuilder AddCacheManager(Action<ConfigurationBuilderCachePart> settings);
|
||||
IOcelotBuilder AddOpenTracing(Action<ButterflyOptions> settings);
|
||||
IOcelotAdministrationBuilder AddAdministration(string path, string secret);
|
||||
}
|
||||
}
|
||||
|
@ -1,307 +1,313 @@
|
||||
using CacheManager.Core;
|
||||
using IdentityServer4.Models;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Ocelot.Authorisation;
|
||||
using Ocelot.Cache;
|
||||
using Ocelot.Claims;
|
||||
using Ocelot.Configuration.Authentication;
|
||||
using Ocelot.Configuration.Creator;
|
||||
using Ocelot.Configuration.File;
|
||||
using Ocelot.Configuration.Parser;
|
||||
using Ocelot.Configuration.Provider;
|
||||
using Ocelot.Configuration.Repository;
|
||||
using Ocelot.Configuration.Setter;
|
||||
using Ocelot.Configuration.Validator;
|
||||
using Ocelot.DownstreamRouteFinder.Finder;
|
||||
using Ocelot.DownstreamRouteFinder.UrlMatcher;
|
||||
using Ocelot.DownstreamUrlCreator;
|
||||
using Ocelot.DownstreamUrlCreator.UrlTemplateReplacer;
|
||||
using Ocelot.Headers;
|
||||
using Ocelot.Infrastructure.Claims.Parser;
|
||||
using Ocelot.Infrastructure.RequestData;
|
||||
using Ocelot.LoadBalancer.LoadBalancers;
|
||||
using Ocelot.Logging;
|
||||
using Ocelot.Middleware;
|
||||
using Ocelot.QueryStrings;
|
||||
using Ocelot.RateLimit;
|
||||
using Ocelot.Request.Builder;
|
||||
using Ocelot.Request.Mapper;
|
||||
using Ocelot.Requester;
|
||||
using Ocelot.Requester.QoS;
|
||||
using Ocelot.Responder;
|
||||
using Ocelot.ServiceDiscovery;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IdentityModel.Tokens.Jwt;
|
||||
using System.Net.Http;
|
||||
using System.Reflection;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
using IdentityServer4.AccessTokenValidation;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Ocelot.Configuration;
|
||||
using Ocelot.Configuration.Builder;
|
||||
using FileConfigurationProvider = Ocelot.Configuration.Provider.FileConfigurationProvider;
|
||||
using Microsoft.Extensions.DependencyInjection.Extensions;
|
||||
using System.Linq;
|
||||
using Ocelot.Raft;
|
||||
using Rafty.Concensus;
|
||||
using Rafty.FiniteStateMachine;
|
||||
using Rafty.Infrastructure;
|
||||
using Rafty.Log;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Ocelot.DependencyInjection
|
||||
{
|
||||
public class OcelotBuilder : IOcelotBuilder
|
||||
{
|
||||
private IServiceCollection _services;
|
||||
private IConfiguration _configurationRoot;
|
||||
|
||||
public OcelotBuilder(IServiceCollection services, IConfiguration configurationRoot)
|
||||
{
|
||||
_configurationRoot = configurationRoot;
|
||||
_services = services;
|
||||
|
||||
//add default cache settings...
|
||||
Action<ConfigurationBuilderCachePart> defaultCachingSettings = x =>
|
||||
{
|
||||
x.WithDictionaryHandle();
|
||||
};
|
||||
|
||||
AddCacheManager(defaultCachingSettings);
|
||||
|
||||
//add ocelot services...
|
||||
_services.Configure<FileConfiguration>(configurationRoot);
|
||||
_services.TryAddSingleton<IHttpResponseHeaderReplacer, HttpResponseHeaderReplacer>();
|
||||
_services.TryAddSingleton<IHttpContextRequestHeaderReplacer, HttpContextRequestHeaderReplacer>();
|
||||
_services.TryAddSingleton<IHeaderFindAndReplaceCreator, HeaderFindAndReplaceCreator>();
|
||||
_services.TryAddSingleton<IOcelotConfigurationCreator, FileOcelotConfigurationCreator>();
|
||||
_services.TryAddSingleton<IOcelotConfigurationRepository, InMemoryOcelotConfigurationRepository>();
|
||||
_services.TryAddSingleton<IConfigurationValidator, FileConfigurationFluentValidator>();
|
||||
_services.TryAddSingleton<IClaimsToThingCreator, ClaimsToThingCreator>();
|
||||
_services.TryAddSingleton<IAuthenticationOptionsCreator, AuthenticationOptionsCreator>();
|
||||
_services.TryAddSingleton<IUpstreamTemplatePatternCreator, UpstreamTemplatePatternCreator>();
|
||||
_services.TryAddSingleton<IRequestIdKeyCreator, RequestIdKeyCreator>();
|
||||
_services.TryAddSingleton<IServiceProviderConfigurationCreator,ServiceProviderConfigurationCreator>();
|
||||
_services.TryAddSingleton<IQoSOptionsCreator, QoSOptionsCreator>();
|
||||
_services.TryAddSingleton<IReRouteOptionsCreator, ReRouteOptionsCreator>();
|
||||
_services.TryAddSingleton<IRateLimitOptionsCreator, RateLimitOptionsCreator>();
|
||||
_services.TryAddSingleton<IBaseUrlFinder, BaseUrlFinder>();
|
||||
_services.TryAddSingleton<IRegionCreator, RegionCreator>();
|
||||
_services.TryAddSingleton<IFileConfigurationRepository, FileConfigurationRepository>();
|
||||
_services.TryAddSingleton<IFileConfigurationSetter, FileConfigurationSetter>();
|
||||
_services.TryAddSingleton<IFileConfigurationProvider, FileConfigurationProvider>();
|
||||
_services.TryAddSingleton<IQosProviderHouse, QosProviderHouse>();
|
||||
_services.TryAddSingleton<IQoSProviderFactory, QoSProviderFactory>();
|
||||
_services.TryAddSingleton<IServiceDiscoveryProviderFactory, ServiceDiscoveryProviderFactory>();
|
||||
_services.TryAddSingleton<ILoadBalancerFactory, LoadBalancerFactory>();
|
||||
_services.TryAddSingleton<ILoadBalancerHouse, LoadBalancerHouse>();
|
||||
_services.TryAddSingleton<IOcelotLoggerFactory, AspDotNetLoggerFactory>();
|
||||
_services.TryAddSingleton<IUrlBuilder, UrlBuilder>();
|
||||
_services.TryAddSingleton<IRemoveOutputHeaders, RemoveOutputHeaders>();
|
||||
_services.TryAddSingleton<IOcelotConfigurationProvider, OcelotConfigurationProvider>();
|
||||
_services.TryAddSingleton<IClaimToThingConfigurationParser, ClaimToThingConfigurationParser>();
|
||||
_services.TryAddSingleton<IClaimsAuthoriser, ClaimsAuthoriser>();
|
||||
_services.TryAddSingleton<IScopesAuthoriser, ScopesAuthoriser>();
|
||||
_services.TryAddSingleton<IAddClaimsToRequest, AddClaimsToRequest>();
|
||||
_services.TryAddSingleton<IAddHeadersToRequest, AddHeadersToRequest>();
|
||||
_services.TryAddSingleton<IAddQueriesToRequest, AddQueriesToRequest>();
|
||||
_services.TryAddSingleton<IClaimsParser, ClaimsParser>();
|
||||
_services.TryAddSingleton<IUrlPathToUrlTemplateMatcher, RegExUrlMatcher>();
|
||||
_services.TryAddSingleton<IPlaceholderNameAndValueFinder, UrlPathPlaceholderNameAndValueFinder>();
|
||||
_services.TryAddSingleton<IDownstreamPathPlaceholderReplacer, DownstreamTemplatePathPlaceholderReplacer>();
|
||||
_services.TryAddSingleton<IDownstreamRouteFinder, DownstreamRouteFinder.Finder.DownstreamRouteFinder>();
|
||||
_services.TryAddSingleton<IHttpRequester, HttpClientHttpRequester>();
|
||||
_services.TryAddSingleton<IHttpResponder, HttpContextResponder>();
|
||||
_services.TryAddSingleton<IRequestCreator, HttpRequestCreator>();
|
||||
_services.TryAddSingleton<IErrorsToHttpStatusCodeMapper, ErrorsToHttpStatusCodeMapper>();
|
||||
_services.TryAddSingleton<IRateLimitCounterHandler, MemoryCacheRateLimitCounterHandler>();
|
||||
_services.TryAddSingleton<IHttpClientCache, MemoryHttpClientCache>();
|
||||
_services.TryAddSingleton<IRequestMapper, RequestMapper>();
|
||||
_services.TryAddSingleton<IHttpHandlerOptionsCreator, HttpHandlerOptionsCreator>();
|
||||
_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
|
||||
// could maybe use a scoped data repository
|
||||
_services.TryAddSingleton<IHttpContextAccessor, HttpContextAccessor>();
|
||||
_services.TryAddSingleton<IRequestScopedDataRepository, HttpDataRepository>();
|
||||
_services.AddMemoryCache();
|
||||
_services.TryAddSingleton<OcelotDiagnosticListener>();
|
||||
|
||||
//add asp.net services..
|
||||
var assembly = typeof(FileConfigurationController).GetTypeInfo().Assembly;
|
||||
|
||||
_services.AddMvcCore()
|
||||
.AddApplicationPart(assembly)
|
||||
.AddControllersAsServices()
|
||||
.AddAuthorization()
|
||||
.AddJsonFormatters();
|
||||
|
||||
_services.AddLogging();
|
||||
_services.AddMiddlewareAnalysis();
|
||||
_services.AddWebEncoders();
|
||||
_services.AddSingleton<IAdministrationPath>(new NullAdministrationPath());
|
||||
}
|
||||
|
||||
public IOcelotAdministrationBuilder AddAdministration(string path, string secret)
|
||||
{
|
||||
var administrationPath = new AdministrationPath(path);
|
||||
|
||||
//add identity server for admin area
|
||||
var identityServerConfiguration = IdentityServerConfigurationCreator.GetIdentityServerConfiguration(secret);
|
||||
|
||||
if (identityServerConfiguration != null)
|
||||
{
|
||||
AddIdentityServer(identityServerConfiguration, administrationPath);
|
||||
}
|
||||
|
||||
var descriptor = new ServiceDescriptor(typeof(IAdministrationPath), administrationPath);
|
||||
_services.Replace(descriptor);
|
||||
return new OcelotAdministrationBuilder(_services, _configurationRoot);
|
||||
}
|
||||
|
||||
public IOcelotBuilder AddStoreOcelotConfigurationInConsul()
|
||||
{
|
||||
var serviceDiscoveryPort = _configurationRoot.GetValue("GlobalConfiguration:ServiceDiscoveryProvider:Port", 0);
|
||||
var serviceDiscoveryHost = _configurationRoot.GetValue("GlobalConfiguration:ServiceDiscoveryProvider:Host", string.Empty);
|
||||
|
||||
var config = new ServiceProviderConfigurationBuilder()
|
||||
.WithServiceDiscoveryProviderPort(serviceDiscoveryPort)
|
||||
.WithServiceDiscoveryProviderHost(serviceDiscoveryHost)
|
||||
.Build();
|
||||
|
||||
_services.AddSingleton<ServiceProviderConfiguration>(config);
|
||||
_services.AddSingleton<ConsulFileConfigurationPoller>();
|
||||
_services.AddSingleton<IFileConfigurationRepository, ConsulFileConfigurationRepository>();
|
||||
return this;
|
||||
}
|
||||
|
||||
public IOcelotBuilder AddCacheManager(Action<ConfigurationBuilderCachePart> settings)
|
||||
{
|
||||
var cacheManagerOutputCache = CacheFactory.Build<CachedResponse>("OcelotOutputCache", settings);
|
||||
var ocelotOutputCacheManager = new OcelotCacheManagerCache<CachedResponse>(cacheManagerOutputCache);
|
||||
|
||||
_services.RemoveAll(typeof(ICacheManager<CachedResponse>));
|
||||
_services.RemoveAll(typeof(IOcelotCache<CachedResponse>));
|
||||
_services.AddSingleton<ICacheManager<CachedResponse>>(cacheManagerOutputCache);
|
||||
_services.AddSingleton<IOcelotCache<CachedResponse>>(ocelotOutputCacheManager);
|
||||
|
||||
var ocelotConfigCacheManagerOutputCache = CacheFactory.Build<IOcelotConfiguration>("OcelotConfigurationCache", settings);
|
||||
var ocelotConfigCacheManager = new OcelotCacheManagerCache<IOcelotConfiguration>(ocelotConfigCacheManagerOutputCache);
|
||||
_services.RemoveAll(typeof(ICacheManager<IOcelotConfiguration>));
|
||||
_services.RemoveAll(typeof(IOcelotCache<IOcelotConfiguration>));
|
||||
_services.AddSingleton<ICacheManager<IOcelotConfiguration>>(ocelotConfigCacheManagerOutputCache);
|
||||
_services.AddSingleton<IOcelotCache<IOcelotConfiguration>>(ocelotConfigCacheManager);
|
||||
|
||||
var fileConfigCacheManagerOutputCache = CacheFactory.Build<FileConfiguration>("FileConfigurationCache", settings);
|
||||
var fileConfigCacheManager = new OcelotCacheManagerCache<FileConfiguration>(fileConfigCacheManagerOutputCache);
|
||||
_services.RemoveAll(typeof(ICacheManager<FileConfiguration>));
|
||||
_services.RemoveAll(typeof(IOcelotCache<FileConfiguration>));
|
||||
_services.AddSingleton<ICacheManager<FileConfiguration>>(fileConfigCacheManagerOutputCache);
|
||||
_services.AddSingleton<IOcelotCache<FileConfiguration>>(fileConfigCacheManager);
|
||||
return this;
|
||||
}
|
||||
|
||||
private void AddIdentityServer(IIdentityServerConfiguration identityServerConfiguration, IAdministrationPath adminPath)
|
||||
{
|
||||
_services.TryAddSingleton<IIdentityServerConfiguration>(identityServerConfiguration);
|
||||
_services.TryAddSingleton<IHashMatcher, HashMatcher>();
|
||||
var identityServerBuilder = _services
|
||||
.AddIdentityServer(o => {
|
||||
o.IssuerUri = "Ocelot";
|
||||
})
|
||||
.AddInMemoryApiResources(Resources(identityServerConfiguration))
|
||||
.AddInMemoryClients(Client(identityServerConfiguration));
|
||||
|
||||
//todo - refactor a method so we know why this is happening
|
||||
var whb = _services.First(x => x.ServiceType == typeof(IWebHostBuilder));
|
||||
var urlFinder = new BaseUrlFinder((IWebHostBuilder)whb.ImplementationInstance);
|
||||
var baseSchemeUrlAndPort = urlFinder.Find();
|
||||
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
|
||||
|
||||
_services.AddAuthentication(IdentityServerAuthenticationDefaults.AuthenticationScheme)
|
||||
.AddIdentityServerAuthentication(o =>
|
||||
{
|
||||
o.Authority = baseSchemeUrlAndPort + adminPath.Path;
|
||||
o.ApiName = identityServerConfiguration.ApiName;
|
||||
o.RequireHttpsMetadata = identityServerConfiguration.RequireHttps;
|
||||
o.SupportedTokens = SupportedTokens.Both;
|
||||
o.ApiSecret = identityServerConfiguration.ApiSecret;
|
||||
});
|
||||
|
||||
//todo - refactor naming..
|
||||
if (string.IsNullOrEmpty(identityServerConfiguration.CredentialsSigningCertificateLocation) || string.IsNullOrEmpty(identityServerConfiguration.CredentialsSigningCertificatePassword))
|
||||
{
|
||||
identityServerBuilder.AddDeveloperSigningCredential();
|
||||
}
|
||||
else
|
||||
{
|
||||
//todo - refactor so calls method?
|
||||
var cert = new X509Certificate2(identityServerConfiguration.CredentialsSigningCertificateLocation, identityServerConfiguration.CredentialsSigningCertificatePassword);
|
||||
identityServerBuilder.AddSigningCredential(cert);
|
||||
}
|
||||
}
|
||||
|
||||
private List<ApiResource> Resources(IIdentityServerConfiguration identityServerConfiguration)
|
||||
{
|
||||
return new List<ApiResource>
|
||||
{
|
||||
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,
|
||||
AllowedGrantTypes = GrantTypes.ClientCredentials,
|
||||
ClientSecrets = new List<Secret> {new Secret(identityServerConfiguration.ApiSecret.Sha256())},
|
||||
AllowedScopes = { identityServerConfiguration.ApiName }
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public interface IOcelotAdministrationBuilder
|
||||
{
|
||||
IOcelotAdministrationBuilder AddRafty();
|
||||
}
|
||||
|
||||
public class OcelotAdministrationBuilder : IOcelotAdministrationBuilder
|
||||
{
|
||||
private IServiceCollection _services;
|
||||
private IConfiguration _configurationRoot;
|
||||
|
||||
public OcelotAdministrationBuilder(IServiceCollection services, IConfiguration configurationRoot)
|
||||
{
|
||||
_configurationRoot = configurationRoot;
|
||||
_services = services;
|
||||
}
|
||||
|
||||
public IOcelotAdministrationBuilder AddRafty()
|
||||
{
|
||||
var settings = new InMemorySettings(4000, 5000, 100, 5000);
|
||||
_services.AddSingleton<ILog, SqlLiteLog>();
|
||||
_services.AddSingleton<IFiniteStateMachine, OcelotFiniteStateMachine>();
|
||||
_services.AddSingleton<ISettings>(settings);
|
||||
_services.AddSingleton<IPeersProvider, FilePeersProvider>();
|
||||
_services.AddSingleton<INode, Node>();
|
||||
_services.Configure<FilePeers>(_configurationRoot);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
}
|
||||
using Butterfly.Client.AspNetCore;
|
||||
using CacheManager.Core;
|
||||
using IdentityServer4.AccessTokenValidation;
|
||||
using IdentityServer4.Models;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.DependencyInjection.Extensions;
|
||||
using Ocelot.Authorisation;
|
||||
using Ocelot.Cache;
|
||||
using Ocelot.Claims;
|
||||
using Ocelot.Configuration;
|
||||
using Ocelot.Configuration.Authentication;
|
||||
using Ocelot.Configuration.Builder;
|
||||
using Ocelot.Configuration.Creator;
|
||||
using Ocelot.Configuration.File;
|
||||
using Ocelot.Configuration.Parser;
|
||||
using Ocelot.Configuration.Provider;
|
||||
using Ocelot.Configuration.Repository;
|
||||
using Ocelot.Configuration.Setter;
|
||||
using Ocelot.Configuration.Validator;
|
||||
using Ocelot.DownstreamRouteFinder.Finder;
|
||||
using Ocelot.DownstreamRouteFinder.UrlMatcher;
|
||||
using Ocelot.DownstreamUrlCreator;
|
||||
using Ocelot.DownstreamUrlCreator.UrlTemplateReplacer;
|
||||
using Ocelot.Headers;
|
||||
using Ocelot.Infrastructure.Claims.Parser;
|
||||
using Ocelot.Infrastructure.RequestData;
|
||||
using Ocelot.LoadBalancer.LoadBalancers;
|
||||
using Ocelot.Logging;
|
||||
using Ocelot.Middleware;
|
||||
using Ocelot.QueryStrings;
|
||||
using Ocelot.Raft;
|
||||
using Ocelot.RateLimit;
|
||||
using Ocelot.Request.Builder;
|
||||
using Ocelot.Request.Mapper;
|
||||
using Ocelot.Requester;
|
||||
using Ocelot.Requester.QoS;
|
||||
using Ocelot.Responder;
|
||||
using Ocelot.ServiceDiscovery;
|
||||
using Rafty.Concensus;
|
||||
using Rafty.FiniteStateMachine;
|
||||
using Rafty.Infrastructure;
|
||||
using Rafty.Log;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IdentityModel.Tokens.Jwt;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
using FileConfigurationProvider = Ocelot.Configuration.Provider.FileConfigurationProvider;
|
||||
|
||||
namespace Ocelot.DependencyInjection
|
||||
{
|
||||
public class OcelotBuilder : IOcelotBuilder
|
||||
{
|
||||
private IServiceCollection _services;
|
||||
private IConfiguration _configurationRoot;
|
||||
|
||||
public OcelotBuilder(IServiceCollection services, IConfiguration configurationRoot)
|
||||
{
|
||||
_configurationRoot = configurationRoot;
|
||||
_services = services;
|
||||
|
||||
//add default cache settings...
|
||||
Action<ConfigurationBuilderCachePart> defaultCachingSettings = x =>
|
||||
{
|
||||
x.WithDictionaryHandle();
|
||||
};
|
||||
|
||||
AddCacheManager(defaultCachingSettings);
|
||||
|
||||
//add ocelot services...
|
||||
_services.Configure<FileConfiguration>(configurationRoot);
|
||||
_services.TryAddSingleton<IHttpResponseHeaderReplacer, HttpResponseHeaderReplacer>();
|
||||
_services.TryAddSingleton<IHttpContextRequestHeaderReplacer, HttpContextRequestHeaderReplacer>();
|
||||
_services.TryAddSingleton<IHeaderFindAndReplaceCreator, HeaderFindAndReplaceCreator>();
|
||||
_services.TryAddSingleton<IOcelotConfigurationCreator, FileOcelotConfigurationCreator>();
|
||||
_services.TryAddSingleton<IOcelotConfigurationRepository, InMemoryOcelotConfigurationRepository>();
|
||||
_services.TryAddSingleton<IConfigurationValidator, FileConfigurationFluentValidator>();
|
||||
_services.TryAddSingleton<IClaimsToThingCreator, ClaimsToThingCreator>();
|
||||
_services.TryAddSingleton<IAuthenticationOptionsCreator, AuthenticationOptionsCreator>();
|
||||
_services.TryAddSingleton<IUpstreamTemplatePatternCreator, UpstreamTemplatePatternCreator>();
|
||||
_services.TryAddSingleton<IRequestIdKeyCreator, RequestIdKeyCreator>();
|
||||
_services.TryAddSingleton<IServiceProviderConfigurationCreator,ServiceProviderConfigurationCreator>();
|
||||
_services.TryAddSingleton<IQoSOptionsCreator, QoSOptionsCreator>();
|
||||
_services.TryAddSingleton<IReRouteOptionsCreator, ReRouteOptionsCreator>();
|
||||
_services.TryAddSingleton<IRateLimitOptionsCreator, RateLimitOptionsCreator>();
|
||||
_services.TryAddSingleton<IBaseUrlFinder, BaseUrlFinder>();
|
||||
_services.TryAddSingleton<IRegionCreator, RegionCreator>();
|
||||
_services.TryAddSingleton<IFileConfigurationRepository, FileConfigurationRepository>();
|
||||
_services.TryAddSingleton<IFileConfigurationSetter, FileConfigurationSetter>();
|
||||
_services.TryAddSingleton<IFileConfigurationProvider, FileConfigurationProvider>();
|
||||
_services.TryAddSingleton<IQosProviderHouse, QosProviderHouse>();
|
||||
_services.TryAddSingleton<IQoSProviderFactory, QoSProviderFactory>();
|
||||
_services.TryAddSingleton<IServiceDiscoveryProviderFactory, ServiceDiscoveryProviderFactory>();
|
||||
_services.TryAddSingleton<ILoadBalancerFactory, LoadBalancerFactory>();
|
||||
_services.TryAddSingleton<ILoadBalancerHouse, LoadBalancerHouse>();
|
||||
_services.TryAddSingleton<IOcelotLoggerFactory, AspDotNetLoggerFactory>();
|
||||
_services.TryAddSingleton<IUrlBuilder, UrlBuilder>();
|
||||
_services.TryAddSingleton<IRemoveOutputHeaders, RemoveOutputHeaders>();
|
||||
_services.TryAddSingleton<IOcelotConfigurationProvider, OcelotConfigurationProvider>();
|
||||
_services.TryAddSingleton<IClaimToThingConfigurationParser, ClaimToThingConfigurationParser>();
|
||||
_services.TryAddSingleton<IClaimsAuthoriser, ClaimsAuthoriser>();
|
||||
_services.TryAddSingleton<IScopesAuthoriser, ScopesAuthoriser>();
|
||||
_services.TryAddSingleton<IAddClaimsToRequest, AddClaimsToRequest>();
|
||||
_services.TryAddSingleton<IAddHeadersToRequest, AddHeadersToRequest>();
|
||||
_services.TryAddSingleton<IAddQueriesToRequest, AddQueriesToRequest>();
|
||||
_services.TryAddSingleton<IClaimsParser, ClaimsParser>();
|
||||
_services.TryAddSingleton<IUrlPathToUrlTemplateMatcher, RegExUrlMatcher>();
|
||||
_services.TryAddSingleton<IPlaceholderNameAndValueFinder, UrlPathPlaceholderNameAndValueFinder>();
|
||||
_services.TryAddSingleton<IDownstreamPathPlaceholderReplacer, DownstreamTemplatePathPlaceholderReplacer>();
|
||||
_services.TryAddSingleton<IDownstreamRouteFinder, DownstreamRouteFinder.Finder.DownstreamRouteFinder>();
|
||||
_services.TryAddSingleton<IHttpRequester, HttpClientHttpRequester>();
|
||||
_services.TryAddSingleton<IHttpResponder, HttpContextResponder>();
|
||||
_services.TryAddSingleton<IRequestCreator, HttpRequestCreator>();
|
||||
_services.TryAddSingleton<IErrorsToHttpStatusCodeMapper, ErrorsToHttpStatusCodeMapper>();
|
||||
_services.TryAddSingleton<IRateLimitCounterHandler, MemoryCacheRateLimitCounterHandler>();
|
||||
_services.TryAddSingleton<IHttpClientCache, MemoryHttpClientCache>();
|
||||
_services.TryAddSingleton<IRequestMapper, RequestMapper>();
|
||||
_services.TryAddSingleton<IHttpHandlerOptionsCreator, HttpHandlerOptionsCreator>();
|
||||
_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
|
||||
// could maybe use a scoped data repository
|
||||
_services.TryAddSingleton<IHttpContextAccessor, HttpContextAccessor>();
|
||||
_services.TryAddSingleton<IRequestScopedDataRepository, HttpDataRepository>();
|
||||
_services.AddMemoryCache();
|
||||
_services.TryAddSingleton<OcelotDiagnosticListener>();
|
||||
|
||||
//add asp.net services..
|
||||
var assembly = typeof(FileConfigurationController).GetTypeInfo().Assembly;
|
||||
|
||||
_services.AddMvcCore()
|
||||
.AddApplicationPart(assembly)
|
||||
.AddControllersAsServices()
|
||||
.AddAuthorization()
|
||||
.AddJsonFormatters();
|
||||
|
||||
_services.AddLogging();
|
||||
_services.AddMiddlewareAnalysis();
|
||||
_services.AddWebEncoders();
|
||||
_services.AddSingleton<IAdministrationPath>(new NullAdministrationPath());
|
||||
}
|
||||
|
||||
public IOcelotAdministrationBuilder AddAdministration(string path, string secret)
|
||||
{
|
||||
var administrationPath = new AdministrationPath(path);
|
||||
|
||||
//add identity server for admin area
|
||||
var identityServerConfiguration = IdentityServerConfigurationCreator.GetIdentityServerConfiguration(secret);
|
||||
|
||||
if (identityServerConfiguration != null)
|
||||
{
|
||||
AddIdentityServer(identityServerConfiguration, administrationPath);
|
||||
}
|
||||
|
||||
var descriptor = new ServiceDescriptor(typeof(IAdministrationPath), administrationPath);
|
||||
_services.Replace(descriptor);
|
||||
return new OcelotAdministrationBuilder(_services, _configurationRoot);
|
||||
}
|
||||
|
||||
public IOcelotBuilder AddStoreOcelotConfigurationInConsul()
|
||||
{
|
||||
var serviceDiscoveryPort = _configurationRoot.GetValue("GlobalConfiguration:ServiceDiscoveryProvider:Port", 0);
|
||||
var serviceDiscoveryHost = _configurationRoot.GetValue("GlobalConfiguration:ServiceDiscoveryProvider:Host", string.Empty);
|
||||
|
||||
var config = new ServiceProviderConfigurationBuilder()
|
||||
.WithServiceDiscoveryProviderPort(serviceDiscoveryPort)
|
||||
.WithServiceDiscoveryProviderHost(serviceDiscoveryHost)
|
||||
.Build();
|
||||
|
||||
_services.AddSingleton<ServiceProviderConfiguration>(config);
|
||||
_services.AddSingleton<ConsulFileConfigurationPoller>();
|
||||
_services.AddSingleton<IFileConfigurationRepository, ConsulFileConfigurationRepository>();
|
||||
return this;
|
||||
}
|
||||
|
||||
public IOcelotBuilder AddCacheManager(Action<ConfigurationBuilderCachePart> settings)
|
||||
{
|
||||
var cacheManagerOutputCache = CacheFactory.Build<CachedResponse>("OcelotOutputCache", settings);
|
||||
var ocelotOutputCacheManager = new OcelotCacheManagerCache<CachedResponse>(cacheManagerOutputCache);
|
||||
|
||||
_services.RemoveAll(typeof(ICacheManager<CachedResponse>));
|
||||
_services.RemoveAll(typeof(IOcelotCache<CachedResponse>));
|
||||
_services.AddSingleton<ICacheManager<CachedResponse>>(cacheManagerOutputCache);
|
||||
_services.AddSingleton<IOcelotCache<CachedResponse>>(ocelotOutputCacheManager);
|
||||
|
||||
var ocelotConfigCacheManagerOutputCache = CacheFactory.Build<IOcelotConfiguration>("OcelotConfigurationCache", settings);
|
||||
var ocelotConfigCacheManager = new OcelotCacheManagerCache<IOcelotConfiguration>(ocelotConfigCacheManagerOutputCache);
|
||||
_services.RemoveAll(typeof(ICacheManager<IOcelotConfiguration>));
|
||||
_services.RemoveAll(typeof(IOcelotCache<IOcelotConfiguration>));
|
||||
_services.AddSingleton<ICacheManager<IOcelotConfiguration>>(ocelotConfigCacheManagerOutputCache);
|
||||
_services.AddSingleton<IOcelotCache<IOcelotConfiguration>>(ocelotConfigCacheManager);
|
||||
|
||||
var fileConfigCacheManagerOutputCache = CacheFactory.Build<FileConfiguration>("FileConfigurationCache", settings);
|
||||
var fileConfigCacheManager = new OcelotCacheManagerCache<FileConfiguration>(fileConfigCacheManagerOutputCache);
|
||||
_services.RemoveAll(typeof(ICacheManager<FileConfiguration>));
|
||||
_services.RemoveAll(typeof(IOcelotCache<FileConfiguration>));
|
||||
_services.AddSingleton<ICacheManager<FileConfiguration>>(fileConfigCacheManagerOutputCache);
|
||||
_services.AddSingleton<IOcelotCache<FileConfiguration>>(fileConfigCacheManager);
|
||||
return this;
|
||||
}
|
||||
|
||||
public IOcelotBuilder AddOpenTracing(Action<ButterflyOptions> settings)
|
||||
{
|
||||
_services.AddTransient<OcelotHttpTracingHandler>();
|
||||
_services.AddButterfly(settings);
|
||||
return this;
|
||||
}
|
||||
|
||||
private void AddIdentityServer(IIdentityServerConfiguration identityServerConfiguration, IAdministrationPath adminPath)
|
||||
{
|
||||
_services.TryAddSingleton<IIdentityServerConfiguration>(identityServerConfiguration);
|
||||
_services.TryAddSingleton<IHashMatcher, HashMatcher>();
|
||||
var identityServerBuilder = _services
|
||||
.AddIdentityServer(o => {
|
||||
o.IssuerUri = "Ocelot";
|
||||
})
|
||||
.AddInMemoryApiResources(Resources(identityServerConfiguration))
|
||||
.AddInMemoryClients(Client(identityServerConfiguration));
|
||||
|
||||
//todo - refactor a method so we know why this is happening
|
||||
var whb = _services.First(x => x.ServiceType == typeof(IWebHostBuilder));
|
||||
var urlFinder = new BaseUrlFinder((IWebHostBuilder)whb.ImplementationInstance);
|
||||
var baseSchemeUrlAndPort = urlFinder.Find();
|
||||
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
|
||||
|
||||
_services.AddAuthentication(IdentityServerAuthenticationDefaults.AuthenticationScheme)
|
||||
.AddIdentityServerAuthentication(o =>
|
||||
{
|
||||
o.Authority = baseSchemeUrlAndPort + adminPath.Path;
|
||||
o.ApiName = identityServerConfiguration.ApiName;
|
||||
o.RequireHttpsMetadata = identityServerConfiguration.RequireHttps;
|
||||
o.SupportedTokens = SupportedTokens.Both;
|
||||
o.ApiSecret = identityServerConfiguration.ApiSecret;
|
||||
});
|
||||
|
||||
//todo - refactor naming..
|
||||
if (string.IsNullOrEmpty(identityServerConfiguration.CredentialsSigningCertificateLocation) || string.IsNullOrEmpty(identityServerConfiguration.CredentialsSigningCertificatePassword))
|
||||
{
|
||||
identityServerBuilder.AddDeveloperSigningCredential();
|
||||
}
|
||||
else
|
||||
{
|
||||
//todo - refactor so calls method?
|
||||
var cert = new X509Certificate2(identityServerConfiguration.CredentialsSigningCertificateLocation, identityServerConfiguration.CredentialsSigningCertificatePassword);
|
||||
identityServerBuilder.AddSigningCredential(cert);
|
||||
}
|
||||
}
|
||||
|
||||
private List<ApiResource> Resources(IIdentityServerConfiguration identityServerConfiguration)
|
||||
{
|
||||
return new List<ApiResource>
|
||||
{
|
||||
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,
|
||||
AllowedGrantTypes = GrantTypes.ClientCredentials,
|
||||
ClientSecrets = new List<Secret> {new Secret(identityServerConfiguration.ApiSecret.Sha256())},
|
||||
AllowedScopes = { identityServerConfiguration.ApiName }
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public interface IOcelotAdministrationBuilder
|
||||
{
|
||||
IOcelotAdministrationBuilder AddRafty();
|
||||
}
|
||||
|
||||
public class OcelotAdministrationBuilder : IOcelotAdministrationBuilder
|
||||
{
|
||||
private IServiceCollection _services;
|
||||
private IConfiguration _configurationRoot;
|
||||
|
||||
public OcelotAdministrationBuilder(IServiceCollection services, IConfiguration configurationRoot)
|
||||
{
|
||||
_configurationRoot = configurationRoot;
|
||||
_services = services;
|
||||
}
|
||||
|
||||
public IOcelotAdministrationBuilder AddRafty()
|
||||
{
|
||||
var settings = new InMemorySettings(4000, 5000, 100, 5000);
|
||||
_services.AddSingleton<ILog, SqlLiteLog>();
|
||||
_services.AddSingleton<IFiniteStateMachine, OcelotFiniteStateMachine>();
|
||||
_services.AddSingleton<ISettings>(settings);
|
||||
_services.AddSingleton<IPeersProvider, FilePeersProvider>();
|
||||
_services.AddSingleton<INode, Node>();
|
||||
_services.Configure<FilePeers>(_configurationRoot);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,8 @@
|
||||
using System;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.Extensions.DiagnosticAdapter;
|
||||
using Butterfly.Client.AspNetCore;
|
||||
using Butterfly.OpenTracing;
|
||||
|
||||
namespace Ocelot.Logging
|
||||
{
|
||||
@ -17,6 +19,7 @@ namespace Ocelot.Logging
|
||||
public virtual void OnMiddlewareStarting(HttpContext httpContext, string name)
|
||||
{
|
||||
_logger.LogTrace($"MiddlewareStarting: {name}; {httpContext.Request.Path}");
|
||||
Event(httpContext, $"MiddlewareStarting: {name}; {httpContext.Request.Path}");
|
||||
}
|
||||
|
||||
[DiagnosticName("Microsoft.AspNetCore.MiddlewareAnalysis.MiddlewareException")]
|
||||
@ -29,6 +32,13 @@ namespace Ocelot.Logging
|
||||
public virtual void OnMiddlewareFinished(HttpContext httpContext, string name)
|
||||
{
|
||||
_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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,46 +1,47 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netcoreapp2.0</TargetFramework>
|
||||
<RuntimeFrameworkVersion>2.0.0</RuntimeFrameworkVersion>
|
||||
<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>
|
||||
<AssemblyTitle>Ocelot</AssemblyTitle>
|
||||
<VersionPrefix>0.0.0-dev</VersionPrefix>
|
||||
<AssemblyName>Ocelot</AssemblyName>
|
||||
<PackageId>Ocelot</PackageId>
|
||||
<PackageTags>API Gateway;.NET core</PackageTags>
|
||||
<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>
|
||||
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute>
|
||||
<GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute>
|
||||
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
|
||||
<GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute>
|
||||
<Authors>Tom Pallister</Authors>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
||||
<DebugType>full</DebugType>
|
||||
<DebugSymbols>True</DebugSymbols>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="FluentValidation" Version="7.2.1"/>
|
||||
<PackageReference Include="IdentityServer4.AccessTokenValidation" Version="2.1.0"/>
|
||||
<PackageReference Include="Microsoft.AspNetCore.All" Version="2.0.0"/>
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="2.0.0"/>
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.FileExtensions" Version="2.0.0"/>
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="2.0.0"/>
|
||||
<PackageReference Include="Microsoft.Extensions.DiagnosticAdapter" Version="2.0.0"/>
|
||||
<PackageReference Include="Microsoft.Extensions.Logging" Version="2.0.0"/>
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="2.0.0"/>
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="2.0.0"/>
|
||||
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="2.0.0"/>
|
||||
<PackageReference Include="System.Text.RegularExpressions" Version="4.3.0"/>
|
||||
<PackageReference Include="CacheManager.Core" Version="1.1.1"/>
|
||||
<PackageReference Include="CacheManager.Microsoft.Extensions.Configuration" Version="1.1.1"/>
|
||||
<PackageReference Include="CacheManager.Microsoft.Extensions.Logging" Version="1.1.1"/>
|
||||
<PackageReference Include="Consul" Version="0.7.2.3"/>
|
||||
<PackageReference Include="Polly" Version="5.3.1"/>
|
||||
<PackageReference Include="IdentityServer4" Version="2.0.2"/>
|
||||
<PackageReference Include="Rafty" Version="0.4.2"/>
|
||||
</ItemGroup>
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netcoreapp2.0</TargetFramework>
|
||||
<RuntimeFrameworkVersion>2.0.0</RuntimeFrameworkVersion>
|
||||
<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>
|
||||
<AssemblyTitle>Ocelot</AssemblyTitle>
|
||||
<VersionPrefix>0.0.0-dev</VersionPrefix>
|
||||
<AssemblyName>Ocelot</AssemblyName>
|
||||
<PackageId>Ocelot</PackageId>
|
||||
<PackageTags>API Gateway;.NET core</PackageTags>
|
||||
<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>
|
||||
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute>
|
||||
<GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute>
|
||||
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
|
||||
<GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute>
|
||||
<Authors>Tom Pallister</Authors>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
||||
<DebugType>full</DebugType>
|
||||
<DebugSymbols>True</DebugSymbols>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Butterfly.Client.AspNetCore" Version="0.0.5" />
|
||||
<PackageReference Include="FluentValidation" Version="7.2.1" />
|
||||
<PackageReference Include="IdentityServer4.AccessTokenValidation" Version="2.1.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.All" Version="2.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="2.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.FileExtensions" Version="2.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="2.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.DiagnosticAdapter" Version="2.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging" Version="2.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="2.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="2.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="2.0.0" />
|
||||
<PackageReference Include="System.Text.RegularExpressions" Version="4.3.0" />
|
||||
<PackageReference Include="CacheManager.Core" Version="1.1.1" />
|
||||
<PackageReference Include="CacheManager.Microsoft.Extensions.Configuration" Version="1.1.1" />
|
||||
<PackageReference Include="CacheManager.Microsoft.Extensions.Logging" Version="1.1.1" />
|
||||
<PackageReference Include="Consul" Version="0.7.2.3" />
|
||||
<PackageReference Include="Polly" Version="5.3.1" />
|
||||
<PackageReference Include="IdentityServer4" Version="2.0.2" />
|
||||
<PackageReference Include="Rafty" Version="0.4.2" />
|
||||
</ItemGroup>
|
||||
</Project>
|
@ -1,20 +1,21 @@
|
||||
using System.Threading.Tasks;
|
||||
using Ocelot.Responses;
|
||||
using Ocelot.Requester.QoS;
|
||||
using System.Net.Http;
|
||||
|
||||
namespace Ocelot.Request.Builder
|
||||
{
|
||||
public sealed class HttpRequestCreator : IRequestCreator
|
||||
{
|
||||
public async Task<Response<Request>> Build(
|
||||
HttpRequestMessage httpRequestMessage,
|
||||
bool isQos,
|
||||
IQoSProvider qosProvider,
|
||||
bool useCookieContainer,
|
||||
bool allowAutoRedirect)
|
||||
{
|
||||
return new OkResponse<Request>(new Request(httpRequestMessage, isQos, qosProvider, allowAutoRedirect, useCookieContainer));
|
||||
}
|
||||
}
|
||||
}
|
||||
using System.Threading.Tasks;
|
||||
using Ocelot.Responses;
|
||||
using Ocelot.Requester.QoS;
|
||||
using System.Net.Http;
|
||||
|
||||
namespace Ocelot.Request.Builder
|
||||
{
|
||||
public sealed class HttpRequestCreator : IRequestCreator
|
||||
{
|
||||
public async Task<Response<Request>> Build(
|
||||
HttpRequestMessage httpRequestMessage,
|
||||
bool isQos,
|
||||
IQoSProvider qosProvider,
|
||||
bool useCookieContainer,
|
||||
bool allowAutoRedirect,
|
||||
bool isTracing)
|
||||
{
|
||||
return new OkResponse<Request>(new Request(httpRequestMessage, isQos, qosProvider, allowAutoRedirect, useCookieContainer, isTracing));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,18 +1,19 @@
|
||||
namespace Ocelot.Request.Builder
|
||||
{
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using Ocelot.Requester.QoS;
|
||||
using Ocelot.Responses;
|
||||
|
||||
public interface IRequestCreator
|
||||
{
|
||||
Task<Response<Request>> Build(
|
||||
HttpRequestMessage httpRequestMessage,
|
||||
bool isQos,
|
||||
IQoSProvider qosProvider,
|
||||
bool useCookieContainer,
|
||||
bool allowAutoRedirect);
|
||||
}
|
||||
}
|
||||
namespace Ocelot.Request.Builder
|
||||
{
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using Ocelot.Requester.QoS;
|
||||
using Ocelot.Responses;
|
||||
|
||||
public interface IRequestCreator
|
||||
{
|
||||
Task<Response<Request>> Build(
|
||||
HttpRequestMessage httpRequestMessage,
|
||||
bool isQos,
|
||||
IQoSProvider qosProvider,
|
||||
bool useCookieContainer,
|
||||
bool allowAutoRedirect,
|
||||
bool isTracing);
|
||||
}
|
||||
}
|
||||
|
@ -1,66 +1,65 @@
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Ocelot.Infrastructure.RequestData;
|
||||
using Ocelot.Logging;
|
||||
using Ocelot.Middleware;
|
||||
using Ocelot.Request.Builder;
|
||||
using Ocelot.Requester.QoS;
|
||||
|
||||
namespace Ocelot.Request.Middleware
|
||||
{
|
||||
public class HttpRequestBuilderMiddleware : OcelotMiddleware
|
||||
{
|
||||
private readonly RequestDelegate _next;
|
||||
private readonly IRequestCreator _requestCreator;
|
||||
private readonly IOcelotLogger _logger;
|
||||
private readonly IQosProviderHouse _qosProviderHouse;
|
||||
|
||||
public HttpRequestBuilderMiddleware(RequestDelegate next,
|
||||
IOcelotLoggerFactory loggerFactory,
|
||||
IRequestScopedDataRepository requestScopedDataRepository,
|
||||
IRequestCreator requestCreator,
|
||||
IQosProviderHouse qosProviderHouse)
|
||||
:base(requestScopedDataRepository)
|
||||
{
|
||||
_next = next;
|
||||
_requestCreator = requestCreator;
|
||||
_qosProviderHouse = qosProviderHouse;
|
||||
_logger = loggerFactory.CreateLogger<HttpRequestBuilderMiddleware>();
|
||||
}
|
||||
|
||||
public async Task Invoke(HttpContext context)
|
||||
{
|
||||
var qosProvider = _qosProviderHouse.Get(DownstreamRoute.ReRoute);
|
||||
|
||||
if (qosProvider.IsError)
|
||||
{
|
||||
_logger.LogDebug("IQosProviderHouse returned an error, setting pipeline error");
|
||||
|
||||
SetPipelineError(qosProvider.Errors);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
var buildResult = await _requestCreator.Build(
|
||||
DownstreamRequest,
|
||||
DownstreamRoute.ReRoute.IsQos,
|
||||
qosProvider.Data,
|
||||
DownstreamRoute.ReRoute.HttpHandlerOptions.UseCookieContainer,
|
||||
DownstreamRoute.ReRoute.HttpHandlerOptions.AllowAutoRedirect);
|
||||
|
||||
if (buildResult.IsError)
|
||||
{
|
||||
_logger.LogDebug("IRequestCreator returned an error, setting pipeline error");
|
||||
|
||||
SetPipelineError(buildResult.Errors);
|
||||
|
||||
return;
|
||||
}
|
||||
_logger.LogDebug("setting upstream request");
|
||||
|
||||
SetUpstreamRequestForThisRequest(buildResult.Data);
|
||||
|
||||
await _next.Invoke(context);
|
||||
}
|
||||
}
|
||||
}
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Ocelot.Infrastructure.RequestData;
|
||||
using Ocelot.Logging;
|
||||
using Ocelot.Middleware;
|
||||
using Ocelot.Request.Builder;
|
||||
using Ocelot.Requester.QoS;
|
||||
|
||||
namespace Ocelot.Request.Middleware
|
||||
{
|
||||
public class HttpRequestBuilderMiddleware : OcelotMiddleware
|
||||
{
|
||||
private readonly RequestDelegate _next;
|
||||
private readonly IRequestCreator _requestCreator;
|
||||
private readonly IOcelotLogger _logger;
|
||||
private readonly IQosProviderHouse _qosProviderHouse;
|
||||
|
||||
public HttpRequestBuilderMiddleware(RequestDelegate next,
|
||||
IOcelotLoggerFactory loggerFactory,
|
||||
IRequestScopedDataRepository requestScopedDataRepository,
|
||||
IRequestCreator requestCreator,
|
||||
IQosProviderHouse qosProviderHouse)
|
||||
:base(requestScopedDataRepository)
|
||||
{
|
||||
_next = next;
|
||||
_requestCreator = requestCreator;
|
||||
_qosProviderHouse = qosProviderHouse;
|
||||
_logger = loggerFactory.CreateLogger<HttpRequestBuilderMiddleware>();
|
||||
}
|
||||
|
||||
public async Task Invoke(HttpContext context)
|
||||
{
|
||||
var qosProvider = _qosProviderHouse.Get(DownstreamRoute.ReRoute);
|
||||
|
||||
if (qosProvider.IsError)
|
||||
{
|
||||
_logger.LogDebug("IQosProviderHouse returned an error, setting pipeline error");
|
||||
|
||||
SetPipelineError(qosProvider.Errors);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
var buildResult = await _requestCreator.Build(
|
||||
DownstreamRequest,
|
||||
DownstreamRoute.ReRoute.IsQos,
|
||||
qosProvider.Data,
|
||||
DownstreamRoute.ReRoute.HttpHandlerOptions.UseCookieContainer,
|
||||
DownstreamRoute.ReRoute.HttpHandlerOptions.AllowAutoRedirect,
|
||||
DownstreamRoute.ReRoute.HttpHandlerOptions.UseTracing);
|
||||
if (buildResult.IsError)
|
||||
{
|
||||
_logger.LogDebug("IRequestCreator returned an error, setting pipeline error");
|
||||
SetPipelineError(buildResult.Errors);
|
||||
return;
|
||||
}
|
||||
|
||||
_logger.LogDebug("setting upstream request");
|
||||
|
||||
SetUpstreamRequestForThisRequest(buildResult.Data);
|
||||
|
||||
await _next.Invoke(context);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,28 +1,32 @@
|
||||
using System.Net.Http;
|
||||
using Ocelot.Requester.QoS;
|
||||
|
||||
namespace Ocelot.Request
|
||||
{
|
||||
public class Request
|
||||
{
|
||||
public Request(
|
||||
HttpRequestMessage httpRequestMessage,
|
||||
bool isQos,
|
||||
IQoSProvider qosProvider,
|
||||
bool allowAutoRedirect,
|
||||
bool useCookieContainer)
|
||||
{
|
||||
HttpRequestMessage = httpRequestMessage;
|
||||
IsQos = isQos;
|
||||
QosProvider = qosProvider;
|
||||
AllowAutoRedirect = allowAutoRedirect;
|
||||
UseCookieContainer = useCookieContainer;
|
||||
}
|
||||
|
||||
public HttpRequestMessage HttpRequestMessage { get; private set; }
|
||||
public bool IsQos { get; private set; }
|
||||
public IQoSProvider QosProvider { get; private set; }
|
||||
public bool AllowAutoRedirect { get; private set; }
|
||||
public bool UseCookieContainer { get; private set; }
|
||||
}
|
||||
}
|
||||
using System.Net.Http;
|
||||
using Ocelot.Requester.QoS;
|
||||
|
||||
namespace Ocelot.Request
|
||||
{
|
||||
public class Request
|
||||
{
|
||||
public Request(
|
||||
HttpRequestMessage httpRequestMessage,
|
||||
bool isQos,
|
||||
IQoSProvider qosProvider,
|
||||
bool allowAutoRedirect,
|
||||
bool useCookieContainer,
|
||||
bool isTracing
|
||||
)
|
||||
{
|
||||
HttpRequestMessage = httpRequestMessage;
|
||||
IsQos = isQos;
|
||||
QosProvider = qosProvider;
|
||||
AllowAutoRedirect = allowAutoRedirect;
|
||||
UseCookieContainer = useCookieContainer;
|
||||
IsTracing = isTracing;
|
||||
}
|
||||
|
||||
public HttpRequestMessage HttpRequestMessage { 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; }
|
||||
}
|
||||
}
|
||||
|
@ -1,66 +1,75 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using Ocelot.Logging;
|
||||
using Ocelot.Requester.QoS;
|
||||
|
||||
namespace Ocelot.Requester
|
||||
{
|
||||
internal class HttpClientBuilder : IHttpClientBuilder
|
||||
{
|
||||
private readonly Dictionary<int, Func<DelegatingHandler>> _handlers = new Dictionary<int, Func<DelegatingHandler>>();
|
||||
|
||||
public IHttpClientBuilder WithQos(IQoSProvider qosProvider, IOcelotLogger logger)
|
||||
{
|
||||
_handlers.Add(5000, () => new PollyCircuitBreakingDelegatingHandler(qosProvider, logger));
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public IHttpClient Create(bool useCookies, bool allowAutoRedirect)
|
||||
{
|
||||
var httpclientHandler = new HttpClientHandler { AllowAutoRedirect = allowAutoRedirect, UseCookies = useCookies};
|
||||
|
||||
var client = new HttpClient(CreateHttpMessageHandler(httpclientHandler));
|
||||
|
||||
return new HttpClientWrapper(client);
|
||||
}
|
||||
|
||||
private HttpMessageHandler CreateHttpMessageHandler(HttpMessageHandler httpMessageHandler)
|
||||
{
|
||||
_handlers
|
||||
.OrderByDescending(handler => handler.Key)
|
||||
.Select(handler => handler.Value)
|
||||
.Reverse()
|
||||
.ToList()
|
||||
.ForEach(handler =>
|
||||
{
|
||||
var delegatingHandler = handler();
|
||||
delegatingHandler.InnerHandler = httpMessageHandler;
|
||||
httpMessageHandler = delegatingHandler;
|
||||
});
|
||||
return httpMessageHandler;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This class was made to make unit testing easier when HttpClient is used.
|
||||
/// </summary>
|
||||
internal class HttpClientWrapper : IHttpClient
|
||||
{
|
||||
public HttpClient Client { get; }
|
||||
|
||||
public HttpClientWrapper(HttpClient client)
|
||||
{
|
||||
Client = client;
|
||||
}
|
||||
|
||||
public Task<HttpResponseMessage> SendAsync(HttpRequestMessage request)
|
||||
{
|
||||
return Client.SendAsync(request);
|
||||
}
|
||||
}
|
||||
}
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Ocelot.Logging;
|
||||
using Ocelot.Requester.QoS;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Ocelot.Requester
|
||||
{
|
||||
internal class HttpClientBuilder : IHttpClientBuilder
|
||||
{
|
||||
private readonly Dictionary<int, Func<DelegatingHandler>> _handlers = new Dictionary<int, Func<DelegatingHandler>>();
|
||||
|
||||
public IHttpClientBuilder WithQos(IQoSProvider qosProvider, IOcelotLogger logger)
|
||||
{
|
||||
_handlers.Add(5000, () => new PollyCircuitBreakingDelegatingHandler(qosProvider, logger));
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
private IHttpClientBuilder WithTracing(IServiceProvider provider)
|
||||
{
|
||||
_handlers.Add(6000, () => provider.GetService<OcelotHttpTracingHandler>());
|
||||
return this;
|
||||
}
|
||||
|
||||
public IHttpClient Create(bool useCookies, bool allowAutoRedirect, bool isTracing, IServiceProvider provider)
|
||||
{
|
||||
var httpclientHandler = new HttpClientHandler { AllowAutoRedirect = allowAutoRedirect, UseCookies = useCookies };
|
||||
if (isTracing)
|
||||
{
|
||||
WithTracing(provider);
|
||||
}
|
||||
var client = new HttpClient(CreateHttpMessageHandler(httpclientHandler));
|
||||
|
||||
return new HttpClientWrapper(client);
|
||||
}
|
||||
|
||||
private HttpMessageHandler CreateHttpMessageHandler(HttpMessageHandler httpMessageHandler)
|
||||
{
|
||||
_handlers
|
||||
.OrderByDescending(handler => handler.Key)
|
||||
.Select(handler => handler.Value)
|
||||
.Reverse()
|
||||
.ToList()
|
||||
.ForEach(handler =>
|
||||
{
|
||||
var delegatingHandler = handler();
|
||||
delegatingHandler.InnerHandler = httpMessageHandler;
|
||||
httpMessageHandler = delegatingHandler;
|
||||
});
|
||||
return httpMessageHandler;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This class was made to make unit testing easier when HttpClient is used.
|
||||
/// </summary>
|
||||
internal class HttpClientWrapper : IHttpClient
|
||||
{
|
||||
public HttpClient Client { get; }
|
||||
|
||||
public HttpClientWrapper(HttpClient client)
|
||||
{
|
||||
Client = client;
|
||||
}
|
||||
|
||||
public Task<HttpResponseMessage> SendAsync(HttpRequestMessage request)
|
||||
{
|
||||
return Client.SendAsync(request);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,83 +1,84 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using Ocelot.Configuration;
|
||||
using Ocelot.Logging;
|
||||
using Ocelot.Responses;
|
||||
using Polly.CircuitBreaker;
|
||||
using Polly.Timeout;
|
||||
|
||||
namespace Ocelot.Requester
|
||||
{
|
||||
public class HttpClientHttpRequester : IHttpRequester
|
||||
{
|
||||
private readonly IHttpClientCache _cacheHandlers;
|
||||
private readonly IOcelotLogger _logger;
|
||||
|
||||
public HttpClientHttpRequester(IOcelotLoggerFactory loggerFactory,
|
||||
IHttpClientCache cacheHandlers)
|
||||
{
|
||||
_logger = loggerFactory.CreateLogger<HttpClientHttpRequester>();
|
||||
_cacheHandlers = cacheHandlers;
|
||||
}
|
||||
|
||||
public async Task<Response<HttpResponseMessage>> GetResponse(Request.Request request)
|
||||
{
|
||||
var builder = new HttpClientBuilder();
|
||||
|
||||
var cacheKey = GetCacheKey(request, builder);
|
||||
|
||||
var httpClient = GetHttpClient(cacheKey, builder, request.UseCookieContainer, request.AllowAutoRedirect);
|
||||
|
||||
try
|
||||
{
|
||||
var response = await httpClient.SendAsync(request.HttpRequestMessage);
|
||||
return new OkResponse<HttpResponseMessage>(response);
|
||||
}
|
||||
catch (TimeoutRejectedException exception)
|
||||
{
|
||||
return
|
||||
new ErrorResponse<HttpResponseMessage>(new RequestTimedOutError(exception));
|
||||
}
|
||||
catch (BrokenCircuitException exception)
|
||||
{
|
||||
return
|
||||
new ErrorResponse<HttpResponseMessage>(new RequestTimedOutError(exception));
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
return new ErrorResponse<HttpResponseMessage>(new UnableToCompleteRequestError(exception));
|
||||
}
|
||||
finally
|
||||
{
|
||||
_cacheHandlers.Set(cacheKey, httpClient, TimeSpan.FromHours(24));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private IHttpClient GetHttpClient(string cacheKey, IHttpClientBuilder builder, bool useCookieContainer, bool allowAutoRedirect)
|
||||
{
|
||||
var httpClient = _cacheHandlers.Get(cacheKey);
|
||||
|
||||
if (httpClient == null)
|
||||
{
|
||||
httpClient = builder.Create(useCookieContainer, allowAutoRedirect);
|
||||
}
|
||||
return httpClient;
|
||||
}
|
||||
|
||||
private string GetCacheKey(Request.Request request, IHttpClientBuilder builder)
|
||||
{
|
||||
string baseUrl = $"{request.HttpRequestMessage.RequestUri.Scheme}://{request.HttpRequestMessage.RequestUri.Authority}";
|
||||
|
||||
if (request.IsQos)
|
||||
{
|
||||
builder.WithQos(request.QosProvider, _logger);
|
||||
baseUrl = $"{baseUrl}{request.QosProvider.CircuitBreaker.CircuitBreakerPolicy.PolicyKey}";
|
||||
}
|
||||
|
||||
return baseUrl;
|
||||
}
|
||||
}
|
||||
}
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using Ocelot.Configuration;
|
||||
using Ocelot.Logging;
|
||||
using Ocelot.Responses;
|
||||
using Polly.CircuitBreaker;
|
||||
using Polly.Timeout;
|
||||
|
||||
namespace Ocelot.Requester
|
||||
{
|
||||
public class HttpClientHttpRequester : IHttpRequester
|
||||
{
|
||||
private readonly IHttpClientCache _cacheHandlers;
|
||||
private readonly IOcelotLogger _logger;
|
||||
private readonly IServiceProvider _serviceProvider;
|
||||
|
||||
public HttpClientHttpRequester(IOcelotLoggerFactory loggerFactory, IHttpClientCache cacheHandlers, IServiceProvider provider)
|
||||
{
|
||||
_logger = loggerFactory.CreateLogger<HttpClientHttpRequester>();
|
||||
_cacheHandlers = cacheHandlers;
|
||||
_serviceProvider = provider;
|
||||
}
|
||||
|
||||
public async Task<Response<HttpResponseMessage>> GetResponse(Request.Request request)
|
||||
{
|
||||
var builder = new HttpClientBuilder();
|
||||
|
||||
var cacheKey = GetCacheKey(request, builder);
|
||||
|
||||
var httpClient = GetHttpClient(cacheKey, builder, request.UseCookieContainer, request.AllowAutoRedirect, request.IsTracing);
|
||||
|
||||
try
|
||||
{
|
||||
var response = await httpClient.SendAsync(request.HttpRequestMessage);
|
||||
return new OkResponse<HttpResponseMessage>(response);
|
||||
}
|
||||
catch (TimeoutRejectedException exception)
|
||||
{
|
||||
return
|
||||
new ErrorResponse<HttpResponseMessage>(new RequestTimedOutError(exception));
|
||||
}
|
||||
catch (BrokenCircuitException exception)
|
||||
{
|
||||
return
|
||||
new ErrorResponse<HttpResponseMessage>(new RequestTimedOutError(exception));
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
return new ErrorResponse<HttpResponseMessage>(new UnableToCompleteRequestError(exception));
|
||||
}
|
||||
finally
|
||||
{
|
||||
_cacheHandlers.Set(cacheKey, httpClient, TimeSpan.FromHours(24));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private IHttpClient GetHttpClient(string cacheKey, IHttpClientBuilder builder, bool useCookieContainer, bool allowAutoRedirect,bool isTracing)
|
||||
{
|
||||
var httpClient = _cacheHandlers.Get(cacheKey);
|
||||
|
||||
if (httpClient == null)
|
||||
{
|
||||
httpClient = builder.Create(useCookieContainer, allowAutoRedirect, isTracing, _serviceProvider);
|
||||
}
|
||||
return httpClient;
|
||||
}
|
||||
|
||||
private string GetCacheKey(Request.Request request, IHttpClientBuilder builder)
|
||||
{
|
||||
string baseUrl = $"{request.HttpRequestMessage.RequestUri.Scheme}://{request.HttpRequestMessage.RequestUri.Authority}";
|
||||
|
||||
if (request.IsQos)
|
||||
{
|
||||
builder.WithQos(request.QosProvider, _logger);
|
||||
baseUrl = $"{baseUrl}{request.QosProvider.CircuitBreaker.CircuitBreakerPolicy.PolicyKey}";
|
||||
}
|
||||
|
||||
return baseUrl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -22,6 +22,6 @@ namespace Ocelot.Requester
|
||||
/// </summary>
|
||||
/// <param name="useCookies">Defines if http client should use cookie container</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);
|
||||
}
|
||||
}
|
||||
|
@ -7,7 +7,5 @@ namespace Ocelot.Requester
|
||||
public interface IHttpRequester
|
||||
{
|
||||
Task<Response<HttpResponseMessage>> GetResponse(Request.Request request);
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -1,9 +1,8 @@
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Ocelot.Infrastructure.RequestData;
|
||||
using Ocelot.Logging;
|
||||
using Ocelot.Middleware;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Ocelot.Requester.Middleware
|
||||
{
|
||||
@ -16,7 +15,8 @@ namespace Ocelot.Requester.Middleware
|
||||
public HttpRequesterMiddleware(RequestDelegate next,
|
||||
IOcelotLoggerFactory loggerFactory,
|
||||
IHttpRequester requester,
|
||||
IRequestScopedDataRepository requestScopedDataRepository)
|
||||
IRequestScopedDataRepository requestScopedDataRepository
|
||||
)
|
||||
:base(requestScopedDataRepository)
|
||||
{
|
||||
_next = next;
|
||||
@ -25,7 +25,7 @@ namespace Ocelot.Requester.Middleware
|
||||
}
|
||||
|
||||
public async Task Invoke(HttpContext context)
|
||||
{
|
||||
{
|
||||
var response = await _requester.GetResponse(Request);
|
||||
|
||||
if (response.IsError)
|
||||
@ -41,4 +41,4 @@ namespace Ocelot.Requester.Middleware
|
||||
SetHttpResponseMessageThisRequest(response.Data);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
62
src/Ocelot/Requester/OcelotHttpTracingHandler.cs
Normal file
62
src/Ocelot/Requester/OcelotHttpTracingHandler.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,10 +1,10 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Ocelot.Errors;
|
||||
using Ocelot.Infrastructure.RequestData;
|
||||
using Ocelot.Logging;
|
||||
using Ocelot.Middleware;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Ocelot.Responder.Middleware
|
||||
{
|
||||
@ -22,7 +22,8 @@ namespace Ocelot.Responder.Middleware
|
||||
IHttpResponder responder,
|
||||
IOcelotLoggerFactory loggerFactory,
|
||||
IRequestScopedDataRepository requestScopedDataRepository,
|
||||
IErrorsToHttpStatusCodeMapper codeMapper)
|
||||
IErrorsToHttpStatusCodeMapper codeMapper
|
||||
)
|
||||
:base(requestScopedDataRepository)
|
||||
{
|
||||
_next = next;
|
||||
@ -33,14 +34,13 @@ namespace Ocelot.Responder.Middleware
|
||||
}
|
||||
|
||||
public async Task Invoke(HttpContext context)
|
||||
{
|
||||
{
|
||||
await _next.Invoke(context);
|
||||
|
||||
if (PipelineError)
|
||||
{
|
||||
var errors = PipelineErrors;
|
||||
_logger.LogError($"{PipelineErrors.Count} pipeline errors found in {MiddlewareName}. Setting error response status code");
|
||||
|
||||
SetErrorResponse(context, errors);
|
||||
}
|
||||
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);
|
||||
_responder.SetErrorResponseOnContext(context, statusCode);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,52 +1,53 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Newtonsoft.Json;
|
||||
using Ocelot.DependencyInjection;
|
||||
using Ocelot.Middleware;
|
||||
using Ocelot.Raft;
|
||||
using Rafty.Concensus;
|
||||
using Rafty.FiniteStateMachine;
|
||||
using Rafty.Infrastructure;
|
||||
using Rafty.Log;
|
||||
using ConfigurationBuilder = Microsoft.Extensions.Configuration.ConfigurationBuilder;
|
||||
|
||||
namespace Ocelot.IntegrationTests
|
||||
{
|
||||
public class RaftStartup
|
||||
{
|
||||
public RaftStartup(IHostingEnvironment env)
|
||||
{
|
||||
var builder = new ConfigurationBuilder()
|
||||
.SetBasePath(env.ContentRootPath)
|
||||
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
|
||||
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
|
||||
.AddJsonFile("peers.json", optional: true, reloadOnChange: true)
|
||||
.AddJsonFile("configuration.json")
|
||||
.AddEnvironmentVariables();
|
||||
|
||||
Configuration = builder.Build();
|
||||
}
|
||||
|
||||
public IConfiguration Configuration { get; }
|
||||
|
||||
public virtual void ConfigureServices(IServiceCollection services)
|
||||
{
|
||||
services
|
||||
.AddOcelot(Configuration)
|
||||
.AddAdministration("/administration", "secret")
|
||||
.AddRafty();
|
||||
}
|
||||
|
||||
public virtual void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
|
||||
{
|
||||
app.UseOcelot().Wait();
|
||||
}
|
||||
}
|
||||
}
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Newtonsoft.Json;
|
||||
using Ocelot.DependencyInjection;
|
||||
using Ocelot.Middleware;
|
||||
using Ocelot.Raft;
|
||||
using Rafty.Concensus;
|
||||
using Rafty.FiniteStateMachine;
|
||||
using Rafty.Infrastructure;
|
||||
using Rafty.Log;
|
||||
using ConfigurationBuilder = Microsoft.Extensions.Configuration.ConfigurationBuilder;
|
||||
|
||||
namespace Ocelot.IntegrationTests
|
||||
{
|
||||
public class RaftStartup
|
||||
{
|
||||
public RaftStartup(IHostingEnvironment env)
|
||||
{
|
||||
var builder = new ConfigurationBuilder()
|
||||
.SetBasePath(env.ContentRootPath)
|
||||
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
|
||||
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
|
||||
.AddJsonFile("peers.json", optional: true, reloadOnChange: true)
|
||||
.AddJsonFile("configuration.json")
|
||||
.AddEnvironmentVariables();
|
||||
|
||||
Configuration = builder.Build();
|
||||
}
|
||||
|
||||
public IConfiguration Configuration { get; }
|
||||
|
||||
public virtual void ConfigureServices(IServiceCollection services)
|
||||
{
|
||||
services
|
||||
.AddOcelot(Configuration)
|
||||
.AddAdministration("/administration", "secret")
|
||||
.AddRafty()
|
||||
;
|
||||
}
|
||||
|
||||
public virtual void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
|
||||
{
|
||||
app.UseOcelot().Wait();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,40 +1,45 @@
|
||||
using System;
|
||||
using CacheManager.Core;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Ocelot.DependencyInjection;
|
||||
using Ocelot.Middleware;
|
||||
using ConfigurationBuilder = Microsoft.Extensions.Configuration.ConfigurationBuilder;
|
||||
|
||||
namespace Ocelot.ManualTest
|
||||
{
|
||||
public class ManualTestStartup
|
||||
{
|
||||
public void ConfigureServices(IServiceCollection services)
|
||||
{
|
||||
Action<ConfigurationBuilderCachePart> settings = (x) =>
|
||||
{
|
||||
x.WithDictionaryHandle();
|
||||
};
|
||||
|
||||
services.AddAuthentication()
|
||||
.AddJwtBearer("TestKey", x =>
|
||||
{
|
||||
x.Authority = "test";
|
||||
x.Audience = "test";
|
||||
});
|
||||
|
||||
services.AddOcelot()
|
||||
.AddCacheManager(settings)
|
||||
.AddAdministration("/administration", "secret");
|
||||
}
|
||||
|
||||
public void Configure(IApplicationBuilder app)
|
||||
{
|
||||
app.UseOcelot().Wait();
|
||||
}
|
||||
}
|
||||
}
|
||||
using System;
|
||||
using CacheManager.Core;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Ocelot.DependencyInjection;
|
||||
using Ocelot.Middleware;
|
||||
using ConfigurationBuilder = Microsoft.Extensions.Configuration.ConfigurationBuilder;
|
||||
|
||||
namespace Ocelot.ManualTest
|
||||
{
|
||||
public class ManualTestStartup
|
||||
{
|
||||
public void ConfigureServices(IServiceCollection services)
|
||||
{
|
||||
Action<ConfigurationBuilderCachePart> settings = (x) =>
|
||||
{
|
||||
x.WithDictionaryHandle();
|
||||
};
|
||||
|
||||
services.AddAuthentication()
|
||||
.AddJwtBearer("TestKey", x =>
|
||||
{
|
||||
x.Authority = "test";
|
||||
x.Audience = "test";
|
||||
});
|
||||
|
||||
services.AddOcelot()
|
||||
.AddCacheManager(settings)
|
||||
.AddOpenTracing(option =>
|
||||
{
|
||||
option.CollectorUrl = "http://localhost:9618";
|
||||
option.Service = "Ocelot.ManualTest";
|
||||
})
|
||||
.AddAdministration("/administration", "secret");
|
||||
}
|
||||
|
||||
public void Configure(IApplicationBuilder app)
|
||||
{
|
||||
app.UseOcelot().Wait();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,287 +1,304 @@
|
||||
{
|
||||
"ReRoutes": [
|
||||
{
|
||||
"DownstreamPathTemplate": "/",
|
||||
"DownstreamScheme": "http",
|
||||
"DownstreamHostAndPorts": [
|
||||
{
|
||||
"Host": "localhost",
|
||||
"Port": 52876
|
||||
}
|
||||
],
|
||||
"UpstreamPathTemplate": "/identityserverexample",
|
||||
"UpstreamHttpMethod": [ "Get" ],
|
||||
"QoSOptions": {
|
||||
"ExceptionsAllowedBeforeBreaking": 3,
|
||||
"DurationOfBreak": 10,
|
||||
"TimeoutValue": 5000
|
||||
},
|
||||
"AuthenticationOptions": {
|
||||
"AuthenticationProviderKey": "TestKey",
|
||||
"AllowedScopes": [
|
||||
"openid",
|
||||
"offline_access"
|
||||
]
|
||||
},
|
||||
"AddHeadersToRequest": {
|
||||
"CustomerId": "Claims[CustomerId] > value",
|
||||
"LocationId": "Claims[LocationId] > value",
|
||||
"UserType": "Claims[sub] > value[0] > |",
|
||||
"UserId": "Claims[sub] > value[1] > |"
|
||||
},
|
||||
"AddClaimsToRequest": {
|
||||
"CustomerId": "Claims[CustomerId] > value",
|
||||
"LocationId": "Claims[LocationId] > value",
|
||||
"UserType": "Claims[sub] > value[0] > |",
|
||||
"UserId": "Claims[sub] > value[1] > |"
|
||||
},
|
||||
"AddQueriesToRequest": {
|
||||
"CustomerId": "Claims[CustomerId] > value",
|
||||
"LocationId": "Claims[LocationId] > value",
|
||||
"UserType": "Claims[sub] > value[0] > |",
|
||||
"UserId": "Claims[sub] > value[1] > |"
|
||||
},
|
||||
"RouteClaimsRequirement": {
|
||||
"UserType": "registered"
|
||||
},
|
||||
"RequestIdKey": "OcRequestId"
|
||||
},
|
||||
{
|
||||
"DownstreamPathTemplate": "/posts",
|
||||
"DownstreamScheme": "https",
|
||||
"DownstreamHostAndPorts": [
|
||||
{
|
||||
"Host": "jsonplaceholder.typicode.com",
|
||||
"Port": 443
|
||||
}
|
||||
],
|
||||
"UpstreamPathTemplate": "/posts",
|
||||
"UpstreamHttpMethod": [ "Get" ],
|
||||
"QoSOptions": {
|
||||
"ExceptionsAllowedBeforeBreaking": 3,
|
||||
"DurationOfBreak": 10,
|
||||
"TimeoutValue": 5000
|
||||
}
|
||||
},
|
||||
{
|
||||
"DownstreamPathTemplate": "/posts/{postId}",
|
||||
"DownstreamScheme": "http",
|
||||
"DownstreamHostAndPorts": [
|
||||
{
|
||||
"Host": "jsonplaceholder.typicode.com",
|
||||
"Port": 80
|
||||
}
|
||||
],
|
||||
"UpstreamPathTemplate": "/posts/{postId}",
|
||||
"UpstreamHttpMethod": [ "Get" ],
|
||||
"RequestIdKey": "ReRouteRequestId",
|
||||
"QoSOptions": {
|
||||
"ExceptionsAllowedBeforeBreaking": 3,
|
||||
"DurationOfBreak": 10,
|
||||
"TimeoutValue": 5000
|
||||
}
|
||||
},
|
||||
{
|
||||
"DownstreamPathTemplate": "/posts/{postId}/comments",
|
||||
"DownstreamScheme": "http",
|
||||
"DownstreamHostAndPorts": [
|
||||
{
|
||||
"Host": "jsonplaceholder.typicode.com",
|
||||
"Port": 80
|
||||
}
|
||||
],
|
||||
"UpstreamPathTemplate": "/posts/{postId}/comments",
|
||||
"UpstreamHttpMethod": [ "Get" ],
|
||||
"QoSOptions": {
|
||||
"ExceptionsAllowedBeforeBreaking": 3,
|
||||
"DurationOfBreak": 10,
|
||||
"TimeoutValue": 5000
|
||||
}
|
||||
},
|
||||
{
|
||||
"DownstreamPathTemplate": "/comments",
|
||||
"DownstreamScheme": "http",
|
||||
"DownstreamHostAndPorts": [
|
||||
{
|
||||
"Host": "jsonplaceholder.typicode.com",
|
||||
"Port": 80
|
||||
}
|
||||
],
|
||||
"UpstreamPathTemplate": "/comments",
|
||||
"UpstreamHttpMethod": [ "Get" ],
|
||||
"QoSOptions": {
|
||||
"ExceptionsAllowedBeforeBreaking": 3,
|
||||
"DurationOfBreak": 10,
|
||||
"TimeoutValue": 5000
|
||||
}
|
||||
},
|
||||
{
|
||||
"DownstreamPathTemplate": "/posts",
|
||||
"DownstreamScheme": "http",
|
||||
"DownstreamHostAndPorts": [
|
||||
{
|
||||
"Host": "jsonplaceholder.typicode.com",
|
||||
"Port": 80
|
||||
}
|
||||
],
|
||||
"UpstreamPathTemplate": "/posts",
|
||||
"UpstreamHttpMethod": [ "Post" ],
|
||||
"QoSOptions": {
|
||||
"ExceptionsAllowedBeforeBreaking": 3,
|
||||
"DurationOfBreak": 10,
|
||||
"TimeoutValue": 5000
|
||||
}
|
||||
},
|
||||
{
|
||||
"DownstreamPathTemplate": "/posts/{postId}",
|
||||
"DownstreamScheme": "http",
|
||||
"DownstreamHostAndPorts": [
|
||||
{
|
||||
"Host": "jsonplaceholder.typicode.com",
|
||||
"Port": 80
|
||||
}
|
||||
],
|
||||
"UpstreamPathTemplate": "/posts/{postId}",
|
||||
"UpstreamHttpMethod": [ "Put" ],
|
||||
"QoSOptions": {
|
||||
"ExceptionsAllowedBeforeBreaking": 3,
|
||||
"DurationOfBreak": 10,
|
||||
"TimeoutValue": 5000
|
||||
}
|
||||
},
|
||||
{
|
||||
"DownstreamPathTemplate": "/posts/{postId}",
|
||||
"DownstreamScheme": "http",
|
||||
"DownstreamHostAndPorts": [
|
||||
{
|
||||
"Host": "jsonplaceholder.typicode.com",
|
||||
"Port": 80
|
||||
}
|
||||
],
|
||||
"UpstreamPathTemplate": "/posts/{postId}",
|
||||
"UpstreamHttpMethod": [ "Patch" ],
|
||||
"QoSOptions": {
|
||||
"ExceptionsAllowedBeforeBreaking": 3,
|
||||
"DurationOfBreak": 10,
|
||||
"TimeoutValue": 5000
|
||||
}
|
||||
},
|
||||
{
|
||||
"DownstreamPathTemplate": "/posts/{postId}",
|
||||
"DownstreamScheme": "http",
|
||||
"DownstreamHostAndPorts": [
|
||||
{
|
||||
"Host": "jsonplaceholder.typicode.com",
|
||||
"Port": 80
|
||||
}
|
||||
],
|
||||
"UpstreamPathTemplate": "/posts/{postId}",
|
||||
"UpstreamHttpMethod": [ "Delete" ],
|
||||
"QoSOptions": {
|
||||
"ExceptionsAllowedBeforeBreaking": 3,
|
||||
"DurationOfBreak": 10,
|
||||
"TimeoutValue": 5000
|
||||
}
|
||||
},
|
||||
{
|
||||
"DownstreamPathTemplate": "/api/products",
|
||||
"DownstreamScheme": "http",
|
||||
"DownstreamHostAndPorts": [
|
||||
{
|
||||
"Host": "jsonplaceholder.typicode.com",
|
||||
"Port": 80
|
||||
}
|
||||
],
|
||||
"UpstreamPathTemplate": "/products",
|
||||
"UpstreamHttpMethod": [ "Get" ],
|
||||
"QoSOptions": {
|
||||
"ExceptionsAllowedBeforeBreaking": 3,
|
||||
"DurationOfBreak": 10,
|
||||
"TimeoutValue": 5000
|
||||
},
|
||||
"FileCacheOptions": { "TtlSeconds": 15 }
|
||||
},
|
||||
{
|
||||
"DownstreamPathTemplate": "/api/products/{productId}",
|
||||
"DownstreamScheme": "http",
|
||||
"DownstreamHostAndPorts": [
|
||||
{
|
||||
"Host": "jsonplaceholder.typicode.com",
|
||||
"Port": 80
|
||||
}
|
||||
],
|
||||
"UpstreamPathTemplate": "/products/{productId}",
|
||||
"UpstreamHttpMethod": [ "Get" ],
|
||||
"FileCacheOptions": { "TtlSeconds": 15 }
|
||||
},
|
||||
{
|
||||
"DownstreamPathTemplate": "/api/products",
|
||||
"DownstreamScheme": "http",
|
||||
"DownstreamHostAndPorts": [
|
||||
{
|
||||
"Host": "jsonplaceholder.typicode.com",
|
||||
"Port": 80
|
||||
}
|
||||
],
|
||||
"UpstreamPathTemplate": "/products",
|
||||
"UpstreamHttpMethod": [ "Post" ],
|
||||
"QoSOptions": {
|
||||
"ExceptionsAllowedBeforeBreaking": 3,
|
||||
"DurationOfBreak": 10,
|
||||
"TimeoutValue": 5000
|
||||
}
|
||||
},
|
||||
{
|
||||
"DownstreamPathTemplate": "/api/products/{productId}",
|
||||
"DownstreamScheme": "http",
|
||||
"DownstreamHostAndPorts": [
|
||||
{
|
||||
"Host": "jsonplaceholder.typicode.com",
|
||||
"Port": 80
|
||||
}
|
||||
],
|
||||
"UpstreamPathTemplate": "/products/{productId}",
|
||||
"UpstreamHttpMethod": [ "Put" ],
|
||||
"QoSOptions": {
|
||||
"ExceptionsAllowedBeforeBreaking": 3,
|
||||
"DurationOfBreak": 10,
|
||||
"TimeoutValue": 5000
|
||||
},
|
||||
"FileCacheOptions": { "TtlSeconds": 15 }
|
||||
},
|
||||
{
|
||||
"DownstreamPathTemplate": "/posts",
|
||||
"DownstreamScheme": "http",
|
||||
"DownstreamHostAndPorts": [
|
||||
{
|
||||
"Host": "jsonplaceholder.typicode.com",
|
||||
"Port": 80
|
||||
}
|
||||
],
|
||||
"UpstreamPathTemplate": "/posts/",
|
||||
"UpstreamHttpMethod": [ "Get" ],
|
||||
"QoSOptions": {
|
||||
"ExceptionsAllowedBeforeBreaking": 3,
|
||||
"DurationOfBreak": 10,
|
||||
"TimeoutValue": 5000
|
||||
},
|
||||
"FileCacheOptions": { "TtlSeconds": 15 }
|
||||
},
|
||||
{
|
||||
"DownstreamPathTemplate": "/",
|
||||
"DownstreamScheme": "http",
|
||||
"DownstreamHostAndPorts": [
|
||||
{
|
||||
"Host": "www.bbc.co.uk",
|
||||
"Port": 80
|
||||
}
|
||||
],
|
||||
"UpstreamPathTemplate": "/bbc/",
|
||||
"UpstreamHttpMethod": [ "Get" ]
|
||||
}
|
||||
],
|
||||
|
||||
"GlobalConfiguration": {
|
||||
"RequestIdKey": "OcRequestId"
|
||||
}
|
||||
}
|
||||
{
|
||||
"ReRoutes": [
|
||||
{
|
||||
"DownstreamPathTemplate": "/api/values",
|
||||
"DownstreamScheme": "http",
|
||||
"UpstreamPathTemplate": "/api/values",
|
||||
"UpstreamHttpMethod": [ "Get" ],
|
||||
"DownstreamHostAndPorts": [
|
||||
{
|
||||
"Host": "localhost",
|
||||
"Port": 5002
|
||||
}
|
||||
],
|
||||
"HttpHandlerOptions": {
|
||||
"AllowAutoRedirect": true,
|
||||
"UseCookieContainer": true,
|
||||
"UseTracing": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"DownstreamPathTemplate": "/",
|
||||
"DownstreamScheme": "http",
|
||||
"DownstreamHostAndPorts": [
|
||||
{
|
||||
"Host": "localhost",
|
||||
"Port": 52876
|
||||
}
|
||||
],
|
||||
"UpstreamPathTemplate": "/identityserverexample",
|
||||
"UpstreamHttpMethod": [ "Get" ],
|
||||
"QoSOptions": {
|
||||
"ExceptionsAllowedBeforeBreaking": 3,
|
||||
"DurationOfBreak": 10,
|
||||
"TimeoutValue": 5000
|
||||
},
|
||||
"AuthenticationOptions": {
|
||||
"AuthenticationProviderKey": "TestKey",
|
||||
"AllowedScopes": [
|
||||
"openid",
|
||||
"offline_access"
|
||||
]
|
||||
},
|
||||
"AddHeadersToRequest": {
|
||||
"CustomerId": "Claims[CustomerId] > value",
|
||||
"LocationId": "Claims[LocationId] > value",
|
||||
"UserType": "Claims[sub] > value[0] > |",
|
||||
"UserId": "Claims[sub] > value[1] > |"
|
||||
},
|
||||
"AddClaimsToRequest": {
|
||||
"CustomerId": "Claims[CustomerId] > value",
|
||||
"LocationId": "Claims[LocationId] > value",
|
||||
"UserType": "Claims[sub] > value[0] > |",
|
||||
"UserId": "Claims[sub] > value[1] > |"
|
||||
},
|
||||
"AddQueriesToRequest": {
|
||||
"CustomerId": "Claims[CustomerId] > value",
|
||||
"LocationId": "Claims[LocationId] > value",
|
||||
"UserType": "Claims[sub] > value[0] > |",
|
||||
"UserId": "Claims[sub] > value[1] > |"
|
||||
},
|
||||
"RouteClaimsRequirement": {
|
||||
"UserType": "registered"
|
||||
},
|
||||
"RequestIdKey": "OcRequestId"
|
||||
},
|
||||
{
|
||||
"DownstreamPathTemplate": "/posts",
|
||||
"DownstreamScheme": "https",
|
||||
"DownstreamHostAndPorts": [
|
||||
{
|
||||
"Host": "jsonplaceholder.typicode.com",
|
||||
"Port": 443
|
||||
}
|
||||
],
|
||||
"UpstreamPathTemplate": "/posts",
|
||||
"UpstreamHttpMethod": [ "Get" ],
|
||||
"QoSOptions": {
|
||||
"ExceptionsAllowedBeforeBreaking": 3,
|
||||
"DurationOfBreak": 10,
|
||||
"TimeoutValue": 5000
|
||||
}
|
||||
},
|
||||
{
|
||||
"DownstreamPathTemplate": "/posts/{postId}",
|
||||
"DownstreamScheme": "http",
|
||||
"DownstreamHostAndPorts": [
|
||||
{
|
||||
"Host": "jsonplaceholder.typicode.com",
|
||||
"Port": 80
|
||||
}
|
||||
],
|
||||
"UpstreamPathTemplate": "/posts/{postId}",
|
||||
"UpstreamHttpMethod": [ "Get" ],
|
||||
"RequestIdKey": "ReRouteRequestId",
|
||||
"QoSOptions": {
|
||||
"ExceptionsAllowedBeforeBreaking": 3,
|
||||
"DurationOfBreak": 10,
|
||||
"TimeoutValue": 5000
|
||||
}
|
||||
},
|
||||
{
|
||||
"DownstreamPathTemplate": "/posts/{postId}/comments",
|
||||
"DownstreamScheme": "http",
|
||||
"DownstreamHostAndPorts": [
|
||||
{
|
||||
"Host": "jsonplaceholder.typicode.com",
|
||||
"Port": 80
|
||||
}
|
||||
],
|
||||
"UpstreamPathTemplate": "/posts/{postId}/comments",
|
||||
"UpstreamHttpMethod": [ "Get" ],
|
||||
"QoSOptions": {
|
||||
"ExceptionsAllowedBeforeBreaking": 3,
|
||||
"DurationOfBreak": 10,
|
||||
"TimeoutValue": 5000
|
||||
}
|
||||
},
|
||||
{
|
||||
"DownstreamPathTemplate": "/comments",
|
||||
"DownstreamScheme": "http",
|
||||
"DownstreamHostAndPorts": [
|
||||
{
|
||||
"Host": "jsonplaceholder.typicode.com",
|
||||
"Port": 80
|
||||
}
|
||||
],
|
||||
"UpstreamPathTemplate": "/comments",
|
||||
"UpstreamHttpMethod": [ "Get" ],
|
||||
"QoSOptions": {
|
||||
"ExceptionsAllowedBeforeBreaking": 3,
|
||||
"DurationOfBreak": 10,
|
||||
"TimeoutValue": 5000
|
||||
}
|
||||
},
|
||||
{
|
||||
"DownstreamPathTemplate": "/posts",
|
||||
"DownstreamScheme": "http",
|
||||
"DownstreamHostAndPorts": [
|
||||
{
|
||||
"Host": "jsonplaceholder.typicode.com",
|
||||
"Port": 80
|
||||
}
|
||||
],
|
||||
"UpstreamPathTemplate": "/posts",
|
||||
"UpstreamHttpMethod": [ "Post" ],
|
||||
"QoSOptions": {
|
||||
"ExceptionsAllowedBeforeBreaking": 3,
|
||||
"DurationOfBreak": 10,
|
||||
"TimeoutValue": 5000
|
||||
}
|
||||
},
|
||||
{
|
||||
"DownstreamPathTemplate": "/posts/{postId}",
|
||||
"DownstreamScheme": "http",
|
||||
"DownstreamHostAndPorts": [
|
||||
{
|
||||
"Host": "jsonplaceholder.typicode.com",
|
||||
"Port": 80
|
||||
}
|
||||
],
|
||||
"UpstreamPathTemplate": "/posts/{postId}",
|
||||
"UpstreamHttpMethod": [ "Put" ],
|
||||
"QoSOptions": {
|
||||
"ExceptionsAllowedBeforeBreaking": 3,
|
||||
"DurationOfBreak": 10,
|
||||
"TimeoutValue": 5000
|
||||
}
|
||||
},
|
||||
{
|
||||
"DownstreamPathTemplate": "/posts/{postId}",
|
||||
"DownstreamScheme": "http",
|
||||
"DownstreamHostAndPorts": [
|
||||
{
|
||||
"Host": "jsonplaceholder.typicode.com",
|
||||
"Port": 80
|
||||
}
|
||||
],
|
||||
"UpstreamPathTemplate": "/posts/{postId}",
|
||||
"UpstreamHttpMethod": [ "Patch" ],
|
||||
"QoSOptions": {
|
||||
"ExceptionsAllowedBeforeBreaking": 3,
|
||||
"DurationOfBreak": 10,
|
||||
"TimeoutValue": 5000
|
||||
}
|
||||
},
|
||||
{
|
||||
"DownstreamPathTemplate": "/posts/{postId}",
|
||||
"DownstreamScheme": "http",
|
||||
"DownstreamHostAndPorts": [
|
||||
{
|
||||
"Host": "jsonplaceholder.typicode.com",
|
||||
"Port": 80
|
||||
}
|
||||
],
|
||||
"UpstreamPathTemplate": "/posts/{postId}",
|
||||
"UpstreamHttpMethod": [ "Delete" ],
|
||||
"QoSOptions": {
|
||||
"ExceptionsAllowedBeforeBreaking": 3,
|
||||
"DurationOfBreak": 10,
|
||||
"TimeoutValue": 5000
|
||||
}
|
||||
},
|
||||
{
|
||||
"DownstreamPathTemplate": "/api/products",
|
||||
"DownstreamScheme": "http",
|
||||
"DownstreamHostAndPorts": [
|
||||
{
|
||||
"Host": "jsonplaceholder.typicode.com",
|
||||
"Port": 80
|
||||
}
|
||||
],
|
||||
"UpstreamPathTemplate": "/products",
|
||||
"UpstreamHttpMethod": [ "Get" ],
|
||||
"QoSOptions": {
|
||||
"ExceptionsAllowedBeforeBreaking": 3,
|
||||
"DurationOfBreak": 10,
|
||||
"TimeoutValue": 5000
|
||||
},
|
||||
"FileCacheOptions": { "TtlSeconds": 15 }
|
||||
},
|
||||
{
|
||||
"DownstreamPathTemplate": "/api/products/{productId}",
|
||||
"DownstreamScheme": "http",
|
||||
"DownstreamHostAndPorts": [
|
||||
{
|
||||
"Host": "jsonplaceholder.typicode.com",
|
||||
"Port": 80
|
||||
}
|
||||
],
|
||||
"UpstreamPathTemplate": "/products/{productId}",
|
||||
"UpstreamHttpMethod": [ "Get" ],
|
||||
"FileCacheOptions": { "TtlSeconds": 15 }
|
||||
},
|
||||
{
|
||||
"DownstreamPathTemplate": "/api/products",
|
||||
"DownstreamScheme": "http",
|
||||
"DownstreamHostAndPorts": [
|
||||
{
|
||||
"Host": "jsonplaceholder.typicode.com",
|
||||
"Port": 80
|
||||
}
|
||||
],
|
||||
"UpstreamPathTemplate": "/products",
|
||||
"UpstreamHttpMethod": [ "Post" ],
|
||||
"QoSOptions": {
|
||||
"ExceptionsAllowedBeforeBreaking": 3,
|
||||
"DurationOfBreak": 10,
|
||||
"TimeoutValue": 5000
|
||||
}
|
||||
},
|
||||
{
|
||||
"DownstreamPathTemplate": "/api/products/{productId}",
|
||||
"DownstreamScheme": "http",
|
||||
"DownstreamHostAndPorts": [
|
||||
{
|
||||
"Host": "jsonplaceholder.typicode.com",
|
||||
"Port": 80
|
||||
}
|
||||
],
|
||||
"UpstreamPathTemplate": "/products/{productId}",
|
||||
"UpstreamHttpMethod": [ "Put" ],
|
||||
"QoSOptions": {
|
||||
"ExceptionsAllowedBeforeBreaking": 3,
|
||||
"DurationOfBreak": 10,
|
||||
"TimeoutValue": 5000
|
||||
},
|
||||
"FileCacheOptions": { "TtlSeconds": 15 }
|
||||
},
|
||||
{
|
||||
"DownstreamPathTemplate": "/posts",
|
||||
"DownstreamScheme": "http",
|
||||
"DownstreamHostAndPorts": [
|
||||
{
|
||||
"Host": "jsonplaceholder.typicode.com",
|
||||
"Port": 80
|
||||
}
|
||||
],
|
||||
"UpstreamPathTemplate": "/posts/",
|
||||
"UpstreamHttpMethod": [ "Get" ],
|
||||
"QoSOptions": {
|
||||
"ExceptionsAllowedBeforeBreaking": 3,
|
||||
"DurationOfBreak": 10,
|
||||
"TimeoutValue": 5000
|
||||
},
|
||||
"FileCacheOptions": { "TtlSeconds": 15 }
|
||||
},
|
||||
{
|
||||
"DownstreamPathTemplate": "/",
|
||||
"DownstreamScheme": "http",
|
||||
"DownstreamHostAndPorts": [
|
||||
{
|
||||
"Host": "www.bbc.co.uk",
|
||||
"Port": 80
|
||||
}
|
||||
],
|
||||
"UpstreamPathTemplate": "/bbc/",
|
||||
"UpstreamHttpMethod": [ "Get" ]
|
||||
}
|
||||
],
|
||||
|
||||
"GlobalConfiguration": {
|
||||
"RequestIdKey": "ot-traceid"
|
||||
}
|
||||
}
|
||||
|
@ -471,7 +471,7 @@ namespace Ocelot.UnitTests.Configuration
|
||||
{
|
||||
var reRouteOptions = new ReRouteOptionsBuilder()
|
||||
.Build();
|
||||
var httpHandlerOptions = new HttpHandlerOptions(true, true);
|
||||
var httpHandlerOptions = new HttpHandlerOptions(true, true,false);
|
||||
|
||||
this.Given(x => x.GivenTheConfigIs(new FileConfiguration
|
||||
{
|
||||
|
@ -23,7 +23,7 @@ namespace Ocelot.UnitTests.Configuration
|
||||
public void should_create_options_with_useCookie_and_allowAutoRedirect_true_as_default()
|
||||
{
|
||||
var fileReRoute = new FileReRoute();
|
||||
var expectedOptions = new HttpHandlerOptions(true, true);
|
||||
var expectedOptions = new HttpHandlerOptions(true, true, false);
|
||||
|
||||
this.Given(x => GivenTheFollowing(fileReRoute))
|
||||
.When(x => WhenICreateHttpHandlerOptions())
|
||||
@ -39,11 +39,12 @@ namespace Ocelot.UnitTests.Configuration
|
||||
HttpHandlerOptions = new FileHttpHandlerOptions
|
||||
{
|
||||
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))
|
||||
.When(x => WhenICreateHttpHandlerOptions())
|
||||
@ -66,6 +67,7 @@ namespace Ocelot.UnitTests.Configuration
|
||||
_httpHandlerOptions.ShouldNotBeNull();
|
||||
_httpHandlerOptions.AllowAutoRedirect.ShouldBe(options.AllowAutoRedirect);
|
||||
_httpHandlerOptions.UseCookieContainer.ShouldBe(options.UseCookieContainer);
|
||||
_httpHandlerOptions.UseTracing.ShouldBe(options.UseTracing);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,9 +1,4 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Text;
|
||||
using CacheManager.Core;
|
||||
using CacheManager.Core;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.AspNetCore.Hosting.Internal;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
@ -13,8 +8,11 @@ using Ocelot.Configuration;
|
||||
using Ocelot.Configuration.File;
|
||||
using Ocelot.Configuration.Setter;
|
||||
using Ocelot.DependencyInjection;
|
||||
using Ocelot.Logging;
|
||||
using Ocelot.Requester;
|
||||
using Shouldly;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using TestStack.BDDfy;
|
||||
using Xunit;
|
||||
|
||||
@ -96,6 +94,16 @@ namespace Ocelot.UnitTests.DependencyInjection
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_set_up_tracing()
|
||||
{
|
||||
this.Given(x => WhenISetUpOcelotServices())
|
||||
.When(x => WhenISetUpOpentracing())
|
||||
.When(x => WhenIAccessOcelotHttpTracingHandler())
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
|
||||
[Fact]
|
||||
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()
|
||||
{
|
||||
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()
|
||||
{
|
||||
try
|
||||
|
@ -17,6 +17,7 @@
|
||||
using Ocelot.Requester.QoS;
|
||||
using Ocelot.Configuration;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Ocelot.Errors;
|
||||
|
||||
public class HttpRequestBuilderMiddlewareTests : ServerHostedMiddlewareTest
|
||||
{
|
||||
@ -51,18 +52,39 @@
|
||||
new ReRouteBuilder()
|
||||
.WithRequestIdKey("LSRequestId")
|
||||
.WithUpstreamHttpMethod(new List<string> { "Get" })
|
||||
.WithHttpHandlerOptions(new HttpHandlerOptions(true, true))
|
||||
.WithHttpHandlerOptions(new HttpHandlerOptions(true, true,false))
|
||||
.Build());
|
||||
|
||||
this.Given(x => x.GivenTheDownStreamUrlIs("any old string"))
|
||||
.And(x => x.GivenTheQosProviderHouseReturns(new OkResponse<IQoSProvider>(new NoQoSProvider())))
|
||||
.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())
|
||||
.Then(x => x.ThenTheScopedDataRepositoryIsCalledCorrectly())
|
||||
.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)
|
||||
{
|
||||
services.AddSingleton<IOcelotLoggerFactory, AspDotNetLoggerFactory>();
|
||||
@ -109,7 +131,9 @@
|
||||
It.IsAny<bool>(),
|
||||
It.IsAny<IQoSProvider>(),
|
||||
It.IsAny<bool>(),
|
||||
It.IsAny<bool>()))
|
||||
It.IsAny<bool>(),
|
||||
It.IsAny<bool>()
|
||||
))
|
||||
.ReturnsAsync(_request);
|
||||
}
|
||||
|
||||
@ -118,5 +142,11 @@
|
||||
_scopedRepository
|
||||
.Verify(x => x.Add("Request", _request.Data), Times.Once());
|
||||
}
|
||||
|
||||
private void ThenTheScopedDataRepositoryQosProviderError()
|
||||
{
|
||||
_scopedRepository
|
||||
.Verify(x => x.Add("OcelotMiddlewareError", true), Times.Once());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,79 +1,86 @@
|
||||
namespace Ocelot.UnitTests.Request
|
||||
{
|
||||
using System.Net.Http;
|
||||
|
||||
using Ocelot.Request.Builder;
|
||||
using Ocelot.Requester.QoS;
|
||||
using Ocelot.Responses;
|
||||
using Shouldly;
|
||||
using TestStack.BDDfy;
|
||||
using Xunit;
|
||||
|
||||
public class HttpRequestCreatorTests
|
||||
{
|
||||
private readonly IRequestCreator _requestCreator;
|
||||
private readonly bool _isQos;
|
||||
private readonly IQoSProvider _qoSProvider;
|
||||
private readonly HttpRequestMessage _requestMessage;
|
||||
private readonly bool _useCookieContainer;
|
||||
private readonly bool _allowAutoRedirect;
|
||||
|
||||
private Response<Ocelot.Request.Request> _response;
|
||||
|
||||
public HttpRequestCreatorTests()
|
||||
{
|
||||
_requestCreator = new HttpRequestCreator();
|
||||
_isQos = true;
|
||||
_qoSProvider = new NoQoSProvider();
|
||||
_useCookieContainer = false;
|
||||
_allowAutoRedirect = false;
|
||||
|
||||
_requestMessage = new HttpRequestMessage();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ShouldBuildRequest()
|
||||
{
|
||||
this.When(x => x.WhenIBuildARequest())
|
||||
.Then(x => x.ThenTheRequestContainsTheRequestMessage())
|
||||
.Then(x => x.ThenTheRequestContainsTheIsQos())
|
||||
.Then(x => x.ThenTheRequestContainsTheQosProvider())
|
||||
.Then(x => x.ThenTheRequestContainsUseCookieContainer())
|
||||
.Then(x => x.ThenTheRequestContainsAllowAutoRedirect())
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
private void WhenIBuildARequest()
|
||||
{
|
||||
_response = _requestCreator.Build(_requestMessage,
|
||||
_isQos, _qoSProvider, _useCookieContainer, _allowAutoRedirect)
|
||||
.GetAwaiter()
|
||||
.GetResult();
|
||||
}
|
||||
|
||||
private void ThenTheRequestContainsTheRequestMessage()
|
||||
{
|
||||
_response.Data.HttpRequestMessage.ShouldBe(_requestMessage);
|
||||
}
|
||||
|
||||
private void ThenTheRequestContainsTheIsQos()
|
||||
{
|
||||
_response.Data.IsQos.ShouldBe(_isQos);
|
||||
}
|
||||
|
||||
private void ThenTheRequestContainsTheQosProvider()
|
||||
{
|
||||
_response.Data.QosProvider.ShouldBe(_qoSProvider);
|
||||
}
|
||||
|
||||
namespace Ocelot.UnitTests.Request
|
||||
{
|
||||
using System.Net.Http;
|
||||
|
||||
using Ocelot.Request.Builder;
|
||||
using Ocelot.Requester.QoS;
|
||||
using Ocelot.Responses;
|
||||
using Shouldly;
|
||||
using TestStack.BDDfy;
|
||||
using Xunit;
|
||||
|
||||
public class HttpRequestCreatorTests
|
||||
{
|
||||
private readonly IRequestCreator _requestCreator;
|
||||
private readonly bool _isQos;
|
||||
private readonly IQoSProvider _qoSProvider;
|
||||
private readonly HttpRequestMessage _requestMessage;
|
||||
private readonly bool _useCookieContainer;
|
||||
private readonly bool _allowAutoRedirect;
|
||||
private readonly bool _useTracing;
|
||||
|
||||
private Response<Ocelot.Request.Request> _response;
|
||||
|
||||
public HttpRequestCreatorTests()
|
||||
{
|
||||
_requestCreator = new HttpRequestCreator();
|
||||
_isQos = true;
|
||||
_qoSProvider = new NoQoSProvider();
|
||||
_useCookieContainer = false;
|
||||
_allowAutoRedirect = false;
|
||||
_useTracing = false;
|
||||
_requestMessage = new HttpRequestMessage();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ShouldBuildRequest()
|
||||
{
|
||||
this.When(x => x.WhenIBuildARequest())
|
||||
.Then(x => x.ThenTheRequestContainsTheRequestMessage())
|
||||
.Then(x => x.ThenTheRequestContainsTheIsQos())
|
||||
.Then(x => x.ThenTheRequestContainsTheQosProvider())
|
||||
.Then(x => x.ThenTheRequestContainsUseCookieContainer())
|
||||
.Then(x => x.ThenTheRequestContainsUseTracing())
|
||||
.Then(x => x.ThenTheRequestContainsAllowAutoRedirect())
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
private void WhenIBuildARequest()
|
||||
{
|
||||
_response = _requestCreator.Build(_requestMessage,
|
||||
_isQos, _qoSProvider, _useCookieContainer, _allowAutoRedirect, _useTracing)
|
||||
.GetAwaiter()
|
||||
.GetResult();
|
||||
}
|
||||
|
||||
private void ThenTheRequestContainsTheRequestMessage()
|
||||
{
|
||||
_response.Data.HttpRequestMessage.ShouldBe(_requestMessage);
|
||||
}
|
||||
|
||||
private void ThenTheRequestContainsTheIsQos()
|
||||
{
|
||||
_response.Data.IsQos.ShouldBe(_isQos);
|
||||
}
|
||||
|
||||
private void ThenTheRequestContainsTheQosProvider()
|
||||
{
|
||||
_response.Data.QosProvider.ShouldBe(_qoSProvider);
|
||||
}
|
||||
|
||||
private void ThenTheRequestContainsUseCookieContainer()
|
||||
{
|
||||
_response.Data.UseCookieContainer.ShouldBe(_useCookieContainer);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void ThenTheRequestContainsUseTracing()
|
||||
{
|
||||
_response.Data.IsTracing.ShouldBe(_useTracing);
|
||||
}
|
||||
|
||||
private void ThenTheRequestContainsAllowAutoRedirect()
|
||||
{
|
||||
_response.Data.AllowAutoRedirect.ShouldBe(_allowAutoRedirect);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,81 +1,81 @@
|
||||
namespace Ocelot.UnitTests.Requester
|
||||
{
|
||||
using System.Net.Http;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Moq;
|
||||
using Ocelot.Logging;
|
||||
using Ocelot.Requester;
|
||||
using Ocelot.Requester.Middleware;
|
||||
using Ocelot.Requester.QoS;
|
||||
using Ocelot.Responses;
|
||||
using TestStack.BDDfy;
|
||||
using Xunit;
|
||||
|
||||
public class HttpRequesterMiddlewareTests : ServerHostedMiddlewareTest
|
||||
{
|
||||
private readonly Mock<IHttpRequester> _requester;
|
||||
private OkResponse<HttpResponseMessage> _response;
|
||||
private OkResponse<Ocelot.Request.Request> _request;
|
||||
|
||||
public HttpRequesterMiddlewareTests()
|
||||
{
|
||||
_requester = new Mock<IHttpRequester>();
|
||||
|
||||
GivenTheTestServerIsConfigured();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_call_scoped_data_repository_correctly()
|
||||
{
|
||||
this.Given(x => x.GivenTheRequestIs(new Ocelot.Request.Request(new HttpRequestMessage(),true, new NoQoSProvider(), false, false)))
|
||||
.And(x => x.GivenTheRequesterReturns(new HttpResponseMessage()))
|
||||
.And(x => x.GivenTheScopedRepoReturns())
|
||||
.When(x => x.WhenICallTheMiddleware())
|
||||
.Then(x => x.ThenTheScopedRepoIsCalledCorrectly())
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
protected override void GivenTheTestServerServicesAreConfigured(IServiceCollection services)
|
||||
{
|
||||
services.AddSingleton<IOcelotLoggerFactory, AspDotNetLoggerFactory>();
|
||||
services.AddLogging();
|
||||
services.AddSingleton(_requester.Object);
|
||||
services.AddSingleton(ScopedRepository.Object);
|
||||
}
|
||||
|
||||
protected override void GivenTheTestServerPipelineIsConfigured(IApplicationBuilder app)
|
||||
{
|
||||
app.UseHttpRequesterMiddleware();
|
||||
}
|
||||
|
||||
private void GivenTheRequestIs(Ocelot.Request.Request request)
|
||||
{
|
||||
_request = new OkResponse<Ocelot.Request.Request>(request);
|
||||
ScopedRepository
|
||||
.Setup(x => x.Get<Ocelot.Request.Request>(It.IsAny<string>()))
|
||||
.Returns(_request);
|
||||
}
|
||||
|
||||
private void GivenTheRequesterReturns(HttpResponseMessage response)
|
||||
{
|
||||
_response = new OkResponse<HttpResponseMessage>(response);
|
||||
_requester
|
||||
.Setup(x => x.GetResponse(It.IsAny<Ocelot.Request.Request>()))
|
||||
.ReturnsAsync(_response);
|
||||
}
|
||||
|
||||
private void GivenTheScopedRepoReturns()
|
||||
{
|
||||
ScopedRepository
|
||||
.Setup(x => x.Add(It.IsAny<string>(), _response.Data))
|
||||
.Returns(new OkResponse());
|
||||
}
|
||||
|
||||
private void ThenTheScopedRepoIsCalledCorrectly()
|
||||
{
|
||||
ScopedRepository
|
||||
.Verify(x => x.Add("HttpResponseMessage", _response.Data), Times.Once());
|
||||
}
|
||||
}
|
||||
}
|
||||
namespace Ocelot.UnitTests.Requester
|
||||
{
|
||||
using System.Net.Http;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Moq;
|
||||
using Ocelot.Logging;
|
||||
using Ocelot.Requester;
|
||||
using Ocelot.Requester.Middleware;
|
||||
using Ocelot.Requester.QoS;
|
||||
using Ocelot.Responses;
|
||||
using TestStack.BDDfy;
|
||||
using Xunit;
|
||||
|
||||
public class HttpRequesterMiddlewareTests : ServerHostedMiddlewareTest
|
||||
{
|
||||
private readonly Mock<IHttpRequester> _requester;
|
||||
private OkResponse<HttpResponseMessage> _response;
|
||||
private OkResponse<Ocelot.Request.Request> _request;
|
||||
|
||||
public HttpRequesterMiddlewareTests()
|
||||
{
|
||||
_requester = new Mock<IHttpRequester>();
|
||||
|
||||
GivenTheTestServerIsConfigured();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void should_call_scoped_data_repository_correctly()
|
||||
{
|
||||
this.Given(x => x.GivenTheRequestIs(new Ocelot.Request.Request(new HttpRequestMessage(),true, new NoQoSProvider(), false, false,false)))
|
||||
.And(x => x.GivenTheRequesterReturns(new HttpResponseMessage()))
|
||||
.And(x => x.GivenTheScopedRepoReturns())
|
||||
.When(x => x.WhenICallTheMiddleware())
|
||||
.Then(x => x.ThenTheScopedRepoIsCalledCorrectly())
|
||||
.BDDfy();
|
||||
}
|
||||
|
||||
protected override void GivenTheTestServerServicesAreConfigured(IServiceCollection services)
|
||||
{
|
||||
services.AddSingleton<IOcelotLoggerFactory, AspDotNetLoggerFactory>();
|
||||
services.AddLogging();
|
||||
services.AddSingleton(_requester.Object);
|
||||
services.AddSingleton(ScopedRepository.Object);
|
||||
}
|
||||
|
||||
protected override void GivenTheTestServerPipelineIsConfigured(IApplicationBuilder app)
|
||||
{
|
||||
app.UseHttpRequesterMiddleware();
|
||||
}
|
||||
|
||||
private void GivenTheRequestIs(Ocelot.Request.Request request)
|
||||
{
|
||||
_request = new OkResponse<Ocelot.Request.Request>(request);
|
||||
ScopedRepository
|
||||
.Setup(x => x.Get<Ocelot.Request.Request>(It.IsAny<string>()))
|
||||
.Returns(_request);
|
||||
}
|
||||
|
||||
private void GivenTheRequesterReturns(HttpResponseMessage response)
|
||||
{
|
||||
_response = new OkResponse<HttpResponseMessage>(response);
|
||||
_requester
|
||||
.Setup(x => x.GetResponse(It.IsAny<Ocelot.Request.Request>()))
|
||||
.ReturnsAsync(_response);
|
||||
}
|
||||
|
||||
private void GivenTheScopedRepoReturns()
|
||||
{
|
||||
ScopedRepository
|
||||
.Setup(x => x.Add(It.IsAny<string>(), _response.Data))
|
||||
.Returns(new OkResponse());
|
||||
}
|
||||
|
||||
private void ThenTheScopedRepoIsCalledCorrectly()
|
||||
{
|
||||
ScopedRepository
|
||||
.Verify(x => x.Add("HttpResponseMessage", _response.Data), Times.Once());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,10 +1,14 @@
|
||||
namespace Ocelot.UnitTests.Responder
|
||||
{
|
||||
using System.Collections.Generic;
|
||||
using System.Net.Http;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Moq;
|
||||
using Ocelot.DownstreamRouteFinder.Finder;
|
||||
using Ocelot.Errors;
|
||||
using Ocelot.Logging;
|
||||
using Ocelot.Requester;
|
||||
using Ocelot.Responder;
|
||||
using Ocelot.Responder.Middleware;
|
||||
using Ocelot.Responses;
|
||||
@ -35,6 +39,17 @@
|
||||
.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)
|
||||
{
|
||||
services.AddSingleton<IOcelotLoggerFactory, AspDotNetLoggerFactory>();
|
||||
@ -68,5 +83,14 @@
|
||||
{
|
||||
//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 }));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user